From acb594b1d825c6e12369cebb941968ec08c840ce Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 06:24:31 +0200 Subject: Adding upstream version 10.0. Signed-off-by: Daniel Baumann --- .clang-format | 4 +- .github/workflows/freeze.yml | 8 +- .gitignore | 1 + Makefile.am | 2 +- alpine/APKBUILD.in | 15 +- babeld/babel_interface.c | 12 +- babeld/babel_interface.h | 1 - babeld/babel_main.c | 33 +- babeld/babeld.c | 74 +- babeld/babeld.h | 1 - babeld/kernel.c | 1 + bfdd/bfd.h | 5 +- bfdd/bfd_packet.c | 5 + bfdd/bfdd.c | 28 +- bfdd/bfdd_cli.c | 12 +- bfdd/bfdd_nb_config.c | 22 +- bfdd/control.c | 9 +- bfdd/ptm_adapter.c | 8 +- bgpd/bgp_addpath.c | 33 +- bgpd/bgp_addpath.h | 8 + bgpd/bgp_advertise.c | 34 +- bgpd/bgp_advertise.h | 38 +- bgpd/bgp_aspath.c | 5 +- bgpd/bgp_aspath.h | 8 + bgpd/bgp_attr.c | 57 +- bgpd/bgp_attr.h | 96 +- bgpd/bgp_bmp.c | 548 +++- bgpd/bgp_bmp.h | 10 +- bgpd/bgp_btoa.c | 1 + bgpd/bgp_clist.c | 1 + bgpd/bgp_conditional_adv.c | 5 +- bgpd/bgp_damp.h | 5 +- bgpd/bgp_debug.c | 188 +- bgpd/bgp_debug.h | 13 +- bgpd/bgp_dump.c | 1 + bgpd/bgp_ecommunity.c | 32 +- bgpd/bgp_ecommunity.h | 7 +- bgpd/bgp_evpn.c | 166 +- bgpd/bgp_evpn.h | 29 +- bgpd/bgp_evpn_mh.c | 46 +- bgpd/bgp_evpn_mh.h | 5 - bgpd/bgp_evpn_vty.c | 8 +- bgpd/bgp_filter.c | 20 +- bgpd/bgp_filter.h | 6 + bgpd/bgp_flowspec_vty.c | 2 +- bgpd/bgp_fsm.c | 100 +- bgpd/bgp_fsm.h | 9 +- bgpd/bgp_label.c | 9 +- bgpd/bgp_labelpool.c | 32 +- bgpd/bgp_main.c | 47 +- bgpd/bgp_memory.c | 2 + bgpd/bgp_memory.h | 2 + bgpd/bgp_mpath.c | 63 +- bgpd/bgp_mplsvpn.c | 171 +- bgpd/bgp_mplsvpn.h | 43 +- bgpd/bgp_mplsvpn_snmp.c | 6 +- bgpd/bgp_network.c | 16 +- bgpd/bgp_nexthop.h | 27 +- bgpd/bgp_nhg.c | 99 + bgpd/bgp_nhg.h | 18 + bgpd/bgp_nht.c | 186 +- bgpd/bgp_nht.h | 11 +- bgpd/bgp_open.c | 87 +- bgpd/bgp_open.h | 2 + bgpd/bgp_packet.c | 564 +++- bgpd/bgp_pbr.h | 2 +- bgpd/bgp_rd.h | 2 +- bgpd/bgp_route.c | 365 +-- bgpd/bgp_route.h | 88 +- bgpd/bgp_routemap.c | 60 +- bgpd/bgp_routemap_nb.c | 11 +- bgpd/bgp_routemap_nb.h | 17 +- bgpd/bgp_routemap_nb_config.c | 165 +- bgpd/bgp_rpki.c | 1382 +++++++-- bgpd/bgp_rpki.h | 2 + bgpd/bgp_snmp.c | 85 + bgpd/bgp_snmp.h | 3 + bgpd/bgp_snmp_bgp4.c | 4 +- bgpd/bgp_snmp_bgp4.h | 4 +- bgpd/bgp_snmp_bgp4v2.c | 204 +- bgpd/bgp_snmp_bgp4v2.h | 10 + bgpd/bgp_trace.h | 94 +- bgpd/bgp_updgrp.c | 35 +- bgpd/bgp_updgrp.h | 25 +- bgpd/bgp_updgrp_adv.c | 56 +- bgpd/bgp_vpn.c | 21 - bgpd/bgp_vty.c | 536 +++- bgpd/bgp_vty.h | 2 + bgpd/bgp_zebra.c | 475 ++-- bgpd/bgp_zebra.h | 5 +- bgpd/bgpd.c | 172 +- bgpd/bgpd.h | 113 +- bgpd/rfapi/bgp_rfapi_cfg.c | 176 +- bgpd/rfapi/rfapi.c | 83 +- bgpd/rfapi/rfapi_import.c | 266 +- bgpd/rfapi/rfapi_import.h | 2 - bgpd/rfapi/rfapi_monitor.c | 2 +- bgpd/rfapi/rfapi_rib.c | 29 +- bgpd/rfapi/rfapi_vty.c | 84 +- bgpd/rfapi/vnc_import_bgp.c | 32 +- bgpd/rfapi/vnc_zebra.c | 2 +- bgpd/subdir.am | 3 + buildtest.sh | 13 +- configure.ac | 243 +- doc/developer/.readthedocs.yaml | 6 + doc/developer/bmp.rst | 49 + doc/developer/building-docker.rst | 10 +- doc/developer/building-frr-for-archlinux.rst | 8 +- doc/developer/building-frr-for-centos6.rst | 4 +- doc/developer/building-frr-for-centos7.rst | 2 - doc/developer/building-frr-for-centos8.rst | 2 - doc/developer/building-frr-for-debian12.rst | 4 +- doc/developer/building-frr-for-debian8.rst | 10 +- doc/developer/building-frr-for-debian9.rst | 4 +- doc/developer/building-frr-for-freebsd10.rst | 4 +- doc/developer/building-frr-for-freebsd11.rst | 4 +- doc/developer/building-frr-for-freebsd13.rst | 4 +- doc/developer/building-frr-for-freebsd14.rst | 122 + doc/developer/building-frr-for-freebsd9.rst | 4 +- doc/developer/building-frr-for-netbsd6.rst | 4 +- doc/developer/building-frr-for-netbsd7.rst | 4 +- doc/developer/building-frr-for-openbsd6.rst | 4 +- doc/developer/building-frr-for-opensuse.rst | 4 +- doc/developer/building-frr-for-ubuntu1404.rst | 7 +- doc/developer/building-frr-for-ubuntu1604.rst | 6 - doc/developer/building-frr-for-ubuntu1804.rst | 7 +- doc/developer/building-frr-for-ubuntu2004.rst | 31 +- doc/developer/building-frr-for-ubuntu2204.rst | 35 +- doc/developer/building-libyang.rst | 6 +- doc/developer/building.rst | 1 + doc/developer/cross-compiling.rst | 4 +- doc/developer/frr-release-procedure.rst | 7 + doc/developer/include-compile.rst | 4 +- doc/developer/mgmtd-dev.rst | 345 ++- doc/developer/northbound/advanced-topics.rst | 95 +- doc/developer/northbound/architecture.rst | 55 +- doc/developer/northbound/demos.rst | 3 + doc/developer/northbound/links.rst | 3 + doc/developer/northbound/northbound.rst | 2 +- .../operational-data-rpcs-and-notifications.rst | 18 +- doc/developer/northbound/plugins-sysrepo.rst | 212 +- .../northbound/ppr-basic-test-topology.rst | 17 +- .../northbound/ppr-mpls-basic-test-topology.rst | 17 +- .../retrofitting-configuration-commands.rst | 197 +- doc/developer/northbound/transactional-cli.rst | 150 +- .../northbound/yang-module-translator.rst | 36 +- doc/developer/northbound/yang-tools.rst | 12 +- doc/developer/process-architecture.rst | 10 +- doc/developer/rcu.rst | 9 + doc/developer/requirements.txt | 1 + doc/developer/scripting.rst | 6 - doc/developer/subdir.am | 1 + doc/developer/topotests.rst | 98 +- doc/figures/cli-oper-state.drawio | 377 +++ doc/figures/cli-oper-state.svg | 3 + doc/user/.readthedocs.yaml | 3 + doc/user/babeld.rst | 3 +- doc/user/basic.rst | 72 +- doc/user/bfd.rst | 6 +- doc/user/bgp.rst | 196 +- doc/user/bmp.rst | 8 +- doc/user/config-include.rst | 12 + doc/user/eigrpd.rst | 12 +- doc/user/installation.rst | 24 +- doc/user/isisd.rst | 3 +- doc/user/ldpd.rst | 4 +- doc/user/mgmtd.rst | 12 +- doc/user/ospf6d.rst | 136 +- doc/user/ospfd.rst | 5 +- doc/user/overview.rst | 2 + doc/user/pathd.rst | 7 +- doc/user/pbr.rst | 6 +- doc/user/pim.rst | 9 +- doc/user/pimv6.rst | 9 +- doc/user/prior-config-files.rst | 23 + doc/user/requirements.txt | 1 + doc/user/ripd.rst | 12 +- doc/user/ripngd.rst | 2 + doc/user/routemap.rst | 14 +- doc/user/rpki.rst | 82 +- doc/user/sharp.rst | 11 +- doc/user/snmptrap.rst | 20 +- doc/user/static.rst | 12 +- doc/user/vrrp.rst | 6 +- doc/user/vtysh.rst | 10 +- doc/user/zebra.rst | 100 +- docker/alpine/Dockerfile | 9 +- docker/alpine/libyang/APKBUILD | 2 +- docker/ubi8-minimal/Dockerfile | 5 +- docker/ubuntu-ci/Dockerfile | 29 +- docker/ubuntu20-ci/README.md | 8 +- docker/ubuntu22-ci/README.md | 14 +- eigrpd/eigrp_cli.c | 211 +- eigrpd/eigrp_const.h | 3 - eigrpd/eigrp_interface.c | 6 +- eigrpd/eigrp_main.c | 18 +- eigrpd/eigrp_network.c | 4 +- eigrpd/eigrp_northbound.c | 90 +- eigrpd/eigrp_routemap.c | 48 +- eigrpd/eigrp_zebra.c | 4 +- fpm/fpm.h | 5 - gdb/lib.txt | 49 +- include/linux/mroute.h | 193 -- include/linux/mroute6.h | 164 -- include/subdir.am | 2 - isisd/isis_adjacency.h | 2 - isisd/isis_affinitymap.c | 30 - isisd/isis_bpf.c | 3 + isisd/isis_circuit.c | 13 +- isisd/isis_cli.c | 280 +- isisd/isis_lsp.c | 2 +- isisd/isis_main.c | 58 +- isisd/isis_nb_config.c | 144 +- isisd/isis_pdu.c | 177 +- isisd/isis_route.c | 18 - isisd/isis_spf.c | 8 +- isisd/isis_sr.c | 3 +- isisd/isis_tlvs.c | 5 + isisd/isis_zebra.c | 4 +- isisd/isisd.c | 31 +- ldpd/control.c | 1 + ldpd/init.c | 6 +- ldpd/lde.c | 6 +- ldpd/ldp.h | 6 + ldpd/ldp_vty_exec.c | 2 +- ldpd/ldp_zebra.c | 13 +- ldpd/ldpd.c | 33 +- ldpd/ldpd.h | 22 +- ldpd/neighbor.c | 15 +- ldpd/socket.c | 1 + lib/.gitignore | 2 + lib/affinitymap.c | 44 +- lib/affinitymap.h | 12 +- lib/affinitymap_cli.c | 44 +- lib/affinitymap_northbound.c | 30 +- lib/agentx.c | 22 +- lib/base64.c | 13 +- lib/bfd.c | 4 +- lib/bitfield.h | 1 + lib/buffer.h | 26 +- lib/command.c | 10 + lib/command.h | 17 +- lib/command_match.c | 4 +- lib/compiler.h | 16 +- lib/config_paths.h.in | 24 + lib/darr.c | 67 +- lib/darr.h | 476 +++- lib/defun_lex.l | 1 + lib/distribute.c | 153 +- lib/distribute.h | 39 +- lib/event.c | 215 +- lib/filter.c | 15 +- lib/filter.h | 6 +- lib/filter_cli.c | 373 +-- lib/filter_nb.c | 225 +- lib/frr_pthread.c | 38 + lib/frr_pthread.h | 3 + lib/frrcu.c | 36 +- lib/frrcu.h | 6 + lib/frrdistance.h | 35 + lib/frrevent.h | 9 + lib/frrlua.c | 41 +- lib/frrlua.h | 26 +- lib/frrscript.c | 2 + lib/frrscript.h | 21 +- lib/frrsendmmsg.h | 30 + lib/frrstr.c | 45 + lib/frrstr.h | 14 + lib/hash.h | 6 +- lib/hook.h | 2 + lib/if.c | 140 +- lib/if.h | 92 +- lib/if_rmap.c | 12 +- lib/imsg-buffer.c | 1 + lib/jhash.c | 22 +- lib/keychain.c | 17 +- lib/keychain.h | 1 + lib/ldp_sync.h | 2 +- lib/libfrr.c | 208 +- lib/libfrr.h | 56 +- lib/libospf.h | 2 +- lib/log.c | 22 +- lib/log_vty.c | 81 + lib/mgmt.proto | 41 +- lib/mgmt_be_client.c | 628 ++-- lib/mgmt_be_client.h | 98 +- lib/mgmt_defines.h | 38 + lib/mgmt_fe_client.c | 320 ++- lib/mgmt_fe_client.h | 91 +- lib/mgmt_msg.c | 107 +- lib/mgmt_msg.h | 6 + lib/mgmt_msg_native.c | 49 + lib/mgmt_msg_native.h | 585 ++++ lib/mlag.c | 10 +- lib/mlag.h | 6 +- lib/netns_linux.c | 8 + lib/network.c | 1 + lib/nexthop.c | 4 +- lib/nexthop_group.c | 27 +- lib/nexthop_group.h | 4 - lib/northbound.c | 1067 +++---- lib/northbound.h | 291 +- lib/northbound_cli.c | 71 +- lib/northbound_cli.h | 2 +- lib/northbound_confd.c | 2 +- lib/northbound_grpc.cpp | 20 +- lib/northbound_oper.c | 1857 ++++++++++++ lib/northbound_sysrepo.c | 60 +- lib/ntop.c | 2 +- lib/pbr.h | 2 +- lib/pid_output.c | 1 + lib/plist.c | 54 +- lib/prefix.h | 26 +- lib/printf/printf-pos.c | 20 +- lib/printf/vfprintf.c | 12 +- lib/privs.c | 9 + lib/pullwr.c | 2 + lib/resolver.c | 5 + lib/resolver.h | 1 + lib/route_types.txt | 4 + lib/routemap.c | 66 +- lib/routemap.h | 2 + lib/routemap_cli.c | 28 +- lib/routemap_northbound.c | 53 +- lib/routing_nb.c | 10 + lib/routing_nb.h | 3 + lib/routing_nb_config.c | 11 +- lib/sigevent.c | 2 + lib/sockopt.c | 16 +- lib/sockunion.h | 35 + lib/srv6.c | 2 +- lib/srv6.h | 1 + lib/subdir.am | 16 + lib/systemd.c | 1 + lib/vector.c | 1 + lib/vrf.c | 117 +- lib/vrf.h | 7 + lib/vty.c | 376 ++- lib/vty.h | 15 +- lib/workqueue.c | 30 +- lib/workqueue.h | 21 +- lib/yang.c | 512 +++- lib/yang.h | 150 +- lib/yang_translator.c | 8 +- lib/yang_wrappers.c | 27 +- lib/yang_wrappers.h | 7 + lib/zclient.c | 148 +- lib/zclient.h | 86 +- lib/zebra.h | 119 +- lib/zlog.c | 20 +- lib/zlog.h | 1 + lib/zlog_5424.c | 5 +- lib/zlog_live.c | 2 + lib/zlog_targets.c | 1 + m4/.gitignore | 1 + m4/ax_recursive_eval.m4 | 31 + mgmtd/mgmt.c | 17 +- mgmtd/mgmt.h | 8 +- mgmtd/mgmt_be_adapter.c | 895 +++--- mgmtd/mgmt_be_adapter.h | 107 +- mgmtd/mgmt_defines.h | 58 - mgmtd/mgmt_ds.c | 46 +- mgmtd/mgmt_ds.h | 6 +- mgmtd/mgmt_fe_adapter.c | 596 +++- mgmtd/mgmt_fe_adapter.h | 52 +- mgmtd/mgmt_history.c | 35 +- mgmtd/mgmt_main.c | 154 +- mgmtd/mgmt_memory.c | 1 + mgmtd/mgmt_memory.h | 1 + mgmtd/mgmt_testc.c | 230 ++ mgmtd/mgmt_txn.c | 1392 ++++----- mgmtd/mgmt_txn.h | 82 +- mgmtd/mgmt_vty.c | 196 +- mgmtd/subdir.am | 53 +- nhrpd/linux.c | 21 +- nhrpd/netlink.h | 1 - nhrpd/netlink_arp.c | 26 +- nhrpd/nhrp_cache.c | 2 + nhrpd/nhrp_interface.c | 4 +- nhrpd/nhrp_main.c | 25 +- nhrpd/nhrp_multicast.c | 4 + nhrpd/nhrp_packet.c | 2 +- nhrpd/nhrp_peer.c | 9 +- nhrpd/nhrp_route.c | 47 +- nhrpd/nhrp_vc.c | 1 + nhrpd/nhrpd.h | 2 +- nhrpd/vici.c | 1 + ospf6d/ospf6_area.c | 4 +- ospf6d/ospf6_asbr.c | 7 +- ospf6d/ospf6_auth_trailer.c | 125 +- ospf6d/ospf6_auth_trailer.h | 9 +- ospf6d/ospf6_gr.c | 27 +- ospf6d/ospf6_interface.c | 871 +++--- ospf6d/ospf6_interface.h | 54 +- ospf6d/ospf6_intra.c | 6 +- ospf6d/ospf6_main.c | 44 +- ospf6d/ospf6_message.c | 103 +- ospf6d/ospf6_message.h | 5 + ospf6d/ospf6_neighbor.c | 567 ++-- ospf6d/ospf6_neighbor.h | 20 + ospf6d/ospf6_route.c | 6 +- ospf6d/ospf6_route.h | 2 +- ospf6d/ospf6_snmp.c | 7 +- ospf6d/ospf6_top.c | 170 +- ospf6d/ospf6_top.h | 2 + ospf6d/ospf6_zebra.c | 28 +- ospf6d/subdir.am | 2 + ospfd/ospf_api.h | 4 - ospfd/ospf_apiserver.c | 2 +- ospfd/ospf_auth.c | 6 + ospfd/ospf_ext.c | 249 +- ospfd/ospf_flood.c | 2 +- ospfd/ospf_gr.c | 42 +- ospfd/ospf_interface.c | 34 +- ospfd/ospf_interface.h | 1 - ospfd/ospf_lsa.c | 3 +- ospfd/ospf_main.c | 48 +- ospfd/ospf_nsm.c | 4 +- ospfd/ospf_opaque.c | 147 +- ospfd/ospf_opaque.h | 1 + ospfd/ospf_packet.c | 6 +- ospfd/ospf_ri.c | 308 +- ospfd/ospf_snmp.c | 5 +- ospfd/ospf_sr.c | 1 + ospfd/ospf_te.c | 508 +++- ospfd/ospf_vty.c | 19 +- ospfd/ospf_zebra.c | 32 +- ospfd/ospfd.c | 68 +- ospfd/ospfd.h | 3 - pathd/path_cli.c | 76 +- pathd/path_main.c | 17 +- pathd/path_nb.c | 4 +- pathd/path_nb_config.c | 44 +- pathd/path_pcep.h | 1 + pathd/path_pcep_cli.c | 356 ++- pathd/path_zebra.c | 5 +- pbrd/pbr_main.c | 28 +- pbrd/pbr_nht.c | 13 +- pbrd/pbr_nht.h | 2 +- pbrd/pbr_vty.c | 3 +- pbrd/pbr_zebra.c | 48 +- pbrd/pbr_zebra.h | 1 + pceplib/pcep_session_logic_counters.c | 193 +- pceplib/pcep_socket_comm_mock.c | 2 +- pceplib/pcep_utils_counters.c | 10 +- pceplib/pcep_utils_counters.h | 27 +- pceplib/test/pcep_msg_messages_test.c | 50 +- pceplib/test/pcep_msg_object_error_types_test.c | 4 +- pceplib/test/pcep_msg_objects_test.c | 71 +- pceplib/test/pcep_msg_tlvs_test.c | 63 +- pceplib/test/pcep_msg_tools_test.c | 24 +- pceplib/test/pcep_pcc_api_test.c | 24 +- pceplib/test/pcep_utils_counters_test.c | 50 +- pimd/pim6_cmd.c | 8 +- pimd/pim6_main.c | 10 + pimd/pim6_mld.c | 1 + pimd/pim_addr.h | 16 +- pimd/pim_cmd.c | 16 +- pimd/pim_cmd_common.c | 63 +- pimd/pim_hello.c | 2 +- pimd/pim_iface.c | 33 +- pimd/pim_igmp_mtrace.c | 3 +- pimd/pim_instance.h | 2 +- pimd/pim_join.c | 6 +- pimd/pim_main.c | 17 +- pimd/pim_mlag.c | 4 +- pimd/pim_mroute.c | 5 +- pimd/pim_mroute.h | 28 +- pimd/pim_msg.c | 138 +- pimd/pim_nb_config.c | 40 +- pimd/pim_nht.c | 55 +- pimd/pim_nht.h | 3 +- pimd/pim_pim.c | 15 +- pimd/pim_rp.c | 2 +- pimd/pim_rpf.c | 2 +- pimd/pim_sock.c | 1 + pimd/pim_tlv.c | 10 +- pimd/pim_upstream.c | 39 +- pimd/pim_upstream.h | 2 + pimd/pim_vxlan.c | 49 +- pimd/pim_vxlan.h | 3 + pimd/pim_zebra.c | 15 +- pimd/pim_zlookup.c | 5 +- pimd/pimd.h | 3 - pkgsrc/bgpd.sh.in | 2 +- pkgsrc/eigrpd.sh.in | 2 +- pkgsrc/mgmtd.sh.in | 2 +- pkgsrc/ospf6d.sh.in | 2 +- pkgsrc/ospfd.sh.in | 2 +- pkgsrc/ripd.sh.in | 2 +- pkgsrc/ripngd.sh.in | 2 +- pkgsrc/zebra.sh.in | 2 +- python/xref2vtysh.py | 27 +- qpb/qpb.h | 1 + redhat/frr.pam | 4 +- redhat/frr.spec.in | 59 +- ripd/rip_cli.c | 394 ++- ripd/rip_interface.c | 29 +- ripd/rip_main.c | 44 +- ripd/rip_nb.c | 82 +- ripd/rip_nb.h | 8 + ripd/rip_nb_config.c | 37 +- ripd/rip_peer.c | 1 + ripd/rip_routemap.c | 2 +- ripd/rip_snmp.c | 2 +- ripd/rip_zebra.c | 2 + ripd/ripd.c | 94 +- ripd/ripd.h | 2 - ripd/subdir.am | 2 +- ripngd/ripng_cli.c | 338 ++- ripngd/ripng_interface.c | 16 +- ripngd/ripng_main.c | 42 +- ripngd/ripng_nb.c | 54 +- ripngd/ripng_nb.h | 8 + ripngd/ripng_nb_config.c | 28 +- ripngd/ripng_peer.c | 1 + ripngd/ripng_routemap.c | 2 +- ripngd/ripngd.c | 90 +- ripngd/ripngd.h | 2 - ripngd/subdir.am | 2 +- sharpd/sharp_logpump.c | 1 + sharpd/sharp_main.c | 50 +- sharpd/sharp_nht.c | 8 +- sharpd/sharp_nht.h | 1 + sharpd/sharp_vty.c | 32 +- sharpd/sharp_zebra.c | 108 +- sharpd/sharp_zebra.h | 5 +- snapcraft/snapcraft.yaml.in | 5 +- staticd/static_bfd.c | 24 +- staticd/static_main.c | 29 +- staticd/static_nb.c | 15 - staticd/static_nb.h | 6 + staticd/static_nb_config.c | 131 +- staticd/static_nht.c | 34 +- staticd/static_routes.c | 201 +- staticd/static_routes.h | 12 +- staticd/static_vrf.c | 126 +- staticd/static_vrf.h | 14 +- staticd/static_vty.c | 241 +- staticd/static_vty.h | 16 - staticd/static_zebra.c | 64 +- tests/bgpd/test_aspath.c | 10 +- tests/bgpd/test_capability.c | 3 +- tests/bgpd/test_packet.c | 1 + tests/bgpd/test_peer_attr.c | 20 +- tests/bgpd/test_peer_attr.py | 2 +- tests/helpers/c/main.c | 1 + tests/isisd/test_isis_spf.c | 3 +- tests/lib/cli/common_cli.c | 1 + tests/lib/northbound/test_oper_data.c | 28 + tests/lib/northbound/test_oper_data.refout | 4 + tests/lib/subdir.am | 3 +- tests/lib/test_darr.c | 145 +- tests/lib/test_darr.py | 8 + tests/lib/test_frrlua.c | 12 +- tests/lib/test_frrscript.c | 4 +- tests/lib/test_grpc.cpp | 6 +- tests/lib/test_heavy_wq.c | 6 - tests/lib/test_privs.c | 1 + tests/ospfd/test_ospf_spf.c | 1 + .../all_protocol_startup/r1/ipv4_routes.ref | 10 + .../all_protocol_startup/r1/ipv6_routes.ref | 10 + .../topotests/all_protocol_startup/r1/ospf6d.conf | 4 +- .../r1/show_bgp_ipv6_summary.ref | 2 +- .../r1/show_ip_bgp_summary.ref | 2 +- .../test_all_protocol_startup.py | 34 +- tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf | 4 +- tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf | 4 +- tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf | 4 +- tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf | 4 +- tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf | 4 +- tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf | 2 +- tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf | 2 +- tests/topotests/bfd_topo3/test_bfd_topo3.py | 111 +- .../topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py | 1 + tests/topotests/bgp_accept_own/pe1/bgpd.conf | 4 +- .../test_bgp_aggregate-address_route-map.py | 37 +- tests/topotests/bgp_bmp/test_bgp_bmp.py | 11 +- tests/topotests/bgp_default_route/r1/bgpd.conf | 1 + tests/topotests/bgp_default_route/r1/zebra.conf | 2 + .../test_bgp_default-originate.py | 14 +- .../bgp_default_route_route_map_set/r1/bgpd.conf | 23 +- .../bgp_default_route_route_map_set/r1/zebra.conf | 2 - .../bgp_default_route_route_map_set/r2/bgpd.conf | 14 +- .../bgp_default_route_route_map_set/r2/zebra.conf | 2 - .../bgp_default_route_route_map_set/r3/bgpd.conf | 8 + .../bgp_default_route_route_map_set/r3/zebra.conf | 4 + .../test_bgp_default-originate_route-map_set.py | 34 +- .../test_bgp_dont_capability_negotiate.py | 45 +- tests/topotests/bgp_dynamic_capability/r1/frr.conf | 6 + tests/topotests/bgp_dynamic_capability/r2/frr.conf | 1 + .../test_bgp_dynamic_capability_addpath.py | 220 ++ .../test_bgp_dynamic_capability_fqdn.py | 122 + ...test_bgp_dynamic_capability_graceful_restart.py | 40 +- .../test_bgp_dynamic_capability_orf.py | 223 ++ .../test_bgp_dynamic_capability_role.py | 41 +- ...test_bgp_dynamic_capability_software_version.py | 41 +- tests/topotests/bgp_ecmp_topo1/exabgp.env | 1 + tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg | 33 +- tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py | 2 +- tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg | 33 +- .../bgp_ecmp_topo1/test_bgp_ecmp_topo1.py | 2 +- tests/topotests/bgp_evpn_mh/test_evpn_mh.py | 2 +- .../test_bgp_evpn_overlay_index_gateway.py | 11 - .../topotests/bgp_evpn_route_map_match/__init__.py | 0 .../topotests/bgp_evpn_route_map_match/c1/frr.conf | 4 + .../topotests/bgp_evpn_route_map_match/c2/frr.conf | 4 + .../topotests/bgp_evpn_route_map_match/r1/frr.conf | 44 + .../topotests/bgp_evpn_route_map_match/r2/frr.conf | 24 + .../test_bgp_evpn_route_map_match.py | 110 + tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py | 1 + .../PE1/bgp.evpn.route.prefix.after.json | 1 + .../PE1/bgp.evpn.route.prefix.before.json | 1 + .../bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf | 13 + .../bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf | 6 + .../test_bgp_evpn_vxlan_svd.py | 72 +- tests/topotests/bgp_features/r1/ospf6d.conf | 6 +- tests/topotests/bgp_features/r2/ospf6d.conf | 6 +- tests/topotests/bgp_features/r3/ospf6d.conf | 6 +- .../bgp_flowspec/test_bgp_flowspec_topo.py | 6 +- tests/topotests/bgp_l3vpn_label_export/__init__.py | 0 .../topotests/bgp_l3vpn_label_export/r1/bgpd.conf | 22 + .../topotests/bgp_l3vpn_label_export/r1/ldpd.conf | 26 + .../bgp_l3vpn_label_export/r1/staticd.conf | 1 + .../topotests/bgp_l3vpn_label_export/r1/zebra.conf | 7 + .../topotests/bgp_l3vpn_label_export/r2/bgpd.conf | 23 + .../topotests/bgp_l3vpn_label_export/r2/ldpd.conf | 24 + .../bgp_l3vpn_label_export/r2/staticd.conf | 1 + .../topotests/bgp_l3vpn_label_export/r2/zebra.conf | 7 + .../test_bgp_l3vpn_label_export.py | 587 ++++ tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py | 11 + tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf | 29 +- tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf | 4 + .../bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py | 31 +- .../bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py | 2 +- .../bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py | 4 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json | 4 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json | 4 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json | 4 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json | 2 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json | 2 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json | 2 +- tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json | 2 +- tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json | 4 +- .../topotests/bgp_lu_topo1/R2/labelpool.summ.json | 2 +- tests/topotests/bgp_multiview_topo1/exabgp.env | 1 + .../bgp_multiview_topo1/peer1/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer1/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer2/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer2/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer3/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer3/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer4/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer4/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer5/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer5/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer6/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer6/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer7/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer7/exabgp.cfg | 33 +- .../bgp_multiview_topo1/peer8/exa-send.py | 2 +- .../topotests/bgp_multiview_topo1/peer8/exabgp.cfg | 33 +- tests/topotests/bgp_oad/__init__.py | 0 tests/topotests/bgp_oad/r1/frr.conf | 21 + tests/topotests/bgp_oad/r2/frr.conf | 18 + tests/topotests/bgp_oad/r3/frr.conf | 22 + tests/topotests/bgp_oad/r4/frr.conf | 16 + tests/topotests/bgp_oad/r5/frr.conf | 17 + tests/topotests/bgp_oad/test_bgp_oad.py | 95 + .../bgp_peer_type_multipath_relax/exabgp.env | 3 +- .../peer1/exa_readpipe.py | 2 +- .../bgp_peer_type_multipath_relax/peer1/exabgp.cfg | 32 +- .../peer2/exa_readpipe.py | 2 +- .../bgp_peer_type_multipath_relax/peer2/exabgp.cfg | 32 +- .../peer3/exa_readpipe.py | 2 +- .../bgp_peer_type_multipath_relax/peer3/exabgp.cfg | 32 +- .../peer4/exa_readpipe.py | 2 +- .../bgp_peer_type_multipath_relax/peer4/exabgp.cfg | 32 +- .../test_bgp_peer-type_multipath-relax.py | 161 +- tests/topotests/bgp_prefix_sid/exabgp.env | 1 + tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg | 190 +- tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg | 31 +- .../bgp_prefix_sid/test_bgp_prefix_sid.py | 34 +- tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg | 46 +- tests/topotests/bgp_prefix_sid2/r1/bgpd.conf | 3 +- .../bgp_prefix_sid2/r1/vpnv6_rib_entry1.json | 4 - .../bgp_prefix_sid2/r1/vpnv6_rib_entry2.json | 4 - .../test_bgp_recursive_route_ebgp_multi_hop.py | 4 +- tests/topotests/bgp_redistribute_table/__init__.py | 0 .../topotests/bgp_redistribute_table/r1/bgpd.conf | 8 + .../r1/ipv4_routes_with_all_redistribute.json | 71 + .../r1/ipv4_routes_with_redistribute.json | 48 + .../r1/ipv4_routes_without_redistribute.json | 25 + .../topotests/bgp_redistribute_table/r1/zebra.conf | 7 + .../topotests/bgp_redistribute_table/r2/bgpd.conf | 10 + .../topotests/bgp_redistribute_table/r2/zebra.conf | 8 + .../test_bgp_redistribute_table.py | 306 ++ .../topotests/bgp_route_server_client/r1/bgpd.conf | 1 + tests/topotests/bgp_rpki_topo1/__init__.py | 0 tests/topotests/bgp_rpki_topo1/r1/bgpd.conf | 14 + tests/topotests/bgp_rpki_topo1/r1/rtrd.py | 330 +++ tests/topotests/bgp_rpki_topo1/r1/staticd.conf | 1 + tests/topotests/bgp_rpki_topo1/r1/vrps.csv | 3 + tests/topotests/bgp_rpki_topo1/r1/zebra.conf | 6 + .../bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json | 37 + .../r2/bgp_table_rmap_rpki_notfound.json | 7 + .../r2/bgp_table_rmap_rpki_valid.json | 1 + .../bgp_rpki_topo1/r2/bgp_table_rpki_any.json | 52 + .../bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json | 22 + .../bgp_rpki_topo1/r2/bgp_table_rpki_valid.json | 35 + tests/topotests/bgp_rpki_topo1/r2/bgpd.conf | 25 + .../bgp_rpki_topo1/r2/rpki_prefix_table.json | 18 + tests/topotests/bgp_rpki_topo1/r2/staticd.conf | 5 + tests/topotests/bgp_rpki_topo1/r2/zebra.conf | 15 + tests/topotests/bgp_rpki_topo1/r3/bgpd.conf | 14 + tests/topotests/bgp_rpki_topo1/r3/rtrd.py | 1 + tests/topotests/bgp_rpki_topo1/r3/staticd.conf | 1 + tests/topotests/bgp_rpki_topo1/r3/vrps.csv | 1 + tests/topotests/bgp_rpki_topo1/r3/zebra.conf | 5 + tests/topotests/bgp_rpki_topo1/r4/bgpd.conf | 6 + tests/topotests/bgp_rpki_topo1/r4/zebra.conf | 4 + .../bgp_rpki_topo1/test_bgp_rpki_topo1.py | 453 +++ .../test_bgp_set_aspath_exclude.py | 104 +- tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf | 21 +- tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf | 18 +- tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf | 9 + .../topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf | 2 + tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf | 25 + tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf | 5 + tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf | 67 + tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf | 5 + .../bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py | 137 +- .../topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf | 1 - .../topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf | 1 - .../topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf | 1 - .../bgp_srv6l3vpn_route_leak/pe1/bgpd.conf | 1 - .../pe1/results/vrf20_ipv4.json | 2 +- tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf | 1 - tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf | 1 - tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf | 1 - tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json | 1 - .../r1/vpnv4_rib_sid_vpn_export_disabled.json | 1 - .../r1/vpnv4_rib_sid_vpn_export_reenabled.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json | 1 - .../r1/vpnv6_rib_sid_vpn_export_disabled.json | 1 - .../r1/vpnv6_rib_sid_vpn_export_reenabled.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json | 1 - .../r2/vpnv4_rib_sid_vpn_export_disabled.json | 1 - .../r2/vpnv4_rib_sid_vpn_export_reenabled.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json | 1 - .../r2/vpnv6_rib_sid_vpn_export_disabled.json | 1 - .../r2/vpnv6_rib_sid_vpn_export_reenabled.json | 1 - .../bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf | 1 - .../bgp_unique_rid/test_bgp_unique_rid.py | 149 + tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf | 1 + tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf | 7 +- tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf | 1 + .../bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py | 60 +- tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf | 1 + .../bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py | 162 ++ .../test_bgp_vpnv4_per_nexthop_label.py | 16 + .../test_bgp_vpnv6_per_nexthop_label.py | 22 +- .../bgp_vrf_leaking_5549_routes/ce1/bgpd.conf | 1 - .../bgp_vrf_leaking_5549_routes/pe1/bgpd.conf | 1 - .../topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg | 2 +- tests/topotests/bgp_vrf_netns/exabgp.env | 1 + tests/topotests/bgp_vrf_netns/peer1/exa-send.py | 2 +- tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg | 33 +- .../bgp_vrf_netns/test_bgp_vrf_netns_topo.py | 2 +- .../bgp_vrf_route_leak_basic/r1/bgpd.conf | 17 + .../bgp_vrf_route_leak_basic/r1/zebra.conf | 10 + .../topotests/bgp_vrf_route_leak_basic/setup_vrfs | 1 + .../test_bgp-vrf-route-leak-basic.py | 313 +- tests/topotests/conftest.py | 36 + tests/topotests/docker/inner/compile_frr.sh | 4 +- tests/topotests/grpc_basic/test_basic_grpc.py | 4 +- .../step1/show_yang_interface_isis_adjacencies.ref | 4 +- .../isis_sr_flex_algo_topo1/rt1/zebra.conf | 1 - .../isis_sr_flex_algo_topo1/rt2/zebra.conf | 1 - .../isis_sr_flex_algo_topo1/rt3/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt0/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt1/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt2/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt3/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt4/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt5/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt6/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt7/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt8/zebra.conf | 1 - .../isis_sr_flex_algo_topo2/rt9/zebra.conf | 1 - tests/topotests/isis_topo1/test_isis_topo1.py | 4 +- tests/topotests/kinds.yaml | 1 + tests/topotests/lib/bgpcheck.py | 63 + tests/topotests/lib/bmp_collector/bmp.py | 3 +- tests/topotests/lib/common_config.py | 7 +- tests/topotests/lib/fe_client.py | 420 +++ tests/topotests/lib/snmptest.py | 156 +- tests/topotests/lib/topogen.py | 47 +- tests/topotests/lib/topotest.py | 226 +- tests/topotests/mgmt_config/test_config.py | 3 +- tests/topotests/mgmt_config/test_regression.py | 22 +- tests/topotests/mgmt_debug_flags/r1/frr.conf | 11 + tests/topotests/mgmt_debug_flags/test_debug.py | 129 + tests/topotests/mgmt_fe_client/mgmt_pb2.py | 1990 +++++++++++++ tests/topotests/mgmt_fe_client/oper.py | 1 + tests/topotests/mgmt_fe_client/r1/frr.conf | 23 + tests/topotests/mgmt_fe_client/test_client.py | 48 + tests/topotests/mgmt_notif/oper.py | 1 + tests/topotests/mgmt_notif/r1/frr.conf | 27 + tests/topotests/mgmt_notif/r2/frr.conf | 27 + tests/topotests/mgmt_notif/test_notif.py | 102 + .../oper-results/result-lib-vrf-default.json | 576 ++++ .../oper-results/result-lib-vrf-nokey.json | 1145 ++++++++ .../mgmt_oper/oper-results/result-lib-vrf-red.json | 576 ++++ .../oper-results/result-lib-vrf-zebra-ribs.json | 572 ++++ .../oper-results/result-lib-vrf-zebra.json | 572 ++++ .../mgmt_oper/oper-results/result-lib.json | 1145 ++++++++ .../oper-results/result-ribs-rib-ipv4-unicast.json | 225 ++ .../oper-results/result-ribs-rib-nokeys.json | 572 ++++ tests/topotests/mgmt_oper/oper.py | 258 ++ tests/topotests/mgmt_oper/r1/frr-scale.conf | 25 + tests/topotests/mgmt_oper/r1/frr-simple.conf | 26 + tests/topotests/mgmt_oper/r1/frr.conf | 41 + .../mgmt_oper/simple-results/result-empty.json | 2 + .../simple-results/result-intf-description.json | 14 + .../result-intf-eth0-description-exact.json | 3 + .../simple-results/result-intf-eth0-exact.json | 22 + .../simple-results/result-intf-eth0-name.json | 9 + .../result-intf-eth0-only-config.json | 21 + .../simple-results/result-intf-eth0-vrf.json | 10 + .../result-intf-eth0-wd-all-tag.json | 13 + .../simple-results/result-intf-eth0-wd-all.json | 7 + .../result-intf-eth0-wd-explicit.json | 5 + .../simple-results/result-intf-eth0-wd-trim.json | 3 + .../result-intf-eth0-with-config.json | 34 + .../mgmt_oper/simple-results/result-intf-name.json | 21 + .../simple-results/result-intf-state-mtu.json | 12 + .../simple-results/result-intf-state.json | 17 + .../simple-results/result-lib-vrf-default.json | 193 ++ .../simple-results/result-lib-vrf-nokey.json | 350 +++ .../simple-results/result-lib-vrf-red.json | 164 ++ .../simple-results/result-lib-vrf-zebra-ribs.json | 189 ++ .../simple-results/result-lib-vrf-zebra.json | 189 ++ .../mgmt_oper/simple-results/result-lib.json | 350 +++ .../result-ribs-rib-ipv4-unicast.json | 110 + .../simple-results/result-ribs-rib-nokeys.json | 189 ++ .../result-ribs-rib-route-nokey.json | 110 + .../result-ribs-rib-route-prefix.json | 50 + .../simple-results/result-singleton-metric.json | 30 + tests/topotests/mgmt_oper/test_oper.py | 123 + tests/topotests/mgmt_oper/test_querying.py | 113 + tests/topotests/mgmt_oper/test_scale.py | 73 + tests/topotests/mgmt_oper/test_simple.py | 203 ++ tests/topotests/mgmt_startup/test_bigconf.py | 3 +- tests/topotests/mgmt_startup/test_cfgfile_var.py | 3 +- tests/topotests/mgmt_startup/test_late_bigconf.py | 3 +- tests/topotests/mgmt_startup/test_late_uniconf.py | 3 +- tests/topotests/mgmt_startup/test_latestart.py | 3 +- tests/topotests/mgmt_tests/test_yang_mgmt.py | 2 +- tests/topotests/munet/base.py | 72 +- tests/topotests/munet/cli.py | 5 +- tests/topotests/munet/linux.py | 11 +- tests/topotests/munet/mutest/userapi.py | 184 +- tests/topotests/munet/native.py | 144 +- tests/topotests/munet/testing/fixtures.py | 2 +- tests/topotests/munet/testing/hooks.py | 4 +- tests/topotests/munet/watchlog.py | 170 ++ tests/topotests/nb_config/r1/frr.conf | 6 + tests/topotests/nb_config/test_nb_config.py | 69 + tests/topotests/nhrp_topo/test_nhrp_topo.py | 21 + .../test_ospf6_ecmp_inter_area.py | 32 +- .../topotests/ospf6_point_to_multipoint/README.md | 137 + .../r1/ip_6_address.nhg.ref | 14 + .../ospf6_point_to_multipoint/r1/ip_6_address.ref | 14 + .../ospf6_point_to_multipoint/r1/ospf6d.conf | 30 + .../r1/show_ipv6_route.ref | 13 + .../ospf6_point_to_multipoint/r1/zebra.conf | 20 + .../r2/ip_6_address.nhg.ref | 14 + .../ospf6_point_to_multipoint/r2/ip_6_address.ref | 14 + .../ospf6_point_to_multipoint/r2/ospf6d.conf | 30 + .../r2/show_ipv6_route.ref | 13 + .../ospf6_point_to_multipoint/r2/zebra.conf | 20 + .../r3/ip_6_address.nhg.ref | 13 + .../ospf6_point_to_multipoint/r3/ip_6_address.ref | 13 + .../ospf6_point_to_multipoint/r3/ospf6d.conf | 37 + .../r3/show_ipv6_route.ref | 12 + .../ospf6_point_to_multipoint/r3/zebra.conf | 23 + .../r4/ip_6_address.nhg.ref | 14 + .../ospf6_point_to_multipoint/r4/ip_6_address.ref | 14 + .../ospf6_point_to_multipoint/r4/ospf6d.conf | 30 + .../r4/show_ipv6_route.ref | 13 + .../ospf6_point_to_multipoint/r4/zebra.conf | 20 + .../test_ospf6_point_to_multipoint.py | 392 +++ tests/topotests/ospf6_topo1/r1/ospf6d.conf | 4 +- tests/topotests/ospf6_topo1/r2/ospf6d.conf | 4 +- tests/topotests/ospf6_topo1/r3/ospf6d.conf | 6 +- tests/topotests/ospf6_topo1/r4/ospf6d.conf | 4 +- .../r1/sharp_installed.json | 4 +- .../ospf_multi_vrf_bgp_route_leak/r1/frr.conf | 6 + .../r1/zebra-vrf-default.txt | 4 +- .../r1/zebra-vrf-neno.txt | 1 + .../ospf_multi_vrf_bgp_route_leak/r2/frr.conf | 6 + .../r2/zebra-vrf-default.txt | 4 +- .../r2/zebra-vrf-ray.txt | 5 +- .../ospf_multi_vrf_bgp_route_leak/r3/frr.conf | 4 + .../r3/zebra-vrf-default.txt | 2 + .../ospf_multi_vrf_bgp_route_leak/r4/frr.conf | 4 + .../r4/zebra-vrf-default.txt | 2 + tests/topotests/ospf_netns_vrf/r1/ospfd.conf | 8 + tests/topotests/ospf_netns_vrf/r1/zebraroute.txt | 2 + .../topotests/ospf_netns_vrf/r1/zebraroutedown.txt | 2 + tests/topotests/ospf_netns_vrf/r2/ospfd.conf | 7 + tests/topotests/ospf_netns_vrf/r2/zebraroute.txt | 2 + .../topotests/ospf_netns_vrf/r2/zebraroutedown.txt | 2 + tests/topotests/ospf_netns_vrf/r3/ospfd.conf | 8 + tests/topotests/ospf_netns_vrf/r3/zebraroute.txt | 2 + .../topotests/ospf_netns_vrf/r3/zebraroutedown.txt | 1 + .../ospf_netns_vrf/test_ospf_netns_vrf.py | 1 + tests/topotests/ospf_suppress_fa/r1/initial.json | 44 + tests/topotests/ospf_suppress_fa/r1/neighbor.json | 13 + tests/topotests/ospf_suppress_fa/r1/post.json | 44 + .../ospf_suppress_fa/test_ospf_suppress_fa.py | 87 +- tests/topotests/ospf_te_topo1/r1/ospfd.conf | 4 +- tests/topotests/ospf_te_topo1/r1/zebra.conf | 4 +- tests/topotests/ospf_te_topo1/r2/ospfd.conf | 8 +- tests/topotests/ospf_te_topo1/r2/zebra.conf | 8 +- tests/topotests/ospf_te_topo1/r3/ospfd.conf | 4 +- tests/topotests/ospf_te_topo1/r3/zebra.conf | 4 +- tests/topotests/ospf_te_topo1/r4/ospfd.conf | 2 +- tests/topotests/ospf_te_topo1/r4/zebra.conf | 2 +- .../topotests/ospf_te_topo1/test_ospf_te_topo1.py | 52 +- tests/topotests/ospf_topo1/r1/ospf6d.conf | 6 +- tests/topotests/ospf_topo1/r2/ospf6d.conf | 4 +- tests/topotests/ospf_topo1/r3/ospf6d.conf | 6 +- tests/topotests/ospf_topo1/r4/ospf6d.conf | 4 +- .../ospf_unnumbered_point_to_multipoint/README.md | 41 + .../r1/ospf-route.json | 1 + .../r1/ospfd.conf | 11 + .../r1/v4_route.json | 122 + .../r1/zebra.conf | 7 + .../r2/ospf-route.json | 1 + .../r2/ospfd.conf | 11 + .../r2/v4_route.json | 122 + .../r2/zebra.conf | 7 + .../r3/ospf-route.json | 1 + .../r3/ospfd.conf | 11 + .../r3/v4_route.json | 126 + .../r3/zebra.conf | 7 + .../test_ospf_unnumbered_point_to_multipoint.py | 155 + tests/topotests/ospfapi/test_ospf_clientapi.py | 251 +- tests/topotests/pbr_topo1/r1/pbr-map.json | 6 +- tests/topotests/pytest.ini | 3 + .../topotests/route_scale/r1/installed.routes.json | 4 +- tests/topotests/route_scale/r1/no.routes.json | 4 +- tests/topotests/srv6_encap_src_addr/__init__.py | 0 .../r1/expected_srv6_encap_src_addr.json | 9 + .../r1/expected_srv6_encap_src_addr_set.json | 9 + .../r1/expected_srv6_encap_src_addr_unset.json | 9 + tests/topotests/srv6_encap_src_addr/r1/setup.sh | 2 + tests/topotests/srv6_encap_src_addr/r1/zebra.conf | 17 + .../test_srv6_encap_src_addr.py | 142 + tests/topotests/srv6_locator/r1/sharpd.conf | 1 - tests/topotests/srv6_locator/r1/zebra.conf | 1 - .../srv6_locator_custom_bits_length/r1/sharpd.conf | 1 - .../srv6_locator_custom_bits_length/r1/zebra.conf | 1 - .../test_srv6_locator.py | 147 - .../test_srv6_locator_custom_bits_length.py | 147 + tests/topotests/srv6_locator_usid/r1/sharpd.conf | 1 - tests/topotests/srv6_locator_usid/r1/zebra.conf | 1 - tests/topotests/srv6_static_route/r1/staticd.conf | 1 - tests/topotests/srv6_static_route/r1/zebra.conf | 1 - tests/topotests/static_simple/r1/mgmtd.conf | 10 + tests/topotests/static_simple/r1/zebra.conf | 10 + .../topotests/static_simple/test_static_simple.py | 11 +- tests/topotests/static_vrf/r1/frr.conf | 18 + tests/topotests/static_vrf/test_static_vrf.py | 126 + .../test_zebra_multiple_connected.py | 17 + tests/topotests/zebra_seg6_route/r1/zebra.conf | 1 - .../zebra_seg6_route/test_zebra_seg6_route.py | 4 +- .../topotests/zebra_seg6local_route/r1/zebra.conf | 1 - tests/zebra/test_lm_plugin.c | 2 +- tools/checkpatch.pl | 5 +- tools/checkpatch.sh | 3 +- tools/etc/frr/support_bundle_commands.conf | 10 +- tools/frr-llvm-cg.c | 2 +- tools/frr-reload.py | 99 +- tools/frr.in | 10 +- tools/frr.service.in | 8 +- tools/frr@.service.in | 8 +- tools/frr_babeltrace.py | 70 + tools/frrcommon.sh.in | 12 +- tools/frrinit.sh.in | 2 +- tools/gen_northbound_callbacks.c | 217 +- tools/gen_yang_deviations.c | 2 +- tools/valgrind.supp | 9 + tools/watchfrr.sh.in | 4 +- vrrpd/vrrp.c | 18 +- vrrpd/vrrp_main.c | 22 +- vrrpd/vrrp_northbound.c | 4 +- vrrpd/vrrp_vty.c | 18 +- vrrpd/vrrp_zebra.c | 9 +- vtysh/.gitignore | 1 - vtysh/subdir.am | 10 - vtysh/vtysh.c | 225 +- vtysh/vtysh.h | 31 +- vtysh/vtysh_config.c | 27 +- vtysh/vtysh_main.c | 6 +- watchfrr/watchfrr.c | 40 +- watchfrr/watchfrr_vty.c | 4 + yang/frr-affinity-map.yang | 11 + yang/frr-bgp-neighbor.yang | 2 +- yang/frr-bgp-route-map.yang | 6 + yang/frr-eigrpd.yang | 5 + yang/frr-filter.yang | 194 +- yang/frr-interface.yang | 9 +- yang/frr-ripd.yang | 11 +- yang/frr-ripngd.yang | 11 +- yang/frr-route-types.yang | 56 +- yang/frr-test-module.yang | 18 + yang/frr-zebra.yang | 787 +++++- yang/ietf/ietf-netconf-acm.yang | 464 +++ yang/ietf/ietf-netconf-with-defaults.yang | 139 + yang/ietf/ietf-netconf.yang | 933 ++++++ yang/subdir.am | 3 + zebra/connected.c | 87 +- zebra/debug.c | 4 + zebra/debug_nl.c | 1 + zebra/dplane_fpm_nl.c | 44 +- zebra/ge_netlink.c | 369 +++ zebra/ge_netlink.h | 49 + zebra/if_ioctl.c | 1 + zebra/if_netlink.c | 83 +- zebra/if_sysctl.c | 2 + zebra/interface.c | 2104 ++------------ zebra/interface.h | 26 +- zebra/ioctl.c | 2 + zebra/irdp_interface.c | 13 +- zebra/irdp_main.c | 39 +- zebra/kernel_netlink.c | 171 +- zebra/kernel_netlink.h | 3 + zebra/kernel_socket.c | 17 +- zebra/label_manager.c | 191 +- zebra/label_manager.h | 15 +- zebra/main.c | 50 +- zebra/netconf_netlink.c | 3 + zebra/redistribute.c | 92 +- zebra/rib.h | 36 +- zebra/router-id.c | 270 +- zebra/router-id.h | 2 +- zebra/rt.h | 3 +- zebra/rt_netlink.c | 116 +- zebra/rt_socket.c | 2 + zebra/rtadv.c | 1283 +-------- zebra/rtadv.h | 25 + zebra/rtread_sysctl.c | 2 + zebra/rule_netlink.c | 3 + zebra/subdir.am | 6 +- zebra/table_manager.c | 64 +- zebra/table_manager.h | 20 +- zebra/tc_netlink.c | 1 + zebra/zapi_msg.c | 151 +- zebra/zapi_msg.h | 9 +- zebra/zebra_affinitymap.c | 90 +- zebra/zebra_cli.c | 2984 +++++++++++++++++++ zebra/zebra_cli.h | 10 + zebra/zebra_dplane.c | 159 +- zebra/zebra_dplane.h | 15 + zebra/zebra_evpn.c | 9 +- zebra/zebra_evpn_mh.c | 359 +-- zebra/zebra_evpn_mh.h | 19 +- zebra/zebra_evpn_neigh.c | 4 + zebra/zebra_fpm.c | 4 + zebra/zebra_fpm_dt.c | 4 + zebra/zebra_fpm_netlink.c | 3 + zebra/zebra_gr.c | 2 +- zebra/zebra_mlag.c | 35 +- zebra/zebra_mpls.c | 99 +- zebra/zebra_mpls.h | 3 +- zebra/zebra_mpls_netlink.c | 4 + zebra/zebra_mpls_openbsd.c | 134 +- zebra/zebra_mpls_vty.c | 3 + zebra/zebra_nb.c | 489 +++- zebra/zebra_nb.h | 211 +- zebra/zebra_nb_config.c | 2989 +++++++++++++++++--- zebra/zebra_nb_state.c | 48 +- zebra/zebra_netns_id.c | 6 + zebra/zebra_netns_notify.c | 1 + zebra/zebra_nhg.c | 199 +- zebra/zebra_nhg.h | 2 +- zebra/zebra_ns.c | 16 +- zebra/zebra_ns.h | 5 +- zebra/zebra_pbr.c | 4 +- zebra/zebra_pbr.h | 2 +- zebra/zebra_ptm.c | 74 +- zebra/zebra_ptm.h | 9 +- zebra/zebra_pw.c | 18 +- zebra/zebra_pw.h | 7 +- zebra/zebra_rib.c | 264 +- zebra/zebra_rnh.c | 10 +- zebra/zebra_routemap.c | 668 +---- zebra/zebra_routemap.h | 12 +- zebra/zebra_router.c | 30 +- zebra/zebra_router.h | 7 + zebra/zebra_script.c | 2 + zebra/zebra_snmp.c | 2 + zebra/zebra_srv6.c | 38 +- zebra/zebra_srv6.h | 7 + zebra/zebra_srv6_vty.c | 89 + zebra/zebra_tc.c | 23 +- zebra/zebra_tc.h | 3 + zebra/zebra_vrf.c | 153 +- zebra/zebra_vty.c | 386 +-- zebra/zebra_vxlan.c | 227 +- zebra/zebra_vxlan.h | 6 +- zebra/zserv.c | 6 +- zebra/zserv.h | 11 +- 1167 files changed, 56059 insertions(+), 19865 deletions(-) create mode 100644 bgpd/bgp_nhg.c create mode 100644 bgpd/bgp_nhg.h create mode 100644 doc/developer/bmp.rst create mode 100644 doc/developer/building-frr-for-freebsd14.rst create mode 100644 doc/developer/requirements.txt create mode 100644 doc/figures/cli-oper-state.drawio create mode 100644 doc/figures/cli-oper-state.svg create mode 100644 doc/user/config-include.rst create mode 100644 doc/user/prior-config-files.rst create mode 100644 doc/user/requirements.txt delete mode 100644 include/linux/mroute.h delete mode 100644 include/linux/mroute6.h create mode 100644 lib/config_paths.h.in create mode 100644 lib/frrdistance.h create mode 100644 lib/frrsendmmsg.h create mode 100644 lib/mgmt_defines.h create mode 100644 lib/mgmt_msg_native.c create mode 100644 lib/mgmt_msg_native.h create mode 100644 lib/northbound_oper.c create mode 100644 m4/ax_recursive_eval.m4 delete mode 100644 mgmtd/mgmt_defines.h create mode 100644 mgmtd/mgmt_testc.c create mode 100644 tests/lib/test_darr.py create mode 100644 tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf create mode 100644 tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py create mode 100644 tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py create mode 100644 tests/topotests/bgp_evpn_route_map_match/__init__.py create mode 100644 tests/topotests/bgp_evpn_route_map_match/c1/frr.conf create mode 100644 tests/topotests/bgp_evpn_route_map_match/c2/frr.conf create mode 100644 tests/topotests/bgp_evpn_route_map_match/r1/frr.conf create mode 100644 tests/topotests/bgp_evpn_route_map_match/r2/frr.conf create mode 100644 tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json create mode 100644 tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json create mode 100644 tests/topotests/bgp_l3vpn_label_export/__init__.py create mode 100644 tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf create mode 100644 tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py create mode 100644 tests/topotests/bgp_oad/__init__.py create mode 100644 tests/topotests/bgp_oad/r1/frr.conf create mode 100644 tests/topotests/bgp_oad/r2/frr.conf create mode 100644 tests/topotests/bgp_oad/r3/frr.conf create mode 100644 tests/topotests/bgp_oad/r4/frr.conf create mode 100644 tests/topotests/bgp_oad/r5/frr.conf create mode 100644 tests/topotests/bgp_oad/test_bgp_oad.py create mode 100644 tests/topotests/bgp_redistribute_table/__init__.py create mode 100644 tests/topotests/bgp_redistribute_table/r1/bgpd.conf create mode 100644 tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json create mode 100644 tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json create mode 100644 tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json create mode 100644 tests/topotests/bgp_redistribute_table/r1/zebra.conf create mode 100644 tests/topotests/bgp_redistribute_table/r2/bgpd.conf create mode 100644 tests/topotests/bgp_redistribute_table/r2/zebra.conf create mode 100644 tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py create mode 100644 tests/topotests/bgp_rpki_topo1/__init__.py create mode 100644 tests/topotests/bgp_rpki_topo1/r1/bgpd.conf create mode 100755 tests/topotests/bgp_rpki_topo1/r1/rtrd.py create mode 100644 tests/topotests/bgp_rpki_topo1/r1/staticd.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r1/vrps.csv create mode 100644 tests/topotests/bgp_rpki_topo1/r1/zebra.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json create mode 120000 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/bgpd.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json create mode 100644 tests/topotests/bgp_rpki_topo1/r2/staticd.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r2/zebra.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r3/bgpd.conf create mode 120000 tests/topotests/bgp_rpki_topo1/r3/rtrd.py create mode 100644 tests/topotests/bgp_rpki_topo1/r3/staticd.conf create mode 120000 tests/topotests/bgp_rpki_topo1/r3/vrps.csv create mode 100644 tests/topotests/bgp_rpki_topo1/r3/zebra.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r4/bgpd.conf create mode 100644 tests/topotests/bgp_rpki_topo1/r4/zebra.conf create mode 100644 tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf create mode 100644 tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf create mode 100644 tests/topotests/lib/bgpcheck.py create mode 100755 tests/topotests/lib/fe_client.py create mode 100644 tests/topotests/mgmt_debug_flags/r1/frr.conf create mode 100644 tests/topotests/mgmt_debug_flags/test_debug.py create mode 100644 tests/topotests/mgmt_fe_client/mgmt_pb2.py create mode 120000 tests/topotests/mgmt_fe_client/oper.py create mode 100644 tests/topotests/mgmt_fe_client/r1/frr.conf create mode 100644 tests/topotests/mgmt_fe_client/test_client.py create mode 120000 tests/topotests/mgmt_notif/oper.py create mode 100644 tests/topotests/mgmt_notif/r1/frr.conf create mode 100644 tests/topotests/mgmt_notif/r2/frr.conf create mode 100644 tests/topotests/mgmt_notif/test_notif.py create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-lib.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json create mode 100644 tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json create mode 100644 tests/topotests/mgmt_oper/oper.py create mode 100644 tests/topotests/mgmt_oper/r1/frr-scale.conf create mode 100644 tests/topotests/mgmt_oper/r1/frr-simple.conf create mode 100644 tests/topotests/mgmt_oper/r1/frr.conf create mode 100644 tests/topotests/mgmt_oper/simple-results/result-empty.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-description.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-name.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-intf-state.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-lib.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json create mode 100644 tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json create mode 100644 tests/topotests/mgmt_oper/test_oper.py create mode 100644 tests/topotests/mgmt_oper/test_querying.py create mode 100644 tests/topotests/mgmt_oper/test_scale.py create mode 100644 tests/topotests/mgmt_oper/test_simple.py create mode 100644 tests/topotests/munet/watchlog.py create mode 100644 tests/topotests/nb_config/r1/frr.conf create mode 100644 tests/topotests/nb_config/test_nb_config.py create mode 100644 tests/topotests/ospf6_point_to_multipoint/README.md create mode 100644 tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref create mode 100644 tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf create mode 100644 tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py create mode 100644 tests/topotests/ospf_suppress_fa/r1/initial.json create mode 100644 tests/topotests/ospf_suppress_fa/r1/neighbor.json create mode 100644 tests/topotests/ospf_suppress_fa/r1/post.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/README.md create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf create mode 100644 tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py create mode 100644 tests/topotests/srv6_encap_src_addr/__init__.py create mode 100644 tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json create mode 100644 tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json create mode 100644 tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json create mode 100644 tests/topotests/srv6_encap_src_addr/r1/setup.sh create mode 100644 tests/topotests/srv6_encap_src_addr/r1/zebra.conf create mode 100755 tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py delete mode 100755 tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py create mode 100755 tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py create mode 100644 tests/topotests/static_vrf/r1/frr.conf create mode 100644 tests/topotests/static_vrf/test_static_vrf.py create mode 100644 yang/ietf/ietf-netconf-acm.yang create mode 100644 yang/ietf/ietf-netconf-with-defaults.yang create mode 100644 yang/ietf/ietf-netconf.yang create mode 100644 zebra/ge_netlink.c create mode 100644 zebra/ge_netlink.h create mode 100644 zebra/zebra_cli.c create mode 100644 zebra/zebra_cli.h diff --git a/.clang-format b/.clang-format index 3971384..d16263d 100644 --- a/.clang-format +++ b/.clang-format @@ -77,10 +77,11 @@ FixNamespaceComments: false ForEachMacros: # lib: outliers: - 'FOR_ALL_INTERFACES' - - 'FOR_ALL_INTERFACES_ADDRESSES' # libyang outliers: - 'LY_FOR_KEYS' - 'LY_LIST_FOR' + - 'LYD_LIST_FOR_INST' + - 'LYD_LIST_FOR_INST_SAFE' - 'LY_TREE_FOR' - 'LY_TREE_DFS_BEGIN' - 'LYD_TREE_DFS_BEGIN' @@ -103,6 +104,7 @@ ForEachMacros: - 'FOREACH_AFI_SAFI' - 'FOREACH_AFI_SAFI_NSF' - 'FOREACH_BE_APPLY_BATCH_IN_LIST' + - 'FOREACH_BE_CLIENT_BITS' - 'FOREACH_BE_TXN_BATCH_IN_LIST' - 'FOREACH_BE_TXN_IN_LIST' - 'FOREACH_CMT_REC' diff --git a/.github/workflows/freeze.yml b/.github/workflows/freeze.yml index f3506d0..a780298 100644 --- a/.github/workflows/freeze.yml +++ b/.github/workflows/freeze.yml @@ -1,4 +1,4 @@ -name: Warn before merging if a "freeze" label exists +name: Warn before merging if a "freeze" or "do not merge" label exists on: pull_request_target: @@ -6,12 +6,12 @@ on: jobs: freeze_warning: - if: ${{ contains(github.event.*.labels.*.name, 'freeze') }} - name: Warn before merging if a "freeze" label exists + if: ${{ contains(github.event.*.labels.*.name, 'freeze') || contains(github.event.*.labels.*.name, 'do not merge') }} + name: Warn before merging if a "freeze" or "do not merge" label exists runs-on: ubuntu-latest steps: - name: Check for "freeze" label run: | - echo "Pull request is labeled as 'freeze'" + echo "Pull request is labeled as 'freeze' or 'do not merge'" echo "This workflow fails so that the pull request cannot be merged." exit 1 diff --git a/.gitignore b/.gitignore index a66e3cc..07cdb11 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ refix /test-suite.log pceplib/test/*.log pceplib/test/*.trs +/tests/topotests/lib/mgmt_pb2.py diff --git a/Makefile.am b/Makefile.am index f56e1b8..0ce716e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,7 @@ MODULE_LDFLAGS = \ $(SAN_FLAGS) \ # end -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE) +DEFS = @DEFS@ -DCONFDATE=$(CONFDATE) AR_FLAGS = @AR_FLAGS@ ARFLAGS = @ARFLAGS@ diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 9a29ff3..2cb3fee 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -27,10 +27,9 @@ source="$pkgname-$pkgver.tar.gz" builddir="$srcdir"/$pkgname-$pkgver +_sysconfdir=/etc _sbindir=/usr/lib/frr -_sysconfdir=/etc/frr _libdir=/usr/lib -_localstatedir=/var/run/frr _user=frr build() { @@ -38,10 +37,10 @@ build() { ./configure \ --prefix=/usr \ - --sbindir=$_sbindir \ --sysconfdir=$_sysconfdir \ + --localstatedir=/var \ + --sbindir=$_sbindir \ --libdir=$_libdir \ - --localstatedir=$_localstatedir \ --enable-rpki \ --enable-vtysh \ --enable-multipath=64 \ @@ -63,7 +62,9 @@ package() { cd "$builddir" make DESTDIR="$pkgdir" install - install -Dm644 "$builddir"/tools/etc/frr/daemons "$pkgdir"$_sysconfdir/daemons - install -d "$pkgdir"/etc/init.d - ln -s ${_sbindir}/frr "$pkgdir"/etc/init.d/frr + install -d $pkgdir/$_sysconfdir/frr + install -m 0644 tools/etc/frr/daemons $pkgdir/$_sysconfdir/frr/daemons + + install -d $pkgdir/$_sysconfdir/init.d + ln -s ${_sbindir}/frr $pkgdir/$_sysconfdir/init.d/frr } diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index ceff472..c4349b5 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -695,6 +695,11 @@ interface_reset(struct interface *ifp) babel_ifp->cost, babel_ifp->ipv4 ? ", IPv4" : ""); + if (babel_ifp->ipv4 != NULL){ + free(babel_ifp->ipv4); + babel_ifp->ipv4 = NULL; + } + return 1; } @@ -734,12 +739,11 @@ int is_interface_ll_address(struct interface *ifp, const unsigned char *address) { struct connected *connected; - struct listnode *node; if(!if_up(ifp)) return 0; - FOR_ALL_INTERFACES_ADDRESSES(ifp, connected, node) { + frr_each (if_connected, ifp->connected, connected) { if (connected->address->family == AF_INET6 && memcmp(&connected->address->u.prefix6, address, IPV6_MAX_BYTELEN) @@ -1345,5 +1349,9 @@ babel_interface_allocate (void) static void babel_interface_free (babel_interface_nfo *babel_ifp) { + if (babel_ifp->ipv4){ + free(babel_ifp->ipv4); + babel_ifp->ipv4 = NULL; + } XFREE(MTYPE_BABEL_IF, babel_ifp); } diff --git a/babeld/babel_interface.h b/babeld/babel_interface.h index 12fa6e2..a585e23 100644 --- a/babeld/babel_interface.h +++ b/babeld/babel_interface.h @@ -82,7 +82,6 @@ static inline int if_up(struct interface *ifp) { return (if_is_operative(ifp) && - ifp->connected != NULL && CHECK_FLAG(babel_get_if_nfo(ifp)->flags, BABEL_IF_IS_UP)); } diff --git a/babeld/babel_main.c b/babeld/babel_main.c index b6126d5..10ab1b5 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -5,6 +5,8 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek /* include zebra library */ #include +#include + #include "getopt.h" #include "if.h" #include "log.h" @@ -56,7 +58,6 @@ unsigned char protocol_group[16]; /* babel's link-local multicast address */ int protocol_port; /* babel's port */ int protocol_socket = -1; /* socket: communicate with others babeld */ -static const char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; static char *babel_vty_addr = NULL; static int babel_vty_port = BABEL_VTY_PORT; @@ -124,18 +125,20 @@ static const struct frr_yang_module_info *const babeld_yang_modules[] = { &frr_vrf_info, }; +/* clang-format off */ FRR_DAEMON_INFO(babeld, BABELD, - .vty_port = BABEL_VTY_PORT, - .proghelp = "Implementation of the BABEL routing protocol.", + .vty_port = BABEL_VTY_PORT, + .proghelp = "Implementation of the BABEL routing protocol.", - .signals = babel_signals, - .n_signals = array_size(babel_signals), + .signals = babel_signals, + .n_signals = array_size(babel_signals), - .privs = &babeld_privs, + .privs = &babeld_privs, - .yang_modules = babeld_yang_modules, - .n_yang_modules = array_size(babeld_yang_modules), + .yang_modules = babeld_yang_modules, + .n_yang_modules = array_size(babeld_yang_modules), ); +/* clang-format on */ int main(int argc, char **argv) @@ -169,8 +172,8 @@ main(int argc, char **argv) } } - snprintf(state_file, sizeof(state_file), "%s/%s", - frr_vtydir, "babel-state"); + snprintf(state_file, sizeof(state_file), "%s/%s", frr_runstatedir, + "babel-state"); /* create the threads handler */ master = frr_init (); @@ -182,8 +185,10 @@ main(int argc, char **argv) change_smoothing_half_life(BABEL_DEFAULT_SMOOTHING_HALF_LIFE); /* init some quagga's dependencies, and babeld's commands */ - if_zapi_callbacks(babel_ifp_create, babel_ifp_up, - babel_ifp_down, babel_ifp_destroy); + hook_register_prio(if_real, 0, babel_ifp_create); + hook_register_prio(if_up, 0, babel_ifp_up); + hook_register_prio(if_down, 0, babel_ifp_down); + hook_register_prio(if_unreal, 0, babel_ifp_destroy); babeld_quagga_init(); /* init zebra client's structure and it's commands */ /* this replace kernel_setup && kernel_setup_socket */ @@ -306,6 +311,8 @@ babel_exit_properly(void) babel_save_state_file(); debugf(BABEL_DEBUG_COMMON, "Remove pid file."); debugf(BABEL_DEBUG_COMMON, "Done."); + + vrf_terminate(); frr_fini(); exit(0); @@ -360,7 +367,7 @@ show_babel_main_configuration (struct vty *vty) "id = %s\n" "kernel_metric = %d\n", state_file, - babeld_di.config_file ? babeld_di.config_file : babel_config_default, + babeld_di.config_file, format_address(protocol_group), protocol_port, babel_vty_addr ? babel_vty_addr : "None", diff --git a/babeld/babeld.c b/babeld/babeld.c index ebf8474..6f0a5a7 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -242,7 +242,7 @@ babel_get_myid(void) /* We failed to get a global EUI64 from the interfaces we were given. Let's try to find an interface with a MAC address. */ for(i = 1; i < 256; i++) { - char buf[INTERFACE_NAMSIZ], *ifname; + char buf[IFNAMSIZ], *ifname; unsigned char eui[8]; ifname = if_indextoname(i, buf); if(ifname == NULL) @@ -696,9 +696,8 @@ DEFPY (babel_set_smoothing_half_life, DEFUN (babel_distribute_list, babel_distribute_list_cmd, - "distribute-list [prefix] ACCESSLIST4_NAME [WORD]", + "distribute-list ACCESSLIST4_NAME [WORD]", "Filter networks in routing updates\n" - "Specify a prefix\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" @@ -710,16 +709,26 @@ DEFUN (babel_distribute_list, if (argv[argc - 1]->type == VARIABLE_TKN) ifname = argv[argc - 1]->arg; - return distribute_list_parser(prefix, true, argv[2 + prefix]->text, + return distribute_list_parser(babel_routing_process->distribute_ctx, + prefix, true, argv[2 + prefix]->text, argv[1 + prefix]->arg, ifname); } +ALIAS (babel_distribute_list, + babel_distribute_list_prefix_cmd, + "distribute-list prefix PREFIXLIST4_NAME [WORD]", + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + DEFUN (babel_no_distribute_list, babel_no_distribute_list_cmd, - "no distribute-list [prefix] ACCESSLIST4_NAME [WORD]", + "no distribute-list ACCESSLIST4_NAME [WORD]", NO_STR "Filter networks in routing updates\n" - "Specify a prefix\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" @@ -731,17 +740,28 @@ DEFUN (babel_no_distribute_list, if (argv[argc - 1]->type == VARIABLE_TKN) ifname = argv[argc - 1]->arg; - return distribute_list_no_parser(vty, prefix, true, + return distribute_list_no_parser(babel_routing_process->distribute_ctx, + vty, prefix, true, argv[3 + prefix]->text, argv[2 + prefix]->arg, ifname); } +ALIAS (babel_no_distribute_list, + babel_no_distribute_list_prefix_cmd, + "no distribute-list prefix PREFIXLIST4_NAME [WORD]", + NO_STR + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + DEFUN (babel_ipv6_distribute_list, babel_ipv6_distribute_list_cmd, - "ipv6 distribute-list [prefix] ACCESSLIST6_NAME [WORD]", + "ipv6 distribute-list ACCESSLIST6_NAME [WORD]", "IPv6\n" "Filter networks in routing updates\n" - "Specify a prefix\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" @@ -753,17 +773,28 @@ DEFUN (babel_ipv6_distribute_list, if (argv[argc - 1]->type == VARIABLE_TKN) ifname = argv[argc - 1]->arg; - return distribute_list_parser(prefix, false, argv[3 + prefix]->text, + return distribute_list_parser(babel_routing_process->distribute_ctx, + prefix, false, argv[3 + prefix]->text, argv[2 + prefix]->arg, ifname); } +ALIAS (babel_ipv6_distribute_list, + babel_ipv6_distribute_list_prefix_cmd, + "ipv6 distribute-list prefix PREFIXLIST6_NAME [WORD]", + "IPv6\n" + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + DEFUN (babel_no_ipv6_distribute_list, babel_no_ipv6_distribute_list_cmd, - "no ipv6 distribute-list [prefix] ACCESSLIST6_NAME [WORD]", + "no ipv6 distribute-list ACCESSLIST6_NAME [WORD]", NO_STR "IPv6\n" "Filter networks in routing updates\n" - "Specify a prefix\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" @@ -775,11 +806,24 @@ DEFUN (babel_no_ipv6_distribute_list, if (argv[argc - 1]->type == VARIABLE_TKN) ifname = argv[argc - 1]->arg; - return distribute_list_no_parser(vty, prefix, false, + return distribute_list_no_parser(babel_routing_process->distribute_ctx, + vty, prefix, false, argv[4 + prefix]->text, argv[3 + prefix]->arg, ifname); } +ALIAS (babel_no_ipv6_distribute_list, + babel_no_ipv6_distribute_list_prefix_cmd, + "no ipv6 distribute-list prefix PREFIXLIST6_NAME [WORD]", + NO_STR + "IPv6\n" + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + void babeld_quagga_init(void) { @@ -797,9 +841,13 @@ babeld_quagga_init(void) install_element(BABEL_NODE, &babel_set_smoothing_half_life_cmd); install_element(BABEL_NODE, &babel_distribute_list_cmd); + install_element(BABEL_NODE, &babel_distribute_list_prefix_cmd); install_element(BABEL_NODE, &babel_no_distribute_list_cmd); + install_element(BABEL_NODE, &babel_no_distribute_list_prefix_cmd); install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd); + install_element(BABEL_NODE, &babel_ipv6_distribute_list_prefix_cmd); install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd); + install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_prefix_cmd); vrf_cmd_init(NULL); diff --git a/babeld/babeld.h b/babeld/babeld.h index 619550f..5573719 100644 --- a/babeld/babeld.h +++ b/babeld/babeld.h @@ -53,7 +53,6 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek #endif -#define BABEL_VTY_PORT 2609 #define BABEL_DEFAULT_CONFIG "babeld.conf" /* Values in milliseconds */ diff --git a/babeld/kernel.c b/babeld/kernel.c index 4fe5bcf..aed6dc9 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -11,6 +11,7 @@ Copyright 2011, 2012 by Matthieu Boutier and Juliusz Chroboczek #include #include #include +#include #include "babeld.h" diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 6c5a1e9..66bf706 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -32,6 +32,8 @@ DECLARE_MGROUP(BFDD); DECLARE_MTYPE(BFDD_CONTROL); DECLARE_MTYPE(BFDD_NOTIFICATION); +#define BFDD_SOCK_NAME "%s/bfdd.sock", frr_runstatedir + /* bfd Authentication Type. */ #define BFD_AUTH_NULL 0 #define BFD_AUTH_SIMPLE 1 @@ -193,7 +195,7 @@ struct bfd_key { uint16_t mhop; struct in6_addr peer; struct in6_addr local; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; char vrfname[VRF_NAMSIZ]; } __attribute__((packed)); @@ -782,6 +784,7 @@ void bfdd_cli_init(void); */ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv); void bfdd_zclient_stop(void); +void bfdd_zclient_terminate(void); void bfdd_zclient_unregister(vrf_id_t vrf_id); void bfdd_zclient_register(vrf_id_t vrf_id); void bfdd_sessions_enable_vrf(struct vrf *vrf); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 5d8bf47..fec195c 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -12,6 +12,11 @@ */ #include +#include + +#ifdef GNU_LINUX +#include +#endif #ifdef BFD_LINUX #include diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 95066b9..243cf5c 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -75,6 +75,8 @@ static void sigterm_handler(void) bfd_vrf_terminate(); + bfdd_zclient_terminate(); + /* Terminate and free() FRR related memory. */ frr_fini(); @@ -115,13 +117,20 @@ static const struct frr_yang_module_info *const bfdd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617, - .proghelp = "Implementation of the BFD protocol.", - .signals = bfd_signals, .n_signals = array_size(bfd_signals), - .privs = &bglobal.bfdd_privs, - .yang_modules = bfdd_yang_modules, - .n_yang_modules = array_size(bfdd_yang_modules), +/* clang-format off */ +FRR_DAEMON_INFO(bfdd, BFD, + .vty_port = BFDD_VTY_PORT, + .proghelp = "Implementation of the BFD protocol.", + + .signals = bfd_signals, + .n_signals = array_size(bfd_signals), + + .privs = &bglobal.bfdd_privs, + + .yang_modules = bfdd_yang_modules, + .n_yang_modules = array_size(bfdd_yang_modules), ); +/* clang-format on */ #define OPTION_CTLSOCK 1001 #define OPTION_DPLANEADDR 2000 @@ -333,8 +342,6 @@ int main(int argc, char *argv[]) " --bfdctl Specify bfdd control socket\n" " --dplaneaddr Specify BFD data plane address\n"); - snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, - "", ""); while (true) { opt = frr_getopt(argc, argv, NULL); if (opt == EOF) @@ -355,9 +362,8 @@ int main(int argc, char *argv[]) } } - if (bfdd_di.pathspace && !ctlsockused) - snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, - "/", bfdd_di.pathspace); + if (!ctlsockused) + snprintf(ctl_path, sizeof(ctl_path), BFDD_SOCK_NAME); /* Initialize FRR infrastructure. */ master = frr_init(); diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 44439c6..75034d2 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -219,24 +219,24 @@ static void _bfd_cli_show_peer(struct vty *vty, const struct lyd_node *dnode, bool show_defaults __attribute__((__unused__)), bool mhop) { - const char *vrf = yang_dnode_get_string(dnode, "./vrf"); + const char *vrf = yang_dnode_get_string(dnode, "vrf"); vty_out(vty, " peer %s", - yang_dnode_get_string(dnode, "./dest-addr")); + yang_dnode_get_string(dnode, "dest-addr")); if (mhop) vty_out(vty, " multihop"); - if (yang_dnode_exists(dnode, "./source-addr")) + if (yang_dnode_exists(dnode, "source-addr")) vty_out(vty, " local-address %s", - yang_dnode_get_string(dnode, "./source-addr")); + yang_dnode_get_string(dnode, "source-addr")); if (strcmp(vrf, VRF_DEFAULT_NAME)) vty_out(vty, " vrf %s", vrf); if (!mhop) { const char *ifname = - yang_dnode_get_string(dnode, "./interface"); + yang_dnode_get_string(dnode, "interface"); if (strcmp(ifname, "*")) vty_out(vty, " interface %s", ifname); } @@ -567,7 +567,7 @@ DEFPY_YANG(no_bfd_profile, no_bfd_profile_cmd, void bfd_cli_show_profile(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, "./name")); + vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, "name")); } ALIAS_YANG(bfd_peer_mult, bfd_profile_mult_cmd, diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 8cf2f0a..48fbe71 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -24,17 +24,17 @@ static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, struct sockaddr_any psa, lsa; /* Required destination parameter. */ - strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa); + strtosa(yang_dnode_get_string(dnode, "dest-addr"), &psa); /* Get optional source address. */ memset(&lsa, 0, sizeof(lsa)); - if (yang_dnode_exists(dnode, "./source-addr")) - strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa); + if (yang_dnode_exists(dnode, "source-addr")) + strtosa(yang_dnode_get_string(dnode, "source-addr"), &lsa); - vrfname = yang_dnode_get_string(dnode, "./vrf"); + vrfname = yang_dnode_get_string(dnode, "vrf"); if (!mhop) { - ifname = yang_dnode_get_string(dnode, "./interface"); + ifname = yang_dnode_get_string(dnode, "interface"); if (strcmp(ifname, "*") == 0) ifname = NULL; } @@ -53,7 +53,7 @@ static int session_iter_cb(const struct lyd_node *dnode, void *arg) struct session_iter *iter = arg; const char *ifname; - ifname = yang_dnode_get_string(dnode, "./interface"); + ifname = yang_dnode_get_string(dnode, "interface"); if (strmatch(ifname, "*")) iter->wildcard = true; @@ -76,7 +76,7 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) switch (args->event) { case NB_EV_VALIDATE: - yang_dnode_get_prefix(&p, args->dnode, "./dest-addr"); + yang_dnode_get_prefix(&p, args->dnode, "dest-addr"); if (mhop) { /* @@ -97,7 +97,7 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) * require interface name, otherwise we can't figure * which interface to use to send the packets. */ - ifname = yang_dnode_get_string(args->dnode, "./interface"); + ifname = yang_dnode_get_string(args->dnode, "interface"); if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6) && strcmp(ifname, "*") == 0) { @@ -112,8 +112,8 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop) sess_dnode = yang_dnode_get_parent(args->dnode, "sessions"); - dest = yang_dnode_get_string(args->dnode, "./dest-addr"); - vrfname = yang_dnode_get_string(args->dnode, "./vrf"); + dest = yang_dnode_get_string(args->dnode, "dest-addr"); + vrfname = yang_dnode_get_string(args->dnode, "vrf"); yang_dnode_iterate(session_iter_cb, &iter, sess_dnode, "./single-hop[dest-addr='%s'][vrf='%s']", @@ -275,7 +275,7 @@ int bfdd_bfd_profile_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - name = yang_dnode_get_string(args->dnode, "./name"); + name = yang_dnode_get_string(args->dnode, "name"); bp = bfd_profile_new(name); nb_running_set_entry(args->dnode, bp); diff --git a/bfdd/control.c b/bfdd/control.c index f435358..98fd813 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -12,6 +12,9 @@ #include +#include +#include + #include #include "bfd.h" @@ -92,11 +95,11 @@ int control_init(const char *path) mode_t umval; struct sockaddr_un sun_ = { .sun_family = AF_UNIX, - .sun_path = BFDD_CONTROL_SOCKET, }; - if (path) - strlcpy(sun_.sun_path, path, sizeof(sun_.sun_path)); + assert(path); + + strlcpy(sun_.sun_path, path, sizeof(sun_.sun_path)); /* Remove previously created sockets. */ unlink(sun_.sun_path); diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 9e7ed11..b5ab2ef 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -826,7 +826,8 @@ static zclient_handler *const bfd_handlers[] = { void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) { - if_zapi_callbacks(bfd_ifp_create, NULL, NULL, bfd_ifp_destroy); + hook_register_prio(if_real, 0, bfd_ifp_create); + hook_register_prio(if_unreal, 0, bfd_ifp_destroy); zclient = zclient_new(master, &zclient_options_default, bfd_handlers, array_size(bfd_handlers)); assert(zclient != NULL); @@ -858,6 +859,11 @@ void bfdd_zclient_stop(void) pc_free_all(); } +void bfdd_zclient_terminate(void) +{ + zclient_free(zclient); +} + /* * Client handling. diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c index de4b4a4..f391c13 100644 --- a/bgpd/bgp_addpath.c +++ b/bgpd/bgp_addpath.c @@ -10,6 +10,8 @@ #include "bgp_addpath.h" #include "bgp_route.h" +#include "bgp_open.h" +#include "bgp_packet.h" static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { { @@ -359,6 +361,31 @@ void bgp_addpath_type_changed(struct bgp *bgp) } } +int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, + uint8_t paths) +{ + int action = CAPABILITY_ACTION_UNSET; + + switch (addpath_type) { + case BGP_ADDPATH_ALL: + case BGP_ADDPATH_BEST_PER_AS: + action = CAPABILITY_ACTION_SET; + break; + case BGP_ADDPATH_BEST_SELECTED: + if (paths) + action = CAPABILITY_ACTION_SET; + else + action = CAPABILITY_ACTION_UNSET; + break; + case BGP_ADDPATH_NONE: + case BGP_ADDPATH_MAX: + action = CAPABILITY_ACTION_UNSET; + break; + } + + return action; +} + /* * Change the addpath type assigned to a peer, or peer group. In addition to * adjusting the counts, peer sessions will be reset as needed to make the @@ -373,6 +400,7 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, struct listnode *node, *nnode; struct peer *tmp_peer; struct peer_group *group; + int action = bgp_addpath_capability_action(addpath_type, paths); if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; @@ -430,9 +458,12 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, } } } else { - peer_change_action(peer, afi, safi, peer_change_reset); + if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) && + !CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) + peer_change_action(peer, afi, safi, peer_change_reset); } + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); } /* diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index 2b91d45..d562000 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -15,6 +15,12 @@ #include "bgpd/bgp_table.h" #include "lib/json.h" +struct bgp_addpath_capability { + uint16_t afi; + uint8_t safi; + uint8_t flags; +}; + #define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d); @@ -57,4 +63,6 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi); void bgp_addpath_type_changed(struct bgp *bgp); +extern int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, + uint8_t paths); #endif diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 17d6592..a81f288 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -36,6 +36,8 @@ struct bgp_advertise_attr *bgp_advertise_attr_new(void) void bgp_advertise_attr_free(struct bgp_advertise_attr *baa) { + bgp_advertise_attr_fifo_fini(&baa->fifo); + XFREE(MTYPE_BGP_ADVERTISE_ATTR, baa); } @@ -46,6 +48,9 @@ static void *bgp_advertise_attr_hash_alloc(void *p) baa = bgp_advertise_attr_new(); baa->attr = ref->attr; + + bgp_advertise_attr_fifo_init(&baa->fifo); + return baa; } @@ -83,36 +88,13 @@ void bgp_advertise_free(struct bgp_advertise *adv) void bgp_advertise_add(struct bgp_advertise_attr *baa, struct bgp_advertise *adv) { - struct bgp_advertise *spot, *prev = NULL; - - spot = baa->adv; - - while (spot) { - prev = spot; - spot = spot->next; - } - - if (prev) { - prev->next = adv; - adv->prev = prev; - } else - adv->prev = NULL; - - adv->next = NULL; - - if (!baa->adv) - baa->adv = adv; + bgp_advertise_attr_fifo_add_tail(&baa->fifo, adv); } void bgp_advertise_delete(struct bgp_advertise_attr *baa, struct bgp_advertise *adv) { - if (adv->next) - adv->next->prev = adv->prev; - if (adv->prev) - adv->prev->next = adv->next; - else - baa->adv = adv->next; + bgp_advertise_attr_fifo_del(&baa->fifo, adv); } struct bgp_advertise_attr *bgp_advertise_attr_intern(struct hash *hash, @@ -187,7 +169,7 @@ void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer, struct attr *attr, for (adj = dest->adj_in; adj; adj = adj->next) { if (adj->peer == peer && adj->addpath_rx_id == addpath_id) { - if (adj->attr != attr) { + if (!attrhash_cmp(adj->attr, attr)) { bgp_attr_unintern(&adj->attr); adj->attr = bgp_attr_intern(attr); } diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 94168e2..4982106 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -11,26 +11,19 @@ PREDECL_DLIST(bgp_adv_fifo); struct update_subgroup; +struct bgp_advertise; -/* BGP advertise attribute. */ -struct bgp_advertise_attr { - /* Head of advertisement pointer. */ - struct bgp_advertise *adv; - - /* Reference counter. */ - unsigned long refcnt; +PREDECL_DLIST(bgp_advertise_attr_fifo); - /* Attribute pointer to be announced. */ - struct attr *attr; -}; +struct bgp_advertise_attr; +/* BGP advertise attribute. */ struct bgp_advertise { /* FIFO for advertisement. */ struct bgp_adv_fifo_item fifo; - /* Link list for same attribute advertise. */ - struct bgp_advertise *next; - struct bgp_advertise *prev; + /* FIFO for this item in the bgp_advertise_attr fifo */ + struct bgp_advertise_attr_fifo_item item; /* Prefix information. */ struct bgp_dest *dest; @@ -45,8 +38,21 @@ struct bgp_advertise { struct bgp_path_info *pathi; }; +DECLARE_DLIST(bgp_advertise_attr_fifo, struct bgp_advertise, item); DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo); +/* BGP advertise attribute. */ +struct bgp_advertise_attr { + /* Head of advertisement pointer. */ + struct bgp_advertise_attr_fifo_head fifo; + + /* Reference counter. */ + unsigned long refcnt; + + /* Attribute pointer to be announced. */ + struct attr *attr; +}; + /* BGP adjacency out. */ struct bgp_adj_out { /* RB Tree of adjacency entries */ @@ -63,14 +69,14 @@ struct bgp_adj_out { uint32_t addpath_tx_id; + /* Attribute hash */ + uint32_t attr_hash; + /* Advertised attribute. */ struct attr *attr; /* Advertisement information. */ struct bgp_advertise *adv; - - /* Attribute hash */ - uint32_t attr_hash; }; RB_HEAD(bgp_adj_out_rb, bgp_adj_out); diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 0e70de9..bc7e893 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -1407,7 +1407,8 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn) last_new_seg = new_seg; seg = seg->next; } - + if (!aspath->refcnt) + aspath_free(aspath); aspath_str_update(new, false); return new; } @@ -1892,7 +1893,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls across 2/4 ASN boundary somewhere, broken.."); hops = seg->length; } - /* fallthru */ + fallthrough; case AS_SEQUENCE: cpasns = MIN(seg->length, hops); hops -= seg->length; diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index ebfc7d0..2a831c3 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -65,6 +65,14 @@ struct aspath { #define ASPATH_STR_DEFAULT_LEN 32 +/* `set as-path exclude ASn' */ +struct aspath_exclude { + struct aspath *aspath; + bool exclude_all; + char *exclude_aspath_acl_name; + struct as_list *exclude_aspath_acl; +}; + /* Prototypes. */ extern void aspath_init(void); extern void aspath_finish(void); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 56e77eb..53420b4 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -864,6 +864,7 @@ bool attrhash_cmp(const void *p1, const void *p2) attr1->df_alg == attr2->df_alg && attr1->nh_ifindex == attr2->nh_ifindex && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex && + attr1->nh_flags == attr2->nh_flags && attr1->distance == attr2->distance && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) && @@ -1381,6 +1382,15 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, (args->startp - STREAM_DATA(BGP_INPUT(peer))) + args->total); + /* Partial optional attributes that are malformed should not cause + * the whole session to be reset. Instead treat it as a withdrawal + * of the routes, if possible. + */ + if (CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS) && + CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL) && + CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL)) + return BGP_ATTR_PARSE_WITHDRAW; + switch (args->type) { /* where an attribute is relatively inconsequential, e.g. it does not * affect route selection, and can be safely ignored, then any such @@ -1390,6 +1400,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, case BGP_ATTR_AS4_AGGREGATOR: case BGP_ATTR_AGGREGATOR: case BGP_ATTR_ATOMIC_AGGREGATE: + case BGP_ATTR_PREFIX_SID: return BGP_ATTR_PARSE_PROCEED; /* Core attributes, particularly ones which may influence route @@ -1417,19 +1428,21 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, BGP_NOTIFY_UPDATE_ERR, subcode, notify_datap, length); return BGP_ATTR_PARSE_ERROR; + default: + /* Unknown attributes, that are handled by this function + * should be treated as withdraw, to prevent one more CVE + * from being introduced. + * RFC 7606 says: + * The "treat-as-withdraw" approach is generally preferred + * and the "session reset" approach is discouraged. + */ + flog_err(EC_BGP_ATTR_FLAG, + "%s(%u) attribute received, while it is not known how to handle it, treating as withdraw", + lookup_msg(attr_str, args->type, NULL), args->type); + break; } - /* Partial optional attributes that are malformed should not cause - * the whole session to be reset. Instead treat it as a withdrawal - * of the routes, if possible. - */ - if (CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS) - && CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL) - && CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL)) - return BGP_ATTR_PARSE_WITHDRAW; - - /* default to reset */ - return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; + return BGP_ATTR_PARSE_WITHDRAW; } /* Find out what is wrong with the path attribute flag bits and log the error. @@ -1839,7 +1852,9 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) * UPDATE message SHALL be handled using the approach of "treat-as- * withdraw". */ - if (peer->sort == BGP_PEER_IBGP && length != 4) { + if ((peer->sort == BGP_PEER_IBGP || + peer->sub_sort == BGP_PEER_EBGP_OAD) && + length != 4) { flog_err(EC_BGP_ATTR_LEN, "LOCAL_PREF attribute length isn't 4 [%u]", length); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -1849,7 +1864,7 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) /* If it is contained in an UPDATE message that is received from an external peer, then this attribute MUST be ignored by the receiving speaker. */ - if (peer->sort == BGP_PEER_EBGP) { + if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD) { STREAM_FORWARD_GETP(peer->curr, length); return BGP_ATTR_PARSE_PROCEED; } @@ -2321,11 +2336,8 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, /* * NOTE: intentional fall through * - for consistency in rx processing - * - * The following comment is to signal GCC this intention - * and suppress the warning */ - /* FALLTHRU */ + fallthrough; case BGP_ATTR_NHLEN_IPV4: stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN); /* Probably needed for RFC 2283 */ @@ -3144,8 +3156,6 @@ enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; enum bgp_attr_parse_ret ret; - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); - uint8_t type; uint16_t length; size_t headersz = sizeof(type) + sizeof(length); @@ -3195,6 +3205,8 @@ enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args) } } + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)); + return BGP_ATTR_PARSE_PROCEED; } @@ -3267,7 +3279,8 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) * the default value of AIGP_SESSION SHOULD be "enabled". */ if (peer->sort == BGP_PEER_EBGP && - !CHECK_FLAG(peer->flags, PEER_FLAG_AIGP)) { + (!CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) || + peer->sub_sort != BGP_PEER_EBGP_OAD)) { zlog_warn( "%pBP received AIGP attribute, but eBGP peer do not support it", peer); @@ -4492,7 +4505,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, } /* Local preference. */ - if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) { + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || + peer->sub_sort == BGP_PEER_EBGP_OAD) { stream_putc(s, BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_LOCAL_PREF); stream_putc(s, 4); @@ -4858,6 +4872,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, /* AIGP */ if (bpi && attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP) && (CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) || + peer->sub_sort == BGP_PEER_EBGP_OAD || peer->sort != BGP_PEER_EBGP)) { /* At the moment only AIGP Metric TLV exists for AIGP * attribute. If more comes in, do not forget to update diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index d30155e..d78f04c 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -155,10 +155,56 @@ struct attr { uint32_t med; uint32_t local_pref; ifindex_t nh_ifindex; + uint8_t nh_flags; + +#define BGP_ATTR_NH_VALID 0x01 /* Path origin attribute */ uint8_t origin; + /* ES info */ + uint8_t es_flags; + /* Path is not "locally-active" on the advertising VTEP. This is + * translated into an ARP-ND ECOM. + */ +#define ATTR_ES_PROXY_ADVERT (1 << 0) + /* Destination ES is present locally. This flag is set on local + * paths and sync paths + */ +#define ATTR_ES_IS_LOCAL (1 << 1) + /* There are one or more non-best paths from ES peers. Note that + * this flag is only set on the local MAC-IP paths in the VNI + * route table (not set in the global routing table). And only + * non-proxy advertisements from an ES peer can result in this + * flag being set. + */ +#define ATTR_ES_PEER_ACTIVE (1 << 2) + /* There are one or more non-best proxy paths from ES peers */ +#define ATTR_ES_PEER_PROXY (1 << 3) + /* An ES peer has router bit set - only applicable if + * ATTR_ES_PEER_ACTIVE is set + */ +#define ATTR_ES_PEER_ROUTER (1 << 4) + + /* These two flags are only set on L3 routes installed in a + * VRF as a result of EVPN MAC-IP route + * XXX - while splitting up per-family attrs these need to be + * classified as non-EVPN + */ +#define ATTR_ES_L3_NHG_USE (1 << 5) +#define ATTR_ES_L3_NHG_ACTIVE (1 << 6) +#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE) + + /* NA router flag (R-bit) support in EVPN */ + uint8_t router_flag; + + /* Distance as applied by Route map */ + uint8_t distance; + + /* EVPN DF preference for DF election on local ESs */ + uint8_t df_alg; + uint16_t df_pref; + /* PMSI tunnel type (RFC 6514). */ enum pta_type pmsi_tnl_type; @@ -173,6 +219,9 @@ struct attr { /* ifIndex corresponding to mp_nexthop_local. */ ifindex_t nh_lla_ifindex; + /* MPLS label */ + mpls_label_t label; + /* Extended Communities attribute. */ struct ecommunity *ecommunity; @@ -214,58 +263,18 @@ struct attr { /* Flag for default gateway extended community in EVPN */ uint8_t default_gw; - /* NA router flag (R-bit) support in EVPN */ - uint8_t router_flag; - - /* ES info */ - uint8_t es_flags; - /* Path is not "locally-active" on the advertising VTEP. This is - * translated into an ARP-ND ECOM. - */ -#define ATTR_ES_PROXY_ADVERT (1 << 0) - /* Destination ES is present locally. This flag is set on local - * paths and sync paths - */ -#define ATTR_ES_IS_LOCAL (1 << 1) - /* There are one or more non-best paths from ES peers. Note that - * this flag is only set on the local MAC-IP paths in the VNI - * route table (not set in the global routing table). And only - * non-proxy advertisements from an ES peer can result in this - * flag being set. - */ -#define ATTR_ES_PEER_ACTIVE (1 << 2) - /* There are one or more non-best proxy paths from ES peers */ -#define ATTR_ES_PEER_PROXY (1 << 3) - /* An ES peer has router bit set - only applicable if - * ATTR_ES_PEER_ACTIVE is set - */ -#define ATTR_ES_PEER_ROUTER (1 << 4) - - /* These two flags are only set on L3 routes installed in a - * VRF as a result of EVPN MAC-IP route - * XXX - while splitting up per-family attrs these need to be - * classified as non-EVPN - */ -#define ATTR_ES_L3_NHG_USE (1 << 5) -#define ATTR_ES_L3_NHG_ACTIVE (1 << 6) -#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE) - /* route tag */ route_tag_t tag; /* Label index */ uint32_t label_index; - /* MPLS label */ - mpls_label_t label; - /* SRv6 VPN SID */ struct bgp_attr_srv6_vpn *srv6_vpn; /* SRv6 L3VPN SID */ struct bgp_attr_srv6_l3vpn *srv6_l3vpn; - uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ #ifdef ENABLE_BGP_VNC @@ -287,8 +296,7 @@ struct attr { /* EVPN local router-mac */ struct ethaddr rmac; - /* Distance as applied by Route map */ - uint8_t distance; + uint16_t encap_tunneltype; /* rmap set table */ uint32_t rmap_table_id; @@ -302,10 +310,6 @@ struct attr { /* SR-TE Color */ uint32_t srte_color; - /* EVPN DF preference and algorithm for DF election on local ESs */ - uint16_t df_pref; - uint8_t df_alg; - /* Nexthop type */ enum nexthop_types_t nh_type; diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 7270802..1de48a0 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -240,60 +240,115 @@ static void bmp_free(struct bmp *bmp) XFREE(MTYPE_BMP_CONN, bmp); } +#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0 +#define BMP_PEER_TYPE_RD_INSTANCE 1 +#define BMP_PEER_TYPE_LOCAL_INSTANCE 2 +#define BMP_PEER_TYPE_LOC_RIB_INSTANCE 3 + +static inline int bmp_get_peer_distinguisher(struct bmp *bmp, afi_t afi, + uint8_t peer_type, + uint64_t *result_ref) +{ + + /* remove this check when the other peer types get correct peer dist. + *(RFC7854) impl. + * for now, always return no error and 0 peer distinguisher as before + */ + if (peer_type != BMP_PEER_TYPE_LOC_RIB_INSTANCE) + return (*result_ref = 0); + + /* sending vrf_id or rd could be turned into an option at some point */ + struct bgp *bgp = bmp->targets->bgp; + + /* vrf default => ok, distinguisher 0 */ + if (bgp->inst_type == VRF_DEFAULT) + return (*result_ref = 0); + + /* use RD if set in VRF config for this AFI */ + struct prefix_rd *prd = &bgp->vpn_policy[afi].tovpn_rd; + + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) { + memcpy(result_ref, prd->val, sizeof(prd->val)); + return 0; + } + + /* VRF has no id => error => message should be skipped */ + if (bgp->vrf_id == VRF_UNKNOWN) + return 1; + + /* use VRF id converted to ::vrf_id 64bits format */ + *result_ref = ((uint64_t)htonl(bgp->vrf_id)) << 32; + return 0; +} + static void bmp_common_hdr(struct stream *s, uint8_t ver, uint8_t type) { stream_putc(s, ver); - stream_putl(s, 0); //dummy message length. will be set later. + stream_putl(s, 0); /* dummy message length. will be set later. */ stream_putc(s, type); } -static void bmp_per_peer_hdr(struct stream *s, struct peer *peer, - uint8_t flags, const struct timeval *tv) +static void bmp_per_peer_hdr(struct stream *s, struct bgp *bgp, + struct peer *peer, uint8_t flags, + uint8_t peer_type_flag, + uint64_t peer_distinguisher, + const struct timeval *tv) { - char peer_distinguisher[8]; - -#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0 -#define BMP_PEER_TYPE_RD_INSTANCE 1 -#define BMP_PEER_TYPE_LOCAL_INSTANCE 2 - #define BMP_PEER_FLAG_V (1 << 7) #define BMP_PEER_FLAG_L (1 << 6) #define BMP_PEER_FLAG_A (1 << 5) + bool is_locrib = peer_type_flag == BMP_PEER_TYPE_LOC_RIB_INSTANCE; + /* Peer Type */ - stream_putc(s, BMP_PEER_TYPE_GLOBAL_INSTANCE); + stream_putc(s, peer_type_flag); /* Peer Flags */ - if (peer->connection->su.sa.sa_family == AF_INET6) + if (!is_locrib && peer->connection->su.sa.sa_family == AF_INET6) SET_FLAG(flags, BMP_PEER_FLAG_V); else UNSET_FLAG(flags, BMP_PEER_FLAG_V); stream_putc(s, flags); /* Peer Distinguisher */ - memset (&peer_distinguisher[0], 0, 8); - stream_put(s, &peer_distinguisher[0], 8); + stream_put(s, (uint8_t *)&peer_distinguisher, 8); /* Peer Address */ - if (peer->connection->su.sa.sa_family == AF_INET6) - stream_put(s, &peer->connection->su.sin6.sin6_addr, 16); - else if (peer->connection->su.sa.sa_family == AF_INET) { + /* Set to 0 if it's a LOC-RIB INSTANCE (RFC 9069) or if it's not an + * IPv4/6 address + */ + if (is_locrib || (peer->connection->su.sa.sa_family != AF_INET6 && + peer->connection->su.sa.sa_family != AF_INET)) { stream_putl(s, 0); stream_putl(s, 0); stream_putl(s, 0); - stream_put_in_addr(s, &peer->connection->su.sin.sin_addr); - } else { stream_putl(s, 0); + } else if (peer->connection->su.sa.sa_family == AF_INET6) + stream_put(s, &peer->connection->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); + else if (peer->connection->su.sa.sa_family == AF_INET) { stream_putl(s, 0); stream_putl(s, 0); stream_putl(s, 0); + stream_put_in_addr(s, &peer->connection->su.sin.sin_addr); } /* Peer AS */ - stream_putl(s, peer->as); + /* set peer ASN but for LOC-RIB INSTANCE (RFC 9069) put the local bgp + * ASN + */ + as_t asn = !is_locrib ? peer->as : bgp->as; + + stream_putl(s, asn); /* Peer BGP ID */ - stream_put_in_addr(s, &peer->remote_id); + /* set router-id but for LOC-RIB INSTANCE (RFC 9069) put the instance + * router-id + */ + struct in_addr *bgp_id = + !is_locrib ? &peer->remote_id : &bgp->router_id; + + stream_put_in_addr(s, bgp_id); /* Timestamp */ if (tv) { @@ -314,11 +369,26 @@ static void bmp_put_info_tlv(struct stream *s, uint16_t type, stream_put(s, string, len); } +static void __attribute__((unused)) +bmp_put_vrftablename_info_tlv(struct stream *s, struct bmp *bmp) +{ + +#define BMP_INFO_TYPE_VRFTABLENAME 3 + const char *vrftablename = "global"; + if (bmp->targets->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + struct vrf *vrf = vrf_lookup_by_id(bmp->targets->bgp->vrf_id); + + vrftablename = vrf ? vrf->name : NULL; + } + if (vrftablename != NULL) + bmp_put_info_tlv(s, BMP_INFO_TYPE_VRFTABLENAME, vrftablename); +} + static int bmp_send_initiation(struct bmp *bmp) { int len; - struct stream *s; - s = stream_new(BGP_MAX_PACKET_SIZE); + struct stream *s = stream_new(BGP_MAX_PACKET_SIZE); + bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION); #define BMP_INFO_TYPE_SYSDESCR 1 @@ -328,7 +398,7 @@ static int bmp_send_initiation(struct bmp *bmp) bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get()); len = stream_get_endp(s); - stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. + stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */ pullwr_write_stream(bmp->pullwr, s); stream_free(s); @@ -375,7 +445,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_PEER_UP_NOTIFICATION); - bmp_per_peer_hdr(s, peer, 0, &uptime_real); + bmp_per_peer_hdr(s, peer->bgp, peer, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, + &uptime_real); /* Local Address (16 bytes) */ if (peer->su_local->sa.sa_family == AF_INET6) @@ -428,7 +500,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_PEER_DOWN_NOTIFICATION); - bmp_per_peer_hdr(s, peer, 0, &uptime_real); + bmp_per_peer_hdr(s, peer->bgp, peer, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, + &uptime_real); type_pos = stream_get_endp(s); stream_putc(s, 0); /* placeholder for down reason */ @@ -460,7 +534,7 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) } len = stream_get_endp(s); - stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. + stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */ return s; } @@ -618,7 +692,8 @@ static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr) s = stream_new(BGP_MAX_PACKET_SIZE); bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); - bmp_per_peer_hdr(s, bmp->targets->bgp->peer_self, 0, &tv); + bmp_per_peer_hdr(s, bmp->targets->bgp, bmp->targets->bgp->peer_self, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &tv); stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO); stream_putw(s, 2); @@ -656,7 +731,8 @@ static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr) s = stream_new(BGP_MAX_PACKET_SIZE); bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); - bmp_per_peer_hdr(s, peer, 0, &bmq->tv); + bmp_per_peer_hdr(s, bmp->targets->bgp, peer, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &bmq->tv); /* BMP Mirror TLV. */ stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE); @@ -763,7 +839,8 @@ static int bmp_peer_backward(struct peer *peer) return 0; } -static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) +static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags, + uint8_t peer_type_flag) { struct peer *peer; struct listnode *node; @@ -771,7 +848,7 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) iana_afi_t pkt_afi = IANA_AFI_IPV4; iana_safi_t pkt_safi = IANA_SAFI_UNICAST; - frrtrace(3, frr_bgp, bmp_eor, afi, safi, flags); + frrtrace(4, frr_bgp, bmp_eor, afi, safi, flags, peer_type_flag); s = stream_new(BGP_MAX_PACKET_SIZE); @@ -803,11 +880,22 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) if (!peer->afc_nego[afi][safi]) continue; + uint64_t peer_distinguisher = 0; + /* skip this message if peer distinguisher is not available */ + if (bmp_get_peer_distinguisher(bmp, afi, peer_type_flag, + &peer_distinguisher)) { + zlog_warn( + "skipping bmp message for reason: can't get peer distinguisher"); + continue; + } + s2 = stream_new(BGP_MAX_PACKET_SIZE); bmp_common_hdr(s2, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING); - bmp_per_peer_hdr(s2, peer, flags, NULL); + + bmp_per_peer_hdr(s2, bmp->targets->bgp, peer, flags, + peer_type_flag, peer_distinguisher, NULL); stream_putl_at(s2, BMP_LENGTH_POS, stream_get_endp(s) + stream_get_endp(s2)); @@ -912,14 +1000,23 @@ static struct stream *bmp_withdraw(const struct prefix *p, } static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, - const struct prefix *p, struct prefix_rd *prd, - struct attr *attr, afi_t afi, safi_t safi, - time_t uptime) + uint8_t peer_type_flag, const struct prefix *p, + struct prefix_rd *prd, struct attr *attr, afi_t afi, + safi_t safi, time_t uptime) { struct stream *hdr, *msg; struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 }; struct timeval uptime_real; + uint64_t peer_distinguisher = 0; + /* skip this message if peer distinguisher is not available */ + if (bmp_get_peer_distinguisher(bmp, afi, peer_type_flag, + &peer_distinguisher)) { + zlog_warn( + "skipping bmp message for reason: can't get peer distinguisher"); + return; + } + monotime_to_realtime(&tv, &uptime_real); if (attr) msg = bmp_update(p, prd, peer, attr, afi, safi); @@ -928,7 +1025,9 @@ static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, hdr = stream_new(BGP_MAX_PACKET_SIZE); bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING); - bmp_per_peer_hdr(hdr, peer, flags, &uptime_real); + bmp_per_peer_hdr(hdr, bmp->targets->bgp, peer, flags, peer_type_flag, + peer_distinguisher, + uptime == (time_t)(-1L) ? NULL : &uptime_real); stream_putl_at(hdr, BMP_LENGTH_POS, stream_get_endp(hdr) + stream_get_endp(msg)); @@ -1039,8 +1138,13 @@ afibreak: zlog_info("bmp[%s] %s %s table completed (EoR)", bmp->remote, afi2str(afi), safi2str(safi)); - bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L); - bmp_eor(bmp, afi, safi, 0); + + bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L, + BMP_PEER_TYPE_GLOBAL_INSTANCE); + bmp_eor(bmp, afi, safi, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE); + bmp_eor(bmp, afi, safi, 0, + BMP_PEER_TYPE_LOC_RIB_INSTANCE); bmp->afistate[afi][safi] = BMP_AFI_LIVE; bmp->syncafi = AFI_MAX; @@ -1051,10 +1155,16 @@ afibreak: prefix_copy(&bmp->syncpos, bgp_dest_get_prefix(bn)); } - if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { + if (CHECK_FLAG(bmp->targets->afimon[afi][safi], + BMP_MON_POSTPOLICY) || + CHECK_FLAG(bmp->targets->afimon[afi][safi], + BMP_MON_LOC_RIB)) { for (bpiter = bgp_dest_get_bgp_path_info(bn); bpiter; bpiter = bpiter->next) { - if (!CHECK_FLAG(bpiter->flags, BGP_PATH_VALID)) + if (!CHECK_FLAG(bpiter->flags, + BGP_PATH_VALID) && + !CHECK_FLAG(bpiter->flags, + BGP_PATH_SELECTED)) continue; if (bpiter->peer->qobj_node.nid <= bmp->syncpeerid) @@ -1065,7 +1175,8 @@ afibreak: bpi = bpiter; } } - if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { + if (CHECK_FLAG(bmp->targets->afimon[afi][safi], + BMP_MON_PREPOLICY)) { for (adjiter = bn->adj_in; adjiter; adjiter = adjiter->next) { if (adjiter->peer->qobj_node.nid @@ -1103,12 +1214,23 @@ afibreak: (safi == SAFI_MPLS_VPN)) prd = (struct prefix_rd *)bgp_dest_get_prefix(bmp->syncrdpos); - if (bpi) - bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bn_p, prd, - bpi->attr, afi, safi, bpi->uptime); + if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_SELECTED) && + CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_LOC_RIB)) { + bmp_monitor(bmp, bpi->peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, + bn_p, prd, bpi->attr, afi, safi, + bpi && bpi->extra ? bpi->extra->bgp_rib_uptime + : (time_t)(-1L)); + } + + if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_VALID) && + CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY)) + bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, + BMP_PEER_TYPE_GLOBAL_INSTANCE, bn_p, prd, bpi->attr, + afi, safi, bpi->uptime); + if (adjin) - bmp_monitor(bmp, adjin->peer, 0, bn_p, prd, adjin->attr, afi, - safi, adjin->uptime); + bmp_monitor(bmp, adjin->peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE, + bn_p, prd, adjin->attr, afi, safi, adjin->uptime); if (bn) bgp_dest_unlock_node(bn); @@ -1116,24 +1238,124 @@ afibreak: return true; } -static struct bmp_queue_entry *bmp_pull(struct bmp *bmp) +static struct bmp_queue_entry * +bmp_pull_from_queue(struct bmp_qlist_head *list, struct bmp_qhash_head *hash, + struct bmp_queue_entry **queuepos_ptr) { struct bmp_queue_entry *bqe; - bqe = bmp->queuepos; + bqe = *queuepos_ptr; if (!bqe) return NULL; - bmp->queuepos = bmp_qlist_next(&bmp->targets->updlist, bqe); + *queuepos_ptr = bmp_qlist_next(list, bqe); bqe->refcount--; if (!bqe->refcount) { - bmp_qhash_del(&bmp->targets->updhash, bqe); - bmp_qlist_del(&bmp->targets->updlist, bqe); + bmp_qhash_del(hash, bqe); + bmp_qlist_del(list, bqe); } return bqe; } +static inline struct bmp_queue_entry *bmp_pull(struct bmp *bmp) +{ + return bmp_pull_from_queue(&bmp->targets->updlist, + &bmp->targets->updhash, &bmp->queuepos); +} + +static inline struct bmp_queue_entry *bmp_pull_locrib(struct bmp *bmp) +{ + return bmp_pull_from_queue(&bmp->targets->locupdlist, + &bmp->targets->locupdhash, + &bmp->locrib_queuepos); +} + +/* TODO BMP_MON_LOCRIB find a way to merge properly this function with + * bmp_wrqueue or abstract it if possible + */ +static bool bmp_wrqueue_locrib(struct bmp *bmp, struct pullwr *pullwr) +{ + + struct bmp_queue_entry *bqe; + struct peer *peer; + struct bgp_dest *bn = NULL; + bool written = false; + + bqe = bmp_pull_locrib(bmp); + if (!bqe) + return false; + + afi_t afi = bqe->afi; + safi_t safi = bqe->safi; + + if (!CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_LOC_RIB)) + goto out; + + switch (bmp->afistate[afi][safi]) { + case BMP_AFI_INACTIVE: + case BMP_AFI_NEEDSYNC: + goto out; + case BMP_AFI_SYNC: + if (prefix_cmp(&bqe->p, &bmp->syncpos) <= 0) + /* currently syncing but have already passed this + * prefix => send it. + */ + break; + + /* currently syncing & haven't reached this prefix yet + * => it'll be sent as part of the table sync, no need here + */ + goto out; + case BMP_AFI_LIVE: + break; + } + + peer = QOBJ_GET_TYPESAFE(bqe->peerid, peer); + if (!peer) { + /* skipping queued item for deleted peer + */ + goto out; + } + if (peer != bmp->targets->bgp->peer_self && !peer_established(peer->connection)) { + /* peer is neither self, nor established + */ + goto out; + } + + bool is_vpn = (bqe->afi == AFI_L2VPN && bqe->safi == SAFI_EVPN) || + (bqe->safi == SAFI_MPLS_VPN); + + struct prefix_rd *prd = is_vpn ? &bqe->rd : NULL; + + bn = bgp_safi_node_lookup(bmp->targets->bgp->rib[afi][safi], safi, + &bqe->p, prd); + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + if (!CHECK_FLAG(bpi->flags, BGP_PATH_SELECTED)) + continue; + if (bpi->peer == peer) + break; + } + + bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, &bqe->p, prd, + bpi ? bpi->attr : NULL, afi, safi, + bpi && bpi->extra ? bpi->extra->bgp_rib_uptime + : (time_t)(-1L)); + written = true; + +out: + if (!bqe->refcount) + XFREE(MTYPE_BMP_QUEUE, bqe); + + if (bn) + bgp_dest_unlock_node(bn); + + return written; +} + static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) { struct bmp_queue_entry *bqe; @@ -1180,11 +1402,10 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) bn = bgp_safi_node_lookup(bmp->targets->bgp->rib[afi][safi], safi, &bqe->p, prd); - - if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { + if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY)) { struct bgp_path_info *bpi; - for (bpi = bn ? bgp_dest_get_bgp_path_info(bn) : NULL; bpi; + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (!CHECK_FLAG(bpi->flags, BGP_PATH_VALID)) continue; @@ -1192,13 +1413,14 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) break; } - bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd, + bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, + BMP_PEER_TYPE_GLOBAL_INSTANCE, &bqe->p, prd, bpi ? bpi->attr : NULL, afi, safi, bpi ? bpi->uptime : monotime(NULL)); written = true; } - if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { + if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_PREPOLICY)) { struct bgp_adj_in *adjin; for (adjin = bn ? bn->adj_in : NULL; adjin; @@ -1206,8 +1428,8 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) if (adjin->peer == peer) break; } - bmp_monitor(bmp, peer, 0, &bqe->p, prd, - adjin ? adjin->attr : NULL, afi, safi, + bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE, + &bqe->p, prd, adjin ? adjin->attr : NULL, afi, safi, adjin ? adjin->uptime : monotime(NULL)); written = true; } @@ -1235,6 +1457,8 @@ static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr) break; if (bmp_wrqueue(bmp, pullwr)) break; + if (bmp_wrqueue_locrib(bmp, pullwr)) + break; if (bmp_wrsync(bmp, pullwr)) break; break; @@ -1253,16 +1477,17 @@ static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof) bmp_free(bmp); } -static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi, - safi_t safi, struct bgp_dest *bn, struct peer *peer) +static struct bmp_queue_entry * +bmp_process_one(struct bmp_targets *bt, struct bmp_qhash_head *updhash, + struct bmp_qlist_head *updlist, struct bgp *bgp, afi_t afi, + safi_t safi, struct bgp_dest *bn, struct peer *peer) { - struct bmp *bmp; struct bmp_queue_entry *bqe, bqeref; size_t refcount; refcount = bmp_session_count(&bt->sessions); if (refcount == 0) - return; + return NULL; memset(&bqeref, 0, sizeof(bqeref)); prefix_copy(&bqeref.p, bgp_dest_get_prefix(bn)); @@ -1275,26 +1500,28 @@ static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi, prefix_copy(&bqeref.rd, (struct prefix_rd *)bgp_dest_get_prefix(bn->pdest)); - bqe = bmp_qhash_find(&bt->updhash, &bqeref); + bqe = bmp_qhash_find(updhash, &bqeref); if (bqe) { if (bqe->refcount >= refcount) /* nothing to do here */ - return; + return NULL; - bmp_qlist_del(&bt->updlist, bqe); + bmp_qlist_del(updlist, bqe); } else { bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe)); memcpy(bqe, &bqeref, sizeof(*bqe)); - bmp_qhash_add(&bt->updhash, bqe); + bmp_qhash_add(updhash, bqe); } bqe->refcount = refcount; - bmp_qlist_add_tail(&bt->updlist, bqe); + bmp_qlist_add_tail(updlist, bqe); + + return bqe; - frr_each (bmp_session, &bt->sessions, bmp) - if (!bmp->queuepos) - bmp->queuepos = bqe; + /* need to update correct queue pos for all sessions of the target after + * a call to this function + */ } static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi, @@ -1316,12 +1543,26 @@ static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi, return 0; frr_each(bmp_targets, &bmpbgp->targets, bt) { - if (!bt->afimon[afi][safi]) + /* check if any monitoring is enabled (ignoring loc-rib since it + * uses another hook & queue + */ + if (!CHECK_FLAG(bt->afimon[afi][safi], ~BMP_MON_LOC_RIB)) continue; - bmp_process_one(bt, bgp, afi, safi, bn, peer); + struct bmp_queue_entry *last_item = + bmp_process_one(bt, &bt->updhash, &bt->updlist, bgp, + afi, safi, bn, peer); + + /* if bmp_process_one returns NULL + * we don't have anything to do next + */ + if (!last_item) + continue; frr_each(bmp_session, &bt->sessions, bmp) { + if (!bmp->queuepos) + bmp->queuepos = last_item; + pullwr_bump(bmp->pullwr); } } @@ -1360,7 +1601,8 @@ static void bmp_stats(struct event *thread) s = stream_new(BGP_MAX_PACKET_SIZE); bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT); - bmp_per_peer_hdr(s, peer, 0, &tv); + bmp_per_peer_hdr(s, bt->bgp, peer, 0, + BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &tv); count_pos = stream_get_endp(s); stream_putl(s, 0); @@ -1530,6 +1772,9 @@ static void bmp_close(struct bmp *bmp) while ((bqe = bmp_pull(bmp))) if (!bqe->refcount) XFREE(MTYPE_BMP_QUEUE, bqe); + while ((bqe = bmp_pull_locrib(bmp))) + if (!bqe->refcount) + XFREE(MTYPE_BMP_QUEUE, bqe); EVENT_OFF(bmp->t_read); pullwr_del(bmp->pullwr); @@ -1633,6 +1878,8 @@ static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name) bmp_session_init(&bt->sessions); bmp_qhash_init(&bt->updhash); bmp_qlist_init(&bt->updlist); + bmp_qhash_init(&bt->locupdhash); + bmp_qlist_init(&bt->locupdlist); bmp_actives_init(&bt->actives); bmp_listeners_init(&bt->listeners); @@ -1663,6 +1910,8 @@ static void bmp_targets_put(struct bmp_targets *bt) bmp_actives_fini(&bt->actives); bmp_qhash_fini(&bt->updhash); bmp_qlist_fini(&bt->updlist); + bmp_qhash_fini(&bt->locupdhash); + bmp_qlist_fini(&bt->locupdlist); XFREE(MTYPE_BMP_ACLNAME, bt->acl_name); XFREE(MTYPE_BMP_ACLNAME, bt->acl6_name); @@ -2206,21 +2455,17 @@ DEFPY(bmp_stats_cfg, return CMD_SUCCESS; } -DEFPY(bmp_monitor_cfg, - bmp_monitor_cmd, - "[no] bmp monitor $policy", - NO_STR - BMP_STR - "Send BMP route monitoring messages\n" - BGP_AF_STR - BGP_AF_STR - BGP_AF_STR - BGP_AF_STR - BGP_AF_STR - BGP_AF_STR - BGP_AF_STR +#define BMP_POLICY_IS_LOCRIB(str) ((str)[0] == 'l') /* __l__oc-rib */ +#define BMP_POLICY_IS_PRE(str) ((str)[1] == 'r') /* p__r__e-policy */ + +DEFPY(bmp_monitor_cfg, bmp_monitor_cmd, + "[no] bmp monitor $policy", + NO_STR BMP_STR + "Send BMP route monitoring messages\n" BGP_AF_STR BGP_AF_STR BGP_AF_STR + BGP_AF_STR BGP_AF_STR BGP_AF_STR BGP_AF_STR "Send state before policy and filter processing\n" - "Send state with policy and filters applied\n") + "Send state with policy and filters applied\n" + "Send state after decision process is applied\n") { int index = 0; uint8_t flag, prev; @@ -2233,7 +2478,9 @@ DEFPY(bmp_monitor_cfg, argv_find_and_parse_afi(argv, argc, &index, &afi); argv_find_and_parse_safi(argv, argc, &index, &safi); - if (policy[1] == 'r') + if (BMP_POLICY_IS_LOCRIB(policy)) + flag = BMP_MON_LOC_RIB; + else if (BMP_POLICY_IS_PRE(policy)) flag = BMP_MON_PREPOLICY; else flag = BMP_MON_POSTPOLICY; @@ -2364,23 +2611,31 @@ DEFPY(show_bmp, safi_t safi; FOREACH_AFI_SAFI (afi, safi) { - const char *str = NULL; - - switch (bt->afimon[afi][safi]) { - case BMP_MON_PREPOLICY: - str = "pre-policy"; - break; - case BMP_MON_POSTPOLICY: - str = "post-policy"; - break; - case BMP_MON_PREPOLICY | BMP_MON_POSTPOLICY: - str = "pre-policy and post-policy"; - break; - } - if (!str) + + uint8_t afimon_flag = bt->afimon[afi][safi]; + + if (!afimon_flag) continue; - vty_out(vty, " Route Monitoring %s %s %s\n", - afi2str(afi), safi2str(safi), str); + + const char *pre_str = + CHECK_FLAG(afimon_flag, + BMP_MON_PREPOLICY) + ? "pre-policy " + : ""; + const char *post_str = + CHECK_FLAG(afimon_flag, + BMP_MON_POSTPOLICY) + ? "post-policy " + : ""; + const char *locrib_str = + CHECK_FLAG(afimon_flag, BMP_MON_LOC_RIB) + ? "loc-rib" + : ""; + + vty_out(vty, + " Route Monitoring %s %s %s%s%s\n", + afi2str(afi), safi2str(safi), pre_str, + post_str, locrib_str); } vty_out(vty, " Listeners:\n"); @@ -2500,13 +2755,18 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty) vty_out(vty, " bmp mirror\n"); FOREACH_AFI_SAFI (afi, safi) { - if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY) + if (CHECK_FLAG(bt->afimon[afi][safi], + BMP_MON_PREPOLICY)) vty_out(vty, " bmp monitor %s %s pre-policy\n", afi2str_lower(afi), safi2str(safi)); - if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY) + if (CHECK_FLAG(bt->afimon[afi][safi], + BMP_MON_POSTPOLICY)) vty_out(vty, " bmp monitor %s %s post-policy\n", afi2str_lower(afi), safi2str(safi)); + if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) + vty_out(vty, " bmp monitor %s %s loc-rib\n", + afi2str_lower(afi), safi2str(safi)); } frr_each (bmp_listeners, &bt->listeners, bl) vty_out(vty, " \n bmp listener %pSU port %d\n", @@ -2555,6 +2815,82 @@ static int bgp_bmp_init(struct event_loop *tm) return 0; } +static int bmp_route_update(struct bgp *bgp, afi_t afi, safi_t safi, + struct bgp_dest *bn, + struct bgp_path_info *old_route, + struct bgp_path_info *new_route) +{ + bool is_locribmon_enabled = false; + bool is_withdraw = old_route && !new_route; + struct bgp_path_info *updated_route = + is_withdraw ? old_route : new_route; + + + /* this should never happen */ + if (!updated_route) { + zlog_warn("%s: no updated route found!", __func__); + return 0; + } + + struct bmp_bgp *bmpbgp = bmp_bgp_get(bgp); + struct peer *peer = updated_route->peer; + struct bmp_targets *bt; + struct bmp *bmp; + + frr_each (bmp_targets, &bmpbgp->targets, bt) { + if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) { + is_locribmon_enabled = true; + break; + } + } + + if (!is_locribmon_enabled) + return 0; + + /* route is not installed in locrib anymore and rib uptime was saved */ + if (old_route && old_route->extra) + bgp_path_info_extra_get(old_route)->bgp_rib_uptime = + (time_t)(-1L); + + /* route is installed in locrib from now on so + * save rib uptime in bgp_path_info_extra + */ + if (new_route) + bgp_path_info_extra_get(new_route)->bgp_rib_uptime = + monotime(NULL); + + frr_each (bmp_targets, &bmpbgp->targets, bt) { + if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) { + + struct bmp_queue_entry *last_item = bmp_process_one( + bt, &bt->locupdhash, &bt->locupdlist, bgp, afi, + safi, bn, peer); + + /* if bmp_process_one returns NULL + * we don't have anything to do next + */ + if (!last_item) + continue; + + frr_each (bmp_session, &bt->sessions, bmp) { + if (!bmp->locrib_queuepos) + bmp->locrib_queuepos = last_item; + + pullwr_bump(bmp->pullwr); + }; + } + }; + + return 0; +} + +static int bgp_bmp_early_fini(void) +{ + resolver_terminate(); + + return 0; +} + static int bgp_bmp_module_init(void) { hook_register(bgp_packet_dump, bmp_mirror_packet); @@ -2565,6 +2901,8 @@ static int bgp_bmp_module_init(void) hook_register(bgp_inst_config_write, bmp_config_write); hook_register(bgp_inst_delete, bmp_bgp_del); hook_register(frr_late_init, bgp_bmp_init); + hook_register(bgp_route_update, bmp_route_update); + hook_register(frr_early_fini, bgp_bmp_early_fini); return 0; } diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h index ab7463f..dadd99e 100644 --- a/bgpd/bgp_bmp.h +++ b/bgpd/bgp_bmp.h @@ -124,6 +124,7 @@ struct bmp { * ahead we need to make sure that refcount is decremented. Also, on * disconnects we need to walk the queue and drop our reference. */ + struct bmp_queue_entry *locrib_queuepos; struct bmp_queue_entry *queuepos; struct bmp_mirrorq *mirrorpos; bool mirror_lost; @@ -215,12 +216,14 @@ struct bmp_targets { int stat_msec; /* only supporting: - * - IPv4 / unicast & multicast - * - IPv6 / unicast & multicast + * - IPv4 / unicast & multicast & VPN + * - IPv6 / unicast & multicast & VPN * - L2VPN / EVPN */ #define BMP_MON_PREPOLICY (1 << 0) #define BMP_MON_POSTPOLICY (1 << 1) +#define BMP_MON_LOC_RIB (1 << 2) + uint8_t afimon[AFI_MAX][SAFI_MAX]; bool mirror; @@ -232,6 +235,9 @@ struct bmp_targets { struct bmp_qhash_head updhash; struct bmp_qlist_head updlist; + struct bmp_qhash_head locupdhash; + struct bmp_qlist_head locupdlist; + uint64_t cnt_accept, cnt_aclrefused; QOBJ_FIELDS; diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c index 8e27d97..1d5034e 100644 --- a/bgpd/bgp_btoa.c +++ b/bgpd/bgp_btoa.c @@ -4,6 +4,7 @@ */ #include +#include #include "zebra.h" #include "stream.h" diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index e522307..8336d6f 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -128,6 +128,7 @@ static void community_entry_free(struct community_entry *entry) XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config); if (entry->reg) bgp_regex_free(entry->reg); + break; default: break; } diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 6ed0dd7..2d96b44 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -122,8 +122,9 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, if (update_type == UPDATE_TYPE_ADVERTISE && subgroup_announce_check(dest, pi, subgrp, dest_p, &attr, &advmap_attr)) { - bgp_adj_out_set_subgroup(dest, subgrp, &attr, - pi); + if (!bgp_adj_out_set_subgroup(dest, subgrp, + &attr, pi)) + bgp_attr_flush(&attr); } else { /* If default originate is enabled for * the peer, do not send explicit diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 5708e6f..6033c34 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -65,7 +65,6 @@ struct bgp_damp_config { /* Non-configurable parameters but fixed at implementation time. * To change this values, init_bgp_damp() should be modified. */ - time_t tmax; /* Max time previous instability retained */ unsigned int reuse_list_size; /* Number of reuse lists */ unsigned int reuse_index_size; /* Size of reuse index array */ @@ -75,8 +74,8 @@ struct bgp_damp_config { unsigned int ceiling; /* Max value a penalty can attain */ unsigned int decay_rate_per_tick; /* Calculated from half-life */ unsigned int decay_array_size; /* Calculated using config parameters */ - double scale_factor; unsigned int reuse_scale_factor; + double scale_factor; /* Decay array per-set based. */ double *decay_array; @@ -87,6 +86,7 @@ struct bgp_damp_config { /* Reuse list array per-set based. */ struct bgp_damp_info **reuse_list; int reuse_offset; + safi_t safi; /* All dampening information which is not on reuse list. */ struct bgp_damp_info *no_reuse_list; @@ -95,7 +95,6 @@ struct bgp_damp_config { struct event *t_reuse; afi_t afi; - safi_t safi; }; #define BGP_DAMP_NONE 0 diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 123a1ca..3ecdc0d 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -16,6 +16,7 @@ #include "memory.h" #include "queue.h" #include "filter.h" +#include "hook.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" @@ -37,6 +38,9 @@ #include "bgpd/bgp_debug_clippy.c" +DEFINE_HOOK(bgp_hook_config_write_debug, (struct vty *vty, bool running), + (vty, running)); + unsigned long conf_bgp_debug_as4; unsigned long conf_bgp_debug_neighbor_events; unsigned long conf_bgp_debug_events; @@ -216,6 +220,7 @@ static void bgp_debug_list_free(struct list *list) listnode_delete(list, filter); prefix_free(&filter->p); XFREE(MTYPE_BGP_DEBUG_STR, filter->host); + XFREE(MTYPE_BGP_DEBUG_STR, filter->plist_name); XFREE(MTYPE_BGP_DEBUG_FILTER, filter); } } @@ -233,15 +238,21 @@ static void bgp_debug_list_print(struct vty *vty, const char *desc, vty_out(vty, "%s", desc); if (list && !list_isempty(list)) { - vty_out(vty, " for"); + vty_out(vty, " for:\n"); for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) { if (filter->host) - vty_out(vty, " %s", filter->host); + vty_out(vty, " %s", filter->host); + + if (filter->plist_name) + vty_out(vty, " with prefix-list %s", + filter->plist_name); if (filter->p && filter->p->family == AF_EVPN) bgp_debug_print_evpn_prefix(vty, "", filter->p); else if (filter->p) vty_out(vty, " %pFX", filter->p); + + vty_out(vty, "\n"); } } @@ -261,7 +272,11 @@ static int bgp_debug_list_conf_print(struct vty *vty, const char *desc, if (list && !list_isempty(list)) { for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) { - if (filter->host) { + if (filter->host && filter->plist_name) { + vty_out(vty, "%s %s prefix-list %s\n", desc, + filter->host, filter->plist_name); + write++; + } else if (filter->host) { vty_out(vty, "%s %s\n", desc, filter->host); write++; } @@ -286,7 +301,8 @@ static int bgp_debug_list_conf_print(struct vty *vty, const char *desc, } static void bgp_debug_list_add_entry(struct list *list, const char *host, - const struct prefix *p) + const struct prefix *p, + const char *plist_name) { struct bgp_debug_filter *filter; @@ -295,13 +311,27 @@ static void bgp_debug_list_add_entry(struct list *list, const char *host, if (host) { filter->host = XSTRDUP(MTYPE_BGP_DEBUG_STR, host); + filter->plist_name = NULL; + filter->plist_v4 = NULL; + filter->plist_v6 = NULL; filter->p = NULL; } else if (p) { filter->host = NULL; + filter->plist_name = NULL; + filter->plist_v4 = NULL; + filter->plist_v6 = NULL; filter->p = prefix_new(); prefix_copy(filter->p, p); } + if (plist_name) { + filter->plist_name = XSTRDUP(MTYPE_BGP_DEBUG_STR, plist_name); + filter->plist_v4 = prefix_list_lookup(AFI_IP, + filter->plist_name); + filter->plist_v6 = prefix_list_lookup(AFI_IP6, + filter->plist_name); + } + listnode_add(list, filter); } @@ -315,6 +345,7 @@ static bool bgp_debug_list_remove_entry(struct list *list, const char *host, if (host && strcmp(filter->host, host) == 0) { listnode_delete(list, filter); XFREE(MTYPE_BGP_DEBUG_STR, filter->host); + XFREE(MTYPE_BGP_DEBUG_STR, filter->plist_name); XFREE(MTYPE_BGP_DEBUG_FILTER, filter); return true; } else if (p && filter->p->prefixlen == p->prefixlen @@ -330,16 +361,20 @@ static bool bgp_debug_list_remove_entry(struct list *list, const char *host, } static bool bgp_debug_list_has_entry(struct list *list, const char *host, - const struct prefix *p) + const struct prefix *p, + const char *plist_name) { struct bgp_debug_filter *filter; struct listnode *node, *nnode; for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) { - if (host) { - if (strcmp(filter->host, host) == 0) { + if (host && plist_name) { + if (strmatch(filter->host, host) && filter->plist_name && + strmatch(filter->plist_name, plist_name)) + return true; + } else if (host) { + if (strmatch(filter->host, host)) return true; - } } else if (p) { if (filter->p->prefixlen == p->prefixlen && prefix_match(filter->p, p)) { @@ -353,7 +388,7 @@ static bool bgp_debug_list_has_entry(struct list *list, const char *host, bool bgp_debug_peer_updout_enabled(char *host) { - return (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host, + return (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host, NULL, NULL)); } @@ -780,14 +815,15 @@ DEFUN (debug_bgp_neighbor_events_peer, bgp_debug_neighbor_events_peers = list_new(); if (bgp_debug_list_has_entry(bgp_debug_neighbor_events_peers, host, - NULL)) { + NULL, NULL)) { vty_out(vty, "BGP neighbor-events debugging is already enabled for %s\n", host); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_neighbor_events_peers, host, NULL); + bgp_debug_list_add_entry(bgp_debug_neighbor_events_peers, host, NULL, + NULL); if (vty->node == CONFIG_NODE) DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS); @@ -927,14 +963,15 @@ DEFUN (debug_bgp_keepalive_peer, if (!bgp_debug_keepalive_peers) bgp_debug_keepalive_peers = list_new(); - if (bgp_debug_list_has_entry(bgp_debug_keepalive_peers, host, NULL)) { + if (bgp_debug_list_has_entry(bgp_debug_keepalive_peers, host, NULL, + NULL)) { vty_out(vty, "BGP keepalive debugging is already enabled for %s\n", host); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_keepalive_peers, host, NULL); + bgp_debug_list_add_entry(bgp_debug_keepalive_peers, host, NULL, NULL); if (vty->node == CONFIG_NODE) DEBUG_ON(keepalive, KEEPALIVE); @@ -1015,15 +1052,16 @@ DEFPY (debug_bgp_bestpath_prefix, if (!bgp_debug_bestpath_prefixes) bgp_debug_bestpath_prefixes = list_new(); - if (bgp_debug_list_has_entry(bgp_debug_bestpath_prefixes, NULL, - prefix)) { + if (bgp_debug_list_has_entry(bgp_debug_bestpath_prefixes, NULL, prefix, + NULL)) { vty_out(vty, "BGP bestpath debugging is already enabled for %s\n", prefix_str); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_bestpath_prefixes, NULL, prefix); + bgp_debug_list_add_entry(bgp_debug_bestpath_prefixes, NULL, prefix, + NULL); if (vty->node == CONFIG_NODE) { DEBUG_ON(bestpath, BESTPATH); @@ -1116,6 +1154,31 @@ DEFUN (debug_bgp_update, return CMD_SUCCESS; } +DEFPY (debug_bgp_update_detail, + debug_bgp_update_detail_cmd, + "[no] debug bgp updates detail", + NO_STR + DEBUG_STR + BGP_STR + "BGP updates\n" + "Show detailed information about updates\n") +{ + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(update, UPDATE_DETAIL); + else + DEBUG_ON(update, UPDATE_DETAIL); + } else { + if (no) + TERM_DEBUG_OFF(update, UPDATE_DETAIL); + else + TERM_DEBUG_ON(update, UPDATE_DETAIL); + vty_out(vty, "BGP updates detail debugging is on\n"); + } + + return CMD_SUCCESS; +} + DEFUN (debug_bgp_update_direct, debug_bgp_update_direct_cmd, "debug bgp updates ", @@ -1150,9 +1213,9 @@ DEFUN (debug_bgp_update_direct, return CMD_SUCCESS; } -DEFUN (debug_bgp_update_direct_peer, +DEFPY (debug_bgp_update_direct_peer, debug_bgp_update_direct_peer_cmd, - "debug bgp updates ", + "debug bgp updates [prefix-list PREFIXLIST_NAME$plist]", DEBUG_STR BGP_STR "BGP updates\n" @@ -1160,7 +1223,9 @@ DEFUN (debug_bgp_update_direct_peer, "Outbound updates\n" "BGP neighbor IP address to debug\n" "BGP IPv6 neighbor to debug\n" - "BGP neighbor on interface to debug\n") + "BGP neighbor on interface to debug\n" + "Use prefix-list to filter prefixes to debug\n" + "Name of prefix-list\n") { int idx_in_out = 3; int idx_peer = 4; @@ -1180,7 +1245,7 @@ DEFUN (debug_bgp_update_direct_peer, if (inbound) { if (bgp_debug_list_has_entry(bgp_debug_update_in_peers, host, - NULL)) { + NULL, plist)) { vty_out(vty, "BGP inbound update debugging is already enabled for %s\n", host); @@ -1190,7 +1255,7 @@ DEFUN (debug_bgp_update_direct_peer, else { if (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host, - NULL)) { + NULL, plist)) { vty_out(vty, "BGP outbound update debugging is already enabled for %s\n", host); @@ -1199,14 +1264,15 @@ DEFUN (debug_bgp_update_direct_peer, } if (inbound) - bgp_debug_list_add_entry(bgp_debug_update_in_peers, host, NULL); + bgp_debug_list_add_entry(bgp_debug_update_in_peers, host, NULL, + plist); else { struct peer *peer; struct peer_af *paf; int afidx; - bgp_debug_list_add_entry(bgp_debug_update_out_peers, host, - NULL); + bgp_debug_list_add_entry(bgp_debug_update_out_peers, host, NULL, + plist); peer = bgp_find_peer(vty, host); if (peer) { @@ -1283,7 +1349,7 @@ DEFUN (no_debug_bgp_update_direct, DEFUN (no_debug_bgp_update_direct_peer, no_debug_bgp_update_direct_peer_cmd, - "no debug bgp updates ", + "no debug bgp updates [prefix-list PREFIXLIST_NAME]", NO_STR DEBUG_STR BGP_STR @@ -1292,7 +1358,9 @@ DEFUN (no_debug_bgp_update_direct_peer, "Outbound updates\n" "BGP neighbor IP address to debug\n" "BGP IPv6 neighbor to debug\n" - "BGP neighbor on interface to debug\n") + "BGP neighbor on interface to debug\n" + "Use prefix-list to filter prefixes to debug\n" + "Name of prefix-list\n") { int idx_in_out = 4; int idx_peer = 5; @@ -1414,15 +1482,15 @@ DEFPY (debug_bgp_update_prefix_afi_safi, if (!bgp_debug_update_prefixes) bgp_debug_update_prefixes = list_new(); - if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, - &argv_p)) { + if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, &argv_p, + NULL)) { vty_out(vty, "BGP updates debugging is already enabled for %pFX\n", &argv_p); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, &argv_p); + bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, &argv_p, NULL); if (vty->node == CONFIG_NODE) { DEBUG_ON(update, UPDATE_PREFIX); @@ -1510,14 +1578,15 @@ DEFPY (debug_bgp_update_prefix, if (!bgp_debug_update_prefixes) bgp_debug_update_prefixes = list_new(); - if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, prefix)) { + if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, prefix, + NULL)) { vty_out(vty, "BGP updates debugging is already enabled for %s\n", prefix_str); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, prefix); + bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, prefix, NULL); if (vty->node == CONFIG_NODE) { DEBUG_ON(update, UPDATE_PREFIX); @@ -1647,13 +1716,14 @@ DEFPY (debug_bgp_zebra_prefix, if (!bgp_debug_zebra_prefixes) bgp_debug_zebra_prefixes = list_new(); - if (bgp_debug_list_has_entry(bgp_debug_zebra_prefixes, NULL, prefix)) { + if (bgp_debug_list_has_entry(bgp_debug_zebra_prefixes, NULL, prefix, + NULL)) { vty_out(vty, "BGP zebra debugging is already enabled for %s\n", prefix_str); return CMD_SUCCESS; } - bgp_debug_list_add_entry(bgp_debug_zebra_prefixes, NULL, prefix); + bgp_debug_list_add_entry(bgp_debug_zebra_prefixes, NULL, prefix, NULL); if (vty->node == CONFIG_NODE) DEBUG_ON(zebra, ZEBRA); @@ -2206,6 +2276,8 @@ DEFUN_NOSH (show_debugging_bgp, cmd_show_lib_debugs(vty); + hook_call(bgp_hook_config_write_debug, vty, false); + return CMD_SUCCESS; } @@ -2265,6 +2337,11 @@ static int bgp_config_write_debug(struct vty *vty) bgp_debug_update_out_peers); } + if (CONF_BGP_DEBUG(update, UPDATE_DETAIL)) { + vty_out(vty, "debug bgp updates detail\n"); + write++; + } + if (CONF_BGP_DEBUG(zebra, ZEBRA)) { if (!bgp_debug_zebra_prefixes || list_isempty(bgp_debug_zebra_prefixes)) { @@ -2340,6 +2417,9 @@ static int bgp_config_write_debug(struct vty *vty) write++; } + if (hook_call(bgp_hook_config_write_debug, vty, true)) + write++; + return write; } @@ -2370,6 +2450,8 @@ void bgp_debug_init(void) install_element(CONFIG_NODE, &debug_bgp_keepalive_cmd); install_element(ENABLE_NODE, &debug_bgp_update_cmd); install_element(CONFIG_NODE, &debug_bgp_update_cmd); + install_element(ENABLE_NODE, &debug_bgp_update_detail_cmd); + install_element(CONFIG_NODE, &debug_bgp_update_detail_cmd); install_element(ENABLE_NODE, &debug_bgp_zebra_cmd); install_element(CONFIG_NODE, &debug_bgp_zebra_cmd); install_element(ENABLE_NODE, &debug_bgp_update_groups_cmd); @@ -2510,7 +2592,8 @@ static int bgp_debug_per_prefix(const struct prefix *p, /* Return true if this peer is on the per_peer_list of peers to debug * for BGP_DEBUG_TYPE */ -static bool bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type, +static bool bgp_debug_per_peer(char *host, const struct prefix *p, + unsigned long term_bgp_debug_type, unsigned int BGP_DEBUG_TYPE, struct list *per_peer_list) { @@ -2522,17 +2605,28 @@ static bool bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type, if (!per_peer_list || list_isempty(per_peer_list)) return true; - else { - if (!host) - return false; + if (!host) + return false; - for (ALL_LIST_ELEMENTS(per_peer_list, node, nnode, - filter)) - if (strcmp(filter->host, host) == 0) - return true; + for (ALL_LIST_ELEMENTS(per_peer_list, node, nnode, filter)) + if (strmatch(filter->host, host) && + filter->plist_name && p) { + struct prefix_list *plist; + afi_t afi = family2afi(p->family); - return false; - } + plist = (afi == AFI_IP) ? filter->plist_v4 + : filter->plist_v6; + + if (!plist) + continue; + + return prefix_list_apply(plist, p) == + PREFIX_PERMIT; + } else if (strmatch(filter->host, host)) { + return true; + } + + return false; } return false; @@ -2545,7 +2639,7 @@ bool bgp_debug_neighbor_events(const struct peer *peer) if (peer) host = peer->host; - return bgp_debug_per_peer(host, term_bgp_debug_neighbor_events, + return bgp_debug_per_peer(host, NULL, term_bgp_debug_neighbor_events, BGP_DEBUG_NEIGHBOR_EVENTS, bgp_debug_neighbor_events_peers); } @@ -2557,7 +2651,7 @@ bool bgp_debug_keepalive(const struct peer *peer) if (peer) host = peer->host; - return bgp_debug_per_peer(host, term_bgp_debug_keepalive, + return bgp_debug_per_peer(host, NULL, term_bgp_debug_keepalive, BGP_DEBUG_KEEPALIVE, bgp_debug_keepalive_peers); } @@ -2571,7 +2665,7 @@ bool bgp_debug_update(const struct peer *peer, const struct prefix *p, host = peer->host; if (inbound) { - if (bgp_debug_per_peer(host, term_bgp_debug_update, + if (bgp_debug_per_peer(host, p, term_bgp_debug_update, BGP_DEBUG_UPDATE_IN, bgp_debug_update_in_peers)) return true; @@ -2579,7 +2673,7 @@ bool bgp_debug_update(const struct peer *peer, const struct prefix *p, /* outbound */ else { - if (bgp_debug_per_peer(host, term_bgp_debug_update, + if (bgp_debug_per_peer(host, p, term_bgp_debug_update, BGP_DEBUG_UPDATE_OUT, bgp_debug_update_out_peers)) return true; diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 118325e..5b09501 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -6,9 +6,15 @@ #ifndef _QUAGGA_BGP_DEBUG_H #define _QUAGGA_BGP_DEBUG_H +#include "hook.h" +#include "vty.h" + #include "bgp_attr.h" #include "bgp_updgrp.h" +DECLARE_HOOK(bgp_hook_config_write_debug, (struct vty *vty, bool running), + (vty, running)); + /* sort of packet direction */ #define DUMP_ON 1 #define DUMP_SEND 2 @@ -96,6 +102,9 @@ extern struct list *bgp_debug_zebra_prefixes; struct bgp_debug_filter { char *host; + char *plist_name; + struct prefix_list *plist_v4; + struct prefix_list *plist_v6; struct prefix *p; }; @@ -109,6 +118,7 @@ struct bgp_debug_filter { #define BGP_DEBUG_UPDATE_IN 0x01 #define BGP_DEBUG_UPDATE_OUT 0x02 #define BGP_DEBUG_UPDATE_PREFIX 0x04 +#define BGP_DEBUG_UPDATE_DETAIL 0x08 #define BGP_DEBUG_ZEBRA 0x01 #define BGP_DEBUG_ALLOW_MARTIANS 0x01 #define BGP_DEBUG_NHT 0x01 @@ -124,9 +134,6 @@ struct bgp_debug_filter { #define BGP_DEBUG_EVPN_MH_ES 0x01 #define BGP_DEBUG_EVPN_MH_RT 0x02 -#define BGP_DEBUG_PACKET_SEND 0x01 -#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 - #define BGP_DEBUG_GRACEFUL_RESTART 0x01 #define BGP_DEBUG_BFD_LIB 0x01 diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 529713e..53b5212 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -4,6 +4,7 @@ */ #include +#include #include "log.h" #include "stream.h" diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index c408edb..a8ae177 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1042,6 +1042,34 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt, return len; } +bool ecommunity_has_route_target(struct ecommunity *ecom) +{ + uint32_t i; + uint8_t *pnt; + uint8_t type = 0; + uint8_t sub_type = 0; + + if (!ecom) + return false; + for (i = 0; i < ecom->size; i++) { + /* Retrieve value field */ + pnt = ecom->val + (i * ecom->unit_size); + + /* High-order octet is the type */ + type = *pnt++; + + if (type == ECOMMUNITY_ENCODE_AS || + type == ECOMMUNITY_ENCODE_IP || + type == ECOMMUNITY_ENCODE_AS4) { + /* Low-order octet of type. */ + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_ROUTE_TARGET) + return true; + } + } + return false; +} + /* Convert extended community attribute to string. * Due to historical reason of industry standard implementation, there * are three types of format: @@ -1581,8 +1609,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, * in the 'Network Address of Next- Hop' * field of the associated MP_REACH_NLRI. */ - struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *) - ecom_eval + 2; + struct ecommunity_ip *ip_ecom = + (struct ecommunity_ip *)&ecom_eval->val[2]; api->u.zr.redirect_ip_v4 = ip_ecom->ip; } else diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 7dc04d2..62e0430 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -117,6 +117,9 @@ struct ecommunity { */ uint8_t unit_size; + /* Disable IEEE floating-point encoding for extended community */ + bool disable_ieee_floating; + /* Size of Extended Communities attribute. */ uint32_t size; @@ -125,9 +128,6 @@ struct ecommunity { /* Human readable format string. */ char *str; - - /* Disable IEEE floating-point encoding for extended community */ - bool disable_ieee_floating; }; struct ecommunity_as { @@ -341,6 +341,7 @@ extern struct ecommunity *ecommunity_str2com(const char *, int, int); extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, int keyword_included); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); +extern bool ecommunity_has_route_target(struct ecommunity *ecom); extern void ecommunity_strfree(char **s); extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2); extern bool ecommunity_match(const struct ecommunity *, diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index ad101f1..a846484 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -311,6 +311,49 @@ static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn) return 0; } +/* Flag if the route is injectable into EVPN. + * This would be following category: + * Non-imported route, + * Non-EVPN imported route, + */ +bool is_route_injectable_into_evpn_non_supp(struct bgp_path_info *pi) +{ + struct bgp_path_info *parent_pi; + struct bgp_table *table; + struct bgp_dest *dest; + + if (pi->sub_type != BGP_ROUTE_IMPORTED || !pi->extra || + !pi->extra->vrfleak || !pi->extra->vrfleak->parent) + return true; + + parent_pi = (struct bgp_path_info *)pi->extra->vrfleak->parent; + dest = parent_pi->net; + if (!dest) + return true; + table = bgp_dest_table(dest); + if (table && + table->afi == AFI_L2VPN && + table->safi == SAFI_EVPN) + return false; + + return true; +} + +/* Flag if the route is injectable into EVPN. + * This would be following category: + * Non-imported route, + * Non-EVPN imported route, + * Non Aggregate suppressed route. + */ +bool is_route_injectable_into_evpn(struct bgp_path_info *pi) +{ + /* do not import aggr suppressed routes */ + if (bgp_path_suppressed(pi)) + return false; + + return is_route_injectable_into_evpn_non_supp(pi); +} + /* * Compare Route Targets. */ @@ -881,7 +924,7 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, zclient_create_header( s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL, bgp->vrf_id); - stream_putl(s, vpn->vni); + stream_putl(s, vpn ? vpn->vni : 0); if (mac) /* Mac Addr */ stream_put(s, &mac->octet, ETH_ALEN); @@ -927,7 +970,7 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, snprintf(esi_buf, sizeof(esi_buf), "-"); zlog_debug( "Tx %s MACIP, VNI %u MAC %pEA IP %pIA flags 0x%x seq %u remote VTEP %pI4 esi %s", - add ? "ADD" : "DEL", vpn->vni, + add ? "ADD" : "DEL", (vpn ? vpn->vni : 0), (mac ? mac : &p->prefix.macip_addr.mac), &p->prefix.macip_addr.ip, flags, seq, &remote_vtep_ip, esi_buf); @@ -970,14 +1013,14 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, zclient_create_header( s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL, bgp->vrf_id); - stream_putl(s, vpn->vni); + stream_putl(s, vpn ? vpn->vni : 0); if (is_evpn_prefix_ipaddr_v4(p)) stream_put_in_addr(s, &p->prefix.imet_addr.ip.ipaddr_v4); else if (is_evpn_prefix_ipaddr_v6(p)) { flog_err( EC_BGP_VTEP_INVALID, "Bad remote IP when trying to %s remote VTEP for VNI %u", - add ? "ADD" : "DEL", vpn->vni); + add ? "ADD" : "DEL", (vpn ? vpn->vni : 0)); return -1; } stream_putl(s, flood_control); @@ -986,7 +1029,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, if (bgp_debug_zebra(NULL)) zlog_debug("Tx %s Remote VTEP, VNI %u remote VTEP %pI4", - add ? "ADD" : "DEL", vpn->vni, + add ? "ADD" : "DEL", (vpn ? vpn->vni : 0), &p->prefix.imet_addr.ip.ipaddr_v4); frrtrace(3, frr_bgp, evpn_bum_vtep_zsend, add, vpn, p); @@ -1638,6 +1681,9 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, vrf_id_to_name(bgp_vrf->vrf_id), evp, &attr.rmac, &attr.nexthop); + frrtrace(4, frr_bgp, evpn_advertise_type5, bgp_vrf->vrf_id, evp, + &attr.rmac, attr.nexthop); + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; if (src_afi == AFI_IP6 && @@ -2280,6 +2326,8 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) if (!dest) return 0; + frrtrace(2, frr_bgp, evpn_withdraw_type5, bgp_vrf->vrf_id, evp); + delete_evpn_route_entry(bgp_evpn, afi, safi, dest, &pi); if (pi) bgp_process(bgp_evpn, dest, afi, safi); @@ -3029,11 +3077,22 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Process for route leaking. */ vpn_leak_from_vrf_update(bgp_get_default(), bgp_vrf, pi); - if (bgp_debug_zebra(NULL)) - zlog_debug("... %s pi dest %p (l %d) pi %p (l %d, f 0x%x)", - new_pi ? "new" : "update", dest, + if (bgp_debug_zebra(NULL)) { + struct ipaddr nhip = {}; + + if (pi->net->rn->p.family == AF_INET6) { + SET_IPADDR_V6(&nhip); + IPV6_ADDR_COPY(&nhip.ipaddr_v6, &pi->attr->mp_nexthop_global); + } else { + SET_IPADDR_V4(&nhip); + IPV4_ADDR_COPY(&nhip.ipaddr_v4, &pi->attr->nexthop); + } + zlog_debug("... %s pi %s dest %p (l %d) pi %p (l %d, f 0x%x) nh %pIA", + new_pi ? "new" : "update", + bgp_vrf->name_pretty, dest, bgp_dest_get_lock_count(dest), pi, pi->lock, - pi->flags); + pi->flags, &nhip); + } bgp_dest_unlock_node(dest); @@ -3342,10 +3401,22 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, return 0; } - if (bgp_debug_zebra(NULL)) - zlog_debug("... delete dest %p (l %d) pi %p (l %d, f 0x%x)", - dest, bgp_dest_get_lock_count(dest), pi, pi->lock, - pi->flags); + if (bgp_debug_zebra(NULL)) { + struct ipaddr nhip = {}; + + if (pi->net->rn->p.family == AF_INET6) { + SET_IPADDR_V6(&nhip); + IPV6_ADDR_COPY(&nhip.ipaddr_v6, &pi->attr->mp_nexthop_global); + } else { + SET_IPADDR_V4(&nhip); + IPV4_ADDR_COPY(&nhip.ipaddr_v4, &pi->attr->nexthop); + } + + zlog_debug("... delete pi %s dest %p (l %d) pi %p (l %d, f 0x%x) nh %pIA", + bgp_vrf->name_pretty, dest, + bgp_dest_get_lock_count(dest), pi, pi->lock, + pi->flags, &nhip); + } /* Process for route leaking. */ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp_vrf, pi); @@ -3590,7 +3661,7 @@ static bool bgp_evpn_route_matches_macvrf_soo(struct bgp_path_info *pi, struct ecommunity *macvrf_soo; bool ret = false; - if (!bgp_evpn->evpn_info) + if (!bgp_evpn || !bgp_evpn->evpn_info) return false; /* We only stamp the mac-vrf soo on routes from our local L2VNI. @@ -6097,7 +6168,7 @@ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn) snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, vpn->rd_id); (void)str2prefix_rd(buf, &vpn->prd); if (vpn->prd_pretty) - XFREE(MTYPE_BGP, vpn->prd_pretty); + XFREE(MTYPE_BGP_NAME, vpn->prd_pretty); UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); } @@ -6203,7 +6274,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); if (vpn->prd_pretty) - XFREE(MTYPE_BGP, vpn->prd_pretty); + XFREE(MTYPE_BGP_NAME, vpn->prd_pretty); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); } @@ -7080,7 +7151,7 @@ void bgp_evpn_cleanup(struct bgp *bgp) } if (bgp->vrf_prd_pretty) - XFREE(MTYPE_BGP, bgp->vrf_prd_pretty); + XFREE(MTYPE_BGP_NAME, bgp->vrf_prd_pretty); } /* @@ -7650,3 +7721,64 @@ bool bgp_evpn_mpath_has_dvni(const struct bgp *bgp_vrf, return false; } + +/* Upon aggregate set trigger unimport suppressed routes + * from EVPN + */ +void bgp_aggr_supp_withdraw_from_evpn(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_dest *agg_dest, *dest, *top; + const struct prefix *aggr_p; + struct bgp_aggregate *bgp_aggregate; + struct bgp_table *table; + struct bgp_path_info *pi; + + if (!bgp_get_evpn() && !advertise_type5_routes(bgp, afi)) + return; + + /* Aggregate-address table walk. */ + table = bgp->rib[afi][safi]; + for (agg_dest = bgp_table_top(bgp->aggregate[afi][safi]); agg_dest; + agg_dest = bgp_route_next(agg_dest)) { + bgp_aggregate = bgp_dest_get_bgp_aggregate_info(agg_dest); + + if (bgp_aggregate == NULL) + continue; + + aggr_p = bgp_dest_get_prefix(agg_dest); + + /* Look all nodes below the aggregate prefix in + * global AFI/SAFI table (IPv4/IPv6). + * Trigger withdrawal (this will be Type-5 routes only) + * from EVPN Global table. + */ + top = bgp_node_get(table, aggr_p); + for (dest = bgp_node_get(table, aggr_p); dest; + dest = bgp_route_next_until(dest, top)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (dest_p->prefixlen <= aggr_p->prefixlen) + continue; + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + if (pi->sub_type == BGP_ROUTE_AGGREGATE) + continue; + + /* Only Suppressed route remove from EVPN */ + if (!bgp_path_suppressed(pi)) + continue; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s aggregated %pFX remove suppressed route %pFX", + __func__, aggr_p, dest_p); + + if (!is_route_injectable_into_evpn_non_supp(pi)) + continue; + + bgp_evpn_withdraw_type5_route(bgp, dest_p, afi, + safi); + } + } + } +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 5547446..c641a64 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -94,31 +94,6 @@ static inline bool is_pi_family_evpn(struct bgp_path_info *pi) return is_pi_family_matching(pi, AFI_L2VPN, SAFI_EVPN); } -/* Flag if the route is injectable into EVPN. This would be either a - * non-imported route or a non-EVPN imported route. - */ -static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) -{ - struct bgp_path_info *parent_pi; - struct bgp_table *table; - struct bgp_dest *dest; - - if (pi->sub_type != BGP_ROUTE_IMPORTED || !pi->extra || - !pi->extra->vrfleak || !pi->extra->vrfleak->parent) - return true; - - parent_pi = (struct bgp_path_info *)pi->extra->vrfleak->parent; - dest = parent_pi->net; - if (!dest) - return true; - table = bgp_dest_table(dest); - if (table && - table->afi == AFI_L2VPN && - table->safi == SAFI_EVPN) - return false; - return true; -} - static inline bool evpn_resolve_overlay_index(void) { struct bgp *bgp = NULL; @@ -206,5 +181,9 @@ extern mpls_label_t *bgp_evpn_path_info_labels_get_l3vni(mpls_label_t *labels, extern vni_t bgp_evpn_path_info_get_l3vni(const struct bgp_path_info *pi); extern bool bgp_evpn_mpath_has_dvni(const struct bgp *bgp_vrf, struct bgp_path_info *mpinfo); +extern bool is_route_injectable_into_evpn(struct bgp_path_info *pi); +extern bool is_route_injectable_into_evpn_non_supp(struct bgp_path_info *pi); +extern void bgp_aggr_supp_withdraw_from_evpn(struct bgp *bgp, afi_t afi, + safi_t safi); #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index ffb1b17..d88c52d 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -37,7 +37,7 @@ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_label.h" -#include "bgpd/bgp_nht.h" +#include "bgpd/bgp_nhg.h" #include "bgpd/bgp_mpath.h" #include "bgpd/bgp_trace.h" @@ -441,6 +441,10 @@ int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es, ? "esr" : (vpn ? "ead-evi" : "ead-es"), &attr->mp_nexthop_global_in); + + frrtrace(4, frr_bgp, evpn_mh_local_ead_es_evi_route_upd, + &es->esi, (vpn ? vpn->vni : 0), evp->prefix.route_type, + attr->mp_nexthop_global_in); } /* Return back the route entry. */ @@ -491,6 +495,8 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, : (vpn ? "ead-evi" : "ead-es"), &es->originator_ip); + frrtrace(4, frr_bgp, evpn_mh_local_ead_es_evi_route_del, &es->esi, + (vpn ? vpn->vni : 0), p->prefix.route_type, es->originator_ip); /* Next, locate route node in the global EVPN routing table. * Note that this table is a 2-level tree (RD-level + Prefix-level) */ @@ -2999,8 +3005,8 @@ static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es, listnode_add(es->es_vrf_list, &es_vrf->es_listnode); /* setup the L3 NHG id for the ES */ - es_vrf->nhg_id = bgp_l3nhg_id_alloc(); - es_vrf->v6_nhg_id = bgp_l3nhg_id_alloc(); + es_vrf->nhg_id = bgp_nhg_id_alloc(); + es_vrf->v6_nhg_id = bgp_nhg_id_alloc(); if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) zlog_debug("es %s vrf %u nhg %u v6_nhg %d create", es->esi_str, @@ -3028,10 +3034,10 @@ static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf) /* Remove the NHG resources */ bgp_evpn_l3nhg_deactivate(es_vrf); if (es_vrf->nhg_id) - bgp_l3nhg_id_free(es_vrf->nhg_id); + bgp_nhg_id_free(es_vrf->nhg_id); es_vrf->nhg_id = 0; if (es_vrf->v6_nhg_id) - bgp_l3nhg_id_free(es_vrf->v6_nhg_id); + bgp_nhg_id_free(es_vrf->v6_nhg_id); es_vrf->v6_nhg_id = 0; /* remove from the ES's VRF list */ @@ -3460,6 +3466,10 @@ static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp, evi_vtep->es_evi->vpn->vni, &evi_vtep->vtep_ip, ead_es ? "ead_es" : "ead_evi"); + frrtrace(4, frr_bgp, evpn_mh_es_evi_vtep_add, + &evi_vtep->es_evi->es->esi, evi_vtep->es_evi->vpn->vni, + evi_vtep->vtep_ip, ead_es); + if (ead_es) SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES); else @@ -3484,6 +3494,10 @@ static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp, evi_vtep->es_evi->vpn->vni, &evi_vtep->vtep_ip, ead_es ? "ead_es" : "ead_evi"); + frrtrace(4, frr_bgp, evpn_mh_es_evi_vtep_del, + &evi_vtep->es_evi->es->esi, evi_vtep->es_evi->vpn->vni, + evi_vtep->vtep_ip, ead_es); + if (ead_es) UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES); else @@ -4443,13 +4457,13 @@ static void bgp_evpn_nh_zebra_update_send(struct bgp_evpn_nh *nh, bool add) stream_putw_at(s, 0, stream_get_endp(s)); - if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES) || bgp_debug_zebra(NULL)) { if (add) - zlog_debug("evpn vrf %s nh %s rmac %pEA add to zebra", + zlog_debug("evpn %s nh %s rmac %pEA add to zebra", nh->bgp_vrf->name_pretty, nh->nh_str, &nh->rmac); - else if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("evpn vrf %s nh %s del to zebra", + else + zlog_debug("evpn %s nh %s del to zebra", nh->bgp_vrf->name_pretty, nh->nh_str); } @@ -4632,7 +4646,7 @@ static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh) continue; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("evpn vrf %s nh %s ref_pi update", + zlog_debug("evpn %s nh %s ref_pi update", nh->bgp_vrf->name_pretty, nh->nh_str); nh->ref_pi = pi; /* If we have a new pi copy rmac from it and update @@ -4710,11 +4724,12 @@ static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info) pi = nh_info->pi; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug("path %s unlinked from nh %s %s", + zlog_debug("path %s unlinked from %s nh %s pathcount %u", pi->net ? prefix2str(&pi->net->rn->p, prefix_buf, sizeof(prefix_buf)) : "", - nh->bgp_vrf->name_pretty, nh->nh_str); + nh->bgp_vrf->name_pretty, nh->nh_str, + listcount(nh->pi_list)); list_delete_node(nh->pi_list, &nh_info->nh_listnode); @@ -4745,7 +4760,7 @@ static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi) if (!bgp_vrf->evpn_nh_table) { if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug("path %pFX linked to vrf %s failed", + zlog_debug("path %pFX linked to %s failed", &pi->net->rn->p, bgp_vrf->name_pretty); return; } @@ -4793,8 +4808,9 @@ static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi) bgp_evpn_path_nh_unlink(nh_info); if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug("path %pFX linked to nh %s %s", &pi->net->rn->p, - nh->bgp_vrf->name_pretty, nh->nh_str); + zlog_debug("path %pFX linked to %s nh %s pathcount %u", + &pi->net->rn->p, nh->bgp_vrf->name_pretty, + nh->nh_str, listcount(nh->pi_list)); /* link mac-ip path to the new nh */ nh_info->nh = nh; diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index ee1f749..cebabb9 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -388,11 +388,6 @@ static inline bool bgp_evpn_attr_is_local_es(struct attr *attr) return attr ? !!(attr->es_flags & ATTR_ES_IS_LOCAL) : false; } -static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr) -{ - return (attr) ? attr->df_pref : 0; -} - static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) { return (es->flags & BGP_EVPNES_OPER_UP) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index eb9f34e..846a82b 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2294,11 +2294,11 @@ static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd, bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); if (bgp_vrf->vrf_prd_pretty) - XFREE(MTYPE_BGP, bgp_vrf->vrf_prd_pretty); + XFREE(MTYPE_BGP_NAME, bgp_vrf->vrf_prd_pretty); /* update RD */ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); - bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); + bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP_NAME, rd_pretty); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); /* We have a new RD for VRF. @@ -2321,7 +2321,7 @@ static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); if (bgp_vrf->vrf_prd_pretty) - XFREE(MTYPE_BGP, bgp_vrf->vrf_prd_pretty); + XFREE(MTYPE_BGP_NAME, bgp_vrf->vrf_prd_pretty); /* We have a new RD for VRF. * Advertise all type-5 routes again with the new RD */ @@ -2343,7 +2343,7 @@ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, /* update RD */ memcpy(&vpn->prd, rd, sizeof(struct prefix_rd)); - vpn->prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); + vpn->prd_pretty = XSTRDUP(MTYPE_BGP_NAME, rd_pretty); SET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); if (is_vni_live(vpn)) diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index ad541b6..a851179 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -205,8 +205,17 @@ static struct as_list *as_list_new(void) static void as_list_free(struct as_list *aslist) { - XFREE(MTYPE_AS_STR, aslist->name); - XFREE(MTYPE_AS_LIST, aslist); + struct aspath_exclude_list *cur_bp = aslist->exclude_list; + struct aspath_exclude_list *next_bp = NULL; + + while (cur_bp) { + next_bp = cur_bp->next; + XFREE(MTYPE_ROUTE_MAP_COMPILED, cur_bp); + cur_bp = next_bp; + } + + XFREE (MTYPE_AS_STR, aslist->name); + XFREE (MTYPE_AS_LIST, aslist); } /* Insert new AS list to list of as_list. Each as_list is sorted by @@ -290,6 +299,7 @@ static void as_list_delete(struct as_list *aslist) { struct as_list_list *list; struct as_filter *filter, *next; + struct aspath_exclude_list *cur_bp; for (filter = aslist->head; filter; filter = next) { next = filter->next; @@ -308,6 +318,12 @@ static void as_list_delete(struct as_list *aslist) else list->head = aslist->next; + cur_bp = aslist->exclude_list; + while (cur_bp) { + cur_bp->bp_as_excl->exclude_aspath_acl = NULL; + cur_bp = cur_bp->next; + } + as_list_free(aslist); } diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h index 1890fd3..2d9f07c 100644 --- a/bgpd/bgp_filter.h +++ b/bgpd/bgp_filter.h @@ -25,6 +25,11 @@ struct as_filter { int64_t seq; }; +struct aspath_exclude_list { + struct aspath_exclude_list *next; + struct aspath_exclude *bp_as_excl; +}; + /* AS path filter list. */ struct as_list { char *name; @@ -34,6 +39,7 @@ struct as_list { struct as_filter *head; struct as_filter *tail; + struct aspath_exclude_list *exclude_list; }; diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index a295ec5..d4ccca8 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -547,7 +547,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, return CMD_SUCCESS; pbr_if = XCALLOC(MTYPE_TMP, sizeof(struct bgp_pbr_interface)); - strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ); + strlcpy(pbr_if->name, ifname, IFNAMSIZ); RB_INSERT(bgp_pbr_interface_head, head, pbr_if); *bgp_pbr_interface_any = false; } else { diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index eef3b64..234dbb0 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -445,7 +445,7 @@ void bgp_timer_set(struct peer_connection *connection) EVENT_OFF(peer->connection->t_pmax_restart); EVENT_OFF(peer->t_refresh_stalepath); - /* fallthru */ + fallthrough; case Clearing: EVENT_OFF(connection->t_start); EVENT_OFF(connection->t_connect); @@ -562,42 +562,45 @@ void bgp_delayopen_timer(struct event *thread) } /* BGP Peer Down Cause */ -const char *const peer_down_str[] = {"", - "Router ID changed", - "Remote AS changed", - "Local AS change", - "Cluster ID changed", - "Confederation identifier changed", - "Confederation peer changed", - "RR client config change", - "RS client config change", - "Update source change", - "Address family activated", - "Admin. shutdown", - "User reset", - "BGP Notification received", - "BGP Notification send", - "Peer closed the session", - "Neighbor deleted", - "Peer-group add member", - "Peer-group delete member", - "Capability changed", - "Passive config change", - "Multihop config change", - "NSF peer closed the session", - "Intf peering v6only config change", - "BFD down received", - "Interface down", - "Neighbor address lost", - "No path to specified Neighbor", - "Waiting for Peer IPv6 LLA", - "Waiting for VRF to be initialized", - "No AFI/SAFI activated for peer", - "AS Set config change", - "Waiting for peer OPEN", - "Reached received prefix count", - "Socket Error", - "Admin. shutdown (RTT)"}; +const char *const peer_down_str[] = { + "", + "Router ID changed", + "Remote AS changed", + "Local AS change", + "Cluster ID changed", + "Confederation identifier changed", + "Confederation peer changed", + "RR client config change", + "RS client config change", + "Update source change", + "Address family activated", + "Admin. shutdown", + "User reset", + "BGP Notification received", + "BGP Notification send", + "Peer closed the session", + "Neighbor deleted", + "Peer-group add member", + "Peer-group delete member", + "Capability changed", + "Passive config change", + "Multihop config change", + "NSF peer closed the session", + "Intf peering v6only config change", + "BFD down received", + "Interface down", + "Neighbor address lost", + "No path to specified Neighbor", + "Waiting for Peer IPv6 LLA", + "Waiting for VRF to be initialized", + "No AFI/SAFI activated for peer", + "AS Set config change", + "Waiting for peer OPEN", + "Reached received prefix count", + "Socket Error", + "Admin. shutdown (RTT)", + "Suppress Fib Turned On or Off", +}; static void bgp_graceful_restart_timer_off(struct peer_connection *connection, struct peer *peer) @@ -2384,6 +2387,7 @@ void bgp_fsm_nht_update(struct peer_connection *connection, struct peer *peer, && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED || peer->bgp->fast_convergence)) BGP_EVENT_ADD(connection, TCP_fatal_error); + break; case Clearing: case Deleted: case BGP_STATUS_MAX: @@ -2716,7 +2720,7 @@ int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, return BGP_GR_SUCCESS; } -int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd) +int bgp_gr_update_all(struct bgp *bgp, enum global_gr_command global_gr_cmd) { enum global_mode global_new_state = GLOBAL_INVALID; enum global_mode global_old_state = GLOBAL_INVALID; @@ -2870,7 +2874,8 @@ enum peer_mode bgp_peer_gr_mode_get(struct peer *peer) return peer->peer_gr_present_state; } -int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd) +int bgp_neighbor_graceful_restart(struct peer *peer, + enum peer_gr_command peer_gr_cmd) { enum peer_mode peer_new_state = PEER_INVALID; enum peer_mode peer_old_state = PEER_INVALID; @@ -2929,8 +2934,8 @@ int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd) return result; } -unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, - int new_peer_state) +unsigned int bgp_peer_gr_action(struct peer *peer, enum peer_mode old_peer_state, + enum peer_mode new_peer_state) { if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) zlog_debug( @@ -2938,7 +2943,7 @@ unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, __func__, print_peer_gr_mode(old_peer_state), print_peer_gr_mode(new_peer_state)); - int bgp_gr_global_mode = GLOBAL_INVALID; + enum global_mode bgp_gr_global_mode = GLOBAL_INVALID; unsigned int ret = BGP_GR_FAILURE; if (old_peer_state == new_peer_state) { @@ -2970,10 +2975,10 @@ unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer); - if (new_peer_state == bgp_gr_global_mode) { - /*This is incremental updates i.e no tear down - *of the existing session - *as the peer is already working in the same mode. + if ((int)new_peer_state == (int)bgp_gr_global_mode) { + /* This is incremental updates i.e no tear down + * of the existing session + * as the peer is already working in the same mode. */ ret = BGP_GR_SUCCESS; } else { @@ -3000,8 +3005,7 @@ unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); - if (old_peer_state == bgp_gr_global_mode) { - + if ((int)old_peer_state == (int)bgp_gr_global_mode) { /* This is incremental updates *i.e no tear down of the existing session *as the peer is already working in the same mode. diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index fd46b79..2e96ac4 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -143,10 +143,11 @@ extern void bgp_adjust_routeadv(struct peer *); DECLARE_HOOK(peer_backward_transition, (struct peer *peer), (peer)); DECLARE_HOOK(peer_established, (struct peer *peer), (peer)); -int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd); -int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd); -unsigned int bgp_peer_gr_action(struct peer *peer, - int old_peer_state, int new_peer_state); +int bgp_gr_update_all(struct bgp *bgp, enum global_gr_command global_gr_cmd); +int bgp_neighbor_graceful_restart(struct peer *peer, + enum peer_gr_command peer_gr_cmd); +unsigned int bgp_peer_gr_action(struct peer *peer, enum peer_mode old_peer_state, + enum peer_mode new_peer_state); void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state); unsigned int bgp_peer_gr_helper_enable(struct peer *peer); unsigned int bgp_peer_gr_enable(struct peer *peer); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index b8ce1ae..7327ab5 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -132,9 +132,8 @@ static void bgp_send_fec_register_label_msg(struct bgp_dest *dest, bool reg, return; if (BGP_DEBUG(labelpool, LABELPOOL)) - zlog_debug("%s: FEC %sregister %pRN label_index=%u label=%u", - __func__, reg ? "" : "un", bgp_dest_to_rnode(dest), - label_index, label); + zlog_debug("%s: FEC %sregister %pBD label_index=%u label=%u", + __func__, reg ? "" : "un", dest, label_index, label); /* If the route node has a local_label assigned or the * path node has an MPLS SR label index allowing zebra to * derive the label, proceed with registration. */ @@ -199,8 +198,8 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, assert(dest); if (BGP_DEBUG(labelpool, LABELPOOL)) - zlog_debug("%s: FEC %pRN label=%u, allocated=%d", __func__, - bgp_dest_to_rnode(dest), new_label, allocated); + zlog_debug("%s: FEC %pBD label=%u, allocated=%d", __func__, + dest, new_label, allocated); if (!allocated) { /* diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 8833386..23e0c19 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -36,6 +36,8 @@ static void lptest_init(void); static void lptest_finish(void); #endif +static void bgp_sync_label_manager(struct event *e); + /* * Remember where pool data are kept */ @@ -448,13 +450,16 @@ void bgp_lp_get( if (lp_fifo_count(&lp->requests) > lp->pending_count) { if (!bgp_zebra_request_label_range(MPLS_LABEL_BASE_ANY, - lp->next_chunksize)) + lp->next_chunksize, true)) return; lp->pending_count += lp->next_chunksize; if ((lp->next_chunksize << 1) <= LP_CHUNK_SIZE_MAX) lp->next_chunksize <<= 1; } + + event_add_timer(bm->master, bgp_sync_label_manager, NULL, 1, + &bm->t_bgp_sync_label_manager); } void bgp_lp_release( @@ -494,8 +499,18 @@ void bgp_lp_release( bf_release_index(chunk->allocated_map, index); chunk->nfree += 1; deallocated = true; + break; } assert(deallocated); + if (deallocated && + chunk->nfree == chunk->last - chunk->first + 1 && + lp_fifo_count(&lp->requests) == 0) { + bgp_zebra_release_label_range(chunk->first, + chunk->last); + list_delete_node(lp->chunks, node); + lp_chunk_free(chunk); + lp->next_chunksize = LP_CHUNK_SIZE_MIN; + } } } } @@ -547,6 +562,10 @@ static void bgp_sync_label_manager(struct event *e) zlog_debug("%s: out of labels, await more", __func__); } + + lp_fifo_add_tail(&lp->requests, lf); + event_add_timer(bm->master, bgp_sync_label_manager, + NULL, 1, &bm->t_bgp_sync_label_manager); break; } @@ -572,9 +591,6 @@ static void bgp_sync_label_manager(struct event *e) finishedrequest: XFREE(MTYPE_BGP_LABEL_FIFO, lf); } - - event_add_timer(bm->master, bgp_sync_label_manager, NULL, 1, - &bm->t_bgp_sync_label_manager); } void bgp_lp_event_chunk(uint32_t first, uint32_t last) @@ -642,7 +658,7 @@ void bgp_lp_event_zebra_up(void) } /* round up */ - chunks_needed = (labels_needed / lp->next_chunksize) + 1; + chunks_needed = (labels_needed + lp->next_chunksize - 1) / lp->next_chunksize; labels_needed = chunks_needed * lp->next_chunksize; /* @@ -650,10 +666,10 @@ void bgp_lp_event_zebra_up(void) */ list_delete_all_node(lp->chunks); - if (!bgp_zebra_request_label_range(MPLS_LABEL_BASE_ANY, labels_needed)) + if (labels_needed && !bgp_zebra_request_label_range(MPLS_LABEL_BASE_ANY, + labels_needed, true)) return; - - lp->pending_count = labels_needed; + lp->pending_count += labels_needed; /* * Invalidate any existing labels and requeue them as requests diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 11917c6..851c488 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -47,14 +47,20 @@ #include "bgpd/bgp_errors.h" #include "bgpd/bgp_script.h" #include "bgpd/bgp_evpn_mh.h" -#include "bgpd/bgp_nht.h" +#include "bgpd/bgp_nhg.h" #include "bgpd/bgp_routemap_nb.h" #include "bgpd/bgp_community_alias.h" +DEFINE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf), + (vty, vrf)); + #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #endif +DEFINE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled), + (vrf, enabled)); + /* bgpd options, we use GNU getopt library. */ static const struct option longopts[] = { { "bgp_port", required_argument, NULL, 'p' }, @@ -199,7 +205,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) bgp_delete(bgp_default); bgp_evpn_mh_finish(); - bgp_l3nhg_finish(); + bgp_nhg_finish(); /* reverse bgp_dump_init */ bgp_dump_finish(); @@ -287,6 +293,7 @@ static int bgp_vrf_enable(struct vrf *vrf) bgp_handle_socket(bgp, vrf, old_vrf_id, true); bgp_instance_up(bgp); + hook_call(bgp_hook_vrf_update, vrf, true); vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP); @@ -333,15 +340,36 @@ static int bgp_vrf_disable(struct vrf *vrf) * "down". */ bgp_instance_down(bgp); bgp_vrf_unlink(bgp, vrf); + hook_call(bgp_hook_vrf_update, vrf, false); } /* Note: This is a callback, the VRF will be deleted by the caller. */ return 0; } +static int bgp_vrf_config_write(struct vty *vty) +{ + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (vrf->vrf_id == VRF_DEFAULT) { + vty_out(vty, "!\n"); + continue; + } + vty_out(vty, "vrf %s\n", vrf->name); + + hook_call(bgp_hook_config_write_vrf, vty, vrf); + + vty_out(vty, "exit-vrf\n!\n"); + } + + return 0; +} + static void bgp_vrf_init(void) { vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); + vrf_cmd_init(bgp_vrf_config_write); } static void bgp_vrf_terminate(void) @@ -357,15 +385,20 @@ static const struct frr_yang_module_info *const bgpd_yang_modules[] = { &frr_bgp_route_map_info, }; -FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(bgpd, BGP, + .vty_port = BGP_VTY_PORT, + .proghelp = "Implementation of the BGP routing protocol.", - .proghelp = "Implementation of the BGP routing protocol.", + .signals = bgp_signals, + .n_signals = array_size(bgp_signals), - .signals = bgp_signals, .n_signals = array_size(bgp_signals), + .privs = &bgpd_privs, - .privs = &bgpd_privs, .yang_modules = bgpd_yang_modules, - .n_yang_modules = array_size(bgpd_yang_modules), + .yang_modules = bgpd_yang_modules, + .n_yang_modules = array_size(bgpd_yang_modules), ); +/* clang-format on */ #define DEPRECATED_OPTIONS "" diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 5c3067f..0764f1e 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -15,6 +15,7 @@ DEFINE_MGROUP(BGPD, "bgpd"); DEFINE_MTYPE(BGPD, BGP, "BGP instance"); +DEFINE_MTYPE(BGPD, BGP_NAME, "BGP Name data"); DEFINE_MTYPE(BGPD, BGP_LISTENER, "BGP listen socket details"); DEFINE_MTYPE(BGPD, BGP_PEER, "BGP peer"); DEFINE_MTYPE(BGPD, BGP_PEER_CONNECTION, "BGP peer connection"); @@ -41,6 +42,7 @@ DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA, "BGP ancillary route info"); DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA_EVPN, "BGP extra info for EVPN"); DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA_FS, "BGP extra info for flowspec"); DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA_VRFLEAK, "BGP extra info for vrf leaking"); +DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA_VNC, "BGP extra info for vnc"); DEFINE_MTYPE(BGPD, BGP_CONN, "BGP connected"); DEFINE_MTYPE(BGPD, BGP_STATIC, "BGP static"); DEFINE_MTYPE(BGPD, BGP_ADVERTISE_ATTR, "BGP adv attr"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 7acb41e..29584cd 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -11,6 +11,7 @@ DECLARE_MGROUP(BGPD); DECLARE_MTYPE(BGP); +DECLARE_MTYPE(BGP_NAME); DECLARE_MTYPE(BGP_LISTENER); DECLARE_MTYPE(BGP_PEER); DECLARE_MTYPE(BGP_PEER_CONNECTION); @@ -37,6 +38,7 @@ DECLARE_MTYPE(BGP_ROUTE_EXTRA); DECLARE_MTYPE(BGP_ROUTE_EXTRA_EVPN); DECLARE_MTYPE(BGP_ROUTE_EXTRA_FS); DECLARE_MTYPE(BGP_ROUTE_EXTRA_VRFLEAK); +DECLARE_MTYPE(BGP_ROUTE_EXTRA_VNC); DECLARE_MTYPE(BGP_CONN); DECLARE_MTYPE(BGP_STATIC); DECLARE_MTYPE(BGP_ADVERTISE_ATTR); diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index f2d1ee0..c773c21 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -554,12 +554,11 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, } if (debug) - zlog_debug( - "%pRN(%s): starting mpath update, newbest %s num candidates %d old-mpath-count %d old-cum-bw %" PRIu64, - bgp_dest_to_rnode(dest), bgp->name_pretty, - new_best ? new_best->peer->host : "NONE", - mp_list ? listcount(mp_list) : 0, old_mpath_count, - old_cum_bw); + zlog_debug("%pBD(%s): starting mpath update, newbest %s num candidates %d old-mpath-count %d old-cum-bw %" PRIu64, + dest, bgp->name_pretty, + new_best ? new_best->peer->host : "NONE", + mp_list ? listcount(mp_list) : 0, old_mpath_count, + old_cum_bw); /* * We perform an ordered walk through both lists in parallel. @@ -590,11 +589,10 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, tmp_info = mp_node ? listgetdata(mp_node) : NULL; if (debug) - zlog_debug( - "%pRN(%s): comparing candidate %s with existing mpath %s", - bgp_dest_to_rnode(dest), bgp->name_pretty, - tmp_info ? tmp_info->peer->host : "NONE", - cur_mpath ? cur_mpath->peer->host : "NONE"); + zlog_debug("%pBD(%s): comparing candidate %s with existing mpath %s", + dest, bgp->name_pretty, + tmp_info ? tmp_info->peer->host : "NONE", + cur_mpath ? cur_mpath->peer->host : "NONE"); /* * If equal, the path was a multipath and is still a multipath. @@ -622,10 +620,8 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf, sizeof(path_buf)); - zlog_debug( - "%pRN: %s is still multipath, cur count %d", - bgp_dest_to_rnode(dest), - path_buf, mpath_count); + zlog_debug("%pBD: %s is still multipath, cur count %d", + dest, path_buf, mpath_count); } } else { mpath_changed = 1; @@ -633,12 +629,10 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf, sizeof(path_buf)); - zlog_debug( - "%pRN: remove mpath %s nexthop %pI4, cur count %d", - bgp_dest_to_rnode(dest), - path_buf, - &cur_mpath->attr->nexthop, - mpath_count); + zlog_debug("%pBD: remove mpath %s nexthop %pI4, cur count %d", + dest, path_buf, + &cur_mpath->attr->nexthop, + mpath_count); } } mp_node = mp_next_node; @@ -663,10 +657,10 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, if (debug) { bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf, sizeof(path_buf)); - zlog_debug( - "%pRN: remove mpath %s nexthop %pI4, cur count %d", - bgp_dest_to_rnode(dest), path_buf, - &cur_mpath->attr->nexthop, mpath_count); + zlog_debug("%pBD: remove mpath %s nexthop %pI4, cur count %d", + dest, path_buf, + &cur_mpath->attr->nexthop, + mpath_count); } cur_mpath = next_mpath; } else { @@ -713,12 +707,10 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, bgp_path_info_path_with_addpath_rx_str( new_mpath, path_buf, sizeof(path_buf)); - zlog_debug( - "%pRN: add mpath %s nexthop %pI4, cur count %d", - bgp_dest_to_rnode(dest), - path_buf, - &new_mpath->attr->nexthop, - mpath_count); + zlog_debug("%pBD: add mpath %s nexthop %pI4, cur count %d", + dest, path_buf, + &new_mpath->attr->nexthop, + mpath_count); } } mp_node = mp_next_node; @@ -737,11 +729,10 @@ void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, all_paths_lb, cum_bw); if (debug) - zlog_debug( - "%pRN(%s): New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw %" PRIu64, - bgp_dest_to_rnode(dest), bgp->name_pretty, - mpath_count, mpath_changed ? "YES" : "NO", - all_paths_lb, cum_bw); + zlog_debug("%pBD(%s): New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw %" PRIu64, + dest, bgp->name_pretty, mpath_count, + mpath_changed ? "YES" : "NO", all_paths_lb, + cum_bw); if (mpath_changed || (bgp_path_info_mpath_count(new_best) != old_mpath_count)) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 18d9f64..af8990a 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -300,7 +300,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi) return; } - if (vpn_leak_to_vpn_active(bgp, afi, NULL)) { + if (vpn_leak_to_vpn_active(bgp, afi, NULL, false)) { label = bgp->vpn_policy[afi].tovpn_label; } @@ -388,6 +388,9 @@ void vpn_leak_zebra_vrf_sid_update_per_af(struct bgp *bgp, afi_t afi) tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); *tovpn_sid_ls = *tovpn_sid; + if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; } @@ -435,6 +438,8 @@ void vpn_leak_zebra_vrf_sid_update_per_vrf(struct bgp *bgp) tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); *tovpn_sid_ls = *tovpn_sid; + if (bgp->tovpn_zebra_vrf_sid_last_sent) + XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); bgp->tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; } @@ -482,6 +487,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi) bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL); XFREE(MTYPE_BGP_SRV6_SID, bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = NULL; } /* @@ -508,6 +514,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp) bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL); XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); + bgp->tovpn_zebra_vrf_sid_last_sent = NULL; } /* @@ -596,6 +603,11 @@ static void sid_register(struct bgp *bgp, const struct in6_addr *sid, listnode_add(bgp->srv6_functions, func); } +void srv6_function_free(struct bgp_srv6_function *func) +{ + XFREE(MTYPE_BGP_SRV6_FUNCTION, func); +} + void sid_unregister(struct bgp *bgp, const struct in6_addr *sid) { struct listnode *node, *nnode; @@ -604,7 +616,7 @@ void sid_unregister(struct bgp *bgp, const struct in6_addr *sid) for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) if (sid_same(&func->sid, sid)) { listnode_delete(bgp->srv6_functions, func); - XFREE(MTYPE_BGP_SRV6_FUNCTION, func); + srv6_function_free(func); } } @@ -1006,9 +1018,11 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn, { struct bgp_path_info *bpi_ultimate; struct bgp *bgp_nexthop; + struct bgp_table *table; bool nh_valid; bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi); + table = bgp_dest_table(bpi_ultimate->net); if (bpi->extra && bpi->extra->vrfleak && bpi->extra->vrfleak->bgp_orig) bgp_nexthop = bpi->extra->vrfleak->bgp_orig; @@ -1024,7 +1038,23 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn, is_pi_family_evpn(bpi_ultimate) || CHECK_FLAG(bpi_ultimate->flags, BGP_PATH_ACCEPT_OWN)) nh_valid = true; - else + else if (bpi_ultimate->type == ZEBRA_ROUTE_BGP && + bpi_ultimate->sub_type == BGP_ROUTE_STATIC && table && + (table->safi == SAFI_UNICAST || + table->safi == SAFI_LABELED_UNICAST)) { + /* the route is defined with the "network " command */ + + if (CHECK_FLAG(bgp_nexthop->flags, BGP_FLAG_IMPORT_CHECK)) + nh_valid = bgp_find_or_add_nexthop(to_bgp, bgp_nexthop, + afi, SAFI_UNICAST, + bpi_ultimate, NULL, + 0, p); + else + /* if "no bgp network import-check" is set, + * then mark the nexthop as valid. + */ + nh_valid = true; + } else /* * TBD do we need to do anything about the * 'connected' parameter? @@ -1225,8 +1255,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, new->extra->vrfleak->parent = bgp_path_info_lock(parent); bgp_dest_lock_node( (struct bgp_dest *)parent->net); - if (bgp_orig) - new->extra->vrfleak->bgp_orig = bgp_lock(bgp_orig); + + new->extra->vrfleak->bgp_orig = bgp_lock(bgp_orig); + if (nexthop_orig) new->extra->vrfleak->nexthop_orig = *nexthop_orig; @@ -1532,6 +1563,9 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ struct bgp_dest *bn; const char *debugmsg; int nexthop_self_flag = 0; + struct ecommunity *old_ecom; + struct ecommunity *new_ecom = NULL; + struct ecommunity *rtlist_ecom; if (debug) zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty); @@ -1559,7 +1593,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ if (!is_route_injectable_into_vpn(path_vrf)) return; - if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) { + if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, false)) { if (debug) zlog_debug("%s: %s skipping: %s", __func__, from_bgp->name, debugmsg); @@ -1569,32 +1603,6 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ /* shallow copy */ static_attr = *path_vrf->attr; - /* - * route map handling - */ - if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) { - struct bgp_path_info info; - route_map_result_t ret; - - memset(&info, 0, sizeof(info)); - info.peer = to_bgp->peer_self; - info.attr = &static_attr; - ret = route_map_apply(from_bgp->vpn_policy[afi] - .rmap[BGP_VPN_POLICY_DIR_TOVPN], - p, &info); - if (RMAP_DENYMATCH == ret) { - bgp_attr_flush(&static_attr); /* free any added parts */ - if (debug) - zlog_debug( - "%s: vrf %s route map \"%s\" says DENY, returning", - __func__, from_bgp->name_pretty, - from_bgp->vpn_policy[afi] - .rmap[BGP_VPN_POLICY_DIR_TOVPN] - ->name); - return; - } - } - if (debug && bgp_attr_get_ecommunity(&static_attr)) { char *s = ecommunity_ecom2str( bgp_attr_get_ecommunity(&static_attr), @@ -1608,29 +1616,62 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ /* * Add the vpn-policy rt-list */ - struct ecommunity *old_ecom; - struct ecommunity *new_ecom; /* Export with the 'from' instance's export RTs. */ /* If doing VRF-to-VRF leaking, strip existing RTs first. */ old_ecom = bgp_attr_get_ecommunity(&static_attr); + rtlist_ecom = from_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]; if (old_ecom) { new_ecom = ecommunity_dup(old_ecom); if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST], BGP_CONFIG_VRF_TO_VRF_EXPORT)) ecommunity_strip_rts(new_ecom); - new_ecom = ecommunity_merge( - new_ecom, from_bgp->vpn_policy[afi] - .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + if (rtlist_ecom) + new_ecom = ecommunity_merge(new_ecom, rtlist_ecom); if (!old_ecom->refcnt) ecommunity_free(&old_ecom); + } else if (rtlist_ecom) { + new_ecom = ecommunity_dup(rtlist_ecom); } else { - new_ecom = ecommunity_dup( - from_bgp->vpn_policy[afi] - .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + new_ecom = NULL; } + bgp_attr_set_ecommunity(&static_attr, new_ecom); + /* + * route map handling + */ + if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = to_bgp->peer_self; + info.attr = &static_attr; + ret = route_map_apply(from_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN], + p, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug("%s: vrf %s route map \"%s\" says DENY, returning", + __func__, from_bgp->name_pretty, + from_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN] + ->name); + return; + } + } + + new_ecom = bgp_attr_get_ecommunity(&static_attr); + if (!ecommunity_has_route_target(new_ecom)) { + ecommunity_free(&new_ecom); + if (debug) + zlog_debug("%s: %s skipping: waiting for a valid export rt list.", + __func__, from_bgp->name_pretty); + return; + } + if (debug && bgp_attr_get_ecommunity(&static_attr)) { char *s = ecommunity_ecom2str( bgp_attr_get_ecommunity(&static_attr), @@ -1884,20 +1925,20 @@ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */ if (!is_route_injectable_into_vpn(path_vrf)) return; - if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) { + if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, true)) { if (debug) zlog_debug("%s: skipping: %s", __func__, debugmsg); return; } - if (debug) - zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf); - - bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, - &(from_bgp->vpn_policy[afi].tovpn_rd)); + bn = bgp_safi_node_lookup(to_bgp->rib[afi][safi], safi, p, + &(from_bgp->vpn_policy[afi].tovpn_rd)); if (!bn) return; + if (debug) + zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf); + /* * vrf -> vpn * match original bpi imported from @@ -2056,6 +2097,7 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ uint32_t num_labels = 0; int nexthop_self_flag = 1; struct bgp_path_info *bpi_ultimate = NULL; + struct bgp_path_info *bpi; int origin_local = 0; struct bgp *src_vrf; struct interface *ifp; @@ -2145,6 +2187,20 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ community_strip_accept_own(&static_attr); + bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, NULL); + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->parent == path_vpn) + break; + } + + if (bpi && leak_update_nexthop_valid(to_bgp, bn, &static_attr, afi, safi, + path_vpn, bpi, src_vrf, p, debug)) + SET_FLAG(static_attr.nh_flags, BGP_ATTR_NH_VALID); + else + UNSET_FLAG(static_attr.nh_flags, BGP_ATTR_NH_VALID); + /* * Nexthop: stash and clear * @@ -2160,12 +2216,22 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ /* If the path has accept-own community and the source VRF * is valid, reset next-hop to self, to allow importing own * routes between different VRFs on the same node. - * Set the nh ifindex to VRF's interface, not the real interface. + */ + + if (src_bgp) + subgroup_announce_reset_nhop(nhfamily, &static_attr); + + bpi_ultimate = bgp_get_imported_bpi_ultimate(path_vpn); + + /* The nh ifindex may not be defined (when the route is + * imported from the network statement => BGP_ROUTE_STATIC) + * or to the real interface. + * Rewrite the nh ifindex to VRF's interface. * Let the kernel to decide with double lookup the real next-hop * interface when installing the route. */ - if (src_bgp) { - subgroup_announce_reset_nhop(nhfamily, &static_attr); + if (src_bgp || bpi_ultimate->sub_type == BGP_ROUTE_STATIC || + bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE) { ifp = if_get_vrf_loopback(src_vrf->vrf_id); if (ifp) static_attr.nh_ifindex = ifp->ifindex; @@ -2237,8 +2303,6 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ new_attr = bgp_attr_intern(&static_attr); bgp_attr_flush(&static_attr); - bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, NULL); - /* * ensure labels are copied * @@ -2253,9 +2317,6 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ */ if (!CHECK_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT)) { - /* work back to original route */ - bpi_ultimate = bgp_get_imported_bpi_ultimate(path_vpn); - /* * if original route was unicast, * then it did not arrive over vpn @@ -2665,7 +2726,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, edir = BGP_VPN_POLICY_DIR_TOVPN; for (afi = 0; afi < AFI_MAX; ++afi) { - if (!vpn_leak_to_vpn_active(bgp, afi, NULL)) + if (!vpn_leak_to_vpn_active(bgp, afi, NULL, false)) continue; if (withdraw) { diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 19b6f4e..cd25899 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -13,6 +13,7 @@ #include "bgpd/bgp_rd.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_label.h" #define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION) #define MPLS_LABEL_IS_NULL(label) \ @@ -112,7 +113,8 @@ static inline bool is_bgp_vrf_mplsvpn(struct bgp *bgp) } static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, - const char **pmsg) + const char **pmsg, + bool ignore_export_rt_list) { if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { @@ -132,8 +134,21 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, return 0; } - /* Is there an RT list set? */ - if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { + /* Before performing withdrawal, VPN activation is checked; however, + * when the route-map modifies the export route-target (RT) list, it + * becomes challenging to determine if VPN prefixes were previously + * present, or not. The 'ignore_export_rt_list' parameter will be + * used to force the withdraw operation by not checking the possible + * route-map changes. + * Of the 'ignore_export_rt_list' is set to false, check the following: + * - Is there an RT list set? + * - Is there a route-map that sets RT communities + */ + if (!ignore_export_rt_list && + !bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] && + (!bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] || + !bgp_route_map_has_extcommunity_rt( + bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]))) { if (pmsg) *pmsg = "rtlist tovpn not defined"; return 0; @@ -165,6 +180,25 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, return 0; } + /* Is there a "manual" export label that isn't allocated yet? */ + if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO) && + bgp_vrf->vpn_policy[afi].tovpn_label != BGP_PREVENT_VRF_2_VRF_LEAK && + bgp_vrf->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE && + (bgp_vrf->vpn_policy[afi].tovpn_label >= MPLS_LABEL_UNRESERVED_MIN && + !CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG))) { + if (!bgp_zebra_request_label_range(bgp_vrf->vpn_policy[afi] + .tovpn_label, + 1, false)) { + if (pmsg) + *pmsg = "manual label could not be allocated"; + return 0; + } + SET_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG); + } + return 1; } @@ -226,8 +260,7 @@ static inline void vpn_leak_prechange(enum vpn_policy_direction direction, vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi); } if ((direction == BGP_VPN_POLICY_DIR_TOVPN) && - vpn_leak_to_vpn_active(bgp_vrf, afi, NULL)) { - + vpn_leak_to_vpn_active(bgp_vrf, afi, NULL, true)) { vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi); } } diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index 0208a6f..3344e9e 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -511,8 +511,8 @@ static int bgp_init_snmp_stats(struct bgp *bgp) { if (is_bgp_vrf_mplsvpn(bgp)) { if (bgp->snmp_stats == NULL) { - bgp->snmp_stats = XCALLOC( - MTYPE_BGP, sizeof(struct bgp_snmp_stats)); + bgp->snmp_stats = XCALLOC(MTYPE_BGP_NAME, + sizeof(struct bgp_snmp_stats)); /* fix up added routes */ if (bgp->snmp_stats) { bgp->snmp_stats->routes_added = @@ -523,7 +523,7 @@ static int bgp_init_snmp_stats(struct bgp *bgp) } } else { if (bgp->snmp_stats) { - XFREE(MTYPE_BGP, bgp->snmp_stats); + XFREE(MTYPE_BGP_NAME, bgp->snmp_stats); bgp->snmp_stats = NULL; } } diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index d831c6a..dbb34b0 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -484,10 +484,23 @@ static void bgp_accept(struct event *thread) /* Dynamic neighbor has been created, let it proceed */ connection1->fd = bgp_sock; + if (bgp_set_socket_ttl(connection1) < 0) { + peer1->last_reset = PEER_DOWN_SOCKET_ERROR; + zlog_err("%s: Unable to set min/max TTL on peer %s (dynamic), error received: %s(%d)", + __func__, peer1->host, + safe_strerror(errno), errno); + return; + } + /* Set the user configured MSS to TCP socket */ if (CHECK_FLAG(peer1->flags, PEER_FLAG_TCP_MSS)) sockopt_tcp_mss_set(bgp_sock, peer1->tcp_mss); + frr_with_privs (&bgpd_privs) { + vrf_bind(peer1->bgp->vrf_id, bgp_sock, + bgp_get_bound_name(connection1)); + } + bgp_peer_reg_with_nht(peer1); bgp_fsm_change_status(connection1, Active); EVENT_OFF(connection1->t_start); @@ -693,7 +706,6 @@ int bgp_update_address(struct interface *ifp, const union sockunion *dst, { struct prefix *p, *sel, d; struct connected *connected; - struct listnode *node; int common; if (!sockunion2hostprefix(dst, &d)) @@ -702,7 +714,7 @@ int bgp_update_address(struct interface *ifp, const union sockunion *dst, sel = NULL; common = -1; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { p = connected->address; if (p->family != d.family) continue; diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 49cbbaf..8308838 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -26,6 +26,8 @@ PREDECL_RBTREE_UNIQ(bgp_nexthop_cache); /* BGP nexthop cache value structure. */ struct bgp_nexthop_cache { + afi_t afi; + /* The ifindex of the outgoing interface *if* it's a v6 LL */ ifindex_t ifindex_ipv6_ll; @@ -37,6 +39,18 @@ struct bgp_nexthop_cache { /* Nexthop number and nexthop linked list.*/ uint8_t nexthop_num; + + /* This flag is set to TRUE for a bnc that is gateway IP overlay index + * nexthop. + */ + bool is_evpn_gwip_nexthop; + + uint16_t change_flags; +#define BGP_NEXTHOP_CHANGED (1 << 0) +#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) +#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) + struct nexthop *nexthop; time_t last_update; uint16_t flags; @@ -70,27 +84,16 @@ struct bgp_nexthop_cache { */ #define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7) - uint16_t change_flags; - -#define BGP_NEXTHOP_CHANGED (1 << 0) -#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) -#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) -#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) + uint32_t srte_color; /* Back pointer to the cache tree this entry belongs to. */ struct bgp_nexthop_cache_head *tree; - uint32_t srte_color; struct prefix prefix; void *nht_info; /* In BGP, peer session */ LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; - - /* This flag is set to TRUE for a bnc that is gateway IP overlay index - * nexthop. - */ - bool is_evpn_gwip_nexthop; }; extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, diff --git a/bgpd/bgp_nhg.c b/bgpd/bgp_nhg.c new file mode 100644 index 0000000..bf0ba77 --- /dev/null +++ b/bgpd/bgp_nhg.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* BGP Nexthop Group Support + * Copyright (C) 2023 NVIDIA Corporation + * Copyright (C) 2023 6WIND + */ + +#include + +#include +#include +#include + + +/**************************************************************************** + * L3 NHGs are used for fast failover of nexthops in the dplane. These are + * the APIs for allocating L3 NHG ids. Management of the L3 NHG itself is + * left to the application using it. + * PS: Currently EVPN host routes is the only app using L3 NHG for fast + * failover of remote ES links. + ***************************************************************************/ +static bitfield_t bgp_nh_id_bitmap; +static uint32_t bgp_nhg_start; + +/* XXX - currently we do nothing on the callbacks */ +static void bgp_nhg_add_cb(const char *name) +{ +} + +static void bgp_nhg_modify_cb(const struct nexthop_group_cmd *nhgc) +{ +} + +static void bgp_nhg_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop) +{ +} + +static void bgp_nhg_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop) +{ +} + +static void bgp_nhg_del_cb(const char *name) +{ +} + +static void bgp_nhg_zebra_init(void) +{ + static bool bgp_nhg_zebra_inited; + + if (bgp_nhg_zebra_inited) + return; + + bgp_nhg_zebra_inited = true; + bgp_nhg_start = zclient_get_nhg_start(ZEBRA_ROUTE_BGP); + nexthop_group_init(bgp_nhg_add_cb, bgp_nhg_modify_cb, + bgp_nhg_add_nexthop_cb, bgp_nhg_del_nexthop_cb, + bgp_nhg_del_cb); +} + +void bgp_nhg_init(void) +{ + uint32_t id_max; + + id_max = MIN(ZEBRA_NHG_PROTO_SPACING - 1, 16 * 1024); + bf_init(bgp_nh_id_bitmap, id_max); + bf_assign_zero_index(bgp_nh_id_bitmap); + + if (BGP_DEBUG(nht, NHT) || BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("bgp nhg range %u - %u", bgp_nhg_start + 1, + bgp_nhg_start + id_max); +} + +void bgp_nhg_finish(void) +{ + bf_free(bgp_nh_id_bitmap); +} + +uint32_t bgp_nhg_id_alloc(void) +{ + uint32_t nhg_id = 0; + + bgp_nhg_zebra_init(); + bf_assign_index(bgp_nh_id_bitmap, nhg_id); + if (nhg_id) + nhg_id += bgp_nhg_start; + + return nhg_id; +} + +void bgp_nhg_id_free(uint32_t nhg_id) +{ + if (!nhg_id || (nhg_id <= bgp_nhg_start)) + return; + + nhg_id -= bgp_nhg_start; + + bf_release_index(bgp_nh_id_bitmap, nhg_id); +} diff --git a/bgpd/bgp_nhg.h b/bgpd/bgp_nhg.h new file mode 100644 index 0000000..370e8ab --- /dev/null +++ b/bgpd/bgp_nhg.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* BGP Nexthop Group Support + * Copyright (C) 2023 NVIDIA Corporation + * Copyright (C) 2023 6WIND + */ + +#ifndef _BGP_NHG_H +#define _BGP_NHG_H + +#include "nexthop_group.h" + +/* APIs for setting up and allocating L3 nexthop group ids */ +extern uint32_t bgp_nhg_id_alloc(void); +extern void bgp_nhg_id_free(uint32_t nhg_id); +extern void bgp_nhg_init(void); +void bgp_nhg_finish(void); + +#endif /* _BGP_NHG_H */ diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 60d6f74..e2c103b 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -386,6 +386,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, bnc = bnc_find(tree, &p, srte_color, ifindex); if (!bnc) { bnc = bnc_new(tree, &p, srte_color, ifindex); + bnc->afi = afi; bnc->bgp = bgp_nexthop; if (BGP_DEBUG(nht, NHT)) zlog_debug("Allocated bnc %pFX(%d)(%u)(%s) peer %p", @@ -405,7 +406,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, if (pi && is_route_parent_evpn(pi)) bnc->is_evpn_gwip_nexthop = true; - if (is_bgp_static_route) { + if (is_bgp_static_route && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); /* If we're toggling the type, re-register */ @@ -897,52 +898,60 @@ void bgp_nht_interface_events(struct peer *peer) bnc->ifindex_ipv6_ll, NULL); } -void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) +void bgp_nexthop_update(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr) { struct bgp_nexthop_cache_head *tree = NULL; struct bgp_nexthop_cache *bnc_nhc, *bnc_import; - struct bgp *bgp; - struct prefix match; - struct zapi_route nhr; + struct bgp *bgp, *bgp_default; + struct bgp_path_info *pi; + struct bgp_dest *dest; + safi_t safi; afi_t afi; - bgp = bgp_lookup_by_vrf_id(vrf_id); - if (!bgp) { - flog_err( - EC_BGP_NH_UPD, - "parse nexthop update: instance not found for vrf_id %u", - vrf_id); - return; - } - - if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) { - zlog_err("%s[%s]: Failure to decode nexthop update", __func__, - bgp->name_pretty); + if (!vrf->info) { + flog_err(EC_BGP_NH_UPD, + "parse nexthop update: instance not found for vrf_id %u", + vrf->vrf_id); return; } - afi = family2afi(match.family); + bgp = (struct bgp *)vrf->info; + afi = family2afi(match->family); tree = &bgp->nexthop_cache_table[afi]; - bnc_nhc = bnc_find(tree, &match, nhr.srte_color, 0); - if (!bnc_nhc) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug( - "parse nexthop update %pFX(%u)(%s): bnc info not found for nexthop cache", - &nhr.prefix, nhr.srte_color, bgp->name_pretty); - } else - bgp_process_nexthop_update(bnc_nhc, &nhr, false); + bnc_nhc = bnc_find(tree, match, nhr->srte_color, 0); + if (bnc_nhc) + bgp_process_nexthop_update(bnc_nhc, nhr, false); + else if (BGP_DEBUG(nht, NHT)) + zlog_debug("parse nexthop update %pFX(%u)(%s): bnc info not found for nexthop cache", + &nhr->prefix, nhr->srte_color, + bgp->name_pretty); tree = &bgp->import_check_table[afi]; - bnc_import = bnc_find(tree, &match, nhr.srte_color, 0); - if (!bnc_import) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug( - "parse nexthop update %pFX(%u)(%s): bnc info not found for import check", - &nhr.prefix, nhr.srte_color, bgp->name_pretty); - } else - bgp_process_nexthop_update(bnc_import, &nhr, true); + bnc_import = bnc_find(tree, match, nhr->srte_color, 0); + if (bnc_import) { + bgp_process_nexthop_update(bnc_import, nhr, true); + + bgp_default = bgp_get_default(); + safi = nhr->safi; + if (bgp != bgp_default && bgp->rib[afi][safi]) { + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, + match, NULL); + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) + if (pi->peer == bgp->peer_self && + pi->type == ZEBRA_ROUTE_BGP && + pi->sub_type == BGP_ROUTE_STATIC) + vpn_leak_from_vrf_update(bgp_default, + bgp, pi); + } + } else if (BGP_DEBUG(nht, NHT)) + zlog_debug("parse nexthop update %pFX(%u)(%s): bnc info not found for import check", + &nhr->prefix, nhr->srte_color, + bgp->name_pretty); /* * HACK: if any BGP route is dependant on an SR-policy that doesn't @@ -955,7 +964,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) * which should provide a better infrastructure to solve this issue in * a more efficient and elegant way. */ - if (nhr.srte_color == 0 && bnc_nhc) { + if (nhr->srte_color == 0 && bnc_nhc) { struct bgp_nexthop_cache *bnc_iter; frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], @@ -965,7 +974,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID)) continue; - bgp_process_nexthop_update(bnc_iter, &nhr, false); + bgp_process_nexthop_update(bnc_iter, nhr, false); } } } @@ -1170,6 +1179,11 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc) */ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc) { + struct bgp_nexthop_cache *import; + struct bgp_nexthop_cache *nexthop; + + struct bgp *bgp = bnc->bgp; + /* Check if we have already registered */ if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) return; @@ -1179,6 +1193,19 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc) return; } + import = bnc_find(&bgp->import_check_table[bnc->afi], &bnc->prefix, 0, + 0); + nexthop = bnc_find(&bgp->nexthop_cache_table[bnc->afi], &bnc->prefix, 0, + 0); + + /* + * If this entry has both a import and a nexthop entry + * then let's not send the unregister quite as of yet + * wait until we only have 1 left + */ + if (import && nexthop) + return; + sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_UNREGISTER); } @@ -1558,90 +1585,3 @@ void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) 0); } } - -/**************************************************************************** - * L3 NHGs are used for fast failover of nexthops in the dplane. These are - * the APIs for allocating L3 NHG ids. Management of the L3 NHG itself is - * left to the application using it. - * PS: Currently EVPN host routes is the only app using L3 NHG for fast - * failover of remote ES links. - ***************************************************************************/ -static bitfield_t bgp_nh_id_bitmap; -static uint32_t bgp_l3nhg_start; - -/* XXX - currently we do nothing on the callbacks */ -static void bgp_l3nhg_add_cb(const char *name) -{ -} - -static void bgp_l3nhg_modify_cb(const struct nexthop_group_cmd *nhgc) -{ -} - -static void bgp_l3nhg_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, - const struct nexthop *nhop) -{ -} - -static void bgp_l3nhg_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, - const struct nexthop *nhop) -{ -} - -static void bgp_l3nhg_del_cb(const char *name) -{ -} - -static void bgp_l3nhg_zebra_init(void) -{ - static bool bgp_l3nhg_zebra_inited; - if (bgp_l3nhg_zebra_inited) - return; - - bgp_l3nhg_zebra_inited = true; - bgp_l3nhg_start = zclient_get_nhg_start(ZEBRA_ROUTE_BGP); - nexthop_group_init(bgp_l3nhg_add_cb, bgp_l3nhg_modify_cb, - bgp_l3nhg_add_nexthop_cb, bgp_l3nhg_del_nexthop_cb, - bgp_l3nhg_del_cb); -} - - -void bgp_l3nhg_init(void) -{ - uint32_t id_max; - - id_max = MIN(ZEBRA_NHG_PROTO_SPACING - 1, 16 * 1024); - bf_init(bgp_nh_id_bitmap, id_max); - bf_assign_zero_index(bgp_nh_id_bitmap); - - if (BGP_DEBUG(nht, NHT) || BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("bgp l3_nhg range %u - %u", bgp_l3nhg_start + 1, - bgp_l3nhg_start + id_max); -} - -void bgp_l3nhg_finish(void) -{ - bf_free(bgp_nh_id_bitmap); -} - -uint32_t bgp_l3nhg_id_alloc(void) -{ - uint32_t nhg_id = 0; - - bgp_l3nhg_zebra_init(); - bf_assign_index(bgp_nh_id_bitmap, nhg_id); - if (nhg_id) - nhg_id += bgp_l3nhg_start; - - return nhg_id; -} - -void bgp_l3nhg_id_free(uint32_t nhg_id) -{ - if (!nhg_id || (nhg_id <= bgp_l3nhg_start)) - return; - - nhg_id -= bgp_l3nhg_start; - - bf_release_index(bgp_nh_id_bitmap, nhg_id); -} diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 0758a0c..e7c6fdc 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -7,9 +7,10 @@ #define _BGP_NHT_H /** - * bgp_parse_nexthop_update() - parse a nexthop update message from Zebra. + * bgp_nexthop_update() - process a nexthop update message from Zebra. */ -extern void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id); +extern void bgp_nexthop_update(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr); /** * bgp_find_or_add_nexthop() - lookup the nexthop cache table for the bnc @@ -78,12 +79,6 @@ extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); extern void evaluate_paths(struct bgp_nexthop_cache *bnc); -/* APIs for setting up and allocating L3 nexthop group ids */ -extern uint32_t bgp_l3nhg_id_alloc(void); -extern void bgp_l3nhg_id_free(uint32_t nhg_id); -extern void bgp_l3nhg_init(void); -void bgp_l3nhg_finish(void); - extern void bgp_nht_ifp_up(struct interface *ifp); extern void bgp_nht_ifp_down(struct interface *ifp); diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index b030c45..6447586 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -340,15 +340,14 @@ static void bgp_capability_orf_not_support(struct peer *peer, iana_afi_t afi, peer->host, afi, safi, type, mode); } -static const struct message orf_type_str[] = { - {ORF_TYPE_RESERVED, "Reserved"}, - {ORF_TYPE_PREFIX, "Prefixlist"}, - {0}}; +const struct message orf_type_str[] = { { ORF_TYPE_RESERVED, "Reserved" }, + { ORF_TYPE_PREFIX, "Prefixlist" }, + { 0 } }; -static const struct message orf_mode_str[] = {{ORF_MODE_RECEIVE, "Receive"}, - {ORF_MODE_SEND, "Send"}, - {ORF_MODE_BOTH, "Both"}, - {0}}; +const struct message orf_mode_str[] = { { ORF_MODE_RECEIVE, "Receive" }, + { ORF_MODE_SEND, "Send" }, + { ORF_MODE_BOTH, "Both" }, + { 0 } }; static int bgp_capability_orf_entry(struct peer *peer, struct capability_header *hdr) @@ -623,17 +622,17 @@ static int bgp_capability_llgr(struct peer *peer, /* Unlike other capability parsing routines, this one returns 0 on error */ static as_t bgp_capability_as4(struct peer *peer, struct capability_header *hdr) { - SET_FLAG(peer->cap, PEER_CAP_AS4_RCV); - if (hdr->length != CAPABILITY_CODE_AS4_LEN) { flog_err(EC_BGP_PKT_OPEN, "%s AS4 capability has incorrect data length %d", peer->host, hdr->length); - return 0; + return -1; } as_t as4 = stream_getl(BGP_INPUT(peer)); + SET_FLAG(peer->cap, PEER_CAP_AS4_RCV); + if (BGP_DEBUG(as4, AS4)) zlog_debug( "%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u", @@ -663,10 +662,8 @@ static int bgp_capability_addpath(struct peer *peer, struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + hdr->length; - SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); - /* Verify length is a multiple of 4 */ - if (hdr->length % 4) { + if (hdr->length % CAPABILITY_CODE_ADDPATH_LEN) { flog_warn( EC_BGP_CAPABILITY_INVALID_LENGTH, "Add Path: Received invalid length %d, non-multiple of 4", @@ -674,23 +671,38 @@ static int bgp_capability_addpath(struct peer *peer, return -1; } - while (stream_get_getp(s) + 4 <= end) { + SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); + + while (stream_get_getp(s) + CAPABILITY_CODE_ADDPATH_LEN <= end) { afi_t afi; safi_t safi; iana_afi_t pkt_afi = stream_getw(s); iana_safi_t pkt_safi = stream_getc(s); uint8_t send_receive = stream_getc(s); + /* If any other value (other than 1-3) is received, then + * the capability SHOULD be treated as not understood + * and ignored. + */ + if (!send_receive || send_receive > 3) { + flog_warn(EC_BGP_CAPABILITY_INVALID_DATA, + "Add Path: Received invalid send/receive value %u in Add Path capability", + send_receive); + continue; + } + if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%s OPEN has %s capability for afi/safi: %s/%s%s%s", - peer->host, - lookup_msg(capcode_str, hdr->code, NULL), - iana_afi2str(pkt_afi), iana_safi2str(pkt_safi), - (send_receive & BGP_ADDPATH_RX) ? ", receive" - : "", - (send_receive & BGP_ADDPATH_TX) ? ", transmit" - : ""); + zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s", + peer->host, + lookup_msg(capcode_str, hdr->code, NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), + CHECK_FLAG(send_receive, BGP_ADDPATH_RX) + ? ", receive" + : "", + CHECK_FLAG(send_receive, BGP_ADDPATH_TX) + ? ", transmit" + : ""); /* Convert AFI, SAFI to internal values, check. */ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { @@ -709,13 +721,19 @@ static int bgp_capability_addpath(struct peer *peer, continue; } - if (send_receive & BGP_ADDPATH_RX) + if (CHECK_FLAG(send_receive, BGP_ADDPATH_RX)) SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); - if (send_receive & BGP_ADDPATH_TX) + if (CHECK_FLAG(send_receive, BGP_ADDPATH_TX)) SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); } return 0; @@ -800,8 +818,6 @@ static int bgp_capability_hostname(struct peer *peer, size_t end = stream_get_getp(s) + hdr->length; uint8_t len; - SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); - len = stream_getc(s); if (stream_get_getp(s) + len > end) { flog_warn( @@ -859,6 +875,8 @@ static int bgp_capability_hostname(struct peer *peer, peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } + SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); + if (bgp_debug_neighbor_events(peer)) { zlog_debug("%s received hostname %s, domainname %s", peer->host, peer->hostname, peer->domainname); @@ -869,14 +887,16 @@ static int bgp_capability_hostname(struct peer *peer, static int bgp_capability_role(struct peer *peer, struct capability_header *hdr) { - SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV); if (hdr->length != CAPABILITY_CODE_ROLE_LEN) { flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, "Role: Received invalid length %d", hdr->length); return -1; } + uint8_t role = stream_getc(BGP_INPUT(peer)); + SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV); + peer->remote_role = role; return 0; } @@ -1020,6 +1040,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, BGP_NOTIFY_OPEN_MALFORMED_ATTR); return -1; } + break; /* we deliberately ignore unknown codes, see below */ default: break; @@ -1876,8 +1897,9 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN); } - /* Hostname capability */ - if (cmd_hostname_get()) { + /* FQDN capability */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN) + && cmd_hostname_get()) { SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); rcapp = stream_get_endp(s); /* Ptr to length placeholder */ @@ -1929,8 +1951,7 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, * or disable its use, and that switch MUST be off by default. */ if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION) || - CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY) || - peer->sort == BGP_PEER_IBGP) { + peer->sort == BGP_PEER_IBGP || peer->sub_sort == BGP_PEER_EBGP_OAD) { SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); rcapp = stream_get_endp(s); diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index a92c56d..34f4b76 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -108,5 +108,7 @@ extern void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json, json_object *json_neigh); extern as_t peek_for_as4_capability(struct peer *peer, uint16_t length); extern const struct message capcode_str[]; +extern const struct message orf_type_str[]; +extern const struct message orf_mode_str[]; #endif /* _QUAGGA_BGP_OPEN_H */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index cae82cb..1f808ee 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -24,6 +24,7 @@ #include "lib_errors.h" #include "bgpd/bgpd.h" +#include "bgpd/bgp_addpath.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_bmp.h" @@ -1211,8 +1212,14 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, unsigned long cap_len; uint16_t len; uint32_t gr_restart_time; + uint8_t addpath_afi_safi_count = 0; + bool adv_addpath_tx = false; + unsigned long number_of_orfs_p; + uint8_t number_of_orfs = 0; const char *capability = lookup_msg(capcode_str, capability_code, "Unknown"); + const char *hostname = cmd_hostname_get(); + const char *domainname = cmd_domainname_get(); if (!peer_established(peer->connection)) return; @@ -1232,7 +1239,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, /* Encode MP_EXT capability. */ switch (capability_code) { case CAPABILITY_CODE_SOFT_VERSION: - SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_ADV); stream_putc(s, action); stream_putc(s, CAPABILITY_CODE_SOFT_VERSION); cap_len = stream_get_endp(s); @@ -1263,6 +1269,9 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, : "Removing", capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_ADV, + action == CAPABILITY_ACTION_SET); break; case CAPABILITY_CODE_MP: stream_putc(s, action); @@ -1282,11 +1291,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, iana_safi2str(pkt_safi)); break; case CAPABILITY_CODE_RESTART: - if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) && - !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) - return; - - SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); stream_putc(s, action); stream_putc(s, CAPABILITY_CODE_RESTART); cap_len = stream_get_endp(s); @@ -1335,13 +1339,10 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + COND_FLAG(peer->cap, PEER_CAP_RESTART_ADV, + action == CAPABILITY_ACTION_SET); break; case CAPABILITY_CODE_LLGR: - if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV)) - return; - - SET_FLAG(peer->cap, PEER_CAP_LLGR_ADV); - stream_putc(s, action); stream_putc(s, CAPABILITY_CODE_LLGR); cap_len = stream_get_endp(s); @@ -1373,25 +1374,214 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, : "Removing", capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_LLGR_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_ADDPATH: + FOREACH_AFI_SAFI (afi, safi) { + if (peer->afc[afi][safi]) { + addpath_afi_safi_count++; + + /* Only advertise addpath TX if a feature that + * will use it is + * configured */ + if (peer->addpath_type[afi][safi] != + BGP_ADDPATH_NONE) + adv_addpath_tx = true; + + /* If we have enabled labeled unicast, we MUST check + * against unicast SAFI because addpath IDs are + * allocated under unicast SAFI, the same as the RIB + * is managed in unicast SAFI. + */ + if (safi == SAFI_LABELED_UNICAST) + if (peer->addpath_type[afi][SAFI_UNICAST] != + BGP_ADDPATH_NONE) + adv_addpath_tx = true; + } + } + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ADDPATH); + stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * + addpath_afi_safi_count); + + FOREACH_AFI_SAFI (afi, safi) { + if (peer->afc[afi][safi]) { + bool adv_addpath_rx = + !CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_DISABLE_ADDPATH_RX); + uint8_t flags = 0; + + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, + &pkt_safi); + + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + + if (adv_addpath_rx) { + SET_FLAG(flags, BGP_ADDPATH_RX); + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_ADV); + } else { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_ADV); + } + + if (adv_addpath_tx) { + SET_FLAG(flags, BGP_ADDPATH_TX); + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_ADV); + if (safi == SAFI_LABELED_UNICAST) + SET_FLAG(peer->af_cap[afi] + [SAFI_UNICAST], + PEER_CAP_ADDPATH_AF_TX_ADV); + } else { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_ADV); + } + + stream_putc(s, flags); + } + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV, + action == CAPABILITY_ACTION_SET); break; - case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_ORF: + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ORF); + cap_len = stream_get_endp(s); + stream_putc(s, 0); + + stream_putw(s, pkt_afi); /* Address Family Identifier */ + stream_putc(s, 0); /* Reserved */ + stream_putc(s, + pkt_safi); /* Subsequent Address Family Identifier */ + + number_of_orfs_p = + stream_get_endp(s); /* Number of ORFs pointer */ + stream_putc(s, 0); /* Number of ORFs */ + + /* Address Prefix ORF */ + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORF_PREFIX_SM) || + CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORF_PREFIX_RM)) { + stream_putc(s, ORF_TYPE_PREFIX); + + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORF_PREFIX_SM) && + CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORF_PREFIX_RM)) { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + stream_putc(s, ORF_MODE_BOTH); + } else if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORF_PREFIX_SM)) { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + stream_putc(s, ORF_MODE_SEND); + } else { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + stream_putc(s, ORF_MODE_RECEIVE); + } + number_of_orfs++; + } else { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + } + + /* Total Number of ORFs. */ + stream_putc_at(s, number_of_orfs_p, number_of_orfs); + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + break; + case CAPABILITY_CODE_FQDN: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_FQDN); + cap_len = stream_get_endp(s); + stream_putc(s, 0); /* Capability Length */ + + len = strlen(hostname); + if (len > BGP_MAX_HOSTNAME) + len = BGP_MAX_HOSTNAME; + + stream_putc(s, len); + stream_put(s, hostname, len); + + if (domainname) { + len = strlen(domainname); + if (len > BGP_MAX_HOSTNAME) + len = BGP_MAX_HOSTNAME; + + stream_putc(s, len); + stream_put(s, domainname, len); + } else + stream_putc(s, 0); + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: - case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: - case CAPABILITY_CODE_FQDN: case CAPABILITY_CODE_ENHE: case CAPABILITY_CODE_EXT_MESSAGE: break; case CAPABILITY_CODE_ROLE: - if (peer->local_role != ROLE_UNDEFINED) { - SET_FLAG(peer->cap, PEER_CAP_ROLE_ADV); - stream_putc(s, action); - stream_putc(s, CAPABILITY_CODE_ROLE); - stream_putc(s, CAPABILITY_CODE_ROLE_LEN); - stream_putc(s, peer->local_role); - } + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ROLE); + stream_putc(s, CAPABILITY_CODE_ROLE_LEN); + stream_putc(s, peer->local_role); + COND_FLAG(peer->cap, PEER_CAP_ROLE_ADV, + action == CAPABILITY_ACTION_SET); break; default: break; @@ -2155,7 +2345,8 @@ static int bgp_update_receive(struct peer_connection *connection, "%pBP rcvd UPDATE with errors in attr(s)!! Withdrawing route.", peer); - if (ret && bgp_debug_update(peer, NULL, NULL, 1)) { + if (ret && bgp_debug_update(peer, NULL, NULL, 1) && + BGP_DEBUG(update, UPDATE_DETAIL)) { zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); peer->rcvd_attr_printed = 1; @@ -2190,7 +2381,7 @@ static int bgp_update_receive(struct peer_connection *connection, } } - if (BGP_DEBUG(update, UPDATE_IN)) + if (BGP_DEBUG(update, UPDATE_IN) && BGP_DEBUG(update, UPDATE_DETAIL)) zlog_debug("%pBP rcvd UPDATE wlen %d attrlen %d alen %d", peer, withdraw_len, attribute_len, update_len); @@ -2872,6 +3063,304 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, return BGP_PACKET_NOOP; } +static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + size_t len = end - data; + afi_t afi; + safi_t safi; + + if (action == CAPABILITY_ACTION_SET) { + if (len % CAPABILITY_CODE_ADDPATH_LEN) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Add Path: Received invalid length %zu, non-multiple of 4", + len); + return; + } + + SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); + + while (data + CAPABILITY_CODE_ADDPATH_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct bgp_addpath_capability bac; + + memcpy(&bac, data, sizeof(bac)); + pkt_afi = ntohs(bac.afi); + pkt_safi = safi_int2iana(bac.safi); + + /* If any other value (other than 1-3) is received, + * then the capability SHOULD be treated as not + * understood and ignored. + */ + if (!bac.flags || bac.flags > 3) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Add Path: Received invalid send/receive value %u in Add Path capability", + bac.flags); + goto ignore; + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s", + peer->host, + lookup_msg(capcode_str, hdr->code, + NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), + (bac.flags & BGP_ADDPATH_RX) + ? ", receive" + : "", + (bac.flags & BGP_ADDPATH_TX) + ? ", transmit" + : ""); + + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, + &safi)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto ignore; + } else if (!peer->afc[afi][safi]) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto ignore; + } + + if (CHECK_FLAG(bac.flags, BGP_ADDPATH_RX)) + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + + if (CHECK_FLAG(bac.flags, BGP_ADDPATH_TX)) + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + +ignore: + data += CAPABILITY_CODE_ADDPATH_LEN; + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + } + + UNSET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); + } +} + +static void bgp_dynamic_capability_orf(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + size_t len = end - data; + + struct capability_mp_data mpc; + uint8_t num; + iana_afi_t pkt_afi; + afi_t afi; + iana_safi_t pkt_safi; + safi_t safi; + uint8_t type; + uint8_t mode; + uint16_t sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV; + uint16_t rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV; + int i; + + if (data + CAPABILITY_CODE_ORF_LEN > end) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "ORF: Received invalid length %zu, less than %d", len, + CAPABILITY_CODE_ORF_LEN); + return; + } + + /* ORF Entry header */ + memcpy(&mpc, data, sizeof(mpc)); + data += sizeof(mpc); + num = *data++; + pkt_afi = ntohs(mpc.afi); + pkt_safi = mpc.safi; + + /* Convert AFI, SAFI to internal values, check. */ + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { + zlog_info("%pBP Addr-family %d/%d not supported. Ignoring the ORF capability", + peer, pkt_afi, pkt_safi); + return; + } + + /* validate number field */ + if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) { + zlog_info("%pBP ORF Capability entry length error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + + if (action == CAPABILITY_ACTION_UNSET) { + UNSET_FLAG(peer->af_cap[afi][safi], sm_cap); + UNSET_FLAG(peer->af_cap[afi][safi], rm_cap); + return; + } + + for (i = 0; i < num; i++) { + if (data + 1 > end) { + flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH, + "%pBP ORF Capability entry length (type) error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + type = *data++; + + if (data + 1 > end) { + flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH, + "%pBP ORF Capability entry length (mode) error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + mode = *data++; + + /* ORF Mode error check */ + switch (mode) { + case ORF_MODE_BOTH: + case ORF_MODE_SEND: + case ORF_MODE_RECEIVE: + break; + default: + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP Addr-family %d/%d has ORF type/mode %d/%d not supported", + peer, afi, safi, type, mode); + continue; + } + + if (!((afi == AFI_IP && safi == SAFI_UNICAST) || + (afi == AFI_IP && safi == SAFI_MULTICAST) || + (afi == AFI_IP6 && safi == SAFI_UNICAST))) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP Addr-family %d/%d unsupported AFI/SAFI received", + peer, afi, safi); + continue; + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP OPEN has %s ORF capability as %s for afi/safi: %s/%s", + peer, lookup_msg(orf_type_str, type, NULL), + lookup_msg(orf_mode_str, mode, NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + switch (mode) { + case ORF_MODE_BOTH: + SET_FLAG(peer->af_cap[afi][safi], sm_cap); + SET_FLAG(peer->af_cap[afi][safi], rm_cap); + break; + case ORF_MODE_SEND: + SET_FLAG(peer->af_cap[afi][safi], sm_cap); + UNSET_FLAG(peer->af_cap[afi][safi], rm_cap); + break; + case ORF_MODE_RECEIVE: + SET_FLAG(peer->af_cap[afi][safi], rm_cap); + UNSET_FLAG(peer->af_cap[afi][safi], sm_cap); + break; + } + } +} + +static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + char str[BGP_MAX_HOSTNAME + 1] = {}; + uint8_t len; + + if (action == CAPABILITY_ACTION_SET) { + /* hostname */ + if (data + 1 > end) { + zlog_err("%pBP: Received invalid FQDN capability (host name length)", + peer); + return; + } + + len = *data; + if (data + len > end) { + zlog_err("%pBP: Received invalid FQDN capability length (host name) %d", + peer, hdr->length); + return; + } + data++; + + if (len > BGP_MAX_HOSTNAME) { + memcpy(&str, data, BGP_MAX_HOSTNAME); + str[BGP_MAX_HOSTNAME] = '\0'; + } else if (len) { + memcpy(&str, data, len); + str[len] = '\0'; + } + data += len; + + if (len) { + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + + peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); + } + + if (data + 1 > end) { + zlog_err("%pBP: Received invalid FQDN capability (domain name length)", + peer); + return; + } + + /* domainname */ + len = *data; + if (data + len > end) { + zlog_err("%pBP: Received invalid FQDN capability length (domain name) %d", + peer, len); + return; + } + data++; + + if (len > BGP_MAX_HOSTNAME) { + memcpy(&str, data, BGP_MAX_HOSTNAME); + str[BGP_MAX_HOSTNAME] = '\0'; + } else if (len) { + memcpy(&str, data, len); + str[len] = '\0'; + } + /* data += len; In case new code is ever added */ + + if (len) { + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + + peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); + } + + SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); + } else { + UNSET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + } +} + static void bgp_dynamic_capability_llgr(uint8_t *pnt, int action, struct capability_header *hdr, struct peer *peer) @@ -3121,6 +3610,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, zlog_err("%pBP: Capability length error", peer); bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); + pnt += length; return BGP_Stop; } action = *pnt; @@ -3133,7 +3623,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, action); bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return BGP_Stop; + goto done; } if (bgp_debug_neighbor_events(peer)) @@ -3145,12 +3635,13 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, zlog_err("%pBP: Capability length error", peer); bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); + pnt += length; return BGP_Stop; } /* Ignore capability when override-capability is set. */ if (CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - continue; + goto done; capability = lookup_msg(capcode_str, hdr->code, "Unknown"); @@ -3165,7 +3656,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, peer, capability, sizeof(struct capability_mp_data), hdr->length); - return BGP_Stop; + goto done; } memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data)); @@ -3180,7 +3671,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, peer, capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); - continue; + goto done; } /* Address family check. */ @@ -3207,7 +3698,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, if (peer_active_nego(peer)) bgp_clear_route(peer, afi, safi); else - return BGP_Stop; + goto done; } break; case CAPABILITY_CODE_RESTART: @@ -3217,7 +3708,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return BGP_Stop; + goto done; } bgp_dynamic_capability_graceful_restart(pnt, action, @@ -3226,13 +3717,19 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, case CAPABILITY_CODE_LLGR: bgp_dynamic_capability_llgr(pnt, action, hdr, peer); break; - case CAPABILITY_CODE_REFRESH: + case CAPABILITY_CODE_ADDPATH: + bgp_dynamic_capability_addpath(pnt, action, hdr, peer); + break; case CAPABILITY_CODE_ORF: + bgp_dynamic_capability_orf(pnt, action, hdr, peer); + break; + case CAPABILITY_CODE_FQDN: + bgp_dynamic_capability_fqdn(pnt, action, hdr, peer); + break; + case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: - case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: - case CAPABILITY_CODE_FQDN: case CAPABILITY_CODE_ENHE: case CAPABILITY_CODE_EXT_MESSAGE: break; @@ -3243,7 +3740,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return BGP_Stop; + goto done; } uint8_t role; @@ -3265,6 +3762,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, break; } +done: pnt += hdr->length + 3; } diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index ed143d9..cb16c4d 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -134,7 +134,7 @@ struct bgp_pbr_entry_main { struct bgp_pbr_interface { RB_ENTRY(bgp_pbr_interface) id_entry; - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; }; RB_HEAD(bgp_pbr_interface_head, bgp_pbr_interface); diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index e38c2fa..93dda17 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -41,8 +41,8 @@ struct rd_as { struct rd_ip { uint16_t type; - struct in_addr ip; uint16_t val; + struct in_addr ip; }; #ifdef ENABLE_BGP_VNC diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 3a850a4..38983e2 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -28,6 +28,8 @@ #include "lib/json.h" #include "lib_errors.h" #include "zclient.h" +#include "frrdistance.h" + #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" @@ -85,6 +87,11 @@ DEFINE_HOOK(bgp_rpki_prefix_status, const struct prefix *prefix), (peer, attr, prefix)); +DEFINE_HOOK(bgp_route_update, + (struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *bn, + struct bgp_path_info *old_route, struct bgp_path_info *new_route), + (bgp, afi, safi, bn, old_route, new_route)); + /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; @@ -113,7 +120,7 @@ DEFINE_HOOK(bgp_process, (bgp, afi, safi, bn, peer, withdraw)); /** Test if path is suppressed. */ -static bool bgp_path_suppressed(struct bgp_path_info *pi) +bool bgp_path_suppressed(struct bgp_path_info *pi) { if (pi->extra == NULL || pi->extra->aggr_suppressors == NULL) return false; @@ -256,6 +263,10 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) XFREE(MTYPE_BGP_ROUTE_EXTRA_FS, e->flowspec); if (e->vrfleak) XFREE(MTYPE_BGP_ROUTE_EXTRA_VRFLEAK, e->vrfleak); +#ifdef ENABLE_BGP_VNC + if (e->vnc) + XFREE(MTYPE_BGP_ROUTE_EXTRA_VNC, e->vnc); +#endif XFREE(MTYPE_BGP_ROUTE_EXTRA, *extra); } @@ -606,6 +617,8 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, struct attr *newattr, *existattr; enum bgp_peer_sort new_sort; enum bgp_peer_sort exist_sort; + enum bgp_peer_sub_sort new_sub_sort; + enum bgp_peer_sub_sort exist_sub_sort; uint32_t new_pref; uint32_t exist_pref; uint32_t new_med; @@ -1140,26 +1153,34 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 7. Peer type check. */ new_sort = peer_new->sort; exist_sort = peer_exist->sort; + new_sub_sort = peer_new->sub_sort; + exist_sub_sort = peer_exist->sub_sort; - if (new_sort == BGP_PEER_EBGP - && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) { + if (new_sort == BGP_PEER_EBGP && + (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED || + exist_sub_sort == BGP_PEER_EBGP_OAD)) { *reason = bgp_path_selection_peer; if (debug) - zlog_debug( - "%s: %s wins over %s due to eBGP peer > iBGP peer", - pfx_buf, new_buf, exist_buf); + zlog_debug("%s: %s wins over %s due to eBGP peer > %s peer", + pfx_buf, new_buf, exist_buf, + (exist_sub_sort == BGP_PEER_EBGP_OAD) + ? "eBGP-OAD" + : "iBGP"); if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) return 1; peer_sort_ret = 1; } - if (exist_sort == BGP_PEER_EBGP - && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) { + if (exist_sort == BGP_PEER_EBGP && + (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED || + new_sub_sort == BGP_PEER_EBGP_OAD)) { *reason = bgp_path_selection_peer; if (debug) - zlog_debug( - "%s: %s loses to %s due to iBGP peer < eBGP peer", - pfx_buf, new_buf, exist_buf); + zlog_debug("%s: %s loses to %s due to %s peer < eBGP peer", + pfx_buf, new_buf, exist_buf, + (exist_sub_sort == BGP_PEER_EBGP_OAD) + ? "eBGP-OAD" + : "iBGP"); if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) return 0; peer_sort_ret = 0; @@ -2162,9 +2183,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, * configured for default-originate */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) { - if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) - return false; - else if (p->family == AF_INET6 && p->prefixlen == 0) + if ((p->family == AF_INET || p->family == AF_INET6) && p->prefixlen == 0) return false; } @@ -2299,8 +2318,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, /* Remove MED if its an EBGP peer - will get overwritten by route-maps */ - if (peer->sort == BGP_PEER_EBGP - && attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { + if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD && + attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { if (from != bgp->peer_self && !transparent && !CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) @@ -2514,8 +2533,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, return false; if (bgp_in_graceful_shutdown(bgp)) { - if (peer->sort == BGP_PEER_IBGP - || peer->sort == BGP_PEER_CONFED) { + if (peer->sort == BGP_PEER_IBGP || + peer->sort == BGP_PEER_CONFED || + peer->sub_sort == BGP_PEER_EBGP_OAD) { attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); attr->local_pref = BGP_GSHUT_LOCAL_PREF; } else { @@ -2645,17 +2665,25 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, /* If this is an iBGP, send Origin Validation State (OVS) * extended community (rfc8097). + * draft-uttaro-idr-bgp-oad states: + * For example, the Origin Validation State Extended Community, + * defined as non-transitive in [RFC8097], can be advertised to + * peers in the same OAD. */ - if (peer->sort == BGP_PEER_IBGP) { + if ((peer->sort == BGP_PEER_IBGP || + peer->sub_sort == BGP_PEER_EBGP_OAD) && + peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI)) { enum rpki_states rpki_state = RPKI_NOT_BEING_USED; rpki_state = hook_call(bgp_rpki_prefix_status, peer, attr, p); if (rpki_state != RPKI_NOT_BEING_USED) - bgp_attr_set_ecommunity( - attr, ecommunity_add_origin_validation_state( - rpki_state, - bgp_attr_get_ecommunity(attr))); + bgp_attr_set_ecommunity(attr, + ecommunity_add_origin_validation_state( + rpki_state, + bgp_attr_get_ecommunity( + attr))); } /* @@ -2816,18 +2844,18 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, /* reap REMOVED routes, if needs be * selected route must stay for a while longer though */ - if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) && - (pi != old_select)) { - dest = bgp_path_info_reap(dest, pi); - assert(dest); - } - if (debug) zlog_debug( "%s: %pBD(%s) pi from %s in holddown", __func__, dest, bgp->name_pretty, pi->peer->host); + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) && + (pi != old_select)) { + dest = bgp_path_info_reap(dest, pi); + assert(dest); + } + continue; } @@ -2958,7 +2986,7 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, { const struct prefix *p; struct peer *onlypeer; - struct attr attr; + struct attr attr = { 0 }, *pattr = &attr; struct bgp *bgp; bool advertise; @@ -2986,26 +3014,30 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, advertise = bgp_check_advertise(bgp, dest, safi); if (selected) { - if (subgroup_announce_check(dest, selected, subgrp, p, &attr, + if (subgroup_announce_check(dest, selected, subgrp, p, pattr, NULL)) { /* Route is selected, if the route is already installed * in FIB, then it is advertised */ if (advertise) { if (!bgp_check_withdrawal(bgp, dest, safi)) { - struct attr *adv_attr = - bgp_attr_intern(&attr); - - bgp_adj_out_set_subgroup(dest, subgrp, - adv_attr, - selected); - } else + if (!bgp_adj_out_set_subgroup(dest, + subgrp, + pattr, + selected)) + bgp_attr_flush(pattr); + } else { bgp_adj_out_unset_subgroup( dest, subgrp, 1, addpath_tx_id); - } - } else + bgp_attr_flush(pattr); + } + } else + bgp_attr_flush(pattr); + } else { bgp_adj_out_unset_subgroup(dest, subgrp, 1, addpath_tx_id); + bgp_attr_flush(pattr); + } } /* If selected is NULL we must withdraw the path using addpath_tx_id */ @@ -3434,6 +3466,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, &bgp->t_rmap_def_originate_eval); } + /* TODO BMP insert rib update hook */ if (old_select) bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED); if (new_select) { @@ -3446,6 +3479,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); } + /* call bmp hook for loc-rib route update / withdraw after flags were + * set + */ + if (old_select || new_select) { + hook_call(bgp_route_update, bgp, afi, safi, dest, old_select, + new_select); + } + + #ifdef ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (old_select != new_select) { @@ -3463,14 +3505,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, } #endif - group_announce_route(bgp, afi, safi, dest, new_select); - - /* unicast routes must also be annouced to labeled-unicast update-groups - */ - if (safi == SAFI_UNICAST) - group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest, - new_select); - /* FIB update. */ if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) && !bgp_option_check(BGP_OPT_NO_FIB)) { @@ -3486,7 +3520,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, */ if (old_select && is_route_parent_evpn(old_select)) - bgp_zebra_withdraw(p, old_select, bgp, safi); + bgp_zebra_withdraw(p, old_select, bgp, afi, + safi); bgp_zebra_announce(dest, p, new_select, bgp, afi, safi); } else { @@ -3496,10 +3531,20 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, || old_select->sub_type == BGP_ROUTE_AGGREGATE || old_select->sub_type == BGP_ROUTE_IMPORTED)) - bgp_zebra_withdraw(p, old_select, bgp, safi); + bgp_zebra_withdraw(p, old_select, bgp, afi, + safi); } } + group_announce_route(bgp, afi, safi, dest, new_select); + + /* unicast routes must also be annouced to labeled-unicast update-groups + */ + if (safi == SAFI_UNICAST) + group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest, + new_select); + + bgp_process_evpn_route_injection(bgp, afi, safi, dest, new_select, old_select); @@ -4151,7 +4196,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, int aspath_loop_count = 0; struct bgp_dest *dest; struct bgp *bgp; - struct attr new_attr; + struct attr new_attr = {}; struct attr *attr_new; struct bgp_path_info *pi; struct bgp_path_info *new = NULL; @@ -4184,10 +4229,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (orig_safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; - memset(&new_attr, 0, sizeof(new_attr)); - new_attr.label_index = BGP_INVALID_LABEL_INDEX; - new_attr.label = MPLS_INVALID_LABEL; - bgp = peer->bgp; dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); /* TODO: Check to see if we can get rid of "is_valid_label" */ @@ -4397,7 +4438,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (pi && pi->attr->rmap_table_id != new_attr.rmap_table_id) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) /* remove from RIB previous entry */ - bgp_zebra_withdraw(p, pi, bgp, safi); + bgp_zebra_withdraw(p, pi, bgp, afi, safi); } if (peer->sort == BGP_PEER_EBGP) { @@ -4490,12 +4531,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, hook_call(bgp_process, bgp, afi, safi, dest, peer, true); /* Same attribute comes in. */ - if (!CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) - && same_attr - && (!has_valid_label - || memcmp(&(bgp_path_info_extra_get(pi))->label, label, - num_labels * sizeof(mpls_label_t)) - == 0)) { + if (!CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) && same_attr && + (!has_valid_label || + (bgp_path_info_extra_get(pi) && + bgp_labels_same((const mpls_label_t *)pi->extra->label, + pi->extra->num_labels, label, + num_labels)))) { if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) && peer->sort == BGP_PEER_EBGP @@ -4680,7 +4721,9 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Update MPLS label */ if (has_valid_label) { extra = bgp_path_info_extra_get(pi); - if (extra->label != label) { + if (!bgp_labels_same((const mpls_label_t *)extra->label, + extra->num_labels, label, + num_labels)) { memcpy(&extra->label, label, num_labels * sizeof(mpls_label_t)); extra->num_labels = num_labels; @@ -4761,9 +4804,10 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, BGP_PATH_VALID); } else { if (BGP_DEBUG(nht, NHT)) { - zlog_debug("%s(%pI4): NH unresolved", + zlog_debug("%s(%pI4): NH unresolved for existing %pFX pi %p flags 0x%x", __func__, - (in_addr_t *)&attr_new->nexthop); + (in_addr_t *)&attr_new->nexthop, + p, pi, pi->flags); } bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); @@ -4808,10 +4852,23 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * updating * the attributes for the route in the VNI(s). */ - if (safi == SAFI_EVPN && - (!same_attr || force_evpn_import) && - CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - bgp_evpn_import_route(bgp, afi, safi, p, pi); + if (safi == SAFI_EVPN) { + if ((!same_attr || force_evpn_import) && + CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + bgp_evpn_import_route(bgp, afi, safi, p, pi); + + /* If existing path is marked invalid then unimport the + * path from EVPN prefix. This will ensure EVPN route + * has only valid paths and path refcount maintained in + * EVPN nexthop is decremented appropriately. + */ + else if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug("%s unimport EVPN %pFX as pi %p is not VALID", + __func__, p, pi); + bgp_evpn_unimport_route(bgp, afi, safi, p, pi); + } + } /* Process change. */ bgp_aggregate_increment(bgp, p, pi, afi, safi); @@ -4865,7 +4922,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Update MPLS label */ if (has_valid_label) { extra = bgp_path_info_extra_get(new); - if (extra->label != label) { + if (!bgp_labels_same((const mpls_label_t *)extra->label, + extra->num_labels, label, num_labels)) { memcpy(&extra->label, label, num_labels * sizeof(mpls_label_t)); extra->num_labels = num_labels; @@ -5146,7 +5204,7 @@ void bgp_withdraw(struct peer *peer, const struct prefix *p, } void bgp_default_originate(struct peer *peer, afi_t afi, safi_t safi, - int withdraw) + bool withdraw) { struct update_subgroup *subgrp; subgrp = peer_subgroup(peer, afi, safi); @@ -5960,10 +6018,10 @@ bool bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter) if (peer->sort == BGP_PEER_IBGP) return true; - if (peer->sort == BGP_PEER_EBGP - && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter) - || FILTER_LIST_OUT_NAME(filter) - || DISTRIBUTE_OUT_NAME(filter))) + if (peer->sort == BGP_PEER_EBGP && + (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter) || + FILTER_LIST_OUT_NAME(filter) || DISTRIBUTE_OUT_NAME(filter) || + UNSUPPRESS_MAP_NAME(filter))) return true; return false; } @@ -5982,7 +6040,7 @@ bool bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter) } static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, - safi_t safi) + afi_t afi, safi_t safi) { struct bgp_dest *dest; struct bgp_path_info *pi; @@ -6006,7 +6064,8 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, || pi->sub_type == BGP_ROUTE_IMPORTED)) { if (bgp_fibupd_safi(safi)) - bgp_zebra_withdraw(p, pi, bgp, safi); + bgp_zebra_withdraw(p, pi, bgp, afi, + safi); } dest = bgp_path_info_reap(dest, pi); @@ -6024,7 +6083,7 @@ void bgp_cleanup_routes(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; ++afi) { if (afi == AFI_L2VPN) continue; - bgp_cleanup_table(bgp, bgp->rib[afi][SAFI_UNICAST], + bgp_cleanup_table(bgp, bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST); /* * VPN and ENCAP and EVPN tables are two-level (RD is top level) @@ -6036,7 +6095,7 @@ void bgp_cleanup_routes(struct bgp *bgp) dest = bgp_route_next(dest)) { table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { - bgp_cleanup_table(bgp, table, safi); + bgp_cleanup_table(bgp, table, afi, safi); bgp_table_finish(&table); bgp_dest_set_bgp_table_info(dest, NULL); dest = bgp_dest_unlock_node(dest); @@ -6049,7 +6108,7 @@ void bgp_cleanup_routes(struct bgp *bgp) dest = bgp_route_next(dest)) { table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { - bgp_cleanup_table(bgp, table, safi); + bgp_cleanup_table(bgp, table, afi, safi); bgp_table_finish(&table); bgp_dest_set_bgp_table_info(dest, NULL); dest = bgp_dest_unlock_node(dest); @@ -6063,7 +6122,7 @@ void bgp_cleanup_routes(struct bgp *bgp) dest = bgp_route_next(dest)) { table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { - bgp_cleanup_table(bgp, table, SAFI_EVPN); + bgp_cleanup_table(bgp, table, afi, SAFI_EVPN); bgp_table_finish(&table); bgp_dest_set_bgp_table_info(dest, NULL); dest = bgp_dest_unlock_node(dest); @@ -6239,13 +6298,14 @@ static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi, struct bgp_path_info *bpi, const struct prefix *p, struct bgp_dest *dest, - struct bgp *bgp) + struct bgp *bgp, + struct bgp *bgp_nexthop) { /* Nexthop reachability check. */ if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST) { if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) { - if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, bpi, - NULL, 0, p)) + if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi, + bpi, NULL, 0, p)) bgp_path_info_set_flag(dest, bpi, BGP_PATH_VALID); else { @@ -6283,7 +6343,7 @@ static void bgp_static_free(struct bgp_static *bgp_static) route_map_counter_decrement(bgp_static->rmap.map); if (bgp_static->prd_pretty) - XFREE(MTYPE_BGP, bgp_static->prd_pretty); + XFREE(MTYPE_BGP_NAME, bgp_static->prd_pretty); XFREE(MTYPE_ATTR, bgp_static->eth_s_id); XFREE(MTYPE_BGP_STATIC, bgp_static); } @@ -6303,6 +6363,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, mpls_label_t label = 0; #endif uint32_t num_labels = 0; + struct bgp *bgp_nexthop = bgp; assert(bgp_static); @@ -6457,9 +6518,11 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, &pi->extra->label[0]); } #endif + if (pi->extra && pi->extra->vrfleak->bgp_orig) + bgp_nexthop = pi->extra->vrfleak->bgp_orig; bgp_nexthop_reachability_check(afi, safi, pi, p, dest, - bgp); + bgp, bgp_nexthop); /* Process change. */ bgp_aggregate_increment(bgp, p, pi, afi, safi); @@ -6509,7 +6572,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, #endif } - bgp_nexthop_reachability_check(afi, safi, new, p, dest, bgp); + bgp_nexthop_reachability_check(afi, safi, new, p, dest, bgp, bgp); /* Aggregate address increment. */ bgp_aggregate_increment(bgp, p, new, afi, safi); @@ -7322,8 +7385,9 @@ static void bgp_aggregate_install( * If the aggregate information has not changed * no need to re-install it again. */ - if (pi && bgp_aggregate_info_same(pi, origin, aspath, community, - ecommunity, lcommunity)) { + if (pi && (!aggregate->rmap.changed && + bgp_aggregate_info_same(pi, origin, aspath, community, + ecommunity, lcommunity))) { bgp_dest_unlock_node(dest); if (aspath) @@ -7577,7 +7641,6 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, */ aggregate->count = 0; aggregate->incomplete_origin_count = 0; - aggregate->incomplete_origin_count = 0; aggregate->egp_origin_count = 0; /* ORIGIN attribute: If at least one route among routes that are @@ -7752,6 +7815,9 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, lcommunity = lcommunity_dup(aggregate->lcommunity); } + /* Unimport suppressed routes from EVPN */ + bgp_aggr_supp_withdraw_from_evpn(bgp, afi, safi); + bgp_aggregate_install(bgp, afi, safi, p, origin, aspath, community, ecommunity, lcommunity, atomic_aggregate, aggregate); @@ -8331,6 +8397,7 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, aggregate->rmap.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); aggregate->rmap.map = route_map_lookup_by_name(rmap); + aggregate->rmap.changed = true; route_map_counter_increment(aggregate->rmap.map); } @@ -9465,14 +9532,12 @@ void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest, const struct prefix *p, struct attr *attr, safi_t safi, bool use_json, json_object *json_ar, bool wide) { - json_object *json_status = NULL; json_object *json_net = NULL; int len; char buff[BUFSIZ]; /* Route status display. */ if (use_json) { - json_status = json_object_new_object(); json_net = json_object_new_object(); } else { vty_out(vty, " *"); @@ -9539,11 +9604,6 @@ void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest, attr->aspath->str); /* Print origin */ -#if CONFDATE > 20231208 -CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") -#endif - json_object_string_add(json_net, "bgpOriginCode", - bgp_origin_str[attr->origin]); json_object_string_add( json_net, "origin", bgp_origin_long_str[attr->origin]); @@ -9599,20 +9659,11 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") if (use_json) { struct bgp_path_info *bpi = bgp_dest_get_bgp_path_info(dest); -#if CONFDATE > 20231208 -CPP_NOTICE("Drop `bgpStatusCodes` from JSON outputs") -#endif - json_object_boolean_true_add(json_status, "*"); - json_object_boolean_true_add(json_status, ">"); json_object_boolean_true_add(json_net, "valid"); json_object_boolean_true_add(json_net, "best"); - if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_MULTIPATH)) { - json_object_boolean_true_add(json_status, "="); + if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_MULTIPATH)) json_object_boolean_true_add(json_net, "multipath"); - } - json_object_object_add(json_net, "appliedStatusSymbols", - json_status); json_object_object_addf(json_ar, json_net, "%pFX", p); } else vty_out(vty, "\n"); @@ -10661,9 +10712,17 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } else { if (json_paths) json_object_string_add( - json_peer, "type", "external"); + json_peer, "type", + (path->peer->sub_sort == + BGP_PEER_EBGP_OAD) + ? "external (oad)" + : "external"); else - vty_out(vty, ", external"); + vty_out(vty, ", %s", + (path->peer->sub_sort == + BGP_PEER_EBGP_OAD) + ? "external (oad)" + : "external"); } } } else if (path->sub_type == BGP_ROUTE_AGGREGATE) { @@ -11599,7 +11658,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t sa total_count); } else vty_out(vty, - "\nDisplayed %ld routes and %ld total paths\n", + "\nDisplayed %ld routes and %ld total paths\n", output_count, total_count); } } @@ -11650,7 +11709,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, total_cum); else vty_out(vty, - "\nDisplayed %ld routes and %ld total paths\n", + "\nDisplayed %ld routes and %ld total paths\n", output_cum, total_cum); } else { if (use_json && output_cum == 0 && json_header_depth == 0) @@ -13925,9 +13984,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix, static void show_adj_route_header(struct vty *vty, struct peer *peer, struct bgp_table *table, int *header1, - int *header2, json_object *json, - json_object *json_scode, - json_object *json_ocode, bool wide, + int *header2, json_object *json, bool wide, bool detail) { uint64_t version = table ? table->version : 0; @@ -13943,10 +14000,6 @@ static void show_adj_route_header(struct vty *vty, struct peer *peer, peer->change_local_as ? peer->change_local_as : peer->local_as); - json_object_object_add(json, "bgpStatusCodes", - json_scode); - json_object_object_add(json, "bgpOriginCodes", - json_ocode); } else { vty_out(vty, "BGP table version is %" PRIu64 @@ -13983,7 +14036,6 @@ static void show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, const char *rmap_name, json_object *json, json_object *json_ar, - json_object *json_scode, json_object *json_ocode, uint16_t show_flags, int *header1, int *header2, char *rd_str, const struct prefix *match, unsigned long *output_count, unsigned long *filtered_count) @@ -14078,8 +14130,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (ret != RMAP_DENY) { show_adj_route_header(vty, peer, table, header1, - header2, json, json_scode, - json_ocode, wide, detail); + header2, json, wide, detail); if (use_json) json_net = json_object_new_object(); @@ -14116,10 +14167,6 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, peer->change_local_as ? peer->change_local_as : peer->local_as); - json_object_object_add(json, "bgpStatusCodes", - json_scode); - json_object_object_add(json, "bgpOriginCodes", - json_ocode); json_object_string_add( json, "bgpOriginatingDefaultNetwork", (afi == AFI_IP) ? "0.0.0.0/0" : "::/0"); @@ -14159,8 +14206,8 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (ain->peer != peer) continue; show_adj_route_header(vty, peer, table, header1, - header2, json, json_scode, - json_ocode, wide, detail); + header2, json, wide, + detail); if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) @@ -14244,10 +14291,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (paf->peer != peer || !adj->attr) continue; - show_adj_route_header( - vty, peer, table, header1, - header2, json, json_scode, - json_ocode, wide, detail); + show_adj_route_header(vty, peer, table, + header1, header2, + json, wide, + detail); const struct prefix *rn_p = bgp_dest_get_prefix(dest); @@ -14311,8 +14358,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, struct bgp_path_info *pi; show_adj_route_header(vty, peer, table, header1, - header2, json, json_scode, - json_ocode, wide, detail); + header2, json, wide, detail); const struct prefix *rn_p = bgp_dest_get_prefix(dest); @@ -14355,8 +14401,6 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, struct bgp *bgp; struct bgp_table *table; json_object *json = NULL; - json_object *json_scode = NULL; - json_object *json_ocode = NULL; json_object *json_ar = NULL; bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -14385,28 +14429,6 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, if (use_json) { json = json_object_new_object(); json_ar = json_object_new_object(); - json_scode = json_object_new_object(); - json_ocode = json_object_new_object(); -#if CONFDATE > 20231208 -CPP_NOTICE("Drop `bgpStatusCodes` from JSON outputs") -#endif - json_object_string_add(json_scode, "suppressed", "s"); - json_object_string_add(json_scode, "damped", "d"); - json_object_string_add(json_scode, "history", "h"); - json_object_string_add(json_scode, "valid", "*"); - json_object_string_add(json_scode, "best", ">"); - json_object_string_add(json_scode, "multipath", "="); - json_object_string_add(json_scode, "internal", "i"); - json_object_string_add(json_scode, "ribFailure", "r"); - json_object_string_add(json_scode, "stale", "S"); - json_object_string_add(json_scode, "removed", "R"); - -#if CONFDATE > 20231208 -CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") -#endif - json_object_string_add(json_ocode, "igp", "i"); - json_object_string_add(json_ocode, "egp", "e"); - json_object_string_add(json_ocode, "incomplete", "?"); } if (!peer || !peer->afc[afi][safi]) { @@ -14417,8 +14439,6 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") vty_out(vty, "%s\n", json_object_to_json_string(json)); json_object_free(json); json_object_free(json_ar); - json_object_free(json_scode); - json_object_free(json_ocode); } else vty_out(vty, "%% No such neighbor or address family\n"); @@ -14436,8 +14456,6 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") vty_out(vty, "%s\n", json_object_to_json_string(json)); json_object_free(json); json_object_free(json_ar); - json_object_free(json_scode); - json_object_free(json_ocode); } else vty_out(vty, "%% Inbound soft reconfiguration not enabled\n"); @@ -14477,11 +14495,11 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") prefix_rd2str(prd, rd_str, sizeof(rd_str), bgp->asnotation); - show_adj_route( - vty, peer, table, afi, safi, type, rmap_name, - json, json_routes, json_scode, json_ocode, - show_flags, &header1, &header2, rd_str, match, - &output_count_per_rd, &filtered_count_per_rd); + show_adj_route(vty, peer, table, afi, safi, type, + rmap_name, json, json_routes, show_flags, + &header1, &header2, rd_str, match, + &output_count_per_rd, + &filtered_count_per_rd); /* Don't include an empty RD in the output! */ if (json_routes && (output_count_per_rd > 0)) @@ -14493,9 +14511,8 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") } } else show_adj_route(vty, peer, table, afi, safi, type, rmap_name, - json, json_ar, json_scode, json_ocode, - show_flags, &header1, &header2, rd_str, match, - &output_count, &filtered_count); + json, json_ar, show_flags, &header1, &header2, + rd_str, match, &output_count, &filtered_count); if (use_json) { if (type == bgp_show_adj_route_advertised) @@ -14507,16 +14524,6 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") json_object_int_add(json, "filteredPrefixCounter", filtered_count); - /* - * These fields only give up ownership to `json` when `header1` - * is used (set to zero). See code in `show_adj_route` and - * `show_adj_route_header`. - */ - if (header1 == 1) { - json_object_free(json_scode); - json_object_free(json_ocode); - } - /* * This is an extremely expensive operation at scale * and non-pretty reduces memory footprint significantly. diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index e9f48ea..2d82f0f 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -193,30 +193,9 @@ struct bgp_path_info_extra_vrfleak { struct prefix nexthop_orig; }; -/* Ancillary information to struct bgp_path_info, - * used for uncommonly used data (aggregation, MPLS, etc.) - * and lazily allocated to save memory. - */ -struct bgp_path_info_extra { - /* Pointer to dampening structure. */ - struct bgp_damp_info *damp_info; - - /** List of aggregations that suppress this path. */ - struct list *aggr_suppressors; - - /* Nexthop reachability check. */ - uint32_t igpmetric; - - /* MPLS label(s) - VNI(s) for EVPN-VxLAN */ - mpls_label_t label[BGP_MAX_LABELS]; - uint32_t num_labels; - - /*For EVPN*/ - struct bgp_path_info_extra_evpn *evpn; - #ifdef ENABLE_BGP_VNC +struct bgp_path_info_extra_vnc { union { - struct { void *rfapi_handle; /* export: NVE advertising this route */ @@ -239,8 +218,36 @@ struct bgp_path_info_extra { struct prefix aux_prefix; /* AFI_L2VPN: the IP addr, if family set */ } import; - } vnc; +}; +#endif + +/* Ancillary information to struct bgp_path_info, + * used for uncommonly used data (aggregation, MPLS, etc.) + * and lazily allocated to save memory. + */ +struct bgp_path_info_extra { + /* Pointer to dampening structure. */ + struct bgp_damp_info *damp_info; + + /** List of aggregations that suppress this path. */ + struct list *aggr_suppressors; + + /* Nexthop reachability check. */ + uint32_t igpmetric; + + /* MPLS label(s) - VNI(s) for EVPN-VxLAN */ + mpls_label_t label[BGP_MAX_LABELS]; + uint32_t num_labels; + + /* timestamp of the rib installation */ + time_t bgp_rib_uptime; + + /*For EVPN*/ + struct bgp_path_info_extra_evpn *evpn; + +#ifdef ENABLE_BGP_VNC + struct bgp_path_info_extra_vnc *vnc; #endif /* For flowspec*/ @@ -366,6 +373,8 @@ struct bgp_static { /* Import check status. */ uint8_t valid; + uint16_t encap_tunneltype; + /* IGP metric. */ uint32_t igpmetric; @@ -391,7 +400,6 @@ struct bgp_static { /* EVPN */ esi_t *eth_s_id; struct ethaddr *router_mac; - uint16_t encap_tunneltype; struct prefix gatewayIp; }; @@ -412,10 +420,22 @@ struct bgp_aggregate { /* AS set generation. */ uint8_t as_set; + /* Optional modify flag to override ORIGIN */ + uint8_t origin; + + /** Are there MED mismatches? */ + bool med_mismatched; + /* MED matching state. */ + /** Did we get the first MED value? */ + bool med_initialized; + /** Match only equal MED. */ + bool match_med; + /* Route-map for aggregated route. */ struct { char *name; struct route_map *map; + bool changed; } rmap; /* Suppress-count. */ @@ -427,9 +447,6 @@ struct bgp_aggregate { /* Count of routes of origin type egp under this aggregate. */ unsigned long egp_origin_count; - /* Optional modify flag to override ORIGIN */ - uint8_t origin; - /* Hash containing the communities of all the * routes under this aggregate. */ @@ -465,13 +482,6 @@ struct bgp_aggregate { /* SAFI configuration. */ safi_t safi; - /** Match only equal MED. */ - bool match_med; - /* MED matching state. */ - /** Did we get the first MED value? */ - bool med_initialized; - /** Are there MED mismatches? */ - bool med_mismatched; /** MED value found in current group. */ uint32_t med_matched_value; @@ -674,6 +684,12 @@ DECLARE_HOOK(bgp_process, struct peer *peer, bool withdraw), (bgp, afi, safi, bn, peer, withdraw)); +/* called when a route is updated in the rib */ +DECLARE_HOOK(bgp_route_update, + (struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *bn, + struct bgp_path_info *old_route, struct bgp_path_info *new_route), + (bgp, afi, safi, bn, old_route, new_route)); + /* BGP show options */ #define BGP_SHOW_OPT_JSON (1 << 0) #define BGP_SHOW_OPT_WIDE (1 << 1) @@ -698,7 +714,8 @@ extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi, bool force); extern void bgp_stop_announce_route_timer(struct peer_af *paf); extern void bgp_announce_route_all(struct peer *); -extern void bgp_default_originate(struct peer *, afi_t, safi_t, int); +extern void bgp_default_originate(struct peer *peer, afi_t afi, safi_t safi, + bool withdraw); extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, const struct bgp_table *table, const struct peer *peer); @@ -905,6 +922,7 @@ extern void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate, extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr); const char * bgp_path_selection_reason2str(enum bgp_path_selection_reason reason); +extern bool bgp_path_suppressed(struct bgp_path_info *pi); extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi); extern const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, safi_t safi); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index e0db43f..d604a7a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2323,17 +2323,10 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = { route_set_aspath_prepend_free, }; -/* `set as-path exclude ASn' */ -struct aspath_exclude { - struct aspath *aspath; - bool exclude_all; - char *exclude_aspath_acl_name; - struct as_list *exclude_aspath_acl; -}; - static void *route_aspath_exclude_compile(const char *arg) { struct aspath_exclude *ase; + struct aspath_exclude_list *ael; const char *str = arg; static const char asp_acl[] = "as-path-access-list"; @@ -2348,16 +2341,41 @@ static void *route_aspath_exclude_compile(const char *arg) ase->exclude_aspath_acl = as_list_lookup(str); } else ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL)); + + if (ase->exclude_aspath_acl) { + ael = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, + sizeof(struct aspath_exclude_list)); + ael->bp_as_excl = ase; + ael->next = ase->exclude_aspath_acl->exclude_list; + ase->exclude_aspath_acl->exclude_list = ael; + } + return ase; } static void route_aspath_exclude_free(void *rule) { struct aspath_exclude *ase = rule; + struct aspath_exclude_list *cur_ael = NULL; + struct aspath_exclude_list *prev_ael = NULL; aspath_free(ase->aspath); if (ase->exclude_aspath_acl_name) XFREE(MTYPE_TMP, ase->exclude_aspath_acl_name); + if (ase->exclude_aspath_acl) + cur_ael = ase->exclude_aspath_acl->exclude_list; + while (cur_ael) { + if (cur_ael->bp_as_excl == ase) { + if (prev_ael) + prev_ael->next = cur_ael->next; + else + ase->exclude_aspath_acl->exclude_list = NULL; + XFREE(MTYPE_ROUTE_MAP_COMPILED, cur_ael); + break; + } + prev_ael = cur_ael; + cur_ael = cur_ael->next; + } XFREE(MTYPE_ROUTE_MAP_COMPILED, ase); } @@ -4442,6 +4460,13 @@ static void bgp_route_map_update_peer_group(const char *rmap_name, filter->map[direct].name) == 0)) filter->map[direct].map = map; + + if (group->conf->default_rmap[afi][safi].name && + strmatch(group->conf->default_rmap[afi][safi] + .name, + rmap_name)) + group->conf->default_rmap[afi][safi].map = + map; } if (filter->usmap.name @@ -4596,6 +4621,7 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, route_map_counter_increment(map); aggregate->rmap.map = map; + aggregate->rmap.changed = true; matched = true; } @@ -4743,6 +4769,24 @@ static void bgp_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } +bool bgp_route_map_has_extcommunity_rt(const struct route_map *map) +{ + struct route_map_index *index = NULL; + struct route_map_rule *set = NULL; + + assert(map); + + for (index = map->head; index; index = index->next) { + for (set = index->set_list.head; set; set = set->next) { + if (set->cmd && set->cmd->str && + (strmatch(set->cmd->str, "extcommunity rt") || + strmatch(set->cmd->str, "extended-comm-list"))) + return true; + } + } + return false; +} + static void bgp_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index abebfe5..096502a 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -147,6 +147,8 @@ 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:comm-list", .cbs = { + .create = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy, .apply_finish = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish, } }, @@ -154,7 +156,6 @@ 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:comm-list/comm-list-name", .cbs = { .modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify, - .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy, } }, { @@ -356,6 +357,8 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator", .cbs = { + .create = lib_route_map_entry_set_action_rmap_set_action_aggregator_create, + .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_destroy, .apply_finish = lib_route_map_entry_set_action_rmap_set_action_aggregator_finish, } }, @@ -363,14 +366,12 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn", .cbs = { .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify, - .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy, } }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address", .cbs = { .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify, - .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy, } }, { @@ -390,6 +391,8 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb", .cbs = { + .create = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_create, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_destroy, .apply_finish = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish, } }, @@ -397,7 +400,6 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type", .cbs = { .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify, - .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy, } }, { @@ -418,7 +420,6 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific", .cbs = { .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify, - .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy, } }, { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 28e4188..d7f0cea 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -60,9 +60,12 @@ int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_mod int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create( + struct nb_cb_create_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy( + struct nb_cb_destroy_args *args); void lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(struct nb_cb_apply_finish_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify(struct nb_cb_modify_args *args); -int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_modify( @@ -127,24 +130,28 @@ int lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify(s int lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_create( + struct nb_cb_create_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_destroy( + struct nb_cb_destroy_args *args); void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish(struct nb_cb_apply_finish_args *args); int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify(struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_create( + struct nb_cb_create_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_destroy( + struct nb_cb_destroy_args *args); void lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish(struct nb_cb_apply_finish_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_destroy( diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 370295b..c1d6ee1 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -522,6 +522,7 @@ lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy( 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); } @@ -1120,6 +1121,27 @@ lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_des /* * XPath = /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list */ +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_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; +} + void lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish( struct nb_cb_apply_finish_args *args) @@ -1135,14 +1157,14 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish( /* Add configuration. */ rhc = nb_running_get_entry(args->dnode, NULL, true); - value = yang_dnode_get_string(args->dnode, "./comm-list-name"); + value = yang_dnode_get_string(args->dnode, "comm-list-name"); - if (yang_dnode_exists(args->dnode, "./comm-list-name-exact-match")) + if (yang_dnode_exists(args->dnode, "comm-list-name-exact-match")) exact_match = yang_dnode_get_bool( args->dnode, "./comm-list-name-exact-match"); - if (yang_dnode_exists(args->dnode, "./comm-list-name-any")) - any = yang_dnode_get_bool(args->dnode, "./comm-list-name-any"); + if (yang_dnode_exists(args->dnode, "comm-list-name-any")) + any = yang_dnode_get_bool(args->dnode, "comm-list-name-any"); if (exact_match) { argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, @@ -1210,23 +1232,6 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_nam return NB_OK; } -int -lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_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:comm-list/comm-list-name-any @@ -1252,9 +1257,8 @@ int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list 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); + break; } return NB_OK; @@ -1286,9 +1290,8 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_nam 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); + break; } return NB_OK; @@ -1450,7 +1453,7 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_destroy( case NB_EV_ABORT: break; case NB_EV_APPLY: - return lib_route_map_entry_match_destroy(args); + return lib_route_map_entry_set_destroy(args); } return NB_OK; @@ -1504,7 +1507,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy( case NB_EV_ABORT: break; case NB_EV_APPLY: - return lib_route_map_entry_match_destroy(args); + return lib_route_map_entry_set_destroy(args); } return NB_OK; @@ -1556,7 +1559,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy( case NB_EV_ABORT: break; case NB_EV_APPLY: - return lib_route_map_entry_match_destroy(args); + return lib_route_map_entry_set_destroy(args); } return NB_OK; @@ -1611,7 +1614,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy( case NB_EV_ABORT: break; case NB_EV_APPLY: - return lib_route_map_entry_match_destroy(args); + return lib_route_map_entry_set_destroy(args); } return NB_OK; @@ -1747,7 +1750,7 @@ int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify( || IN6_IS_ADDR_LINKLOCAL(&i6a)) return NB_ERR_VALIDATION; } - /* FALLTHROUGH */ + return NB_OK; case NB_EV_PREPARE: case NB_EV_ABORT: return NB_OK; @@ -2734,6 +2737,27 @@ lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy( * xpath = * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator */ +int lib_route_map_entry_set_action_rmap_set_action_aggregator_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_aggregator_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_set_destroy(args); + } + + return NB_OK; +} + void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish( struct nb_cb_apply_finish_args *args) { @@ -2745,8 +2769,8 @@ void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish( /* Add configuration. */ rhc = nb_running_get_entry(args->dnode, NULL, true); - asn = yang_dnode_get_string(args->dnode, "./aggregator-asn"); - addr = yang_dnode_get_string(args->dnode, "./aggregator-address"); + asn = yang_dnode_get_string(args->dnode, "aggregator-asn"); + addr = yang_dnode_get_string(args->dnode, "aggregator-address"); argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, strlen(asn) + strlen(addr) + 2); @@ -2798,22 +2822,6 @@ lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify( return NB_OK; } -int -lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_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_set_destroy(args); - } - - return NB_OK; -} - /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address @@ -2833,22 +2841,6 @@ lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_mod return NB_OK; } -int -lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_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_set_destroy(args); - } - - return NB_OK; -} - /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name @@ -2917,6 +2909,27 @@ lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy( * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb */ +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_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_set_destroy(args); + } + + return NB_OK; +} + void lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( struct nb_cb_apply_finish_args *args) @@ -2929,7 +2942,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( /* Add configuration. */ rhc = nb_running_get_entry(args->dnode, NULL, true); - lb_type = yang_dnode_get_enum(args->dnode, "./lb-type"); + lb_type = yang_dnode_get_enum(args->dnode, "lb-type"); /* Set destroy information. */ rhc->rhc_shook = generic_set_delete; @@ -2938,7 +2951,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( switch (lb_type) { case EXPLICIT_BANDWIDTH: - bandwidth = yang_dnode_get_uint16(args->dnode, "./bandwidth"); + bandwidth = yang_dnode_get_uint16(args->dnode, "bandwidth"); snprintf(str, sizeof(str), "%d", bandwidth); break; case CUMULATIVE_BANDWIDTH: @@ -2948,7 +2961,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( snprintf(str, sizeof(str), "%s", "num-multipaths"); } - if (yang_dnode_get_bool(args->dnode, "./two-octet-as-specific")) + if (yang_dnode_get_bool(args->dnode, "two-octet-as-specific")) strlcat(str, " non-transitive", sizeof(str)); ret = generic_set_add(rhc->rhc_rmi, "extcommunity bandwidth", str, @@ -2972,13 +2985,6 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify( return NB_OK; } -int -lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_set_destroy(args); -} - /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth @@ -2994,7 +3000,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy( struct nb_cb_destroy_args *args) { - return lib_route_map_entry_set_destroy(args); + return NB_OK; } /* @@ -3043,7 +3049,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy( case NB_EV_ABORT: break; case NB_EV_APPLY: - return lib_route_map_entry_match_destroy(args); + return lib_route_map_entry_set_destroy(args); } return NB_OK; @@ -3060,13 +3066,6 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec return NB_OK; } -int -lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_set_destroy(args); -} - /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-none diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index f0b2ffd..22f78fb 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -25,6 +25,7 @@ #include "memory.h" #include "frrevent.h" #include "filter.h" +#include "lib_errors.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgp_advertise.h" @@ -33,6 +34,7 @@ #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_rpki.h" +#include "bgpd/bgp_debug.h" #include "northbound_cli.h" #include "lib/network.h" @@ -43,27 +45,33 @@ #include "bgpd/bgp_rpki_clippy.c" +DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_TEMP, "BGP RPKI Intermediate Buffer"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_REVALIDATE, "BGP RPKI Revalidation"); +#define STR_SEPARATOR 10 + #define POLLING_PERIOD_DEFAULT 3600 #define EXPIRE_INTERVAL_DEFAULT 7200 #define RETRY_INTERVAL_DEFAULT 600 #define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3 -static struct event *t_rpki_sync; - #define RPKI_DEBUG(...) \ - if (rpki_debug) { \ + if (rpki_debug_conf || rpki_debug_term) { \ zlog_debug("RPKI: " __VA_ARGS__); \ } #define RPKI_OUTPUT_STRING "Control rpki specific settings\n" struct cache { - enum { TCP, SSH } type; + enum { + TCP, +#if defined(FOUND_SSH) + SSH +#endif + } type; struct tr_socket *tr_socket; union { struct tr_tcp_config *tcp_config; @@ -71,6 +79,7 @@ struct cache { } tr_config; struct rtr_socket *rtr_socket; uint8_t preference; + struct rpki_vrf *rpki_vrf; }; enum return_values { SUCCESS = 0, ERROR = -1 }; @@ -83,32 +92,61 @@ struct rpki_for_each_record_arg { enum asnotation_mode asnotation; }; -static int start(void); -static void stop(void); -static int reset(bool force); -static struct rtr_mgr_group *get_connected_group(void); -static void print_prefix_table(struct vty *vty, json_object *json); +struct rpki_vrf { + struct rtr_mgr_config *rtr_config; + struct list *cache_list; + bool rtr_is_running; + bool rtr_is_stopping; + bool rtr_is_synced; + _Atomic int rtr_update_overflow; + unsigned int polling_period; + unsigned int expire_interval; + unsigned int retry_interval; + int rpki_sync_socket_rtr; + int rpki_sync_socket_bgpd; + char *vrfname; + struct event *t_rpki_sync; + + QOBJ_FIELDS; +}; + +static pthread_key_t rpki_pthread; + +static struct rpki_vrf *find_rpki_vrf(const char *vrfname); +static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled); +static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf); +static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf); +static int bgp_rpki_write_debug(struct vty *vty, bool running); +static int start(struct rpki_vrf *rpki_vrf); +static void stop(struct rpki_vrf *rpki_vrf); +static int reset(bool force, struct rpki_vrf *rpki_vrf); +static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf); +static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf, + json_object *json, bool count_only); static void install_cli_commands(void); static int config_write(struct vty *vty); static int config_on_exit(struct vty *vty); static void free_cache(struct cache *cache); -static struct rtr_mgr_group *get_groups(void); +static struct rtr_mgr_group *get_groups(struct list *cache_list); #if defined(FOUND_SSH) -static int add_ssh_cache(const char *host, const unsigned int port, - const char *username, const char *client_privkey_path, +static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, + const unsigned int port, const char *username, + const char *client_privkey_path, const char *server_pubkey_path, const uint8_t preference, const char *bindaddr); #endif static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket); -static struct cache *find_cache(const uint8_t preference); -static void rpki_delete_all_cache_nodes(void); -static int add_tcp_cache(const char *host, const char *port, - const uint8_t preference, const char *bindaddr); +static struct cache *find_cache(const uint8_t preference, + struct list *cache_list); +static void rpki_delete_all_cache_nodes(struct rpki_vrf *rpki_vrf); +static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, + const char *port, const uint8_t preference, + const char *bindaddr); static void print_record(const struct pfx_record *record, struct vty *vty, json_object *json, enum asnotation_mode asnotation); -static bool is_synchronized(void); -static bool is_running(void); -static bool is_stopping(void); +static bool is_synchronized(struct rpki_vrf *rpki_vrf); +static bool is_running(struct rpki_vrf *rpki_vrf); +static bool is_stopping(struct rpki_vrf *rpki_vrf); static void route_match_free(void *rule); static enum route_map_cmd_result_t route_match(void *rule, const struct prefix *prefix, @@ -116,20 +154,14 @@ static enum route_map_cmd_result_t route_match(void *rule, void *object); static void *route_match_compile(const char *arg); static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi); -static void revalidate_all_routes(void); - -static struct rtr_mgr_config *rtr_config; -static struct list *cache_list; -static bool rtr_is_running; -static bool rtr_is_stopping; -static bool rtr_is_synced; -static _Atomic int rtr_update_overflow; -static bool rpki_debug; -static unsigned int polling_period; -static unsigned int expire_interval; -static unsigned int retry_interval; -static int rpki_sync_socket_rtr; -static int rpki_sync_socket_bgpd; +static void revalidate_all_routes(struct rpki_vrf *rpki_vrf); + +static bool rpki_debug_conf, rpki_debug_term; + +DECLARE_QOBJ_TYPE(rpki_vrf); +DEFINE_QOBJ_TYPE(rpki_vrf); + +struct list *rpki_vrf_list; static struct cmd_node rpki_node = { .name = "rpki", @@ -139,6 +171,16 @@ static struct cmd_node rpki_node = { .config_write = config_write, .node_exit = config_on_exit, }; + +static struct cmd_node rpki_vrf_node = { + .name = "rpki", + .node = RPKI_VRF_NODE, + .parent_node = VRF_NODE, + .prompt = "%s(config-vrf-rpki)# ", + .config_write = NULL, + .node_exit = config_on_exit, +}; + static const struct route_map_rule_cmd route_match_rpki_cmd = { "rpki", route_match, route_match_compile, route_match_free}; @@ -246,10 +288,141 @@ static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket) return rtr_socket; } -static struct cache *find_cache(const uint8_t preference) +static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled) +{ + struct rpki_vrf *rpki; + + if (vrf->vrf_id == VRF_DEFAULT) + rpki = find_rpki_vrf(NULL); + else + rpki = find_rpki_vrf(vrf->name); + if (!rpki) + return 0; + + if (enabled) + start(rpki); + else + stop(rpki); + return 1; +} + +/* tcp identifier : : + * ssh identifier : @: + */ +static struct rpki_vrf *find_rpki_vrf_from_ident(const char *ident) { +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; +#endif + struct tr_tcp_config *tcp_config; + struct listnode *rpki_vrf_nnode; + unsigned int cache_port, port; struct listnode *cache_node; + struct rpki_vrf *rpki_vrf; struct cache *cache; + bool is_tcp = true; + size_t host_len; + char *endptr; + char *host; + char *ptr; + char *buf; + + /* extract the */ + ptr = strrchr(ident, ':'); + if (!ptr) + return NULL; + + ptr++; + /* extract port */ + port = atoi(ptr); + if (port == 0) + /* not ours */ + return NULL; + + /* extract host */ + ptr--; + host_len = (size_t)(ptr - ident); + buf = XCALLOC(MTYPE_BGP_RPKI_TEMP, host_len + 1); + memcpy(buf, ident, host_len); + buf[host_len] = '\0'; + endptr = strrchr(buf, '@'); + + /* ssh session */ + if (endptr) { + host = XCALLOC(MTYPE_BGP_RPKI_TEMP, + (size_t)(buf + host_len - endptr) + 1); + memcpy(host, endptr + 1, (size_t)(buf + host_len - endptr) + 1); + is_tcp = false; + } else { + host = buf; + buf = NULL; + } + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, + cache)) { + if ((cache->type == TCP && !is_tcp) +#if defined(FOUND_SSH) + || (cache->type == SSH && is_tcp) +#endif + ) + continue; + + if (is_tcp) { + tcp_config = cache->tr_config.tcp_config; + cache_port = atoi(tcp_config->port); + if (cache_port != port) + continue; + if (strlen(tcp_config->host) != strlen(host)) + continue; + if (memcmp(tcp_config->host, host, host_len) == + 0) + break; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + if (port != ssh_config->port) + continue; + if (strmatch(ssh_config->host, host)) + break; + } +#endif + } + if (cache) + break; + } + if (host) + XFREE(MTYPE_BGP_RPKI_TEMP, host); + if (buf) + XFREE(MTYPE_BGP_RPKI_TEMP, buf); + return rpki_vrf; +} + +static struct rpki_vrf *find_rpki_vrf(const char *vrfname) +{ + struct listnode *rpki_vrf_nnode; + struct rpki_vrf *rpki_vrf; + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) { + if (!vrfname && !rpki_vrf->vrfname) + /* rpki_vrf struct of the default VRF */ + return rpki_vrf; + if (vrfname && rpki_vrf->vrfname && + strmatch(vrfname, rpki_vrf->vrfname)) + return rpki_vrf; + } + return NULL; +} + +static struct cache *find_cache(const uint8_t preference, + struct list *cache_list) +{ + struct listnode *cache_node; + struct cache *cache; + + if (!cache_list) + return NULL; for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { if (cache->preference == preference) @@ -258,14 +431,15 @@ static struct cache *find_cache(const uint8_t preference) return NULL; } -static void rpki_delete_all_cache_nodes(void) +static void rpki_delete_all_cache_nodes(struct rpki_vrf *rpki_vrf) { struct listnode *cache_node, *cache_next; struct cache *cache; - for (ALL_LIST_ELEMENTS(cache_list, cache_node, cache_next, cache)) { - rtr_mgr_remove_group(rtr_config, cache->preference); - listnode_delete(cache_list, cache); + for (ALL_LIST_ELEMENTS(rpki_vrf->cache_list, cache_node, cache_next, + cache)) { + rtr_mgr_remove_group(rpki_vrf->rtr_config, cache->preference); + listnode_delete(rpki_vrf->cache_list, cache); } } @@ -315,7 +489,14 @@ static void print_record_cb(const struct pfx_record *record, void *data) print_record(record, vty, arg->json, arg->asnotation); } -static struct rtr_mgr_group *get_groups(void) +static void count_record_cb(const struct pfx_record *record, void *data) +{ + struct rpki_for_each_record_arg *arg = data; + + (*arg->prefix_amount)++; +} + +static struct rtr_mgr_group *get_groups(struct list *cache_list) { struct listnode *cache_node; struct rtr_mgr_group *rtr_mgr_groups; @@ -344,19 +525,19 @@ static struct rtr_mgr_group *get_groups(void) return rtr_mgr_groups; } -inline bool is_synchronized(void) +inline bool is_synchronized(struct rpki_vrf *rpki_vrf) { - return rtr_is_synced; + return rpki_vrf->rtr_is_synced; } -inline bool is_running(void) +inline bool is_running(struct rpki_vrf *rpki_vrf) { - return rtr_is_running; + return rpki_vrf->rtr_is_running; } -inline bool is_stopping(void) +inline bool is_stopping(struct rpki_vrf *rpki_vrf) { - return rtr_is_stopping; + return rpki_vrf->rtr_is_stopping; } static void pfx_record_to_prefix(struct pfx_record *record, @@ -408,23 +589,26 @@ static void bgpd_sync_callback(struct event *thread) struct listnode *node; struct prefix prefix; struct pfx_record rec; + struct rpki_vrf *rpki_vrf = EVENT_ARG(thread); + struct vrf *vrf = NULL; - event_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, rpki_vrf, + rpki_vrf->rpki_sync_socket_bgpd, NULL); - if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) { - while (read(rpki_sync_socket_bgpd, &rec, + if (atomic_load_explicit(&rpki_vrf->rtr_update_overflow, + memory_order_seq_cst)) { + while (read(rpki_vrf->rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record)) != -1) ; - atomic_store_explicit(&rtr_update_overflow, 0, + atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 0, memory_order_seq_cst); - revalidate_all_routes(); + revalidate_all_routes(rpki_vrf); return; } - int retval = - read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record)); + int retval = read(rpki_vrf->rpki_sync_socket_bgpd, &rec, + sizeof(struct pfx_record)); if (retval != sizeof(struct pfx_record)) { RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd"); return; @@ -433,9 +617,23 @@ static void bgpd_sync_callback(struct event *thread) afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6; + if (rpki_vrf->vrfname) { + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) { + zlog_err("%s(): vrf for rpki %s not found", __func__, + rpki_vrf->vrfname); + return; + } + } + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { safi_t safi; + if (!vrf && bgp->vrf_id != VRF_DEFAULT) + continue; + if (vrf && bgp->vrf_id != vrf->vrf_id) + continue; + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { struct bgp_table *table = bgp->rib[afi][safi]; struct rpki_revalidate_prefix *rrp; @@ -501,15 +699,30 @@ static void bgp_rpki_revalidate_peer(struct event *thread) XFREE(MTYPE_BGP_RPKI_REVALIDATE, rvp); } -static void revalidate_all_routes(void) +static void revalidate_all_routes(struct rpki_vrf *rpki_vrf) { struct bgp *bgp; struct listnode *node; + struct vrf *vrf = NULL; + + if (rpki_vrf->vrfname) { + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) { + zlog_err("%s(): vrf for rpki %s not found", __func__, + rpki_vrf->vrfname); + return; + } + } for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { struct peer *peer; struct listnode *peer_listnode; + if (!vrf && bgp->vrf_id != VRF_DEFAULT) + continue; + if (vrf && bgp->vrf_id != vrf->vrf_id) + continue; + for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) { afi_t afi; safi_t safi; @@ -542,21 +755,49 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)), const struct pfx_record rec, const bool added __attribute__((unused))) { - if (is_stopping() || - atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) + struct rpki_vrf *rpki_vrf; + const char *msg; + const struct rtr_socket *rtr = rec.socket; + const char *ident; + + if (!rtr) { + msg = "could not find rtr_socket from cb_sync_rtr"; + goto err; + } + if (!rtr->tr_socket) { + msg = "could not find tr_socket from cb_sync_rtr"; + goto err; + } + ident = rtr->tr_socket->ident_fp(rtr->tr_socket->socket); + if (!ident) { + msg = "could not find ident from cb_sync_rtr"; + goto err; + } + rpki_vrf = find_rpki_vrf_from_ident(ident); + if (!rpki_vrf) { + msg = "could not find rpki_vrf"; + goto err; + } + + if (is_stopping(rpki_vrf) || + atomic_load_explicit(&rpki_vrf->rtr_update_overflow, + memory_order_seq_cst)) return; - int retval = - write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record)); + int retval = write(rpki_vrf->rpki_sync_socket_rtr, &rec, + sizeof(struct pfx_record)); if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) - atomic_store_explicit(&rtr_update_overflow, 1, + atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 1, memory_order_seq_cst); else if (retval != sizeof(struct pfx_record)) RPKI_DEBUG("Could not write to rpki_sync_socket_rtr"); + return; +err: + zlog_err("RPKI: %s", msg); } -static void rpki_init_sync_socket(void) +static void rpki_init_sync_socket(struct rpki_vrf *rpki_vrf) { int fds[2]; const char *msg; @@ -566,22 +807,22 @@ static void rpki_init_sync_socket(void) msg = "could not open rpki sync socketpair"; goto err; } - rpki_sync_socket_rtr = fds[0]; - rpki_sync_socket_bgpd = fds[1]; + rpki_vrf->rpki_sync_socket_rtr = fds[0]; + rpki_vrf->rpki_sync_socket_bgpd = fds[1]; - if (set_nonblocking(rpki_sync_socket_rtr) != 0) { + if (set_nonblocking(rpki_vrf->rpki_sync_socket_rtr) != 0) { msg = "could not set rpki_sync_socket_rtr to non blocking"; goto err; } - if (set_nonblocking(rpki_sync_socket_bgpd) != 0) { + if (set_nonblocking(rpki_vrf->rpki_sync_socket_bgpd) != 0) { msg = "could not set rpki_sync_socket_bgpd to non blocking"; goto err; } - event_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, rpki_vrf, + rpki_vrf->rpki_sync_socket_bgpd, NULL); return; @@ -591,139 +832,195 @@ err: } +static struct rpki_vrf *bgp_rpki_allocate(const char *vrfname) +{ + struct rpki_vrf *rpki_vrf; + + /* initialise default vrf cache list */ + rpki_vrf = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct rpki_vrf)); + + rpki_vrf->cache_list = list_new(); + rpki_vrf->cache_list->del = (void (*)(void *)) & free_cache; + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; + rpki_vrf->expire_interval = EXPIRE_INTERVAL_DEFAULT; + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; + + if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) + rpki_vrf->vrfname = XSTRDUP(MTYPE_BGP_RPKI_CACHE, vrfname); + QOBJ_REG(rpki_vrf, rpki_vrf); + listnode_add(rpki_vrf_list, rpki_vrf); + + return rpki_vrf; +} + static int bgp_rpki_init(struct event_loop *master) { - rpki_debug = false; - rtr_is_running = false; - rtr_is_stopping = false; - rtr_is_synced = false; + rpki_debug_conf = false; + rpki_debug_term = false; - cache_list = list_new(); - cache_list->del = (void (*)(void *)) & free_cache; + rpki_vrf_list = list_new(); - polling_period = POLLING_PERIOD_DEFAULT; - expire_interval = EXPIRE_INTERVAL_DEFAULT; - retry_interval = RETRY_INTERVAL_DEFAULT; install_cli_commands(); - rpki_init_sync_socket(); + return 0; } static int bgp_rpki_fini(void) { - stop(); - list_delete(&cache_list); + struct listnode *node, *nnode; + struct rpki_vrf *rpki_vrf; + + for (ALL_LIST_ELEMENTS(rpki_vrf_list, node, nnode, rpki_vrf)) { + stop(rpki_vrf); + list_delete(&rpki_vrf->cache_list); - close(rpki_sync_socket_rtr); - close(rpki_sync_socket_bgpd); + close(rpki_vrf->rpki_sync_socket_rtr); + close(rpki_vrf->rpki_sync_socket_bgpd); + + listnode_delete(rpki_vrf_list, rpki_vrf); + QOBJ_UNREG(rpki_vrf); + if (rpki_vrf->vrfname) + XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf->vrfname); + XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf); + } return 0; } static int bgp_rpki_module_init(void) { + pthread_key_create(&rpki_pthread, NULL); + lrtr_set_alloc_functions(malloc_wrapper, realloc_wrapper, free_wrapper); hook_register(bgp_rpki_prefix_status, rpki_validate_prefix); hook_register(frr_late_init, bgp_rpki_init); hook_register(frr_early_fini, bgp_rpki_fini); + hook_register(bgp_hook_config_write_debug, &bgp_rpki_write_debug); + hook_register(bgp_hook_vrf_update, &bgp_rpki_vrf_update); + hook_register(bgp_hook_config_write_vrf, &bgp_rpki_hook_write_vrf); return 0; } static void sync_expired(struct event *thread) { - if (!rtr_mgr_conf_in_sync(rtr_config)) { + struct rpki_vrf *rpki_vrf = EVENT_ARG(thread); + + if (!rtr_mgr_conf_in_sync(rpki_vrf->rtr_config)) { RPKI_DEBUG("rtr_mgr is not synced, retrying."); - event_add_timer(bm->master, sync_expired, NULL, + event_add_timer(bm->master, sync_expired, rpki_vrf, BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT, - &t_rpki_sync); + &rpki_vrf->t_rpki_sync); return; } RPKI_DEBUG("rtr_mgr sync is done."); - rtr_is_synced = true; + rpki_vrf->rtr_is_synced = true; } -static int start(void) +static int start(struct rpki_vrf *rpki_vrf) { + struct list *cache_list = NULL; + struct vrf *vrf; int ret; - rtr_is_stopping = false; - rtr_is_synced = false; - rtr_update_overflow = 0; + rpki_vrf->rtr_is_stopping = false; + rpki_vrf->rtr_is_synced = false; + rpki_vrf->rtr_update_overflow = 0; + cache_list = rpki_vrf->cache_list; + rpki_vrf->rtr_update_overflow = 0; - if (list_isempty(cache_list)) { + if (!cache_list || list_isempty(cache_list)) { RPKI_DEBUG( "No caches were found in config. Prefix validation is off."); return ERROR; } - RPKI_DEBUG("Init rtr_mgr."); + + if (rpki_vrf->vrfname) + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (!vrf || !CHECK_FLAG(vrf->status, VRF_ACTIVE)) { + RPKI_DEBUG("VRF %s not present or disabled", rpki_vrf->vrfname); + return ERROR; + } + + RPKI_DEBUG("Init rtr_mgr (%s).", vrf->name); int groups_len = listcount(cache_list); - struct rtr_mgr_group *groups = get_groups(); + struct rtr_mgr_group *groups = get_groups(rpki_vrf->cache_list); - RPKI_DEBUG("Polling period: %d", polling_period); - ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period, - expire_interval, retry_interval, - rpki_update_cb_sync_rtr, NULL, NULL, NULL); + RPKI_DEBUG("Polling period: %d", rpki_vrf->polling_period); + ret = rtr_mgr_init(&rpki_vrf->rtr_config, groups, groups_len, + rpki_vrf->polling_period, rpki_vrf->expire_interval, + rpki_vrf->retry_interval, rpki_update_cb_sync_rtr, + NULL, NULL, NULL); if (ret == RTR_ERROR) { - RPKI_DEBUG("Init rtr_mgr failed."); + RPKI_DEBUG("Init rtr_mgr failed (%s).", vrf->name); return ERROR; } - RPKI_DEBUG("Starting rtr_mgr."); - ret = rtr_mgr_start(rtr_config); + RPKI_DEBUG("Starting rtr_mgr (%s).", vrf->name); + ret = rtr_mgr_start(rpki_vrf->rtr_config); if (ret == RTR_ERROR) { - RPKI_DEBUG("Starting rtr_mgr failed."); - rtr_mgr_free(rtr_config); + RPKI_DEBUG("Starting rtr_mgr failed (%s).", vrf->name); + rtr_mgr_free(rpki_vrf->rtr_config); return ERROR; } - event_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync); + event_add_timer(bm->master, sync_expired, rpki_vrf, 0, + &rpki_vrf->t_rpki_sync); XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); - rtr_is_running = true; + rpki_vrf->rtr_is_running = true; return SUCCESS; } -static void stop(void) +static void stop(struct rpki_vrf *rpki_vrf) { - rtr_is_stopping = true; - if (is_running()) { - EVENT_OFF(t_rpki_sync); - rtr_mgr_stop(rtr_config); - rtr_mgr_free(rtr_config); - rtr_is_running = false; + rpki_vrf->rtr_is_stopping = true; + if (is_running(rpki_vrf)) { + EVENT_OFF(rpki_vrf->t_rpki_sync); + rtr_mgr_stop(rpki_vrf->rtr_config); + rtr_mgr_free(rpki_vrf->rtr_config); + rpki_vrf->rtr_is_running = false; } } -static int reset(bool force) +static int reset(bool force, struct rpki_vrf *rpki_vrf) { - if (is_running() && !force) + if (is_running(rpki_vrf) && !force) return SUCCESS; RPKI_DEBUG("Resetting RPKI Session"); - stop(); - return start(); + stop(rpki_vrf); + return start(rpki_vrf); } -static struct rtr_mgr_group *get_connected_group(void) +static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf) { + struct list *cache_list; + + if (!rpki_vrf) + return NULL; + + cache_list = rpki_vrf->cache_list; if (!cache_list || list_isempty(cache_list)) return NULL; - return rtr_mgr_get_first_group(rtr_config); + return rtr_mgr_get_first_group(rpki_vrf->rtr_config); } static void print_prefix_table_by_asn(struct vty *vty, as_t as, + struct rpki_vrf *rpki_vrf, json_object *json) { unsigned int number_of_ipv4_prefixes = 0; unsigned int number_of_ipv6_prefixes = 0; - struct rtr_mgr_group *group = get_connected_group(); + struct rtr_mgr_group *group = get_connected_group(rpki_vrf); struct rpki_for_each_record_arg arg; json_object *json_records = NULL; @@ -732,8 +1029,14 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, arg.json = NULL; arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); + if (!rpki_vrf) + return; + if (!group) { - if (!json) + if (json) { + json_object_string_add(json, "error", "Cannot find a connected group."); + vty_json(vty, json); + } else vty_out(vty, "Cannot find a connected group.\n"); return; } @@ -772,42 +1075,58 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, vty_json(vty, json); } -static void print_prefix_table(struct vty *vty, json_object *json) +static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf, + json_object *json, bool count_only) { struct rpki_for_each_record_arg arg; unsigned int number_of_ipv4_prefixes = 0; unsigned int number_of_ipv6_prefixes = 0; - struct rtr_mgr_group *group = get_connected_group(); + struct rtr_mgr_group *group = get_connected_group(rpki_vrf); json_object *json_records = NULL; + if (!rpki_vrf) + return; + + group = get_connected_group(rpki_vrf); arg.vty = vty; arg.json = NULL; arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); if (!group) { - if (!json) + if (json) { + json_object_string_add(json, "error", "Cannot find a connected group."); + vty_json(vty, json); + } else vty_out(vty, "Cannot find a connected group.\n"); return; } struct pfx_table *pfx_table = group->sockets[0]->pfx_table; - if (!json) { - vty_out(vty, "RPKI/RTR prefix table\n"); - vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", - "Origin-AS"); - } else { - json_records = json_object_new_array(); - json_object_object_add(json, "prefixes", json_records); - arg.json = json_records; + if (!count_only) { + if (!json) { + vty_out(vty, "RPKI/RTR prefix table\n"); + vty_out(vty, "%-40s %s %s\n", "Prefix", + "Prefix Length", "Origin-AS"); + } else { + json_records = json_object_new_array(); + json_object_object_add(json, "prefixes", json_records); + arg.json = json_records; + } } arg.prefix_amount = &number_of_ipv4_prefixes; - pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg); + if (count_only) + pfx_table_for_each_ipv4_record(pfx_table, count_record_cb, &arg); + else + pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg); arg.prefix_amount = &number_of_ipv6_prefixes; - pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg); + if (count_only) + pfx_table_for_each_ipv6_record(pfx_table, count_record_cb, &arg); + else + pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg); if (!json) { vty_out(vty, "Number of IPv4 Prefixes: %u\n", @@ -832,8 +1151,25 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, as_t as_number = 0; struct lrtr_ip_addr ip_addr_prefix; enum pfxv_state result; + struct bgp *bgp = peer->bgp; + struct vrf *vrf; + struct rpki_vrf *rpki_vrf; + + if (!bgp) + return 0; + + vrf = vrf_lookup_by_id(bgp->vrf_id); + if (!vrf) + return 0; + + if (vrf->vrf_id == VRF_DEFAULT) + rpki_vrf = find_rpki_vrf(NULL); + else + rpki_vrf = find_rpki_vrf(vrf->name); + if (!rpki_vrf || !is_synchronized(rpki_vrf)) + return 0; - if (!is_synchronized()) + if (!is_synchronized(rpki_vrf)) return RPKI_NOT_BEING_USED; // No aspath means route comes from iBGP @@ -878,7 +1214,7 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, } // Do the actual validation - rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix, + rtr_mgr_validate(rpki_vrf->rtr_config, as_number, &ip_addr_prefix, prefix->prefixlen, &result); // Print Debug output @@ -911,15 +1247,26 @@ static int add_cache(struct cache *cache) { uint8_t preference = cache->preference; struct rtr_mgr_group group; + struct list *cache_list; + struct rpki_vrf *rpki_vrf; + + rpki_vrf = cache->rpki_vrf; + if (!rpki_vrf) + return ERROR; group.preference = preference; group.sockets_len = 1; group.sockets = &cache->rtr_socket; - if (is_running()) { + cache_list = rpki_vrf->cache_list; + if (!cache_list) + return ERROR; + + if (is_running(rpki_vrf)) { init_tr_socket(cache); - if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) { + if (rtr_mgr_add_group(rpki_vrf->rtr_config, &group) != + RTR_SUCCESS) { free_tr_socket(cache); return ERROR; } @@ -930,8 +1277,165 @@ static int add_cache(struct cache *cache) return SUCCESS; } -static int add_tcp_cache(const char *host, const char *port, - const uint8_t preference, const char *bindaddr) +static int rpki_create_socket(void *_cache) +{ + struct timeval prev_snd_tmout, prev_rcv_tmout, timeout; + struct cache *cache = (struct cache *)_cache; + struct rpki_vrf *rpki_vrf; + struct tr_tcp_config *tcp_config; + struct addrinfo *res = NULL; + struct addrinfo hints = {}; + socklen_t optlen; + char *host, *port; + struct vrf *vrf; + int cancel_state; + int socket; + int ret; +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; + char s_port[10]; +#endif + + if (!cache) + return -1; + + rpki_vrf = cache->rpki_vrf; + + /* + * the rpki infrastructure can call this function + * multiple times per pthread. Why? I have absolutely + * no idea, and I am not sure I care a whole bunch. + * Why does this matter? Well when we attempt to + * hook this pthread into the rcu structure multiple + * times the rcu code asserts on shutdown. Clearly + * upset that you have rcu data associated with a pthread + * that has not been cleaned up. And frankly this is rightly so. + * + * At this point we know that this function is not + * called a million bajillion times so let's just + * add a bit of insurance by looking to see if + * some thread specific code has been set for this + * pthread. If not, hook into the rcu code and + * make things happy. + * + * IF YOU PUT A ZLOG_XXXX prior to the call into + * frr_pthread_non_controlled_startup in this function + * BGP WILL CRASH. You have been warned. + */ + if (!pthread_getspecific(rpki_pthread) && + frr_pthread_non_controlled_startup(cache->rtr_socket->thread_id, + "RPKI RTRLIB socket", + "rpki_create_socket") < 0) + return -1; + + pthread_setspecific(rpki_pthread, &rpki_pthread); + + if (rpki_vrf->vrfname == NULL) + vrf = vrf_lookup_by_id(VRF_DEFAULT); + else + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) + return -1; + + if (!CHECK_FLAG(vrf->status, VRF_ACTIVE) || vrf->vrf_id == VRF_UNKNOWN) + return -1; + + if (cache->type == TCP) { + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + tcp_config = cache->tr_config.tcp_config; + host = tcp_config->host; + port = tcp_config->port; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + host = ssh_config->host; + snprintf(s_port, sizeof(s_port), "%u", ssh_config->port); + port = s_port; + + hints.ai_flags |= AI_NUMERICHOST; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + } +#endif + + frr_with_privs (&bgpd_privs) { + ret = vrf_getaddrinfo(host, port, &hints, &res, vrf->vrf_id); + } + if (ret != 0) { + flog_err_sys(EC_LIB_SOCKET, "getaddrinfo: %s", + gai_strerror(ret)); + return -1; + } + + frr_with_privs (&bgpd_privs) { + socket = vrf_socket(res->ai_family, res->ai_socktype, + res->ai_protocol, vrf->vrf_id, NULL); + } + if (socket < 0) { + freeaddrinfo(res); + return -1; + } + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancel_state); + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + optlen = sizeof(prev_rcv_tmout); + ret = getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, + &optlen); + if (ret < 0) + zlog_warn("%s: failed to getsockopt SO_RCVTIMEO for socket %d", + __func__, socket); + ret = getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, + &optlen); + if (ret < 0) + zlog_warn("%s: failed to getsockopt SO_SNDTIMEO for socket %d", + __func__, socket); + ret = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout)); + if (ret < 0) + zlog_warn("%s: failed to setsockopt SO_RCVTIMEO for socket %d", + __func__, socket); + + ret = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(timeout)); + if (ret < 0) + zlog_warn("%s: failed to setsockopt SO_SNDTIMEO for socket %d", + __func__, socket); + + if (connect(socket, res->ai_addr, res->ai_addrlen) == -1) { + freeaddrinfo(res); + close(socket); + pthread_setcancelstate(cancel_state, NULL); + return -1; + } + + freeaddrinfo(res); + pthread_setcancelstate(cancel_state, NULL); + + ret = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, + sizeof(prev_rcv_tmout)); + if (ret < 0) + zlog_warn("%s: failed to setsockopt SO_RCVTIMEO for socket %d", + __func__, socket); + + ret = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, + sizeof(prev_snd_tmout)); + if (ret < 0) + zlog_warn("%s: failed to setsockopt SO_SNDTIMEO for socket %d", + __func__, socket); + + return socket; +} + +static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, + const char *port, const uint8_t preference, + const char *bindaddr) { struct rtr_socket *rtr_socket; struct tr_tcp_config *tcp_config = @@ -948,8 +1452,11 @@ static int add_tcp_cache(const char *host, const char *port, else tcp_config->bindaddr = NULL; + tcp_config->data = cache; + tcp_config->new_socket = rpki_create_socket; rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = TCP; cache->tr_socket = tr_socket; cache->tr_config.tcp_config = tcp_config; @@ -958,15 +1465,16 @@ static int add_tcp_cache(const char *host, const char *port, int ret = add_cache(cache); if (ret != SUCCESS) { + tcp_config->data = NULL; free_cache(cache); } - return ret; } #if defined(FOUND_SSH) -static int add_ssh_cache(const char *host, const unsigned int port, - const char *username, const char *client_privkey_path, +static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, + const unsigned int port, const char *username, + const char *client_privkey_path, const char *server_pubkey_path, const uint8_t preference, const char *bindaddr) { @@ -984,6 +1492,8 @@ static int add_ssh_cache(const char *host, const unsigned int port, ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr); else ssh_config->bindaddr = NULL; + ssh_config->data = cache; + ssh_config->new_socket = rpki_create_socket; ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username); ssh_config->client_privkey_path = @@ -993,6 +1503,7 @@ static int add_ssh_cache(const char *host, const unsigned int port, rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = SSH; cache->tr_socket = tr_socket; cache->tr_config.ssh_config = ssh_config; @@ -1001,6 +1512,7 @@ static int add_ssh_cache(const char *host, const unsigned int port, int ret = add_cache(cache); if (ret != SUCCESS) { + ssh_config->data = NULL; free_cache(cache); } @@ -1036,25 +1548,72 @@ static void free_cache(struct cache *cache) XFREE(MTYPE_BGP_RPKI_CACHE, cache); } -static int config_write(struct vty *vty) +static int bgp_rpki_write_debug(struct vty *vty, bool running) { - struct listnode *cache_node; - struct cache *cache; - - if (rpki_debug) + if (rpki_debug_conf && running) { vty_out(vty, "debug rpki\n"); + return 1; + } + if ((rpki_debug_conf || rpki_debug_term) && !running) { + vty_out(vty, " BGP RPKI debugging is on\n"); + return 1; + } + return 0; +} - vty_out(vty, "!\n"); - vty_out(vty, "rpki\n"); +static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf) +{ + int ret; - if (polling_period != POLLING_PERIOD_DEFAULT) - vty_out(vty, " rpki polling_period %d\n", polling_period); - if (retry_interval != RETRY_INTERVAL_DEFAULT) - vty_out(vty, " rpki retry_interval %d\n", retry_interval); - if (expire_interval != EXPIRE_INTERVAL_DEFAULT) - vty_out(vty, " rpki expire_interval %d\n", expire_interval); + ret = bgp_rpki_write_vrf(vty, vrf); + if (ret == ERROR) + return 0; + return ret; +} - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { +static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf) +{ + struct listnode *cache_node; + struct cache *cache; + struct rpki_vrf *rpki_vrf = NULL; + char sep[STR_SEPARATOR]; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (!vrf) { + rpki_vrf = find_rpki_vrf(NULL); + snprintf(sep, sizeof(sep), "%s", ""); + } else if (vrf->vrf_id != VRF_DEFAULT) { + rpki_vrf = find_rpki_vrf(vrf->name); + snprintf(sep, sizeof(sep), "%s", " "); + vrf_id = vrf->vrf_id; + } else + return ERROR; + + if (!rpki_vrf) + return ERROR; + + if (rpki_vrf->cache_list && list_isempty(rpki_vrf->cache_list) && + rpki_vrf->polling_period == POLLING_PERIOD_DEFAULT && + rpki_vrf->retry_interval == RETRY_INTERVAL_DEFAULT && + rpki_vrf->expire_interval == EXPIRE_INTERVAL_DEFAULT) + /* do not display the default config values */ + return 0; + + if (vrf_id == VRF_DEFAULT) + vty_out(vty, "%s!\n", sep); + vty_out(vty, "%srpki\n", sep); + + if (rpki_vrf->polling_period != POLLING_PERIOD_DEFAULT) + vty_out(vty, "%s rpki polling_period %d\n", sep, + rpki_vrf->polling_period); + if (rpki_vrf->retry_interval != RETRY_INTERVAL_DEFAULT) + vty_out(vty, "%s rpki retry_interval %d\n", sep, + rpki_vrf->retry_interval); + if (rpki_vrf->expire_interval != EXPIRE_INTERVAL_DEFAULT) + vty_out(vty, "%s rpki expire_interval %d\n", sep, + rpki_vrf->expire_interval); + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { switch (cache->type) { struct tr_tcp_config *tcp_config; #if defined(FOUND_SSH) @@ -1062,8 +1621,8 @@ 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, - tcp_config->port); + vty_out(vty, "%s rpki cache %s %s ", sep, + tcp_config->host, tcp_config->port); if (tcp_config->bindaddr) vty_out(vty, "source %s ", tcp_config->bindaddr); @@ -1071,13 +1630,13 @@ static int config_write(struct vty *vty) #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, "%s rpki cache %s %u %s %s %s ", sep, ssh_config->host, ssh_config->port, ssh_config->username, ssh_config->client_privkey_path, ssh_config->server_hostkey_path != NULL ? ssh_config->server_hostkey_path - : " "); + : ""); if (ssh_config->bindaddr) vty_out(vty, "source %s ", ssh_config->bindaddr); @@ -1089,17 +1648,66 @@ static int config_write(struct vty *vty) vty_out(vty, "preference %hhu\n", cache->preference); } - vty_out(vty, "exit\n"); + + vty_out(vty, "%sexit\n%s", sep, vrf_id == VRF_DEFAULT ? "!\n" : ""); return 1; } +static int config_write(struct vty *vty) +{ + return bgp_rpki_write_vrf(vty, NULL); +} + +static struct rpki_vrf *get_rpki_vrf(const char *vrfname) +{ + struct rpki_vrf *rpki_vrf = NULL; + struct vrf *vrf = NULL; + + if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) { + vrf = vrf_lookup_by_name(vrfname); + if (!vrf) + return NULL; + rpki_vrf = find_rpki_vrf(vrf->name); + } else + /* default VRF */ + rpki_vrf = find_rpki_vrf(NULL); + + return rpki_vrf; +} + DEFUN_NOSH (rpki, rpki_cmd, "rpki", "Enable rpki and enter rpki configuration mode\n") { - vty->node = RPKI_NODE; + struct rpki_vrf *rpki_vrf; + char *vrfname = NULL; + struct vrf *vrf; + + if (vty->node == CONFIG_NODE) + vty->node = RPKI_NODE; + else { + vrf = VTY_GET_CONTEXT(vrf); + + if (!vrf) + return CMD_WARNING; + + vty->node = RPKI_VRF_NODE; + if (vrf->vrf_id != VRF_DEFAULT) + vrfname = vrf->name; + } + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) { + rpki_vrf = bgp_rpki_allocate(vrfname); + + rpki_init_sync_socket(rpki_vrf); + } + if (vty->node == RPKI_VRF_NODE) + VTY_PUSH_CONTEXT_SUB(vty->node, rpki_vrf); + else + VTY_PUSH_CONTEXT(vty->node, rpki_vrf); return CMD_SUCCESS; } @@ -1109,23 +1717,49 @@ DEFPY (no_rpki, NO_STR "Enable rpki and enter rpki configuration mode\n") { - rpki_delete_all_cache_nodes(); - stop(); + struct rpki_vrf *rpki_vrf; + char *vrfname = NULL; + + if (vty->node == VRF_NODE) { + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (vrf->vrf_id != VRF_DEFAULT) + vrfname = vrf->name; + } + + rpki_vrf = find_rpki_vrf(vrfname); + + rpki_delete_all_cache_nodes(rpki_vrf); + stop(rpki_vrf); + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; + rpki_vrf->expire_interval = EXPIRE_INTERVAL_DEFAULT; + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; + return CMD_SUCCESS; } -DEFUN (bgp_rpki_start, +DEFPY (bgp_rpki_start, bgp_rpki_start_cmd, - "rpki start", + "rpki start [vrf NAME$vrfname]", RPKI_OUTPUT_STRING - "start rpki support\n") + "start rpki support\n" + VRF_CMD_HELP_STR) { - if (listcount(cache_list) == 0) + struct list *cache_list = NULL; + struct rpki_vrf *rpki_vrf; + + rpki_vrf = get_rpki_vrf(vrfname); + + if (!rpki_vrf) + return CMD_WARNING; + + cache_list = rpki_vrf->cache_list; + if (!cache_list || listcount(cache_list) == 0) vty_out(vty, "Could not start rpki because no caches are configured\n"); - if (!is_running()) { - if (start() == ERROR) { + if (!is_running(rpki_vrf)) { + if (start(rpki_vrf) == ERROR) { RPKI_DEBUG("RPKI failed to start"); return CMD_WARNING; } @@ -1133,14 +1767,19 @@ DEFUN (bgp_rpki_start, return CMD_SUCCESS; } -DEFUN (bgp_rpki_stop, +DEFPY (bgp_rpki_stop, bgp_rpki_stop_cmd, - "rpki stop", + "rpki stop [vrf NAME$vrfname]", RPKI_OUTPUT_STRING - "start rpki support\n") + "start rpki support\n" + VRF_CMD_HELP_STR) { - if (is_running()) - stop(); + struct rpki_vrf *rpki_vrf; + + rpki_vrf = get_rpki_vrf(vrfname); + + if (rpki_vrf && is_running(rpki_vrf)) + stop(rpki_vrf); return CMD_SUCCESS; } @@ -1152,7 +1791,17 @@ DEFPY (rpki_polling_period, "Set polling period\n" "Polling period value\n") { - polling_period = pp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + rpki_vrf->polling_period = pp; return CMD_SUCCESS; } @@ -1164,7 +1813,17 @@ DEFUN (no_rpki_polling_period, "Set polling period back to default\n" "Polling period value\n") { - polling_period = POLLING_PERIOD_DEFAULT; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; return CMD_SUCCESS; } @@ -1175,8 +1834,18 @@ DEFPY (rpki_expire_interval, "Set expire interval\n" "Expire interval value\n") { - if ((unsigned int)tmp >= polling_period) { - expire_interval = tmp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + if ((unsigned int)tmp >= rpki_vrf->polling_period) { + rpki_vrf->expire_interval = tmp; return CMD_SUCCESS; } @@ -1192,7 +1861,17 @@ DEFUN (no_rpki_expire_interval, "Set expire interval back to default\n" "Expire interval value\n") { - expire_interval = polling_period * 2; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + rpki_vrf->expire_interval = rpki_vrf->polling_period * 2; return CMD_SUCCESS; } @@ -1203,7 +1882,17 @@ DEFPY (rpki_retry_interval, "Set retry interval\n" "retry interval value\n") { - retry_interval = tmp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + rpki_vrf->retry_interval = tmp; return CMD_SUCCESS; } @@ -1215,7 +1904,17 @@ DEFUN (no_rpki_retry_interval, "Set retry interval back to default\n" "retry interval value\n") { - retry_interval = RETRY_INTERVAL_DEFAULT; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; return CMD_SUCCESS; } @@ -1238,8 +1937,24 @@ DEFPY(rpki_cache, rpki_cache_cmd, int return_value; struct listnode *cache_node; struct cache *current_cache; + struct rpki_vrf *rpki_vrf; + bool init; - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) { + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + if (!rpki_vrf || !rpki_vrf->cache_list) + return CMD_WARNING; + + init = !!list_isempty(rpki_vrf->cache_list); + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, + current_cache)) { if (current_cache->preference == preference) { vty_out(vty, "Cache with preference %ld is already configured\n", @@ -1248,21 +1963,20 @@ DEFPY(rpki_cache, rpki_cache_cmd, } } - // use ssh connection if (ssh_uname) { #if defined(FOUND_SSH) - return_value = - add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey, - server_pubkey, preference, bindaddr_str); + return_value = add_ssh_cache(rpki_vrf, cache, sshport, ssh_uname, + ssh_privkey, server_pubkey, + preference, bindaddr_str); #else return_value = SUCCESS; vty_out(vty, "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n"); #endif } else { // use tcp connection - return_value = - add_tcp_cache(cache, tcpport, preference, bindaddr_str); + return_value = add_tcp_cache(rpki_vrf, cache, tcpport, + preference, bindaddr_str); } if (return_value == ERROR) { @@ -1270,6 +1984,9 @@ DEFPY(rpki_cache, rpki_cache_cmd, return CMD_WARNING; } + if (init) + start(rpki_vrf); + return CMD_SUCCESS; } @@ -1291,18 +2008,31 @@ DEFPY (no_rpki_cache, "Preference of the cache server\n" "Preference value\n") { - struct cache *cache_p = find_cache(preference); + struct cache *cache_p; + struct list *cache_list = NULL; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); - if (!cache_p) { + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + cache_list = rpki_vrf->cache_list; + cache_p = find_cache(preference, cache_list); + if (!rpki_vrf || !cache_p) { vty_out(vty, "Could not find cache with preference %ld\n", preference); return CMD_WARNING; } - if (is_running() && listcount(cache_list) == 1) { - stop(); - } else if (is_running()) { - if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) { + if (is_running(rpki_vrf) && listcount(cache_list) == 1) { + stop(rpki_vrf); + } else if (is_running(rpki_vrf)) { + if (rtr_mgr_remove_group(rpki_vrf->rtr_config, preference) == + RTR_ERROR) { vty_out(vty, "Could not remove cache with preference %ld\n", preference); @@ -1318,68 +2048,107 @@ DEFPY (no_rpki_cache, DEFPY (show_rpki_prefix_table, show_rpki_prefix_table_cmd, - "show rpki prefix-table [json$uj]", + "show rpki $prefixkind [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show validated prefixes which were received from RPKI Cache\n" + "Show prefixes count which were received from RPKI Cache\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; + + if (uj) + json = json_object_new_object(); + + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } - if (!is_synchronized()) { - if (!uj) + if (is_synchronized(rpki_vrf)) { + if (strmatch(prefixkind, "prefix-count")) + print_prefix_table(vty, rpki_vrf, json, true); + else + print_prefix_table(vty, rpki_vrf, json, false); + } else { + if (json) { + json_object_string_add(json, "error", "No Connection to RPKI cache server."); + vty_json(vty, json); + } else vty_out(vty, "No connection to RPKI cache server.\n"); return CMD_WARNING; } - if (uj) - json = json_object_new_object(); - - print_prefix_table(vty, json); return CMD_SUCCESS; } DEFPY (show_rpki_as_number, show_rpki_as_number_cmd, - "show rpki as-number ASNUM$by_asn [json$uj]", + "show rpki as-number ASNUM$by_asn [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup by ASN in prefix table\n" "AS Number\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; + + if (uj) + json = json_object_new_object(); + + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } - if (!is_synchronized()) { - if (!uj) + if (!is_synchronized(rpki_vrf)) { + if (json) { + json_object_string_add(json, "error", "No Connection to RPKI cache server."); + vty_json(vty, json); + } else vty_out(vty, "No Connection to RPKI cache server.\n"); return CMD_WARNING; } - if (uj) - json = json_object_new_object(); - - print_prefix_table_by_asn(vty, by_asn, json); + print_prefix_table_by_asn(vty, by_asn, rpki_vrf, json); return CMD_SUCCESS; } DEFPY (show_rpki_prefix, show_rpki_prefix_cmd, - "show rpki prefix [ASNUM$asn] [json$uj]", + "show rpki prefix [ASNUM$asn] [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup IP prefix and optionally ASN in prefix table\n" "IPv4 prefix\n" "IPv6 prefix\n" "AS Number\n" + VRF_CMD_HELP_STR JSON_STR) { json_object *json = NULL; json_object *json_records = NULL; enum asnotation_mode asnotation; + struct rpki_vrf *rpki_vrf; - if (!is_synchronized()) { - if (!uj) + if (uj) + json = json_object_new_object(); + + rpki_vrf = get_rpki_vrf(vrfname); + + if (!rpki_vrf || !is_synchronized(rpki_vrf)) { + if (json) { + json_object_string_add(json, "error", "No Connection to RPKI cache server."); + vty_json(vty, json); + } else vty_out(vty, "No Connection to RPKI cache server.\n"); return CMD_WARNING; } @@ -1392,7 +2161,10 @@ DEFPY (show_rpki_prefix, memcpy(addr_str, prefix_str, addr_len); if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) { - if (!json) + if (json) { + json_object_string_add(json, "error", "Invalid IP prefix."); + vty_json(vty, json); + } else vty_out(vty, "Invalid IP prefix\n"); return CMD_WARNING; } @@ -1401,16 +2173,17 @@ DEFPY (show_rpki_prefix, unsigned int match_count = 0; enum pfxv_state result; - if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count, - asn, &addr, prefix->prefixlen, + if (pfx_table_validate_r(rpki_vrf->rtr_config->pfx_table, &matches, + &match_count, asn, &addr, prefix->prefixlen, &result) != PFX_SUCCESS) { - if (!json) + if (json) { + json_object_string_add(json, "error", "Prefix lookup failed."); + vty_json(vty, json); + } else vty_out(vty, "Prefix lookup failed\n"); return CMD_WARNING; } - if (uj) - json = json_object_new_object(); if (!json) { vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", @@ -1439,10 +2212,11 @@ DEFPY (show_rpki_prefix, DEFPY (show_rpki_cache_server, show_rpki_cache_server_cmd, - "show rpki cache-server [json$uj]", + "show rpki cache-server [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show configured cache server\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; @@ -1450,6 +2224,7 @@ DEFPY (show_rpki_cache_server, struct json_object *json_servers = NULL; struct listnode *cache_node; struct cache *cache; + struct rpki_vrf *rpki_vrf; if (uj) { json = json_object_new_object(); @@ -1457,7 +2232,14 @@ DEFPY (show_rpki_cache_server, json_object_object_add(json, "servers", json_servers); } - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (json) + vty_json(vty, json); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { if (cache->type == TCP) { if (!json) { vty_out(vty, @@ -1532,10 +2314,11 @@ DEFPY (show_rpki_cache_server, DEFPY (show_rpki_cache_connection, show_rpki_cache_connection_cmd, - "show rpki cache-connection [json$uj]", + "show rpki cache-connection [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show to which RPKI Cache Servers we have a connection\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; @@ -1544,25 +2327,35 @@ DEFPY (show_rpki_cache_connection, struct listnode *cache_node; struct cache *cache; struct rtr_mgr_group *group; + struct rpki_vrf *rpki_vrf; if (uj) json = json_object_new_object(); - if (!is_synchronized()) { - if (!json) - vty_out(vty, "No connection to RPKI cache server.\n"); - else + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (json) vty_json(vty, json); + return CMD_SUCCESS; + } + + if (!is_synchronized(rpki_vrf)) { + if (json) { + json_object_string_add(json, "error", "No connection to RPKI cache server."); + vty_json(vty, json); + } else + vty_out(vty, "No connection to RPKI cache server.\n"); return CMD_SUCCESS; } - group = get_connected_group(); + group = get_connected_group(rpki_vrf); if (!group) { - if (!json) - vty_out(vty, "Cannot find a connected group.\n"); - else + if (json) { + json_object_string_add(json, "error", "Cannot find a connected group."); vty_json(vty, json); + } else + vty_out(vty, "Cannot find a connected group.\n"); return CMD_SUCCESS; } @@ -1575,7 +2368,7 @@ DEFPY (show_rpki_cache_connection, json_object_object_add(json, "connections", json_conns); } - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { struct tr_tcp_config *tcp_config; #if defined(FOUND_SSH) struct tr_ssh_config *ssh_config; @@ -1656,19 +2449,110 @@ DEFPY (show_rpki_cache_connection, return CMD_SUCCESS; } +DEFPY(show_rpki_configuration, show_rpki_configuration_cmd, + "show rpki configuration [vrf NAME$vrfname] [json$uj]", + SHOW_STR RPKI_OUTPUT_STRING + "Show RPKI configuration\n" + VRF_CMD_HELP_STR + JSON_STR) +{ + struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; + + if (uj) + json = json_object_new_object(); + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (uj) { + json_object_boolean_add(json, "enabled", + !!listcount(rpki_vrf->cache_list)); + json_object_int_add(json, "serversCount", + listcount(rpki_vrf->cache_list)); + json_object_int_add(json, "pollingPeriodSeconds", + rpki_vrf->polling_period); + json_object_int_add(json, "retryIntervalSeconds", + rpki_vrf->retry_interval); + json_object_int_add(json, "expireIntervalSeconds", + rpki_vrf->expire_interval); + + vty_json(vty, json); + + return CMD_SUCCESS; + } + + vty_out(vty, "rpki is %s", + listcount(rpki_vrf->cache_list) ? "Enabled" : "Disabled"); + + if (list_isempty(rpki_vrf->cache_list)) { + vty_out(vty, "\n"); + return CMD_SUCCESS; + } + + vty_out(vty, " (%d cache servers configured)", + listcount(rpki_vrf->cache_list)); + vty_out(vty, "\n"); + vty_out(vty, "\tpolling period %d\n", rpki_vrf->polling_period); + vty_out(vty, "\tretry interval %d\n", rpki_vrf->retry_interval); + vty_out(vty, "\texpire interval %d\n", rpki_vrf->expire_interval); + + return CMD_SUCCESS; +} + static int config_on_exit(struct vty *vty) { - reset(false); + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + reset(false, rpki_vrf); return 1; } -DEFUN (rpki_reset, +DEFPY(rpki_reset, rpki_reset_cmd, + "rpki reset [vrf NAME$vrfname]", + RPKI_OUTPUT_STRING + "reset rpki\n" + VRF_CMD_HELP_STR) +{ + struct rpki_vrf *rpki_vrf; + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) + return CMD_WARNING; + + return reset(true, rpki_vrf) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; +} + +DEFPY (rpki_reset_config_mode, + rpki_reset_config_mode_cmd, "rpki reset", RPKI_OUTPUT_STRING "reset rpki\n") { - return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf) + return CMD_WARNING_CONFIG_FAILED; + + return reset(true, rpki_vrf) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; } DEFUN (debug_rpki, @@ -1677,7 +2561,10 @@ DEFUN (debug_rpki, DEBUG_STR "Enable debugging for rpki\n") { - rpki_debug = true; + if (vty->node == CONFIG_NODE) + rpki_debug_conf = true; + else + rpki_debug_term = true; return CMD_SUCCESS; } @@ -1688,7 +2575,10 @@ DEFUN (no_debug_rpki, DEBUG_STR "Disable debugging for rpki\n") { - rpki_debug = false; + if (vty->node == CONFIG_NODE) + rpki_debug_conf = false; + else + rpki_debug_term = false; return CMD_SUCCESS; } @@ -1735,8 +2625,9 @@ static void install_cli_commands(void) // TODO: make config write work install_node(&rpki_node); install_default(RPKI_NODE); + install_node(&rpki_vrf_node); + install_default(RPKI_VRF_NODE); install_element(CONFIG_NODE, &rpki_cmd); - install_element(ENABLE_NODE, &rpki_cmd); install_element(CONFIG_NODE, &no_rpki_cmd); @@ -1745,7 +2636,7 @@ static void install_cli_commands(void) /* Install rpki reset command */ install_element(ENABLE_NODE, &rpki_reset_cmd); - install_element(RPKI_NODE, &rpki_reset_cmd); + install_element(RPKI_NODE, &rpki_reset_config_mode_cmd); /* Install rpki polling period commands */ install_element(RPKI_NODE, &rpki_polling_period_cmd); @@ -1763,12 +2654,35 @@ static void install_cli_commands(void) install_element(RPKI_NODE, &rpki_cache_cmd); install_element(RPKI_NODE, &no_rpki_cache_cmd); + /* RPKI_VRF_NODE commands */ + install_element(VRF_NODE, &rpki_cmd); + install_element(VRF_NODE, &no_rpki_cmd); + /* Install rpki reset command */ + install_element(RPKI_VRF_NODE, &rpki_reset_config_mode_cmd); + + /* Install rpki polling period commands */ + install_element(RPKI_VRF_NODE, &rpki_polling_period_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_polling_period_cmd); + + /* Install rpki expire interval commands */ + install_element(RPKI_VRF_NODE, &rpki_expire_interval_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_expire_interval_cmd); + + /* Install rpki retry interval commands */ + install_element(RPKI_VRF_NODE, &rpki_retry_interval_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_retry_interval_cmd); + + /* Install rpki cache commands */ + install_element(RPKI_VRF_NODE, &rpki_cache_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_cache_cmd); + /* Install show commands */ install_element(VIEW_NODE, &show_rpki_prefix_table_cmd); install_element(VIEW_NODE, &show_rpki_cache_connection_cmd); install_element(VIEW_NODE, &show_rpki_cache_server_cmd); install_element(VIEW_NODE, &show_rpki_prefix_cmd); install_element(VIEW_NODE, &show_rpki_as_number_cmd); + install_element(VIEW_NODE, &show_rpki_configuration_cmd); /* Install debug commands */ install_element(CONFIG_NODE, &debug_rpki_cmd); diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h index de28715..4f2f87d 100644 --- a/bgpd/bgp_rpki.h +++ b/bgpd/bgp_rpki.h @@ -8,6 +8,8 @@ #ifndef __BGP_RPKI_H__ #define __BGP_RPKI_H__ +extern struct zebra_privs_t bgpd_privs; + enum rpki_states { RPKI_NOT_BEING_USED, RPKI_VALID, diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index ce9442c..065ea76 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -29,10 +29,93 @@ #include "bgpd/bgp_snmp_bgp4.h" #include "bgpd/bgp_snmp_bgp4v2.h" #include "bgpd/bgp_mplsvpn_snmp.h" +#include "bgpd/bgp_snmp_clippy.c" + + + +static int bgp_cli_snmp_traps_config_write(struct vty *vty); + +DEFPY(bgp_snmp_traps_rfc4273, bgp_snmp_traps_rfc4273_cmd, + "[no$no] bgp snmp traps rfc4273", + NO_STR BGP_STR + "Configure BGP SNMP\n" + "Configure SNMP traps for BGP\n" + "Configure use of rfc4273 SNMP traps for BGP\n") +{ + if (no) { + UNSET_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273); + return CMD_SUCCESS; + } + SET_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273); + return CMD_SUCCESS; +} + +DEFPY(bgp_snmp_traps_bgp4_mibv2, bgp_snmp_traps_bgp4_mibv2_cmd, + "[no$no] bgp snmp traps bgp4-mibv2", + NO_STR BGP_STR + "Configure BGP SNMP\n" + "Configure SNMP traps for BGP\n" + "Configure use of BGP4-MIBv2 SNMP traps for BGP\n") +{ + if (no) { + UNSET_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2); + return CMD_SUCCESS; + } + SET_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2); + return CMD_SUCCESS; +} + +static void bgp_snmp_traps_init(void) +{ + install_element(CONFIG_NODE, &bgp_snmp_traps_rfc4273_cmd); + install_element(CONFIG_NODE, &bgp_snmp_traps_bgp4_mibv2_cmd); + + SET_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273); + /* BGP4MIBv2 traps are disabled by default */ +} + +int bgp_cli_snmp_traps_config_write(struct vty *vty) +{ + int write = 0; + + if (!CHECK_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273)) { + vty_out(vty, "no bgp snmp traps rfc4273\n"); + write++; + } + if (CHECK_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2)) { + vty_out(vty, "bgp snmp traps bgp4-mibv2\n"); + write++; + } + + return write; +} + +int bgpTrapEstablished(struct peer *peer) +{ + if (CHECK_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273)) + bgp4TrapEstablished(peer); + + if (CHECK_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2)) + bgpv2TrapEstablished(peer); + + return 0; +} + +int bgpTrapBackwardTransition(struct peer *peer) +{ + if (CHECK_FLAG(bm->options, BGP_OPT_TRAPS_RFC4273)) + bgp4TrapBackwardTransition(peer); + + if (CHECK_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2)) + bgpv2TrapBackwardTransition(peer); + + return 0; +} static int bgp_snmp_init(struct event_loop *tm) { smux_init(tm); + bgp_snmp_traps_init(); bgp_snmp_bgp4_init(tm); bgp_snmp_bgp4v2_init(tm); bgp_mpls_l3vpn_module_init(); @@ -44,6 +127,8 @@ static int bgp_snmp_module_init(void) hook_register(peer_status_changed, bgpTrapEstablished); hook_register(peer_backward_transition, bgpTrapBackwardTransition); hook_register(frr_late_init, bgp_snmp_init); + hook_register(bgp_snmp_traps_config_write, + bgp_cli_snmp_traps_config_write); return 0; } diff --git a/bgpd/bgp_snmp.h b/bgpd/bgp_snmp.h index d324782..12ec652 100644 --- a/bgpd/bgp_snmp.h +++ b/bgpd/bgp_snmp.h @@ -15,4 +15,7 @@ #define IPADDRESS ASN_IPADDRESS #define GAUGE32 ASN_UNSIGNED +extern int bgpTrapEstablished(struct peer *peer); +extern int bgpTrapBackwardTransition(struct peer *peer); + #endif /* _FRR_BGP_SNMP_H_ */ diff --git a/bgpd/bgp_snmp_bgp4.c b/bgpd/bgp_snmp_bgp4.c index 692e232..3d04dc2 100644 --- a/bgpd/bgp_snmp_bgp4.c +++ b/bgpd/bgp_snmp_bgp4.c @@ -757,7 +757,7 @@ static struct variable bgp_variables[] = { {6, 1, 14}}, }; -int bgpTrapEstablished(struct peer *peer) +int bgp4TrapEstablished(struct peer *peer) { int ret; struct in_addr addr; @@ -782,7 +782,7 @@ int bgpTrapEstablished(struct peer *peer) return 0; } -int bgpTrapBackwardTransition(struct peer *peer) +int bgp4TrapBackwardTransition(struct peer *peer) { int ret; struct in_addr addr; diff --git a/bgpd/bgp_snmp_bgp4.h b/bgpd/bgp_snmp_bgp4.h index ccf00d6..67f7cc6 100644 --- a/bgpd/bgp_snmp_bgp4.h +++ b/bgpd/bgp_snmp_bgp4.h @@ -69,8 +69,8 @@ #define BGP4PATHATTRBEST 13 #define BGP4PATHATTRUNKNOWN 14 -extern int bgpTrapEstablished(struct peer *peer); -extern int bgpTrapBackwardTransition(struct peer *peer); +extern int bgp4TrapEstablished(struct peer *peer); +extern int bgp4TrapBackwardTransition(struct peer *peer); extern int bgp_snmp_bgp4_init(struct event_loop *tm); #endif /* _FRR_BGP_SNMP_BGP4_H_ */ diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c index f022284..0c8ed33 100644 --- a/bgpd/bgp_snmp_bgp4v2.c +++ b/bgpd/bgp_snmp_bgp4v2.c @@ -32,6 +32,7 @@ SNMP_LOCAL_VARIABLES static oid bgpv2_oid[] = {BGP4V2MIB}; +static oid bgpv2_trap_oid[] = { BGP4V2MIB, 0 }; static struct in_addr bgp_empty_addr = {}; static struct peer *peer_lookup_all_vrf(struct ipaddr *addr) @@ -435,7 +436,6 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, struct bgp_path_info *path, *min; struct bgp_dest *dest; union sockunion su; - unsigned int len; struct ipaddr paddr = {}; size_t namelen = v ? v->namelen : BGP4V2_NLRI_ENTRY_OFFSET; sa_family_t family; @@ -445,20 +445,28 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, size_t afi_len; long prefix_type = 0; long peer_addr_type = 0; + long nrli_index = 1; + long cur_index = 0; /* Bgp4V2AddressFamilyIdentifierTC limited to IPv6 */ if (name[namelen - 1] > IANA_AFI_IPV6) return NULL; afi = afi_iana2int(name[namelen - 1]); afi_len = afi == AFI_IP ? IN_ADDR_SIZE : IN6_ADDR_SIZE; + assert(IS_VALID_AFI(afi)); #define BGP_NLRI_ENTRY_OFFSET namelen +#define BGP4V2_NLRI_V4_V4_OFFSET IN_ADDR_SIZE + IN_ADDR_SIZE + 5 +#define BGP4V2_NLRI_V4_V6_OFFSET IN_ADDR_SIZE + IN6_ADDR_SIZE + 5 +#define BGP4V2_NLRI_V6_V6_OFFSET IN6_ADDR_SIZE + IN6_ADDR_SIZE + 5 sockunion_init(&su); if (exact) { - if (*length - namelen != BGP_NLRI_ENTRY_OFFSET) + if (*length - namelen != BGP4V2_NLRI_V4_V4_OFFSET && + *length - namelen != BGP4V2_NLRI_V4_V6_OFFSET && + *length - namelen != BGP4V2_NLRI_V6_V6_OFFSET) return NULL; /* Set OID offset for prefix type */ @@ -502,12 +510,17 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, su.sin.sin_family = family; /* get bgp4V2PeerRemoteAddr*/ - if (family == AF_INET) + if (family == AF_INET) { oid2in_addr(offset, IN_ADDR_SIZE, &su.sin.sin_addr); - else + offset += IN_ADDR_SIZE; + } else { oid2in6_addr(offset, &su.sin6.sin6_addr); + offset += IN6_ADDR_SIZE; + } - /* bgp4V2NlriIndex currently ignored */ + /* bgp4V2NlriIndex */ + nrli_index = *offset; + offset++; /* Lookup node */ dest = bgp_node_lookup(bgp->rib[afi][safi], addr); @@ -515,8 +528,11 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, for (path = bgp_dest_get_bgp_path_info(dest); path; path = path->next) if (sockunion_same(&path->peer->connection->su, - &su)) - return path; + &su)) { + cur_index++; + if (cur_index == nrli_index) + return path; + } bgp_dest_unlock_node(dest); } @@ -527,11 +543,9 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, /* Set OID offset for prefix type */ offset = name + namelen; offsetlen = *length - namelen; - len = offsetlen; if (offsetlen == 0) { dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); - safi = SAFI_UNICAST; } else { /* bgp4V2NlriAfi is already get */ @@ -571,18 +585,13 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, offsetlen--; /* get node */ - dest = bgp_node_get(bgp->rib[afi][safi], addr); + dest = bgp_node_lookup(bgp->rib[afi][safi], addr); } if (!dest) return NULL; if (offsetlen > 0) { - len = offsetlen; - if (len > afi_len) - len = afi_len; - - /* get bgp4V2PeerRemoteAddrType */ peer_addr_type = *offset; if (peer_addr_type == IANA_AFI_IPV4) @@ -591,10 +600,17 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, family = AF_INET6; offset++; - if (family == AF_INET) + if (family == AF_INET) { oid2in_addr(offset, IN_ADDR_SIZE, &paddr.ip._v4_addr); - else + offset += IN_ADDR_SIZE; + } else { oid2in6_addr(offset, &paddr.ip._v6_addr); + offset += IN6_ADDR_SIZE; + } + /* get bgp4V2NlriIndex */ + nrli_index = *offset; + offset++; + } else { /* default case start with ipv4*/ if (afi == AFI_IP) @@ -602,11 +618,13 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, else family = AF_INET6; memset(&paddr.ip, 0, sizeof(paddr.ip)); + nrli_index = 1; } do { min = NULL; min_family = 0; + cur_index = 0; for (path = bgp_dest_get_bgp_path_info(dest); path; path = path->next) { @@ -616,19 +634,44 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, if (path_family < family) continue; - if (family == AF_INET - && IPV4_ADDR_CMP(&paddr.ip._v4_addr, - &path->peer->connection->su.sin.sin_addr) - >= 0) + if (family == AF_INET && + IPV4_ADDR_CMP(&paddr.ip._v4_addr, + &path->peer->connection->su.sin + .sin_addr) > 0) + continue; + else if (family == AF_INET6 && + IPV6_ADDR_CMP(&paddr.ip._v6_addr, + &path->peer->connection->su.sin6 + .sin6_addr) > 0) + continue; + + if (family == AF_INET && + IPV4_ADDR_CMP(&paddr.ip._v4_addr, + &path->peer->connection->su.sin + .sin_addr) == 0) { + if (cur_index == nrli_index) { + min = path; + min_family = family; + nrli_index++; + break; + } + cur_index++; continue; - else if (family == AF_INET6 - && IPV6_ADDR_CMP( - &paddr.ip._v6_addr, - &path->peer->connection->su.sin6.sin6_addr) - >= 0) + } else if (family == AF_INET6 && + IPV6_ADDR_CMP(&paddr.ip._v6_addr, + &path->peer->connection->su + .sin6.sin6_addr) == 0) { + if (cur_index == nrli_index) { + min = path; + min_family = family; + nrli_index++; + break; + } + cur_index++; continue; + } - /* first valid path its the min*/ + /* first valid path its the min peer addr*/ if (!min) { min = path; min_family = path_family; @@ -704,7 +747,7 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, /* Encode bgp4V2NlriIndex*/ - *offset = 1; + *offset = nrli_index; offset++; *length = offset - name; @@ -718,6 +761,7 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length, } memset(&paddr.ip, 0, sizeof(paddr.ip)); + nrli_index = 1; } while ((dest = bgp_route_next(dest))); @@ -896,6 +940,37 @@ static uint8_t *bgp4v2PathAttrTable(struct variable *v, oid name[], return NULL; } +/* BGP V2 Traps. */ +static struct trap_object bgpv2TrapEstListv4[] = { + { 6, { 1, 2, 1, BGP4V2_PEER_STATE, 1, 1 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 1, 1 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 1, 1 } } +}; + +static struct trap_object bgpv2TrapEstListv6[] = { + { 6, { 1, 2, 1, BGP4V2_PEER_STATE, 1, 2 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 1, 2 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 1, 2 } } +}; + +static struct trap_object bgpv2TrapBackListv4[] = { + { 6, { 1, 2, 1, BGP4V2_PEER_STATE, 1, 1 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 1, 1 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 1, 1 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_CODE_RECEIVED, 1, 1 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_SUBCODE_RECEIVED, 1, 1 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_RECEIVED_TEXT, 1, 1 } } +}; + +static struct trap_object bgpv2TrapBackListv6[] = { + { 6, { 1, 2, 1, BGP4V2_PEER_STATE, 1, 2 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_LOCAL_PORT, 1, 2 } }, + { 6, { 1, 2, 1, BGP4V2_PEER_REMOTE_PORT, 1, 2 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_CODE_RECEIVED, 1, 2 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_SUBCODE_RECEIVED, 1, 2 } }, + { 6, { 1, 3, 1, BGP4V2_PEER_LAST_ERROR_RECEIVED_TEXT, 1, 2 } } +}; + static struct variable bgpv2_variables[] = { /* bgp4V2PeerEntry */ {BGP4V2_PEER_INSTANCE, @@ -1515,6 +1590,79 @@ static struct variable bgpv2_variables[] = { {1, 9, 1, BGP4V2_NLRI_PATH_ATTR_UNKNOWN, 1, 2}}, }; +int bgpv2TrapEstablished(struct peer *peer) +{ + oid index[sizeof(oid) * IN6_ADDR_SIZE]; + size_t length; + + if (!CHECK_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2)) + return 0; + + /* Check if this peer just went to Established */ + if ((peer->connection->ostatus != OpenConfirm) || + !(peer_established(peer->connection))) + return 0; + + switch (sockunion_family(&peer->connection->su)) { + case AF_INET: + oid_copy_in_addr(index, &peer->connection->su.sin.sin_addr); + length = IN_ADDR_SIZE; + smux_trap(bgpv2_variables, array_size(bgpv2_variables), + bgpv2_trap_oid, array_size(bgpv2_trap_oid), bgpv2_oid, + sizeof(bgpv2_oid) / sizeof(oid), index, length, + bgpv2TrapEstListv4, array_size(bgpv2TrapEstListv4), + BGP4V2ESTABLISHED); + break; + case AF_INET6: + oid_copy_in6_addr(index, &peer->connection->su.sin6.sin6_addr); + length = IN6_ADDR_SIZE; + smux_trap(bgpv2_variables, array_size(bgpv2_variables), + bgpv2_trap_oid, array_size(bgpv2_trap_oid), bgpv2_oid, + sizeof(bgpv2_oid) / sizeof(oid), index, length, + bgpv2TrapEstListv6, array_size(bgpv2TrapEstListv6), + BGP4V2ESTABLISHED); + break; + default: + break; + } + + return 0; +} + +int bgpv2TrapBackwardTransition(struct peer *peer) +{ + oid index[sizeof(oid) * IN6_ADDR_SIZE]; + size_t length; + + if (!CHECK_FLAG(bm->options, BGP_OPT_TRAPS_BGP4MIBV2)) + return 0; + + switch (sockunion_family(&peer->connection->su)) { + case AF_INET: + oid_copy_in_addr(index, &peer->connection->su.sin.sin_addr); + length = IN_ADDR_SIZE; + smux_trap(bgpv2_variables, array_size(bgpv2_variables), + bgpv2_trap_oid, array_size(bgpv2_trap_oid), bgpv2_oid, + sizeof(bgpv2_oid) / sizeof(oid), index, length, + bgpv2TrapBackListv4, array_size(bgpv2TrapBackListv4), + BGP4V2BACKWARDTRANSITION); + break; + case AF_INET6: + oid_copy_in6_addr(index, &peer->connection->su.sin6.sin6_addr); + length = IN6_ADDR_SIZE; + smux_trap(bgpv2_variables, array_size(bgpv2_variables), + bgpv2_trap_oid, array_size(bgpv2_trap_oid), bgpv2_oid, + sizeof(bgpv2_oid) / sizeof(oid), index, length, + bgpv2TrapBackListv6, array_size(bgpv2TrapBackListv6), + BGP4V2BACKWARDTRANSITION); + break; + default: + break; + } + + return 0; +} + int bgp_snmp_bgp4v2_init(struct event_loop *tm) { REGISTER_MIB("mibII/bgpv2", bgpv2_variables, variable, bgpv2_oid); diff --git a/bgpd/bgp_snmp_bgp4v2.h b/bgpd/bgp_snmp_bgp4v2.h index 6587a82..ca35533 100644 --- a/bgpd/bgp_snmp_bgp4v2.h +++ b/bgpd/bgp_snmp_bgp4v2.h @@ -16,6 +16,14 @@ * offset 1.3.6.1.3.5.1.1.2.1.x.(1|2).(4|16) = 13 * offset 1.3.6.1.4.1.7336.3.2.1.1.2.1.x.1.(1|2) = 16 */ + + +/* bgpTraps */ +#define BGP4V2ESTABLISHED 1 +#define BGP4V2BACKWARDTRANSITION 2 + +/* bgpPeerTable */ + #define BGP4V2_PEER_ENTRY_OFFSET 13 #define BGP4V2_PEER_INSTANCE 1 #define BGP4V2_PEER_LOCAL_ADDR_TYPE 2 @@ -84,5 +92,7 @@ #define BGP4V2_BACKWARD_TRANSITION_NOTIFICATION 2 extern int bgp_snmp_bgp4v2_init(struct event_loop *tm); +extern int bgpv2TrapEstablished(struct peer *peer); +extern int bgpv2TrapBackwardTransition(struct peer *peer); #endif /* _FRR_BGP_SNMP_BGP4V2_H_ */ diff --git a/bgpd/bgp_trace.h b/bgpd/bgp_trace.h index 964393a..a77a25e 100644 --- a/bgpd/bgp_trace.h +++ b/bgpd/bgp_trace.h @@ -135,11 +135,12 @@ TRACEPOINT_LOGLEVEL(frr_bgp, bmp_mirror_packet, TRACE_INFO) TRACEPOINT_EVENT( frr_bgp, bmp_eor, - TP_ARGS(afi_t, afi, safi_t, safi, uint8_t, flags), + TP_ARGS(afi_t, afi, safi_t, safi, uint8_t, flags, uint8_t, peer_type_flag), TP_FIELDS( ctf_integer(afi_t, afi, afi) ctf_integer(safi_t, safi, safi) ctf_integer(uint8_t, flags, flags) + ctf_integer(uint8_t, peer_type_flag, peer_type_flag) ) ) @@ -307,7 +308,8 @@ TRACEPOINT_EVENT( struct in_addr, vtep, esi_t *, esi), TP_FIELDS( ctf_string(action, add ? "add" : "del") - ctf_integer(vni_t, vni, vpn->vni) + ctf_integer(vni_t, vni, (vpn ? vpn->vni : 0)) + ctf_integer(uint32_t, eth_tag, &pfx->prefix.macip_addr.eth_tag) ctf_array(unsigned char, mac, &pfx->prefix.macip_addr.mac, sizeof(struct ethaddr)) ctf_array(unsigned char, ip, &pfx->prefix.macip_addr.ip, @@ -325,7 +327,7 @@ TRACEPOINT_EVENT( const struct prefix_evpn *, pfx), TP_FIELDS( ctf_string(action, add ? "add" : "del") - ctf_integer(vni_t, vni, vpn->vni) + ctf_integer(vni_t, vni, (vpn ? vpn->vni : 0)) ctf_integer_network_hex(unsigned int, vtep, pfx->prefix.imet_addr.ip.ipaddr_v4.s_addr) ) @@ -434,6 +436,64 @@ TRACEPOINT_EVENT( ) TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_es_evi_del_zrecv, TRACE_INFO) +TRACEPOINT_EVENT( + frr_bgp, + evpn_mh_es_evi_vtep_add, + TP_ARGS(esi_t *, esi, vni_t, vni, struct in_addr, vtep, + uint8_t, ead_es), + TP_FIELDS( + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + ctf_integer(vni_t, vni, vni) + ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr) + ctf_integer(uint8_t, ead_es, ead_es) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_es_evi_vtep_add, TRACE_INFO) + +TRACEPOINT_EVENT( + frr_bgp, + evpn_mh_es_evi_vtep_del, + TP_ARGS(esi_t *, esi, vni_t, vni, struct in_addr, vtep, + uint8_t, ead_es), + TP_FIELDS( + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + ctf_integer(vni_t, vni, vni) + ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr) + ctf_integer(uint8_t, ead_es, ead_es) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_es_evi_vtep_del, TRACE_INFO) + +TRACEPOINT_EVENT( + frr_bgp, + evpn_mh_local_ead_es_evi_route_upd, + TP_ARGS(esi_t *, esi, vni_t, vni, + uint8_t, route_type, + struct in_addr, vtep), + TP_FIELDS( + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + ctf_integer(vni_t, vni, vni) + ctf_integer(uint8_t, route_type, route_type) + ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_ead_es_evi_route_upd, TRACE_INFO) + +TRACEPOINT_EVENT( + frr_bgp, + evpn_mh_local_ead_es_evi_route_del, + TP_ARGS(esi_t *, esi, vni_t, vni, + uint8_t, route_type, + struct in_addr, vtep), + TP_FIELDS( + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + ctf_integer(vni_t, vni, vni) + ctf_integer(uint8_t, route_type, route_type) + ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_ead_es_evi_route_del, TRACE_INFO) + TRACEPOINT_EVENT( frr_bgp, evpn_local_vni_add_zrecv, @@ -494,6 +554,34 @@ TRACEPOINT_EVENT( ) TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_macip_del_zrecv, TRACE_INFO) +TRACEPOINT_EVENT( + frr_bgp, + evpn_advertise_type5, + TP_ARGS(vrf_id_t, vrf, const struct prefix_evpn *, pfx, + struct ethaddr *, rmac, struct in_addr, vtep), + TP_FIELDS( + ctf_integer(int, vrf_id, vrf) + ctf_array(unsigned char, ip, &pfx->prefix.prefix_addr.ip, + sizeof(struct ipaddr)) + ctf_array(unsigned char, rmac, rmac, + sizeof(struct ethaddr)) + ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_advertise_type5, TRACE_INFO) + +TRACEPOINT_EVENT( + frr_bgp, + evpn_withdraw_type5, + TP_ARGS(vrf_id_t, vrf, const struct prefix_evpn *, pfx), + TP_FIELDS( + ctf_integer(int, vrf_id, vrf) + ctf_array(unsigned char, ip, &pfx->prefix.prefix_addr.ip, + sizeof(struct ipaddr)) + ) +) +TRACEPOINT_LOGLEVEL(frr_bgp, evpn_withdraw_type5, TRACE_INFO) + TRACEPOINT_EVENT( frr_bgp, evpn_local_l3vni_add_zrecv, diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index a2006c3..d13515a 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -128,6 +128,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi, dst->bgp = src->bgp; dst->sort = src->sort; + dst->sub_sort = src->sub_sort; dst->as = src->as; dst->v_routeadv = src->v_routeadv; dst->flags = src->flags; @@ -342,6 +343,7 @@ static unsigned int updgrp_hash_key_make(const void *p) key = 0; key = jhash_1word(peer->sort, key); /* EBGP or IBGP */ + key = jhash_1word(peer->sub_sort, key); /* OAD */ key = jhash_1word((peer->flags & PEER_UPDGRP_FLAGS), key); key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key); key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key); @@ -447,19 +449,18 @@ static unsigned int updgrp_hash_key_make(const void *p) * STATEMENT STAYS UP TO DATE */ if (bgp_debug_neighbor_events(peer)) { - zlog_debug( - "%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %ju", - peer, peer->sort, - (intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS), - (intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS)); - zlog_debug( - "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u, as_path_loop_detection: %d", - peer, (uint32_t)peer->addpath_type[afi][safi], - CHECK_FLAG(peer->cap, PEER_UPDGRP_CAP_FLAGS), - CHECK_FLAG(peer->af_cap[afi][safi], - PEER_UPDGRP_AF_CAP_FLAGS), - peer->v_routeadv, peer->change_local_as, - peer->as_path_loop_detection); + zlog_debug("%pBP Update Group Hash: sort: %d sub_sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %ju", + peer, peer->sort, peer->sub_sort, + (intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS), + (intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS)); + zlog_debug("%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %ju UpdGrpCapAFFlag: %u route_adv: %u change local as: %u, as_path_loop_detection: %d", + peer, (uint32_t)peer->addpath_type[afi][safi], + (intmax_t)CHECK_FLAG(peer->cap, + PEER_UPDGRP_CAP_FLAGS), + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_UPDGRP_AF_CAP_FLAGS), + peer->v_routeadv, peer->change_local_as, + peer->as_path_loop_detection); zlog_debug( "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s", peer, peer->max_packet_size, peer->pmax_out[afi][safi], @@ -1698,14 +1699,14 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg) */ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); - subgroup_default_originate(subgrp, 0); + subgroup_default_originate(subgrp, false); } else { /* * This is a explicit withdraw, since the * routemap is not present in routemap lib. need - * to pass 1 for withdraw arg. + * to pass `true` for withdraw arg. */ - subgroup_default_originate(subgrp, 1); + subgroup_default_originate(subgrp, true); } } update_subgroup_set_needs_refresh(subgrp, 0); @@ -2101,7 +2102,7 @@ update_group_default_originate_route_map_walkcb(struct update_group *updgrp, */ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); - subgroup_default_originate(subgrp, 0); + subgroup_default_originate(subgrp, false); } } diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 0d866f3..5e8b537 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -40,17 +40,16 @@ (PEER_FLAG_LOCAL_AS_NO_PREPEND | PEER_FLAG_LOCAL_AS_REPLACE_AS) #define PEER_UPDGRP_AF_FLAGS \ - (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY \ - | PEER_FLAG_SEND_LARGE_COMMUNITY \ - | PEER_FLAG_DEFAULT_ORIGINATE | PEER_FLAG_REFLECTOR_CLIENT \ - | PEER_FLAG_RSERVER_CLIENT | PEER_FLAG_NEXTHOP_SELF \ - | PEER_FLAG_NEXTHOP_UNCHANGED | PEER_FLAG_FORCE_NEXTHOP_SELF \ - | PEER_FLAG_AS_PATH_UNCHANGED | PEER_FLAG_MED_UNCHANGED \ - | PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED | PEER_FLAG_REMOVE_PRIVATE_AS \ - | PEER_FLAG_REMOVE_PRIVATE_AS_ALL \ - | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE \ - | PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE \ - | PEER_FLAG_AS_OVERRIDE) + (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY | \ + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI | PEER_FLAG_SEND_LARGE_COMMUNITY | \ + PEER_FLAG_DEFAULT_ORIGINATE | PEER_FLAG_REFLECTOR_CLIENT | \ + PEER_FLAG_RSERVER_CLIENT | PEER_FLAG_NEXTHOP_SELF | \ + PEER_FLAG_NEXTHOP_UNCHANGED | PEER_FLAG_FORCE_NEXTHOP_SELF | \ + PEER_FLAG_AS_PATH_UNCHANGED | PEER_FLAG_MED_UNCHANGED | \ + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED | PEER_FLAG_REMOVE_PRIVATE_AS | \ + PEER_FLAG_REMOVE_PRIVATE_AS_ALL | \ + PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE | \ + PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE | PEER_FLAG_AS_OVERRIDE) #define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV) @@ -428,7 +427,7 @@ extern void subgroup_announce_route(struct update_subgroup *subgrp); extern void subgroup_announce_all(struct update_subgroup *subgrp); extern void subgroup_default_originate(struct update_subgroup *subgrp, - int withdraw); + bool withdraw); extern void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *dest, struct bgp_path_info *pi); @@ -442,7 +441,7 @@ extern struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp, extern void bgp_adj_out_remove_subgroup(struct bgp_dest *dest, struct bgp_adj_out *adj, struct update_subgroup *subgrp); -extern void bgp_adj_out_set_subgroup(struct bgp_dest *dest, +extern bool bgp_adj_out_set_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, struct attr *attr, struct bgp_path_info *path); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index ccbb23e..267a756 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -198,9 +198,8 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("%s: afi=%s, safi=%s, p=%pRN", __func__, - afi2str(afi), safi2str(safi), - bgp_dest_to_rnode(ctx->dest)); + zlog_debug("%s: afi=%s, safi=%s, p=%pBD", __func__, + afi2str(afi), safi2str(safi), ctx->dest); UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { /* @@ -479,7 +478,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, bgp_advertise_delete(baa, adv); /* Fetch next advertise candidate. */ - next = baa->adv; + next = bgp_advertise_attr_fifo_first(&baa->fifo); /* Unintern BGP advertise attribute. */ bgp_advertise_attr_unintern(subgrp->hash, baa); @@ -497,7 +496,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, return next; } -void bgp_adj_out_set_subgroup(struct bgp_dest *dest, +bool bgp_adj_out_set_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, struct attr *attr, struct bgp_path_info *path) { @@ -517,7 +516,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, bgp = SUBGRP_INST(subgrp); if (DISABLE_BGP_ANNOUNCE) - return; + return false; /* Look for adjacency information. */ adj = adj_lookup( @@ -533,7 +532,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, bgp_addpath_id_for_peer(peer, afi, safi, &path->tx_addpath)); if (!adj) - return; + return false; subgrp->pscount++; } @@ -572,7 +571,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, * will never be able to coalesce the 3rd peer down */ subgrp->version = MAX(subgrp->version, dest->version); - return; + return false; } if (adj->adv) @@ -620,6 +619,8 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, bgp_adv_fifo_add_tail(&subgrp->sync->update, adv); subgrp->version = MAX(subgrp->version, dest->version); + + return true; } /* The only time 'withdraw' will be false is if we are sending @@ -734,7 +735,7 @@ void subgroup_announce_table(struct update_subgroup *subgrp, if (safi != SAFI_MPLS_VPN && safi != SAFI_ENCAP && safi != SAFI_EVPN && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) - subgroup_default_originate(subgrp, 0); + subgroup_default_originate(subgrp, false); subgrp->pscount = 0; SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING); @@ -826,11 +827,12 @@ void subgroup_announce_route(struct update_subgroup *subgrp) } } -void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) +void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw) { struct bgp *bgp; - struct attr attr; + struct attr attr = { 0 }; struct attr *new_attr = &attr; + struct aspath *aspath; struct prefix p; struct peer *from; struct bgp_dest *dest; @@ -868,6 +870,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) /* make coverity happy */ assert(attr.aspath); + aspath = attr.aspath; attr.med = 0; attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); @@ -915,8 +918,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) bgp_attr_flush(new_attr); new_attr = bgp_attr_intern( tmp_pi.attr); - bgp_attr_flush(tmp_pi.attr); } + bgp_attr_flush(tmp_pi.attr); subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) ? AF_INET6 @@ -941,7 +944,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) SUBGRP_STATUS_DEFAULT_ORIGINATE))) SET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); - withdraw = 1; + withdraw = true; } } @@ -968,18 +971,19 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) if (dest) { for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { - if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) - if (subgroup_announce_check( - dest, pi, subgrp, - bgp_dest_get_prefix(dest), - &attr, NULL)) { - struct attr *default_attr = - bgp_attr_intern(&attr); - - bgp_adj_out_set_subgroup( - dest, subgrp, - default_attr, pi); - } + if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + continue; + + if (subgroup_announce_check(dest, pi, subgrp, + bgp_dest_get_prefix( + dest), + &attr, NULL)) { + if (!bgp_adj_out_set_subgroup(dest, + subgrp, + &attr, pi)) + bgp_attr_flush(&attr); + } else + bgp_attr_flush(&attr); } bgp_dest_unlock_node(dest); } @@ -1023,7 +1027,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) } } - aspath_unintern(&attr.aspath); + aspath_unintern(&aspath); } /* diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c index 0ed1d04..8fe24eb 100644 --- a/bgpd/bgp_vpn.c +++ b/bgpd/bgp_vpn.c @@ -30,8 +30,6 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, int rd_header; int header = 1; json_object *json = NULL; - json_object *json_scode = NULL; - json_object *json_ocode = NULL; json_object *json_adv = NULL; json_object *json_routes = NULL; char rd_str[BUFSIZ]; @@ -47,21 +45,8 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, } if (use_json) { - json_scode = json_object_new_object(); - json_ocode = json_object_new_object(); json = json_object_new_object(); json_adv = json_object_new_object(); - - json_object_string_add(json_scode, "suppressed", "s"); - json_object_string_add(json_scode, "damped", "d"); - json_object_string_add(json_scode, "history", "h"); - json_object_string_add(json_scode, "valid", "*"); - json_object_string_add(json_scode, "best", ">"); - json_object_string_add(json_scode, "internal", "i"); - - json_object_string_add(json_ocode, "igp", "i"); - json_object_string_add(json_ocode, "egp", "e"); - json_object_string_add(json_ocode, "incomplete", "?"); } for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; @@ -117,12 +102,6 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, json_object_int_add( json, "localAS", bgp->as); - json_object_object_add(json, - "bgpStatusCodes", - json_scode); - json_object_object_add(json, - "bgpOriginCodes", - json_ocode); } else { vty_out(vty, "BGP table version is 0, local router ID is %pI4\n", diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index e49baff..9530a66 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -5,6 +5,10 @@ #include +#ifdef GNU_LINUX +#include //RT_TABLE_XXX +#endif + #include "command.h" #include "lib/json.h" #include "lib/sockopt.h" @@ -122,12 +126,17 @@ FRR_CFG_DEFAULT_BOOL(BGP_SOFT_VERSION_CAPABILITY, { .val_bool = true, .match_profile = "datacenter", }, { .val_bool = false }, ); +FRR_CFG_DEFAULT_BOOL(BGP_ENFORCE_FIRST_AS, + { .val_bool = false, .match_version = "< 9.1", }, + { .val_bool = true }, +); DEFINE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)); DEFINE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp)); DEFINE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp)); +DEFINE_HOOK(bgp_snmp_traps_config_write, (struct vty * vty), (vty)); static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -310,7 +319,7 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) /* refresh functions */ for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) { listnode_delete(bgp->srv6_functions, func); - XFREE(MTYPE_BGP_SRV6_FUNCTION, func); + srv6_function_free(func); } /* refresh tovpn_sid */ @@ -614,6 +623,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, if (DFLT_BGP_SOFT_VERSION_CAPABILITY) SET_FLAG((*bgp)->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY); + if (DFLT_BGP_ENFORCE_FIRST_AS) + SET_FLAG((*bgp)->flags, BGP_FLAG_ENFORCE_FIRST_AS); ret = BGP_SUCCESS; } @@ -1591,8 +1602,9 @@ DEFUN_NOSH (router_bgp, * - update asnotation if explicitly mentioned */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) { - XFREE(MTYPE_BGP, bgp->as_pretty); - bgp->as_pretty = XSTRDUP(MTYPE_BGP, argv[idx_asn]->arg); + XFREE(MTYPE_BGP_NAME, bgp->as_pretty); + bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, + argv[idx_asn]->arg); if (!CHECK_FLAG(bgp->config, BGP_CONFIG_ASNOTATION) && asnotation != ASNOTATION_UNDEFINED) { SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION); @@ -2827,6 +2839,23 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, return CMD_SUCCESS; } +DEFPY(bgp_enforce_first_as, + bgp_enforce_first_as_cmd, + "[no] bgp enforce-first-as", + NO_STR + BGP_STR + "Enforce the first AS for EBGP routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (no) + UNSET_FLAG(bgp->flags, BGP_FLAG_ENFORCE_FIRST_AS); + else + SET_FLAG(bgp->flags, BGP_FLAG_ENFORCE_FIRST_AS); + + return CMD_SUCCESS; +} + DEFPY(bgp_lu_uses_explicit_null, bgp_lu_uses_explicit_null_cmd, "[no] bgp labeled-unicast $value", NO_STR BGP_STR @@ -3231,6 +3260,8 @@ DEFUN (bgp_graceful_restart_disable, GR_DISABLE) { int ret = BGP_GR_FAILURE; + struct listnode *node, *nnode; + struct peer *peer; if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) zlog_debug( @@ -3249,6 +3280,15 @@ DEFUN (bgp_graceful_restart_disable, vty_out(vty, "Graceful restart configuration changed, reset all peers to take effect\n"); + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, + CAPABILITY_CODE_RESTART, + CAPABILITY_ACTION_UNSET); + bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, + CAPABILITY_CODE_LLGR, + CAPABILITY_ACTION_UNSET); + } + return bgp_vty_return(vty, ret); } @@ -5706,6 +5746,37 @@ DEFUN (no_neighbor_dont_capability_negotiate, PEER_FLAG_DONT_CAPABILITY); } +/* neighbor capability fqdn */ +DEFPY (neighbor_capability_fqdn, + neighbor_capability_fqdn_cmd, + "[no$no] neighbor $neighbor capability fqdn", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise fqdn capability to the peer\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + ret = peer_flag_unset_vty(vty, neighbor, + PEER_FLAG_CAPABILITY_FQDN); + else + ret = peer_flag_set_vty(vty, neighbor, + PEER_FLAG_CAPABILITY_FQDN); + + bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, CAPABILITY_CODE_FQDN, + no ? CAPABILITY_ACTION_UNSET + : CAPABILITY_ACTION_SET); + + return ret; +} + /* neighbor capability extended next hop encoding */ DEFUN (neighbor_capability_enhe, neighbor_capability_enhe_cmd, @@ -5831,24 +5902,37 @@ DEFUN (neighbor_capability_orf_prefix, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); + int ret; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if (strmatch(argv[idx_send_recv]->text, "send")) - return peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_SM); + if (strmatch(argv[idx_send_recv]->text, "send")) { + ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_SET); + return ret; + } - if (strmatch(argv[idx_send_recv]->text, "receive")) - return peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_RM); + if (strmatch(argv[idx_send_recv]->text, "receive")) { + ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_SET); + return ret; + } - if (strmatch(argv[idx_send_recv]->text, "both")) - return peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_SM) - | peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_RM); + if (strmatch(argv[idx_send_recv]->text, "both")) { + ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) | + peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_SET); + return ret; + } return CMD_WARNING_CONFIG_FAILED; } @@ -5883,24 +5967,37 @@ DEFUN (no_neighbor_capability_orf_prefix, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); + int ret; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if (strmatch(argv[idx_send_recv]->text, "send")) - return peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_SM); + if (strmatch(argv[idx_send_recv]->text, "send")) { + ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_UNSET); + return ret; + } - if (strmatch(argv[idx_send_recv]->text, "receive")) - return peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_RM); + if (strmatch(argv[idx_send_recv]->text, "receive")) { + ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_UNSET); + return ret; + } - if (strmatch(argv[idx_send_recv]->text, "both")) - return peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_SM) - | peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_ORF_PREFIX_RM); + if (strmatch(argv[idx_send_recv]->text, "both")) { + ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) | + peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF, + CAPABILITY_ACTION_UNSET); + return ret; + } return CMD_WARNING_CONFIG_FAILED; } @@ -6387,6 +6484,32 @@ ALIAS_HIDDEN( "Send Standard Community attributes\n" "Send Large Community attributes\n") +DEFPY (neighbor_ecommunity_rpki, + neighbor_ecommunity_rpki_cmd, + "[no$no] neighbor $neighbor send-community extended rpki", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Send Community attribute to this neighbor\n" + "Send Extended Community attributes\n" + "Send RPKI Extended Community attributes\n") +{ + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + return peer_af_flag_unset_vty(vty, neighbor, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI); + else + return peer_af_flag_set_vty(vty, neighbor, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI); +} + /* neighbor soft-reconfig. */ DEFUN (neighbor_soft_reconfiguration, neighbor_soft_reconfiguration_cmd, @@ -6900,6 +7023,28 @@ DEFPY(no_neighbor_role, return ret; } +DEFPY (neighbor_oad, + neighbor_oad_cmd, + "[no$no] neighbor $neighbor oad", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Set peering session type to EBGP-OAD\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + peer->sub_sort = 0; + else if (peer->sort == BGP_PEER_EBGP) + peer->sub_sort = BGP_PEER_EBGP_OAD; + + return CMD_SUCCESS; +} + /* disable-connected-check */ DEFUN (neighbor_disable_connected_check, neighbor_disable_connected_check_cmd, @@ -8840,13 +8985,21 @@ DEFUN(neighbor_disable_addpath_rx, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); + int ret; + int action; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - return peer_af_flag_set_vty(vty, peer_str, afi, safi, - PEER_FLAG_DISABLE_ADDPATH_RX); + action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); + + ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_DISABLE_ADDPATH_RX); + + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); + + return ret; } DEFUN(no_neighbor_disable_addpath_rx, @@ -8861,13 +9014,21 @@ DEFUN(no_neighbor_disable_addpath_rx, struct peer *peer; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); + int ret; + int action; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; - return peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_DISABLE_ADDPATH_RX); + action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); + + ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_DISABLE_ADDPATH_RX); + + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); + + return ret; } DEFUN (neighbor_addpath_tx_all_paths, @@ -8879,13 +9040,15 @@ DEFUN (neighbor_addpath_tx_all_paths, { int idx_peer = 1; struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_ALL, 0); + bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_ALL, 0); + return CMD_SUCCESS; } @@ -8905,20 +9068,20 @@ DEFUN (no_neighbor_addpath_tx_all_paths, { int idx_peer = 2; struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)] - != BGP_ADDPATH_ALL) { + if (peer->addpath_type[afi][safi] != BGP_ADDPATH_ALL) { vty_out(vty, "%% Peer not currently configured to transmit all paths."); return CMD_WARNING_CONFIG_FAILED; } - bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_NONE, 0); + bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0); return CMD_SUCCESS; } @@ -9298,13 +9461,13 @@ DEFPY (af_rd_vpn_export, bgp_get_default(), bgp); if (yes) { - bgp->vpn_policy[afi].tovpn_rd_pretty = - XSTRDUP(MTYPE_BGP, rd_str); + bgp->vpn_policy[afi].tovpn_rd_pretty = XSTRDUP(MTYPE_BGP_NAME, + rd_str); bgp->vpn_policy[afi].tovpn_rd = prd; SET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } else { - XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); + XFREE(MTYPE_BGP_NAME, bgp->vpn_policy[afi].tovpn_rd_pretty); UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } @@ -9394,7 +9557,7 @@ DEFPY (af_label_vpn_export, "Automatically assign a label\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - mpls_label_t label = MPLS_LABEL_NONE; + mpls_label_t label = (mpls_label_t)label_val; afi_t afi; int idx = 0; bool yes = true; @@ -9402,24 +9565,28 @@ DEFPY (af_label_vpn_export, if (argv_find(argv, argc, "no", &idx)) yes = false; - /* If "no ...", squash trailing parameter */ - if (!yes) - label_auto = NULL; - - if (yes) { - if (!label_auto) - label = label_val; /* parser should force unsigned */ - } - afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) return CMD_WARNING_CONFIG_FAILED; - - if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) - /* no change */ - return CMD_SUCCESS; + if (yes) { + if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) + /* no change */ + return CMD_SUCCESS; + if (!label_auto && label == bgp->vpn_policy[afi].tovpn_label) + /* no change */ + return CMD_SUCCESS; + } else { + if (label_auto && !CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) + /* no match */ + return CMD_WARNING_CONFIG_FAILED; + if (!label_auto && label_val && + label != bgp->vpn_policy[afi].tovpn_label) + /* no change */ + return CMD_WARNING_CONFIG_FAILED; + } /* * pre-change: un-export vpn routes (vpn->vrf routes unaffected) @@ -9427,9 +9594,16 @@ DEFPY (af_label_vpn_export, vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), bgp); - if (!label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG)) { + bgp_zebra_release_label_range(bgp->vpn_policy[afi].tovpn_label, + bgp->vpn_policy[afi].tovpn_label); + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG); + } else if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + /* release any previous auto label */ if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { /* @@ -9446,16 +9620,32 @@ DEFPY (af_label_vpn_export, &bgp->vpn_policy[afi], bgp->vpn_policy[afi].tovpn_label); } - UNSET_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO); } - bgp->vpn_policy[afi].tovpn_label = label; - if (label_auto) { - SET_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO); - bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi], - vpn_leak_label_callback); + if (yes) { + if (label_auto) { + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO); + /* fetch a label */ + bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; + bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi], + vpn_leak_label_callback); + } else { + bgp->vpn_policy[afi].tovpn_label = label; + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO); + if (bgp->vpn_policy[afi].tovpn_label >= + MPLS_LABEL_UNRESERVED_MIN && + bgp_zebra_request_label_range(bgp->vpn_policy[afi] + .tovpn_label, + 1, false)) + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG); + } + } else { + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO); + bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; } /* post-change: re-export vpn routes */ @@ -10506,7 +10696,7 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, /* one clear bgp command to rule them all */ DEFUN (clear_ip_bgp_all, clear_ip_bgp_all_cmd, - "clear [ip] bgp [ VIEWVRFNAME] [ []] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|ASNUM|external|peer-group PGNAME> []|in [prefix-filter]|out|message-stats>]", + "clear [ip] bgp [ VIEWVRFNAME] [ []] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|ASNUM|external|peer-group PGNAME> []|in [prefix-filter]|out|message-stats|capabilities>]", CLEAR_STR IP_STR BGP_STR @@ -10529,7 +10719,8 @@ DEFUN (clear_ip_bgp_all, BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n" BGP_SOFT_OUT_STR - "Reset message statistics\n") + "Reset message statistics\n" + "Resend capabilities\n") { char *vrf = NULL; @@ -10586,7 +10777,7 @@ DEFUN (clear_ip_bgp_all, clr_sort = clear_external; } - /* []|in [prefix-filter]|out|message-stats>] */ + /* []|in [prefix-filter]|out|message-stats|capabilities>] */ if (argv_find(argv, argc, "soft", &idx)) { if (argv_find(argv, argc, "in", &idx) || argv_find(argv, argc, "out", &idx)) @@ -10603,6 +10794,8 @@ DEFUN (clear_ip_bgp_all, clr_type = BGP_CLEAR_SOFT_OUT; } else if (argv_find(argv, argc, "message-stats", &idx)) { clr_type = BGP_CLEAR_MESSAGE_STATS; + } else if (argv_find(argv, argc, "capabilities", &idx)) { + clr_type = BGP_CLEAR_CAPABILITIES; } else clr_type = BGP_CLEAR_SOFT_NONE; @@ -11534,8 +11727,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, : bgp->name); } else { vty_out(vty, - "BGP router identifier %pI4, local AS number %s vrf-id %d", + "BGP router identifier %pI4, local AS number %s %s vrf-id %d", &bgp->router_id, bgp->as_pretty, + bgp->name_pretty, bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id); @@ -12152,12 +12346,10 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, safi, true)); } else { - vty_out(vty, - "\n%s Summary (%s):\n", + vty_out(vty, "\n%s Summary:\n", get_afi_safi_str(afi, safi, - false), - bgp->name_pretty); + false)); } } bgp_show_summary(vty, bgp, afi, safi, fpeer, @@ -16780,10 +16972,11 @@ ALIAS_HIDDEN( DEFUN (bgp_redistribute_ipv4_ospf, bgp_redistribute_ipv4_ospf_cmd, - "redistribute (1-65535)", + "redistribute (1-65535)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -16803,7 +16996,18 @@ DEFUN (bgp_redistribute_ipv4_ospf, argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; } - protocol = ZEBRA_ROUTE_TABLE; + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } bgp_redist_add(bgp, AFI_IP, protocol, instance); @@ -16811,18 +17015,20 @@ DEFUN (bgp_redistribute_ipv4_ospf, } ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf, bgp_redistribute_ipv4_ospf_hidden_cmd, - "redistribute (1-65535)", + "redistribute (1-65535)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n") DEFUN (bgp_redistribute_ipv4_ospf_rmap, bgp_redistribute_ipv4_ospf_rmap_cmd, - "redistribute (1-65535) route-map RMAP_NAME", + "redistribute (1-65535) route-map RMAP_NAME", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Route map reference\n" "Pointer to route-map entries\n") @@ -16838,6 +17044,8 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap, struct route_map *route_map = route_map_lookup_warn_noexist(vty, argv[idx_word]->arg); + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { @@ -16847,10 +17055,20 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap, argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; } - protocol = ZEBRA_ROUTE_TABLE; + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } - instance = strtoul(argv[idx_number]->arg, NULL, 10); red = bgp_redist_add(bgp, AFI_IP, protocol, instance); changed = bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map); @@ -16859,20 +17077,22 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap, ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf_rmap, bgp_redistribute_ipv4_ospf_rmap_hidden_cmd, - "redistribute (1-65535) route-map RMAP_NAME", + "redistribute (1-65535) route-map RMAP_NAME", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Route map reference\n" "Pointer to route-map entries\n") DEFUN (bgp_redistribute_ipv4_ospf_metric, bgp_redistribute_ipv4_ospf_metric_cmd, - "redistribute (1-65535) metric (0-4294967295)", + "redistribute (1-65535) metric (0-4294967295)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n") @@ -16887,6 +17107,8 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric, int protocol; bool changed; + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { @@ -16896,10 +17118,20 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric, argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; } - protocol = ZEBRA_ROUTE_TABLE; + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } - instance = strtoul(argv[idx_number]->arg, NULL, 10); metric = strtoul(argv[idx_number_2]->arg, NULL, 10); red = bgp_redist_add(bgp, AFI_IP, protocol, instance); @@ -16910,20 +17142,22 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric, ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf_metric, bgp_redistribute_ipv4_ospf_metric_hidden_cmd, - "redistribute (1-65535) metric (0-4294967295)", + "redistribute (1-65535) metric (0-4294967295)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n") DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, bgp_redistribute_ipv4_ospf_rmap_metric_cmd, - "redistribute (1-65535) route-map RMAP_NAME metric (0-4294967295)", + "redistribute (1-65535) route-map RMAP_NAME metric (0-4294967295)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Route map reference\n" "Pointer to route-map entries\n" @@ -16943,6 +17177,8 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, struct route_map *route_map = route_map_lookup_warn_noexist(vty, argv[idx_word]->arg); + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { @@ -16952,10 +17188,20 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; } - protocol = ZEBRA_ROUTE_TABLE; + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } - instance = strtoul(argv[idx_number]->arg, NULL, 10); metric = strtoul(argv[idx_number_2]->arg, NULL, 10); red = bgp_redist_add(bgp, AFI_IP, protocol, instance); @@ -16969,10 +17215,11 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, ALIAS_HIDDEN( bgp_redistribute_ipv4_ospf_rmap_metric, bgp_redistribute_ipv4_ospf_rmap_metric_hidden_cmd, - "redistribute (1-65535) route-map RMAP_NAME metric (0-4294967295)", + "redistribute (1-65535) route-map RMAP_NAME metric (0-4294967295)", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Route map reference\n" "Pointer to route-map entries\n" @@ -16981,10 +17228,11 @@ ALIAS_HIDDEN( DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, bgp_redistribute_ipv4_ospf_metric_rmap_cmd, - "redistribute (1-65535) metric (0-4294967295) route-map RMAP_NAME", + "redistribute (1-65535) metric (0-4294967295) route-map RMAP_NAME", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n" @@ -17004,6 +17252,8 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, struct route_map *route_map = route_map_lookup_warn_noexist(vty, argv[idx_word]->arg); + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { @@ -17012,8 +17262,18 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, "%% Only default BGP instance can use '%s'\n", argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; - } - protocol = ZEBRA_ROUTE_TABLE; + } else if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } instance = strtoul(argv[idx_number]->arg, NULL, 10); @@ -17030,10 +17290,11 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, ALIAS_HIDDEN( bgp_redistribute_ipv4_ospf_metric_rmap, bgp_redistribute_ipv4_ospf_metric_rmap_hidden_cmd, - "redistribute (1-65535) metric (0-4294967295) route-map RMAP_NAME", + "redistribute (1-65535) metric (0-4294967295) route-map RMAP_NAME", "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n" @@ -17042,11 +17303,12 @@ ALIAS_HIDDEN( DEFUN (no_bgp_redistribute_ipv4_ospf, no_bgp_redistribute_ipv4_ospf_cmd, - "no redistribute (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]", + "no redistribute (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]", NO_STR "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n" @@ -17059,6 +17321,8 @@ DEFUN (no_bgp_redistribute_ipv4_ospf, unsigned short instance; int protocol; + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { @@ -17068,21 +17332,32 @@ DEFUN (no_bgp_redistribute_ipv4_ospf, argv[idx_ospf_table]->arg); return CMD_WARNING_CONFIG_FAILED; } - protocol = ZEBRA_ROUTE_TABLE; + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", + strlen("table-direct")) == 0) { + protocol = ZEBRA_ROUTE_TABLE_DIRECT; + if (instance == RT_TABLE_MAIN || + instance == RT_TABLE_LOCAL) { + vty_out(vty, + "%% 'table-direct', can not use %u routing table\n", + instance); + return CMD_WARNING_CONFIG_FAILED; + } + } else + protocol = ZEBRA_ROUTE_TABLE; } - instance = strtoul(argv[idx_number]->arg, NULL, 10); bgp_redistribute_unset(bgp, AFI_IP, protocol, instance); return CMD_SUCCESS; } ALIAS_HIDDEN( no_bgp_redistribute_ipv4_ospf, no_bgp_redistribute_ipv4_ospf_hidden_cmd, - "no redistribute (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]", + "no redistribute (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]", NO_STR "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" "Non-main Kernel Routing Table\n" + "Non-main Kernel Routing Table - Direct\n" "Instance ID/Table ID\n" "Metric for redistributed routes\n" "Default metric\n" @@ -17427,8 +17702,8 @@ bool peergroup_flag_check(struct peer *peer, uint64_t flag) return !!CHECK_FLAG(peer->flags_override, flag); } -static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint64_t flag) +bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint64_t flag) { if (!peer_group_active(peer)) { if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) @@ -17876,6 +18151,9 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, ? " strict-mode" : ""); + if (peer->sub_sort == BGP_PEER_EBGP_OAD) + vty_out(vty, " neighbor %s oad\n", addr); + /* ttl-security hops */ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) { if (!peer_group_active(peer) @@ -17900,8 +18178,13 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, addr); /* enforce-first-as */ - if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) - vty_out(vty, " neighbor %s enforce-first-as\n", addr); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ENFORCE_FIRST_AS)) { + if (!peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) + vty_out(vty, " no neighbor %s enforce-first-as\n", addr); + } else { + if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) + vty_out(vty, " neighbor %s enforce-first-as\n", addr); + } /* update-source */ if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { @@ -17966,14 +18249,30 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, } /* capability software-version */ - if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION)) - vty_out(vty, " neighbor %s capability software-version\n", - addr); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY)) { + if (!peergroup_flag_check(peer, + PEER_FLAG_CAPABILITY_SOFT_VERSION)) + vty_out(vty, + " no neighbor %s capability software-version\n", + addr); + } else { + if (peergroup_flag_check(peer, + PEER_FLAG_CAPABILITY_SOFT_VERSION)) + vty_out(vty, + " neighbor %s capability software-version\n", + addr); + } /* dont-capability-negotiation */ if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); + /* capability fqdn */ + if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_FQDN)) + vty_out(vty, + " no neighbor %s capability fqdn\n", + addr); + /* override-capability */ if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) vty_out(vty, " neighbor %s override-capability\n", addr); @@ -18190,6 +18489,12 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, if (flag_slcomm) vty_out(vty, " no neighbor %s send-community large\n", addr); + + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI)) + vty_out(vty, + " no neighbor %s send-community extended rpki\n", + addr); } /* Default information */ @@ -18434,6 +18739,8 @@ int bgp_config_write(struct vty *vty) safi_t safi; uint32_t tovpn_sid_index = 0; + hook_call(bgp_snmp_traps_config_write, vty); + if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); @@ -18524,6 +18831,15 @@ int bgp_config_write(struct vty *vty) ? "" : "no "); + /* bgp enforce-first-as */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_ENFORCE_FIRST_AS) != + SAVE_BGP_ENFORCE_FIRST_AS) + vty_out(vty, " %sbgp enforce-first-as\n", + CHECK_FLAG(bgp->flags, + BGP_FLAG_ENFORCE_FIRST_AS) + ? "" + : "no "); + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) && !!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL)) vty_out(vty, " bgp labeled-unicast explicit-null\n"); @@ -19443,6 +19759,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_role_strict_cmd); install_element(BGP_NODE, &no_neighbor_role_cmd); + /* "neighbor oad" commands. */ + install_element(BGP_NODE, &neighbor_oad_cmd); + /* "neighbor aigp" commands. */ install_element(BGP_NODE, &neighbor_aigp_cmd); @@ -19516,6 +19835,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd); install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd); + /* bgp enforce-first-as */ + install_element(BGP_NODE, &bgp_enforce_first_as_cmd); + /* bgp labeled-unicast explicit-null */ install_element(BGP_NODE, &bgp_lu_uses_explicit_null_cmd); @@ -20058,6 +20380,15 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &neighbor_send_community_type_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_type_cmd); + install_element(BGP_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV4_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV6_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_ecommunity_rpki_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_ecommunity_rpki_cmd); /* "neighbor route-reflector" commands.*/ install_element(BGP_NODE, &neighbor_route_reflector_client_hidden_cmd); @@ -20293,6 +20624,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_dont_capability_negotiate_cmd); install_element(BGP_NODE, &no_neighbor_dont_capability_negotiate_cmd); + /* "neighbor capability fqdn" command. */ + install_element(BGP_NODE, &neighbor_capability_fqdn_cmd); + /* "neighbor ebgp-multihop" commands. */ install_element(BGP_NODE, &neighbor_ebgp_multihop_cmd); install_element(BGP_NODE, &neighbor_ebgp_multihop_ttl_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index a105b6d..4955e4c 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -171,5 +171,7 @@ extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, const char *neighbor, int as_type, as_t as, uint16_t show_flags); extern bool peergroup_flag_check(struct peer *peer, uint64_t flag); +extern bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint64_t flag); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 5b69de0..87f2e55 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -24,6 +24,7 @@ #include "mpls.h" #include "vxlan.h" #include "pbr.h" +#include "frrdistance.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_route.h" @@ -72,9 +73,10 @@ static inline bool bgp_install_info_to_zebra(struct bgp *bgp) return false; if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { - zlog_debug( - "%s: No zebra instance to talk to, not installing information", - __func__); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug( + "%s: No zebra instance to talk to, not installing information", + __func__); return false; } @@ -98,13 +100,6 @@ static int bgp_router_id_update(ZAPI_CALLBACK_ARGS) return 0; } -/* Nexthop update message from zebra. */ -static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS) -{ - bgp_parse_nexthop_update(cmd, vrf_id); - return 0; -} - /* Set or clear interface on which unnumbered neighbor is configured. This * would in turn cause BGP to initiate or turn off IPv6 RAs on this * interface. @@ -151,7 +146,6 @@ static void bgp_start_interface_nbrs(struct bgp *bgp, struct interface *ifp) static void bgp_nbr_connected_add(struct bgp *bgp, struct nbr_connected *ifc) { - struct listnode *node; struct connected *connected; struct interface *ifp; struct prefix *p; @@ -160,7 +154,7 @@ static void bgp_nbr_connected_add(struct bgp *bgp, struct nbr_connected *ifc) * valid local address on the interface. */ ifp = ifc->ifp; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { p = connected->address; if (p->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) @@ -232,7 +226,7 @@ static int bgp_ifp_up(struct interface *ifp) if (!bgp) return 0; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c)) + frr_each (if_connected, ifp->connected, c) bgp_connected_add(bgp, c); for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc)) @@ -263,7 +257,7 @@ static int bgp_ifp_down(struct interface *ifp) if (!bgp) return 0; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c)) + frr_each (if_connected, ifp->connected, c) bgp_connected_delete(bgp, c); for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc)) @@ -564,7 +558,6 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) struct interface *if_lookup_by_ipv4(struct in_addr *addr, vrf_id_t vrf_id) { struct vrf *vrf; - struct listnode *cnode; struct interface *ifp; struct connected *connected; struct prefix_ipv4 p; @@ -579,7 +572,7 @@ struct interface *if_lookup_by_ipv4(struct in_addr *addr, vrf_id_t vrf_id) p.prefixlen = IPV4_MAX_BITLEN; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET) @@ -593,7 +586,6 @@ struct interface *if_lookup_by_ipv4(struct in_addr *addr, vrf_id_t vrf_id) struct interface *if_lookup_by_ipv4_exact(struct in_addr *addr, vrf_id_t vrf_id) { struct vrf *vrf; - struct listnode *cnode; struct interface *ifp; struct connected *connected; struct prefix *cp; @@ -603,7 +595,7 @@ struct interface *if_lookup_by_ipv4_exact(struct in_addr *addr, vrf_id_t vrf_id) return NULL; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET) @@ -618,7 +610,6 @@ struct interface *if_lookup_by_ipv6(struct in6_addr *addr, ifindex_t ifindex, vrf_id_t vrf_id) { struct vrf *vrf; - struct listnode *cnode; struct interface *ifp; struct connected *connected; struct prefix_ipv6 p; @@ -633,7 +624,7 @@ struct interface *if_lookup_by_ipv6(struct in6_addr *addr, ifindex_t ifindex, p.prefixlen = IPV6_MAX_BITLEN; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET6) @@ -654,7 +645,6 @@ struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr, ifindex_t ifindex, vrf_id_t vrf_id) { struct vrf *vrf; - struct listnode *cnode; struct interface *ifp; struct connected *connected; struct prefix *cp; @@ -664,7 +654,7 @@ struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr, return NULL; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET6) @@ -683,11 +673,10 @@ struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr, static int if_get_ipv6_global(struct interface *ifp, struct in6_addr *addr) { - struct listnode *cnode; struct connected *connected; struct prefix *cp; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET6) @@ -701,11 +690,10 @@ static int if_get_ipv6_global(struct interface *ifp, struct in6_addr *addr) static bool if_get_ipv6_local(struct interface *ifp, struct in6_addr *addr) { - struct listnode *cnode; struct connected *connected; struct prefix *cp; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if (cp->family == AF_INET6) @@ -719,11 +707,10 @@ static bool if_get_ipv6_local(struct interface *ifp, struct in6_addr *addr) static int if_get_ipv4_address(struct interface *ifp, struct in_addr *addr) { - struct listnode *cnode; struct connected *connected; struct prefix *cp; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { cp = connected->address; if ((cp->family == AF_INET) && !ipv4_martian(&(cp->u.prefix4))) { @@ -1207,83 +1194,49 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, } static bool bgp_zebra_use_nhop_weighted(struct bgp *bgp, struct attr *attr, - uint64_t tot_bw, uint32_t *nh_weight) + uint32_t *nh_weight) { - uint32_t bw; - uint64_t tmp; - - bw = attr->link_bw; /* zero link-bandwidth and link-bandwidth not present are treated * as the same situation. */ - if (!bw) { + if (!attr->link_bw) { /* the only situations should be if we're either told * to skip or use default weight. */ if (bgp->lb_handling == BGP_LINK_BW_SKIP_MISSING) return false; *nh_weight = BGP_ZEBRA_DEFAULT_NHOP_WEIGHT; - } else { - tmp = (uint64_t)bw * 100; - *nh_weight = ((uint32_t)(tmp / tot_bw)); - } + } else + *nh_weight = attr->link_bw; return true; } -void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, - struct bgp_path_info *info, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_zebra_announce_parse_nexthop( + struct bgp_path_info *info, const struct prefix *p, struct bgp *bgp, + struct zapi_route *api, unsigned int *valid_nh_count, afi_t afi, + safi_t safi, uint32_t *nhg_id, uint32_t *metric, route_tag_t *tag, + bool *allow_recursion) { - struct zapi_route api = { 0 }; struct zapi_nexthop *api_nh; int nh_family; - unsigned int valid_nh_count = 0; - bool allow_recursion = false; - uint8_t distance; - struct peer *peer; struct bgp_path_info *mpinfo; struct bgp *bgp_orig; - uint32_t metric; struct attr local_attr; struct bgp_path_info local_info; struct bgp_path_info *mpinfo_cp = &local_info; - route_tag_t tag; mpls_label_t *labels; uint32_t num_labels = 0; mpls_label_t nh_label; int nh_othervrf = 0; bool nh_updated = false; bool do_wt_ecmp; - uint64_t cum_bw = 0; - uint32_t nhg_id = 0; - bool is_add; uint32_t ttl = 0; uint32_t bos = 0; uint32_t exp = 0; - /* - * BGP is installing this route and bgp has been configured - * to suppress announcements until the route has been installed - * let's set the fact that we expect this route to be installed - */ - if (BGP_SUPPRESS_FIB_ENABLED(bgp)) - SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); - - /* Don't try to install if we're not connected to Zebra or Zebra doesn't - * know of this instance. - */ - if (!bgp_install_info_to_zebra(bgp)) - return; - - if (bgp->main_zebra_update_hold) - return; - - if (safi == SAFI_FLOWSPEC) { - bgp_pbr_update_entry(bgp, bgp_dest_get_prefix(dest), info, afi, - safi, true); - return; - } + /* Determine if we're doing weighted ECMP or not */ + do_wt_ecmp = bgp_path_info_mpath_chkwtd(bgp, info); /* * vrf leaking support (will have only one nexthop) @@ -1292,62 +1245,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, info->extra->vrfleak->bgp_orig) nh_othervrf = 1; - /* Make Zebra API structure. */ - api.vrf_id = bgp->vrf_id; - api.type = ZEBRA_ROUTE_BGP; - api.safi = safi; - api.prefix = *p; - SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - - peer = info->peer; - - if (info->type == ZEBRA_ROUTE_BGP - && info->sub_type == BGP_ROUTE_IMPORTED) { - - /* Obtain peer from parent */ - if (info->extra && info->extra->vrfleak && - info->extra->vrfleak->parent) - peer = ((struct bgp_path_info *)(info->extra->vrfleak - ->parent)) - ->peer; - } - - tag = info->attr->tag; - - if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED - || info->sub_type == BGP_ROUTE_AGGREGATE) { - SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); - SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); - } - - if ((peer->sort == BGP_PEER_EBGP && peer->ttl != BGP_DEFAULT_TTL) - || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) - || CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - - allow_recursion = true; - - if (info->attr->rmap_table_id) { - SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); - api.tableid = info->attr->rmap_table_id; - } - - if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) - SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); - - /* Metric is currently based on the best-path only */ - metric = info->attr->med; - - /* Determine if we're doing weighted ECMP or not */ - do_wt_ecmp = bgp_path_info_mpath_chkwtd(bgp, info); - if (do_wt_ecmp) - cum_bw = bgp_path_info_mpath_cumbw(info); - /* EVPN MAC-IP routes are installed with a L3 NHG id */ - if (bgp_evpn_path_es_use_nhg(bgp, info, &nhg_id)) { + if (nhg_id && bgp_evpn_path_es_use_nhg(bgp, info, nhg_id)) { mpinfo = NULL; - api.nhgid = nhg_id; - if (nhg_id) - SET_FLAG(api.message, ZAPI_MESSAGE_NHG); + zapi_route_set_nhg_id(api, nhg_id); } else { mpinfo = info; } @@ -1359,7 +1260,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, bool is_evpn; bool is_parent_evpn; - if (valid_nh_count >= multipath_num) + if (*valid_nh_count >= multipath_num) break; *mpinfo_cp = *mpinfo; @@ -1382,16 +1283,19 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, */ if (do_wt_ecmp) { if (!bgp_zebra_use_nhop_weighted(bgp, mpinfo->attr, - cum_bw, &nh_weight)) + &nh_weight)) continue; } - api_nh = &api.nexthops[valid_nh_count]; + if (CHECK_FLAG(info->flags, BGP_PATH_SELECTED)) + api_nh = &api->nexthops[*valid_nh_count]; + else + api_nh = &api->backup_nexthops[*valid_nh_count]; if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) api_nh->srte_color = bgp_attr_get_color(info->attr); - if (bgp_debug_zebra(&api.prefix)) { + if (bgp_debug_zebra(&api->prefix)) { if (mpinfo->extra) { zlog_debug("%s: p=%pFX, bgp_is_valid_label: %d", __func__, p, @@ -1416,8 +1320,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, /* metric/tag is only allowed to be * overridden on 1st nexthop */ if (mpinfo == info) { - metric = mpinfo_cp->attr->med; - tag = mpinfo_cp->attr->tag; + if (metric) + *metric = mpinfo_cp->attr->med; + if (tag) + *tag = mpinfo_cp->attr->tag; } } @@ -1458,11 +1364,11 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, /* Allow recursion if it is a multipath group with both * eBGP and iBGP paths. */ - if (!allow_recursion - && CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX) - && (mpinfo->peer->sort == BGP_PEER_IBGP - || mpinfo->peer->sort == BGP_PEER_CONFED)) - allow_recursion = true; + if (allow_recursion && !*allow_recursion && + CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX) && + (mpinfo->peer->sort == BGP_PEER_IBGP || + mpinfo->peer->sort == BGP_PEER_CONFED)) + *allow_recursion = true; if (mpinfo->extra) { labels = mpinfo->extra->label; @@ -1515,7 +1421,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, &exp, &bos); if (nh_label < MPLS_LABEL_UNRESERVED_MIN) { - if (bgp_debug_zebra(&api.prefix)) + if (bgp_debug_zebra(&api->prefix)) zlog_debug( "skip invalid SRv6 routes: transposition scheme is used, but label is too small"); continue; @@ -1532,8 +1438,156 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); } - valid_nh_count++; + (*valid_nh_count)++; + } +} + +static void bgp_debug_zebra_nh(struct zapi_route *api) +{ + int i; + int nh_family; + char nh_buf[INET6_ADDRSTRLEN]; + char eth_buf[ETHER_ADDR_STRLEN + 7] = { '\0' }; + char buf1[ETHER_ADDR_STRLEN]; + char label_buf[20]; + char sid_buf[20]; + char segs_buf[256]; + struct zapi_nexthop *api_nh; + int count; + + count = api->nexthop_num; + for (i = 0; i < count; i++) { + api_nh = &api->nexthops[i]; + switch (api_nh->type) { + case NEXTHOP_TYPE_IFINDEX: + nh_buf[0] = '\0'; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + nh_family = AF_INET; + inet_ntop(nh_family, &api_nh->gate, nh_buf, + sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + nh_family = AF_INET6; + inet_ntop(nh_family, &api_nh->gate, nh_buf, + sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + strlcpy(nh_buf, "blackhole", sizeof(nh_buf)); + break; + default: + /* Note: add new nexthop case */ + assert(0); + break; + } + + label_buf[0] = '\0'; + eth_buf[0] = '\0'; + segs_buf[0] = '\0'; + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && + !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) + snprintf(label_buf, sizeof(label_buf), "label %u", + api_nh->labels[0]); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) && + !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) { + inet_ntop(AF_INET6, &api_nh->seg6_segs[0], sid_buf, + sizeof(sid_buf)); + snprintf(segs_buf, sizeof(segs_buf), "segs %s", sid_buf); + } + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN) && + !is_zero_mac(&api_nh->rmac)) + snprintf(eth_buf, sizeof(eth_buf), " RMAC %s", + prefix_mac2str(&api_nh->rmac, buf1, + sizeof(buf1))); + zlog_debug(" nhop [%d]: %s if %u VRF %u wt %u %s %s %s", i + 1, + nh_buf, api_nh->ifindex, api_nh->vrf_id, + api_nh->weight, label_buf, segs_buf, eth_buf); } +} + +void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, + struct bgp_path_info *info, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct bgp_path_info *bpi_ultimate; + struct zapi_route api = { 0 }; + unsigned int valid_nh_count = 0; + bool allow_recursion = false; + uint8_t distance; + struct peer *peer; + uint32_t metric; + route_tag_t tag; + bool is_add; + uint32_t nhg_id = 0; + uint32_t recursion_flag = 0; + + /* + * BGP is installing this route and bgp has been configured + * to suppress announcements until the route has been installed + * let's set the fact that we expect this route to be installed + */ + if (BGP_SUPPRESS_FIB_ENABLED(bgp)) + SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); + + /* Don't try to install if we're not connected to Zebra or Zebra doesn't + * know of this instance. + */ + if (!bgp_install_info_to_zebra(bgp)) + return; + + if (bgp->main_zebra_update_hold) + return; + + if (safi == SAFI_FLOWSPEC) { + bgp_pbr_update_entry(bgp, bgp_dest_get_prefix(dest), info, afi, + safi, true); + return; + } + + /* Make Zebra API structure. */ + api.vrf_id = bgp->vrf_id; + api.type = ZEBRA_ROUTE_BGP; + api.safi = safi; + api.prefix = *p; + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + + peer = info->peer; + + if (info->type == ZEBRA_ROUTE_BGP) { + bpi_ultimate = bgp_get_imported_bpi_ultimate(info); + peer = bpi_ultimate->peer; + } + + tag = info->attr->tag; + + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED + || info->sub_type == BGP_ROUTE_AGGREGATE) { + SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + } + + if ((peer->sort == BGP_PEER_EBGP && peer->ttl != BGP_DEFAULT_TTL) + || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) + || CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + + allow_recursion = true; + + if (info->attr->rmap_table_id) { + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + api.tableid = info->attr->rmap_table_id; + } + + if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); + + /* Metric is currently based on the best-path only */ + metric = info->attr->med; + + bgp_zebra_announce_parse_nexthop(info, p, bgp, &api, &valid_nh_count, + afi, safi, &nhg_id, &metric, &tag, + &allow_recursion); is_add = (valid_nh_count || nhg_id) ? true : false; @@ -1593,75 +1647,12 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, } if (bgp_debug_zebra(p)) { - char nh_buf[INET6_ADDRSTRLEN]; - char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'}; - char buf1[ETHER_ADDR_STRLEN]; - char label_buf[20]; - char sid_buf[20]; - char segs_buf[256]; - int i; - zlog_debug( "Tx route %s VRF %u %pFX metric %u tag %" ROUTE_TAG_PRI " count %d nhg %d", is_add ? "add" : "delete", bgp->vrf_id, &api.prefix, api.metric, api.tag, api.nexthop_num, nhg_id); - for (i = 0; i < api.nexthop_num; i++) { - api_nh = &api.nexthops[i]; - - switch (api_nh->type) { - case NEXTHOP_TYPE_IFINDEX: - nh_buf[0] = '\0'; - break; - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - nh_family = AF_INET; - inet_ntop(nh_family, &api_nh->gate, nh_buf, - sizeof(nh_buf)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - nh_family = AF_INET6; - inet_ntop(nh_family, &api_nh->gate, nh_buf, - sizeof(nh_buf)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - strlcpy(nh_buf, "blackhole", sizeof(nh_buf)); - break; - default: - /* Note: add new nexthop case */ - assert(0); - break; - } - - label_buf[0] = '\0'; - eth_buf[0] = '\0'; - segs_buf[0] = '\0'; - if (CHECK_FLAG(api_nh->flags, - ZAPI_NEXTHOP_FLAG_LABEL) && - !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) - snprintf(label_buf, sizeof(label_buf), - "label %u", api_nh->labels[0]); - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) && - !CHECK_FLAG(api_nh->flags, - ZAPI_NEXTHOP_FLAG_EVPN)) { - inet_ntop(AF_INET6, &api_nh->seg6_segs[0], - sid_buf, sizeof(sid_buf)); - snprintf(segs_buf, sizeof(segs_buf), "segs %s", - sid_buf); - } - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN) && - !is_zero_mac(&api_nh->rmac)) - snprintf(eth_buf, sizeof(eth_buf), " RMAC %s", - prefix_mac2str(&api_nh->rmac, - buf1, sizeof(buf1))); - zlog_debug(" nhop [%d]: %s if %u VRF %u wt %u %s %s %s", - i + 1, nh_buf, api_nh->ifindex, - api_nh->vrf_id, api_nh->weight, - label_buf, segs_buf, eth_buf); - } - - int recursion_flag = 0; + bgp_debug_zebra_nh(&api); if (CHECK_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION)) recursion_flag = 1; @@ -1728,7 +1719,7 @@ void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi, } void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info, - struct bgp *bgp, safi_t safi) + struct bgp *bgp, afi_t afi, safi_t safi) { struct zapi_route api; struct peer *peer; @@ -1747,7 +1738,7 @@ void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info, if (safi == SAFI_FLOWSPEC) { peer = info->peer; - bgp_pbr_update_entry(peer->bgp, p, info, AFI_IP, safi, false); + bgp_pbr_update_entry(peer->bgp, p, info, afi, safi, false); return; } @@ -1788,7 +1779,7 @@ void bgp_zebra_withdraw_table_all_subtypes(struct bgp *bgp, afi_t afi, safi_t sa if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && (pi->type == ZEBRA_ROUTE_BGP)) bgp_zebra_withdraw(bgp_dest_get_prefix(dest), - pi, bgp, safi); + pi, bgp, afi, safi); } } } @@ -2060,7 +2051,8 @@ void bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type, struct listnode *node, *nnode; struct bgp_redist *red; - if (type != ZEBRA_ROUTE_TABLE || instance != 0) + if ((type != ZEBRA_ROUTE_TABLE && type != ZEBRA_ROUTE_TABLE_DIRECT) || + instance != 0) return _bgp_redistribute_unset(bgp, afi, type, instance); /* walk over instance */ @@ -2359,7 +2351,7 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) enum zapi_rule_notify_owner note; struct bgp_pbr_action *bgp_pbra; struct bgp_pbr_rule *bgp_pbr = NULL; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, ifname, ¬e)) @@ -2605,8 +2597,12 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, /* Find the bgp route node */ dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi, &p, &bgp->vrf_prd); - if (!dest) + if (!dest) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: %pFX does not exist in the BGP table, nothing to do for %u", + __func__, &p, note); return -1; + } switch (note) { case ZAPI_ROUTE_INSTALLED: @@ -2615,7 +2611,7 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("route %pRN : INSTALLED", (void *)dest); + zlog_debug("route %pBD : INSTALLED", dest); /* Find the best route */ for (pi = dest->info; pi; pi = pi->next) { /* Process aggregate route */ @@ -2628,7 +2624,7 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, group_announce_route(bgp, afi, safi, dest, new_select); else { flog_err(EC_BGP_INVALID_ROUTE, - "selected route %pRN not found", (void *)dest); + "selected route %pBD not found", dest); bgp_dest_unlock_node(dest); return -1; @@ -2641,13 +2637,13 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, */ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("route %pRN: Removed from Fib", (void *)dest); + zlog_debug("route %pBD: Removed from Fib", dest); break; case ZAPI_ROUTE_FAIL_INSTALL: new_select = NULL; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("route: %pRN Failed to Install into Fib", - (void *)dest); + zlog_debug("route: %pBD Failed to Install into Fib", + dest); UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED); for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { @@ -2660,8 +2656,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, break; case ZAPI_ROUTE_BETTER_ADMIN_WON: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("route: %pRN removed due to better admin won", - (void *)dest); + zlog_debug("route: %pBD removed due to better admin won", + dest); new_select = NULL; UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED); @@ -2675,8 +2671,7 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, /* No action required */ break; case ZAPI_ROUTE_REMOVE_FAIL: - zlog_warn("%s: Route %pRN failure to remove", - __func__, (void *)dest); + zlog_warn("%s: Route %pBD failure to remove", __func__, dest); break; } @@ -2958,12 +2953,11 @@ static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) is_anycast_mac = stream_getl(s); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug( - "Rx L3-VNI ADD VRF %s VNI %u Originator-IP %pI4 RMAC svi-mac %pEA vrr-mac %pEA filter %s svi-if %u", - vrf_id_to_name(vrf_id), l3vni, &originator_ip, - &svi_rmac, &vrr_rmac, - filter ? "prefix-routes-only" : "none", - svi_ifindex); + zlog_debug("Rx L3VNI ADD VRF %s VNI %u Originator-IP %pI4 RMAC svi-mac %pEA vrr-mac %pEA filter %s svi-if %u", + vrf_id_to_name(vrf_id), l3vni, + &originator_ip, &svi_rmac, &vrr_rmac, + filter ? "prefix-routes-only" : "none", + svi_ifindex); frrtrace(8, frr_bgp, evpn_local_l3vni_add_zrecv, l3vni, vrf_id, &svi_rmac, &vrr_rmac, filter, originator_ip, @@ -2974,7 +2968,7 @@ static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) is_anycast_mac); } else { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx L3-VNI DEL VRF %s VNI %u", + zlog_debug("Rx L3VNI DEL VRF %s VNI %u", vrf_id_to_name(vrf_id), l3vni); frrtrace(2, frr_bgp, evpn_local_l3vni_del_zrecv, l3vni, vrf_id); @@ -3235,7 +3229,7 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) if (prefix_match((struct prefix *)&loc.prefix, (struct prefix *)&tmp_prefi)) { listnode_delete(bgp->srv6_functions, func); - XFREE(MTYPE_BGP_SRV6_FUNCTION, func); + srv6_function_free(func); } } @@ -3339,7 +3333,6 @@ static zclient_handler *const bgp_handlers[] = { [ZEBRA_INTERFACE_NBR_ADDRESS_DELETE] = bgp_interface_nbr_address_delete, [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = zebra_read_route, - [ZEBRA_NEXTHOP_UPDATE] = bgp_read_nexthop_update, [ZEBRA_FEC_UPDATE] = bgp_read_fec_update, [ZEBRA_LOCAL_ES_ADD] = bgp_zebra_process_local_es_add, [ZEBRA_LOCAL_ES_DEL] = bgp_zebra_process_local_es_del, @@ -3432,6 +3425,9 @@ static bool bgp_zebra_label_manager_connect(void) /* tell label pool that zebra is connected */ bgp_lp_event_zebra_up(); + /* tell BGP L3VPN that label manager is available */ + if (bgp_get_default()) + vpn_leak_postchange_all(); return true; } @@ -3442,13 +3438,12 @@ static void bgp_zebra_capabilities(struct zclient_capabilities *cap) void bgp_zebra_init(struct event_loop *master, unsigned short instance) { - struct zclient_options options = zclient_options_default; - - options.synchronous = true; zclient_num_connects = 0; - if_zapi_callbacks(bgp_ifp_create, bgp_ifp_up, - bgp_ifp_down, bgp_ifp_destroy); + hook_register_prio(if_real, 0, bgp_ifp_create); + hook_register_prio(if_up, 0, bgp_ifp_up); + hook_register_prio(if_down, 0, bgp_ifp_down); + hook_register_prio(if_unreal, 0, bgp_ifp_destroy); /* Set default values. */ zclient = zclient_new(master, &zclient_options_default, bgp_handlers, @@ -3456,10 +3451,11 @@ void bgp_zebra_init(struct event_loop *master, unsigned short instance) zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs); zclient->zebra_connected = bgp_zebra_connected; zclient->zebra_capabilities = bgp_zebra_capabilities; + zclient->nexthop_update = bgp_nexthop_update; zclient->instance = instance; /* Initialize special zclient for synchronous message exchanges. */ - zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync = zclient_new(master, &zclient_options_sync, NULL, 0); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_BGP; zclient_sync->instance = instance; @@ -3930,7 +3926,8 @@ void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, zebra_send_mpls_labels(zclient, cmd, &zl); } -bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size, + bool label_auto) { int ret; uint32_t start, end; @@ -3952,7 +3949,13 @@ bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size) return false; } - bgp_lp_event_chunk(start, end); + if (label_auto) + /* label automatic is serviced by the bgp label pool + * manager, which allocates label chunks in + * pre-pools, and which needs to be notified about + * new chunks availability + */ + bgp_lp_event_chunk(start, end); return true; } diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 0edae04..396c833 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -34,7 +34,7 @@ extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, extern void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi); extern void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *path, struct bgp *bgp, - safi_t safi); + afi_t afi, safi_t safi); /* Announce routes of any bgp subtype of a table to zebra */ extern void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi, @@ -124,6 +124,7 @@ extern void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, enum lsp_types_t ltype, struct prefix *p, uint32_t num_labels, mpls_label_t out_labels[]); -extern bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +extern bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size, + bool label_auto); extern void bgp_zebra_release_label_range(uint32_t start, uint32_t end); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 4b72798..1a03a06 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -63,6 +63,7 @@ #include "bgpd/bgp_vty.h" #include "bgpd/bgp_mpath.h" #include "bgpd/bgp_nht.h" +#include "bgpd/bgp_nhg.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_memory.h" @@ -186,7 +187,6 @@ int bgp_option_set(int flag) int bgp_option_unset(int flag) { switch (flag) { - /* Fall through. */ case BGP_OPT_NO_ZEBRA: case BGP_OPT_NO_FIB: UNSET_FLAG(bm->options, flag); @@ -410,6 +410,9 @@ void bgp_router_id_static_set(struct bgp *bgp, struct in_addr id) void bm_wait_for_fib_set(bool set) { bool send_msg = false; + struct bgp *bgp; + struct peer *peer; + struct listnode *next, *node; if (bm->wait_for_fib == set) return; @@ -428,12 +431,32 @@ void bm_wait_for_fib_set(bool set) if (send_msg && zclient) zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, set); + + /* + * If this is configed at a time when peers are already set + * FRR needs to reset the connection(s) as that some installs + * may have already happened in some shape fashion or form + * let's just start over + */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (!BGP_IS_VALID_STATE_FOR_NOTIF( + peer->connection->status)) + continue; + + peer->last_reset = PEER_DOWN_SUPPRESS_FIB_PENDING; + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } } /* Set the suppress fib pending for the bgp configuration */ void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set) { bool send_msg = false; + struct peer *peer; + struct listnode *node; if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) return; @@ -465,6 +488,21 @@ void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set) zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, set); } + + /* + * If this is configed at a time when peers are already set + * FRR needs to reset the connection as that some installs + * may have already happened in some shape fashion or form + * let's just start over + */ + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (!BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) + continue; + + peer->last_reset = PEER_DOWN_SUPPRESS_FIB_PENDING; + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } } /* BGP's cluster-id control. */ @@ -577,8 +615,8 @@ void bgp_confederation_id_set(struct bgp *bgp, as_t as, const char *as_str) already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION); bgp->confed_id = as; if (bgp->confed_id_pretty) - XFREE(MTYPE_BGP, bgp->confed_id_pretty); - bgp->confed_id_pretty = XSTRDUP(MTYPE_BGP, as_str); + XFREE(MTYPE_BGP_NAME, bgp->confed_id_pretty); + bgp->confed_id_pretty = XSTRDUP(MTYPE_BGP_NAME, as_str); bgp_config_set(bgp, BGP_CONFIG_CONFEDERATION); /* If we were doing confederation already, this is just an external @@ -631,7 +669,7 @@ void bgp_confederation_id_unset(struct bgp *bgp) struct listnode *node, *nnode; bgp->confed_id = 0; - XFREE(MTYPE_BGP, bgp->confed_id_pretty); + XFREE(MTYPE_BGP_NAME, bgp->confed_id_pretty); bgp_config_unset(bgp, BGP_CONFIG_CONFEDERATION); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -685,7 +723,7 @@ void bgp_confederation_peers_add(struct bgp *bgp, as_t as, const char *as_str) bgp->confed_peers[bgp->confed_peers_cnt].as = as; bgp->confed_peers[bgp->confed_peers_cnt].as_pretty = - XSTRDUP(MTYPE_BGP, as_str); + XSTRDUP(MTYPE_BGP_NAME, as_str); bgp->confed_peers_cnt++; if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) { @@ -723,7 +761,7 @@ void bgp_confederation_peers_remove(struct bgp *bgp, as_t as) for (i = 0; i < bgp->confed_peers_cnt; i++) if (bgp->confed_peers[i].as == as) { - XFREE(MTYPE_BGP, bgp->confed_peers[i].as_pretty); + XFREE(MTYPE_BGP_NAME, bgp->confed_peers[i].as_pretty); for (j = i + 1; j < bgp->confed_peers_cnt; j++) { bgp->confed_peers[j - 1].as = bgp->confed_peers[j].as; @@ -1266,9 +1304,9 @@ static void peer_free(struct peer *peer) bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0); if (peer->change_local_as_pretty) - XFREE(MTYPE_BGP, peer->change_local_as_pretty); + XFREE(MTYPE_BGP_NAME, peer->change_local_as_pretty); if (peer->as_pretty) - XFREE(MTYPE_BGP, peer->as_pretty); + XFREE(MTYPE_BGP_NAME, peer->as_pretty); bgp_peer_connection_free(&peer->connection); @@ -1428,7 +1466,9 @@ static void bgp_srv6_init(struct bgp *bgp) bgp->srv6_enabled = false; memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name)); bgp->srv6_locator_chunks = list_new(); + bgp->srv6_locator_chunks->del = srv6_locator_chunk_list_free; bgp->srv6_functions = list_new(); + bgp->srv6_functions->del = (void (*)(void *))srv6_function_free; } static void bgp_srv6_cleanup(struct bgp *bgp) @@ -1472,6 +1512,8 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI); SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); @@ -1479,6 +1521,8 @@ struct peer *peer_new(struct bgp *bgp) PEER_FLAG_SEND_COMMUNITY); SET_FLAG(peer->af_flags_invert[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY_RPKI); SET_FLAG(peer->af_flags_invert[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; @@ -1492,6 +1536,15 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ENFORCE_FIRST_AS)) + SET_FLAG(peer->flags, PEER_FLAG_ENFORCE_FIRST_AS); + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY)) + SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_SOFT_VERSION); + + SET_FLAG(peer->flags_invert, PEER_FLAG_CAPABILITY_FQDN); + SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN); + /* Initialize per peer bgp GR FSM */ bgp_peer_gr_init(peer); @@ -1538,6 +1591,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) /* copy tcp_mss value */ peer_dst->tcp_mss = peer_src->tcp_mss; (void)peer_sort(peer_dst); + peer_dst->sub_sort = peer_src->sub_sort; peer_dst->rmap_type = peer_src->rmap_type; peer_dst->local_role = peer_src->local_role; @@ -1603,6 +1657,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname); } peer_dst->ttl = peer_src->ttl; + peer_dst->gtsm_hops = peer_src->gtsm_hops; } static int bgp_peer_conf_if_to_su_update_v4(struct peer_connection *connection, @@ -1611,12 +1666,11 @@ static int bgp_peer_conf_if_to_su_update_v4(struct peer_connection *connection, struct connected *ifc; struct prefix p; uint32_t addr; - struct listnode *node; /* If our IPv4 address on the interface is /30 or /31, we can derive the * IPv4 address of the other end. */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { if (ifc->address && (ifc->address->family == AF_INET)) { prefix_copy(&p, CONNECTED_PREFIX(ifc)); if (p.prefixlen == 30) { @@ -1863,7 +1917,7 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, peer->as = remote_as; /* internal and external values do not use as_pretty */ if (as_str && asn_str2asn(as_str, NULL)) - peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); + peer->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_str); peer->as_type = as_type; peer->local_id = bgp->router_id; peer->v_holdtime = bgp->default_holdtime; @@ -1985,10 +2039,10 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified, peer->as = as; if (as_specified == AS_SPECIFIED && as_str) { if (peer->as_pretty) - XFREE(MTYPE_BGP, peer->as_pretty); - peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); + XFREE(MTYPE_BGP_NAME, peer->as_pretty); + peer->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_str); } else if (peer->as_type == AS_UNSPECIFIED && peer->as_pretty) - XFREE(MTYPE_BGP, peer->as_pretty); + XFREE(MTYPE_BGP_NAME, peer->as_pretty); peer->as_type = as_specified; if (bgp_config_check(peer->bgp, BGP_CONFIG_CONFEDERATION) @@ -3147,6 +3201,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, peer->as_type = group->conf->as_type; peer->as = group->conf->as; peer->sort = group->conf->sort; + peer->sub_sort = group->conf->sub_sort; } ptype = peer_sort(peer); @@ -3287,9 +3342,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp)); bgp->as = *as; if (as_pretty) - bgp->as_pretty = XSTRDUP(MTYPE_BGP, as_pretty); + bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_pretty); else - bgp->as_pretty = XSTRDUP(MTYPE_BGP, asn_asn2asplain(*as)); + bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, asn_asn2asplain(*as)); if (asnotation != ASNOTATION_UNDEFINED) { bgp->asnotation = asnotation; @@ -3418,14 +3473,14 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_mplsvpn_nh_label_bind_cache_init(&bgp->mplsvpn_nh_label_bind); if (name) - bgp->name = XSTRDUP(MTYPE_BGP, name); + bgp->name = XSTRDUP(MTYPE_BGP_NAME, name); event_add_timer(bm->master, bgp_startup_timer_expire, bgp, bgp->restart_time, &bgp->t_startup); /* printable name we can use in debug messages */ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) { - bgp->name_pretty = XSTRDUP(MTYPE_BGP, "VRF default"); + bgp->name_pretty = XSTRDUP(MTYPE_BGP_NAME, "VRF default"); } else { const char *n; int len; @@ -3437,7 +3492,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, len = 4 + 1 + strlen(n) + 1; /* "view foo\0" */ - bgp->name_pretty = XCALLOC(MTYPE_BGP, len); + bgp->name_pretty = XCALLOC(MTYPE_BGP_NAME, len); snprintf(bgp->name_pretty, len, "%s %s", (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) ? "VRF" @@ -4040,7 +4095,8 @@ void bgp_free(struct bgp *bgp) if (bgp->vpn_policy[afi].rtlist[dir]) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); if (bgp->vpn_policy[afi].tovpn_rd_pretty) - XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); + XFREE(MTYPE_BGP_NAME, + bgp->vpn_policy[afi].tovpn_rd_pretty); if (bgp->vpn_policy[afi].tovpn_sid_locator != NULL) srv6_locator_chunk_free( &bgp->vpn_policy[afi].tovpn_sid_locator); @@ -4057,10 +4113,14 @@ void bgp_free(struct bgp *bgp) bgp_srv6_cleanup(bgp); bgp_confederation_id_unset(bgp); - XFREE(MTYPE_BGP, bgp->as_pretty); - XFREE(MTYPE_BGP, bgp->name); - XFREE(MTYPE_BGP, bgp->name_pretty); - XFREE(MTYPE_BGP, bgp->snmp_stats); + for (int i = 0; i < bgp->confed_peers_cnt; i++) + XFREE(MTYPE_BGP_NAME, bgp->confed_peers[i].as_pretty); + + XFREE(MTYPE_BGP_NAME, bgp->as_pretty); + XFREE(MTYPE_BGP_NAME, bgp->name); + XFREE(MTYPE_BGP_NAME, bgp->name_pretty); + XFREE(MTYPE_BGP_NAME, bgp->snmp_stats); + XFREE(MTYPE_BGP_CONFED_LIST, bgp->confed_peers); XFREE(MTYPE_BGP, bgp); } @@ -4522,6 +4582,7 @@ static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_AIGP, 0, peer_change_none}, {PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none}, {PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_none}, + {PEER_FLAG_CAPABILITY_FQDN, 0, peer_change_none}, {0, 0, 0}}; static const struct peer_flag_action peer_af_flag_action_list[] = { @@ -4552,9 +4613,10 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out}, {PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out}, {PEER_FLAG_WEIGHT, 0, peer_change_reset_in}, - {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset}, + {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_none}, {PEER_FLAG_SOO, 0, peer_change_reset}, {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset}, + {PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out}, {0, 0, 0}}; /* Proper action set. */ @@ -4979,6 +5041,16 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, else if (flag == PEER_FLAG_ORF_PREFIX_RM) peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + /* We should not reset the session if + * dynamic capability is enabled and we + * are changing the ORF prefix flags. + */ + if ((CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) && + CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) && + (flag == PEER_FLAG_ORF_PREFIX_RM || + flag == PEER_FLAG_ORF_PREFIX_SM)) + action.type = peer_change_none; + peer_change_action(peer, afi, safi, action.type); } } @@ -5039,6 +5111,18 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, member->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + /* We should not reset the session if + * dynamic capability is enabled and we + * are changing the ORF prefix flags. + */ + if ((CHECK_FLAG(peer->cap, + PEER_CAP_DYNAMIC_RCV) && + CHECK_FLAG(peer->cap, + PEER_CAP_DYNAMIC_ADV)) && + (flag == PEER_FLAG_ORF_PREFIX_RM || + flag == PEER_FLAG_ORF_PREFIX_SM)) + action.type = peer_change_none; + peer_change_action(member, afi, safi, action.type); } @@ -5624,7 +5708,7 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi, if (peer_established(peer->connection) && peer->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(peer, afi, safi)); - bgp_default_originate(peer, afi, safi, 0); + bgp_default_originate(peer, afi, safi, false); bgp_announce_route(peer, afi, safi, false); } @@ -5667,7 +5751,7 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi, member->afc_nego[afi][safi]) { update_group_adjust_peer( peer_af_find(member, afi, safi)); - bgp_default_originate(member, afi, safi, 0); + bgp_default_originate(member, afi, safi, false); bgp_announce_route(member, afi, safi, false); } } @@ -5712,7 +5796,7 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi) if (peer_established(peer->connection) && peer->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(peer, afi, safi)); - bgp_default_originate(peer, afi, safi, 1); + bgp_default_originate(peer, afi, safi, true); bgp_announce_route(peer, afi, safi, false); } @@ -5751,7 +5835,7 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi) if (peer_established(member->connection) && member->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(member, afi, safi)); - bgp_default_originate(member, afi, safi, 1); + bgp_default_originate(member, afi, safi, true); bgp_announce_route(member, afi, safi, false); } } @@ -6440,8 +6524,8 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, peer->change_local_as = as; if (as_str) { if (peer->change_local_as_pretty) - XFREE(MTYPE_BGP, peer->change_local_as_pretty); - peer->change_local_as_pretty = XSTRDUP(MTYPE_BGP, as_str); + XFREE(MTYPE_BGP_NAME, peer->change_local_as_pretty); + peer->change_local_as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_str); } (void)peer_sort(peer); @@ -6478,8 +6562,8 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, replace_as); member->change_local_as = as; if (as_str) - member->change_local_as_pretty = - XSTRDUP(MTYPE_BGP, as_str); + member->change_local_as_pretty = XSTRDUP(MTYPE_BGP_NAME, + as_str); } return 0; @@ -6505,7 +6589,7 @@ int peer_local_as_unset(struct peer *peer) peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND); peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS); peer->change_local_as = 0; - XFREE(MTYPE_BGP, peer->change_local_as_pretty); + XFREE(MTYPE_BGP_NAME, peer->change_local_as_pretty); } /* Check if handling a regular peer. */ @@ -6536,7 +6620,7 @@ int peer_local_as_unset(struct peer *peer) UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); member->change_local_as = 0; - XFREE(MTYPE_BGP, member->change_local_as_pretty); + XFREE(MTYPE_BGP_NAME, member->change_local_as_pretty); /* Send notification or stop peer depending on state. */ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) { @@ -8024,6 +8108,16 @@ static void peer_reset_message_stats(struct peer *peer) } } +/* Helper function to resend some BGP capabilities that are uncontrolled. + * For instance, FQDN capability, that can't be turned off, but let's say + * we changed the hostname, we need to resend it. + */ +static void peer_clear_capabilities(struct peer *peer, afi_t afi, safi_t safi) +{ + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_FQDN, + CAPABILITY_ACTION_SET); +} + /* * If peer clear is invoked in a loop for all peers on the BGP instance, * it may end up freeing the doppelganger, and if this was the next node @@ -8132,6 +8226,9 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi, if (stype == BGP_CLEAR_MESSAGE_STATS) peer_reset_message_stats(peer); + if (stype == BGP_CLEAR_CAPABILITIES) + peer_clear_capabilities(peer, afi, safi); + return 0; } @@ -8220,7 +8317,7 @@ void bgp_master_init(struct event_loop *master, const int buffer_size, /* mpls label dynamic allocation pool */ bgp_lp_init(bm->master, &bm->labelpool); - bgp_l3nhg_init(); + bgp_nhg_init(); bgp_evpn_mh_init(); QOBJ_REG(bm, bgp_master); } @@ -8240,10 +8337,9 @@ static void bgp_if_finish(struct bgp *bgp) return; FOR_ALL_INTERFACES (vrf, ifp) { - struct listnode *c_node, *c_nnode; struct connected *c; - for (ALL_LIST_ELEMENTS(ifp->connected, c_node, c_nnode, c)) + frr_each_safe (if_connected, ifp->connected, c) bgp_connected_delete(bgp, c); } } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index ca1411a..0f69095 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -34,6 +34,9 @@ #include "lib/bfd.h" +DECLARE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf), + (vty, vrf)); + #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -121,6 +124,8 @@ struct bgp_master { #define BGP_OPT_NO_FIB (1 << 0) #define BGP_OPT_NO_LISTEN (1 << 1) #define BGP_OPT_NO_ZEBRA (1 << 2) +#define BGP_OPT_TRAPS_RFC4273 (1 << 3) +#define BGP_OPT_TRAPS_BGP4MIBV2 (1 << 4) uint64_t updgrp_idspace; uint64_t subgrp_idspace; @@ -217,6 +222,8 @@ struct vpn_policy { #define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2) #define BGP_VPN_POLICY_TOVPN_SID_AUTO (1 << 3) #define BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP (1 << 4) +/* Manual label is registered with zebra label manager */ +#define BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG (1 << 5) /* * If we are importing another vrf into us keep a list of @@ -514,6 +521,7 @@ struct bgp { /* For BGP-LU, force IPv6 local prefixes to use ipv6-explicit-null label */ #define BGP_FLAG_LU_IPV6_EXPLICIT_NULL (1ULL << 34) #define BGP_FLAG_SOFT_VERSION_CAPABILITY (1ULL << 35) +#define BGP_FLAG_ENFORCE_FIRST_AS (1ULL << 36) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. @@ -832,7 +840,10 @@ DECLARE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)); DECLARE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)); +DECLARE_HOOK(bgp_snmp_traps_config_write, (struct vty *vty), (vty)); DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp)); +DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled), + (vrf, enabled)); /* Thread callback information */ struct afi_safi_info { @@ -877,10 +888,10 @@ struct peer_group { struct bgp_notify { uint8_t code; uint8_t subcode; - char *data; bgp_size_t length; - uint8_t *raw_data; bool hard_reset; + char *data; + uint8_t *raw_data; }; /* Next hop self address. */ @@ -971,6 +982,14 @@ enum bgp_peer_sort { BGP_PEER_CONFED, }; +/* BGP peering sub-types + * E.g.: + * EBGP-OAD - https://datatracker.ietf.org/doc/html/draft-uttaro-idr-bgp-oad + */ +enum bgp_peer_sub_sort { + BGP_PEER_EBGP_OAD = 1, +}; + /* BGP message header and packet size. */ #define BGP_MARKER_SIZE 16 #define BGP_HEADER_SIZE 19 @@ -1032,7 +1051,8 @@ enum peer_gr_command { NO_PEER_HELPER_CMD }; -typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, int, int); +typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, enum peer_mode, + enum peer_mode); struct bgp_peer_gr { enum peer_mode next_state; @@ -1129,6 +1149,11 @@ struct peer_connection { int fd; + /* Thread flags */ + _Atomic uint32_t thread_flags; +#define PEER_THREAD_WRITES_ON (1U << 0) +#define PEER_THREAD_READS_ON (1U << 1) + /* Packet receive and send buffer. */ pthread_mutex_t io_mtx; // guards ibuf, obuf struct stream_fifo *ibuf; // packets waiting to be processed @@ -1159,11 +1184,6 @@ struct peer_connection { union sockunion su; #define BGP_CONNECTION_SU_UNSPEC(connection) \ (connection->su.sa.sa_family == AF_UNSPEC) - - /* Thread flags */ - _Atomic uint32_t thread_flags; -#define PEER_THREAD_WRITES_ON (1U << 0) -#define PEER_THREAD_READS_ON (1U << 1) }; extern struct peer_connection *bgp_peer_connection_new(struct peer *peer); extern void bgp_peer_connection_free(struct peer_connection **connection); @@ -1197,6 +1217,7 @@ struct peer { as_t local_as; enum bgp_peer_sort sort; + enum bgp_peer_sub_sort sub_sort; /* Peer's Change local AS number. */ as_t change_local_as; @@ -1279,39 +1300,39 @@ struct peer { uint8_t afc_recv[AFI_MAX][SAFI_MAX]; /* Capability flags (reset in bgp_stop) */ - uint32_t cap; -#define PEER_CAP_REFRESH_ADV (1U << 0) /* refresh advertised */ -#define PEER_CAP_REFRESH_RCV (1U << 2) /* refresh rfc received */ -#define PEER_CAP_DYNAMIC_ADV (1U << 3) /* dynamic advertised */ -#define PEER_CAP_DYNAMIC_RCV (1U << 4) /* dynamic received */ -#define PEER_CAP_RESTART_ADV (1U << 5) /* restart advertised */ -#define PEER_CAP_RESTART_RCV (1U << 6) /* restart received */ -#define PEER_CAP_AS4_ADV (1U << 7) /* as4 advertised */ -#define PEER_CAP_AS4_RCV (1U << 8) /* as4 received */ + uint64_t cap; +#define PEER_CAP_REFRESH_ADV (1ULL << 0) /* refresh advertised */ +#define PEER_CAP_REFRESH_RCV (1ULL << 2) /* refresh rfc received */ +#define PEER_CAP_DYNAMIC_ADV (1ULL << 3) /* dynamic advertised */ +#define PEER_CAP_DYNAMIC_RCV (1ULL << 4) /* dynamic received */ +#define PEER_CAP_RESTART_ADV (1ULL << 5) /* restart advertised */ +#define PEER_CAP_RESTART_RCV (1ULL << 6) /* restart received */ +#define PEER_CAP_AS4_ADV (1ULL << 7) /* as4 advertised */ +#define PEER_CAP_AS4_RCV (1ULL << 8) /* as4 received */ /* sent graceful-restart restart (R) bit */ -#define PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV (1U << 9) +#define PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV (1ULL << 9) /* received graceful-restart restart (R) bit */ -#define PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV (1U << 10) -#define PEER_CAP_ADDPATH_ADV (1U << 11) /* addpath advertised */ -#define PEER_CAP_ADDPATH_RCV (1U << 12) /* addpath received */ -#define PEER_CAP_ENHE_ADV (1U << 13) /* Extended nexthop advertised */ -#define PEER_CAP_ENHE_RCV (1U << 14) /* Extended nexthop received */ -#define PEER_CAP_HOSTNAME_ADV (1U << 15) /* hostname advertised */ -#define PEER_CAP_HOSTNAME_RCV (1U << 16) /* hostname received */ -#define PEER_CAP_ENHANCED_RR_ADV (1U << 17) /* enhanced rr advertised */ -#define PEER_CAP_ENHANCED_RR_RCV (1U << 18) /* enhanced rr received */ -#define PEER_CAP_EXTENDED_MESSAGE_ADV (1U << 19) -#define PEER_CAP_EXTENDED_MESSAGE_RCV (1U << 20) -#define PEER_CAP_LLGR_ADV (1U << 21) -#define PEER_CAP_LLGR_RCV (1U << 22) +#define PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV (1ULL << 10) +#define PEER_CAP_ADDPATH_ADV (1ULL << 11) /* addpath advertised */ +#define PEER_CAP_ADDPATH_RCV (1ULL << 12) /* addpath received */ +#define PEER_CAP_ENHE_ADV (1ULL << 13) /* Extended nexthop advertised */ +#define PEER_CAP_ENHE_RCV (1ULL << 14) /* Extended nexthop received */ +#define PEER_CAP_HOSTNAME_ADV (1ULL << 15) /* hostname advertised */ +#define PEER_CAP_HOSTNAME_RCV (1ULL << 16) /* hostname received */ +#define PEER_CAP_ENHANCED_RR_ADV (1ULL << 17) /* enhanced rr advertised */ +#define PEER_CAP_ENHANCED_RR_RCV (1ULL << 18) /* enhanced rr received */ +#define PEER_CAP_EXTENDED_MESSAGE_ADV (1ULL << 19) +#define PEER_CAP_EXTENDED_MESSAGE_RCV (1ULL << 20) +#define PEER_CAP_LLGR_ADV (1ULL << 21) +#define PEER_CAP_LLGR_RCV (1ULL << 22) /* sent graceful-restart notification (N) bit */ -#define PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV (1U << 23) +#define PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV (1ULL << 23) /* received graceful-restart notification (N) bit */ -#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24) -#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */ -#define PEER_CAP_ROLE_RCV (1U << 26) /* role received */ -#define PEER_CAP_SOFT_VERSION_ADV (1U << 27) -#define PEER_CAP_SOFT_VERSION_RCV (1U << 28) +#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1ULL << 24) +#define PEER_CAP_ROLE_ADV (1ULL << 25) /* role advertised */ +#define PEER_CAP_ROLE_RCV (1ULL << 26) /* role received */ +#define PEER_CAP_SOFT_VERSION_ADV (1ULL << 27) +#define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28) /* Capability flags (reset in bgp_stop) */ uint32_t af_cap[AFI_MAX][SAFI_MAX]; @@ -1439,6 +1460,7 @@ struct peer { #define PEER_FLAG_AIGP (1ULL << 34) #define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35) #define PEER_FLAG_CAPABILITY_SOFT_VERSION (1ULL << 36) +#define PEER_FLAG_CAPABILITY_FQDN (1ULL << 37) /* fqdn capability */ /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART @@ -1505,6 +1527,7 @@ struct peer { #define PEER_FLAG_MAX_PREFIX_FORCE (1ULL << 26) #define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 27) #define PEER_FLAG_SOO (1ULL << 28) +#define PEER_FLAG_SEND_EXT_COMMUNITY_RPKI (1ULL << 29) #define PEER_FLAG_ACCEPT_OWN (1ULL << 63) enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -1742,6 +1765,7 @@ struct peer { #define PEER_DOWN_PFX_COUNT 33U /* Reached received prefix count */ #define PEER_DOWN_SOCKET_ERROR 34U /* Some socket error happened */ #define PEER_DOWN_RTT_SHUTDOWN 35U /* Automatically shutdown due to RTT */ +#define PEER_DOWN_SUPPRESS_FIB_PENDING 36U /* Suppress fib pending changed */ /* * Remember to update peer_down_str in bgp_fsm.c when you add * a new value to the last_reset reason @@ -1868,11 +1892,11 @@ struct bgp_nlri { /* SAFI. */ uint8_t safi; /* iana_safi_t */ - /* Pointer to NLRI byte stream. */ - uint8_t *nlri; - /* Length of whole NLRI. */ bgp_size_t length; + + /* Pointer to NLRI byte stream. */ + uint8_t *nlri; }; /* BGP versions. */ @@ -2042,7 +2066,6 @@ struct bgp_nlri { #define BGP_UPTIME_LEN 25 /* Default configuration settings for bgpd. */ -#define BGP_VTY_PORT 2605 #define BGP_DEFAULT_CONFIG "bgpd.conf" /* BGP Dynamic Neighbors feature */ @@ -2064,7 +2087,8 @@ enum bgp_clear_type { BGP_CLEAR_SOFT_IN, BGP_CLEAR_SOFT_BOTH, BGP_CLEAR_SOFT_IN_ORF_PREFIX, - BGP_CLEAR_MESSAGE_STATS + BGP_CLEAR_MESSAGE_STATS, + BGP_CLEAR_CAPABILITIES, }; /* Macros. */ @@ -2430,6 +2454,8 @@ extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp); extern void bgp_route_map_terminate(void); +extern bool bgp_route_map_has_extcommunity_rt(const struct route_map *map); + extern int peer_cmp(struct peer *p1, struct peer *p2); extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi, @@ -2721,6 +2747,9 @@ extern bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size); extern bool bgp_path_attribute_treat_as_withdraw(struct peer *peer, char *buf, size_t size); + +extern void srv6_function_free(struct bgp_srv6_function *func); + #ifdef _FRR_ATTRIBUTE_PRINTFRR /* clang-format off */ #pragma FRR printfrr_ext "%pBP" (struct peer *) diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 5b6961d..a452ebe 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -531,7 +531,7 @@ rfapi_group_new(struct bgp *bgp, rfapi_group_cfg_type_t type, const char *name) rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG, sizeof(struct rfapi_nve_group_cfg)); rfg->type = type; - rfg->name = strdup(name); + rfg->name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, name); /* add to tail of list */ listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg); rfg->label = MPLS_LABEL_NONE; @@ -832,8 +832,8 @@ DEFUN (vnc_redistribute_protocol, if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) { VNC_REDIST_DISABLE(bgp, afi, type); /* disabled view implicitly */ - free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); - bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, + bgp->rfapi_cfg->redist_bgp_exterior_view_name); } bgp->rfapi_cfg->redist_bgp_exterior_view = bgp; } @@ -873,10 +873,8 @@ DEFUN (vnc_no_redistribute_protocol, VNC_REDIST_DISABLE(bgp, afi, type); if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) { - if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) { - free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); - bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; - } + XFREE(MTYPE_RFAPI_GROUP_CFG, + bgp->rfapi_cfg->redist_bgp_exterior_view_name); bgp->rfapi_cfg->redist_bgp_exterior_view = NULL; } @@ -905,9 +903,10 @@ DEFUN (vnc_redistribute_bgp_exterior, return CMD_WARNING_CONFIG_FAILED; } - if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) - free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); - bgp->rfapi_cfg->redist_bgp_exterior_view_name = strdup(argv[5]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, + bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = + XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[5]->arg); /* could be NULL if name is not defined yet */ bgp->rfapi_cfg->redist_bgp_exterior_view = bgp_lookup_by_name(argv[5]->arg); @@ -935,9 +934,9 @@ DEFUN (vnc_redistribute_nvegroup, */ bgp->rfapi_cfg->rfg_redist = bgp_rfapi_cfg_match_byname( bgp, argv[3]->arg, RFAPI_GROUP_CFG_NVE); - if (bgp->rfapi_cfg->rfg_redist_name) - free(bgp->rfapi_cfg->rfg_redist_name); - bgp->rfapi_cfg->rfg_redist_name = strdup(argv[3]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, bgp->rfapi_cfg->rfg_redist_name); + bgp->rfapi_cfg->rfg_redist_name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[3]->arg); vnc_redistribute_postchange(bgp); @@ -959,9 +958,7 @@ DEFUN (vnc_redistribute_no_nvegroup, vnc_redistribute_prechange(bgp); bgp->rfapi_cfg->rfg_redist = NULL; - if (bgp->rfapi_cfg->rfg_redist_name) - free(bgp->rfapi_cfg->rfg_redist_name); - bgp->rfapi_cfg->rfg_redist_name = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, bgp->rfapi_cfg->rfg_redist_name); vnc_redistribute_postchange(bgp); @@ -1030,9 +1027,7 @@ DEFUN (vnc_redist_bgpdirect_no_prefixlist, vnc_redistribute_prechange(bgp); - if (hc->plist_redist_name[route_type][afi]) - free(hc->plist_redist_name[route_type][afi]); - hc->plist_redist_name[route_type][afi] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->plist_redist_name[route_type][afi]); hc->plist_redist[route_type][afi] = NULL; vnc_redistribute_postchange(bgp); @@ -1074,9 +1069,9 @@ DEFUN (vnc_redist_bgpdirect_prefixlist, vnc_redistribute_prechange(bgp); - if (hc->plist_redist_name[route_type][afi]) - free(hc->plist_redist_name[route_type][afi]); - hc->plist_redist_name[route_type][afi] = strdup(argv[5]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->plist_redist_name[route_type][afi]); + hc->plist_redist_name[route_type][afi] = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[5]->arg); hc->plist_redist[route_type][afi] = prefix_list_lookup(afi, argv[5]->arg); @@ -1110,9 +1105,7 @@ DEFUN (vnc_redist_bgpdirect_no_routemap, vnc_redistribute_prechange(bgp); - if (hc->routemap_redist_name[route_type]) - free(hc->routemap_redist_name[route_type]); - hc->routemap_redist_name[route_type] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->routemap_redist_name[route_type]); hc->routemap_redist[route_type] = NULL; vnc_redistribute_postchange(bgp); @@ -1144,15 +1137,16 @@ DEFUN (vnc_redist_bgpdirect_routemap, vnc_redistribute_prechange(bgp); - if (hc->routemap_redist_name[route_type]) - free(hc->routemap_redist_name[route_type]); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->routemap_redist_name[route_type]); /* If the old route map config overwrite with new * route map config , old routemap counter have to be * reduced. */ route_map_counter_decrement(hc->routemap_redist[route_type]); - hc->routemap_redist_name[route_type] = strdup(argv[4]->arg); + hc->routemap_redist_name[route_type] = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[4]->arg); + hc->routemap_redist[route_type] = route_map_lookup_by_name(argv[4]->arg); route_map_counter_increment(hc->routemap_redist[route_type]); @@ -1197,9 +1191,8 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist, vnc_redistribute_prechange(bgp); - if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) - free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); - rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; vnc_redistribute_postchange(bgp); @@ -1238,10 +1231,10 @@ DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist, vnc_redistribute_prechange(bgp); - if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) - free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = - strdup(argv[4]->arg); + XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[4]->arg); rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = prefix_list_lookup(afi, argv[4]->arg); @@ -1272,11 +1265,10 @@ DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap, vnc_redistribute_prechange(bgp); - if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) - free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); route_map_counter_decrement( rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]); - rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = NULL; rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = NULL; vnc_redistribute_postchange(bgp); @@ -1305,12 +1297,12 @@ DEFUN (vnc_nve_group_redist_bgpdirect_routemap, vnc_redistribute_prechange(bgp); - if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) - free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); route_map_counter_decrement( rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]); rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = - strdup(argv[3]->arg); + XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[3]->arg); rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = route_map_lookup_by_name(argv[3]->arg); route_map_counter_increment( @@ -1451,7 +1443,7 @@ DEFUN (vnc_export_nvegroup, } rfgn = rfgn_new(); - rfgn->name = strdup(argv[5]->arg); + rfgn->name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[5]->arg); rfgn->rfg = rfg_new; /* OK if not set yet */ listnode_add(bgp->rfapi_cfg->rfg_export_direct_bgp_l, rfgn); @@ -1487,7 +1479,7 @@ DEFUN (vnc_export_nvegroup, } rfgn = rfgn_new(); - rfgn->name = strdup(argv[5]->arg); + rfgn->name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[5]->arg); rfgn->rfg = rfg_new; /* OK if not set yet */ listnode_add(bgp->rfapi_cfg->rfg_export_zebra_l, rfgn); @@ -1531,7 +1523,7 @@ DEFUN (vnc_no_export_nvegroup, if (rfgn->rfg) vnc_direct_bgp_del_group(bgp, rfgn->rfg); - free(rfgn->name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfgn->name); list_delete_node( bgp->rfapi_cfg->rfg_export_direct_bgp_l, node); @@ -1548,7 +1540,7 @@ DEFUN (vnc_no_export_nvegroup, if (rfgn->name && !strcmp(rfgn->name, argv[6]->arg)) { if (rfgn->rfg) vnc_zebra_del_group(bgp, rfgn->rfg); - free(rfgn->name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfgn->name); list_delete_node( bgp->rfapi_cfg->rfg_export_zebra_l, node); @@ -1612,9 +1604,8 @@ DEFUN (vnc_nve_group_export_no_prefixlist, || (rfg->plist_export_zebra_name[afi] && strmatch(argv[idx]->arg, rfg->plist_export_zebra_name[afi]))) { - if (rfg->plist_export_zebra_name[afi]) - free(rfg->plist_export_zebra_name[afi]); - rfg->plist_export_zebra_name[afi] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->plist_export_zebra_name[afi]); rfg->plist_export_zebra[afi] = NULL; vnc_zebra_reexport_group_afi(bgp, rfg, afi); @@ -1667,18 +1658,18 @@ DEFUN (vnc_nve_group_export_prefixlist, idx = argc - 1; if (is_bgp) { - if (rfg->plist_export_bgp_name[afi]) - free(rfg->plist_export_bgp_name[afi]); - rfg->plist_export_bgp_name[afi] = strdup(argv[idx]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->plist_export_bgp_name[afi]); + rfg->plist_export_bgp_name[afi] = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[idx]->arg); rfg->plist_export_bgp[afi] = prefix_list_lookup(afi, argv[idx]->arg); vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi); } else { - if (rfg->plist_export_zebra_name[afi]) - free(rfg->plist_export_zebra_name[afi]); - rfg->plist_export_zebra_name[afi] = strdup(argv[idx]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->plist_export_zebra_name[afi]); + rfg->plist_export_zebra_name[afi] = + XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[idx]->arg); rfg->plist_export_zebra[afi] = prefix_list_lookup(afi, argv[idx]->arg); @@ -1720,7 +1711,8 @@ DEFUN (vnc_nve_group_export_no_routemap, switch (argv[idx]->text[0]) { case 'z': is_bgp = 0; - /* fall thru */ + idx += 2; + break; case 'b': idx += 2; break; @@ -1734,10 +1726,9 @@ DEFUN (vnc_nve_group_export_no_routemap, || (rfg->routemap_export_bgp_name && strmatch(argv[idx]->arg, rfg->routemap_export_bgp_name))) { - if (rfg->routemap_export_bgp_name) - free(rfg->routemap_export_bgp_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, + rfg->routemap_export_bgp_name); route_map_counter_decrement(rfg->routemap_export_bgp); - rfg->routemap_export_bgp_name = NULL; rfg->routemap_export_bgp = NULL; vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP); @@ -1748,10 +1739,8 @@ DEFUN (vnc_nve_group_export_no_routemap, || (rfg->routemap_export_zebra_name && strmatch(argv[idx]->arg, rfg->routemap_export_zebra_name))) { - if (rfg->routemap_export_zebra_name) - free(rfg->routemap_export_zebra_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->routemap_export_zebra_name); route_map_counter_decrement(rfg->routemap_export_zebra); - rfg->routemap_export_zebra_name = NULL; rfg->routemap_export_zebra = NULL; vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP); @@ -1795,20 +1784,20 @@ DEFUN (vnc_nve_group_export_routemap, idx = argc - 1; if (is_bgp) { - if (rfg->routemap_export_bgp_name) - free(rfg->routemap_export_bgp_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->routemap_export_bgp_name); route_map_counter_decrement(rfg->routemap_export_bgp); - rfg->routemap_export_bgp_name = strdup(argv[idx]->arg); + rfg->routemap_export_bgp_name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[idx]->arg); rfg->routemap_export_bgp = route_map_lookup_by_name(argv[idx]->arg); route_map_counter_increment(rfg->routemap_export_bgp); vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP); vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6); } else { - if (rfg->routemap_export_zebra_name) - free(rfg->routemap_export_zebra_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->routemap_export_zebra_name); route_map_counter_decrement(rfg->routemap_export_zebra); - rfg->routemap_export_zebra_name = strdup(argv[idx]->arg); + rfg->routemap_export_zebra_name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[idx]->arg); rfg->routemap_export_zebra = route_map_lookup_by_name(argv[idx]->arg); route_map_counter_increment(rfg->routemap_export_zebra); @@ -1853,9 +1842,8 @@ DEFUN (vnc_nve_export_no_prefixlist, if (((argc > 6) && hc->plist_export_bgp_name[afi] && strmatch(argv[6]->text, hc->plist_export_bgp_name[afi])) || (argc <= 6)) { - - free(hc->plist_export_bgp_name[afi]); - hc->plist_export_bgp_name[afi] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, + hc->plist_export_bgp_name[afi]); hc->plist_export_bgp[afi] = NULL; vnc_direct_bgp_reexport(bgp, afi); } @@ -1864,9 +1852,8 @@ DEFUN (vnc_nve_export_no_prefixlist, && strmatch(argv[6]->text, hc->plist_export_zebra_name[afi])) || (argc <= 6)) { - - free(hc->plist_export_zebra_name[afi]); - hc->plist_export_zebra_name[afi] = NULL; + XFREE(MTYPE_RFAPI_GROUP_CFG, + hc->plist_export_zebra_name[afi]); hc->plist_export_zebra[afi] = NULL; /* TBD vnc_zebra_rh_reexport(bgp, afi); */ } @@ -1899,16 +1886,16 @@ DEFUN (vnc_nve_export_prefixlist, } if (argv[2]->arg[0] == 'b') { - if (hc->plist_export_bgp_name[afi]) - free(hc->plist_export_bgp_name[afi]); - hc->plist_export_bgp_name[afi] = strdup(argv[5]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->plist_export_bgp_name[afi]); + hc->plist_export_bgp_name[afi] = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[5]->arg); hc->plist_export_bgp[afi] = prefix_list_lookup(afi, argv[5]->arg); vnc_direct_bgp_reexport(bgp, afi); } else { - if (hc->plist_export_zebra_name[afi]) - free(hc->plist_export_zebra_name[afi]); - hc->plist_export_zebra_name[afi] = strdup(argv[5]->arg); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->plist_export_zebra_name[afi]); + hc->plist_export_zebra_name[afi] = + XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[5]->arg); hc->plist_export_zebra[afi] = prefix_list_lookup(afi, argv[5]->arg); /* TBD vnc_zebra_rh_reexport(bgp, afi); */ @@ -1936,10 +1923,9 @@ DEFUN (vnc_nve_export_no_routemap, if (((argc > 5) && hc->routemap_export_bgp_name && strmatch(argv[5]->text, hc->routemap_export_bgp_name)) || (argc <= 5)) { - - free(hc->routemap_export_bgp_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, + hc->routemap_export_bgp_name); route_map_counter_decrement(hc->routemap_export_bgp); - hc->routemap_export_bgp_name = NULL; hc->routemap_export_bgp = NULL; vnc_direct_bgp_reexport(bgp, AFI_IP); vnc_direct_bgp_reexport(bgp, AFI_IP6); @@ -1949,9 +1935,8 @@ DEFUN (vnc_nve_export_no_routemap, && strmatch(argv[5]->text, hc->routemap_export_zebra_name)) || (argc <= 5)) { - free(hc->routemap_export_zebra_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->routemap_export_zebra_name); route_map_counter_decrement(hc->routemap_export_zebra); - hc->routemap_export_zebra_name = NULL; hc->routemap_export_zebra = NULL; /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ @@ -1976,20 +1961,20 @@ DEFUN (vnc_nve_export_routemap, hc = bgp->rfapi_cfg; if (argv[2]->arg[0] == 'b') { - if (hc->routemap_export_bgp_name) - free(hc->routemap_export_bgp_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->routemap_export_bgp_name); route_map_counter_decrement(hc->routemap_export_bgp); - hc->routemap_export_bgp_name = strdup(argv[4]->arg); + hc->routemap_export_bgp_name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[4]->arg); hc->routemap_export_bgp = route_map_lookup_by_name(argv[4]->arg); route_map_counter_increment(hc->routemap_export_bgp); vnc_direct_bgp_reexport(bgp, AFI_IP); vnc_direct_bgp_reexport(bgp, AFI_IP6); } else { - if (hc->routemap_export_zebra_name) - free(hc->routemap_export_zebra_name); + XFREE(MTYPE_RFAPI_GROUP_CFG, hc->routemap_export_zebra_name); route_map_counter_decrement(hc->routemap_export_zebra); - hc->routemap_export_zebra_name = strdup(argv[4]->arg); + hc->routemap_export_zebra_name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, + argv[4]->arg); hc->routemap_export_zebra = route_map_lookup_by_name(argv[4]->arg); route_map_counter_increment(hc->routemap_export_zebra); @@ -2319,7 +2304,7 @@ static void bgp_rfapi_delete_nve_group(struct vty *vty, /* NULL = no output */ } /* delete it */ - free(rfg->name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->name); if (rfg->rfapi_import_table) rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); if (rfg->rt_import_list) @@ -3410,7 +3395,7 @@ DEFUN_NOSH (vnc_l2_group, vty_out(vty, "Can't allocate memory for L2 group\n"); return CMD_WARNING_CONFIG_FAILED; } - rfg->name = strdup(argv[2]->arg); + rfg->name = XSTRDUP(MTYPE_RFAPI_GROUP_CFG, argv[2]->arg); /* add to tail of list */ listnode_add(bgp->rfapi_cfg->l2_groups, rfg); } @@ -3428,7 +3413,7 @@ static void bgp_rfapi_delete_l2_group(struct vty *vty, /* NULL = no output */ struct rfapi_l2_group_cfg *rfg) { /* delete it */ - free(rfg->name); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg->name); if (rfg->rt_import_list) ecommunity_free(&rfg->rt_import_list); if (rfg->rt_export_list) @@ -3590,7 +3575,9 @@ DEFUN (vnc_l2_group_rt, switch (argv[1]->arg[0]) { case 'b': - do_export = 1; /* fall through */ + do_export = 1; + do_import = 1; + break; case 'i': do_import = 1; break; @@ -3840,7 +3827,8 @@ struct rfapi_cfg *bgp_rfapi_cfg_new(struct rfapi_rfp_cfg *cfg) static void bgp_rfapi_rfgn_list_delete(void *data) { struct rfapi_rfg_name *rfgn = data; - free(rfgn->name); + + XFREE(MTYPE_RFAPI_GROUP_CFG, rfgn->name); rfgn_free(rfgn); } diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index ff7137b..382af05 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -46,11 +46,6 @@ #include "bgpd/rfapi/rfapi_encap_tlv.h" #include "bgpd/rfapi/vnc_debug.h" -#ifdef HAVE_GLIBC_BACKTRACE -/* for backtrace and friends */ -#include -#endif /* HAVE_GLIBC_BACKTRACE */ - #define DEBUG_CLEANUP 0 struct ethaddr rfapi_ethaddr0 = {{0}}; @@ -371,20 +366,19 @@ void del_vnc_route(struct rfapi_descriptor *rfd, for (bpi = (bn ? bgp_dest_get_bgp_path_info(bn) : NULL); bpi; bpi = bpi->next) { - vnc_zlog_debug_verbose( "%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%" PRIu64, __func__, bpi, bpi->peer, bpi->type, bpi->sub_type, - (bpi->extra ? bpi->extra->vnc.export.rfapi_handle + (bpi->extra ? bpi->extra->vnc->vnc.export.rfapi_handle : NULL), CHECK_FLAG(bpi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF) - ? bpi->attr->local_pref : 0)); - - if (bpi->peer == peer && bpi->type == type - && bpi->sub_type == sub_type && bpi->extra - && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { + ? bpi->attr->local_pref + : 0)); + if (bpi->peer == peer && bpi->type == type && + bpi->sub_type == sub_type && bpi->extra && + bpi->extra->vnc->vnc.export.rfapi_handle == (void *)rfd) { vnc_zlog_debug_verbose("%s: matched it", __func__); break; @@ -397,8 +391,8 @@ void del_vnc_route(struct rfapi_descriptor *rfd, * route. Leave the route itself in place. * TBD add return code reporting of success/failure */ - if (!bpi || !bpi->extra - || !bpi->extra->vnc.export.local_nexthops) { + if (!bpi || !bpi->extra || + !bpi->extra->vnc->vnc.export.local_nexthops) { /* * no local nexthops */ @@ -414,16 +408,16 @@ void del_vnc_route(struct rfapi_descriptor *rfd, struct listnode *node; struct rfapi_nexthop *pLnh = NULL; - for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc.export.local_nexthops, + for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc->vnc.export + .local_nexthops, node, pLnh)) { - if (prefix_same(&pLnh->addr, &lnh->addr)) { break; } } if (pLnh) { - listnode_delete(bpi->extra->vnc.export.local_nexthops, + listnode_delete(bpi->extra->vnc->vnc.export.local_nexthops, pLnh); /* silly rabbit, listnode_delete doesn't invoke @@ -464,8 +458,8 @@ void del_vnc_route(struct rfapi_descriptor *rfd, /* * Delete local_nexthops list */ - if (bpi->extra && bpi->extra->vnc.export.local_nexthops) - list_delete(&bpi->extra->vnc.export.local_nexthops); + if (bpi->extra && bpi->extra->vnc->vnc.export.local_nexthops) + list_delete(&bpi->extra->vnc->vnc.export.local_nexthops); bgp_aggregate_decrement(bgp, p, bpi, afi, safi); bgp_path_info_delete(bn, bpi); @@ -907,11 +901,10 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ */ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { /* probably only need to check - * bpi->extra->vnc.export.rfapi_handle */ - if (bpi->peer == rfd->peer && bpi->type == type - && bpi->sub_type == sub_type && bpi->extra - && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { - + * bpi->extra->vnc->vnc.export.rfapi_handle */ + if (bpi->peer == rfd->peer && bpi->type == type && + bpi->sub_type == sub_type && bpi->extra && + bpi->extra->vnc->vnc.export.rfapi_handle == (void *)rfd) { break; } } @@ -923,11 +916,11 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ * what is advertised via BGP */ if (lnh) { - if (!bpi->extra->vnc.export.local_nexthops) { + if (!bpi->extra->vnc->vnc.export.local_nexthops) { /* TBD make arrangements to free when needed */ - bpi->extra->vnc.export.local_nexthops = + bpi->extra->vnc->vnc.export.local_nexthops = list_new(); - bpi->extra->vnc.export.local_nexthops->del = + bpi->extra->vnc->vnc.export.local_nexthops->del = rfapi_nexthop_free; } @@ -937,10 +930,9 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ struct listnode *node; struct rfapi_nexthop *pLnh = NULL; - for (ALL_LIST_ELEMENTS_RO( - bpi->extra->vnc.export.local_nexthops, - node, pLnh)) { - + for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc->vnc.export + .local_nexthops, + node, pLnh)) { if (prefix_same(&pLnh->addr, &lnh->addr)) { break; } @@ -951,9 +943,9 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ */ if (!pLnh) { pLnh = rfapi_nexthop_new(lnh); - listnode_add( - bpi->extra->vnc.export.local_nexthops, - pLnh); + listnode_add(bpi->extra->vnc->vnc.export + .local_nexthops, + pLnh); } } @@ -1025,7 +1017,9 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ /* save backref to rfapi handle */ bgp_path_info_extra_get(new); - new->extra->vnc.export.rfapi_handle = (void *)rfd; + new->extra->vnc = XCALLOC(MTYPE_BGP_ROUTE_EXTRA_VNC, + sizeof(struct bgp_path_info_extra_vnc)); + new->extra->vnc->vnc.export.rfapi_handle = (void *)rfd; encode_label(label_val, &new->extra->label[0]); /* debug */ @@ -2091,24 +2085,7 @@ int rfapi_close(void *handle) vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); #ifdef RFAPI_WHO_IS_CALLING_ME -#ifdef HAVE_GLIBC_BACKTRACE -#define RFAPI_DEBUG_BACKTRACE_NENTRIES 5 - { - void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; - char **syms; - int i; - size_t size; - - size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); - syms = backtrace_symbols(buf, size); - for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; - ++i) { - vnc_zlog_debug_verbose("backtrace[%2d]: %s", i, - syms[i]); - } - free(syms); - } -#endif + zlog_backtrace(LOG_INFO); #endif bgp = rfd->bgp; diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index a93e186..168b8e4 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -44,11 +44,6 @@ #include "bgpd/rfapi/rfapi_encap_tlv.h" #include "bgpd/rfapi/vnc_debug.h" -#ifdef HAVE_GLIBC_BACKTRACE -/* for backtrace and friends */ -#include -#endif /* HAVE_GLIBC_BACKTRACE */ - #undef DEBUG_MONITOR_MOVE_SHORTER #undef DEBUG_RETURNED_NHL #undef DEBUG_ROUTE_COUNTERS @@ -77,32 +72,6 @@ struct rfapi_withdraw { int lockoffset; }; -/* - * DEBUG FUNCTION - * It's evil and fiendish. It's compiler-dependent. - * ? Might need LDFLAGS -rdynamic to produce all function names - */ -void rfapiDebugBacktrace(void) -{ -#ifdef HAVE_GLIBC_BACKTRACE -#define RFAPI_DEBUG_BACKTRACE_NENTRIES 200 - void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; - char **syms; - size_t i; - size_t size; - - size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); - syms = backtrace_symbols(buf, size); - - for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i) { - vnc_zlog_debug_verbose("backtrace[%2zu]: %s", i, syms[i]); - } - - free(syms); -#else -#endif -} - /* * DEBUG FUNCTION * Count remote routes and compare with actively-maintained values. @@ -428,18 +397,22 @@ int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p) * advertisement */ if (bpi->extra) { - switch (bpi->extra->vnc.import.un_family) { + switch (bpi->extra->vnc->vnc.import.un_family) { case AF_INET: if (p) { - p->family = bpi->extra->vnc.import.un_family; - p->u.prefix4 = bpi->extra->vnc.import.un.addr4; + p->family = + bpi->extra->vnc->vnc.import.un_family; + p->u.prefix4 = + bpi->extra->vnc->vnc.import.un.addr4; p->prefixlen = IPV4_MAX_BITLEN; } return 0; case AF_INET6: if (p) { - p->family = bpi->extra->vnc.import.un_family; - p->u.prefix6 = bpi->extra->vnc.import.un.addr6; + p->family = + bpi->extra->vnc->vnc.import.un_family; + p->u.prefix6 = + bpi->extra->vnc->vnc.import.un.addr6; p->prefixlen = IPV6_MAX_BITLEN; } return 0; @@ -475,9 +448,11 @@ static struct bgp_path_info *rfapiBgpInfoCreate(struct attr *attr, new->attr = bgp_attr_intern(attr); bgp_path_info_extra_get(new); + new->extra->vnc = XCALLOC(MTYPE_BGP_ROUTE_EXTRA_VNC, + sizeof(struct bgp_path_info_extra_vnc)); if (prd) { - new->extra->vnc.import.rd = *prd; - new->extra->vnc.import.create_time = monotime(NULL); + new->extra->vnc->vnc.import.rd = *prd; + new->extra->vnc->vnc.import.create_time = monotime(NULL); } if (label) encode_label(*label, &new->extra->label[0]); @@ -842,13 +817,13 @@ static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi) * If there is a timer waiting to delete this bpi, cancel * the timer and delete immediately */ - if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) - && bpi->extra->vnc.import.timer) { + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && + bpi->extra->vnc->vnc.import.timer) { struct rfapi_withdraw *wcb = - EVENT_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc->vnc.import.timer); } next = bpi->next; @@ -1136,15 +1111,15 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, */ if (rfapiGetVncTunnelUnAddr(bpi1->attr, &pfx_un1)) { if (bpi1->extra) { - pfx_un1.family = bpi1->extra->vnc.import.un_family; - switch (bpi1->extra->vnc.import.un_family) { + pfx_un1.family = bpi1->extra->vnc->vnc.import.un_family; + switch (bpi1->extra->vnc->vnc.import.un_family) { case AF_INET: pfx_un1.u.prefix4 = - bpi1->extra->vnc.import.un.addr4; + bpi1->extra->vnc->vnc.import.un.addr4; break; case AF_INET6: pfx_un1.u.prefix6 = - bpi1->extra->vnc.import.un.addr6; + bpi1->extra->vnc->vnc.import.un.addr6; break; default: pfx_un1.family = AF_UNSPEC; @@ -1155,15 +1130,15 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, if (rfapiGetVncTunnelUnAddr(bpi2->attr, &pfx_un2)) { if (bpi2->extra) { - pfx_un2.family = bpi2->extra->vnc.import.un_family; - switch (bpi2->extra->vnc.import.un_family) { + pfx_un2.family = bpi2->extra->vnc->vnc.import.un_family; + switch (bpi2->extra->vnc->vnc.import.un_family) { case AF_INET: pfx_un2.u.prefix4 = - bpi2->extra->vnc.import.un.addr4; + bpi2->extra->vnc->vnc.import.un.addr4; break; case AF_INET6: pfx_un2.u.prefix6 = - bpi2->extra->vnc.import.un.addr6; + bpi2->extra->vnc->vnc.import.un.addr6; break; default: pfx_un2.family = AF_UNSPEC; @@ -1268,9 +1243,8 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, new->prefix = *rprefix; - if (bpi->extra - && decode_rd_type(bpi->extra->vnc.import.rd.val) - == RD_TYPE_VNC_ETH) { + if (bpi->extra && decode_rd_type(bpi->extra->vnc->vnc.import.rd.val) == + RD_TYPE_VNC_ETH) { /* ethernet */ struct rfapi_vn_option *vo; @@ -1289,7 +1263,8 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, &vo->v.l2addr.tag_id); /* local_nve_id comes from lower byte of RD type */ - vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1]; + vo->v.l2addr.local_nve_id = + bpi->extra->vnc->vnc.import.rd.val[1]; /* label comes from MP_REACH_NLRI label */ vo->v.l2addr.label = decode_label(&bpi->extra->label[0]); @@ -1300,8 +1275,9 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, * If there is an auxiliary prefix (i.e., host IP address), * use it as the nexthop prefix instead of the query prefix */ - if (bpi->extra->vnc.import.aux_prefix.family) { - rfapiQprefix2Rprefix(&bpi->extra->vnc.import.aux_prefix, + if (bpi->extra->vnc->vnc.import.aux_prefix.family) { + rfapiQprefix2Rprefix(&bpi->extra->vnc->vnc.import + .aux_prefix, &new->prefix); } } @@ -1402,15 +1378,16 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, /* * use cached UN address from ENCAP route */ - new->un_address.addr_family = bpi->extra->vnc.import.un_family; + new->un_address.addr_family = + bpi->extra->vnc->vnc.import.un_family; switch (new->un_address.addr_family) { case AF_INET: new->un_address.addr.v4 = - bpi->extra->vnc.import.un.addr4; + bpi->extra->vnc->vnc.import.un.addr4; break; case AF_INET6: new->un_address.addr.v6 = - bpi->extra->vnc.import.un.addr6; + bpi->extra->vnc->vnc.import.un.addr6; break; default: zlog_warn("%s: invalid UN addr family (%d) for bpi %p", @@ -1537,7 +1514,7 @@ static int rfapiNhlAddNodeRoutes( if (is_l2) { /* L2 routes: semantic nexthop in aux_prefix; VN addr * ain't it */ - pfx_vn = bpi->extra->vnc.import.aux_prefix; + pfx_vn = bpi->extra->vnc->vnc.import.aux_prefix; } else { rfapiNexthop2Prefix(bpi->attr, &pfx_vn); } @@ -1709,7 +1686,7 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( #ifdef DEBUG_RETURNED_NHL vnc_zlog_debug_verbose("%s: called with node pfx=%rRN", __func__, rn); - rfapiDebugBacktrace(); + zlog_backtrace(LOG_INFO); #endif rfapiQprefix2Rprefix(p, &rprefix); @@ -2011,9 +1988,10 @@ static int rfapi_bi_peer_rd_cmp(const void *b1, const void *b2) /* * compare RDs */ - return vnc_prefix_cmp( - (const struct prefix *)&bpi1->extra->vnc.import.rd, - (const struct prefix *)&bpi2->extra->vnc.import.rd); + return vnc_prefix_cmp((const struct prefix *)&bpi1->extra->vnc->vnc + .import.rd, + (const struct prefix *)&bpi2->extra->vnc->vnc + .import.rd); } /* @@ -2038,8 +2016,8 @@ static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2) /* * compare RDs */ - rc = vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc.import.rd, - (struct prefix *)&bpi2->extra->vnc.import.rd); + rc = vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc->vnc.import.rd, + (struct prefix *)&bpi2->extra->vnc->vnc.import.rd); if (rc) { return rc; } @@ -2056,19 +2034,18 @@ static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2) * because there is no guarantee of the order the test key and * the real key will be passed) */ - if ((bpi1->extra->vnc.import.aux_prefix.family == AF_ETHERNET - && (bpi1->extra->vnc.import.aux_prefix.prefixlen == 1)) - || (bpi2->extra->vnc.import.aux_prefix.family == AF_ETHERNET - && (bpi2->extra->vnc.import.aux_prefix.prefixlen == 1))) { - + if ((bpi1->extra->vnc->vnc.import.aux_prefix.family == AF_ETHERNET && + (bpi1->extra->vnc->vnc.import.aux_prefix.prefixlen == 1)) || + (bpi2->extra->vnc->vnc.import.aux_prefix.family == AF_ETHERNET && + (bpi2->extra->vnc->vnc.import.aux_prefix.prefixlen == 1))) { /* * wildcard aux address specified */ return 0; } - return vnc_prefix_cmp(&bpi1->extra->vnc.import.aux_prefix, - &bpi2->extra->vnc.import.aux_prefix); + return vnc_prefix_cmp(&bpi1->extra->vnc->vnc.import.aux_prefix, + &bpi2->extra->vnc->vnc.import.aux_prefix); } @@ -2086,7 +2063,7 @@ static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ assert(bpi->extra); vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, - bpi->peer, &bpi->extra->vnc.import.rd); + bpi->peer, &bpi->extra->vnc->vnc.import.rd); sl = RFAPI_RDINDEX_W_ALLOC(rn); if (!sl) { @@ -2123,11 +2100,10 @@ static void rfapiItBiIndexDump(struct agg_node *rn) char buf[RD_ADDRSTRLEN]; char buf_aux_pfx[PREFIX_STRLEN]; - prefix_rd2str( - &k->extra->vnc.import.rd, buf, sizeof(buf), - bgp_get_asnotation(k->peer ? k->peer->bgp : NULL)); - if (k->extra->vnc.import.aux_prefix.family) { - prefix2str(&k->extra->vnc.import.aux_prefix, + prefix_rd2str(&k->extra->vnc->vnc.import.rd, buf, sizeof(buf), + bgp_get_asnotation(k->peer ? k->peer->bgp : NULL)); + if (k->extra->vnc->vnc.import.aux_prefix.family) { + prefix2str(&k->extra->vnc->vnc.import.aux_prefix, buf_aux_pfx, sizeof(buf_aux_pfx)); } else strlcpy(buf_aux_pfx, "(none)", sizeof(buf_aux_pfx)); @@ -2146,6 +2122,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( int rc; struct bgp_path_info bpi_fake = {0}; struct bgp_path_info_extra bpi_extra = {0}; + struct bgp_path_info_extra_vnc bpi_extra_vnc = { 0 }; struct bgp_path_info *bpi_result; sl = RFAPI_RDINDEX(rn); @@ -2178,27 +2155,25 @@ static struct bgp_path_info *rfapiItBiIndexSearch( for (bpi_result = rn->info; bpi_result; bpi_result = bpi_result->next) { #ifdef DEBUG_BI_SEARCH - vnc_zlog_debug_verbose( - "%s: bpi has prd=%pRDP, peer=%p", __func__, - &bpi_result->extra->vnc.import.rd, - bpi_result->peer); + vnc_zlog_debug_verbose("%s: bpi has prd=%pRDP, peer=%p", + __func__, + &bpi_result->extra->vnc->vnc + .import.rd, + bpi_result->peer); #endif - if (peer == bpi_result->peer - && !prefix_cmp((struct prefix *)&bpi_result->extra - ->vnc.import.rd, - (struct prefix *)prd)) { - + if (peer == bpi_result->peer && + !prefix_cmp((struct prefix *)&bpi_result->extra->vnc + ->vnc.import.rd, + (struct prefix *)prd)) { #ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose( "%s: peer and RD same, doing aux_prefix check", __func__); #endif - if (!aux_prefix - || !prefix_cmp( - aux_prefix, - &bpi_result->extra->vnc.import - .aux_prefix)) { - + if (!aux_prefix || + !prefix_cmp(aux_prefix, + &bpi_result->extra->vnc->vnc + .import.aux_prefix)) { #ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose("%s: match", __func__); @@ -2212,13 +2187,14 @@ static struct bgp_path_info *rfapiItBiIndexSearch( bpi_fake.peer = peer; bpi_fake.extra = &bpi_extra; - bpi_fake.extra->vnc.import.rd = *prd; + bpi_fake.extra->vnc = &bpi_extra_vnc; + bpi_fake.extra->vnc->vnc.import.rd = *prd; if (aux_prefix) { - bpi_fake.extra->vnc.import.aux_prefix = *aux_prefix; + bpi_fake.extra->vnc->vnc.import.aux_prefix = *aux_prefix; } else { /* wildcard */ - bpi_fake.extra->vnc.import.aux_prefix.family = AF_ETHERNET; - bpi_fake.extra->vnc.import.aux_prefix.prefixlen = 1; + bpi_fake.extra->vnc->vnc.import.aux_prefix.family = AF_ETHERNET; + bpi_fake.extra->vnc->vnc.import.aux_prefix.prefixlen = 1; } rc = skiplist_search(sl, (void *)&bpi_fake, (void *)&bpi_result); @@ -2244,7 +2220,7 @@ static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */ int rc; vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, - bpi->peer, &bpi->extra->vnc.import.rd); + bpi->peer, &bpi->extra->vnc->vnc.import.rd); sl = RFAPI_RDINDEX(rn); assert(sl); @@ -2292,11 +2268,10 @@ rfapiMonitorEncapAdd(struct rfapi_import_table *import_table, RFAPI_MONITOR_ENCAP_W_ALLOC(rn) = m; /* for easy lookup when deleting vpn route */ - vpn_bpi->extra->vnc.import.hme = m; + vpn_bpi->extra->vnc->vnc.import.hme = m; - vnc_zlog_debug_verbose( - "%s: it=%p, vpn_bpi=%p, afi=%d, encap rn=%p, setting vpn_bpi->extra->vnc.import.hme=%p", - __func__, import_table, vpn_bpi, afi, rn, m); + vnc_zlog_debug_verbose("%s: it=%p, vpn_bpi=%p, afi=%d, encap rn=%p, setting vpn_bpi->extra->vnc->vnc.import.hme=%p", + __func__, import_table, vpn_bpi, afi, rn, m); RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0); bgp_attr_intern(vpn_bpi->attr); @@ -2310,7 +2285,7 @@ static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi) vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi); if (vpn_bpi->extra) { struct rfapi_monitor_encap *hme = - vpn_bpi->extra->vnc.import.hme; + vpn_bpi->extra->vnc->vnc.import.hme; if (hme) { @@ -2334,7 +2309,7 @@ static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi) agg_unlock_node(hme->rn); /* decr ref count */ XFREE(MTYPE_RFAPI_MONITOR_ENCAP, hme); - vpn_bpi->extra->vnc.import.hme = NULL; + vpn_bpi->extra->vnc->vnc.import.hme = NULL; } } } @@ -2573,21 +2548,21 @@ static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi, vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__, vpn_bpi->extra); - vpn_bpi->extra->vnc.import.un_family = AF_INET; - vpn_bpi->extra->vnc.import.un.addr4 = + vpn_bpi->extra->vnc->vnc.import.un_family = AF_INET; + vpn_bpi->extra->vnc->vnc.import.un.addr4 = encap_bpi->attr->mp_nexthop_global_in; break; case AF_INET6: - vpn_bpi->extra->vnc.import.un_family = AF_INET6; - vpn_bpi->extra->vnc.import.un.addr6 = + vpn_bpi->extra->vnc->vnc.import.un_family = AF_INET6; + vpn_bpi->extra->vnc->vnc.import.un.addr6 = encap_bpi->attr->mp_nexthop_global; break; default: zlog_warn("%s: invalid encap nexthop length: %d", __func__, encap_bpi->attr->mp_nexthop_len); - vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC; + vpn_bpi->extra->vnc->vnc.import.un_family = AF_UNSPEC; break; } } @@ -2612,9 +2587,9 @@ rfapiWithdrawEncapUpdateCachedUn(struct rfapi_import_table *import_table, __func__); return 1; } - vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC; - memset(&vpn_bpi->extra->vnc.import.un, 0, - sizeof(vpn_bpi->extra->vnc.import.un)); + vpn_bpi->extra->vnc->vnc.import.un_family = AF_UNSPEC; + memset(&vpn_bpi->extra->vnc->vnc.import.un, 0, + sizeof(vpn_bpi->extra->vnc->vnc.import.un)); if (CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) { if (rfapiGetVncTunnelUnAddr(vpn_bpi->attr, NULL)) { UNSET_FLAG(vpn_bpi->flags, BGP_PATH_VALID); @@ -2785,9 +2760,9 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, assert(bpi->extra); if (lifetime > UINT32_MAX / 1001) { /* sub-optimal case, but will probably never happen */ - bpi->extra->vnc.import.timer = NULL; + bpi->extra->vnc->vnc.import.timer = NULL; event_add_timer(bm->master, timer_service_func, wcb, lifetime, - &bpi->extra->vnc.import.timer); + &bpi->extra->vnc->vnc.import.timer); } else { static uint32_t jitter; uint32_t lifetime_msec; @@ -2801,10 +2776,10 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, lifetime_msec = (lifetime * 1000) + jitter; - bpi->extra->vnc.import.timer = NULL; + bpi->extra->vnc->vnc.import.timer = NULL; event_add_timer_msec(bm->master, timer_service_func, wcb, lifetime_msec, - &bpi->extra->vnc.import.timer); + &bpi->extra->vnc->vnc.import.timer); } /* re-sort route list (BGP_PATH_REMOVED routes are last) */ @@ -3017,17 +2992,16 @@ static void rfapiBgpInfoFilteredImportEncap( * Compare RDs * * RD of import table bpi is in - * bpi->extra->vnc.import.rd RD of info_orig is in prd + * bpi->extra->vnc->vnc.import.rd RD of info_orig is in prd */ if (!bpi->extra) { vnc_zlog_debug_verbose("%s: no bpi->extra", __func__); continue; } - if (prefix_cmp( - (struct prefix *)&bpi->extra->vnc.import.rd, - (struct prefix *)prd)) { - + if (prefix_cmp((struct prefix *)&bpi->extra->vnc->vnc + .import.rd, + (struct prefix *)prd)) { vnc_zlog_debug_verbose("%s: prd does not match", __func__); continue; @@ -3071,13 +3045,14 @@ static void rfapiBgpInfoFilteredImportEncap( * a previous withdraw, we must cancel its * timer. */ - if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) - && bpi->extra->vnc.import.timer) { + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && + bpi->extra->vnc->vnc.import.timer) { struct rfapi_withdraw *wcb = EVENT_ARG( - bpi->extra->vnc.import.timer); + bpi->extra->vnc->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc->vnc.import + .timer); } if (action == FIF_ACTION_UPDATE) { @@ -3163,12 +3138,12 @@ static void rfapiBgpInfoFilteredImportEncap( vnc_zlog_debug_verbose( "%s: removing holddown bpi matching NVE of new route", __func__); - if (bpi->extra->vnc.import.timer) { + if (bpi->extra->vnc->vnc.import.timer) { struct rfapi_withdraw *wcb = - EVENT_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc->vnc.import.timer); } rfapiExpireEncapNow(import_table, rn, bpi); } @@ -3523,13 +3498,14 @@ void rfapiBgpInfoFilteredImportVPN( * a previous withdraw, we must cancel its * timer. */ - if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) - && bpi->extra->vnc.import.timer) { + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && + bpi->extra->vnc->vnc.import.timer) { struct rfapi_withdraw *wcb = EVENT_ARG( - bpi->extra->vnc.import.timer); + bpi->extra->vnc->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc->vnc.import + .timer); import_table->holddown_count[afi] -= 1; RFAPI_UPDATE_ITABLE_COUNT( @@ -3602,7 +3578,7 @@ void rfapiBgpInfoFilteredImportVPN( /* Not a big deal, just means VPN route got here first */ vnc_zlog_debug_verbose("%s: no encap route for vn addr %pFX", __func__, &vn_prefix); - info_new->extra->vnc.import.un_family = AF_UNSPEC; + info_new->extra->vnc->vnc.import.un_family = AF_UNSPEC; } if (rn) { @@ -3624,7 +3600,7 @@ void rfapiBgpInfoFilteredImportVPN( vnc_zlog_debug_verbose("%s: setting BPI's aux_prefix", __func__); - info_new->extra->vnc.import.aux_prefix = *aux_prefix; + info_new->extra->vnc->vnc.import.aux_prefix = *aux_prefix; } vnc_zlog_debug_verbose("%s: inserting bpi %p at prefix %pRN #%d", @@ -3742,12 +3718,12 @@ void rfapiBgpInfoFilteredImportVPN( vnc_zlog_debug_verbose( "%s: removing holddown bpi matching NVE of new route", __func__); - if (bpi->extra->vnc.import.timer) { + if (bpi->extra->vnc->vnc.import.timer) { struct rfapi_withdraw *wcb = - EVENT_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc->vnc.import.timer); } rfapiExpireVpnNow(import_table, rn, bpi, 0); } @@ -4479,12 +4455,10 @@ static void rfapiDeleteRemotePrefixesIt( if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { if (!delete_holddown) continue; - if (bpi->extra->vnc.import.timer) { - struct rfapi_withdraw *wcb = - EVENT_ARG( - bpi->extra->vnc - .import - .timer); + if (bpi->extra->vnc->vnc.import.timer) { + struct rfapi_withdraw *wcb = EVENT_ARG( + bpi->extra->vnc->vnc + .import.timer); wcb->import_table ->holddown_count[afi] -= @@ -4494,8 +4468,8 @@ static void rfapiDeleteRemotePrefixesIt( afi, 1); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - EVENT_OFF(bpi->extra->vnc.import - .timer); + EVENT_OFF(bpi->extra->vnc->vnc + .import.timer); } } else { if (!delete_active) diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h index dd06afe..1a37e1c 100644 --- a/bgpd/rfapi/rfapi_import.h +++ b/bgpd/rfapi/rfapi_import.h @@ -57,8 +57,6 @@ struct rfapi_import_table { extern uint8_t rfapiRfpCost(struct attr *attr); -extern void rfapiDebugBacktrace(void); - extern void rfapiCheckRouteCount(void); /* diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index 3fe957b..146e0d1 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -354,7 +354,7 @@ struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd, * If there is a cached ENCAP UN address, it's a usable * VPN route */ - if (bpi->extra && bpi->extra->vnc.import.un_family) { + if (bpi->extra && bpi->extra->vnc->vnc.import.un_family) { break; } diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 5784f95..316904e 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -663,9 +663,8 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, /* * VN options */ - if (bpi->extra - && decode_rd_type(bpi->extra->vnc.import.rd.val) - == RD_TYPE_VNC_ETH) { + if (bpi->extra && decode_rd_type(bpi->extra->vnc->vnc.import.rd.val) == + RD_TYPE_VNC_ETH) { /* ethernet route */ struct rfapi_vn_option *vo; @@ -678,8 +677,8 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, /* copy from RD already stored in bpi, so we don't need it_node */ - memcpy(&vo->v.l2addr.macaddr, bpi->extra->vnc.import.rd.val + 2, - ETH_ALEN); + memcpy(&vo->v.l2addr.macaddr, + bpi->extra->vnc->vnc.import.rd.val + 2, ETH_ALEN); (void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr), &vo->v.l2addr.logical_net_id); @@ -688,7 +687,8 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, &vo->v.l2addr.tag_id); /* local_nve_id comes from RD */ - vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1]; + vo->v.l2addr.local_nve_id = + bpi->extra->vnc->vnc.import.rd.val[1]; /* label comes from MP_REACH_NLRI label */ vo->v.l2addr.label = decode_label(&bpi->extra->label[0]); @@ -701,8 +701,8 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, /* * If there is an auxiliary IP address (L2 can have it), copy it */ - if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { - ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + if (bpi->extra && bpi->extra->vnc->vnc.import.aux_prefix.family) { + ri->rk.aux_prefix = bpi->extra->vnc->vnc.import.aux_prefix; } } @@ -746,13 +746,13 @@ int rfapiRibPreloadBi( memset((void *)&rk, 0, sizeof(rk)); rk.vn = *pfx_vn; - rk.rd = bpi->extra->vnc.import.rd; + rk.rd = bpi->extra->vnc->vnc.import.rd; /* * If there is an auxiliary IP address (L2 can have it), copy it */ - if (bpi->extra->vnc.import.aux_prefix.family) { - rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + if (bpi->extra->vnc->vnc.import.aux_prefix.family) { + rk.aux_prefix = bpi->extra->vnc->vnc.import.aux_prefix; } /* @@ -1629,12 +1629,13 @@ void rfapiRibUpdatePendingNode( ri = rfapi_info_new(); ri->rk.vn = pfx_nh; - ri->rk.rd = bpi->extra->vnc.import.rd; + ri->rk.rd = bpi->extra->vnc->vnc.import.rd; /* * If there is an auxiliary IP address (L2 can have it), copy it */ - if (bpi->extra->vnc.import.aux_prefix.family) { - ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + if (bpi->extra->vnc->vnc.import.aux_prefix.family) { + ri->rk.aux_prefix = + bpi->extra->vnc->vnc.import.aux_prefix; } if (rfapiGetUnAddrOfVpnBi(bpi, &ri->un)) { diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 252b6d6..5da99db 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -419,22 +419,21 @@ void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, else vty_out(vty, " label=%u", decode_label(&bpi->extra->label[0])); + } - if (bpi->attr->srv6_l3vpn || bpi->attr->srv6_vpn) { - struct in6_addr *sid_tmp = - bpi->attr->srv6_l3vpn - ? (&bpi->attr->srv6_l3vpn->sid) - : (&bpi->attr->srv6_vpn->sid); - vty_out(vty, " sid=%pI6", sid_tmp); - - if (bpi->attr->srv6_l3vpn && - bpi->attr->srv6_l3vpn->loc_block_len != 0) { - vty_out(vty, " sid_structure=[%d,%d,%d,%d]", - bpi->attr->srv6_l3vpn->loc_block_len, - bpi->attr->srv6_l3vpn->loc_node_len, - bpi->attr->srv6_l3vpn->func_len, - bpi->attr->srv6_l3vpn->arg_len); - } + if (bpi->attr->srv6_l3vpn || bpi->attr->srv6_vpn) { + struct in6_addr *sid_tmp = + bpi->attr->srv6_l3vpn ? (&bpi->attr->srv6_l3vpn->sid) + : (&bpi->attr->srv6_vpn->sid); + vty_out(vty, " sid=%pI6", sid_tmp); + + if (bpi->attr->srv6_l3vpn && + bpi->attr->srv6_l3vpn->loc_block_len != 0) { + vty_out(vty, " sid_structure=[%d,%d,%d,%d]", + bpi->attr->srv6_l3vpn->loc_block_len, + bpi->attr->srv6_l3vpn->loc_node_len, + bpi->attr->srv6_l3vpn->func_len, + bpi->attr->srv6_l3vpn->arg_len); } } @@ -519,9 +518,10 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) if (!bpi) return; - if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra - && bpi->extra->vnc.import.timer) { - struct event *t = (struct event *)bpi->extra->vnc.import.timer; + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra && + bpi->extra->vnc->vnc.import.timer) { + struct event *t = + (struct event *)bpi->extra->vnc->vnc.import.timer; r = snprintf(p, REMAIN, " [%4lu] ", event_timer_remain_second(t)); @@ -534,12 +534,12 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) if (bpi->extra) { /* TBD This valid only for SAFI_MPLS_VPN, but not for encap */ - if (decode_rd_type(bpi->extra->vnc.import.rd.val) - == RD_TYPE_VNC_ETH) { + if (decode_rd_type(bpi->extra->vnc->vnc.import.rd.val) == + RD_TYPE_VNC_ETH) { has_macaddr = 1; - memcpy(macaddr.octet, bpi->extra->vnc.import.rd.val + 2, - 6); - l2hid = bpi->extra->vnc.import.rd.val[1]; + memcpy(macaddr.octet, + bpi->extra->vnc->vnc.import.rd.val + 2, 6); + l2hid = bpi->extra->vnc->vnc.import.rd.val[1]; } } @@ -670,11 +670,11 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) l2o_buf.label, l2o_buf.logical_net_id, l2o_buf.local_nve_id, HVTYNL); } - if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { + if (bpi->extra && bpi->extra->vnc->vnc.import.aux_prefix.family) { const char *sp; - sp = rfapi_ntop(bpi->extra->vnc.import.aux_prefix.family, - &bpi->extra->vnc.import.aux_prefix.u.prefix, + sp = rfapi_ntop(bpi->extra->vnc->vnc.import.aux_prefix.family, + &bpi->extra->vnc->vnc.import.aux_prefix.u.prefix, buf, BUFSIZ); buf[BUFSIZ - 1] = 0; if (sp) { @@ -1097,14 +1097,14 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, fp(out, "%-10s ", buf_lifetime); } - if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra - && bpi->extra->vnc.import.timer) { - + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra && + bpi->extra->vnc->vnc.import.timer) { uint32_t remaining; time_t age; char buf_age[BUFSIZ]; - struct event *t = (struct event *)bpi->extra->vnc.import.timer; + struct event *t = + (struct event *)bpi->extra->vnc->vnc.import.timer; remaining = event_timer_remain_second(t); #ifdef RFAPI_REGISTRATIONS_REPORT_AGE @@ -1125,11 +1125,10 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, fp(out, "%-10s ", buf_age); } else if (RFAPI_LOCAL_BI(bpi)) { - char buf_age[BUFSIZ]; - if (bpi->extra && bpi->extra->vnc.import.create_time) { - rfapiFormatAge(bpi->extra->vnc.import.create_time, + if (bpi->extra && bpi->extra->vnc->vnc.import.create_time) { + rfapiFormatAge(bpi->extra->vnc->vnc.import.create_time, buf_age, BUFSIZ); } else { buf_age[0] = '?'; @@ -1145,13 +1144,14 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, * print that on the next line */ - if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { + if (bpi->extra && bpi->extra->vnc->vnc.import.aux_prefix.family) { const char *sp; - sp = rfapi_ntop( - bpi->extra->vnc.import.aux_prefix.family, - &bpi->extra->vnc.import.aux_prefix.u.prefix, - buf_ntop, BUFSIZ); + sp = rfapi_ntop(bpi->extra->vnc->vnc.import.aux_prefix + .family, + &bpi->extra->vnc->vnc.import.aux_prefix + .u.prefix, + buf_ntop, BUFSIZ); buf_ntop[BUFSIZ - 1] = 0; if (sp && strcmp(buf_vn, sp) != 0) { @@ -1545,10 +1545,9 @@ void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd, vty_out(vty, " bd=%p%s", bd, HVTYNL); for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { - if (bpi->peer == rfd->peer && bpi->type == type - && bpi->sub_type == BGP_ROUTE_RFP && bpi->extra - && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { - + if (bpi->peer == rfd->peer && bpi->type == type && + bpi->sub_type == BGP_ROUTE_RFP && bpi->extra && + bpi->extra->vnc->vnc.export.rfapi_handle == (void *)rfd) { rfapiPrintBi(vty, bpi); printed = 1; } @@ -4151,6 +4150,7 @@ static int rfapi_vty_show_nve_summary(struct vty *vty, case SHOW_NVE_SUMMARY_RESPONSES: rfapiRibShowResponsesSummary(vty); + break; case SHOW_NVE_SUMMARY_UNKNOWN_NVES: case SHOW_NVE_SUMMARY_MAX: diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 3fcc0e6..c067b7a 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -1696,8 +1696,8 @@ static void vnc_import_bgp_exterior_add_route_it( have_usable_route = 1; if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import - .rd; + prd = &bpi_interior->extra->vnc->vnc + .import.rd; label = decode_label( &bpi_interior->extra->label[0]); } else @@ -1865,8 +1865,8 @@ void vnc_import_bgp_exterior_del_route( have_usable_route = 1; if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import - .rd; + prd = &bpi_interior->extra->vnc->vnc + .import.rd; label = decode_label( &bpi_interior->extra->label[0]); } else @@ -2013,7 +2013,7 @@ void vnc_import_bgp_exterior_add_route_interior( assert(pfx_exterior); if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import.rd; + prd = &bpi_interior->extra->vnc->vnc.import.rd; label = decode_label( &bpi_interior->extra->label[0]); } else @@ -2126,8 +2126,8 @@ void vnc_import_bgp_exterior_add_route_interior( for (bpi = par->info; bpi; bpi = bpi->next) { if (bpi->extra) { - prd = &bpi->extra->vnc.import - .rd; + prd = &bpi->extra->vnc->vnc + .import.rd; label = decode_label( &bpi->extra->label[0]); } else @@ -2148,8 +2148,8 @@ void vnc_import_bgp_exterior_add_route_interior( * the new interior route at longer prefix. */ if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import - .rd; + prd = &bpi_interior->extra->vnc->vnc + .import.rd; label = decode_label( &bpi_interior->extra->label[0]); } else @@ -2267,7 +2267,7 @@ void vnc_import_bgp_exterior_add_route_interior( * new interior route at the longer prefix. */ if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import.rd; + prd = &bpi_interior->extra->vnc->vnc.import.rd; label = decode_label( &bpi_interior->extra->label[0]); } else @@ -2375,7 +2375,7 @@ void vnc_import_bgp_exterior_del_route_interior( uint32_t label = 0; if (bpi_interior->extra) { - prd = &bpi_interior->extra->vnc.import.rd; + prd = &bpi_interior->extra->vnc->vnc.import.rd; label = decode_label(&bpi_interior->extra->label[0]); } else prd = NULL; @@ -2452,7 +2452,7 @@ void vnc_import_bgp_exterior_del_route_interior( continue; if (bpi->extra) { - prd = &bpi->extra->vnc.import.rd; + prd = &bpi->extra->vnc->vnc.import.rd; label = decode_label( &bpi->extra->label[0]); } else @@ -2805,14 +2805,14 @@ void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) assert(bpi->extra); - rfd = bpi->extra->vnc.export.rfapi_handle; + rfd = bpi->extra->vnc->vnc.export.rfapi_handle; vnc_zlog_debug_verbose( - "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc->vnc.export.rfapi_handle=%p [passing rfd=%p]", __func__, bpi, bpi->peer, bpi->type, bpi->sub_type, - (bpi->extra ? bpi->extra->vnc.export - .rfapi_handle + (bpi->extra ? bpi->extra->vnc->vnc + .export.rfapi_handle : NULL), rfd); diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index c886bee..82c08ca 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -872,7 +872,7 @@ static zclient_handler *const vnc_handlers[] = { void vnc_zebra_init(struct event_loop *master) { /* Set default values. */ - zclient_vnc = zclient_new(master, &zclient_options_default, + zclient_vnc = zclient_new(master, &zclient_options_auxiliary, vnc_handlers, array_size(vnc_handlers)); zclient_init(zclient_vnc, ZEBRA_ROUTE_VNC, 0, &bgpd_privs); } diff --git a/bgpd/subdir.am b/bgpd/subdir.am index c2dd207..6d6fad0 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -77,6 +77,7 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_zebra.c \ bgpd/bgpd.c \ bgpd/bgp_trace.c \ + bgpd/bgp_nhg.c \ # end if ENABLE_BGP_VNC @@ -160,6 +161,7 @@ noinst_HEADERS += \ bgpd/bgp_zebra.h \ bgpd/bgpd.h \ bgpd/bgp_trace.h \ + bgpd/bgp_nhg.h \ \ bgpd/rfapi/bgp_rfapi_cfg.h \ bgpd/rfapi/rfapi_import.h \ @@ -214,6 +216,7 @@ clippy_scan += \ bgpd/bgp_rpki.c \ bgpd/bgp_vty.c \ bgpd/bgp_nexthop.c \ + bgpd/bgp_snmp.c \ # end nodist_bgpd_bgpd_SOURCES = \ diff --git a/buildtest.sh b/buildtest.sh index 7edc4a5..90ef60e 100755 --- a/buildtest.sh +++ b/buildtest.sh @@ -5,7 +5,7 @@ # builds some git commit of FRR in some different configurations # usage: buildtest.sh [commit [configurations...]] -basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/frr --localstatedir=/var/run/frr --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror" +basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror" configs_base="gcc|$basecfg" @@ -37,9 +37,10 @@ trap errfunc ERR COMMITREF="$1" COMMITISH="`git rev-list --max-count=1 ${COMMITREF:-HEAD}`" -TEMP="`mktemp -t -d frrbuild.XXXXXX`" +TEMP="`mktemp -d -t frrbuild.XXXXXX`" BASE="`pwd`" CONFIGS="$2" +MAKE="${MAKE:-make}" echo using temporary directory: $TEMP echo git commit used: @@ -59,7 +60,7 @@ echo -e "\n\n\n\n\033[33;1mmaking dist tarball\033[m" mkdir build_dist cd build_dist ../source/configure -make distdir=sdist dist-gzip +${MAKE} distdir=sdist dist-gzip cd .. tar zxvf build_dist/sdist.tar.gz @@ -79,9 +80,9 @@ for cfg in ${CONFIGS:-$defconfigs}; do mkdir "$bdir" cd "$bdir" ../sdist/configure $args - make -j5 - make check - make DESTDIR="$TEMP/inst_$cfg" install + ${MAKE} -j5 + ${MAKE} check + ${MAKE} DESTDIR="$TEMP/inst_$cfg" install cd .. done diff --git a/configure.ac b/configure.ac index 3d90a11..0a40661 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.69]) -AC_INIT([frr], [9.1], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [10.0], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -20,6 +20,79 @@ AC_CONFIG_SRCDIR([lib/zebra.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([m4/ac]) +dnl ------------------------------ +dnl system paths +dnl ------------------------------ +dnl Versions of FRR (or Quagga, or Zebra) before ca. 9.2 used sysconfdir and +dnl localstatedir as-is, without appending /frr. The /frr was expected to be +dnl given on ./configure invocations. +dnl +dnl This does not match standard behavior by other packages and makes FRR +dnl specific packaging changes necessary to add these options. localstatedir +dnl was also misused to include the /run part (it normally is only /var), +dnl leaving no path configuration option that references /var itself. This +dnl is because runstatedir did not exist in ancient autoconf. +dnl +dnl The path options have been changed to expect plain / system prefix +dnl directories. As a temporary workaround to not break packaging, eventual +dnl /frr suffixes are stripped and a warning is printed. + +path_warn_banner=false + +AC_MSG_CHECKING([whether --sysconfdir option is FRR-specific]) +case "$sysconfdir" in + */frr) + AC_MSG_RESULT([yes, ends in /frr - removing suffix]) + AC_MSG_WARN([Please remove /frr suffix from --sysconfdir="${sysconfdir}" (it should be /etc in 99% of cases)]) + sysconfdir="${sysconfdir%/frr}" + path_warn_banner=true + ;; + *) + AC_MSG_RESULT([no, as expected]) + ;; +esac + +frr_sysconfdir="\${sysconfdir}/frr" + +AC_MSG_CHECKING([whether --localstatedir option is FRR-specific]) +case "$localstatedir" in + */run/frr) + AC_MSG_RESULT([yes, ends in /run/frr - removing suffix]) + AC_MSG_WARN([Please remove /run/frr suffix from --localstatedir=${localstatedir} (it should be /var in 99% of cases)]) + localstatedir="${localstatedir%/run/frr}" + path_warn_banner=true + ;; + */frr) + AC_MSG_RESULT([yes, ends in /frr - removing suffix]) + AC_MSG_WARN([The --localstatedir=${localstatedir} option seems to include /frr but not /run, this is unexpected. Please check for consistency.)]) + localstatedir="${localstatedir%/frr}" + path_warn_banner=true + ;; + *) + AC_MSG_RESULT([no, as expected]) + ;; +esac + +dnl runstatedir is either ${localstatedir}/run or plain /run +dnl the change of localstatedir above may impact this +dnl +dnl note runstatedir was never used with /frr as the other two above, so does +dnl not need the same cleanup hack +: "${runstatedir:=\${localstatedir\}/run}" +frr_runstatedir="\${runstatedir}/frr" + +if $path_warn_banner; then + AC_MSG_WARN([^]) + AC_MSG_WARN([^]) + AC_MSG_WARN([^ warnings regarding system path configuration were printed above]) + AC_MSG_WARN([^ paths have been adjusted by temporary workarounds]) + AC_MSG_WARN([^ please fix your ./configure invocation (remove /frr) so it will work without the workarounds]) + AC_MSG_WARN([^]) + AC_MSG_WARN([^]) +fi + +frr_libstatedir="\${localstatedir}/lib/frr" + dnl ----------------------------------- dnl Get hostname and other information. dnl ----------------------------------- @@ -130,10 +203,10 @@ AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directo ]) AC_SUBST([moduledir], [$moduledir]) -AC_ARG_WITH([scriptdir], [AS_HELP_STRING([--with-scriptdir=DIR], [script directory (${sysconfdir}/scripts)])], [ +AC_ARG_WITH([scriptdir], [AS_HELP_STRING([--with-scriptdir=DIR], [script directory (${sysconfdir}/frr/scripts)])], [ scriptdir="$withval" ], [ - scriptdir="\${sysconfdir}/scripts" + scriptdir="\${frr_sysconfdir}/scripts" ]) AC_SUBST([scriptdir], [$scriptdir]) @@ -365,6 +438,7 @@ AC_C_FLAG([-Wpointer-arith]) AC_C_FLAG([-Wbad-function-cast]) AC_C_FLAG([-Wwrite-strings]) AC_C_FLAG([-Wundef]) +AC_C_FLAG([-Wimplicit-fallthrough]) if test "$enable_gcc_ultra_verbose" = "yes" ; then AC_C_FLAG([-Wcast-qual]) AC_C_FLAG([-Wmissing-noreturn]) @@ -378,6 +452,7 @@ else fi AC_C_FLAG([-Wno-unused-parameter]) AC_C_FLAG([-Wno-missing-field-initializers]) +AC_C_FLAG([-Wno-microsoft-anon-tag]) AC_C_FLAG([-Wc++-compat], [], [CXX_COMPAT_CFLAGS="-Wc++-compat"]) AC_SUBST([CXX_COMPAT_CFLAGS]) @@ -626,6 +701,8 @@ AC_ARG_ENABLE([mgmtd], AS_HELP_STRING([--disable-mgmtd], [do not build mgmtd])) AC_ARG_ENABLE([mgmtd_local_validations], AS_HELP_STRING([--enable-mgmtd-local-validations], [dev: unimplemented local validation])) +AC_ARG_ENABLE([mgmtd_test_be_client], + AS_HELP_STRING([--enable-mgmtd-test-be-client], [build test backend client])) AC_ARG_ENABLE([ripd], AS_HELP_STRING([--disable-ripd], [do not build ripd])) AC_ARG_ENABLE([ripngd], @@ -714,7 +791,7 @@ AC_ARG_ENABLE([realms], AC_ARG_ENABLE([rtadv], AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) AC_ARG_ENABLE([irdp], - AS_HELP_STRING([--disable-irdp], [disable IRDP server support in zebra (enabled by default if supported)])) + AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra])) AC_ARG_ENABLE([capabilities], AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities])) AC_ARG_ENABLE([gcc_ultra_verbose], @@ -1179,9 +1256,6 @@ m4_define([FRR_INCLUDES], /* Required for MAXSIG */ #include #include -#ifdef __APPLE__ -# define __APPLE_USE_RFC_3542 -#endif #include #include #include @@ -1582,6 +1656,21 @@ FRR_INCLUDES #endif ])dnl +AC_CHECK_HEADERS([netinet6/ip6_mroute.h], [], [],[ + #include + #include + #include + #include + #include +]) + +m4_define([FRR_INCLUDES], +FRR_INCLUDES +[#ifdef HAVE_NETINET_IP6_MROUTE_H +# include +#endif +])dnl + AC_MSG_CHECKING([for RFC3678 protocol-independed API]) AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM([[ @@ -1739,6 +1828,10 @@ AS_IF([test "$enable_mgmtd" != "no"], [ ]) ]) +AS_IF([test "$enable_mgmtd_test_be_client" = "yes"], [ + AC_DEFINE([HAVE_MGMTD_TESTC], [1], [mgmtd_testc]) +]) + AS_IF([test "$enable_ripd" != "no"], [ AC_DEFINE([HAVE_RIPD], [1], [ripd]) ]) @@ -1954,9 +2047,9 @@ AC_SUBST([SNMP_CFLAGS]) dnl --------------- dnl libyang dnl --------------- -PKG_CHECK_MODULES([LIBYANG], [libyang >= 2.0.0], , [ - AC_MSG_ERROR([libyang (>= 2.0.0) was not found on your system.]) -]) +PKG_CHECK_MODULES([LIBYANG], [libyang >= 2.1.128], , [ +AC_MSG_ERROR([m4_normalize([libyang >= 2.1.128 is required, and was not found on your system. +Pleaes consult doc/developer/building-libyang.rst for instructions on installing or building libyang.])])]) ac_cflags_save="$CFLAGS" CFLAGS="$CFLAGS $LIBYANG_CFLAGS" AC_CHECK_MEMBER([struct lyd_node.priv], [], [ @@ -1965,6 +2058,13 @@ AC_CHECK_MEMBER([struct lyd_node.priv], [], [ Instructions for this are included in the build documentation for your platform at http://docs.frrouting.org/projects/dev-guide/en/latest/building.html]) ]) ], [[#include ]]) + +AC_CHECK_LIB([yang],[lyd_find_xpath3],[],[AC_MSG_ERROR([m4_normalize([ +libyang missing lyd_find_xpath3])])]) +dnl -- don't add lyd_new_list3 to this list unless bug is fixed upstream +dnl -- https://github.com/CESNET/libyang/issues/2149 +AC_CHECK_FUNCS([ly_strerrcode ly_strvecode lyd_trim_xpath]) + CFLAGS="$ac_cflags_save" dnl --------------- @@ -2255,6 +2355,9 @@ yes) no) IRDP=false ;; +*) + IRDP=false + ;; esac @@ -2635,87 +2738,27 @@ else fi AC_SUBST([CONFDATE]) -dnl ------------------------------ -dnl set paths for state directory -dnl ------------------------------ -AC_MSG_CHECKING([directory to use for state file]) -if test "$prefix" = "NONE"; then - frr_statedir_prefix=""; -else - frr_statedir_prefix=${prefix} -fi -if test "$localstatedir" = '${prefix}/var'; then - for FRR_STATE_DIR in ${frr_statedir_prefix}/var/run dnl - ${frr_statedir_prefix}/var/adm dnl - ${frr_statedir_prefix}/etc dnl - /var/run dnl - /var/adm dnl - /etc dnl - /dev/null; - do - test -d $FRR_STATE_DIR && break - done - frr_statedir=$FRR_STATE_DIR -else - frr_statedir=${localstatedir} -fi -if test "$frr_statedir" = "/dev/null"; then - AC_MSG_ERROR([STATE DIRECTORY NOT FOUND! FIX OR SPECIFY --localstatedir!]) -fi -AC_MSG_RESULT([${frr_statedir}]) -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([MGMTD_FE_SERVER_PATH], ["$frr_statedir/mgmtd_fe.sock"], [mgmtd frontend server socket]) -AC_DEFINE_UNQUOTED([MGMTD_BE_SERVER_PATH], ["$frr_statedir/mgmtd_be.sock"], [mgmtd backend server socket]) -AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) -AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information]) -AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number 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]) - -dnl autoconf does this, but it does it too late... -test "$prefix" = "NONE" && prefix=$ac_default_prefix -test "$exec_prefix" = "NONE" && exec_prefix='${prefix}' - dnl get the full path, recursing through variables... -vtysh_bin="$bindir/vtysh" -for I in 1 2 3 4 5 6 7 8 9 10; do - eval vtysh_bin="\"$vtysh_bin\"" -done -AC_DEFINE_UNQUOTED([VTYSH_BIN_PATH], ["$vtysh_bin"], [path to vtysh binary]) -AC_SUBST([vtysh_bin]) - -CFG_SYSCONF="$sysconfdir" -CFG_SBIN="$sbindir" -CFG_BIN="$bindir" -CFG_STATE="$frr_statedir" -CFG_MODULE="$moduledir" -CFG_YANGMODELS="$yangmodelsdir" -CFG_SCRIPT="$scriptdir" -for I in 1 2 3 4 5 6 7 8 9 10; do - eval CFG_SYSCONF="\"$CFG_SYSCONF\"" - eval CFG_SBIN="\"$CFG_SBIN\"" - eval CFG_BIN="\"$CFG_BIN\"" - eval CFG_STATE="\"$CFG_STATE\"" - eval CFG_MODULE="\"$CFG_MODULE\"" - eval CFG_YANGMODELS="\"$CFG_YANGMODELS\"" - eval CFG_SCRIPT="\"$CFG_SCRIPT\"" -done -AC_SUBST([CFG_SYSCONF]) -AC_SUBST([CFG_SBIN]) -AC_SUBST([CFG_BIN]) -AC_SUBST([CFG_STATE]) -AC_SUBST([CFG_MODULE]) -AC_SUBST([CFG_SCRIPT]) -AC_SUBST([CFG_YANGMODELS]) -AC_DEFINE_UNQUOTED([MODULE_PATH], ["$CFG_MODULE"], [path to modules]) -AC_DEFINE_UNQUOTED([SCRIPT_PATH], ["$CFG_SCRIPT"], [path to scripts]) -AC_DEFINE_UNQUOTED([YANG_MODELS_PATH], ["$CFG_YANGMODELS"], [path to YANG data models]) -AC_DEFINE_UNQUOTED([WATCHFRR_SH_PATH], ["${CFG_SBIN%/}/watchfrr.sh"], [path to watchfrr.sh]) +AC_DEFUN([AX_SUBST_EXPANDED], [ + AX_RECURSIVE_EVAL([[$]$1], [e_$1]) + AC_SUBST([e_$1]) +]) + +AX_SUBST_EXPANDED([bindir]) +AX_SUBST_EXPANDED([sbindir]) +AX_SUBST_EXPANDED([frr_sysconfdir]) +AX_SUBST_EXPANDED([frr_runstatedir]) +AX_SUBST_EXPANDED([frr_libstatedir]) +AX_SUBST_EXPANDED([moduledir]) +AX_SUBST_EXPANDED([yangmodelsdir]) +AX_SUBST_EXPANDED([scriptdir]) + +dnl strip duplicate trailing slashes if necessary +dnl note this uses e_bindir / e_sbindir created above +watchfrr_sh="\${e_sbindir%/}/watchfrr.sh" +AX_SUBST_EXPANDED([watchfrr_sh]) +vtysh_bin="\${e_bindir%/}/vtysh" +AX_SUBST_EXPANDED([vtysh_bin]) dnl various features AM_CONDITIONAL([SUPPORT_REALMS], [test "$enable_realms" = "yes"]) @@ -2750,6 +2793,7 @@ AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"]) AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"]) AM_CONDITIONAL([BGPD], [test "$enable_bgpd" != "no"]) AM_CONDITIONAL([MGMTD], [test "$enable_mgmtd" != "no"]) +AM_CONDITIONAL([MGMTD_TESTC], [test "$enable_mgmtd_test_be_client" = "yes"]) AM_CONDITIONAL([RIPD], [test "$enable_ripd" != "no"]) AM_CONDITIONAL([OSPFD], [test "$enable_ospfd" != "no"]) AM_CONDITIONAL([LDPD], [test "$enable_ldpd" != "no"]) @@ -2787,6 +2831,7 @@ AC_CONFIG_FILES([ alpine/APKBUILD snapcraft/snapcraft.yaml lib/version.h + lib/config_paths.h tests/lib/cli/test_cli.refout pkgsrc/mgmtd.sh pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh @@ -2831,7 +2876,7 @@ fi FRR_ALL_CCLS_FLAGS="$(echo ${LIBYANG_CFLAGS} ${LUA_INCLUDE} ${SQLITE3_CFLAGS} | sed -e 's/ */ /g')" FRR_ALL_CCLS_CFLAGS="$(echo ${CFLAGS} ${WERROR} ${AC_CFLAGS} ${SAN_FLAGS} | sed -e 's/ */ /g')" ac_frr_confdate="${CONFDATE}" - ac_frr_sysconfdir="${sysconfdir}/" + ac_frr_sysconfdir="${frr_sysconfdir}/" ]) ]) @@ -2874,10 +2919,10 @@ compiler : ${CC} compiler flags : ${CFLAGS} ${WERROR} ${AC_CFLAGS} ${SAN_FLAGS} make : ${MAKE-make} linker flags : ${LDFLAGS} ${SAN_FLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} -state file directory : ${frr_statedir} -config file directory : `eval echo \`echo ${sysconfdir}\`` -module directory : ${CFG_MODULE} -script directory : ${CFG_SCRIPT} +state file directory : ${e_frr_runstatedir} +config file directory : ${e_frr_sysconfdir} +module directory : ${e_moduledir} +script directory : ${e_scriptdir} user to run as : ${enable_user} group to run as : ${enable_group} group for vty sockets : ${enable_vty_group} @@ -2899,3 +2944,11 @@ fi if test "$frr_py_mod_pytest" = "false"; then AC_MSG_WARN([pytest is missing, unit tests cannot be performed]) fi + +if $path_warn_banner; then + AC_MSG_WARN([^]) + AC_MSG_WARN([^]) + AC_MSG_WARN([^ warnings regarding system path configuration were printed at the very top of output]) + AC_MSG_WARN([^ paths have been adjusted by temporary workarounds]) + AC_MSG_WARN([^ please fix your ./configure invocation (remove /frr) so it will work without the workarounds]) +fi diff --git a/doc/developer/.readthedocs.yaml b/doc/developer/.readthedocs.yaml index 113672f..90ee5c7 100644 --- a/doc/developer/.readthedocs.yaml +++ b/doc/developer/.readthedocs.yaml @@ -6,6 +6,12 @@ build: os: ubuntu-22.04 tools: python: "3.11" + apt_packages: + - graphviz + +python: + install: + - requirements: doc/developer/requirements.txt # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/doc/developer/bmp.rst b/doc/developer/bmp.rst new file mode 100644 index 0000000..1c0e4b0 --- /dev/null +++ b/doc/developer/bmp.rst @@ -0,0 +1,49 @@ +.. _bmp: + +*** +BMP +*** + +RFC 7854 +======== +Missing features (non exhaustive): + - Per-Peer Header + + - Peer Type Flag + - Peer Distingsher + + - Peer Up + + - Reason codes (according to TODO comments in code) + +Peer Type Flag and Peer Distinguisher can be implemented easily using RFC 9069's base code. + +RFC 9069 +======== +Everything that isn't listed here is implemented and should be working. +Missing features (should be exhaustive): + +- Per-Peer Header + + - Timestamp + + - set to 0 + - value is now saved `struct bgp_path_info -> locrib_uptime` + - needs testing + +- Peer Up/Down + + - VRF/Table Name TLV + + - code for TLV exists + - need better RFC understanding + +- Peer Down Only + + - Reason code (bc not supported in RFC 7854 either) + +- Statistics Report + + - Stat Type = 8: (64-bit Gauge) Number of routes in Loc-RIB. + - Stat Type = 10: Number of routes in per-AFI/SAFI Loc-RIB. The value is + structured as: 2-byte AFI, 1-byte SAFI, followed by a 64-bit Gauge. diff --git a/doc/developer/building-docker.rst b/doc/developer/building-docker.rst index 9d42784..644e02b 100644 --- a/doc/developer/building-docker.rst +++ b/doc/developer/building-docker.rst @@ -14,7 +14,7 @@ source-built FRR on the following base platforms: The following platform images are used to support Travis CI and can also be used to reproduce topotest failures when the docker host is Ubuntu -(tested on 18.04 and 20.04): +(tested on 20.04 and 22.04): * Ubuntu 20.04 * Ubuntu 22.04 @@ -139,12 +139,12 @@ Build image (from project root directory):: Running Full Topotest:: - docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules \ + docker run --init -it --privileged --name frr-ubuntu20 -v /lib/modules:/lib/modules \ frr-ubuntu20:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' Extract results from the above run into `run-results` dir and analyze:: - tests/topotest/analyze.py -C frr -Ar run-results + tests/topotests/analyze.py -C frr-ubuntu20 -Ar run-results Start the container:: @@ -176,12 +176,12 @@ Build image (from project root directory):: Running Full Topotest:: - docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules \ + docker run --init -it --privileged --name frr-ubuntu22 -v /lib/modules:/lib/modules \ frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' Extract results from the above run into `run-results` dir and analyze:: - tests/topotest/analyze.py -C frr -Ar run-results + tests/topotests/analyze.py -C frr-ubuntu22 -Ar run-results Start the container:: diff --git a/doc/developer/building-frr-for-archlinux.rst b/doc/developer/building-frr-for-archlinux.rst index 406d22d..8b0df21 100644 --- a/doc/developer/building-frr-for-archlinux.rst +++ b/doc/developer/building-frr-for-archlinux.rst @@ -11,18 +11,12 @@ Installing Dependencies git autoconf automake libtool make cmake pcre readline texinfo \ pkg-config pam json-c bison flex python-pytest \ c-ares python python2-ipaddress python-sphinx \ - net-snmp perl libcap libelf libunwind + net-snmp perl libcap libelf libunwind protobuf-c .. include:: building-libunwind-note.rst .. include:: building-libyang.rst -Protobuf -^^^^^^^^ - -.. code-block:: console - - sudo pacman -S protobuf-c ZeroMQ ^^^^^^ diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index 233d089..3531162 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -124,7 +124,7 @@ Install libyang and its dependencies: sudo yum install pcre-devel doxygen cmake git clone https://github.com/CESNET/libyang.git cd libyang - git checkout 090926a89d59a3c4000719505d563aaf6ac60f2 + git checkout v2.1.128 mkdir build ; cd build cmake -DENABLE_LYD_PRIV=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr -D CMAKE_BUILD_TYPE:String="Release" .. make build-rpm @@ -161,10 +161,8 @@ an example.) ./configure \ --bindir=/usr/bin \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --libdir=/usr/lib/frr \ --libexecdir=/usr/lib/frr \ - --localstatedir=/var/run/frr \ --with-moduledir=/usr/lib/frr/modules \ --disable-pimd \ --enable-snmp=agentx \ diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index e6da830..eabf515 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -58,10 +58,8 @@ an example.) ./configure \ --bindir=/usr/bin \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --libdir=/usr/lib/frr \ --libexecdir=/usr/lib/frr \ - --localstatedir=/var/run/frr \ --with-moduledir=/usr/lib/frr/modules \ --enable-snmp=agentx \ --enable-multipath=64 \ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst index 6d18e7b..2d514ea 100644 --- a/doc/developer/building-frr-for-centos8.rst +++ b/doc/developer/building-frr-for-centos8.rst @@ -52,10 +52,8 @@ an example.) ./configure \ --bindir=/usr/bin \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --libdir=/usr/lib/frr \ --libexecdir=/usr/lib/frr \ - --localstatedir=/var/run/frr \ --with-moduledir=/usr/lib/frr/modules \ --enable-snmp=agentx \ --enable-multipath=64 \ diff --git a/doc/developer/building-frr-for-debian12.rst b/doc/developer/building-frr-for-debian12.rst index ca882ee..06bc18c 100644 --- a/doc/developer/building-frr-for-debian12.rst +++ b/doc/developer/building-frr-for-debian12.rst @@ -47,9 +47,9 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --localstatedir=/var/opt/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 7071cb6..fe4eeea 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -57,9 +57,9 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ @@ -118,9 +118,9 @@ Troubleshooting The local state directory must exist and have the correct permissions applied for the frrouting daemons to start. In the above ./configure -example the local state directory is set to /var/run/frr -(--localstatedir=/var/run/frr) Debian considers /var/run/frr to be -temporary and this is removed after a reboot. +example the local state directory is set to ``/var`` such that ``/var/run/frr`` +is used. Debian considers ``/var/run/frr`` to be temporary and this is removed +after a reboot. When using a different local state directory you need to create the new directory and change the ownership to the frr user, for example: diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 1b2f1b9..a590cf7 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -47,9 +47,9 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --localstatedir=/var/opt/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst index 707f1e7..beefb59 100644 --- a/doc/developer/building-frr-for-freebsd10.rst +++ b/doc/developer/building-frr-for-freebsd10.rst @@ -60,9 +60,9 @@ an example) export LDFLAGS="-L/usr/local/lib" export CPPFLAGS="-I/usr/local/include" ./configure \ - --sysconfdir=/usr/local/etc/frr \ + --sysconfdir=/usr/local/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --prefix=/usr/local \ --enable-multipath=64 \ --enable-user=frr \ diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst index af0b72b..7c8fb83 100644 --- a/doc/developer/building-frr-for-freebsd11.rst +++ b/doc/developer/building-frr-for-freebsd11.rst @@ -65,9 +65,9 @@ an example) setenv CPPFLAGS -I/usr/local/include ln -s /usr/local/bin/sphinx-build-3.6 /usr/local/bin/sphinx-build ./configure \ - --sysconfdir=/usr/local/etc/frr \ + --sysconfdir=/usr/local/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --prefix=/usr/local \ --enable-multipath=64 \ --enable-user=frr \ diff --git a/doc/developer/building-frr-for-freebsd13.rst b/doc/developer/building-frr-for-freebsd13.rst index 0bc8277..86506a9 100644 --- a/doc/developer/building-frr-for-freebsd13.rst +++ b/doc/developer/building-frr-for-freebsd13.rst @@ -52,9 +52,9 @@ an example) ./bootstrap.sh export MAKE=gmake LDFLAGS=-L/usr/local/lib CPPFLAGS=-I/usr/local/include ./configure \ - --sysconfdir=/usr/local/etc/frr \ + --sysconfdir=/usr/local/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --prefix=/usr/local \ --enable-multipath=64 \ --enable-user=frr \ diff --git a/doc/developer/building-frr-for-freebsd14.rst b/doc/developer/building-frr-for-freebsd14.rst new file mode 100644 index 0000000..b3fd37a --- /dev/null +++ b/doc/developer/building-frr-for-freebsd14.rst @@ -0,0 +1,122 @@ +FreeBSD 14 +========== + +FreeBSD 14 restrictions: +------------------------ + +- MPLS is not supported on ``FreeBSD``. MPLS requires a Linux Kernel + (4.5 or higher). LDP can be built, but may have limited use without + MPLS +- PIM for IPv6 is not currently supported on ``FreeBSD``. + +Install required packages +------------------------- + +Add packages: (Allow the install of the package management tool if this +is first package install and asked) + +.. code-block:: shell + + pkg install autoconf automake bison c-ares git gmake json-c libtool \ + libunwind libyang2 pkgconf protobuf-c py39-pytest py39-sphinx texinfo + +.. include:: building-libunwind-note.rst + +Get FRR, compile it and install it (from Git) +--------------------------------------------- + +**This assumes you want to build and install FRR from source and not using any +packages** + +Add frr group and user +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: shell + + pw groupadd frr -g 101 + pw groupadd frrvty -g 102 + pw adduser frr -g 101 -u 101 -G 102 -c "FRR suite" \ + -d /usr/local/etc/frr -s /usr/sbin/nologin + + +Download Source, configure and compile it +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(You may prefer different options on configure statement. These are just +an example) + +.. code-block:: shell + + git clone https://github.com/frrouting/frr.git frr + cd frr + ./bootstrap.sh + export MAKE=gmake LDFLAGS=-L/usr/local/lib CPPFLAGS=-I/usr/local/include + ./configure \ + --sysconfdir=/usr/local/etc \ + --localstatedir=/var \ + --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ + --prefix=/usr/local \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-vty-group=frrvty \ + --enable-configfile-mask=0640 \ + --enable-logfile-mask=0640 \ + --enable-fpm \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion + gmake + gmake check + sudo gmake install + +Create empty FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: shell + + sudo mkdir /usr/local/etc/frr + +For integrated config file: + +.. code-block:: shell + + sudo touch /usr/local/etc/frr/frr.conf + +For individual config files: + +.. note:: Integrated config is preferred to individual config. + +.. code-block:: shell + + sudo touch /usr/local/etc/frr/babeld.conf + sudo touch /usr/local/etc/frr/bfdd.conf + sudo touch /usr/local/etc/frr/bgpd.conf + sudo touch /usr/local/etc/frr/eigrpd.conf + sudo touch /usr/local/etc/frr/isisd.conf + sudo touch /usr/local/etc/frr/ldpd.conf + sudo touch /usr/local/etc/frr/nhrpd.conf + sudo touch /usr/local/etc/frr/ospf6d.conf + sudo touch /usr/local/etc/frr/ospfd.conf + sudo touch /usr/local/etc/frr/pbrd.conf + sudo touch /usr/local/etc/frr/pimd.conf + sudo touch /usr/local/etc/frr/ripd.conf + sudo touch /usr/local/etc/frr/ripngd.conf + sudo touch /usr/local/etc/frr/staticd.conf + sudo touch /usr/local/etc/frr/zebra.conf + sudo chown -R frr:frr /usr/local/etc/frr/ + sudo touch /usr/local/etc/frr/vtysh.conf + sudo chown frr:frrvty /usr/local/etc/frr/vtysh.conf + sudo chmod 640 /usr/local/etc/frr/*.conf + +Enable IP & IPv6 forwarding +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Add the following lines to the end of ``/etc/sysctl.conf``: + +:: + + # Routing: We need to forward packets + net.inet.ip.forwarding=1 + net.inet6.ip6.forwarding=1 + +**Reboot** or use ``sysctl`` to apply the same config to the running system. diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst index 3033287..9f9073d 100644 --- a/doc/developer/building-frr-for-freebsd9.rst +++ b/doc/developer/building-frr-for-freebsd9.rst @@ -70,9 +70,9 @@ an example) export LDFLAGS="-L/usr/local/lib" export CPPFLAGS="-I/usr/local/include" ./configure \ - --sysconfdir=/usr/local/etc/frr \ + --sysconfdir=/usr/local/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --prefix=/usr/local \ --enable-multipath=64 \ --enable-user=frr \ diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index 8958862..77c0e00 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -64,9 +64,9 @@ an example) export LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" export CPPFLAGS="-I/usr/pkg/include" ./configure \ - --sysconfdir=/usr/pkg/etc/frr \ + --sysconfdir=/usr/pkg/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index e751ba3..abb04a0 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -55,9 +55,9 @@ an example) export LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" export CPPFLAGS="-I/usr/pkg/include" ./configure \ - --sysconfdir=/usr/pkg/etc/frr \ + --sysconfdir=/usr/pkg/etc \ + --localstatedir=/var \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ - --localstatedir=/var/run/frr \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/developer/building-frr-for-openbsd6.rst b/doc/developer/building-frr-for-openbsd6.rst index 00bc2e5..6d7f346 100644 --- a/doc/developer/building-frr-for-openbsd6.rst +++ b/doc/developer/building-frr-for-openbsd6.rst @@ -71,8 +71,8 @@ an example) export LDFLAGS="-L/usr/local/lib" export CPPFLAGS="-I/usr/local/include" ./configure \ - --sysconfdir=/etc/frr \ - --localstatedir=/var/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --enable-multipath=64 \ --enable-user=_frr \ --enable-group=_frr \ diff --git a/doc/developer/building-frr-for-opensuse.rst b/doc/developer/building-frr-for-opensuse.rst index 3ff445b..6e9913d 100644 --- a/doc/developer/building-frr-for-opensuse.rst +++ b/doc/developer/building-frr-for-opensuse.rst @@ -13,11 +13,13 @@ 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 libcap-devel libyang-devel \ + python3-Sphinx perl patch libcap-devel \ libelf-devel libunwind-devel protobuf-c .. include:: building-libunwind-note.rst +.. include:: building-libyang.rst + Building & Installing FRR ------------------------- diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst index cc6c3c0..dd3f98a 100644 --- a/doc/developer/building-frr-for-ubuntu1404.rst +++ b/doc/developer/building-frr-for-ubuntu1404.rst @@ -14,16 +14,11 @@ Installing Dependencies git autoconf automake libtool make libreadline-dev texinfo \ pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev python3-sphinx install-info build-essential \ + protobuf-c-compiler libprotobuf-c-dev \ libsnmp-dev perl libcap-dev libelf-dev .. include:: building-libyang.rst -Protobuf -^^^^^^^^ - -.. code-block:: console - - sudo apt-get install protobuf-c-compiler libprotobuf-c-dev Building & Installing FRR ------------------------- diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index e5c2389..f3b6aa0 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -19,12 +19,6 @@ Installing Dependencies .. include:: building-libyang.rst -Protobuf -^^^^^^^^ - -.. code-block:: console - - sudo apt-get install protobuf-c-compiler libprotobuf-c-dev Building & Installing FRR ------------------------- diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst index fcfd94e..b4880e2 100644 --- a/doc/developer/building-frr-for-ubuntu1804.rst +++ b/doc/developer/building-frr-for-ubuntu1804.rst @@ -15,18 +15,13 @@ Installing Dependencies pkg-config libpam0g-dev libjson-c-dev bison flex \ libc-ares-dev python3-dev python3-sphinx \ install-info build-essential libsnmp-dev perl libcap-dev \ + protobuf-c-compiler libprotobuf-c-dev \ libelf-dev libunwind-dev .. include:: building-libunwind-note.rst .. include:: building-libyang.rst -Protobuf -^^^^^^^^ - -.. code-block:: console - - sudo apt-get install protobuf-c-compiler libprotobuf-c-dev ZeroMQ ^^^^^^ diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst index fdfc25d..3db97c4 100644 --- a/doc/developer/building-frr-for-ubuntu2004.rst +++ b/doc/developer/building-frr-for-ubuntu2004.rst @@ -15,34 +15,33 @@ Installing Dependencies pkg-config libpam0g-dev libjson-c-dev bison flex \ libc-ares-dev python3-dev python3-sphinx \ install-info build-essential libsnmp-dev perl \ - libcap-dev python2 libelf-dev libunwind-dev + protobuf-c-compiler libprotobuf-c-dev \ + libcap-dev libelf-dev libunwind-dev .. include:: building-libunwind-note.rst -Note that Ubuntu 20 no longer installs python 2.x, so it must be -installed explicitly. Ensure that your system has a symlink named -``/usr/bin/python`` pointing at ``/usr/bin/python3``. +.. include:: building-libyang.rst -In addition, ``pip`` for python2 must be installed if you wish to run -the FRR topotests. That version of ``pip`` is not available from the -ubuntu apt repositories; in order to install it: +GRPC +^^^^ +If GRPC is enabled using ``--enable-grpc`` the following packages should be +installed. -.. code-block:: shell +.. code-block:: console - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py - sudo python2 ./get-pip.py + sudo apt-get install libgrpc++-dev protobuf-compiler-grpc - # And verify the installation - pip2 --version -.. include:: building-libyang.rst +Config Rollbacks +^^^^^^^^^^^^^^^^ -Protobuf -^^^^^^^^ +If config rollbacks are enabled using ``--enable-config-rollbacks`` +the sqlite3 developer package also should be installed. .. code-block:: console - sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + sudo apt install libsqlite3-dev + ZeroMQ ^^^^^^ diff --git a/doc/developer/building-frr-for-ubuntu2204.rst b/doc/developer/building-frr-for-ubuntu2204.rst index 97bdf88..c898c3c 100644 --- a/doc/developer/building-frr-for-ubuntu2204.rst +++ b/doc/developer/building-frr-for-ubuntu2204.rst @@ -15,40 +15,21 @@ Installing Dependencies pkg-config libpam0g-dev libjson-c-dev bison flex \ libc-ares-dev python3-dev python3-sphinx \ install-info build-essential libsnmp-dev perl \ - libcap-dev python2 libelf-dev libunwind-dev \ - libyang2 libyang2-dev + libcap-dev libelf-dev libunwind-dev \ + protobuf-c-compiler libprotobuf-c-dev .. include:: building-libunwind-note.rst -Note that Ubuntu >= 20 no longer installs python 2.x, so it must be -installed explicitly. Ensure that your system has a symlink named -``/usr/bin/python`` pointing at ``/usr/bin/python3``. +.. include:: building-libyang.rst -.. code-block:: shell - - sudo ln -s /usr/bin/python3 /usr/bin/python - python --version - -In addition, ``pip`` for python2 must be installed if you wish to run -the FRR topotests. That version of ``pip`` is not available from the -ubuntu apt repositories; in order to install it: - -.. code-block:: shell - - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py - sudo python2 ./get-pip.py - - # And verify the installation - pip2 --version - - -Protobuf -^^^^^^^^ -This is optional +GRPC +^^^^ +If GRPC is enabled using ``--enable-grpc`` the following packages should be +installed. .. code-block:: console - sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + sudo apt-get install libgrpc++-dev protobuf-compiler-grpc Config Rollbacks diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst index c36cd34..a46c793 100644 --- a/doc/developer/building-libyang.rst +++ b/doc/developer/building-libyang.rst @@ -10,11 +10,11 @@ The FRR project builds some binary ``libyang`` packages. RPM packages are at our `RPM repository `_. DEB packages are available as CI artifacts `here -`_. +`_. .. warning:: - ``libyang`` version 2.0.0 or newer is required to build FRR. + ``libyang`` version 2.1.128 or newer is required to build FRR. .. note:: @@ -39,7 +39,7 @@ DEB packages are available as CI artifacts `here git clone https://github.com/CESNET/libyang.git cd libyang - git checkout v2.0.0 + git checkout v2.1.128 mkdir build; cd build cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr \ -D CMAKE_BUILD_TYPE:String="Release" .. diff --git a/doc/developer/building.rst b/doc/developer/building.rst index 8ca0c13..6762604 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -21,6 +21,7 @@ Building FRR building-frr-for-freebsd10 building-frr-for-freebsd11 building-frr-for-freebsd13 + building-frr-for-freebsd14 building-frr-for-netbsd6 building-frr-for-netbsd7 building-frr-for-openbsd6 diff --git a/doc/developer/cross-compiling.rst b/doc/developer/cross-compiling.rst index 3bf78f7..af99262 100644 --- a/doc/developer/cross-compiling.rst +++ b/doc/developer/cross-compiling.rst @@ -239,9 +239,9 @@ the last thing to actually build is FRR itself: --host=${HOST_ARCH} \ --with-sysroot=/usr/${HOST_ARCH} \ --with-clippy=./build-clippy/lib/clippy \ - --sysconfdir=/etc/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir="\${prefix}/lib/frr" \ - --localstatedir=/var/run/frr \ --prefix=/usr \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst index 9378637..9dbc9b4 100644 --- a/doc/developer/frr-release-procedure.rst +++ b/doc/developer/frr-release-procedure.rst @@ -13,6 +13,13 @@ Stage 1 - Preparation Note: use ``tools/release_notes.py`` to help draft release notes changelog + .. code-block:: console + + ./tools/release_notes.py -b dev/9.1 -t frr-9.0.1 + + dev/9.1 is the branch to be renamed to stable/9.1, and frr-9.0.1 in this + example is the latest tag from which to generate the logs. + #. Checkout the existing ``dev/`` branch. .. code-block:: console diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst index b98d237..49fd6c8 100644 --- a/doc/developer/include-compile.rst +++ b/doc/developer/include-compile.rst @@ -14,8 +14,8 @@ obtained by running ``./configure -h``. The options shown below are examples. --sbindir=\${prefix}/lib/frr \ --libdir=\${prefix}/lib/frr \ --libexecdir=\${prefix}/lib/frr \ - --localstatedir=/var/run/frr \ - --sysconfdir=/etc/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --with-moduledir=\${prefix}/lib/frr/modules \ --enable-configfile-mask=0640 \ --enable-logfile-mask=0640 \ diff --git a/doc/developer/mgmtd-dev.rst b/doc/developer/mgmtd-dev.rst index 9839aa8..2404ffe 100644 --- a/doc/developer/mgmtd-dev.rst +++ b/doc/developer/mgmtd-dev.rst @@ -12,7 +12,7 @@ MGMTD Development ================= Overview -^^^^^^^^ +-------- ``mgmtd`` (Management Daemon) is a new centralized management daemon for FRR. @@ -33,18 +33,64 @@ each daemon. ``mgmtd`` currently provides the CLI interface for each daemon that has been converted to it, but in the future RESTCONF and NETCONF servers can easily be added as *front-ends* to mgmtd to support those protocols as well. +Conversion Status +^^^^^^^^^^^^^^^^^ + +Fully Converted To MGMTD +"""""""""""""""""""""""" + +- lib/distribute +- lib/filter +- lib/if_rmap +- lib/routemap +- lib/affinitymap +- lib/if +- lib/vrf +- ripd +- ripngd +- staticd +- zebra (* - partial) + +Converted To Northbound +""""""""""""""""""""""" +- bfdd +- pathd +- pbrd +- pimd + +Converted To Northbound With Issues +""""""""""""""""""""""""""""""""""" +- eigrp +- isisd + +Unconverted +""""""""""" +- babel +- bgpd +- ldpd +- lib/event +- lib/keychain +- lib/log_vty +- lib/nexthop_group +- lib/zlog_5424_cli +- nhrpd +- ospfd +- ospf6d +- pceplib +- qdb +- sharpd +- vrrpd Converting A Daemon to MGMTD -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------- -A daemon must first be transitioned to the new *northbound* interface if that -has not already been done (see `this northbound conversion documentation -`_ -for how to do this). Once this is done a few simple steps are all that is -required move the daemon over to ``mgmtd`` control. +A daemon must first be transitioned to the new :ref:`northbound` interface if that +has not already been done (see :ref:`nb-retrofit` for how to do this). Once this +is done a few simple steps are all that is required move the daemon over to +``mgmtd`` control. Overview of Changes -------------------- +^^^^^^^^^^^^^^^^^^^ Adding support for a *northbound* converted daemon involves very little work. It requires enabling *frontend* (CLI and YANG) and *backend* (YANG) support. @@ -52,64 +98,126 @@ requires enabling *frontend* (CLI and YANG) and *backend* (YANG) support. Front-End Interface: -1. Add YANG module file to ``mgmtd/subdir.am`` (e.g., ``yang/frr-staticd.c``) -2. Add YANG module description into array defined in ``mgmtd/mgmt_main.c`` -3. Add CLI handler file[s] to ``mgmtd/subdir.am`` (e.g., ``staticd/static_vty.c``) -4. [if needed] Exclude (#ifndef) non-configuration CLI handlers from CLI source - file (e.g., inside ``staticd/static_vty.c``) +#. Add YANG module file to ``mgmtd/subdir.am`` (e.g., ``yang/frr-staticd.yang.c``). + +#. Add CLI handler file[s] to ``mgmtd/subdir.am``. The `subdir.am` variable to + use is indicated in the next 2 steps. + + #. [if needed] Exclude (:code:`#ifndef`) non-configuration CLI handlers from + CLI source file (e.g., inside :file:`staticd/static_vty.c`) and add the + file to :code:`nodist_mgmtd_libmgmt_be_nb_la_SOURCES` in + :file:`mgmtd/subdir.am`. + + #. [otherwise] Remove CLI handler file from _SOURCES variable in the daemon + :file:`subdir.am` file (e.g in :file:`staticd/subdir.am`) and add to + :code:`mgmtd_libmgmtd_a_SOURCES` in :file:`mgmtd/subdir.am`. + +#. In order to have mgmtd try and load existing per-daemon config files, add + the daemon to the :code:`mgmt_daemons` array in :file:`lib/vty.c`. With the + official release of the mgmtd code FRR is no longer supporting per daemon log + files but it will take a while before all of the topotest is converted. + +#. In the daemon's :code:`struct frr_daemon_info` (i.e., inside it's + :code:`FRR_DAEMON_INFO()`) set the `.flags` bit `FRR_NO_SPLIT_CONFIG`. This + will keep the daemon from trying to read it's per-daemon config file as mgmtd + will now be doing this. + +#. Add the daemon's YANG module description[s] into the array + :code:`mgmt_yang_modules` defined in :file:`mgmtd/mgmt_main.c` (see + :ref:`mgmtd-config-write`). Make sure that all YANG modules that the daemon + uses are present in the mgmtd list. To find this list look in the daemon's + equivalent yang module array variable. + +#. Initialize the CLI handlers inside :code:`mgmt_vty_init` in :file:`mgmtd/mgmt_vty.c`. + +#. Direct ``vtysh`` to send CLI commands to ``mgmtd`` by modifying + ``vtysh/vtysh.h``. At the top of this file each daemon has a bit + ``#define``'d (e.g., ``#define VTYSH_STATICD 0x08000``) below this there are + groupings, replace all the uses of the daemons bit with ``VTYSH_MGMTD`` + instead so that the CLI commands get properly routed to ``mgmtd`` rather than + the daemon now. + + #. Remove initialization (and installation) of library CLI routines. These will + correspond with the VTYSH removals from the last step i.e.,: + + - change access_list_init() to access_list_init_new(false) and remove from + VTYSH_ACL_CONFIG (leave in VTYSH_ACL_SHOW). + - remove if_cmd_init_default() => remove from VTYSH_INTERFACE_SUBSET + - remove if_cmd_init() => remove from VTYSH_INTERFACE_SUBSET + - change route_map_init() to route_map_init_new(false) and remove from + VTYSH_ROUTE_MAP_CONFIG (leave in VTYSH_ROUTE_MAP_SHOW). + - remove vrf_cmd_init(NULL) => remove from VTYSH_INTERFACE_SUBSET + ... Back-End Interface: -5. Add XPATHs mappings to a couple arrays to direct ``mgmtd`` at your daemon in - ``mgmtd/mgmt_be_adapter.c`` - +#. In the daemon's main file initialize the BE client library. You add a global + `struct mgmt_be_client *mgmt_be_client` near the daemons `event_loop *master` + variable. Then where the daemon used to initialize it's CLI/VTY code replace + that with the client initialization by calling `mgmt_be_client_create`. + Likewise in the daemon's sigint cleanup code, operational walks should be + canceled with a call to `nb_oper_cancel_all_walks`, and then the BE client + should be destroyed with a call to `mgmt_be_client_destroy` and to be safe + NULL out the global `mgmt_be_client` variable. + +#. In ``mgmtd/mgmt_be_adapter.c`` add xpath prefix mappings to a one or both + mapping arrays (``be_client_config_xpaths`` and ``be_client_oper_xpaths``) to + direct ``mgmtd`` to send config and oper-state requests to your daemon. NOTE: + make sure to include library supported xpaths prefixes as well (e.g., + "/frr-interface:lib"). A good way to figure these paths out are to look in + each of the YANG modules that the daemon uses and include each of their paths + in the array. Add YANG and CLI into MGMTD ---------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As an example here is the addition made to ``mgmtd/subdir.am`` for adding ``staticd`` support. .. code-block:: make - if STATICD - nodist_mgmtd_mgmtd_SOURCES += \ + if STATICD + nodist_mgmtd_mgmtd_SOURCES += \ yang/frr-staticd.yang.c \ yang/frr-bfdd.yang.c \ # end - nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c - endif + nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c + endif An here is the addition to the modules array in ``mgmtd/mgmt_main.c``: .. code-block:: c - static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + #ifdef HAVE_STATICD + extern const struct frr_yang_module_info frr_staticd_info; + #endif + + static const struct frr_yang_module_info *const mgmt_yang_modules[] = { &frr_filter_info, ... - #ifdef HAVE_STATICD - &(struct frr_yang_module_info){.name = "frr-staticd", - .ignore_cbs = true}, - #endif - } + #ifdef HAVE_STATICD + &frr_staticd_info, + #endif + } -CLI Handlers ------------- +CLI Config and Show Handlers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The daemon's CLI handlers for configuration (which having been converted to -*northbound* now simply generate YANG changes) will be linked directly into +The daemon's CLI handlers for configuration (which having been converted to the +:ref:`northbound` now simply generate YANG changes) will be linked directly into ``mgmtd``. If the operational and debug CLI commands are kept in files separate from the daemon's configuration CLI commands then no extra work is required. Otherwise some CPP #ifndef's will be required. -Currently ``mgmtd`` supports configuration CLI but not operational -state so if both types of CLI handlers are present in a single file (e.g. a -``xxx_vty.c`` or ``xxx_cli.c`` file ) then #ifndef will be used to exclude these -non-configuration CLI handlers from ``mgmtd``. The same goes for *debug* CLI -handlers. For example: +``mgmtd`` supports both config and operational state. However, many +daemons have not had their operational state CLI commands converted over to the +new YANG based methods. If that is the case and if both types of CLI handlers +are present in a single file (e.g. a ``xxx_vty.c`` or ``xxx_cli.c`` file) then +:code:`#ifndef` will need to be used to exclude the non-config CLI handlers from +``mgmtd``. The same goes for unconverted *debug* CLI handlers. For example: .. code-block:: c @@ -121,7 +229,7 @@ handlers. For example: } #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY - DEFPY(daemon_show_oepr, daemon_show_oepr_cmd, + DEFPY(daemon_show_oper, daemon_show_oper_cmd, "show daemon oper [all]" ... { @@ -140,83 +248,136 @@ handlers. For example: } +.. _mgmtd-config-write: + +CLI Config Write Handlers (:code:`cli_show`) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To support writing out the CLI configuration file the northbound API defines a +2 callbacks (:code:`cli_show` and :code:`cli_show_end`). Pointers to these +callbacks used to live side-by-side in a daemons :code:`struct frr_yang_module_info`, +with the daemons back-end configuration and operational state callbacks +(normally in a file named `_nb.c`). + +However, these 2 functionalities need to be split up now. The *frontend* config +writing callbacks (:code:`cli_show`) should now be linked into ``mgmtd`` while +the *backend* config and oper-state callbacks (e.g., :code:`create`, +:code:`modify`, etc) should continue to be linked into the daemon. + +So you will need to define 2 :code:`struct frr_yang_module_info` arrays. + +#. The existing array remains in the same place in the daemon, but with all the + :code:`cli_show` handlers removed. + +#. The removed :code:`cli_show` handlers should be added to a new + :code:`struct frr_yang_module_info` array. This second array should be + included in the same file that includes that actual function pointed to by + the the :code:`cli_show` callbacks (i.e., the file is compiled into + ``mgmtd``). + + This new :code:`struct frr_yang_module_info` array is the one to be included + in mgmtd in `mgmt_yang_modules` inside ``mgmtd/mgmt_main.c``. + +Back-End Client Connection +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order for your daemon to communicate with mgmtd you need to initialize the +backend client library. You normally do this where you used to initialize your +CLI/VTY code. + +.. code-block:: c + + ... + struct event_loop *master; -Add Back-End XPATH mappings ---------------------------- + static struct mgmt_be_client *mgmt_be_client; + ... + + int main(int argc, char **argv) + { + ... + rip_init(); + rip_if_init(); + mgmt_be_client = mgmt_be_client_create("ripd", NULL, 0, master); + +Likewise the client should be cleaned up in the daemon cleanup routine. + +.. code-block:: c + + /* SIGINT handler. */ + static void sigint(void) + { + zlog_notice("Terminating on signal"); + ... + nb_oper_cancel_all_walks(); + mgmt_be_client_destroy(mgmt_be_client); + mgmt_be_client = NULL; + + +Back-End XPATH mappings +^^^^^^^^^^^^^^^^^^^^^^^ In order for ``mgmtd`` to direct configuration to your daemon you need to add some XPATH mappings to ``mgmtd/mgmt_be_adapter.c``. These XPATHs determine which configuration changes get sent over the *back-end* interface to your daemon. +There are 2 arrays to update the first for config support and the second for +operational state. -Below are the strings added for staticd support: +Below are the strings added for staticd config support: .. code-block:: c - static const struct mgmt_be_xpath_map_init mgmt_xpath_map_init[] = { - { - .xpath_regexp = "/frr-vrf:lib/*", - .subscr_info = - { - #if HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = - MGMT_SUBSCR_VALIDATE_CFG | - MGMT_SUBSCR_NOTIFY_CFG, - #endif - }, - }, - ... - { - .xpath_regexp = - "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", - .subscr_info = - { - #if HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = - MGMT_SUBSCR_VALIDATE_CFG | - MGMT_SUBSCR_NOTIFY_CFG, - #endif - }, - }, - }; - - #if HAVE_STATICD - static struct mgmt_be_client_xpath staticd_xpaths[] = { - { - .xpath = "/frr-vrf:lib/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - ... - { - .xpath = - "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - }; - #endif - - static struct mgmt_be_client_xpath_map - mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { - #ifdef HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, - array_size(staticd_xpaths)}, - #endif - }; + #if HAVE_STATICD + static const char *const staticd_xpaths[] = { + "/frr-vrf:lib", + "/frr-interface:lib", + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + NULL, + }; + #endif + + static const char *const *be_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + #ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = staticd_xpaths, + #endif + }; +Below are the strings added for zebra operational state support (note zebra is +not conditionalized b/c it should always be present): + +.. code-block:: c + + static const char *const zebra_oper_xpaths[] = { + "/frr-interface:lib/interface", + "/frr-vrf:lib/vrf/frr-zebra:zebra", + "/frr-zebra:zebra", + NULL, + }; + + static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths, + }; MGMTD Internals -^^^^^^^^^^^^^^^ +--------------- This section will describe the internal functioning of ``mgmtd``, for now a couple diagrams are included to aide in source code perusal. -The client side of a CLI change +The client side of a CLI configuration change .. figure:: ../figures/cli-change-client.svg :align: center -The server (mgmtd) side of a CLI change +The server (mgmtd) side of a CLI configuration change .. figure:: ../figures/cli-change-mgmtd.svg :align: center + + +The client and server sides of oper-state query + +.. figure:: ../figures/cli-oper-state.svg + :align: center diff --git a/doc/developer/northbound/advanced-topics.rst b/doc/developer/northbound/advanced-topics.rst index bee29a9..edfc10b 100644 --- a/doc/developer/northbound/advanced-topics.rst +++ b/doc/developer/northbound/advanced-topics.rst @@ -1,3 +1,11 @@ +Advanced Topics +=============== + +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 1 + Auto-generated CLI commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -34,13 +42,16 @@ CLI on a separate program The flexible design of the northbound architecture opens the door to move the CLI to a separate program in the long-term future. Some -advantages of doing so would be: \* Treat the CLI as just another -northbound client, instead of having CLI commands embedded in the -binaries of all FRR daemons. \* Improved robustness: bugs in CLI -commands (e.g. null-pointer dereferences) or in the CLI code itself -wouldn’t affect the FRR daemons. \* Foster innovation by allowing other -CLI programs to be implemented, possibly using higher level programming -languages. +advantages of doing so would be: + +* Treat the CLI as just another northbound client, instead of having CLI + commands embedded in the binaries of all FRR daemons. + +* Improved robustness: bugs in CLI commands (e.g. null-pointer dereferences) or + in the CLI code itself wouldn’t affect the FRR daemons. + +* Foster innovation by allowing other CLI programs to be implemented, possibly + using higher level programming languages. The problem, however, is that the northbound retrofitting process will convert only the CLI configuration commands and EXEC commands in a first @@ -232,40 +243,42 @@ vtysh support As explained in the [[Transactional CLI]] page, all commands introduced by the transactional CLI are not yet available in *vtysh*. This needs to be addressed in the short term future. Some challenges for doing that -work include: \* How to display configurations (running, candidates and -rollbacks) in a more clever way? The implementation of the -``show running-config`` command in *vtysh* is not something that should -be followed as an example. A better idea would be to fetch the desired -configuration from all daemons (encoded in JSON for example), merge them -all into a single ``lyd_node`` variable and then display the combined -configurations from this variable (the configuration merges would -transparently take care of combining the shared configuration objects). -In order to be able to manipulate the JSON configurations, *vtysh* will -need to load the YANG modules from all daemons at startup (this might -have a minimal impact on startup time). The only issue with this -approach is that the ``cli_show()`` callbacks from all daemons are -embedded in their binaries and thus not accessible externally. It might -be necessary to compile these callbacks on a separate shared library so -that they are accessible to *vtysh* too. Other than that, displaying the -combined configurations in the JSON/XML formats should be -straightforward. \* With the current design, transaction IDs are -per-daemon and not global across all FRR daemons. This means that the -same transaction ID can represent different transactions on different -daemons. Given this observation, how to implement the -``rollback configuration`` command in *vtysh*? The easy solution would -be to add a ``daemon WORD`` argument to specify the context of the -rollback, but per-daemon rollbacks would certainly be confusing and -convoluted to end users. A better idea would be to attack the root of -the problem: change configuration transactions to be global instead of -being per-daemon. This involves a bigger change in the northbound -architecture, and would have implications on how transactions are stored -in the SQL database (daemon-specific and shared configuration objects -would need to have their own tables or columns). \* Loading -configuration files in the JSON or XML formats will be tricky, as -*vtysh* will need to know which sections of the configuration should be -sent to which daemons. *vtysh* will either need to fetch the YANG -modules implemented by all daemons at runtime or obtain this information -at compile-time somehow. +work include: + +* How to display configurations (running, candidates and rollbacks) in a more + clever way? The implementation of the ``show running-config`` command in + *vtysh* is not something that should be followed as an example. A better idea + would be to fetch the desired configuration from all daemons (encoded in JSON + for example), merge them all into a single ``lyd_node`` variable and then + display the combined configurations from this variable (the configuration + merges would transparently take care of combining the shared configuration + objects). In order to be able to manipulate the JSON configurations, *vtysh* + will need to load the YANG modules from all daemons at startup (this might + have a minimal impact on startup time). The only issue with this approach is + that the ``cli_show()`` callbacks from all daemons are embedded in their + binaries and thus not accessible externally. It might be necessary to compile + these callbacks on a separate shared library so that they are accessible to + *vtysh* too. Other than that, displaying the combined configurations in the + JSON/XML formats should be straightforward. + +* With the current design, transaction IDs are per-daemon and not global across + all FRR daemons. This means that the same transaction ID can represent + different transactions on different daemons. Given this observation, how to + implement the ``rollback configuration`` command in *vtysh*? The easy solution + would be to add a ``daemon WORD`` argument to specify the context of the + rollback, but per-daemon rollbacks would certainly be confusing and convoluted + to end users. A better idea would be to attack the root of the problem: change + configuration transactions to be global instead of being per-daemon. This + involves a bigger change in the northbound architecture, and would have + implications on how transactions are stored in the SQL database + (daemon-specific and shared configuration objects would need to have their own + tables or columns). + +* Loading configuration files in the JSON or XML formats will be tricky, as + *vtysh* will need to know which sections of the configuration should be sent + to which daemons. *vtysh* will either need to fetch the YANG modules + implemented by all daemons at runtime or obtain this information at + compile-time somehow. Detecting type mismatches at compile-time ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/developer/northbound/architecture.rst b/doc/developer/northbound/architecture.rst index e571971..5fd89c3 100644 --- a/doc/developer/northbound/architecture.rst +++ b/doc/developer/northbound/architecture.rst @@ -1,3 +1,6 @@ +Architecture +============ + Introduction ------------ @@ -42,30 +45,34 @@ and `CoAP `__. In addition to being management-protocol independent, some other -advantages of using YANG in FRR are listed below: \* Have a formal -contract between FRR and application developers (management clients). A -management client that has access to the FRR YANG models knows about all -existing configuration options available for use. This information can -be used to auto-generate user-friendly interfaces like Web-UIs, custom -CLIs and even code bindings for several different programming languages. -Using `PyangBind `__, for -example, it’s possible to generate Python class hierarchies from YANG -models and use these classes to instantiate objects that mirror the -structure of the YANG modules and can be serialized/deserialized using -different encoding formats. \* Support different encoding formats for -instance data. Currently only JSON and XML are supported, but -`GPB `__ and -`CBOR `__ are other viable options in the long term. -Additional encoding formats can be implemented in the *libyang* library -for optimal performance, or externally by translating data to/from one -of the supported formats (with a performance penalty). \* Have a formal -mechanism to introduce backward-incompatible changes based on `semantic -versioning `__ (not part of the -YANG standard, which allows backward-compatible module updates only). \* -Provide seamless support to the industry-standard NETCONF/RESTCONF -protocols as alternative management APIs. If FRR configuration/state -data is modeled using YANG, supporting YANG-based protocols like NETCONF -and RESTCONF is much easier. +advantages of using YANG in FRR are listed below: + +* Have a formal contract between FRR and application developers (management + clients). A management client that has access to the FRR YANG models knows + about all existing configuration options available for use. This information + can be used to auto-generate user-friendly interfaces like Web-UIs, custom + CLIs and even code bindings for several different programming languages. Using + `PyangBind `__, for example, it’s + possible to generate Python class hierarchies from YANG models and use these + classes to instantiate objects that mirror the structure of the YANG modules + and can be serialized/deserialized using different encoding formats. + +* Support different encoding formats for instance data. Currently only JSON and + XML are supported, but `GPB + `__ and `CBOR + `__ are other viable options in the long term. Additional + encoding formats can be implemented in the *libyang* library for optimal + performance, or externally by translating data to/from one of the supported + formats (with a performance penalty). + +* Have a formal mechanism to introduce backward-incompatible changes based on + `semantic versioning `__ (not part of + the YANG standard, which allows backward-compatible module updates only). + +* Provide seamless support to the industry-standard NETCONF/RESTCONF protocols + as alternative management APIs. If FRR configuration/state data is modeled + using YANG, supporting YANG-based protocols like NETCONF and RESTCONF is much + easier. As important as shifting to a model-driven management paradigm, the new northbound architecture also introduces the concept of configuration diff --git a/doc/developer/northbound/demos.rst b/doc/developer/northbound/demos.rst index 876bd25..8a0f6ad 100644 --- a/doc/developer/northbound/demos.rst +++ b/doc/developer/northbound/demos.rst @@ -1,3 +1,6 @@ +Demos +===== + Transactional CLI ----------------- diff --git a/doc/developer/northbound/links.rst b/doc/developer/northbound/links.rst index e80374c..6cec176 100644 --- a/doc/developer/northbound/links.rst +++ b/doc/developer/northbound/links.rst @@ -1,3 +1,6 @@ +Links +===== + RFCs ~~~~ diff --git a/doc/developer/northbound/northbound.rst b/doc/developer/northbound/northbound.rst index 7dddf06..c5f4e2f 100644 --- a/doc/developer/northbound/northbound.rst +++ b/doc/developer/northbound/northbound.rst @@ -11,11 +11,11 @@ Northbound API transactional-cli retrofitting-configuration-commands operational-data-rpcs-and-notifications - plugins-sysrepo advanced-topics yang-tools yang-module-translator demos links + plugins-sysrepo ppr-basic-test-topology ppr-mpls-basic-test-topology diff --git a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst index 554bc17..5cb70ca 100644 --- a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst +++ b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst @@ -1,3 +1,11 @@ +Operational Data, RPCs and Notifications +======================================== + +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 1 + Operational data ~~~~~~~~~~~~~~~~ @@ -330,10 +338,12 @@ CLI can take too long, potentially long enough to the point of triggering some protocol timeouts and bringing sessions down. To avoid this kind of problem, northbound clients are encouraged to do -one of the following: \* Create a separate pthread for handling requests -to fetch operational data. \* Iterate over YANG lists and leaf-lists -asynchronously, returning a maximum number of elements per time instead -of returning all elements in one shot. +one of the following: + +* Create a separate pthread for handling requests to fetch operational data. + +* Iterate over YANG lists and leaf-lists asynchronously, returning a maximum + number of elements per time instead of returning all elements in one shot. In order to handle both cases correctly, the ``get_next`` callbacks need to use locks to prevent the YANG lists from being modified while they diff --git a/doc/developer/northbound/plugins-sysrepo.rst b/doc/developer/northbound/plugins-sysrepo.rst index 186c3a0..0cfdb82 100644 --- a/doc/developer/northbound/plugins-sysrepo.rst +++ b/doc/developer/northbound/plugins-sysrepo.rst @@ -1,137 +1,193 @@ +Plugins Sysrepo +=============== + Installation ------------ Required dependencies ^^^^^^^^^^^^^^^^^^^^^ +Install FRR build required dependencies, check `Building FRR +`_ document for specific platform required packages. +Below are debian systems required packages: -:: +.. code-block:: console - # apt-get install git cmake build-essential bison flex libpcre3-dev libev-dev \ - libavl-dev libprotobuf-c-dev protobuf-c-compiler libcmocka0 \ - libcmocka-dev doxygen libssl-dev libssl-dev libssh-dev + sudo apt-get install git autoconf automake libtool make \ + libprotobuf-c-dev protobuf-c-compiler build-essential \ + python3-dev python3-pytest python3-sphinx libjson-c-dev \ + libelf-dev libreadline-dev cmake libcap-dev bison flex \ + pkg-config texinfo gdb libgrpc-dev python3-grpc-tools libpcre2-dev libyang ^^^^^^^ -:: +.. note:: - # apt-get install libyang0.16 libyang-dev + FRR requires version 2.1.128 or newer, in this document we will + be compiling and installing libyang version 2.1.148. -Sysrepo -^^^^^^^ +.. code-block:: console -:: + git clone https://github.com/CESNET/libyang.git + cd libyang + git checkout v2.1.148 + mkdir build; cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DCMAKE_BUILD_TYPE:String="Release" .. + make + sudo make install - $ git clone https://github.com/sysrepo/sysrepo.git - $ cd sysrepo/ - $ mkdir build; cd build - $ cmake -DCMAKE_BUILD_TYPE=Release -DGEN_LANGUAGE_BINDINGS=OFF .. && make - # make install +Sysrepo +^^^^^^^ -libnetconf2 -^^^^^^^^^^^ +.. note:: -:: + The following code block assumes you have installed libyang v2.1.148, if you have + libyang v2.1.128 change sysrepo version to 2.2.105. - $ git clone https://github.com/CESNET/libnetconf2.git - $ cd libnetconf2/ - $ mkdir build; cd build - $ cmake .. && make - # make install +.. code-block:: console -netopeer2 -^^^^^^^^^ + git clone https://github.com/sysrepo/sysrepo.git + cd sysrepo/ + git checkout v2.2.150 + mkdir build; cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DCMAKE_BUILD_TYPE:String="Release" .. + make + sudo make install -:: +Verify that sysrepo is installed correctly: - $ git clone https://github.com/CESNET/Netopeer2.git - $ cd Netopeer2 - $ cd server - $ mkdir build; cd build - $ cmake .. && make - # make install +.. code-block:: console -**Note:** If ``make install`` fails as it can’t find -``libsysrepo.so.0.7``, then run ``ldconfig`` and try again as it might -not have updated the lib search path + sudo sysrepoctl -l FRR ^^^ -Build and install FRR using the ``--enable-sysrepo`` configure-time -option. +Follow the steps of `Building FRR +`_ + + +Make sure to use ``--enable-sysrepo`` configure-time option while building FRR. + +Below is an example of frr configure-time options, your options +might vary, however in order to allow sysrepo plugin you have +to keep ``--enable-sysrepo`` option: + +.. code-block:: console + + ./bootstrap.sh + ./configure \ + --localstatedir=/var/opt/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-vty-group=frrvty \ + --enable-configfile-mask=0640 \ + --enable-logfile-mask=0640 \ + --enable-fpm \ + --enable-sysrepo \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion + make + make check + sudo make install + Initialization -------------- -Install the FRR YANG modules in the Sysrepo datastore: +Install FRR YANG modules in Sysrepo datastore: -:: +.. code-block:: console - # sysrepoctl --install /usr/local/share/yang/ietf-interfaces@2018-01-09.yang - # sysrepoctl --install /usr/local/share/yang/frr-vrf.yang - # sysrepoctl --install /usr/local/share/yang/frr-interface.yang - # sysrepoctl --install /usr/local/share/yang/frr-route-types.yang - # sysrepoctl --install /usr/local/share/yang/frr-filter.yang - # sysrepoctl --install /usr/local/share/yang/frr-route-map.yang - # sysrepoctl --install /usr/local/share/yang/frr-isisd.yang - # sysrepoctl --install /usr/local/share/yang/frr-ripd.yang - # sysrepoctl --install /usr/local/share/yang/frr-ripngd.yang - # sysrepoctl -c frr-vrf --owner frr --group frr - # sysrepoctl -c frr-interface --owner frr --group frr - # sysrepoctl -c frr-route-types --owner frr --group frr - # sysrepoctl -c frr-filter --owner frr --group frr - # sysrepoctl -c frr-route-map --owner frr --group frr - # sysrepoctl -c frr-isisd --owner frr --group frr - # sysrepoctl -c frr-ripd --owner frr --group frr - # sysrepoctl -c frr-ripngd --owner frr --group frr + cd frr/yang/ + sudo sysrepoctl -i ./ietf/ietf-interfaces.yang -o frr -g frr + sudo sysrepoctl -i frr-vrf.yang -o frr -g frr + sudo sysrepoctl -i frr-interface.yang -o frr -g frr + sudo sysrepoctl -i frr-route-types.yang -o frr -g frr + sudo sysrepoctl -i frr-filter.yang -o frr -g frr + sudo sysrepoctl -i frr-route-map.yang -o frr -g frr + sudo sysrepoctl -i frr-isisd.yang -o frr -g frr + sudo sysrepoctl -i frr-bfdd.yang -o frr -g frr + sudo sysrepoctl -i ./ietf/ietf-routing-types.yang -o frr -g frr + sudo sysrepoctl -i frr-nexthop.yang -o frr -g frr + sudo sysrepoctl -i frr-if-rmap.yang -o frr -g frr + sudo sysrepoctl -i frr-ripd.yang -o frr -g frr + sudo sysrepoctl -i frr-ripngd.yang -o frr -g frr + sudo sysrepoctl -i frr-affinity-map.yang -o frr -g frr + sudo sysrepoctl -i ./ietf/frr-deviations-ietf-interfaces.yang -o frr -g frr -Start netopeer2-server: -:: +Start FRR daemons with sysrepo plugin: - # netopeer2-server -d & +.. code-block:: console -Start the FRR daemons with the sysrepo module: + sudo /usr/lib/frr/isisd -M sysrepo --log stdout -:: - - # isisd -M sysrepo --log=stdout +Any daemon running with ``-M sysrepo`` will subscribe to its frr yang moduels +on sysrepo and you be able to configure it by editing module configuration on sysrepo. Managing the configuration -------------------------- -The following NETCONF scripts can be used to show and edit the FRR -configuration: -https://github.com/rzalamena/ietf-hackathon-brazil-201907/tree/master/netconf-scripts +Testing +^^^^^^^ -Example: +To test FRR intergartion with sysrepo, ``sysrepocfg`` tool can be used +to edit frr configuration on sysrepo -:: +Example: - # ./netconf-edit.py 127.0.0.1 - # ./netconf-get-config.py 127.0.0.1 - testnetlevel-1 +Edit sysrepo running datastore configuration for the desiged frr module: -.. +.. code-block:: console - NOTE: the ncclient library needs to be installed first: - ``apt install -y python3-ncclient`` + sudo sysrepocfg -E nano -d running -m frr-isisd -f json -The *sysrepocfg* tool can also be used to show/edit the FRR -configuration. Example: +Paste the following json configuration: -:: +.. code-block:: console - # sysrepocfg --format=json --import=frr-isisd.json --datastore=running frr-isisd - # sysrepocfg --format=json --export --datastore=running frr-isisd { "frr-isisd:isis": { "instance": [ { "area-tag": "testnet", + "vrf": "default", "is-type": "level-1" } ] } } + +Exit and save config to the same file. + +After that, this configuration should get reflected to vtysh: + +.. code-block:: console + + show run + Building configuration... + + Current configuration: + ! + frr version 9.2-dev-MyOwnFRRVersion + frr defaults traditional + hostname bullseye + ! + router isis testnet + is-type level-1 + exit + ! + end + +NETCONF +^^^^^^^ + +To manage sysrepo configuration through netconf +you can use `netopeer2 `_ as a netfconf server that can +be easily integrated with sysrepo. diff --git a/doc/developer/northbound/ppr-basic-test-topology.rst b/doc/developer/northbound/ppr-basic-test-topology.rst index a680ed7..4929c9b 100644 --- a/doc/developer/northbound/ppr-basic-test-topology.rst +++ b/doc/developer/northbound/ppr-basic-test-topology.rst @@ -1,15 +1,10 @@ -Table of Contents -~~~~~~~~~~~~~~~~~ +IS-IS PPR Basic +=============== -- `Software <#software>`__ -- `Topology <#topology>`__ -- `Configuration <#configuration>`__ - - - `CLI <#configuration-cli>`__ - - `YANG <#configuration-yang>`__ - -- `Verification - Control Plane <#verification-cplane>`__ -- `Verification - Forwarding Plane <#verification-fplane>`__ +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 2 Software ~~~~~~~~ diff --git a/doc/developer/northbound/ppr-mpls-basic-test-topology.rst b/doc/developer/northbound/ppr-mpls-basic-test-topology.rst index cedb795..aceec5f 100644 --- a/doc/developer/northbound/ppr-mpls-basic-test-topology.rst +++ b/doc/developer/northbound/ppr-mpls-basic-test-topology.rst @@ -1,15 +1,10 @@ -Table of Contents -~~~~~~~~~~~~~~~~~ +IS-IS PPR Basic MPLS +==================== -- `Software <#software>`__ -- `Topology <#topology>`__ -- `Configuration <#configuration>`__ - - - `CLI <#configuration-cli>`__ - - `YANG <#configuration-yang>`__ - -- `Verification - Control Plane <#verification-cplane>`__ -- `Verification - Forwarding Plane <#verification-fplane>`__ +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 2 Software ~~~~~~~~ diff --git a/doc/developer/northbound/retrofitting-configuration-commands.rst b/doc/developer/northbound/retrofitting-configuration-commands.rst index b407246..d328be9 100644 --- a/doc/developer/northbound/retrofitting-configuration-commands.rst +++ b/doc/developer/northbound/retrofitting-configuration-commands.rst @@ -1,5 +1,16 @@ + +.. _nb-retrofit: + Retrofitting Configuration Commands ------------------------------------ +=================================== + +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 2 + +Retrofitting process +-------------------- This page explains how to convert existing CLI configuration commands to the new northbound model. This documentation is meant to be the primary @@ -7,9 +18,6 @@ reference for developers working on the northbound retrofitting process. We’ll show several examples taken from the ripd northbound conversion to illustrate some concepts described herein. -Retrofitting process --------------------- - Step 1: writing a YANG module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -30,34 +38,41 @@ possible to facilitate the process of writing module translators using the [[YANG module translator]]. As an example, the frr-ripd YANG module incorporated several parts of the IETF RIP YANG module. The repositories below contain big collections of YANG models that might be used as a -reference: \* https://github.com/YangModels/yang \* -https://github.com/openconfig/public +reference: + +* https://github.com/YangModels/yang + +* https://github.com/openconfig/public When writing a YANG module, it’s highly recommended to follow the guidelines from `RFC 6087 `__. In general, most commands should be modeled fairly easy. Here are a few -guidelines specific to authors of FRR YANG models: \* Use -presence-containers or lists to model commands that change the CLI node -(e.g. ``router rip``, ``interface eth0``). This way, if the -presence-container or list entry is removed, all configuration options -below them are removed automatically (exactly like the CLI behaves when -a configuration object is removed using a *no* command). This -recommendation is orthogonal to the `YANG authoring guidelines for -OpenConfig -models `__ -where the use of presence containers is discouraged. OpenConfig YANG -models however were not designed to replicate the behavior of legacy CLI -commands. \* When using YANG lists, be careful to identify what should -be the key leaves. In the ``offset-list WORD (0-16) IFNAME`` -command, for example, both the direction (````) and the -interface name should be the keys of the list. This can be only known by -analyzing the data structures used to store the commands. \* For -clarity, use non-presence containers to group leaves that are associated -to the same configuration command (as we’ll see later, this also -facilitate the process of writing ``cli_show`` callbacks). \* YANG -leaves of type *enumeration* should define explicitly the value of each -*enum* option based on the value used in the FRR source code. \* Default -values should be taken from the source code whenever they exist. +guidelines specific to authors of FRR YANG models: + +* Use presence-containers or lists to model commands that change the CLI node + (e.g. ``router rip``, ``interface eth0``). This way, if the presence-container + or list entry is removed, all configuration options below them are removed + automatically (exactly like the CLI behaves when a configuration object is + removed using a *no* command). This recommendation is orthogonal to the `YANG + authoring guidelines for OpenConfig models + `__ + where the use of presence containers is discouraged. OpenConfig YANG models + however were not designed to replicate the behavior of legacy CLI commands. + +* When using YANG lists, be careful to identify what should be the key leaves. + In the ``offset-list WORD (0-16) IFNAME`` command, for example, both + the direction (````) and the interface name should be the keys of the + list. This can be only known by analyzing the data structures used to store + the commands. + +* For clarity, use non-presence containers to group leaves that are associated + to the same configuration command (as we’ll see later, this also facilitate + the process of writing ``cli_show`` callbacks). + +* YANG leaves of type *enumeration* should define explicitly the value of each + *enum* option based on the value used in the FRR source code. + +* Default values should be taken from the source code whenever they exist. Some commands are more difficult to model and demand the use of more advanced YANG constructs like *choice*, *when* and *must* statements. @@ -726,15 +741,17 @@ Configuration options are edited individually ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Several CLI commands edit multiple configuration options at the same -time. Some examples taken from ripd: \* -``timers basic (5-2147483647) (5-2147483647) (5-2147483647)`` - -*/frr-ripd:ripd/instance/timers/flush-interval* - -*/frr-ripd:ripd/instance/timers/holddown-interval* - -*/frr-ripd:ripd/instance/timers/update-interval* \* -``distance (1-255) A.B.C.D/M [WORD]`` - -*/frr-ripd:ripd/instance/distance/source/prefix* - -*/frr-ripd:ripd/instance/distance/source/distance* - -*/frr-ripd:ripd/instance/distance/source/access-list* +time. Some examples taken from ripd: + +* ``timers basic (5-2147483647) (5-2147483647) (5-2147483647)`` + * */frr-ripd:ripd/instance/timers/flush-interval* + * */frr-ripd:ripd/instance/timers/holddown-interval* + * */frr-ripd:ripd/instance/timers/update-interval* + +* ``distance (1-255) A.B.C.D/M [WORD]`` + * */frr-ripd:ripd/instance/distance/source/prefix* + * */frr-ripd:ripd/instance/distance/source/distance* + * */frr-ripd:ripd/instance/distance/source/access-list* In the new northbound model, there’s one or more separate callbacks for each configuration option. This usually has implications when converting @@ -965,34 +982,30 @@ future. For libfrr commands, it’s not possible to centralize all commands in a single file because the *extract.pl* script from *vtysh* treats commands differently depending on the file in which they are defined (e.g. DEFUNs -from *lib/routemap.c* are installed using the ``VTYSH_RMAP`` constant, +from *lib/routemap.c* are installed using the ``VTYSH_RMAP_SHOW`` constant, which identifies the daemons that support route-maps). In this case, the CLI commands should be rewritten but maintained in the same file. Since all CLI configuration commands from FRR will need to be rewritten, this is an excellent opportunity to rework this part of the code to make the commands easier to maintain and extend. These are the three main -recommendations: 1. Always use DEFPY instead of DEFUN to improve code -readability. 2. Always try to join multiple DEFUNs into a single DEFPY -whenever possible. As an example, there’s no need to have both -``distance (1-255) A.B.C.D/M`` and ``distance (1-255) A.B.C.D/M WORD`` -when a single ``distance (1-255) A.B.C.D/M [WORD]`` would suffice. 3. -When necessary, create a separate DEFPY for ``no`` commands so that part -of the configuration command can be made optional for convenience. -Example: -``no timers basic [(5-2147483647) (5-2147483647) (5-2147483647)]``. In -this example, everything after ``no timers basic`` is ignored by FRR, so -it makes sense to accept ``no timers basic`` as a valid command. But it -also makes sense to accept all parameters -(``no timers basic (5-2147483647) (5-2147483647) (5-2147483647)``) to -make it easier to remove the command just by prefixing a “no” to it. +recommendations: + +#. Always use DEFPY instead of DEFUN to improve code readability +#. Always try to join multiple DEFUNs into a single DEFPY whenever possible. As + an example, there’s no need to have both ``distance (1-255) A.B.C.D/M`` and + ``distance (1-255) A.B.C.D/M WORD`` when a single ``distance (1-255) + A.B.C.D/M [WORD]`` would suffice. +#. When making a negative form of a command, put ``[no]`` in the positive form + and use ``![...]`` to mark portions of the command that should be optional + only in the ``no`` version. To rewrite a CLI command as a dumb wrapper around the northbound callbacks, use the ``nb_cli_cfg_change()`` function. This function accepts as a parameter an array of ``cli_config_change`` structures that specify the changes that need to performed on the candidate configuration. Here’s the declaration of this structure (taken from the -*lib/northbound_cli.h* file): +``lib/northbound_cli.h`` file): .. code:: c @@ -1005,7 +1018,7 @@ configuration. Here’s the declaration of this structure (taken from the /* * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or - * NB_OP_DELETE). + * NB_OP_DESTROY). */ enum nb_operation operation; @@ -1034,16 +1047,23 @@ changing the candidate configuration. the northbound callbacks are not involved). Other important details to keep in mind while rewriting the CLI -commands: \* ``nb_cli_cfg_change()`` returns CLI errors codes -(e.g. ``CMD_SUCCESS``, ``CMD_WARNING``), so the return value of this -function can be used as the return value of CLI commands. \* Calls to -``VTY_PUSH_CONTEXT`` and ``VTY_PUSH_CONTEXT_SUB`` should be converted to -calls to ``VTY_PUSH_XPATH``. Similarly, the following macros aren’t -necessary anymore and can be removed: ``VTY_DECLVAR_CONTEXT``, -``VTY_DECLVAR_CONTEXT_SUB``, ``VTY_GET_CONTEXT`` and -``VTY_CHECK_CONTEXT``. The ``nb_cli_cfg_change()`` functions uses the -``VTY_CHECK_XPATH`` macro to check if the data node being edited still -exists before doing anything else. +commands: + +* ``nb_cli_cfg_change()`` returns CLI errors codes (e.g. ``CMD_SUCCESS``, + ``CMD_WARNING``), so the return value of this function can be used as the + return value of CLI commands. + +* Calls to ``VTY_PUSH_CONTEXT`` and ``VTY_PUSH_CONTEXT_SUB`` should be converted + to calls to ``VTY_PUSH_XPATH``. Similarly, the following macros aren’t + necessary anymore and can be removed: + + * ``VTY_DECLVAR_CONTEXT`` + * ``VTY_DECLVAR_CONTEXT_SUB`` + * ``VTY_GET_CONTEXT`` + * ``VTY_CHECK_CONTEXT``. + + The ``nb_cli_cfg_change()`` functions uses the ``VTY_CHECK_XPATH`` macro to + check if the data node being edited still exists before doing anything else. The examples below provide additional details about how the conversion should be done. @@ -1205,7 +1225,7 @@ This example shows how to create a list entry: }, { .xpath = "./access-list", - .operation = acl ? NB_OP_MODIFY : NB_OP_DELETE, + .operation = acl ? NB_OP_MODIFY : NB_OP_DESTROY, .value = acl, }, }; @@ -1242,7 +1262,7 @@ When deleting a list entry, all non-key leaves can be ignored: struct cli_config_change changes[] = { { .xpath = ".", - .operation = NB_OP_DELETE, + .operation = NB_OP_DESTROY, }, }; @@ -1785,10 +1805,13 @@ Implementation of the ``cli_show`` callback: } This is the most complex ``cli_show`` callback we have in ripd. Its -complexity comes from the following: \* The -``ip rip authentication mode ...`` command changes two YANG leaves at -the same time. \* Part of the command should be hidden when the -``show_defaults`` parameter is set to false. +complexity comes from the following: + +* The ``ip rip authentication mode ...`` command changes two YANG leaves at the + same time. + +* Part of the command should be hidden when the ``show_defaults`` parameter is + set to false. This is the behavior we want to implement: @@ -1838,19 +1861,27 @@ As mentioned in the fourth step, the northbound retrofitting process can happen gradually over time, since both “old” and “new” commands can coexist without problems. Once all commands from a given daemon were converted, we can proceed to the consolidation step, which consists of -the following: \* Remove the vty configuration lock, which is enabled by -default in all daemons. Now multiple users should be able to edit the -configuration concurrently, using either shared or private candidate -configurations. \* Reference commit: -`57dccdb1 `__. -\* Stop using the qobj infrastructure to keep track of configuration -objects. This is not necessary anymore, the northbound uses a similar -mechanism to keep track of YANG data nodes in the candidate -configuration. \* Reference commit: -`4e6d63ce `__. -\* Make the daemon SIGHUP handler re-read the configuration file (and -ensure it’s not doing anything other than that). \* Reference commit: -`5e57edb4 `__. +the following: + +* Remove the vty configuration lock, which is enabled by default in all daemons. + Now multiple users should be able to edit the configuration concurrently, + using either shared or private candidate configurations. + +* Reference commit: `57dccdb1 + `__. + +* Stop using the qobj infrastructure to keep track of configuration objects. + This is not necessary anymore, the northbound uses a similar mechanism to keep + track of YANG data nodes in the candidate configuration. + +* Reference commit: `4e6d63ce + `__. + +* Make the daemon SIGHUP handler re-read the configuration file (and ensure it’s + not doing anything other than that). + +* Reference commit: `5e57edb4 + `__. Final Considerations -------------------- diff --git a/doc/developer/northbound/transactional-cli.rst b/doc/developer/northbound/transactional-cli.rst index 439bb6a..5c495d3 100644 --- a/doc/developer/northbound/transactional-cli.rst +++ b/doc/developer/northbound/transactional-cli.rst @@ -1,25 +1,10 @@ -Table of Contents ------------------ - -- `Introduction <#introduction>`__ -- `Configuration modes <#config-modes>`__ -- `New commands <#retrofitting-process>`__ - - - `commit check <#cmd1>`__ - - `commit <#cmd2>`__ - - `discard <#cmd3>`__ - - `configuration database max-transactions <#cmd4>`__ - - `configuration load <#cmd5>`__ - - `rollback configuration <#cmd6>`__ - - `show configuration candidate <#cmd7>`__ - - `show configuration compare <#cmd8>`__ - - `show configuration running <#cmd9>`__ - - `show configuration transaction <#cmd10>`__ - - `show yang module <#cmd11>`__ - - `show yang module-translator <#cmd12>`__ - - `update <#cmd13>`__ - - `yang module-translator load <#cmd14>`__ - - `yang module-translator unload <#cmd15>`__ +Transactional CLI +================= + +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 1 Introduction ~~~~~~~~~~~~ @@ -70,18 +55,21 @@ Configuration modes ~~~~~~~~~~~~~~~~~~~ When using the transactional CLI (``--tcli``), FRR supports three -different forms of the ``configure`` command: \* ``configure terminal``: -in this mode, a single candidate configuration is shared by all users. -This means that one user might delete a configuration object that’s -being edited by another user, in which case the CLI will detect and -report the problem. If one user issues the ``commit`` command, all -changes done by all users are committed. \* ``configure private``: users -have a private candidate configuration that is edited separately from -the other users. The ``commit`` command commits only the changes done by -the user. \* ``configure exclusive``: similar to ``configure private``, -but also locks the running configuration to prevent other users from -changing it. The configuration lock is released when the user exits the -configuration mode. +different forms of the ``configure`` command: + +* ``configure terminal``: in this mode, a single candidate configuration is + shared by all users. This means that one user might delete a configuration + object that’s being edited by another user, in which case the CLI will detect + and report the problem. If one user issues the ``commit`` command, all changes + done by all users are committed. + +* ``configure private``: users have a private candidate configuration that is + edited separately from the other users. The ``commit`` command commits only + the changes done by the user. + +* ``configure exclusive``: similar to ``configure private``, but also locks the + running configuration to prevent other users from changing it. The + configuration lock is released when the user exits the configuration mode. When using ``configure terminal`` or ``configure private``, the candidate configuration being edited might become outdated if another @@ -112,12 +100,14 @@ Check if the candidate configuration is valid or not. Commit the changes done in the candidate configuration into the running configuration. -Options: \* ``force``: commit even if the candidate configuration is -outdated. It’s usually a better option to use the ``update`` command -instead. \* ``comment LINE...``: assign a comment to the configuration -transaction. This comment is displayed when viewing the recorded -transactions in the output of the ``show configuration transaction`` -command. +Options: + +* ``force``: commit even if the candidate configuration is outdated. It’s + usually a better option to use the ``update`` command instead. + +* ``comment LINE...``: assign a comment to the configuration transaction. This + comment is displayed when viewing the recorded transactions in the output of + the ``show configuration transaction`` command. ``discard`` ''''''''''' @@ -140,10 +130,13 @@ respectively. It’s also possible to load a configuration from a previous transaction by specifying the desired transaction ID (``(1-4294967296)``). -Options: \* ``translate WORD``: translate the JSON/XML configuration -file using the YANG module translator. \* ``replace``: replace the -candidate by the loaded configuration. The default is to merge the -loaded configuration into the candidate configuration. +Options: + +* ``translate WORD``: translate the JSON/XML configuration file using the YANG + module translator. + +* ``replace``: replace the candidate by the loaded configuration. The default is + to merge the loaded configuration into the candidate configuration. ``rollback configuration (1-4294967296)`` ''''''''''''''''''''''''''''''''''''''''' @@ -156,39 +149,42 @@ identified by its transaction ID (``(1-4294967296)``). Show the candidate configuration. -Options: \* ``json``: show the configuration in the JSON format. \* -``xml``: show the configuration in the XML format. \* -``translate WORD``: translate the JSON/XML output using the YANG module -translator. \* ``with-defaults``: show default values that are hidden by -default. \* ``changes``: show only the changes done in the candidate -configuration. +Options: + +* ``json``: show the configuration in the JSON format. +* ``xml``: show the configuration in the XML format. +* ``translate WORD``: translate the JSON/XML output using the YANG module translator. +* ``with-defaults``: show default values that are hidden by default. +* ``changes``: show only the changes done in the candidate configuration. ``show configuration compare [ [translate WORD]]`` '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Show the difference between two different configurations. -Options: \* ``json``: show the configuration differences in the JSON -format. \* ``xml``: show the configuration differences in the XML -format. \* ``translate WORD``: translate the JSON/XML output using the -YANG module translator. +Options: + +* ``json``: show the configuration differences in the JSON format. +* ``xml``: show the configuration differences in the XML format. +* ``translate WORD``: translate the JSON/XML output using the YANG module translator. ``show configuration running [ [translate WORD]] [with-defaults]`` '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Show the running configuration. -Options: \* ``json``: show the configuration in the JSON format. \* -``xml``: show the configuration in the XML format. \* -``translate WORD``: translate the JSON/XML output using the YANG module -translator. \* ``with-defaults``: show default values that are hidden by -default. +Options: - NOTE: ``show configuration running`` shows only the running - configuration as known by the northbound layer. Configuration - commands not converted to the new northbound model will not be - displayed. To show the full running configuration, the legacy - ``show running-config`` command must be used. +* ``json``: show the configuration in the JSON format. +* ``xml``: show the configuration in the XML format. +* ``translate WORD``: translate the JSON/XML output using the YANG module translator. +* ``with-defaults``: show default values that are hidden by default. + +NOTE: ``show configuration running`` shows only the running +configuration as known by the northbound layer. Configuration +commands not converted to the new northbound model will not be +displayed. To show the full running configuration, the legacy +``show running-config`` command must be used. ``show configuration transaction [(1-4294967296) [ [translate WORD]] [changes]]`` ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -199,12 +195,13 @@ configuration associated to the previously committed transaction. When a transaction ID is not given, show all recorded transactions in the rollback log. -Options: \* ``json``: show the configuration in the JSON format. \* -``xml``: show the configuration in the XML format. \* -``translate WORD``: translate the JSON/XML output using the YANG module -translator. \* ``with-defaults``: show default values that are hidden by -default. \* ``changes``: show changes compared to the previous -transaction. +Options: + +* ``json``: show the configuration in the JSON format. +* ``xml``: show the configuration in the XML format. +* ``translate WORD``: translate the JSON/XML output using the YANG module translator. +* ``with-defaults``: show default values that are hidden by default. +* ``changes``: show changes compared to the previous transaction. ``show yang module [module-translator WORD] [WORD ]`` '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -212,11 +209,14 @@ transaction. When a YANG module is not given, show all loaded YANG modules. Otherwise, show detailed information about the given module. -Options: \* ``module-translator WORD``: change the context to modules -loaded by the specified YANG module translator. \* ``summary``: display -summary information about the module. \* ``tree``: display module in the -tree (RFC 8340) format. \* ``yang``: display module in the YANG format. -\* ``yin``: display module in the YIN format. +Options: + +* ``module-translator WORD``: change the context to modules loaded by the + specified YANG module translator. +* ``summary``: display summary information about the module. +* ``tree``: display module in the tree (RFC 8340) format. +* ``yang``: display module in the YANG format. +* ``yin``: display module in the YIN format. ``show yang module-translator`` ''''''''''''''''''''''''''''''' diff --git a/doc/developer/northbound/yang-module-translator.rst b/doc/developer/northbound/yang-module-translator.rst index aa527ce..17ae160 100644 --- a/doc/developer/northbound/yang-module-translator.rst +++ b/doc/developer/northbound/yang-module-translator.rst @@ -1,11 +1,10 @@ -Table of Contents ------------------ +YANG Module Translation +======================= -- `Introduction <#introduction>`__ -- `Deviation Modules <#deviation-modules>`__ -- `Translation Tables <#translation-tables>`__ -- `CLI Demonstration <#cli-demonstration>`__ -- `Implementation Details <#implementation-details>`__ +.. contents:: Table of contents + :local: + :backlinks: entry + :depth: 1 Introduction ------------ @@ -421,15 +420,20 @@ this shortcoming and make it possible to create more powerful YANG module translators. YANG module translators can be evaluated based on the following metrics: -\* Translation potential: is it possible to make complex translations, -taking several variables into account? \* Complexity: measure of how -easy or hard it is to write a module translator. \* Speed: measure of -how fast the translation can be achieved. Translation speed is of -fundamental importance, especially for operational data. \* Robustness: -can the translator be checked for inconsistencies at load time? A module -translator based on scripts wouldn’t fare well on this metric. \* -Round-trip conversions: can the translated data be translated back to -the original format without information loss? + +* Translation potential: is it possible to make complex translations, taking + several variables into account? + +* Complexity: measure of how easy or hard it is to write a module translator. + +* Speed: measure of how fast the translation can be achieved. Translation speed + is of fundamental importance, especially for operational data. + +* Robustness: can the translator be checked for inconsistencies at load time? A + module translator based on scripts wouldn’t fare well on this metric. + +* Round-trip conversions: can the translated data be translated back to the + original format without information loss? CLI Demonstration ----------------- diff --git a/doc/developer/northbound/yang-tools.rst b/doc/developer/northbound/yang-tools.rst index 346efca..fb5a287 100644 --- a/doc/developer/northbound/yang-tools.rst +++ b/doc/developer/northbound/yang-tools.rst @@ -1,5 +1,5 @@ Yang Tools -~~~~~~~~~~ +========== Here's some information about various tools for working with yang models. @@ -83,17 +83,19 @@ Indent a YANG file: --keep-comments -f yang --yang-canonical \ module.yang -o module.yang -Generate skeleton instance data: \* XML: +Generate skeleton instance data: -.. code:: sh +* XML: + + .. code:: sh $ pyang -p \ -f sample-xml-skeleton --sample-xml-skeleton-defaults \ module.yang [augmented-module1.yang ...] -o module.xml -- JSON: +* JSON: -.. code:: sh + .. code:: sh $ pyang -p \ -f jsonxsl module.yang -o module.xsl diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst index 06ee6a3..85126ca 100644 --- a/doc/developer/process-architecture.rst +++ b/doc/developer/process-architecture.rst @@ -87,7 +87,7 @@ are given by integer macros in :file:`frrevent.h` and are: ``EVENT_EXECUTE`` Just before a task is run its type is changed to this. This is used to show - ``X`` as the type in the output of :clicmd:`show thread cpu`. + ``X`` as the type in the output of :clicmd:`show event cpu`. The programmer never has to work with these types explicitly. Each type of task is created and queued via special-purpose functions (actually macros, but @@ -238,16 +238,16 @@ proceeding. In addition, the existing commands to show statistics and other information for tasks within the event driven model have been expanded to handle multiple -pthreads; running :clicmd:`show thread cpu` will display the usual event +pthreads; running :clicmd:`show event cpu` will display the usual event breakdown, but it will do so for each pthread running in the program. For example, :ref:`bgpd` runs a dedicated I/O pthread and shows the following -output for :clicmd:`show thread cpu`: +output for :clicmd:`show event cpu`: :: - frr# show thread cpu + frr# show event cpu - Thread statistics for bgpd: + Event statistics for bgpd: Showing statistics for pthread main ------------------------------------ diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst index 4fd5658..2335e8f 100644 --- a/doc/developer/rcu.rst +++ b/doc/developer/rcu.rst @@ -232,6 +232,15 @@ Internals that case, either all of the library's threads must be registered for RCU, or the code must instead pass a (non-RCU) copy of the data to the library. +.. c:function:: int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, const char *os_name) + + If a pthread is started outside the control of normal pthreads in frr + then frr_pthread_non_controlled_startup should be called. This will + properly setup both the pthread with rcu usage as well as some data + structures pertaining to the name of the pthread. This is especially + important if the pthread created ends up calling back into FRR and + one of the various zlog_XXX functions is called. + .. c:function:: void rcu_shutdown(void) Stop the RCU sweeper thread and make sure all cleanup has finished. diff --git a/doc/developer/requirements.txt b/doc/developer/requirements.txt new file mode 100644 index 0000000..483a4e9 --- /dev/null +++ b/doc/developer/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst index 202f003..7a43314 100644 --- a/doc/developer/scripting.rst +++ b/doc/developer/scripting.rst @@ -488,12 +488,6 @@ match *exactly*. In the above example, we defined encoders/decoders for a value of ``struct prefix *``, but not ``struct prefix`` or ``const struct prefix *``. -``const`` values are a special case. We want to use them in our Lua scripts -but not modify them, so creating a decoder for them would be meaningless. -But we still need a decoder for the type of value so that the compiler will be -satisfied. -For that, use ``lua_decode_noop``: - .. code-block:: diff #define DECODE_ARGS_WITH_STATE(L, value) \ diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 0deb0f5..652ee4e 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -5,6 +5,7 @@ dev_RSTFILES = \ doc/developer/bgp-typecodes.rst \ doc/developer/bgpd.rst \ + doc/developer/bmp.rst \ doc/developer/building-frr-for-alpine.rst \ doc/developer/building-frr-for-archlinux.rst \ doc/developer/building-frr-for-centos6.rst \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index b8f213b..9b9058b 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -8,14 +8,15 @@ Topotests is a suite of topology tests for FRR built on top of micronet. Installation and Setup ---------------------- -Topotests run under python3. Additionally, for ExaBGP (which is used -in some of the BGP tests) an older python2 version (and the python2 -version of ``pip``) must be installed. +Topotests run under python3. -Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11. +Tested with Ubuntu 22.04,Ubuntu 20.04, and Debian 12. -Instructions are the same for all setups (i.e. ExaBGP is only used for -BGP tests). +Python protobuf version < 4 is required b/c python protobuf >= 4 requires a +protoc >= 3.19, and older package versions are shipped by in the above distros. + +Instructions are the same for all setups. However, ExaBGP is only used for +BGP tests. Tshark is only required if you enable any packet captures on test runs. @@ -35,15 +36,23 @@ Installing Topotest Requirements tshark \ valgrind python3 -m pip install wheel - python3 -m pip install 'pytest>=6.2.4' - python3 -m pip install 'pytest-xdist>=2.3.0' + python3 -m pip install 'pytest>=6.2.4' 'pytest-xdist>=2.3.0' python3 -m pip install 'scapy>=2.4.5' python3 -m pip install xmltodict - # Use python2 pip to install older ExaBGP - python2 -m pip install 'exabgp<4.0.0' + python3 -m pip install git+https://github.com/Exa-Networks/exabgp@0659057837cd6c6351579e9f0fa47e9fb7de7311 useradd -d /var/run/exabgp/ -s /bin/false exabgp - # To enable the gRPC topotest install: +The version of protobuf package that is installed on your system will determine +which versions of the python protobuf packages you need to install. + +.. code:: shell + # - Either - For protobuf version <= 3.12 + python3 -m pip install 'protobuf<4' + + # - OR- for protobuf version >= 3.21 + python3 -m pip install 'protobuf>=4' + + # To enable the gRPC topotest also install: python3 -m pip install grpcio grpcio-tools @@ -116,9 +125,9 @@ If you prefer to manually build FRR, then use the following suggested config: ./configure \ --prefix=/usr \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-vtysh \ --enable-pimd \ --enable-pim6d \ @@ -230,8 +239,8 @@ the number of the test we are interested in along with ``--errmsg`` option. ~/frr/tests/topotests# ./analyze.py -Ar run-save -T0 --errmsg bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge: AssertionError: BGP did not converge: - IPv4 Unicast Summary (VIEW 1): - BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1 + IPv4 Unicast Summary: + BGP router identifier 172.30.1.1, local AS number 100 VIEW 1 vrf-id -1 BGP table version 1 RIB entries 1, using 184 bytes of memory Peers 3, using 2169 KiB of memory @@ -266,8 +275,8 @@ select the first failed test case. > assert False, "BGP did not converge:\n%s" % bgpStatus E AssertionError: BGP did not converge: E - E IPv4 Unicast Summary (VIEW 1): - E BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1 + E IPv4 Unicast Summary: + E BGP router identifier 172.30.1.1, local AS number 100 VIEW 1 vrf-id -1 [...] E Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc E 172.16.1.1 4 65001 0 0 0 0 0 never Connect 0 N/A @@ -386,8 +395,9 @@ for ``master`` branch: ./bootstrap.sh ./configure \ --enable-address-sanitizer \ - --prefix=/usr/lib/frr --sysconfdir=/etc/frr \ - --localstatedir=/var/run/frr \ + --prefix=/usr/lib/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \ --with-moduledir=/usr/lib/frr/modules \ --enable-multipath=0 --enable-rtadv \ @@ -559,6 +569,8 @@ Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``. sudo -E pytest --vtysh=rt1,rt2 all-protocol-startup +.. _debug_with_gdb: + Debugging with GDB """""""""""""""""" @@ -583,6 +595,12 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router --gdb-breakpoints=nb_config_diff \ all-protocol-startup +Finally, for Emacs users, you can specify ``--gdb-use-emacs``. When specified +the first router and daemon to be launched in gdb will be launched and run with +Emacs gdb functionality by using `emacsclient --eval` commands. This provides an +IDE debugging experience for Emacs users. This functionality works best when +using password-less sudo. + Reporting Memleaks with FRR Memory Statistics """"""""""""""""""""""""""""""""""""""""""""" @@ -631,16 +649,26 @@ Detecting Memleaks with Valgrind """""""""""""""""""""""""""""""" Topotest can automatically launch all daemons with ``valgrind`` to check for -memleaks. This is enabled by specifying 1 or 2 CLI arguments. -``--valgrind-memleaks`` will enable general memleak detection, and -``--valgrind-extra`` enables extra functionality including generating a -suppression file. The suppression file ``tools/valgrind.supp`` is used when -memleak detection is enabled. +memleaks. This is enabled by specifying 1 to 3 CLI arguments. +``--valgrind-memleaks`` enables memleak detection. ``--valgrind-extra`` enables +extra functionality including generating a suppression file. The suppression +file ``tools/valgrind.supp`` is used when memleak detection is enabled. Finally, +``--valgrind-leak-kinds=KINDS`` can be used to modify what types of links are +reported. This corresponds to valgrind's ``--show-link-kinds`` arg. The value is +either ``all`` or a comma-separated list of types: +``definite,indirect,possible,reachable``. The default is ``definite,possible``. .. code:: shell sudo -E pytest --valgrind-memleaks all-protocol-startup +.. note:: GDB can be used in conjection with valgrind. + + When you enable ``--valgrind-memleaks`` and you also launch various daemons + under GDB (debug_with_gdb_) topotest will connect the two utilities using + ``--vgdb-error=0`` and attaching to a ``vgdb`` process. This is very + useful for debugging bugs with use of uninitialized errors, et al. + Collecting Performance Data using perf(1) """"""""""""""""""""""""""""""""""""""""" @@ -662,6 +690,28 @@ during the config_timing test. To specify different arguments for ``perf record``, one can use the ``--perf-options`` this will replace the ``-g`` used by default. +Running Daemons under RR Debug (``rr record``) +"""""""""""""""""""""""""""""""""""""""""""""" + +Topotest can automatically launch any daemon under ``rr(1)`` to collect +execution state. The daemon is run in the foreground with ``rr record``. + +The execution state will be saved in the router specific directory +(in a `rr` subdir that rr creates) under the test's run directoy. + +Here's an example of collecting ``rr`` execution state from ``mgmtd`` on router +``r1`` during the ``config_timing`` test. + +.. code:: console + + $ sudo -E pytest --rr-routers=r1 --rr-daemons=mgmtd config_timing + ... + $ find /tmp/topotests/ -name '*perf.data*' + /tmp/topotests/config_timing.test_config_timing/r1/perf.data + +To specify additional arguments for ``rr record``, one can use the +``--rr-options``. + .. _topotests_docker: Running Tests with Docker diff --git a/doc/figures/cli-oper-state.drawio b/doc/figures/cli-oper-state.drawio new file mode 100644 index 0000000..4b86b58 --- /dev/null +++ b/doc/figures/cli-oper-state.drawio @@ -0,0 +1,377 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/figures/cli-oper-state.svg b/doc/figures/cli-oper-state.svg new file mode 100644 index 0000000..bda7d7b --- /dev/null +++ b/doc/figures/cli-oper-state.svg @@ -0,0 +1,3 @@ + + +
Frontend CLI (mgmtd)
Frontend CLI (mgmtd)
MGMTD
MGMTD
xpath
xpath
txn
txn
clients (bitmask)
clients (bitmask)
fe_adapter_handle_get_tree
fe_adapter_handle_get_tree
be_adapter_handle_get_tree
mgmt_txn_notify_tree_data_reply
------------------------------------
merge tree data
when all clients respond or timeout
be_adapter_handle_get_tree...
mgmt_txn_send_get_tree_oper
mgmt_txn_send_get_tree_oper
mgmt_be_send_native
mgmt_be_send_native
socket connection
FE adapter -> FE client
MGMT_MSG_CODE_TREE_DATA
struct mgmt_msg_tree_data
socket connection...
txn_get_tree_data_done
fe_adapter_send_tree_data
txn_get_tree_data_done...
session->get_tree_notify
session->get_tree_notify
vty_mgmt_resume_response
vty_mgmt_resume_response
vty_mgmt_get_tree_result_notified
displays result
vty_mgmt_get_tree_result_notified...
clients (bitmask)
clients (bitmask)
mgmt_be_interested_clients
mgmt_be_interested_clients
vty_mgmt_send_get_tree_req
vty_mgmt_send_get_tree_req
socket connection
FE client -> FE adapter
MGMT_MSG_CODE_GET_TREE
struct mgmt_msg_get_tree
socket connection...
mgmt_fe_send_get_tree_req
mgmt_fe_send_get_tree_req
xpath
xpath
fe_adapter_handle_native_msg
fe_adapter_handle_native_msg
"show mgmt get-data-tree WORD$path [json|xml]"
"show mgmt get-data-tree WORD...
EVENT: VTYSH_READ
EVENT: VTYSH_READ
VTYSH
VTYSH
mgmt_create_txn
mgmt_create_txn
mgmt_txn_req_alloc
mgmt_txn_req_alloc
txn_req
MGMTD_TXN_PROC_GETTREE
txn_req...
for each of the clients
in bitmask
for ea...
be_client_send_native_msg
be_client_send_native_msg
be_adapter_handle_native_msg
be_adapter_handle_native_msg
socket connection
BE client -> BE adapter
MGMT_MSG_CODE_TREE_DATA
struct mgmt_msg_tree_data
socket connection...
be_client_send_native_msg
be_client_send_native_msg
Backend Client (ospfd, staticd, ...)
Backend Client (ospfd, staticd, ...)
(1) build oper state tree
struct mgmt_msg_tree_data
(1) build oper state tree...
(2)
(2)
be_client_handle_get_tree
be_client_handle_get_tree
nb_oper_data_iterate
nb_oper_data_iterate
be_client_handle_native_msg
be_client_handle_native_msg
be_client_send_native_msg
be_client_send_native_msg
for each of the
queried BE clients
for ea...
socket connection
BE adapter -> BE client
MGMT_MSG_CODE_GET_TREE
struct mgmt_msg_get_tree
socket connection...
fe_client_handle_native_msg
fe_client_handle_native_msg
socket async
socket async
timer/event async
timer/event async
function sync
function sync
mgmt_fe_adapter.c
mgmt_fe_adapter.c
mgmt_txn.c
mgmt_txn.c
mgmt_be_adapter.c
mgmt_be_adapter.c
z
z
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/user/.readthedocs.yaml b/doc/user/.readthedocs.yaml index c5a11da..ba5698c 100644 --- a/doc/user/.readthedocs.yaml +++ b/doc/user/.readthedocs.yaml @@ -7,6 +7,9 @@ build: tools: python: "3.11" +python: + install: + - requirements: doc/user/requirements.txt # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/user/conf.py diff --git a/doc/user/babeld.rst b/doc/user/babeld.rst index bda0045..b7b7c1f 100644 --- a/doc/user/babeld.rst +++ b/doc/user/babeld.rst @@ -26,8 +26,7 @@ The *zebra* daemon must be running before *babeld* is invoked. Also, if *zebra* is restarted then *babeld* must be too. -Configuration of *babeld* is done in its configuration file -:file:`babeld.conf`. +.. include:: config-include.rst .. _babel-configuration: diff --git a/doc/user/basic.rst b/doc/user/basic.rst index bbf24c5..5fdd188 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -11,45 +11,22 @@ The following sections discuss commands common to all the routing daemons. Config Commands =============== - - - - -In a config file, you can write the debugging options, a vty's password, +In the config file, you can write the debugging options, a vty's password, routing daemon configurations, a log file name, and so forth. This information forms the initial command set for a routing beast as it is starting. -Config files are generally found in |INSTALL_PREFIX_ETC|. - -Config Methods --------------- - -There are two ways of configuring FRR. - -Traditionally each of the daemons had its own config file. The daemon name plus -``.conf`` was the default config file name. For example, zebra's default config -file was :file:`zebra.conf`. This method is deprecated. - -Because of the amount of config files this creates, and the tendency of one -daemon to rely on others for certain functionality, most deployments now use -"integrated" configuration. In this setup all configuration goes into a single -file, typically :file:`/etc/frr/frr.conf`. When starting up FRR using an init -script or systemd, ``vtysh`` is invoked to read the config file and send the -appropriate portions to only the daemons interested in them. Running -configuration updates are persisted back to this single file using ``vtysh``. -This is the recommended method. To use this method, add the following line to -:file:`/etc/frr/vtysh.conf`: - -.. code-block:: frr - - service integrated-vtysh-config +.. _config-file: -If you installed from source or used a package, this is probably already -present. +Integrated Config File +---------------------- -If desired, you can specify a config file using the :option:`-f` or -:option:`--config_file` options when starting a daemon. +FRR uses a single configuration file located in |INSTALL_PREFIX_ETC|/frr.conf. +When FRR is started using an init script or ``systemd``, ``vtysh`` is invoked to +read the config file and send the appropriate portions to only the daemons +interested in them. Running configuration updates are persisted back to this +single file using ``vtysh`` as well. +.. include:: prior-config-files.rst .. _basic-config-commands: @@ -152,6 +129,20 @@ Basic Config Commands deprecated ``log trap`` command) will be used. The ``no`` form of the command disables logging to a file. +.. clicmd:: log daemon DAEMON file [FILENAME [LEVEL]] + + Configure file logging for a single FRR daemon. If you want to log + into a file, please specify ``filename`` as in this example: + + :: + + log daemon bgpd file /var/log/frr/bgpd.log informational + + If the optional second argument specifying the logging level is not present, + the default logging level (typically debugging, but can be changed using the + deprecated ``log trap`` command) will be used. The ``no`` form of the command + disables logging to a file for a single FRR daemon. + .. clicmd:: log syslog [LEVEL] Enable logging output to syslog. If the optional second argument specifying @@ -215,7 +206,7 @@ Basic Config Commands enabled log destinations. The note that logging includes full command lines, including passwords. If the daemon startup option `--command-log-always` is used to start the daemon then this command is turned on by default - and cannot be turned off and the [no] form of the command is dissallowed. + and cannot be turned off and the [no] form of the command is disallowed. .. clicmd:: log filtered-file [FILENAME [LEVEL]] @@ -673,24 +664,29 @@ Terminal Mode Commands .. _common-show-commands: -.. clicmd:: show thread cpu [r|w|t|e|x] +.. clicmd:: show event cpu [r|w|t|e|x] This command displays system run statistics for all the different event types. If no options is specified all different run types are displayed together. Additionally you can ask to look at (r)ead, (w)rite, (t)imer, (e)vent and e(x)ecute thread event types. -.. clicmd:: show thread poll +.. clicmd:: show event poll This command displays FRR's poll data. It allows a glimpse into how we are setting each individual fd for the poll command at that point in time. -.. clicmd:: show thread timers +.. clicmd:: show event timers This command displays FRR's timer data for timers that will pop in the future. +.. clicmd:: show configuration running [ [translate WORD]] [with-defaults] DAEMON + + This command displays the northbound/YANG configuration data for a + daemon in text/vty, json, or xml format. + .. clicmd:: show yang operational-data XPATH [{format |translate TRANSLATOR|with-config}] DAEMON Display the YANG operational data starting from XPATH. The default @@ -773,7 +769,7 @@ These options apply to all |PACKAGE_NAME| daemons. .. option:: --command-log-always Cause the daemon to always log commands entered to the specified log file. - This also makes the `no log commands` command dissallowed. Enabling this + This also makes the `no log commands` command disallowed. Enabling this is suggested if you have need to track what the operator is doing on this router. diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 6c57822..3ca104a 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -27,6 +27,8 @@ This document will focus on the later implementation: *bfdd*. Starting BFD ============ +.. include:: config-include.rst + *bfdd* default configuration file is :file:`bfdd.conf`. *bfdd* searches the current directory first then |INSTALL_PREFIX_ETC|/bfdd.conf. All of *bfdd*'s command must be configured in :file:`bfdd.conf`. @@ -44,9 +46,7 @@ may also be specified (:ref:`common-invocation-options`). /usr/lib/frr/bfdd --bfdctl /tmp/bfdd.sock - The default UNIX socket location is: - - #define BFDD_CONTROL_SOCKET "|INSTALL_PREFIX_STATE|/bfdd.sock" + The default UNIX socket location is |INSTALL_PREFIX_STATE|/bfdd.sock This option overrides the location addition that the -N option provides to the bfdd.sock diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 5b8ec11..53dc551 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -14,10 +14,7 @@ interdomain routing protocol. BGP-4 is described in :rfc:`1771` and updated by Starting BGP ============ -The default configuration file of *bgpd* is :file:`bgpd.conf`. *bgpd* searches -the current directory first, followed by |INSTALL_PREFIX_ETC|/bgpd.conf. All of -*bgpd*'s commands must be configured in :file:`bgpd.conf` when the integrated -config is not being used. +.. include:: config-include.rst *bgpd* specific invocation options are described below. Common options may also be specified (:ref:`common-invocation-options`). @@ -494,8 +491,8 @@ Require policy on EBGP exit1# show bgp summary - IPv4 Unicast Summary (VRF default): - BGP router identifier 10.10.10.1, local AS number 65001 vrf-id 0 + IPv4 Unicast Summary: + BGP router identifier 10.10.10.1, local AS number 65001 VRF default vrf-id 0 BGP table version 4 RIB entries 7, using 1344 bytes of memory Peers 2, using 43 KiB of memory @@ -527,6 +524,27 @@ Reject routes with AS_SET or AS_CONFED_SET types This command enables rejection of incoming and outgoing routes having AS_SET or AS_CONFED_SET type. +Enforce first AS +---------------- + +.. clicmd:: bgp enforce-first-as + + To configure a router to deny an update received from an external BGP (eBGP) + peer that does not list its autonomous system number at the beginning of + the `AS_PATH` in the incoming update, use the ``bgp enforce-first-as`` command + in router configuration mode. + + In order to exclude an arbitrary neighbor from this enforcement, use the + command ``no neighbor NAME enforce-first-as``. And vice-versa if a global + enforcement is disabled, you can override this behavior per neighbor too. + + Default: enabled. + +.. note:: + + If you have a peering to RS (Route-Server), most likely you MUST disable the + first AS enforcement. + Suppress duplicate updates -------------------------- @@ -1309,10 +1327,31 @@ section for the specific AF to redistribute into. Protocol availability for redistribution is determined by BGP AF; for example, you cannot redistribute OSPFv3 into ``address-family ipv4 unicast`` as OSPFv3 supports IPv6. -.. clicmd:: redistribute [metric (0-4294967295)] [route-map WORD] +.. clicmd:: redistribute [metric (0-4294967295)] [route-map WORD] Redistribute routes from other protocols into BGP. +.. clicmd:: redistribute (1-65535)] [metric (0-4294967295)] [route-map WORD] + + Redistribute routes from a routing table ID into BGP. There are two + techniques for redistribution: + + - Standard Table Redistribution ``table (1-65535)``: + - Routes from the specified routing table ID are imported into the + default routing table using the ``ip import-table ID`` command. + - These routes are identified by the protocol type "T[ID]" when + displayed with ``show (ip|ipv6) route``. + - The ``redistribute table ID`` command then integrates these routes + into BGP. + + - Direct Table Redistribution ``table-direct (1-65535)``: + - This method directly imports routes from the designated routing table + ID into BGP, omitting the step of adding to the default routing table. + - This method is especially relevant when the specified table ID is + checked against routing by appending the appropriate `ip rules`. + +Redistribute routes from a routing table number into BGP. + .. clicmd:: redistribute vnc-direct Redistribute VNC direct (not via zebra) routes to BGP process. @@ -1432,6 +1471,23 @@ Defining Peers peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN` command the connection will be denied. +.. clicmd:: neighbor PEER oad + + Mark a peer belonging to the One Administrative Domain. + + Some networks span more than one autonomous system and require more + flexibility in the propagation of path attributes.It is worth noting that + these multi-AS networks have a common or single administrative entity. + These networks are said to belong to One Administrative Domain (OAD). + It is desirable to carry IBGP-only attributes across EBGP peerings when + the peers belong to an OAD. + + Enabling this peering sub-type will allow the propagation of non-transitive + attributes across EBGP peerings (e.g. local-preference). Make sure to + turn this peering type on for all peers in the OAD. + + Disabled by default. + .. clicmd:: bgp listen range peer-group PGNAME Accept connections from any peers in the specified prefix. Configuration @@ -1509,7 +1565,10 @@ Configuring Peers Discard updates received from the specified (eBGP) peer if the AS_PATH attribute does not contain the PEER's ASN as the first AS_PATH segment. - Default: disabled. + You can enable or disable this enforcement globally too using + ``bgp enforce-first-as`` command. + + Default: enabled. .. clicmd:: neighbor PEER extended-optional-parameters @@ -1545,7 +1604,10 @@ Configuring Peers Configure an unnumbered BGP peer. ``PEER`` should be an interface name. The session will be established via IPv6 link locals. Use ``internal`` for iBGP - and ``external`` for eBGP sessions, or specify an ASN if you wish. + and ``external`` for eBGP sessions, or specify an ASN if you wish. Finally + this connection type is meant for point to point connections. If you are + on an ethernet segment and attempt to use this with more than one bgp + neighbor, only one neighbor will come up, due to how this feature works. .. clicmd:: neighbor PEER next-hop-self [force] @@ -1564,10 +1626,12 @@ Configuring Peers .. clicmd:: neighbor PEER update-source - Specify the IPv4 source address to use for the :abbr:`BGP` session to this - neighbour, may be specified as either an IPv4 address directly or as an + Specify the IPv4 or IPv6 source address to use for the :abbr:`BGP` session to this + neighbour, may be specified as either an IP address directly or as an interface name (in which case the *zebra* daemon MUST be running in order - for *bgpd* to be able to retrieve interface state). + for *bgpd* to be able to retrieve interface state). When there are multiple + addresses on the choosen IFNAME then BGP will use the address that matches + the most number of bits in comparison to the destination peer address. .. code-block:: frr @@ -1610,7 +1674,18 @@ Configuring Peers modifying the `net.core.optmem_max` sysctl to a larger value to avoid out of memory errors from the linux kernel. -.. clicmd:: neighbor PEER send-community +.. clicmd:: neighbor PEER send-community + + Send the communities to the peer. + + Default: enabled. + +.. clicmd:: neighbor PEER send-community extended rpki + + Send the extended RPKI communities to the peer. RPKI extended community + can be send only to iBGP and eBGP-OAD peers. + + Default: enabled. .. clicmd:: neighbor PEER weight WEIGHT @@ -1730,6 +1805,18 @@ Configuring Peers This includes changing graceful-restart (LLGR also) timers, enabling/disabling add-path, and other supported capabilities. +.. clicmd:: neighbor PEER capability fqdn + + Allow BGP to negotiate the FQDN Capability with its peers. + + FQDN Capability defines a new BGP message (CAPABILITY) allowing the + use of peer's name and domain name. + + This capability is activated by default. The ``no neighbor PEER capability + fqdn`` avoid negotiation of that capability. This is useful for peers who + are not supporting this capability or supporting BGP Capabilities + Negotiation RFC 2842. + .. clicmd:: neighbor accept-own Enable handling of self-originated VPN routes containing ``accept-own`` community. @@ -1860,8 +1947,8 @@ Configuring Peers .. code-block:: frr - IPv4 Unicast Summary (VRF default): - BGP router identifier 10.0.0.6, local AS number 65001 vrf-id 0 + IPv4 Unicast Summary: + BGP router identifier 10.0.0.6, local AS number 65001 VRF default vrf-id 0 BGP table version 12 RIB entries 23, using 4600 bytes of memory Peers 3, using 2174 KiB of memory @@ -2059,7 +2146,6 @@ Capability Negotiation .. clicmd:: neighbor PEER strict-capability-match - Strictly compares remote capabilities and local capabilities. If capabilities are different, send Unsupported Capability error then reset connection. @@ -2986,7 +3072,33 @@ address-family: Specifies the route-target list to be attached to a route (export) or the route-target list to match against (import) when exporting/importing between - the current unicast VRF and VPN. + the current unicast VRF and VPN. The `rt vpn export RTLIST` command is not + mandatory and can be replaced or completed by the `set extcommunity rt` + command in the route-map attached with the `route-map vpn export`. The below + configuration illustrates how the route target is selected based on the + prefixes, and not solely on vrf criterium: + + .. code-block:: frr + + access-list acl1 permit 192.0.2.0/24 + access-list acl2 permit 192.0.3.0/24 + route-map rmap permit 10 + match address acl1 + set extcommunity rt 65001:10 + ! + route-map rmap permit 20 + match address acl1 + set extcommunity rt 65001:20 + ! + router bgp 65001 vrf vrf1 + ! + address-family ipv4 unicast + rd vpn export 65001:1 + import vpn + export vpn + rt vpn import 65001:1 + route-map vpn export rmap + The RTLIST is a space-separated list of route-targets, which are BGP extended community values as described in @@ -3374,7 +3486,7 @@ The import filtering described in item (2) is constrained just to Type-2 The EVPN MAC-VRF Site-of-Origin can be configured using a single CLI command under ``address-family l2vpn evpn`` of the EVPN underlay BGP instance. -.. clicmd:: [no] mac-vrf soo +.. clicmd:: mac-vrf soo Example configuration: @@ -3520,7 +3632,7 @@ route maybe fragmented. The number of EVIs per-EAD route can be configured via the following BGP command - -.. clicmd:: [no] ead-es-frag evi-limit (1-1000) +.. clicmd:: ead-es-frag evi-limit (1-1000) Sample Configuration ^^^^^^^^^^^^^^^^^^^^^ @@ -3698,7 +3810,7 @@ When default route is present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2.1/3 *> 192.0.2.1/32 10.10.10.1 0 0 1 i *> 192.0.2.5/32 10.10.10.1 0 0 1 i - Displayed 4 routes and 4 total paths + Displayed 4 routes and 4 total paths Router2# show ip bgp neighbors 10.10.20.3 !--- Output suppressed. @@ -3746,7 +3858,7 @@ When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2 *> 192.0.2.1/32 10.10.10.1 0 0 1 i *> 192.0.2.5/32 10.10.10.1 0 0 1 i - Displayed 3 routes and 3 total paths + Displayed 3 routes and 3 total paths Router2# show ip bgp neighbors 10.10.20.3 @@ -3815,12 +3927,20 @@ Debugging information on BGP events such as peer connection / disconnection, session establishment / teardown, and capability negotiation. -.. clicmd:: debug bgp updates +.. clicmd:: debug bgp updates [detail] Enable or disable debugging for BGP updates. This provides information on BGP UPDATE messages transmitted and received between local and remote instances. + If ``detail`` is specified, the output will include the full BGP UPDATE with + detailed information such as attribute length, withdraw length, and more. + +.. clicmd:: debug bgp updates [ [prefix-list WORD]] + + Enable or disable debugging for BGP updates. Optionally, you can specify + a prefix-list to filter the updates for an arbitrary neighbor. + .. clicmd:: debug bgp keepalives Enable or disable debugging for BGP keepalives. This provides information on @@ -3921,6 +4041,26 @@ The following are available in the top level *enable* mode: Clear BGP message statistics for a specified peer or for all peers, optionally filtered by activated address-family and sub-address-family. +.. clicmd:: clear bgp [ipv4|ipv6] [unicast] PEER|\* capabilities + + Clear specific BGP capabilities for a specified peer or for all peers. This + includes such capabilities like FQDN capability, that can't be controlled by + any other configuration knob. + + For example, if you want to change the FQDN, you MUST reset the BGP session + in order to send a new FQDN capability to the peer. This command allows you + to resend FQDN capability without resetting the session. + + .. code-block:: frr + + hostname bgp-new.example.com + clear bgp 10.10.10.1 capabilities + +.. note:: + + Changing the hostname is possible only when connected to the specific daemon. + If you change the hostname via ``vtysh``, it won't be changed. + The following are available in the ``router bgp`` mode: .. clicmd:: write-quanta (1-64) @@ -4044,8 +4184,8 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. exit1# show ip bgp summary wide - IPv4 Unicast Summary (VRF default): - BGP router identifier 192.168.100.1, local AS number 65534 vrf-id 0 + IPv4 Unicast Summary: + BGP router identifier 192.168.100.1, local AS number 65534 VRF default vrf-id 0 BGP table version 3 RIB entries 5, using 920 bytes of memory Peers 1, using 27 KiB of memory @@ -4339,7 +4479,7 @@ incoming/outgoing directions. Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) Last update: Wed May 8 12:54:41 2023 - Displayed 2 routes and 2 total paths + Displayed 2 routes and 2 total paths .. code-block:: frr @@ -4364,7 +4504,7 @@ incoming/outgoing directions. Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) Last update: Wed May 8 12:45:01 2023 - Displayed 2 routes and 2 total paths + Displayed 2 routes and 2 total paths Instance vrf3: @@ -4389,7 +4529,7 @@ incoming/outgoing directions. Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 Last update: Fri May 8 02:41:55 2023 - Displayed 2 routes and 2 total paths + Displayed 2 routes and 2 total paths .. code-block:: frr @@ -4417,7 +4557,7 @@ incoming/outgoing directions. Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 Last update: Fri May 8 02:23:55 2023 - Displayed 2 routes and 2 total paths + Displayed 2 routes and 2 total paths .. _bgp-display-routes-by-community: diff --git a/doc/user/bmp.rst b/doc/user/bmp.rst index 1983995..0f46832 100644 --- a/doc/user/bmp.rst +++ b/doc/user/bmp.rst @@ -36,8 +36,8 @@ The `BMP` implementation in FRR has the following properties: successfully. OPEN messages for failed sessions cannot currently be mirrored. -- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast and - multicast SAFIs. Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not +- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast, multicast, + EVPN and VPN SAFIs. Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not currently supported. - monitoring peers that have BGP **add-path** enabled on the session will @@ -146,10 +146,10 @@ associated with a particular ``bmp targets``: Send BMP Statistics (counter) messages at the specified interval (in milliseconds.) -.. clicmd:: bmp monitor AFI SAFI +.. clicmd:: bmp monitor AFI SAFI Perform Route Monitoring for the specified AFI and SAFI. Only IPv4 and - IPv6 are currently valid for AFI. SAFI valid values are currently + IPv6 are currently valid for AFI. SAFI valid values are currently unicast, multicast, evpn and vpn. Other AFI/SAFI combinations may be added in the future. diff --git a/doc/user/config-include.rst b/doc/user/config-include.rst new file mode 100644 index 0000000..3a34151 --- /dev/null +++ b/doc/user/config-include.rst @@ -0,0 +1,12 @@ +.. +.. January 12 2024, Christian Hopps +.. +.. Copyright (c) 2024, LabN Consulting, L.L.C. +.. +.. + +Configuration for the daemon should be saved in the FRR integrated configuration +file located in |INSTALL_PREFIX_ETC|/frr.conf, see :ref:`config-file` for more +information on system configuration. + +.. include:: prior-config-files.rst diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index fa157c4..58a2957 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -24,21 +24,17 @@ known topology. Starting and Stopping eigrpd ============================ -The default configuration file name of *eigrpd*'s is :file:`eigrpd.conf`. When -invocation *eigrpd* searches directory |INSTALL_PREFIX_ETC|. If -:file:`eigrpd.conf` is not there next search current directory. If an -integrated config is specified configuration is written into :file:`frr.conf`. +.. include:: config-include.rst -The EIGRP protocol requires interface information maintained by *zebra* daemon. -So running *zebra* is mandatory to run *eigrpd*. Thus minimum sequence for -running EIGRP is: +If starting daemons by hand then please note, the EIGRP protocol requires +interface information maintained by *zebra* daemon. So running *zebra* is +mandatory to run *eigrpd*. Thus minimum sequence for running EIGRP is: :: # zebra -d # eigrpd -d - Please note that *zebra* must be invoked before *eigrpd*. To stop *eigrpd*, please use:: diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 24c6c22..fb9e23d 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -234,10 +234,9 @@ options from the list below. assigned to the realm. See the tc man page. This option is currently not compatible with the usage of nexthop groups in the linux kernel itself. -.. option:: --disable-irdp +.. option:: --enable-irdp - Disable IRDP server support. This is enabled by default if we have - both `struct in_pktinfo` and `struct icmphdr` available to us. + Enable IRDP server support. This is deprecated. .. option:: --disable-rtadv @@ -394,13 +393,20 @@ options to the configuration script. .. option:: --sysconfdir - Look for configuration files in `dir` [`prefix`/etc]. Note that sample - configuration files will be installed here. + Look for configuration files in `dir`/frr [`prefix`/etc]. Note that sample + configuration files will be installed here. Should be ``/etc`` unless + your platform splits package configuration locations. .. option:: --localstatedir - Configure zebra to use `dir` for local state files, such as pid files and - unix sockets. + Configure base directory for local state. Indirectly controls + ``--runstatedir``. Should be ``/var`` in most cases. + +.. option:: --runstatedir + + Configure FRR to use `dir`/frr for local state files, such as pid files and + unix sockets. Should be ``/var/run`` (default through ``--localstatedir``) + or ``/run`` in most cases. .. option:: --with-scriptdir @@ -579,9 +585,9 @@ the options you chose: ./configure \ --prefix=/usr \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-pimd \ --enable-watchfrr \ ... diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 63c9213..d37dfa6 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -22,8 +22,7 @@ interface information from *zebra* in order to function. Therefore *zebra* must be running before invoking *isisd*. Also, if *zebra* is restarted then *isisd* must be too. -Like other daemons, *isisd* configuration is done in :abbr:`ISIS` specific -configuration file :file:`isisd.conf`. +.. include:: config-include.rst .. _isis-router: diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 682443a..cbed734 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -32,9 +32,7 @@ options (:ref:`common-invocation-options`). The *zebra* daemon must be running before *ldpd* is invoked. -Configuration of *ldpd* is done in its configuration file -:file:`ldpd.conf`. - +.. include:: config-include.rst .. _understanding-ldp: diff --git a/doc/user/mgmtd.rst b/doc/user/mgmtd.rst index 42bc860..aa7ccaa 100644 --- a/doc/user/mgmtd.rst +++ b/doc/user/mgmtd.rst @@ -371,22 +371,22 @@ MGMT Daemon debug commands The following debug commands enable debugging within the management daemon: -.. clicmd:: [no] debug mgmt backend +.. clicmd:: debug mgmt backend Enable[/Disable] debugging messages related to backend operations within the management daemon. -.. clicmd:: [no] debug mgmt datastore +.. clicmd:: debug mgmt datastore Enable[/Disable] debugging messages related to YANG datastore operations within the management daemon. -.. clicmd:: [no] debug mgmt frontend +.. clicmd:: debug mgmt frontend Enable[/Disable] debugging messages related to frontend operations within the management daemon. -.. clicmd:: [no] debug mgmt transaction +.. clicmd:: debug mgmt transaction Enable[/Disable] debugging messages related to transactions within the management daemon. @@ -398,12 +398,12 @@ MGMT Client debug commands The following debug commands enable debugging within the management front and backend clients: -.. clicmd:: [no] debug mgmt client backend +.. clicmd:: debug mgmt client backend Enable[/Disable] debugging messages related to backend operations inside the backend mgmtd clients. -.. clicmd:: [no] debug mgmt client frontend +.. clicmd:: debug mgmt client frontend Enable[/Disable] debugging messages related to frontend operations inside the frontend mgmtd clients. diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 2f4c956..ad58610 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -9,8 +9,13 @@ described in :rfc:`2740`. .. _ospf6-router: -OSPF6 router -============ +Configuring OSPF6 +***************** + +.. include:: config-include.rst + +Configuration Commands +====================== .. clicmd:: router ospf6 [vrf NAME] @@ -312,10 +317,135 @@ OSPF6 interface Sets interface's Inf-Trans-Delay. Default value is 1. -.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point) +.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point|point-to-multipoint) Set explicitly network type for specified interface. + The only functional difference between ``point-to-point`` (PtP) and + ``point-to-multipoint`` (PtMP) mode is the packet addressing for database + flooding and updates. PtP will use multicast packets while PtMP will + unicast them. Apart from this, + :clicmd:`ipv6 ospf6 p2p-p2mp connected-prefixes ` has a + different default for PtP and PtMP. There are no other differences, in + particular FRR does not impose a limit of one neighbor in PtP mode. + + FRR does not support NBMA mode for IPv6 and likely never will, as NBMA is + considered deprecated for IPv6. Refer to `this IETF OSPF working group + discussion + `_ + for context. + +OSPF6 point-to-point and point-to-multipoint operation +====================================================== + +OSPFv3, by default, operates in broadcast mode where it elects a DR and BDR +for each network segment. This can be changed to point-to-point (PtP) / +point-to-multipoint (PtMP) mode by configuration. The actual physical +interface characteristics do not matter for this setting, all interfaces can +be configured for all modes. However, routers must be configured for the same +mode to form adjacencies. + +The main advantages of PtP/PtMP mode are: + +- no DR/BDR election +- adjacencies can be suppressed in a pairwise manner for any two routers, e.g. + to represent the underlying topology if it isn't a true full mesh +- distinct costs can be set for each pair of routers and direction + +The main downside is less efficient flooding on networks with a large number +of OSPFv3 routers. + +.. warning:: + + All options in this section should be considered "advanced" configuration + options. Inconsistent or nonsensical combinations can easily result in a + non-functional setup. + +.. clicmd:: ipv6 ospf6 p2p-p2mp disable-multicast-hello + + Disables sending normal multicast hellos when in PtP/PtMP mode. Some + vendors do this automatically for PtMP mode while others have a separate + ``no-broadcast`` option matching this. + + If this setting is used, you must issue + :clicmd:`ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)` for each + neighbor to send unicast hello packets. + +.. clicmd:: ipv6 ospf6 p2p-p2mp config-neighbors-only + + Only form adjacencies with neighbors that are explicitly configured with + the :clicmd:`ipv6 ospf6 neighbor X:X::X:X` command. Hellos from other + routers are ignored. + + .. warning:: + + This setting is not intended to provide any security benefit. Do not + run OSPFv3 over untrusted links without additional security measures + (e.g. IPsec.) + +.. clicmd:: ipv6 ospf6 p2p-p2mp connected-prefixes + + For global/ULA prefixes configured on this interfaces, do (not) advertise + the full prefix to the area. Regardless of this setting, the router's own + address, as a /128 host route with the "LA" (Local Address) bit set, will + always be advertised. + + The default is to include connected prefixes for PtP mode and exclude them + for PtMP mode. Since these prefixes will cover other router's addresses, + these addresses can become unreachable if the link is partitioned if the + other router does not advertise the address as a /128. However, conversely, + if all routers have this flag set, the overall prefix will not be advertised + anywhere. End hosts on this link will therefore be unreachable (and + blackholing best-practices for non-existing prefixes apply.) It may be + preferable to have only one router announce the connected prefix. + + The Link LSA (which is not propagated into the area) always includes all + prefixes on the interface. This setting only affects the Router LSA that + is visible to all routers in the area. + + .. note:: + + Before interacting with this setting, consider either not configuring + any global/ULA IPv6 address on the interface, or directly configuring a + /128 if needed. OSPFv3 relies exclusively on link-local addresses to do + its signaling and there is absolutely no reason to configure global/ULA + addresses as far as OSPFv3 is concerned. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X + + Explicitly configure a neighbor by its link-local address on this interface. + This statement has no effect other than allowing an adjacency when + :clicmd:`ipv6 ospf6 p2p-p2mp config-neighbors-only` is set. This command + does **not** cause unicast hellos to be sent. + + Only link-local addresses can be used to establish explicit neighbors. + When using this command, you should probably assign static IPv6 link-local + addresses to all routers on this link. It would technically be possible to + use the neighbor's Router ID (IPv4 address) here to ease working with + changing link-local addresses but this is not planned as a feature at the + time of writing. Global/ULA IPv6 addresses cannot be supported here due to + the way OSPFv3 works. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535) + + Send unicast hellos to this neighbor at the specified interval (in seconds.) + The interval is only used while there is no adjacency with this neighbor. + As soon as an adjacency is formed, the interface's + :clicmd:`ipv6 ospf6 hello-interval HELLOINTERVAL` value is used. + (``hello-interval`` must be the same on all routers on this link.) + + :rfc:`2328` recommends a "much larger" value than ``hello-interval`` for + this setting, but this is a legacy of ATM and X.25 networks and nowadays you + should probably just use the same value as for ``hello-interval``. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X cost (1-65535) + + Use a distinct cost for paths traversing this neighbor. The default is + to use the interface's cost value (which may be automatically calculated + based on link bandwidth.) Note that costs are directional in OSPF and the + reverse direction must be set on the other router. + + OSPF6 route-map =============== diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index d61522b..3bc4487 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -32,8 +32,7 @@ Configuring OSPF Therefore *zebra* must be running before invoking *ospfd*. Also, if *zebra* is restarted then *ospfd* must be too. -Like other daemons, *ospfd* configuration is done in :abbr:`OSPF` specific -configuration file :file:`ospfd.conf` when the integrated config is not used. +.. include:: config-include.rst .. _ospf-multi-instance: @@ -853,7 +852,7 @@ Graceful Restart affects the restarting router. By default 'strict-lsa-checking' is enabled" -.. clicmd:: graceful-restart helper supported-grace-time +.. clicmd:: graceful-restart helper supported-grace-time (10-1800) Supports as HELPER for configured grace period. diff --git a/doc/user/overview.rst b/doc/user/overview.rst index a1cb0cc..2ef88ac 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -428,6 +428,8 @@ BGP :t:`Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages. A. Azimov, E. Bogomazov, R. Bush, K. Patel, K. Sriram. May 2022.` - :rfc:`9384` :t:`A BGP Cease NOTIFICATION Subcode for Bidirectional Forwarding Detection (BFD). J. Haas. March 2023.` +- :rfc:`9494` + :t:`Long-Lived Graceful Restart for BGP. J. Uttaro, E. Chen, B. Decraene, J. Scudder. November 2023.` OSPF ---- diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index ec107fb..2519ac4 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -327,7 +327,7 @@ Configuration Commands Delete or specify a bandwidth constraint for a dynamic candidate path. -.. clicmd:: metric [bound] METRIC VALUE [required] +.. clicmd:: metric [bound] METRIC VALUE [required] [computed] Delete or specify a metric constraint for a dynamic candidate path. @@ -475,6 +475,9 @@ Configuration Commands Specify the maximum SID depth in a PCC definition. +.. clicmd:: no msd [(1-32)] + + Default the maximum SID depth to 4. .. clicmd:: peer WORD [precedence (1-255)] @@ -531,7 +534,7 @@ retrieved via PCEP a random number based name is generated. Display PCC information. -.. clicmd:: show sr-te pcep session [NAME] +.. clicmd:: show sr-te pcep session [NAME] [json] Display the information of a PCEP session, if not name is specified all the sessions will be displayed. diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 7a4effd..6ea153c 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -15,11 +15,7 @@ the default Linux kernel dataplane provider. Starting PBR ============ -Default configuration file for *pbrd* is :file:`pbrd.conf`. The typical -location of :file:`pbrd.conf` is |INSTALL_PREFIX_ETC|/pbrd.conf. - -If FRR is using integrated config, then :file:`pbrd.conf` need not be -present and the :file:`frr.conf` is read instead. +.. include:: config-include.rst .. program:: pbrd diff --git a/doc/user/pim.rst b/doc/user/pim.rst index d70c3c0..80a6a27 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -23,12 +23,11 @@ network for optimizing forwarding of overlay BUM traffic. Starting and Stopping pimd ========================== -The default configuration file name of *pimd*'s is :file:`pimd.conf`. When -invoked *pimd* searches directory |INSTALL_PREFIX_ETC|. If -:file:`pimd.conf` is not there then next search current directory. +.. include:: config-include.rst -*pimd* requires zebra for proper operation. Additionally *pimd* depends on -routing properly setup and working in the network that it is working on. +If starting daemons by hand then please note, *pimd* requires zebra for proper +operation. Additionally *pimd* depends on routing properly setup and working in +the network that it is working on. :: diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst index 8569390..d550c8e 100644 --- a/doc/user/pimv6.rst +++ b/doc/user/pimv6.rst @@ -15,12 +15,11 @@ do S,G mrouting. Starting and Stopping pim6d =========================== -The default configuration file name of *pim6d*'s is :file:`pim6d.conf`. When -invoked *pim6d* searches directory |INSTALL_PREFIX_ETC|. If -:file:`pim6d.conf` is not there then next search current directory. +.. include:: config-include.rst -*pim6d* requires zebra for proper operation. Additionally *pim6d* depends on -routing properly setup and working in the network that it is working on. +If starting daemons by hand then please note, *pim6d* requires zebra for proper +operation. Additionally *pim6d* depends on routing properly setup and working in +the network that it is working on. :: diff --git a/doc/user/prior-config-files.rst b/doc/user/prior-config-files.rst new file mode 100644 index 0000000..a01b688 --- /dev/null +++ b/doc/user/prior-config-files.rst @@ -0,0 +1,23 @@ +.. +.. January 12 2024, Christian Hopps +.. +.. Copyright (c) 2024, LabN Consulting, L.L.C. +.. +.. + +Prior versions of FRR supported reading and writing per-daemon config files; +however, with the introduction of the centralized management daemon ``mgmtd`` +this could no longer be supported. + +In order to allow for an orderly transition from per-daemon config files to the +integrated config file, FRR daemons will continue to try and **read** their +specific per-daemon configuration file as before. Additionally the config can +still be loaded directly using the ``-f`` or ``--config-file`` CLI options; +however, these files will **not** be updated when the configuration is written +(e.g., with the ``write mem`` command). + +.. warning:: + + Per-daemon files will **no longer** be updated when the user issues a ``write + memory`` command. Therefore these per-daemon config files should only be used + as a mechanism for transitioning to the integrated config, and then removed. diff --git a/doc/user/requirements.txt b/doc/user/requirements.txt new file mode 100644 index 0000000..483a4e9 --- /dev/null +++ b/doc/user/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index f9c7724..ea13dc9 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -21,15 +21,15 @@ version 1 as described in RFC1058. Starting and Stopping ripd ========================== -The default configuration file name of *ripd*'s is :file:`ripd.conf`. When -invocation *ripd* searches directory |INSTALL_PREFIX_ETC|. If :file:`ripd.conf` -is not there next search current directory. +.. include:: config-include.rst RIP uses UDP port 520 to send and receive RIP packets. So the user must have the capability to bind the port, generally this means that the user must have -superuser privileges. RIP protocol requires interface information maintained by -*zebra* daemon. So running *zebra* is mandatory to run *ripd*. Thus minimum -sequence for running RIP is like below: +superuser privileges. + +If starting daemons by hand then please note, RIP protocol requires interface +information maintained by *zebra* daemon. So running *zebra* is mandatory to run +*ripd*. Thus minimum sequence for running RIP is like below: :: diff --git a/doc/user/ripngd.rst b/doc/user/ripngd.rst index 1e78294..f898bed 100644 --- a/doc/user/ripngd.rst +++ b/doc/user/ripngd.rst @@ -12,6 +12,8 @@ reincarnation of the RIP protocol. Invoking ripngd =============== +.. include:: config-include.rst + There are no `ripngd` specific invocation options. Common options can be specified (:ref:`common-invocation-options`). diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 3d43e74..791762a 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -185,9 +185,11 @@ Route Map Match Command Matches the specified `local-preference`. -.. clicmd:: match community COMMUNITY_LIST +.. clicmd:: match community COMMUNITY_LIST [] - Matches the specified `community_list` + Matches the specified `community_list`. ``exact-match`` specifies to + do the exact matching of the communities, while ``any`` - can match any + community specified in COMMUNITY_LIST. .. clicmd:: match peer IPV4_ADDR @@ -378,13 +380,13 @@ Route Map Exit Action Command .. clicmd:: on-match next -.. clicmd:: continue - Proceed on to the next entry in the route-map. -.. clicmd:: on-match goto N +.. clicmd:: continue (1-65535) -.. clicmd:: continue N + Proceed to the specified sequence in the route-map. + +.. clicmd:: on-match goto N Proceed processing the route-map at the first entry whose order is >= N diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 4053536..76910ee 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -62,8 +62,9 @@ otherwise ``bgpd`` daemon won't startup. This command enables the RPKI configuration mode. Most commands that start with *rpki* can only be used in this mode. - When it is used in a telnet session, leaving of this mode cause rpki to be - initialized. + This command is available either in *configure node* for default *vrf* or + in *vrf node* for specific *vrf*. When it is used in a telnet session, + leaving of this mode cause rpki to be initialized. Executing this command alone does not activate prefix validation. You need to configure at least one reachable cache server. See section @@ -90,6 +91,9 @@ Examples of the error:: router(config)# rpki % [BGP] Unknown command: rpki + router(config-vrf)# rpki + % [BGP] Unknown command: rpki + Note that the RPKI commands will be available in vtysh when running ``find rpki`` regardless of whether the module is loaded. @@ -98,7 +102,14 @@ Note that the RPKI commands will be available in vtysh when running Configuring RPKI/RTR Cache Servers ---------------------------------- -The following commands are independent of a specific cache server. +RPKI/RTR can be configured independently, either in configure node, or in *vrf* +sub context. If configured in configure node, the core *bgp* instance of default +*vrf* is impacted by the configuration. + +Each RPKI/RTR context is mapped to a *vrf* and can be made up of a specific list +of cache-servers, and specific settings. + +The following commands are available for independent of a specific cache server. .. clicmd:: rpki polling_period (1-3600) @@ -166,7 +177,7 @@ Validating BGP Updates .. code-block:: frr ! Allow for invalid routes in route selection process - route bgp 60001 + route bgp 65001 ! ! Set local preference of invalid prefixes to 10 route-map rpki permit 10 @@ -200,35 +211,39 @@ Debugging Displaying RPKI --------------- -.. clicmd:: show rpki prefix [(1-4294967295)] [json] +.. clicmd:: show rpki configuration [vrf NAME] [json] + + Display RPKI configuration state including timers values. + +.. clicmd:: show rpki prefix [(1-4294967295)] [vrf NAME] [json] Display validated prefixes received from the cache servers filtered by the specified prefix. -.. clicmd:: show rpki as-number ASN [json] +.. clicmd:: show rpki as-number ASN [vrf NAME] [json] Display validated prefixes received from the cache servers filtered by ASN. -.. clicmd:: show rpki prefix-table [json] +.. clicmd:: show rpki prefix-table [vrf NAME] [json] Display all validated prefix to origin AS mappings/records which have been received from the cache servers and stored in the router. Based on this data, the router validates BGP Updates. -.. clicmd:: show rpki cache-server [json] +.. clicmd:: show rpki cache-server [vrf NAME] [json] Display all configured cache servers, whether active or not. -.. clicmd:: show rpki cache-connection [json] +.. clicmd:: show rpki cache-connection [vrf NAME] [json] Display all cache connections, and show which is connected or not. -.. clicmd:: show bgp [afi] [safi] rpki +.. clicmd:: show bgp [vrf NAME] [afi] [safi] rpki Display for the specified prefix or address the bgp paths that match the given rpki state. -.. clicmd:: show bgp [afi] [safi] rpki +.. clicmd:: show bgp [vrf NAME] [afi] [safi] rpki Display all prefixes that match the given rpki state. @@ -244,25 +259,52 @@ RPKI Configuration Example debug bgp keepalives debug rpki ! + vrf VRF1 + rpki + rpki polling_period 1000 + rpki timeout 10 + ! SSH Example: + rpki cache example.com 22 rtr-ssh ./ssh_key/id_rsa preference 1 + ! TCP Example: + rpki cache rpki-validator.realmv6.org 8282 preference 2 + exit + ! + exit-vrf + ! rpki rpki polling_period 1000 rpki timeout 10 ! SSH Example: - rpki cache example.com source 141.22.28.223 22 rtr-ssh ./ssh_key/id_rsa ./ssh_key/id_rsa.pub preference 1 + rpki cache example.com source 198.51.100.223 22 rtr-ssh ./ssh_key/id_rsa preference 1 ! TCP Example: rpki cache rpki-validator.realmv6.org 8282 preference 2 exit ! - router bgp 60001 - bgp router-id 141.22.28.223 - network 192.168.0.0/16 - neighbor 123.123.123.0 remote-as 60002 - neighbor 123.123.123.0 route-map rpki in - neighbor 123.123.123.0 update-source 141.22.28.223 + router bgp 65001 + bgp router-id 198.51.100.223 + neighbor 203.0.113.1 remote-as 65002 + neighbor 203.0.113.1 update-source 198.51.100.223 + address-family ipv4 + network 192.0.2.0/24 + neighbor 203.0.113.1 route-map rpki in + exit-address-family + ! + address-family ipv6 + neighbor 203.0.113.1 activate + neighbor 203.0.113.1 route-map rpki in + exit-address-family + ! + router bgp 65001 vrf VRF1 + bgp router-id 198.51.100.223 + neighbor 203.0.113.1 remote-as 65002 + address-family ipv4 + network 192.0.2.0/24 + neighbor 203.0.113.1 route-map rpki in + exit-address-family ! address-family ipv6 - neighbor 123.123.123.0 activate - neighbor 123.123.123.0 route-map rpki in + neighbor 203.0.113.1 activate + neighbor 203.0.113.1 route-map rpki in exit-address-family ! route-map rpki permit 10 diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 3e73a59..2be38a3 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -13,11 +13,7 @@ labs. Starting SHARP ============== -Default configuration file for *sharpd* is :file:`sharpd.conf`. The typical -location of :file:`sharpd.conf` is |INSTALL_PREFIX_ETC|/sharpd.conf. - -If the user is using integrated config, then :file:`sharpd.conf` need not be -present and the :file:`frr.conf` is read instead. +.. include:: config-include.rst .. program:: sharpd @@ -67,6 +63,11 @@ keyword. At present, no sharp commands will be preserved in the config. Install a label into the kernel that causes the specified vrf NAME table to be used for pop and forward operations when the specified label is seen. +.. clicmd:: sharp watch [vrf VRF_NAME] neighbor + + Instruct zebra to notify sharpd about neighbor events in the specified vrf. + If no vrf is specified then assume default. + .. clicmd:: sharp watch |import [connected] Instruct zebra to monitor and notify sharp when the specified nexthop is diff --git a/doc/user/snmptrap.rst b/doc/user/snmptrap.rst index 7e306b7..df534e2 100644 --- a/doc/user/snmptrap.rst +++ b/doc/user/snmptrap.rst @@ -4,8 +4,9 @@ Handling SNMP Traps To handle snmp traps make sure your snmp setup of frr works correctly as described in the frr documentation in :ref:`snmp-support`. -The BGP4 mib will send traps on peer up/down events. These should be visible in -your snmp logs with a message similar to: +BGP handles both :rfc:`4273` and [Draft-IETF-idr-bgp4-mibv2-11]_ MIBs. +The BGP4 MIBs will send traps on peer up/down events. These should be +visible in your snmp logs with a message similar to: :: @@ -199,3 +200,18 @@ a siren, have your display flash, etc., be creative ;). # mail the notification echo "$MAIL" | mail -s "$SUBJECT" $EMAILADDR + +.. _traps-mib-selection: + +Traps Mib Selection in BGP +-------------------------- + +Both :rfc:`4273` and [Draft-IETF-idr-bgp4-mibv2-11]_ MIBs define traps for +dealing with up/down events and state transition. The user has the +possibility to select the MIB he wants to receive traps from: + +.. clicmd:: bgp snmp traps + +By default, only rfc4273 traps are enabled and sent. + +.. [Draft-IETF-idr-bgp4-mibv2-11] diff --git a/doc/user/static.rst b/doc/user/static.rst index d405276..922c71a 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -12,21 +12,13 @@ of static routes. Starting STATIC =============== -Default configuration file for *staticd* is :file:`staticd.conf`. The typical -location of :file:`staticd.conf` is |INSTALL_PREFIX_ETC|/staticd.conf. - -If the user is using integrated config, then :file:`staticd.conf` need not be -present and the :file:`frr.conf` is read instead. - -If the user has not fully upgraded to using the staticd.conf and still has -a non-integrated config with zebra.conf holding the static routes, *staticd* -will read in the :file:`zebrad.conf` as a backup. - .. program:: staticd :abbr:`STATIC` supports all the common FRR daemon start options which are documented elsewhere. +.. include:: config-include.rst + .. _static-route-commands: Static Route Commands diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst index ef3aebe..d99fc23 100644 --- a/doc/user/vrrp.rst +++ b/doc/user/vrrp.rst @@ -24,11 +24,7 @@ protocol. Starting VRRP ============= -The configuration file for *vrrpd* is :file:`vrrpd.conf`. The typical location -of :file:`vrrpd.conf` is |INSTALL_PREFIX_ETC|/vrrpd.conf. - -If using integrated config, then :file:`vrrpd.conf` need not be present and -:file:`frr.conf` is read instead. +.. include:: config-include.rst .. program:: vrrpd diff --git a/doc/user/vtysh.rst b/doc/user/vtysh.rst index adbdf34..9722231 100644 --- a/doc/user/vtysh.rst +++ b/doc/user/vtysh.rst @@ -131,14 +131,14 @@ could be made SGID (set group ID) to the |INSTALL_VTY_GROUP| group. at all. -.. _integrated-configuration-mode: +.. _integrated-configuration-file: -Integrated configuration mode +Integrated configuration file ============================= -Integrated configuration mode uses a single configuration file, -:file:`frr.conf`, for all daemons. This replaces the individual files like -:file:`zebra.conf` or :file:`bgpd.conf`. +FRR uses a single configuration file, :file:`frr.conf`, for all daemons. This +replaces the individual files like :file:`zebra.conf` or :file:`bgpd.conf` used +in previous versions of the software. :file:`frr.conf` is located in |INSTALL_PREFIX_ETC|. All daemons check for the existence of this file at startup, and if it exists will not load their diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index ba6e3bf..30b0204 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -50,7 +50,8 @@ Besides the common invocation options (:ref:`common-invocation-options`), the When *Zebra* starts with this option, the VRF backend is based on Linux network namespaces. That implies that all network namespaces discovered by ZEBRA will create an associated VRF. The other daemons will operate on the VRF - VRF defined by *Zebra*, as usual. + VRF defined by *Zebra*, as usual. If this option is specified when running + *Zebra*, one must also specify the same option for *mgmtd*. .. seealso:: :ref:`zebra-vrf` @@ -177,16 +178,15 @@ Standard Commands work on non-linux systems at all. 'enable' and 'disable' will respectively turn on and off mpls on the given interface. -.. clicmd:: multicast +.. clicmd:: multicast Enable or disable multicast flag for the interface. -.. clicmd:: bandwidth (1-10000000) +.. clicmd:: bandwidth (1-1000000) - - Set bandwidth value of the interface in kilobits/sec. This is for + Set bandwidth value of the interface in Megabits/sec. This is for calculating OSPF cost. This command does not affect the actual device configuration. @@ -213,20 +213,14 @@ Link Parameters Commands .. clicmd:: link-params + Enter into the link parameters sub node. This command activates the link + parameters and allows to configure routing information that could be used + as part of Traffic Engineering on this interface. MPLS-TE must be enabled at + the OSPF (:ref:`ospf-traffic-engineering`) or ISIS + (:ref:`isis-traffic-engineering`) router level in complement to this. To + disable link parameters, use the ``no`` version of this command. - Enter into the link parameters sub node. At least 'enable' must be - set to activate the link parameters, and consequently routing - information that could be used as part of Traffic Engineering on - this interface. MPLS-TE must be enable at the OSPF - (:ref:`ospf-traffic-engineering`) or ISIS - (:ref:`isis-traffic-engineering`) router level in complement to - this. - - Under link parameter statement, the following commands set the different TE values: - -.. clicmd:: enable - - Enable link parameters for this interface. +Under link parameter statement, the following commands set the different TE values: .. clicmd:: metric (0-4294967295) @@ -252,7 +246,7 @@ Link Parameters Commands as specified in RFC3630 (OSPF) or RFC5305 (ISIS). Admin-group is also known as Resource Class/Color in the OSPF protocol. -.. clicmd:: [no] affinity AFFINITY-MAP-NAME +.. clicmd:: affinity AFFINITY-MAP-NAME This commands configures the Traffic Engineering Admin-Group of the interface using the affinity-map definitions (:ref:`affinity-map`). @@ -263,7 +257,7 @@ Link Parameters Commands ``admin-grp`` and ``affinity`` commands provide two ways of setting admin-groups. They cannot be both set on the same interface. -.. clicmd:: [no] affinity-mode [extended|standard|both] +.. clicmd:: affinity-mode [extended|standard|both] This commands configures which admin-group format is set by the affinity command. ``extended`` Admin-Group is the default and uses the RFC7308 format. @@ -783,9 +777,25 @@ presence of the entry. 21 Static 10.125.0.2 IPv4 Explicit Null +MPLS label chunks +----------------- + +MPLS label chunks are handled in the zebra label manager service, +which ensures a same label value or label chunk can not be used by +multiple CP routing daemons at the same time. + +Label requests originate from CP routing daemons, and are resolved +over the default MPLS range (16-1048575). There are two kind of +requests: +- Static label requests request an exact label value or range. For +instance, segment routing label blocks requests originating from +IS-IS are part of it. +- Dynamic label requests only need a range of label values. The +'bgp l3vpn export auto' command uses such requests. + Allocated label chunks table can be dumped using the command -.. clicmd:: show debugging label-table +.. clicmd:: show debugging label-table [json] :: @@ -796,6 +806,15 @@ Allocated label chunks table can be dumped using the command Proto ospf: [20000/21000] Proto isis: [22000/23000] +.. clicmd:: mpls label dynamic-block (16-1048575) (16-1048575) + + Define a range of labels where dynamic label requests will + allocate label chunks from. This command guarantees that + static label values outside that range will not conflict + with the dynamic label requests. When the dynamic-block + range is configured, static label requests that match that + range are not accepted. + .. _zebra-srv6: Segment-Routing IPv6 @@ -813,6 +832,35 @@ FRR's cli or frr.conf or zebra.conf. This section shows how to configure SRv6 on FRR. Of course SRv6 can be used as standalone, and this section also helps that case. +.. clicmd:: show segment-routing srv6 manager [json] + + This command dumps the SRv6 information configured on zebra, including + the encapsulation parameters (e.g., the IPv6 source address used for + the encapsulated packets). + + Example:: + + router# sh segment-routing srv6 manager + Parameters: + Encapsulation: + Source Address: + Configured: fc00:0:1::1 + + + To get the same information in json format, you can use the ``json`` keyword:: + + rose-srv6# sh segment-routing srv6 manager json + { + "parameters":{ + "encapsulation":{ + "sourceAddress":{ + "configured":"fc00:0:1::1" + } + } + } + } + + .. clicmd:: show segment-routing srv6 locator [json] This command dump SRv6-locator configured on zebra. SRv6-locator is used @@ -973,6 +1021,14 @@ and this section also helps that case. ! ... +.. clicmd:: encapsulation + + Configure parameters for SRv6 encapsulation. + +.. clicmd:: source-address X:X::X:X + + Configure the source address of the outer encapsulating IPv6 header. + .. _multicast-rib-commands: Multicast RIB Commands @@ -1434,8 +1490,6 @@ zebra Terminal Mode Commands .. clicmd:: show ip prefix-list [NAME] -.. clicmd:: show route-map [NAME] - .. clicmd:: show ip protocol .. clicmd:: show ip forward diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 98e8407..1cff06f 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Create a basic stage set up to build APKs -FROM alpine:3.18 as alpine-builder +FROM alpine:3.19 as alpine-builder RUN apk add \ --update-cache \ abuild \ @@ -24,7 +24,7 @@ RUN cd /src/libyang \ && abuild -r -P /pkgs/apk # This stage builds a dist tarball from the source -FROM alpine:3.18 as source-builder +FROM alpine:3.19 as source-builder RUN mkdir -p /src/alpine /pkgs/apk COPY alpine/APKBUILD.in /src/alpine COPY --from=alpine-apk-builder-libyang /pkgs/apk/src /pkgs/apk @@ -33,8 +33,7 @@ RUN source /src/alpine/APKBUILD.in \ && apk add \ --no-cache \ --update-cache \ - $makedepends \ - && pip install pytest + $makedepends COPY . /src ARG PKGVER RUN cd /src \ @@ -58,7 +57,7 @@ RUN cd /dist \ && abuild -r -P /pkgs/apk # This stage installs frr from the apk -FROM alpine:3.18 +FROM alpine:3.19 RUN mkdir -p /pkgs/apk COPY --from=alpine-apk-builder /pkgs/apk/ /pkgs/apk/ RUN apk add \ diff --git a/docker/alpine/libyang/APKBUILD b/docker/alpine/libyang/APKBUILD index 04e943f..6973fd6 100755 --- a/docker/alpine/libyang/APKBUILD +++ b/docker/alpine/libyang/APKBUILD @@ -1,7 +1,7 @@ # Contributor: Sören Tempel # Maintainer: Christian Franke pkgname=libyang -pkgver=2.1.80 +pkgver=2.1.128 pkgrel=0 pkgdesc="YANG data modelling language parser and toolkit" url="https://github.com/CESNET/libyang" diff --git a/docker/ubi8-minimal/Dockerfile b/docker/ubi8-minimal/Dockerfile index 53f8d25..14a5cdd 100644 --- a/docker/ubi8-minimal/Dockerfile +++ b/docker/ubi8-minimal/Dockerfile @@ -10,6 +10,7 @@ ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo # and later reinstall it again: https://bugzilla.redhat.com/show_bug.cgi?id=1668185 RUN rpm --quiet -e --nodeps tzdata >/dev/null 2>&1 +# Keep the list of dependencies alphabetically sorted for easier maintenance RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ autoconf \ automake \ @@ -29,6 +30,7 @@ RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 i pcre-devel \ pkgconfig \ platform-python-devel \ + protobuf-c-devel \ python3-devel \ python3-pytest \ python3-sphinx \ @@ -37,7 +39,6 @@ RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 i systemd-devel \ texinfo \ tzdata \ - protobuf-c-devel \ && microdnf --disableplugin=subscription-manager clean all RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-181/RedHat-8-x86_64-Packages/libyang-2.1.80-1.el8.x86_64.rpm \ @@ -92,12 +93,14 @@ ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8 +# Keep the list of dependencies alphabetically sorted for easier maintenance RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ c-ares \ initscripts \ net-snmp-agent-libs \ net-snmp-libs \ openssl \ + protobuf-c \ python3 \ shadow-utils \ systemd \ diff --git a/docker/ubuntu-ci/Dockerfile b/docker/ubuntu-ci/Dockerfile index 939a43e..5a49806 100644 --- a/docker/ubuntu-ci/Dockerfile +++ b/docker/ubuntu-ci/Dockerfile @@ -41,17 +41,24 @@ RUN apt update && apt upgrade -y && \ cmake \ libpcre2-dev \ && \ + # GRPC extra build requirements + apt-get install -y \ + libgrpc-dev \ + libgrpc++-dev \ + protobuf-compiler-grpc \ + && \ # Runtime/triage/testing requirements apt-get install -y \ curl \ gdb \ + kmod \ iproute2 \ iputils-ping \ liblua5.3-dev \ libssl-dev \ lua5.3 \ net-tools \ - python2 \ + python3 \ python3-pip \ snmp \ snmp-mibs-downloader \ @@ -66,17 +73,12 @@ RUN apt update && apt upgrade -y && \ wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/iana/IANA-IPPM-METRICS-REGISTRY-MIB -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB && \ wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/SNMPv2-PDU -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU && \ wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/IPATM-IPMC-MIB -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB && \ - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \ - python2 /tmp/get-pip.py && \ - rm -f /tmp/get-pip.py && \ python3 -m pip install wheel && \ - python3 -m pip install pytest && \ - python3 -m pip install pytest-sugar && \ - python3 -m pip install pytest-xdist && \ - python3 -m pip install "scapy>=2.4.2" && \ + python3 -m pip install 'protobuf<4' grpcio grpcio-tools && \ + python3 -m pip install 'pytest>=6.2.4' 'pytest-xdist>=2.3.0' && \ + python3 -m pip install 'scapy>=2.4.5' && \ python3 -m pip install xmltodict && \ - python3 -m pip install grpcio grpcio-tools && \ - python2 -m pip install 'exabgp<4.0.0' + python3 -m pip install git+https://github.com/Exa-Networks/exabgp@0659057837cd6c6351579e9f0fa47e9fb7de7311 RUN groupadd -r -g 92 frr && \ groupadd -r -g 85 frrvty && \ @@ -93,7 +95,7 @@ USER frr:frr RUN cd && pwd && ls -al && \ git clone https://github.com/CESNET/libyang.git && \ cd libyang && \ - git checkout v2.1.80 && \ + git checkout v2.1.128 && \ mkdir build; cd build && \ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ -DCMAKE_BUILD_TYPE:String="Release" .. && \ @@ -106,14 +108,15 @@ RUN cd ~/frr && \ ./bootstrap.sh && \ ./configure \ --prefix=/usr \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-sharpd \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ --enable-config-rollbacks \ + --enable-grpc \ --enable-vty-group=frrvty \ --enable-snmp=agentx \ --enable-scripting \ diff --git a/docker/ubuntu20-ci/README.md b/docker/ubuntu20-ci/README.md index 536f8e2..6c0ff44 100644 --- a/docker/ubuntu20-ci/README.md +++ b/docker/ubuntu20-ci/README.md @@ -11,13 +11,13 @@ docker build -t frr-ubuntu20:latest --build-arg=UBUNTU_VERSION=20.04 -f docker/u # Running Full Topotest ``` -docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' +docker run --init -it --privileged --name frr-ubuntu20 -v /lib/modules:/lib/modules frr-ubuntu20:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' ``` # Extract results from the above run into `run-results` dir and analyze ``` -tests/topotest/analyze.py -C frr -Ar run-results +tests/topotests/analyze.py -C frr-ubuntu20 -Ar run-results ``` # Running @@ -41,13 +41,13 @@ docker exec -it frr-ubuntu20 bash # topotest -- when Host O/S is Ubuntu only ``` -docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' +docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py' ``` # stop & remove container ``` -docker stop frr-ubuntu20 ; docker rm frr-ubuntu18 +docker stop frr-ubuntu20 ; docker rm frr-ubuntu20 ``` # remove image diff --git a/docker/ubuntu22-ci/README.md b/docker/ubuntu22-ci/README.md index 403abbf..73f4a10 100644 --- a/docker/ubuntu22-ci/README.md +++ b/docker/ubuntu22-ci/README.md @@ -11,43 +11,43 @@ docker build -t frr-ubuntu22:latest -f docker/ubuntu-ci/Dockerfile . # Running Full Topotest ``` -docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' +docker run --init -it --privileged --name frr-ubuntu22 -v /lib/modules:/lib/modules frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' ``` # Extract results from the above run into `run-results` dir and analyze ``` -tests/topotest/analyze.py -C frr -Ar run-results +tests/topotests/analyze.py -C frr-ubuntu22 -Ar run-results ``` # Running ``` -docker run -d --init --privileged --name frr --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu22:latest +docker run -d --init --privileged --name frr-ubuntu22 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu22:latest ``` # make check ``` -docker exec frr bash -c 'cd ~/frr ; make check' +docker exec frr-ubuntu22 bash -c 'cd ~/frr ; make check' ``` # interactive bash ``` -docker exec -it frr bash +docker exec -it frr-ubuntu22 bash ``` # topotest -- when Host O/S is Ubuntu only ``` -docker exec frr bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' +docker exec frr-ubuntu22 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py' ``` # stop & remove container ``` -docker stop frr ; docker rm frr +docker stop frr-ubuntu22 ; docker rm frr-ubuntu22 ``` # remove image diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c index 213834a..eaede73 100644 --- a/eigrpd/eigrp_cli.c +++ b/eigrpd/eigrp_cli.c @@ -69,8 +69,8 @@ DEFPY_YANG( void eigrp_cli_show_header(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *asn = yang_dnode_get_string(dnode, "./asn"); - const char *vrf = yang_dnode_get_string(dnode, "./vrf"); + const char *asn = yang_dnode_get_string(dnode, "asn"); + const char *vrf = yang_dnode_get_string(dnode, "vrf"); vty_out(vty, "router eigrp %s", asn); if (strcmp(vrf, VRF_DEFAULT_NAME)) @@ -131,12 +131,14 @@ DEFPY_YANG( "Suppress routing updates on an interface\n" "Interface to suppress on\n") { + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./passive-interface[.='%s']", ifname); + if (no) - nb_cli_enqueue_change(vty, "./passive-interface", - NB_OP_DESTROY, ifname); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); else - nb_cli_enqueue_change(vty, "./passive-interface", - NB_OP_CREATE, ifname); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -323,18 +325,18 @@ void eigrp_cli_show_metrics(struct vty *vty, const struct lyd_node *dnode, { const char *k1, *k2, *k3, *k4, *k5, *k6; - k1 = yang_dnode_exists(dnode, "./K1") ? - yang_dnode_get_string(dnode, "./K1") : "0"; - k2 = yang_dnode_exists(dnode, "./K2") ? - yang_dnode_get_string(dnode, "./K2") : "0"; - k3 = yang_dnode_exists(dnode, "./K3") ? - yang_dnode_get_string(dnode, "./K3") : "0"; - k4 = yang_dnode_exists(dnode, "./K4") ? - yang_dnode_get_string(dnode, "./K4") : "0"; - k5 = yang_dnode_exists(dnode, "./K5") ? - yang_dnode_get_string(dnode, "./K5") : "0"; - k6 = yang_dnode_exists(dnode, "./K6") ? - yang_dnode_get_string(dnode, "./K6") : "0"; + k1 = yang_dnode_exists(dnode, "K1") ? + yang_dnode_get_string(dnode, "K1") : "0"; + k2 = yang_dnode_exists(dnode, "K2") ? + yang_dnode_get_string(dnode, "K2") : "0"; + k3 = yang_dnode_exists(dnode, "K3") ? + yang_dnode_get_string(dnode, "K3") : "0"; + k4 = yang_dnode_exists(dnode, "K4") ? + yang_dnode_get_string(dnode, "K4") : "0"; + k5 = yang_dnode_exists(dnode, "K5") ? + yang_dnode_get_string(dnode, "K5") : "0"; + k6 = yang_dnode_exists(dnode, "K6") ? + yang_dnode_get_string(dnode, "K6") : "0"; vty_out(vty, " metric weights %s %s %s %s %s", k1, k2, k3, k4, k5); @@ -354,12 +356,14 @@ DEFPY_YANG( "Enable routing on an IP network\n" "EIGRP network prefix\n") { + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./network[.='%s']", prefix_str); + if (no) - nb_cli_enqueue_change(vty, "./network", NB_OP_DESTROY, - prefix_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); else - nb_cli_enqueue_change(vty, "./network", NB_OP_CREATE, - prefix_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -383,12 +387,14 @@ DEFPY_YANG( "Specify a neighbor router\n" "Neighbor address\n") { + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./neighbor[.='%s']", addr_str); + if (no) - nb_cli_enqueue_change(vty, "./neighbor", NB_OP_DESTROY, - addr_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); else - nb_cli_enqueue_change(vty, "./neighbor", NB_OP_CREATE, - addr_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -401,6 +407,117 @@ void eigrp_cli_show_neighbor(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, " neighbor %s\n", prefix); } +/* + * XPath: /frr-eigrpd:eigrpd/instance/distribute-list + */ +DEFPY_YANG (eigrp_distribute_list, + eigrp_distribute_list_cmd, + "distribute-list ACCESSLIST4_NAME$name $dir [WORD$ifname]", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (eigrp_distribute_list_prefix, + eigrp_distribute_list_prefix_cmd, + "distribute-list prefix PREFIXLIST4_NAME$name $dir [WORD$ifname]", + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (eigrp_no_distribute_list, + eigrp_no_distribute_list_cmd, + "no distribute-list [ACCESSLIST4_NAME$name] $dir [WORD$ifname]", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (eigrp_no_distribute_list_prefix, + eigrp_no_distribute_list_prefix_cmd, + "no distribute-list prefix [PREFIXLIST4_NAME$name] $dir [WORD$ifname]", + NO_STR + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + /* * XPath: /frr-eigrpd:eigrpd/instance/redistribute * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map @@ -456,19 +573,19 @@ DEFPY_YANG( void eigrp_cli_show_redistribute(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *proto = yang_dnode_get_string(dnode, "./protocol"); + const char *proto = yang_dnode_get_string(dnode, "protocol"); const char *bw, *delay, *load, *mtu, *rlbt; - bw = yang_dnode_exists(dnode, "./metrics/bandwidth") ? - yang_dnode_get_string(dnode, "./metrics/bandwidth") : NULL; - delay = yang_dnode_exists(dnode, "./metrics/delay") ? - yang_dnode_get_string(dnode, "./metrics/delay") : NULL; - rlbt = yang_dnode_exists(dnode, "./metrics/reliability") ? - yang_dnode_get_string(dnode, "./metrics/reliability") : NULL; - load = yang_dnode_exists(dnode, "./metrics/load") ? - yang_dnode_get_string(dnode, "./metrics/load") : NULL; - mtu = yang_dnode_exists(dnode, "./metrics/mtu") ? - yang_dnode_get_string(dnode, "./metrics/mtu") : NULL; + bw = yang_dnode_exists(dnode, "metrics/bandwidth") ? + yang_dnode_get_string(dnode, "metrics/bandwidth") : NULL; + delay = yang_dnode_exists(dnode, "metrics/delay") ? + yang_dnode_get_string(dnode, "metrics/delay") : NULL; + rlbt = yang_dnode_exists(dnode, "metrics/reliability") ? + yang_dnode_get_string(dnode, "metrics/reliability") : NULL; + load = yang_dnode_exists(dnode, "metrics/load") ? + yang_dnode_get_string(dnode, "metrics/load") : NULL; + mtu = yang_dnode_exists(dnode, "metrics/mtu") ? + yang_dnode_get_string(dnode, "metrics/mtu") : NULL; vty_out(vty, " redistribute %s", proto); if (bw || rlbt || delay || load || mtu) @@ -658,8 +775,9 @@ DEFPY_YANG( as_str); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); - nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, prefix_str); + snprintf(xpath_auth, sizeof(xpath_auth), + "%s/summarize-addresses[.='%s']", xpath, prefix_str); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -681,8 +799,9 @@ DEFPY_YANG( as_str); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); - nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, prefix_str); + snprintf(xpath_auth, sizeof(xpath_auth), + "%s/summarize-addresses[.='%s']", xpath, prefix_str); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -693,7 +812,7 @@ void eigrp_cli_show_summarize_address(struct vty *vty, { const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); - uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + uint16_t asn = yang_dnode_get_uint16(instance, "asn"); const char *summarize_address = yang_dnode_get_string(dnode, NULL); vty_out(vty, " ip summary-address eigrp %d %s\n", asn, @@ -759,7 +878,7 @@ void eigrp_cli_show_authentication(struct vty *vty, { const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); - uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + uint16_t asn = yang_dnode_get_uint16(instance, "asn"); const char *crypt = yang_dnode_get_string(dnode, NULL); vty_out(vty, " ip authentication mode eigrp %d %s\n", asn, crypt); @@ -819,7 +938,7 @@ void eigrp_cli_show_keychain(struct vty *vty, const struct lyd_node *dnode, { const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); - uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + uint16_t asn = yang_dnode_get_uint16(instance, "asn"); const char *keychain = yang_dnode_get_string(dnode, NULL); vty_out(vty, " ip authentication key-chain eigrp %d %s\n", asn, @@ -875,6 +994,10 @@ eigrp_cli_init(void) install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd); install_element(EIGRP_NODE, &eigrp_network_cmd); install_element(EIGRP_NODE, &eigrp_neighbor_cmd); + install_element(EIGRP_NODE, &eigrp_distribute_list_cmd); + install_element(EIGRP_NODE, &eigrp_distribute_list_prefix_cmd); + install_element(EIGRP_NODE, &eigrp_no_distribute_list_cmd); + install_element(EIGRP_NODE, &eigrp_no_distribute_list_prefix_cmd); install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); vrf_cmd_init(NULL); diff --git a/eigrpd/eigrp_const.h b/eigrpd/eigrp_const.h index 607719d..05fbae3 100644 --- a/eigrpd/eigrp_const.h +++ b/eigrpd/eigrp_const.h @@ -61,9 +61,6 @@ /* IP TTL for EIGRP protocol. */ #define EIGRP_IP_TTL 1 -/* VTY port number. */ -#define EIGRP_VTY_PORT 2613 - /* Default configuration file name for eigrp. */ #define EIGRP_DEFAULT_CONFIG "eigrpd.conf" diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index 7bb1617..fb8f47e 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -202,8 +202,10 @@ struct list *eigrp_iflist; void eigrp_if_init(void) { - if_zapi_callbacks(eigrp_ifp_create, eigrp_ifp_up, - eigrp_ifp_down, eigrp_ifp_destroy); + hook_register_prio(if_real, 0, eigrp_ifp_create); + hook_register_prio(if_up, 0, eigrp_ifp_up); + hook_register_prio(if_down, 0, eigrp_ifp_down); + hook_register_prio(if_unreal, 0, eigrp_ifp_destroy); /* Initialize Zebra interface data structure. */ // hook_register_prio(if_add, 0, eigrp_if_new); hook_register_prio(if_del, 0, eigrp_if_delete_hook); diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index 6d7443b..552ba7c 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -96,6 +96,8 @@ static void sigint(void) zlog_notice("Terminating on signal"); eigrp_terminate(); + keychain_terminate(); + exit(0); } @@ -132,16 +134,20 @@ static const struct frr_yang_module_info *const eigrpd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(eigrpd, EIGRP, .vty_port = EIGRP_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(eigrpd, EIGRP, + .vty_port = EIGRP_VTY_PORT, + .proghelp = "Implementation of the EIGRP routing protocol.", - .proghelp = "Implementation of the EIGRP routing protocol.", + .signals = eigrp_signals, + .n_signals = array_size(eigrp_signals), - .signals = eigrp_signals, - .n_signals = array_size(eigrp_signals), + .privs = &eigrpd_privs, - .privs = &eigrpd_privs, .yang_modules = eigrpd_yang_modules, - .n_yang_modules = array_size(eigrpd_yang_modules), + .yang_modules = eigrpd_yang_modules, + .n_yang_modules = array_size(eigrpd_yang_modules), ); +/* clang-format on */ /* EIGRPd main routine. */ int main(int argc, char **argv, char **envp) diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c index ef567fe..5ca5a18 100644 --- a/eigrpd/eigrp_network.c +++ b/eigrpd/eigrp_network.c @@ -233,13 +233,11 @@ static void eigrp_network_run_interface(struct eigrp *eigrp, struct prefix *p, struct interface *ifp) { struct eigrp_interface *ei; - struct listnode *cnode; struct connected *co; /* if interface prefix is match specified prefix, then create socket and join multicast group. */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) { - + frr_each (if_connected, ifp->connected, co) { if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY)) continue; diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index f50abb7..4aeb635 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -14,6 +14,7 @@ #include "lib/table.h" #include "lib/vrf.h" #include "lib/zclient.h" +#include "lib/distribute.h" #include "eigrp_structs.h" #include "eigrpd.h" @@ -28,18 +29,18 @@ static void redistribute_get_metrics(const struct lyd_node *dnode, { memset(em, 0, sizeof(*em)); - if (yang_dnode_exists(dnode, "./bandwidth")) - em->bandwidth = yang_dnode_get_uint32(dnode, "./bandwidth"); - if (yang_dnode_exists(dnode, "./delay")) - em->delay = yang_dnode_get_uint32(dnode, "./delay"); + if (yang_dnode_exists(dnode, "bandwidth")) + em->bandwidth = yang_dnode_get_uint32(dnode, "bandwidth"); + if (yang_dnode_exists(dnode, "delay")) + em->delay = yang_dnode_get_uint32(dnode, "delay"); #if 0 /* TODO: How does MTU work? */ - if (yang_dnode_exists(dnode, "./mtu")) - em->mtu[0] = yang_dnode_get_uint32(dnode, "./mtu"); + if (yang_dnode_exists(dnode, "mtu")) + em->mtu[0] = yang_dnode_get_uint32(dnode, "mtu"); #endif - if (yang_dnode_exists(dnode, "./load")) - em->load = yang_dnode_get_uint32(dnode, "./load"); - if (yang_dnode_exists(dnode, "./reliability")) - em->reliability = yang_dnode_get_uint32(dnode, "./reliability"); + if (yang_dnode_exists(dnode, "load")) + em->load = yang_dnode_get_uint32(dnode, "load"); + if (yang_dnode_exists(dnode, "reliability")) + em->reliability = yang_dnode_get_uint32(dnode, "reliability"); } static struct eigrp_interface *eigrp_interface_lookup(const struct eigrp *eigrp, @@ -73,7 +74,7 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) /* NOTHING */ break; case NB_EV_PREPARE: - vrf = yang_dnode_get_string(args->dnode, "./vrf"); + vrf = yang_dnode_get_string(args->dnode, "vrf"); pVrf = vrf_lookup_by_name(vrf); if (pVrf) @@ -81,7 +82,7 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) else vrfid = VRF_DEFAULT; - eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "asn"), vrfid); args->resource->ptr = eigrp; break; @@ -701,6 +702,22 @@ static int eigrpd_instance_neighbor_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +/* + * XPath: /frr-eigrpd:eigrpd/instance/distribute-list + */ +static int eigrpd_instance_distribute_list_create(struct nb_cb_create_args *args) +{ + struct eigrp *eigrp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + eigrp = nb_running_get_entry(args->dnode, NULL, true); + group_distribute_list_create_helper(args, eigrp->distribute_ctx); + + return NB_OK; +} + /* * XPath: /frr-eigrpd:eigrpd/instance/redistribute */ @@ -715,7 +732,7 @@ static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) switch (args->event) { case NB_EV_VALIDATE: - proto = yang_dnode_get_enum(args->dnode, "./protocol"); + proto = yang_dnode_get_enum(args->dnode, "protocol"); vrfname = yang_dnode_get_string(args->dnode, "../vrf"); pVrf = vrf_lookup_by_name(vrfname); @@ -733,7 +750,7 @@ static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) break; case NB_EV_APPLY: eigrp = nb_running_get_entry(args->dnode, NULL, true); - proto = yang_dnode_get_enum(args->dnode, "./protocol"); + proto = yang_dnode_get_enum(args->dnode, "protocol"); redistribute_get_metrics(args->dnode, &metrics); eigrp_redistribute_set(eigrp, proto, metrics); break; @@ -755,7 +772,7 @@ static int eigrpd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: eigrp = nb_running_get_entry(args->dnode, NULL, true); - proto = yang_dnode_get_enum(args->dnode, "./protocol"); + proto = yang_dnode_get_enum(args->dnode, "protocol"); eigrp_redistribute_unset(eigrp, proto); break; } @@ -1120,7 +1137,7 @@ static int lib_interface_eigrp_instance_create(struct nb_cb_create_args *args) break; } - eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "asn"), ifp->vrf->vrf_id); eif = eigrp_interface_lookup(eigrp, ifp->name); if (eif == NULL) @@ -1132,7 +1149,7 @@ static int lib_interface_eigrp_instance_create(struct nb_cb_create_args *args) break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "asn"), ifp->vrf->vrf_id); eif = eigrp_interface_lookup(eigrp, ifp->name); if (eif == NULL) @@ -1402,6 +1419,45 @@ const struct frr_yang_module_info frr_eigrpd_info = { .cli_show = eigrp_cli_show_neighbor, } }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/distribute-list", + .cbs = { + .create = eigrpd_instance_distribute_list_create, + .destroy = group_distribute_list_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/distribute-list/in/access-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/distribute-list/out/access-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/distribute-list/in/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/distribute-list/out/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, { .xpath = "/frr-eigrpd:eigrpd/instance/redistribute", .cbs = { diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index 84f27d0..420cb6c 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -1107,60 +1107,14 @@ ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag (0-65535)", NO_STR SET_STR "Tag value for routing protocol\n" "Tag value\n") -DEFUN (eigrp_distribute_list, - eigrp_distribute_list_cmd, - "distribute-list [prefix] ACCESSLIST_NAME [WORD]", - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - const char *ifname = NULL; - int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0; - - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; - - return distribute_list_parser(prefix, true, argv[2 + prefix]->text, - argv[1 + prefix]->arg, ifname); -} - -DEFUN (eigrp_no_distribute_list, - eigrp_no_distribute_list_cmd, - "no distribute-list [prefix] ACCESSLIST_NAME [WORD]", - NO_STR - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - const char *ifname = NULL; - int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0; - - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; - - return distribute_list_no_parser(vty, prefix, true, - argv[3 + prefix]->text, - argv[2 + prefix]->arg, ifname); -} - - /* Route-map init */ -void eigrp_route_map_init() +void eigrp_route_map_init(void) { route_map_init(); route_map_init_vty(); route_map_add_hook(eigrp_route_map_update); route_map_delete_hook(eigrp_route_map_update); - install_element(EIGRP_NODE, &eigrp_distribute_list_cmd); - install_element(EIGRP_NODE, &eigrp_no_distribute_list_cmd); - /*route_map_install_match (&route_match_metric_cmd); route_map_install_match (&route_match_interface_cmd);*/ /*route_map_install_match (&route_match_ip_next_hop_cmd); diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index a5cecb9..a0eff68 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -98,9 +98,7 @@ static zclient_handler *const eigrp_handlers[] = { void eigrp_zebra_init(void) { - struct zclient_options opt = {.receive_notify = false}; - - zclient = zclient_new(master, &opt, eigrp_handlers, + zclient = zclient_new(master, &zclient_options_default, eigrp_handlers, array_size(eigrp_handlers)); zclient_init(zclient, ZEBRA_ROUTE_EIGRP, 0, &eigrpd_privs); diff --git a/fpm/fpm.h b/fpm/fpm.h index b08310f..70c0df5 100644 --- a/fpm/fpm.h +++ b/fpm/fpm.h @@ -62,11 +62,6 @@ */ #define FPM_DEFAULT_IP (htonl (INADDR_LOOPBACK)) -/* - * default port for fpm connections - */ -#define FPM_DEFAULT_PORT 2620 - /* * Largest message that can be sent to or received from the FPM. */ diff --git a/gdb/lib.txt b/gdb/lib.txt index 5d22321..435ec7e 100644 --- a/gdb/lib.txt +++ b/gdb/lib.txt @@ -306,8 +306,9 @@ define mq_walk end set $mg = $mg->next end +end -document mg_walk +document mq_walk Walk the memory data structures to show what is holding memory. Arguments: @@ -315,3 +316,49 @@ Arguments: sure where to start pass it mg_first, which is a global DS for all memory allocated in FRR end + +define __darr_meta + set $_ = ((struct darr_metadata *)$arg0) - 1 +end +document __darr_meta +Store a pointer to the struct darr_metadata in $_ for the given dynamic array. + +Argument: a pointer to a darr dynamic array. +Returns: pointer to the struct darr_metadata in $_. +end + +define darr_meta + __darr_meta $arg0 + p *$_ +end +document darr_meta +Print the struct darr_metadata for the given dynamic array. Store the value +in $_ as well. + +Argument: a pointer to a darr dynamic array. +Returns: pointer to the struct darr_metadata in $_. +end + +define darr_len + __darr_meta $arg0 + set $_ = $_->len + p $_ +end +document darr_len +Print the length of the given dynamic array, and store in $_. + +Argument: a pointer to a darr dynamic array. +Returns: length of the array. +end + +define darr_cap + __darr_meta $arg0 + set $_ = $_->cap + p $_ +end +document darr_len +Print the capacity of the given dynamic array, and store in $_. + +Argument: a pointer to a darr dynamic array. +Returns: capacity of the array. +end diff --git a/include/linux/mroute.h b/include/linux/mroute.h deleted file mode 100644 index a0bfdcb..0000000 --- a/include/linux/mroute.h +++ /dev/null @@ -1,193 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _UAPI__LINUX_MROUTE_H -#define _UAPI__LINUX_MROUTE_H - -#ifdef __LINUX__ -#include -#include -#endif -#include /* For struct in_addr. */ - -/* Based on the MROUTING 3.5 defines primarily to keep - * source compatibility with BSD. - * - * See the mrouted code for the original history. - * - * Protocol Independent Multicast (PIM) data structures included - * Carlos Picoto (cap@di.fc.ul.pt) - */ - -#define MRT_BASE 200 -#define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */ -#define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */ -#define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */ -#define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */ -#define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */ -#define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */ -#define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */ -#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ -#define MRT_PIM (MRT_BASE+8) /* enable PIM code */ -#define MRT_TABLE (MRT_BASE+9) /* Specify mroute table ID */ -#define MRT_ADD_MFC_PROXY (MRT_BASE+10) /* Add a (*,*|G) mfc entry */ -#define MRT_DEL_MFC_PROXY (MRT_BASE+11) /* Del a (*,*|G) mfc entry */ -#define MRT_FLUSH (MRT_BASE+12) /* Flush all mfc entries and/or vifs */ -#define MRT_MAX (MRT_BASE+12) - -#ifndef SIOCGETVIFCNT -#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ -#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) -#define SIOCGETRPF (SIOCPROTOPRIVATE+2) -#endif - -#ifndef MAXVIFS -#define MAXVIFS 32 -#endif -/* MRT_FLUSH optional flags */ -#define MRT_FLUSH_MFC 1 /* Flush multicast entries */ -#define MRT_FLUSH_MFC_STATIC 2 /* Flush static multicast entries */ -#define MRT_FLUSH_VIFS 4 /* Flush multicast vifs */ -#define MRT_FLUSH_VIFS_STATIC 8 /* Flush static multicast vifs */ - -typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */ -typedef unsigned short vifi_t; -#define ALL_VIFS ((vifi_t)(-1)) - -/* Same idea as select */ - -#define VIFM_SET(n,m) ((m)|=(1<<(n))) -#define VIFM_CLR(n,m) ((m)&=~(1<<(n))) -#define VIFM_ISSET(n,m) ((m)&(1<<(n))) -#define VIFM_CLRALL(m) ((m)=0) -#define VIFM_COPY(mfrom,mto) ((mto)=(mfrom)) -#define VIFM_SAME(m1,m2) ((m1)==(m2)) - -/* Passed by mrouted for an MRT_ADD_VIF - again we use the - * mrouted 3.6 structures for compatibility - */ -struct vifctl { - vifi_t vifc_vifi; /* Index of VIF */ - unsigned char vifc_flags; /* VIFF_ flags */ - unsigned char vifc_threshold; /* ttl limit */ - unsigned int vifc_rate_limit; /* Rate limiter values (NI) */ - union { - struct in_addr vifc_lcl_addr; /* Local interface address */ - int vifc_lcl_ifindex; /* Local interface index */ - }; - struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */ -}; - -#define VIFF_TUNNEL 0x1 /* IPIP tunnel */ -#define VIFF_SRCRT 0x2 /* NI */ -#define VIFF_REGISTER 0x4 /* register vif */ -#define VIFF_USE_IFINDEX 0x8 /* use vifc_lcl_ifindex instead of - vifc_lcl_addr to find an interface */ - -/* Cache manipulation structures for mrouted and PIMd */ -struct mfcctl { - struct in_addr mfcc_origin; /* Origin of mcast */ - struct in_addr mfcc_mcastgrp; /* Group in question */ - vifi_t mfcc_parent; /* Where it arrived */ - unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */ - unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */ - unsigned int mfcc_byte_cnt; - unsigned int mfcc_wrong_if; - int mfcc_expire; -}; - -/* Group count retrieval for mrouted */ -struct sioc_sg_req { - struct in_addr src; - struct in_addr grp; - unsigned long pktcnt; - unsigned long bytecnt; - unsigned long wrong_if; -}; - -/* To get vif packet counts */ -struct sioc_vif_req { - vifi_t vifi; /* Which iface */ - unsigned long icount; /* In packets */ - unsigned long ocount; /* Out packets */ - unsigned long ibytes; /* In bytes */ - unsigned long obytes; /* Out bytes */ -}; - -/* This is the format the mroute daemon expects to see IGMP control - * data. Magically happens to be like an IP packet as per the original - */ -struct igmpmsg { - uint32_t unused1,unused2; - unsigned char im_msgtype; /* What is this */ - unsigned char im_mbz; /* Must be zero */ - unsigned char im_vif; /* Low 8 bits of Interface */ - unsigned char im_vif_hi; /* High 8 bits of Interface */ - struct in_addr im_src,im_dst; -}; - -/* ipmr netlink table attributes */ -enum { - IPMRA_TABLE_UNSPEC, - IPMRA_TABLE_ID, - IPMRA_TABLE_CACHE_RES_QUEUE_LEN, - IPMRA_TABLE_MROUTE_REG_VIF_NUM, - IPMRA_TABLE_MROUTE_DO_ASSERT, - IPMRA_TABLE_MROUTE_DO_PIM, - IPMRA_TABLE_VIFS, - IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE, - __IPMRA_TABLE_MAX -}; -#define IPMRA_TABLE_MAX (__IPMRA_TABLE_MAX - 1) - -/* ipmr netlink vif attribute format - * [ IPMRA_TABLE_VIFS ] - nested attribute - * [ IPMRA_VIF ] - nested attribute - * [ IPMRA_VIFA_xxx ] - */ -enum { - IPMRA_VIF_UNSPEC, - IPMRA_VIF, - __IPMRA_VIF_MAX -}; -#define IPMRA_VIF_MAX (__IPMRA_VIF_MAX - 1) - -/* vif-specific attributes */ -enum { - IPMRA_VIFA_UNSPEC, - IPMRA_VIFA_IFINDEX, - IPMRA_VIFA_VIF_ID, - IPMRA_VIFA_FLAGS, - IPMRA_VIFA_BYTES_IN, - IPMRA_VIFA_BYTES_OUT, - IPMRA_VIFA_PACKETS_IN, - IPMRA_VIFA_PACKETS_OUT, - IPMRA_VIFA_LOCAL_ADDR, - IPMRA_VIFA_REMOTE_ADDR, - IPMRA_VIFA_PAD, - __IPMRA_VIFA_MAX -}; -#define IPMRA_VIFA_MAX (__IPMRA_VIFA_MAX - 1) - -/* ipmr netlink cache report attributes */ -enum { - IPMRA_CREPORT_UNSPEC, - IPMRA_CREPORT_MSGTYPE, - IPMRA_CREPORT_VIF_ID, - IPMRA_CREPORT_SRC_ADDR, - IPMRA_CREPORT_DST_ADDR, - IPMRA_CREPORT_PKT, - IPMRA_CREPORT_TABLE, - __IPMRA_CREPORT_MAX -}; -#define IPMRA_CREPORT_MAX (__IPMRA_CREPORT_MAX - 1) - -/* That's all usermode folks */ - -#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */ - -/* Pseudo messages used by mrouted */ -#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */ -#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */ -#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */ -#define IGMPMSG_WRVIFWHOLE 4 /* For PIM Register and assert processing */ - -#endif /* _UAPI__LINUX_MROUTE_H */ diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h deleted file mode 100644 index 8c06406..0000000 --- a/include/linux/mroute6.h +++ /dev/null @@ -1,164 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _UAPI__LINUX_MROUTE6_H -#define _UAPI__LINUX_MROUTE6_H - -#ifdef __LINUX__ -#include -#include -#include -#endif -#include /* For struct sockaddr_in6. */ - -/* - * Based on the MROUTING 3.5 defines primarily to keep - * source compatibility with BSD. - * - * See the pim6sd code for the original history. - * - * Protocol Independent Multicast (PIM) data structures included - * Carlos Picoto (cap@di.fc.ul.pt) - * - */ - -#define MRT6_BASE 200 -#define MRT6_INIT (MRT6_BASE) /* Activate the kernel mroute code */ -#define MRT6_DONE (MRT6_BASE+1) /* Shutdown the kernel mroute */ -#define MRT6_ADD_MIF (MRT6_BASE+2) /* Add a virtual interface */ -#define MRT6_DEL_MIF (MRT6_BASE+3) /* Delete a virtual interface */ -#define MRT6_ADD_MFC (MRT6_BASE+4) /* Add a multicast forwarding entry */ -#define MRT6_DEL_MFC (MRT6_BASE+5) /* Delete a multicast forwarding entry */ -#define MRT6_VERSION (MRT6_BASE+6) /* Get the kernel multicast version */ -#define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */ -#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */ -#define MRT6_TABLE (MRT6_BASE+9) /* Specify mroute table ID */ -#define MRT6_ADD_MFC_PROXY (MRT6_BASE+10) /* Add a (*,*|G) mfc entry */ -#define MRT6_DEL_MFC_PROXY (MRT6_BASE+11) /* Del a (*,*|G) mfc entry */ -#define MRT6_FLUSH (MRT6_BASE+12) /* Flush all mfc entries and/or vifs */ -#define MRT6_MAX (MRT6_BASE+12) - -#ifndef SIOCGETMIFCNT_IN6 -#define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */ -#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1) -#define SIOCGETRPF (SIOCPROTOPRIVATE+2) -#endif - -#ifndef MAXMIFS -#define MAXMIFS 32 -#endif -/* MRT6_FLUSH optional flags */ -#define MRT6_FLUSH_MFC 1 /* Flush multicast entries */ -#define MRT6_FLUSH_MFC_STATIC 2 /* Flush static multicast entries */ -#define MRT6_FLUSH_MIFS 4 /* Flushing multicast vifs */ -#define MRT6_FLUSH_MIFS_STATIC 8 /* Flush static multicast vifs */ - -typedef unsigned long mifbitmap_t; /* User mode code depends on this lot */ -typedef unsigned short mifi_t; -#define ALL_MIFS ((mifi_t)(-1)) - -#ifndef IF_SETSIZE -#define IF_SETSIZE 256 -#endif - -typedef uint32_t if_mask; -#define NIFBITS (sizeof(if_mask) * 8) /* bits per mask */ - -typedef struct if_set { - /* __KERNEL_DIV_ROUND_UP() */ - if_mask ifs_bits[(IF_SETSIZE + NIFBITS - 1) / NIFBITS]; -} if_set; - -#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) -#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS))) -#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS))) -#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f))) -#define IF_ZERO(p) bzero(p, sizeof(*(p))) - -/* - * Passed by mrouted for an MRT_ADD_MIF - again we use the - * mrouted 3.6 structures for compatibility - */ - -struct mif6ctl { - mifi_t mif6c_mifi; /* Index of MIF */ - unsigned char mif6c_flags; /* MIFF_ flags */ - unsigned char vifc_threshold; /* ttl limit */ - __u16 mif6c_pifi; /* the index of the physical IF */ - unsigned int vifc_rate_limit; /* Rate limiter values (NI) */ -}; - -#define MIFF_REGISTER 0x1 /* register vif */ - -/* - * Cache manipulation structures for mrouted and PIMd - */ - -struct mf6cctl { - struct sockaddr_in6 mf6cc_origin; /* Origin of mcast */ - struct sockaddr_in6 mf6cc_mcastgrp; /* Group in question */ - mifi_t mf6cc_parent; /* Where it arrived */ - struct if_set mf6cc_ifset; /* Where it is going */ -}; - -/* - * Group count retrieval for pim6sd - */ - -struct sioc_sg_req6 { - struct sockaddr_in6 src; - struct sockaddr_in6 grp; - unsigned long pktcnt; - unsigned long bytecnt; - unsigned long wrong_if; -}; - -/* - * To get vif packet counts - */ - -struct sioc_mif_req6 { - mifi_t mifi; /* Which iface */ - unsigned long icount; /* In packets */ - unsigned long ocount; /* Out packets */ - unsigned long ibytes; /* In bytes */ - unsigned long obytes; /* Out bytes */ -}; - -/* - * That's all usermode folks - */ - - - -/* - * Structure used to communicate from kernel to multicast router. - * We'll overlay the structure onto an MLD header (not an IPv6 heder like igmpmsg{} - * used for IPv4 implementation). This is because this structure will be passed via an - * IPv6 raw socket, on which an application will only receiver the payload i.e the data after - * the IPv6 header and all the extension headers. (See section 3 of RFC 3542) - */ - -struct mrt6msg { -#define MRT6MSG_NOCACHE 1 -#define MRT6MSG_WRONGMIF 2 -#define MRT6MSG_WHOLEPKT 3 /* used for use level encap */ -#define MRT6MSG_WRMIFWHOLE 4 /* For PIM Register and assert processing */ - __u8 im6_mbz; /* must be zero */ - __u8 im6_msgtype; /* what type of message */ - __u16 im6_mif; /* mif rec'd on */ - __u32 im6_pad; /* padding for 64 bit arch */ - struct in6_addr im6_src, im6_dst; -}; - -/* ip6mr netlink cache report attributes */ -enum { - IP6MRA_CREPORT_UNSPEC, - IP6MRA_CREPORT_MSGTYPE, - IP6MRA_CREPORT_MIF_ID, - IP6MRA_CREPORT_SRC_ADDR, - IP6MRA_CREPORT_DST_ADDR, - IP6MRA_CREPORT_PKT, - __IP6MRA_CREPORT_MAX -}; -#define IP6MRA_CREPORT_MAX (__IP6MRA_CREPORT_MAX - 1) - -#endif /* _UAPI__LINUX_MROUTE6_H */ diff --git a/include/subdir.am b/include/subdir.am index 4fa88a0..6f07b6b 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -17,7 +17,5 @@ noinst_HEADERS += \ include/linux/seg6_hmac.h \ include/linux/seg6_iptunnel.h \ include/linux/seg6_local.h \ - include/linux/mroute.h \ - include/linux/mroute6.h \ include/linux/pkt_cls.h \ # end diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index dc18105..b5c7dd8 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -74,12 +74,10 @@ struct isis_adjacency { struct nlpids nlpids; /* protocols spoken ... */ struct in_addr *ipv4_addresses; unsigned int ipv4_address_count; - struct in_addr router_address; struct in6_addr *ll_ipv6_addrs; /* Link local IPv6 neighbor address */ unsigned int ll_ipv6_count; struct in6_addr *global_ipv6_addrs; /* Global IPv6 neighbor address */ unsigned int global_ipv6_count; - struct in6_addr router_address6; uint8_t prio[ISIS_LEVELS]; /* priorityOfNeighbour for DIS */ int circuit_t; /* from hello PDU hdr */ int level; /* level (1 or 2) */ diff --git a/isisd/isis_affinitymap.c b/isisd/isis_affinitymap.c index 41bad0a..595091d 100644 --- a/isisd/isis_affinitymap.c +++ b/isisd/isis_affinitymap.c @@ -11,35 +11,6 @@ #ifndef FABRICD -static bool isis_affinity_map_check_use(const char *affmap_name) -{ - struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); - struct isis_area *area; - struct listnode *area_node, *fa_node; - struct flex_algo *fa; - struct affinity_map *map; - uint16_t pos; - - if (!isis) - return false; - - map = affinity_map_get(affmap_name); - pos = map->bit_position; - - for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) { - for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node, - fa)) { - if (admin_group_get(&fa->admin_group_exclude_any, - pos) || - admin_group_get(&fa->admin_group_include_any, - pos) || - admin_group_get(&fa->admin_group_include_all, pos)) - return true; - } - } - return false; -} - static void isis_affinity_map_update(const char *affmap_name, uint16_t old_pos, uint16_t new_pos) { @@ -90,7 +61,6 @@ void isis_affinity_map_init(void) { affinity_map_init(); - affinity_map_set_check_use_hook(isis_affinity_map_check_use); affinity_map_set_update_hook(isis_affinity_map_update); } diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 96d6291..47f51a7 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -8,6 +8,9 @@ */ #include + +#include + #if ISIS_METHOD == ISIS_METHOD_BPF #include #include diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index ffa6ad3..7819b20 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -489,7 +489,6 @@ static uint8_t isis_circuit_id_gen(struct isis *isis, struct interface *ifp) void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) { - struct listnode *node, *nnode; struct connected *conn; if (if_is_broadcast(ifp)) { @@ -509,20 +508,18 @@ void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) circuit->circ_type = CIRCUIT_T_UNKNOWN; } - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn)) + frr_each (if_connected, ifp->connected, conn) isis_circuit_add_addr(circuit, conn); - } void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp) { - struct listnode *node, *nnode; struct connected *conn; assert(circuit->interface == ifp); /* destroy addresses */ - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn)) + frr_each_safe (if_connected, ifp->connected, conn) isis_circuit_del_addr(circuit, conn); circuit->circ_type = CIRCUIT_T_UNKNOWN; @@ -1680,6 +1677,8 @@ void isis_circuit_init(void) #else if_cmd_init_default(); #endif - if_zapi_callbacks(isis_ifp_create, isis_ifp_up, - isis_ifp_down, isis_ifp_destroy); + hook_register_prio(if_real, 0, isis_ifp_create); + hook_register_prio(if_up, 0, isis_ifp_up); + hook_register_prio(if_down, 0, isis_ifp_down); + hook_register_prio(if_unreal, 0, isis_ifp_destroy); } diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 9718a45..2b19cbb 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -85,13 +85,13 @@ void cli_show_router_isis(struct vty *vty, const struct lyd_node *dnode, { const char *vrf = NULL; - vrf = yang_dnode_get_string(dnode, "./vrf"); + vrf = yang_dnode_get_string(dnode, "vrf"); vty_out(vty, "!\n"); vty_out(vty, "router isis %s", - yang_dnode_get_string(dnode, "./area-tag")); + yang_dnode_get_string(dnode, "area-tag")); if (!strmatch(vrf, VRF_DEFAULT_NAME)) - vty_out(vty, " vrf %s", yang_dnode_get_string(dnode, "./vrf")); + vty_out(vty, " vrf %s", yang_dnode_get_string(dnode, "vrf")); vty_out(vty, "\n"); } @@ -172,7 +172,7 @@ DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd, * If both ipv4 and ipv6 are off delete the interface isis container. */ if (strmatch(ip, "ipv6")) { - if (!yang_dnode_get_bool(dnode, "./ipv4-routing")) + if (!yang_dnode_get_bool(dnode, "ipv4-routing")) nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_DESTROY, NULL); else @@ -180,7 +180,7 @@ DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd, "./frr-isisd:isis/ipv6-routing", NB_OP_MODIFY, "false"); } else { - if (!yang_dnode_get_bool(dnode, "./ipv6-routing")) + if (!yang_dnode_get_bool(dnode, "ipv6-routing")) nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_DESTROY, NULL); else @@ -280,16 +280,16 @@ void cli_show_ip_isis_bfd_monitoring(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (!yang_dnode_get_bool(dnode, "./enabled")) { + if (!yang_dnode_get_bool(dnode, "enabled")) { if (show_defaults) vty_out(vty, " no isis bfd\n"); } else { vty_out(vty, " isis bfd\n"); } - if (yang_dnode_exists(dnode, "./profile")) + if (yang_dnode_exists(dnode, "profile")) vty_out(vty, " isis bfd profile %s\n", - yang_dnode_get_string(dnode, "./profile")); + yang_dnode_get_string(dnode, "profile")); } /* @@ -300,8 +300,12 @@ DEFPY_YANG(net, net_cmd, "[no] net WORD", "A Network Entity Title for this process (OSI only)\n" "XX.XXXX. ... .XXX.XX Network entity title (NET)\n") { - nb_cli_enqueue_change(vty, "./area-address", - no ? NB_OP_DESTROY : NB_OP_CREATE, net); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, XPATH_MAXLEN, "./area-address[.='%s']", net); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -588,9 +592,9 @@ void cli_show_isis_area_pwd(struct vty *vty, const struct lyd_node *dnode, const char *snp; vty_out(vty, " area-password %s %s", - yang_dnode_get_string(dnode, "./password-type"), - yang_dnode_get_string(dnode, "./password")); - snp = yang_dnode_get_string(dnode, "./authenticate-snp"); + yang_dnode_get_string(dnode, "password-type"), + yang_dnode_get_string(dnode, "password")); + snp = yang_dnode_get_string(dnode, "authenticate-snp"); if (!strmatch("none", snp)) vty_out(vty, " authenticate snp %s", snp); vty_out(vty, "\n"); @@ -638,9 +642,9 @@ void cli_show_isis_domain_pwd(struct vty *vty, const struct lyd_node *dnode, const char *snp; vty_out(vty, " domain-password %s %s", - yang_dnode_get_string(dnode, "./password-type"), - yang_dnode_get_string(dnode, "./password")); - snp = yang_dnode_get_string(dnode, "./authenticate-snp"); + yang_dnode_get_string(dnode, "password-type"), + yang_dnode_get_string(dnode, "password")); + snp = yang_dnode_get_string(dnode, "authenticate-snp"); if (!strmatch("none", snp)) vty_out(vty, " authenticate snp %s", snp); vty_out(vty, "\n"); @@ -861,17 +865,17 @@ void cli_show_isis_lsp_timers(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { const char *l1_refresh = - yang_dnode_get_string(dnode, "./level-1/refresh-interval"); + yang_dnode_get_string(dnode, "level-1/refresh-interval"); const char *l2_refresh = - yang_dnode_get_string(dnode, "./level-2/refresh-interval"); + yang_dnode_get_string(dnode, "level-2/refresh-interval"); const char *l1_lifetime = - yang_dnode_get_string(dnode, "./level-1/maximum-lifetime"); + yang_dnode_get_string(dnode, "level-1/maximum-lifetime"); const char *l2_lifetime = - yang_dnode_get_string(dnode, "./level-2/maximum-lifetime"); + yang_dnode_get_string(dnode, "level-2/maximum-lifetime"); const char *l1_gen = - yang_dnode_get_string(dnode, "./level-1/generation-interval"); + yang_dnode_get_string(dnode, "level-1/generation-interval"); const char *l2_gen = - yang_dnode_get_string(dnode, "./level-2/generation-interval"); + yang_dnode_get_string(dnode, "level-2/generation-interval"); if (strmatch(l1_refresh, l2_refresh) && strmatch(l1_lifetime, l2_lifetime) && strmatch(l1_gen, l2_gen)) vty_out(vty, @@ -980,8 +984,8 @@ void cli_show_isis_spf_min_interval(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " spf-interval %s\n", l1); @@ -1051,11 +1055,11 @@ void cli_show_isis_spf_ietf_backoff(struct vty *vty, { vty_out(vty, " spf-delay-ietf init-delay %s short-delay %s long-delay %s holddown %s time-to-learn %s\n", - yang_dnode_get_string(dnode, "./init-delay"), - yang_dnode_get_string(dnode, "./short-delay"), - yang_dnode_get_string(dnode, "./long-delay"), - yang_dnode_get_string(dnode, "./hold-down"), - yang_dnode_get_string(dnode, "./time-to-learn")); + yang_dnode_get_string(dnode, "init-delay"), + yang_dnode_get_string(dnode, "short-delay"), + yang_dnode_get_string(dnode, "long-delay"), + yang_dnode_get_string(dnode, "hold-down"), + yang_dnode_get_string(dnode, "time-to-learn")); } /* @@ -1364,15 +1368,15 @@ static void vty_print_def_origin(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " default-information originate %s %s", family, level); - if (yang_dnode_get_bool(dnode, "./always")) + if (yang_dnode_get_bool(dnode, "always")) vty_out(vty, " always"); - if (yang_dnode_exists(dnode, "./route-map")) + if (yang_dnode_exists(dnode, "route-map")) vty_out(vty, " route-map %s", - yang_dnode_get_string(dnode, "./route-map")); - if (show_defaults || !yang_dnode_is_default(dnode, "./metric")) + yang_dnode_get_string(dnode, "route-map")); + if (show_defaults || !yang_dnode_is_default(dnode, "metric")) vty_out(vty, " metric %s", - yang_dnode_get_string(dnode, "./metric")); + yang_dnode_get_string(dnode, "metric")); vty_out(vty, "\n"); } @@ -1381,7 +1385,7 @@ void cli_show_isis_def_origin_ipv4(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *level = yang_dnode_get_string(dnode, "./level"); + const char *level = yang_dnode_get_string(dnode, "level"); vty_print_def_origin(vty, dnode, "ipv4", level, show_defaults); } @@ -1390,7 +1394,7 @@ void cli_show_isis_def_origin_ipv6(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *level = yang_dnode_get_string(dnode, "./level"); + const char *level = yang_dnode_get_string(dnode, "level"); vty_print_def_origin(vty, dnode, "ipv6", level, show_defaults); } @@ -1505,22 +1509,22 @@ static void vty_print_redistribute(struct vty *vty, const struct lyd_node *dnode if (table) { level = yang_dnode_get_string(dnode, "../level"); - tableid = yang_dnode_get_uint16(dnode, "./table"); + tableid = yang_dnode_get_uint16(dnode, "table"); vty_out(vty, " redistribute %s table %d ", family, tableid); } else { - protocol = yang_dnode_get_string(dnode, "./protocol"); + protocol = yang_dnode_get_string(dnode, "protocol"); if (!table && strmatch(protocol, "table")) return; - level = yang_dnode_get_string(dnode, "./level"); + level = yang_dnode_get_string(dnode, "level"); vty_out(vty, " redistribute %s %s ", family, protocol); } vty_out(vty, "%s", level); - if (show_defaults || !yang_dnode_is_default(dnode, "./metric")) + if (show_defaults || !yang_dnode_is_default(dnode, "metric")) vty_out(vty, " metric %s", - yang_dnode_get_string(dnode, "%s", "./metric")); + yang_dnode_get_string(dnode, "%s", "metric")); - if (yang_dnode_exists(dnode, "./route-map")) - routemap = yang_dnode_get_string(dnode, "./route-map"); + if (yang_dnode_exists(dnode, "route-map")) + routemap = yang_dnode_get_string(dnode, "route-map"); if (routemap) vty_out(vty, " route-map %s", routemap); vty_out(vty, "\n"); @@ -1557,8 +1561,8 @@ void cli_show_isis_redistribute_ipv6_table(struct vty *vty, int cli_cmp_isis_redistribute_table(const struct lyd_node *dnode1, const struct lyd_node *dnode2) { - uint16_t table1 = yang_dnode_get_uint16(dnode1, "./table"); - uint16_t table2 = yang_dnode_get_uint16(dnode2, "./table"); + uint16_t table1 = yang_dnode_get_uint16(dnode1, "table"); + uint16_t table2 = yang_dnode_get_uint16(dnode2, "table"); return table1 - table2; } @@ -1619,7 +1623,7 @@ void cli_show_isis_mt_ipv4_multicast(struct vty *vty, bool show_defaults) { vty_out(vty, " topology ipv4-multicast"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1628,7 +1632,7 @@ void cli_show_isis_mt_ipv4_mgmt(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " topology ipv4-mgmt"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1638,7 +1642,7 @@ void cli_show_isis_mt_ipv6_unicast(struct vty *vty, bool show_defaults) { vty_out(vty, " topology ipv6-unicast"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1648,7 +1652,7 @@ void cli_show_isis_mt_ipv6_multicast(struct vty *vty, bool show_defaults) { vty_out(vty, " topology ipv6-multicast"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1657,7 +1661,7 @@ void cli_show_isis_mt_ipv6_mgmt(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " topology ipv6-mgmt"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1666,7 +1670,7 @@ void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " topology ipv6-dstsrc"); - if (yang_dnode_get_bool(dnode, "./overload")) + if (yang_dnode_get_bool(dnode, "overload")) vty_out(vty, " overload"); vty_out(vty, "\n"); } @@ -1771,13 +1775,13 @@ void cli_show_isis_label_blocks(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " segment-routing global-block %s %s", - yang_dnode_get_string(dnode, "./srgb/lower-bound"), - yang_dnode_get_string(dnode, "./srgb/upper-bound")); - if (!yang_dnode_is_default(dnode, "./srlb/lower-bound") - || !yang_dnode_is_default(dnode, "./srlb/upper-bound")) + yang_dnode_get_string(dnode, "srgb/lower-bound"), + yang_dnode_get_string(dnode, "srgb/upper-bound")); + if (!yang_dnode_is_default(dnode, "srlb/lower-bound") + || !yang_dnode_is_default(dnode, "srlb/upper-bound")) vty_out(vty, " local-block %s %s", - yang_dnode_get_string(dnode, "./srlb/lower-bound"), - yang_dnode_get_string(dnode, "./srlb/upper-bound")); + yang_dnode_get_string(dnode, "srlb/lower-bound"), + yang_dnode_get_string(dnode, "srlb/upper-bound")); vty_out(vty, "\n"); } @@ -1899,11 +1903,11 @@ void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode, const char *sid_value; bool n_flag_clear; - prefix = yang_dnode_get_string(dnode, "./prefix"); - lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior"); - sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type"); - sid_value = yang_dnode_get_string(dnode, "./sid-value"); - n_flag_clear = yang_dnode_get_bool(dnode, "./n-flag-clear"); + prefix = yang_dnode_get_string(dnode, "prefix"); + lh_behavior = yang_dnode_get_string(dnode, "last-hop-behavior"); + sid_value_type = yang_dnode_get_string(dnode, "sid-value-type"); + sid_value = yang_dnode_get_string(dnode, "sid-value"); + n_flag_clear = yang_dnode_get_bool(dnode, "n-flag-clear"); vty_out(vty, " segment-routing prefix %s", prefix); if (strmatch(sid_value_type, "absolute")) @@ -2013,12 +2017,12 @@ void cli_show_isis_prefix_sid_algorithm(struct vty *vty, bool n_flag_clear; uint32_t algorithm; - prefix = yang_dnode_get_string(dnode, "./prefix"); - sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type"); - sid_value = yang_dnode_get_string(dnode, "./sid-value"); - algorithm = yang_dnode_get_uint32(dnode, "./algo"); - lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior"); - n_flag_clear = yang_dnode_get_bool(dnode, "./n-flag-clear"); + prefix = yang_dnode_get_string(dnode, "prefix"); + sid_value_type = yang_dnode_get_string(dnode, "sid-value-type"); + sid_value = yang_dnode_get_string(dnode, "sid-value"); + algorithm = yang_dnode_get_uint32(dnode, "algo"); + lh_behavior = yang_dnode_get_string(dnode, "last-hop-behavior"); + n_flag_clear = yang_dnode_get_bool(dnode, "n-flag-clear"); vty_out(vty, " segment-routing prefix %s", prefix); vty_out(vty, " algorithm %u", algorithm); @@ -2220,22 +2224,22 @@ void cli_show_isis_srv6_node_msd(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " node-msd\n"); - if (yang_dnode_get_uint8(dnode, "./max-segs-left") != + if (yang_dnode_get_uint8(dnode, "max-segs-left") != yang_get_default_uint8("%s/msd/node-msd/max-segs-left", ISIS_SRV6)) vty_out(vty, " max-segs-left %u\n", - yang_dnode_get_uint8(dnode, "./max-segs-left")); - if (yang_dnode_get_uint8(dnode, "./max-end-pop") != + yang_dnode_get_uint8(dnode, "max-segs-left")); + if (yang_dnode_get_uint8(dnode, "max-end-pop") != yang_get_default_uint8("%s/msd/node-msd/max-end-pop", ISIS_SRV6)) vty_out(vty, " max-end-pop %u\n", - yang_dnode_get_uint8(dnode, "./max-end-pop")); - if (yang_dnode_get_uint8(dnode, "./max-h-encaps") != + yang_dnode_get_uint8(dnode, "max-end-pop")); + if (yang_dnode_get_uint8(dnode, "max-h-encaps") != yang_get_default_uint8("%s/msd/node-msd/max-h-encaps", ISIS_SRV6)) vty_out(vty, " max-h-encaps %u\n", - yang_dnode_get_uint8(dnode, "./max-h-encaps")); - if (yang_dnode_get_uint8(dnode, "./max-end-d") != + yang_dnode_get_uint8(dnode, "max-h-encaps")); + if (yang_dnode_get_uint8(dnode, "max-end-d") != yang_get_default_uint8("%s/msd/node-msd/max-end-d", ISIS_SRV6)) vty_out(vty, " max-end-d %u\n", - yang_dnode_get_uint8(dnode, "./max-end-d")); + yang_dnode_get_uint8(dnode, "max-end-d")); } /* @@ -2381,8 +2385,8 @@ void cli_show_isis_frr_lfa_tiebreaker(struct vty *vty, bool show_defaults) { vty_out(vty, " fast-reroute lfa tiebreaker %s index %s %s\n", - yang_dnode_get_string(dnode, "./type"), - yang_dnode_get_string(dnode, "./index"), + yang_dnode_get_string(dnode, "type"), + yang_dnode_get_string(dnode, "index"), dnode->parent->parent->schema->name); } @@ -2554,8 +2558,8 @@ void cli_show_ip_isis_password(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " isis password %s %s\n", - yang_dnode_get_string(dnode, "./password-type"), - yang_dnode_get_string(dnode, "./password")); + yang_dnode_get_string(dnode, "password-type"), + yang_dnode_get_string(dnode, "password")); } /* @@ -2601,8 +2605,8 @@ DEFPY_YANG(no_isis_metric, no_isis_metric_cmd, void cli_show_ip_isis_metric(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis metric %s\n", l1); @@ -2660,8 +2664,8 @@ void cli_show_ip_isis_hello_interval(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis hello-interval %s\n", l1); @@ -2718,8 +2722,8 @@ DEFPY_YANG(no_isis_hello_multiplier, no_isis_hello_multiplier_cmd, void cli_show_ip_isis_hello_multi(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis hello-multiplier %s\n", l1); @@ -2837,8 +2841,8 @@ void cli_show_ip_isis_csnp_interval(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis csnp-interval %s\n", l1); @@ -2896,8 +2900,8 @@ void cli_show_ip_isis_psnp_interval(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis psnp-interval %s\n", l1); @@ -3054,12 +3058,16 @@ void cli_show_ip_isis_circ_type(struct vty *vty, const struct lyd_node *dnode, } static int ag_change(struct vty *vty, int argc, struct cmd_token **argv, - const char *xpath, bool no, int start_idx) + const char *xpath_base, bool no, int start_idx) { - for (int i = start_idx; i < argc; i++) + char xpath[XPATH_MAXLEN]; + + for (int i = start_idx; i < argc; i++) { + snprintf(xpath, XPATH_MAXLEN, "%s[.='%s']", xpath_base, + argv[i]->arg); nb_cli_enqueue_change(vty, xpath, - no ? NB_OP_DESTROY : NB_OP_CREATE, - argv[i]->arg); + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + } return nb_cli_apply_changes(vty, NULL); } @@ -3140,8 +3148,8 @@ DEFPY_YANG(no_isis_priority, no_isis_priority_cmd, void cli_show_ip_isis_priority(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + const char *l1 = yang_dnode_get_string(dnode, "level-1"); + const char *l2 = yang_dnode_get_string(dnode, "level-2"); if (strmatch(l1, l2)) vty_out(vty, " isis priority %s\n", l1); @@ -3162,8 +3170,8 @@ void cli_show_ip_isis_frr(struct vty *vty, const struct lyd_node *dnode, bool l1_link_fallback, l2_link_fallback; /* Classic LFA */ - l1_enabled = yang_dnode_get_bool(dnode, "./level-1/lfa/enable"); - l2_enabled = yang_dnode_get_bool(dnode, "./level-2/lfa/enable"); + l1_enabled = yang_dnode_get_bool(dnode, "level-1/lfa/enable"); + l2_enabled = yang_dnode_get_bool(dnode, "level-2/lfa/enable"); if (l1_enabled || l2_enabled) { if (l1_enabled == l2_enabled) { @@ -3180,8 +3188,8 @@ void cli_show_ip_isis_frr(struct vty *vty, const struct lyd_node *dnode, } /* Remote LFA */ - l1_enabled = yang_dnode_get_bool(dnode, "./level-1/remote-lfa/enable"); - l2_enabled = yang_dnode_get_bool(dnode, "./level-2/remote-lfa/enable"); + l1_enabled = yang_dnode_get_bool(dnode, "level-1/remote-lfa/enable"); + l2_enabled = yang_dnode_get_bool(dnode, "level-2/remote-lfa/enable"); if (l1_enabled || l2_enabled) { if (l1_enabled == l2_enabled) { @@ -3199,16 +3207,16 @@ void cli_show_ip_isis_frr(struct vty *vty, const struct lyd_node *dnode, } /* TI-LFA */ - l1_enabled = yang_dnode_get_bool(dnode, "./level-1/ti-lfa/enable"); - l2_enabled = yang_dnode_get_bool(dnode, "./level-2/ti-lfa/enable"); + l1_enabled = yang_dnode_get_bool(dnode, "level-1/ti-lfa/enable"); + l2_enabled = yang_dnode_get_bool(dnode, "level-2/ti-lfa/enable"); l1_node_protection = - yang_dnode_get_bool(dnode, "./level-1/ti-lfa/node-protection"); + yang_dnode_get_bool(dnode, "level-1/ti-lfa/node-protection"); l2_node_protection = - yang_dnode_get_bool(dnode, "./level-2/ti-lfa/node-protection"); + yang_dnode_get_bool(dnode, "level-2/ti-lfa/node-protection"); l1_link_fallback = - yang_dnode_get_bool(dnode, "./level-1/ti-lfa/link-fallback"); + yang_dnode_get_bool(dnode, "level-1/ti-lfa/link-fallback"); l2_link_fallback = - yang_dnode_get_bool(dnode, "./level-2/ti-lfa/link-fallback"); + yang_dnode_get_bool(dnode, "level-2/ti-lfa/link-fallback"); if (l1_enabled || l2_enabled) { @@ -3302,31 +3310,27 @@ DEFPY(isis_lfa_exclude_interface, isis_lfa_exclude_interface_cmd, "Exclude an interface from computation\n" "Interface name\n") { + char xpath[XPATH_MAXLEN]; + if (!level || strmatch(level, "level-1")) { - if (no) { - nb_cli_enqueue_change( - vty, - "./frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface", - NB_OP_DESTROY, ifname); - } else { - nb_cli_enqueue_change( - vty, - "./frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface", - NB_OP_CREATE, ifname); - } + snprintf(xpath, sizeof(xpath), + "./frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface[.='%s']", + ifname); + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); } if (!level || strmatch(level, "level-2")) { - if (no) { - nb_cli_enqueue_change( - vty, - "./frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface", - NB_OP_DESTROY, ifname); - } else { - nb_cli_enqueue_change( - vty, - "./frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface", - NB_OP_CREATE, ifname); - } + snprintf(xpath, sizeof(xpath), + "./frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface[.='%s']", + ifname); + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); } return nb_cli_apply_changes(vty, NULL); @@ -3874,24 +3878,24 @@ void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, uint32_t priority; char type_str[10]; - algorithm = yang_dnode_get_uint32(dnode, "./flex-algo"); + algorithm = yang_dnode_get_uint32(dnode, "flex-algo"); vty_out(vty, " flex-algo %u\n", algorithm); - if (yang_dnode_exists(dnode, "./advertise-definition")) + if (yang_dnode_exists(dnode, "advertise-definition")) vty_out(vty, " advertise-definition\n"); - if (yang_dnode_exists(dnode, "./dplane-sr-mpls")) + if (yang_dnode_exists(dnode, "dplane-sr-mpls")) vty_out(vty, " dataplane sr-mpls\n"); - if (yang_dnode_exists(dnode, "./dplane-srv6")) + if (yang_dnode_exists(dnode, "dplane-srv6")) vty_out(vty, " dataplane srv6\n"); - if (yang_dnode_exists(dnode, "./dplane-ip")) + if (yang_dnode_exists(dnode, "dplane-ip")) vty_out(vty, " dataplane ip\n"); - if (yang_dnode_exists(dnode, "./prefix-metric")) + if (yang_dnode_exists(dnode, "prefix-metric")) vty_out(vty, " prefix-metric\n"); - if (yang_dnode_exists(dnode, "./metric-type")) { - metric_type = yang_dnode_get_enum(dnode, "./metric-type"); + if (yang_dnode_exists(dnode, "metric-type")) { + metric_type = yang_dnode_get_enum(dnode, "metric-type"); if (metric_type != MT_IGP) { flex_algo_metric_type_print(type_str, sizeof(type_str), metric_type); @@ -3899,8 +3903,8 @@ void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, } } - if (yang_dnode_exists(dnode, "./priority")) { - priority = yang_dnode_get_uint32(dnode, "./priority"); + if (yang_dnode_exists(dnode, "priority")) { + priority = yang_dnode_get_uint32(dnode, "priority"); if (priority != FLEX_ALGO_PRIO_DEFAULT) vty_out(vty, " priority %u\n", priority); } diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 1b3491f..77573cd 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -703,7 +703,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) snprintf(id, sizeof(id), "%.14s", cmd_hostname_get()); else - snprintf(id, sizeof(id), "%pSY", lsp_id); + snprintfrr(id, sizeof(id), "%pSY", lsp_id); if (frag) snprintf(dest, dest_len, "%s.%02x-%02x", id, diff --git a/isisd/isis_main.c b/isisd/isis_main.c index da4c7bc..60ec8cd 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -51,9 +51,16 @@ /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" -/* Default vty port */ -#define ISISD_VTY_PORT 2608 -#define FABRICD_VTY_PORT 2618 + +#define FABRICD_STATE_NAME "%s/fabricd.json", frr_libstatedir +#define ISISD_STATE_NAME "%s/isisd.json", frr_libstatedir + +/* The typo was there before. Do not fix it! The point is to load mis-saved + * state files from older versions. + * + * Also fabricd was using the same file. Sigh. + */ +#define ISISD_COMPAT_STATE_NAME "%s/isid-restart.json", frr_runstatedir /* isisd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -212,24 +219,41 @@ static void isis_config_end(void) isis_config_finish(t_isis_cfg); } +/* actual paths filled in main() */ +static char state_path[512]; +static char state_compat_path[512]; +static char *state_paths[] = { + state_path, + state_compat_path, + NULL, +}; + +/* clang-format off */ +FRR_DAEMON_INFO( #ifdef FABRICD -FRR_DAEMON_INFO(fabricd, OPEN_FABRIC, .vty_port = FABRICD_VTY_PORT, + fabricd, OPEN_FABRIC, - .proghelp = "Implementation of the OpenFabric routing protocol.", + .vty_port = FABRICD_VTY_PORT, + .proghelp = "Implementation of the OpenFabric routing protocol.", #else -FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT, + isisd, ISIS, - .proghelp = "Implementation of the IS-IS routing protocol.", + .vty_port = ISISD_VTY_PORT, + .proghelp = "Implementation of the IS-IS routing protocol.", #endif - .copyright = - "Copyright (c) 2001-2002 Sampo Saaristo, Ofer Wald and Hannes Gredler", + .copyright = "Copyright (c) 2001-2002 Sampo Saaristo, Ofer Wald and Hannes Gredler", - .signals = isisd_signals, - .n_signals = array_size(isisd_signals), + .signals = isisd_signals, + .n_signals = array_size(isisd_signals), - .privs = &isisd_privs, .yang_modules = isisd_yang_modules, - .n_yang_modules = array_size(isisd_yang_modules), + .privs = &isisd_privs, + + .yang_modules = isisd_yang_modules, + .n_yang_modules = array_size(isisd_yang_modules), + + .state_paths = state_paths, ); +/* clang-format on */ /* * Main routine of isisd. Parse arguments and handle IS-IS state machine. @@ -269,6 +293,14 @@ int main(int argc, char **argv, char **envp) } } +#ifdef FABRICD + snprintf(state_path, sizeof(state_path), FABRICD_STATE_NAME); +#else + snprintf(state_path, sizeof(state_path), ISISD_STATE_NAME); +#endif + snprintf(state_compat_path, sizeof(state_compat_path), + ISISD_COMPAT_STATE_NAME); + /* thread master */ isis_master_init(frr_init()); master = im->master; diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index b6ee073..5794e16 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -60,8 +60,8 @@ int isis_instance_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); - area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); + vrf_name = yang_dnode_get_string(args->dnode, "vrf"); + area_tag = yang_dnode_get_string(args->dnode, "area-tag"); area = isis_area_lookup_by_vrf(area_tag, vrf_name); if (area) @@ -650,12 +650,12 @@ int isis_instance_lsp_generation_interval_level_2_modify( */ void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args) { - long init_delay = yang_dnode_get_uint16(args->dnode, "./init-delay"); - long short_delay = yang_dnode_get_uint16(args->dnode, "./short-delay"); - long long_delay = yang_dnode_get_uint16(args->dnode, "./long-delay"); - long holddown = yang_dnode_get_uint16(args->dnode, "./hold-down"); + long init_delay = yang_dnode_get_uint16(args->dnode, "init-delay"); + long short_delay = yang_dnode_get_uint16(args->dnode, "short-delay"); + long long_delay = yang_dnode_get_uint16(args->dnode, "long-delay"); + long holddown = yang_dnode_get_uint16(args->dnode, "hold-down"); long timetolearn = - yang_dnode_get_uint16(args->dnode, "./time-to-learn"); + yang_dnode_get_uint16(args->dnode, "time-to-learn"); struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); char *buf = XCALLOC(MTYPE_TMP, bufsiz); @@ -926,11 +926,11 @@ int isis_instance_spf_prefix_priorities_medium_access_list_name_destroy( */ void area_password_apply_finish(struct nb_cb_apply_finish_args *args) { - const char *password = yang_dnode_get_string(args->dnode, "./password"); + const char *password = yang_dnode_get_string(args->dnode, "password"); struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); - int pass_type = yang_dnode_get_enum(args->dnode, "./password-type"); + int pass_type = yang_dnode_get_enum(args->dnode, "password-type"); uint8_t snp_auth = - yang_dnode_get_enum(args->dnode, "./authenticate-snp"); + yang_dnode_get_enum(args->dnode, "authenticate-snp"); switch (pass_type) { case ISIS_PASSWD_TYPE_CLEARTXT: @@ -997,11 +997,11 @@ int isis_instance_area_password_authenticate_snp_modify( */ void domain_password_apply_finish(struct nb_cb_apply_finish_args *args) { - const char *password = yang_dnode_get_string(args->dnode, "./password"); + const char *password = yang_dnode_get_string(args->dnode, "password"); struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); - int pass_type = yang_dnode_get_enum(args->dnode, "./password-type"); + int pass_type = yang_dnode_get_enum(args->dnode, "password-type"); uint8_t snp_auth = - yang_dnode_get_enum(args->dnode, "./authenticate-snp"); + yang_dnode_get_enum(args->dnode, "authenticate-snp"); switch (pass_type) { case ISIS_PASSWD_TYPE_CLEARTXT: @@ -1073,9 +1073,9 @@ void default_info_origin_apply_finish(const struct lyd_node *dnode, int family) unsigned long metric = 0; const char *routemap = NULL; struct isis_area *area = nb_running_get_entry(dnode, NULL, true); - int level = yang_dnode_get_enum(dnode, "./level"); + int level = yang_dnode_get_enum(dnode, "level"); - if (yang_dnode_get_bool(dnode, "./always")) { + if (yang_dnode_get_bool(dnode, "always")) { originate_type = DEFAULT_ORIGINATE_ALWAYS; } else if (family == AF_INET6) { zlog_warn( @@ -1083,10 +1083,10 @@ void default_info_origin_apply_finish(const struct lyd_node *dnode, int family) __func__); } - if (yang_dnode_exists(dnode, "./metric")) - metric = yang_dnode_get_uint32(dnode, "./metric"); - if (yang_dnode_exists(dnode, "./route-map")) - routemap = yang_dnode_get_string(dnode, "./route-map"); + if (yang_dnode_exists(dnode, "metric")) + metric = yang_dnode_get_uint32(dnode, "metric"); + if (yang_dnode_exists(dnode, "route-map")) + routemap = yang_dnode_get_string(dnode, "route-map"); isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, originate_type, 0); @@ -1119,7 +1119,7 @@ int isis_instance_default_information_originate_ipv4_destroy( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - level = yang_dnode_get_enum(args->dnode, "./level"); + level = yang_dnode_get_enum(args->dnode, "level"); isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE, 0); return NB_OK; @@ -1182,7 +1182,7 @@ int isis_instance_default_information_originate_ipv6_destroy( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - level = yang_dnode_get_enum(args->dnode, "./level"); + level = yang_dnode_get_enum(args->dnode, "level"); isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE, 0); return NB_OK; @@ -1236,14 +1236,14 @@ void redistribute_apply_finish(const struct lyd_node *dnode, int family) const char *routemap = NULL; struct isis_area *area; - type = yang_dnode_get_enum(dnode, "./protocol"); - level = yang_dnode_get_enum(dnode, "./level"); + type = yang_dnode_get_enum(dnode, "protocol"); + level = yang_dnode_get_enum(dnode, "level"); area = nb_running_get_entry(dnode, NULL, true); - if (yang_dnode_exists(dnode, "./metric")) - metric = yang_dnode_get_uint32(dnode, "./metric"); - if (yang_dnode_exists(dnode, "./route-map")) - routemap = yang_dnode_get_string(dnode, "./route-map"); + if (yang_dnode_exists(dnode, "metric")) + metric = yang_dnode_get_uint32(dnode, "metric"); + if (yang_dnode_exists(dnode, "route-map")) + routemap = yang_dnode_get_string(dnode, "route-map"); isis_redist_set(area, level, family, type, metric, routemap, 0, 0); } @@ -1273,8 +1273,8 @@ int isis_instance_redistribute_ipv4_destroy(struct nb_cb_destroy_args *args) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - level = yang_dnode_get_enum(args->dnode, "./level"); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + level = yang_dnode_get_enum(args->dnode, "level"); + type = yang_dnode_get_enum(args->dnode, "protocol"); isis_redist_unset(area, level, AF_INET, type, 0); return NB_OK; @@ -1333,12 +1333,12 @@ int isis_instance_redistribute_ipv4_table_create(struct nb_cb_create_args *args) level = yang_dnode_get_enum(args->dnode, "../level"); area = nb_running_get_entry(args->dnode, "../.", true); - if (yang_dnode_exists(args->dnode, "./metric")) - metric = yang_dnode_get_uint32(args->dnode, "./metric"); - if (yang_dnode_exists(args->dnode, "./route-map")) - routemap = yang_dnode_get_string(args->dnode, "./route-map"); + if (yang_dnode_exists(args->dnode, "metric")) + metric = yang_dnode_get_uint32(args->dnode, "metric"); + if (yang_dnode_exists(args->dnode, "route-map")) + routemap = yang_dnode_get_string(args->dnode, "route-map"); - table = yang_dnode_get_uint16(args->dnode, "./table"); + table = yang_dnode_get_uint16(args->dnode, "table"); isis_redist_set(area, level, AF_INET, type, metric, routemap, 0, table); return NB_OK; @@ -1355,7 +1355,7 @@ int isis_instance_redistribute_ipv4_table_destroy(struct nb_cb_destroy_args *arg area = nb_running_get_entry(args->dnode, "../.", true); level = yang_dnode_get_enum(args->dnode, "../level"); type = yang_dnode_get_enum(args->dnode, "../protocol"); - table = yang_dnode_get_uint16(args->dnode, "./table"); + table = yang_dnode_get_uint16(args->dnode, "table"); isis_redist_unset(area, level, AF_INET, type, table); return NB_OK; @@ -1379,8 +1379,8 @@ int isis_instance_redistribute_ipv6_destroy(struct nb_cb_destroy_args *args) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - level = yang_dnode_get_enum(args->dnode, "./level"); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + level = yang_dnode_get_enum(args->dnode, "level"); + type = yang_dnode_get_enum(args->dnode, "protocol"); isis_redist_unset(area, level, AF_INET6, type, 0); return NB_OK; @@ -1732,8 +1732,8 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - index = yang_dnode_get_uint8(args->dnode, "./index"); - type = yang_dnode_get_enum(args->dnode, "./type"); + index = yang_dnode_get_uint8(args->dnode, "index"); + type = yang_dnode_get_enum(args->dnode, "type"); tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL1, index, type); nb_running_set_entry(args->dnode, tie_b); @@ -1881,8 +1881,8 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - index = yang_dnode_get_uint8(args->dnode, "./index"); - type = yang_dnode_get_enum(args->dnode, "./type"); + index = yang_dnode_get_uint8(args->dnode, "index"); + type = yang_dnode_get_enum(args->dnode, "type"); tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL2, index, type); nb_running_set_entry(args->dnode, tie_b); @@ -2212,10 +2212,10 @@ int isis_instance_segment_routing_label_blocks_pre_validate( uint32_t srlb_lbound; uint32_t srlb_ubound; - srgb_lbound = yang_dnode_get_uint32(args->dnode, "./srgb/lower-bound"); - srgb_ubound = yang_dnode_get_uint32(args->dnode, "./srgb/upper-bound"); - srlb_lbound = yang_dnode_get_uint32(args->dnode, "./srlb/lower-bound"); - srlb_ubound = yang_dnode_get_uint32(args->dnode, "./srlb/upper-bound"); + srgb_lbound = yang_dnode_get_uint32(args->dnode, "srgb/lower-bound"); + srgb_ubound = yang_dnode_get_uint32(args->dnode, "srgb/upper-bound"); + srlb_lbound = yang_dnode_get_uint32(args->dnode, "srlb/lower-bound"); + srlb_ubound = yang_dnode_get_uint32(args->dnode, "srlb/upper-bound"); /* Check that the block size does not exceed 65535 */ if ((srgb_ubound - srgb_lbound + 1) > 65535) { @@ -2255,8 +2255,8 @@ void isis_instance_segment_routing_srgb_apply_finish( uint32_t lower_bound, upper_bound; area = nb_running_get_entry(args->dnode, NULL, true); - lower_bound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); - upper_bound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + lower_bound = yang_dnode_get_uint32(args->dnode, "lower-bound"); + upper_bound = yang_dnode_get_uint32(args->dnode, "upper-bound"); isis_sr_cfg_srgb_update(area, lower_bound, upper_bound); } @@ -2321,8 +2321,8 @@ void isis_instance_segment_routing_srlb_apply_finish( uint32_t lower_bound, upper_bound; area = nb_running_get_entry(args->dnode, NULL, true); - lower_bound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); - upper_bound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + lower_bound = yang_dnode_get_uint32(args->dnode, "lower-bound"); + upper_bound = yang_dnode_get_uint32(args->dnode, "upper-bound"); isis_sr_cfg_srlb_update(area, lower_bound, upper_bound); } @@ -2428,7 +2428,7 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); pcfg = isis_sr_cfg_prefix_add(area, &prefix, SR_ALGORITHM_SPF); nb_running_set_entry(args->dnode, pcfg); @@ -2466,13 +2466,13 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate( enum sr_sid_value_type sid_type; struct isis_prefix_sid psid = {}; - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); srgb_lbound = yang_dnode_get_uint32( args->dnode, "../../label-blocks/srgb/lower-bound"); srgb_ubound = yang_dnode_get_uint32( args->dnode, "../../label-blocks/srgb/upper-bound"); - sid = yang_dnode_get_uint32(args->dnode, "./sid-value"); - sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type"); + sid = yang_dnode_get_uint32(args->dnode, "sid-value"); + sid_type = yang_dnode_get_enum(args->dnode, "sid-value-type"); /* Check for invalid indexes/labels. */ srgb_range = srgb_ubound - srgb_lbound + 1; @@ -2636,8 +2636,8 @@ int isis_instance_segment_routing_algorithm_prefix_sid_create( return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); - algorithm = yang_dnode_get_uint32(args->dnode, "./algo"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + algorithm = yang_dnode_get_uint32(args->dnode, "algo"); pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm); pcfg->algorithm = algorithm; @@ -2676,13 +2676,13 @@ int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate( enum sr_sid_value_type sid_type; struct isis_prefix_sid psid = {}; - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); srgb_lbound = yang_dnode_get_uint32( args->dnode, "../../label-blocks/srgb/lower-bound"); srgb_ubound = yang_dnode_get_uint32( args->dnode, "../../label-blocks/srgb/upper-bound"); - sid = yang_dnode_get_uint32(args->dnode, "./sid-value"); - sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type"); + sid = yang_dnode_get_uint32(args->dnode, "sid-value"); + sid_type = yang_dnode_get_enum(args->dnode, "sid-value-type"); /* Check for invalid indexes/labels. */ srgb_range = srgb_ubound - srgb_lbound + 1; @@ -2840,8 +2840,8 @@ int isis_instance_flex_algo_create(struct nb_cb_create_args *args) uint32_t priority = FLEX_ALGO_PRIO_DEFAULT; struct isis_flex_algo_alloc_arg arg; - algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); - advertise = yang_dnode_exists(args->dnode, "./advertise-definition"); + algorithm = yang_dnode_get_uint32(args->dnode, "flex-algo"); + advertise = yang_dnode_exists(args->dnode, "advertise-definition"); switch (args->event) { case NB_EV_APPLY: @@ -2875,7 +2875,7 @@ int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args) struct isis_area *area; uint32_t algorithm; - algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "flex-algo"); area = nb_running_get_entry(args->dnode, NULL, true); switch (args->event) { @@ -2904,8 +2904,8 @@ int isis_instance_flex_algo_advertise_definition_modify( uint32_t algorithm; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); - advertise = yang_dnode_exists(args->dnode, "./../advertise-definition"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); + advertise = yang_dnode_exists(args->dnode, "../advertise-definition"); switch (args->event) { case NB_EV_APPLY: @@ -2937,7 +2937,7 @@ int isis_instance_flex_algo_advertise_definition_destroy( area = nb_running_get_entry(args->dnode, NULL, true); - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); switch (args->event) { case NB_EV_APPLY: @@ -3153,7 +3153,7 @@ int isis_instance_flex_algo_prefix_metric_create(struct nb_cb_create_args *args) if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); switch (args->event) { case NB_EV_APPLY: @@ -3188,7 +3188,7 @@ int isis_instance_flex_algo_prefix_metric_destroy( if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); switch (args->event) { case NB_EV_APPLY: @@ -3223,7 +3223,7 @@ static int isis_instance_flex_algo_dplane_set(struct nb_cb_create_args *args, if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); switch (args->event) { case NB_EV_APPLY: @@ -3265,7 +3265,7 @@ static int isis_instance_flex_algo_dplane_unset(struct nb_cb_destroy_args *args, if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); switch (args->event) { case NB_EV_APPLY: @@ -3348,7 +3348,7 @@ int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args) if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); metric_type = yang_dnode_get_enum(args->dnode, NULL); switch (args->event) { @@ -3388,7 +3388,7 @@ int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args) if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); priority = yang_dnode_get_uint32(args->dnode, NULL); switch (args->event) { @@ -3424,7 +3424,7 @@ int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args) if (!area) return NB_ERR_RESOURCE; - algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + algorithm = yang_dnode_get_uint32(args->dnode, "../flex-algo"); priority = yang_dnode_get_uint32(args->dnode, NULL); switch (args->event) { @@ -3754,7 +3754,7 @@ int lib_interface_isis_create(struct nb_cb_create_args *args) { struct interface *ifp; struct isis_circuit *circuit = NULL; - const char *area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); + const char *area_tag = yang_dnode_get_string(args->dnode, "area-tag"); switch (args->event) { case NB_EV_PREPARE: diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 0cd43a7..6f410d3 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -111,6 +111,7 @@ struct iih_info { bool v6_usable; struct isis_tlvs *tlvs; + int calculated_type; }; static int process_p2p_hello(struct iih_info *iih) @@ -151,6 +152,76 @@ static int process_p2p_hello(struct iih_info *iih) struct isis_adjacency *adj = iih->circuit->u.p2p.neighbor; /* If an adjacency exists, check it is with the source of the hello * packets */ + if (((iih->circuit->area->is_type == IS_LEVEL_1) && + ((iih->circuit->is_type_config == IS_LEVEL_1_AND_2) || + (iih->circuit->is_type_config == IS_LEVEL_1))) || + ((iih->circuit->area->is_type == IS_LEVEL_1_AND_2) && + (iih->circuit->is_type_config == IS_LEVEL_1) && + ((iih->circ_type == IS_LEVEL_1) || + (iih->circ_type == IS_LEVEL_1_AND_2))) || + ((iih->circuit->area->is_type == IS_LEVEL_1_AND_2) && + (iih->circuit->is_type_config == IS_LEVEL_1_AND_2) && + (iih->circ_type == IS_LEVEL_1))) { + if (!isis_tlvs_area_addresses_match(iih->tlvs, + iih->circuit->area + ->area_addrs)) { + if (IS_DEBUG_ADJ_PACKETS) { + zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s, cir id %u, length %u", + iih->circuit->area->area_tag, + iih->circuit->interface->name, + circuit_t2string( + iih->circuit->is_type), + iih->circuit->circuit_id, + iih->pdu_len); + } + + return ISIS_WARNING; + } + + iih->calculated_type = IS_LEVEL_1; + + } + + else if (((iih->circuit->area->is_type == IS_LEVEL_2) && + ((iih->circuit->is_type_config == IS_LEVEL_1_AND_2) || + (iih->circuit->is_type_config == IS_LEVEL_2))) || + ((iih->circuit->area->is_type == IS_LEVEL_1_AND_2) && + (iih->circuit->is_type_config == IS_LEVEL_2) && + ((iih->circ_type == IS_LEVEL_2) || + (iih->circ_type == IS_LEVEL_1_AND_2))) || + ((iih->circuit->area->is_type == IS_LEVEL_1_AND_2) && + (iih->circuit->is_type_config == IS_LEVEL_1_AND_2) && + (iih->circ_type == IS_LEVEL_2))) { + iih->calculated_type = IS_LEVEL_2; + } + + else if ((iih->circuit->area->is_type == IS_LEVEL_1_AND_2) && + (iih->circuit->is_type_config == IS_LEVEL_1_AND_2) && + (iih->circ_type == IS_LEVEL_1_AND_2)) { + iih->calculated_type = IS_LEVEL_1_AND_2; + + if (!isis_tlvs_area_addresses_match(iih->tlvs, + iih->circuit->area + ->area_addrs)) { + iih->calculated_type = IS_LEVEL_2; + } + } + + else { + if (IS_DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { + zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s, cir id %u, length %u", + iih->circuit->area->area_tag, + iih->circuit->interface->name, + circuit_t2string( + iih->circuit->is_type), + iih->circuit->circuit_id, + iih->pdu_len); + } + } + return ISIS_WARNING; + } + if (adj) { if (memcmp(iih->sys_id, adj->sysid, ISIS_SYS_ID_LEN)) { zlog_debug( @@ -160,12 +231,13 @@ static int process_p2p_hello(struct iih_info *iih) return ISIS_OK; } } - if (!adj || adj->level != iih->circ_type) { + if (!adj || adj->level != iih->calculated_type) { if (!adj) { - adj = isis_new_adj(iih->sys_id, NULL, iih->circ_type, - iih->circuit); + adj = isis_new_adj(iih->sys_id, NULL, + iih->calculated_type, iih->circuit); + } else { - adj->level = iih->circ_type; + adj->level = iih->calculated_type; } iih->circuit->u.p2p.neighbor = adj; /* Build lsp with the new neighbor entry when a new @@ -174,7 +246,7 @@ static int process_p2p_hello(struct iih_info *iih) * when an adjacency is up. This will result in the new * adjacency entry getting added to the lsp tlv neighbor list. */ - adj->circuit_t = iih->circ_type; + adj->circuit_t = iih->calculated_type; isis_adj_state_change(&adj, ISIS_ADJ_INITIALIZING, NULL); adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } @@ -205,45 +277,35 @@ static int process_p2p_hello(struct iih_info *iih) /* 8.2.5.2 a) a match was detected */ if (isis_tlvs_area_addresses_match(iih->tlvs, iih->circuit->area->area_addrs)) { - /* 8.2.5.2 a) 2) If the system is L1 - table 5 */ - if (iih->circuit->area->is_type == IS_LEVEL_1) { + /* 8.2.5.2 a) 2) If the calculated type is L1 - table 5 */ + if (iih->calculated_type == IS_LEVEL_1) { switch (iih->circ_type) { case IS_LEVEL_1: + isis_adj_process_threeway(adj, tw_adj, + iih->calculated_type); + break; case IS_LEVEL_1_AND_2: - if (adj->adj_state != ISIS_ADJ_UP - || adj->adj_usage == ISIS_ADJ_LEVEL1) { + if ((adj->adj_state != ISIS_ADJ_UP) || + (adj->adj_usage == ISIS_ADJ_LEVEL1) || + (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL1); - } - break; - case IS_LEVEL_2: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (7) reject - wrong system type event - */ - zlog_warn("wrongSystemType"); - return ISIS_WARNING; - } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { - /* (6) down - wrong system */ - isis_adj_state_change(&adj, - ISIS_ADJ_DOWN, - "Wrong System"); + iih->calculated_type); } break; } } - /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */ - if (iih->circuit->area->is_type == IS_LEVEL_1_AND_2) { + /* 8.2.5.2 a) 3) If the calculated type is L1L2 - table 6 */ + if (iih->calculated_type == IS_LEVEL_1_AND_2) { switch (iih->circ_type) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP || adj->adj_usage == ISIS_ADJ_LEVEL1) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL1); - } else if ((adj->adj_usage - == ISIS_ADJ_LEVEL1AND2) - || (adj->adj_usage - == ISIS_ADJ_LEVEL2)) { + iih->calculated_type); + } else if ((adj->adj_usage == ISIS_ADJ_LEVEL2) || + (adj->adj_usage == + ISIS_ADJ_LEVEL1AND2)) { /* (8) down - wrong system */ isis_adj_state_change(&adj, ISIS_ADJ_DOWN, @@ -254,10 +316,10 @@ static int process_p2p_hello(struct iih_info *iih) if (adj->adj_state != ISIS_ADJ_UP || adj->adj_usage == ISIS_ADJ_LEVEL2) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL2); - } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) - || (adj->adj_usage - == ISIS_ADJ_LEVEL1AND2)) { + iih->calculated_type); + } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || + (adj->adj_usage == + ISIS_ADJ_LEVEL1AND2)) { /* (8) down - wrong system */ isis_adj_state_change(&adj, ISIS_ADJ_DOWN, @@ -268,10 +330,9 @@ static int process_p2p_hello(struct iih_info *iih) if (adj->adj_state != ISIS_ADJ_UP || adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL1AND2); - } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) - || (adj->adj_usage - == ISIS_ADJ_LEVEL2)) { + iih->calculated_type); + } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || + (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (8) down - wrong system */ isis_adj_state_change(&adj, ISIS_ADJ_DOWN, @@ -282,44 +343,26 @@ static int process_p2p_hello(struct iih_info *iih) } /* 8.2.5.2 a) 4) If the system is L2 - table 7 */ - if (iih->circuit->area->is_type == IS_LEVEL_2) { + if (iih->calculated_type == IS_LEVEL_2) { switch (iih->circ_type) { - case IS_LEVEL_1: - if (adj->adj_state != ISIS_ADJ_UP) { - /* (5) reject - wrong system type event - */ - zlog_warn("wrongSystemType"); - return ISIS_WARNING; - } else if ((adj->adj_usage - == ISIS_ADJ_LEVEL1AND2) - || (adj->adj_usage - == ISIS_ADJ_LEVEL2)) { - /* (6) down - wrong system */ - isis_adj_state_change(&adj, - ISIS_ADJ_DOWN, - "Wrong System"); - } - break; case IS_LEVEL_1_AND_2: - case IS_LEVEL_2: - if (adj->adj_state != ISIS_ADJ_UP - || adj->adj_usage == ISIS_ADJ_LEVEL2) { + if (adj->adj_state != ISIS_ADJ_UP || + adj->adj_usage == ISIS_ADJ_LEVEL2 || + adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL2); - } else if (adj->adj_usage - == ISIS_ADJ_LEVEL1AND2) { - /* (6) down - wrong system */ - isis_adj_state_change(&adj, - ISIS_ADJ_DOWN, - "Wrong System"); + iih->calculated_type); } break; + case IS_LEVEL_2: + isis_adj_process_threeway(adj, tw_adj, + iih->calculated_type); + break; } } } /* 8.2.5.2 b) if no match was detected */ else if (listcount(iih->circuit->area->area_addrs) > 0) { - if (iih->circuit->area->is_type == IS_LEVEL_1) { + if (iih->calculated_type == IS_LEVEL_1) { /* 8.2.5.2 b) 1) is_type L1 and adj is not up */ if (adj->adj_state != ISIS_ADJ_UP) { isis_adj_state_change(&adj, ISIS_ADJ_DOWN, @@ -359,7 +402,7 @@ static int process_p2p_hello(struct iih_info *iih) if (adj->adj_state != ISIS_ADJ_UP || adj->adj_usage == ISIS_ADJ_LEVEL2) { isis_adj_process_threeway(adj, tw_adj, - ISIS_ADJ_LEVEL2); + iih->calculated_type); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - wrong system */ isis_adj_state_change(&adj, diff --git a/isisd/isis_route.c b/isisd/isis_route.c index be92dcc..b907c96 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -260,24 +260,6 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p, ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) SET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); - /* update neighbor router address */ - switch (prefix->family) { - case AF_INET: - if (depth == 2 && prefix->prefixlen == IPV4_MAX_BITLEN) - adj->router_address = prefix->u.prefix4; - break; - case AF_INET6: - if (depth == 2 && prefix->prefixlen == IPV6_MAX_BITLEN - && (!src_p || !src_p->prefixlen)) { - adj->router_address6 = prefix->u.prefix6; - } - break; - default: - flog_err(EC_LIB_DEVELOPMENT, - "%s: unknown address family [%d]", __func__, - prefix->family); - exit(1); - } adjinfo2nexthop(prefix->family, rinfo->nexthops, adj, sr, label_stack); if (!allow_ecmp) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index a2230cd..7a4b45a 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -378,6 +378,8 @@ isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb, static void _isis_spftree_del(struct isis_spftree *spftree) { + void *info, *backup_info; + hash_clean_and_free(&spftree->prefix_sids, NULL); isis_zebra_rlfa_unregister_all(spftree); isis_rlfa_list_clear(spftree); @@ -391,10 +393,12 @@ static void _isis_spftree_del(struct isis_spftree *spftree) list_delete(&spftree->sadj_list); isis_vertex_queue_free(&spftree->tents); isis_vertex_queue_free(&spftree->paths); - isis_route_table_info_free(spftree->route_table->info); - isis_route_table_info_free(spftree->route_table_backup->info); + info = spftree->route_table->info; + backup_info = spftree->route_table_backup->info; route_table_finish(spftree->route_table); route_table_finish(spftree->route_table_backup); + isis_route_table_info_free(info); + isis_route_table_info_free(backup_info); } void isis_spftree_del(struct isis_spftree *spftree) diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index 76cde6d..1d69dbb 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -936,7 +936,6 @@ int sr_if_addr_update(struct interface *ifp) struct isis_circuit *circuit; struct isis_area *area; struct connected *connected; - struct listnode *node; bool need_lsp_regenerate = false; /* Get corresponding circuit */ @@ -948,7 +947,7 @@ int sr_if_addr_update(struct interface *ifp) if (!area) return 0; - FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { + frr_each (if_connected, ifp->connected, connected) { for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { pcfgs[i] = isis_sr_cfg_prefix_find( area, connected->address, i); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index ecf43fa..2b0a58b 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -13,6 +13,11 @@ #include #include +#ifdef CRYPTO_OPENSSL +#include +#include +#endif + #ifdef CRYPTO_INTERNAL #include "md5.h" #endif diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 8252c1a..18a0c49 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -1395,9 +1395,7 @@ void isis_zebra_init(struct event_loop *master, int instance) zclient->zebra_connected = isis_zebra_connected; /* Initialize special zclient for synchronous message exchanges. */ - struct zclient_options options = zclient_options_default; - options.synchronous = true; - zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync = zclient_new(master, &zclient_options_sync, NULL, 0); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_ISIS; zclient_sync->instance = instance; diff --git a/isisd/isisd.c b/isisd/isisd.c index b1064d8..772eb97 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -3345,36 +3345,20 @@ void isis_area_advertise_high_metrics_set(struct isis_area *area, } } -/* - * Returns the path of the file (non-volatile memory) that contains restart - * information. - */ -char *isis_restart_filepath(void) -{ - static char filepath[MAXPATHLEN]; - snprintf(filepath, sizeof(filepath), ISISD_RESTART, ""); - return filepath; -} - /* * Record in non-volatile memory the overload on startup time. */ void isis_restart_write_overload_time(struct isis_area *isis_area, uint32_t overload_time) { - char *filepath; const char *area_name; json_object *json; json_object *json_areas; json_object *json_area; - filepath = isis_restart_filepath(); + json = frr_daemon_state_load(); area_name = isis_area->area_tag; - json = json_object_from_file(filepath); - if (json == NULL) - json = json_object_new_object(); - json_object_object_get_ex(json, "areas", &json_areas); if (!json_areas) { json_areas = json_object_new_object(); @@ -3389,8 +3373,8 @@ void isis_restart_write_overload_time(struct isis_area *isis_area, json_object_int_add(json_area, "overload_time", isis_area->overload_on_startup_time); - json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); - json_object_free(json); + + frr_daemon_state_save(&json); } /* @@ -3398,7 +3382,6 @@ void isis_restart_write_overload_time(struct isis_area *isis_area, */ uint32_t isis_restart_read_overload_time(struct isis_area *isis_area) { - char *filepath; const char *area_name; json_object *json; json_object *json_areas; @@ -3406,12 +3389,9 @@ uint32_t isis_restart_read_overload_time(struct isis_area *isis_area) json_object *json_overload_time; uint32_t overload_time = 0; - filepath = isis_restart_filepath(); area_name = isis_area->area_tag; - json = json_object_from_file(filepath); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "areas", &json_areas); if (!json_areas) { @@ -3433,8 +3413,7 @@ uint32_t isis_restart_read_overload_time(struct isis_area *isis_area) json_object_object_del(json_areas, area_name); - json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); return overload_time; } diff --git a/ldpd/control.c b/ldpd/control.c index db52d46..a08ce4c 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -6,6 +6,7 @@ */ #include +#include #include #include "ldpd.h" diff --git a/ldpd/init.c b/ldpd/init.c index f0cb98e..ef78247 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -229,9 +229,11 @@ send_capability(struct nbr *nbr, uint16_t capability, int enable) * Announcement Parameter in Capability messages sent to * its peers". */ - /* FALLTHROUGH */ + fatalx("send_capability: An LDP speaker MUST NOT include the Dynamic Capability Announcement Parameter"); + break; default: fatalx("send_capability: unsupported capability"); + break; } if (err) { @@ -333,7 +335,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) * parameter and process any other Capability Parameters * in the message". */ - /* FALLTHROUGH */ + fallthrough; default: if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNSSUPORTDCAP, diff --git a/ldpd/lde.c b/ldpd/lde.c index c7e915d..ef4aeca 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -2135,12 +2135,8 @@ static void zclient_sync_retry(struct event *thread) */ static void zclient_sync_init(void) { - struct zclient_options options = zclient_options_default; - - options.synchronous = true; - /* Initialize special zclient for synchronous message exchanges. */ - zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync = zclient_new(master, &zclient_options_sync, NULL, 0); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_LDP; zclient_sync->session_id = 1; /* Distinguish from main session */ diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 33cf93c..1f0fdb5 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -12,6 +12,12 @@ #ifndef _LDP_H_ #define _LDP_H_ +/* this does not include "%s/", frr_runstatedir because the command-line + * override option specifies a *directory* rather than a full file name. + * Therefore the final part is needed on its own. + */ +#define LDPD_SOCK_NAME "ldpd.sock" + /* misc */ #define LDP_VERSION 1 #define LDP_PORT 646 diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c index 906b5c1..f3bcd1b 100644 --- a/ldpd/ldp_vty_exec.c +++ b/ldpd/ldp_vty_exec.c @@ -1106,7 +1106,7 @@ show_lib_msg(struct vty *vty, struct imsg *imsg, struct show_params *params) if (params->lib.remote_label != NO_LABEL && params->lib.remote_label != rt->remote_label) return (0); - /* FALLTHROUGH */ + fallthrough; case IMSG_CTL_SHOW_LIB_RCVD: rt = imsg->data; diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 0fd5d46..df682a1 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -330,7 +330,6 @@ void kif_redistribute(const char *ifname) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - struct listnode *cnode; struct interface *ifp; struct connected *ifc; struct kif kif; @@ -343,7 +342,7 @@ kif_redistribute(const char *ifname) ifp2kif(ifp, &kif); main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif)); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { ifc2kaddr(ifp, ifc, &ka); main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka)); } @@ -400,7 +399,6 @@ ldp_ifp_destroy(struct interface *ifp) static int ldp_interface_status_change(struct interface *ifp) { - struct listnode *node; struct connected *ifc; struct kif kif; struct kaddr ka; @@ -411,12 +409,12 @@ ldp_interface_status_change(struct interface *ifp) main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif)); if (if_is_operative(ifp)) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { ifc2kaddr(ifp, ifc, &ka); main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka)); } } else { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { ifc2kaddr(ifp, ifc, &ka); main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka)); } @@ -682,7 +680,10 @@ static zclient_handler *const ldp_handlers[] = { void ldp_zebra_init(struct event_loop *master) { - if_zapi_callbacks(ldp_ifp_create, ldp_ifp_up, ldp_ifp_down, ldp_ifp_destroy); + hook_register_prio(if_real, 0, ldp_ifp_create); + hook_register_prio(if_up, 0, ldp_ifp_up); + hook_register_prio(if_down, 0, ldp_ifp_down); + hook_register_prio(if_unreal, 0, ldp_ifp_destroy); /* Set default values. */ zclient = zclient_new(master, &zclient_options_default, ldp_handlers, diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 3c616d4..da29a4f 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -9,6 +9,9 @@ */ #include + +#include +#include #include #include "ldpd.h" @@ -101,7 +104,6 @@ void ldp_agentx_enabled(void) enum ldpd_process ldpd_process; #define LDP_DEFAULT_CONFIG "ldpd.conf" -#define LDP_VTY_PORT 2612 /* Master of threads. */ struct event_loop *master; @@ -194,6 +196,7 @@ static const struct frr_yang_module_info *const ldpd_yang_modules[] = { &frr_vrf_info, }; +/* clang-format off */ FRR_DAEMON_INFO(ldpd, LDP, .vty_port = LDP_VTY_PORT, @@ -207,6 +210,7 @@ FRR_DAEMON_INFO(ldpd, LDP, .yang_modules = ldpd_yang_modules, .n_yang_modules = array_size(ldpd_yang_modules), ); +/* clang-format on */ static void ldp_config_fork_apply(struct event *t) { @@ -229,12 +233,8 @@ main(int argc, char *argv[]) int lflag = 0, eflag = 0; int pipe_parent2ldpe[2], pipe_parent2ldpe_sync[2]; int pipe_parent2lde[2], pipe_parent2lde_sync[2]; - char *ctl_sock_name; bool ctl_sock_used = false; - snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, - "", ""); - ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -260,21 +260,8 @@ main(int argc, char *argv[]) break; case OPTION_CTLSOCK: ctl_sock_used = true; - ctl_sock_name = strrchr(LDPD_SOCKET, '/'); - if (ctl_sock_name) - /* skip '/' */ - ctl_sock_name++; - else - /* - * LDPD_SOCKET configured as relative path - * during config? Should really never happen for - * sensible config - */ - ctl_sock_name = (char *)LDPD_SOCKET; - strlcpy(ctl_sock_path, optarg, sizeof(ctl_sock_path)); - strlcat(ctl_sock_path, "/", sizeof(ctl_sock_path)); - strlcat(ctl_sock_path, ctl_sock_name, - sizeof(ctl_sock_path)); + snprintf(ctl_sock_path, sizeof(ctl_sock_path), + "%s/" LDPD_SOCK_NAME, optarg); break; case 'n': init.instance = atoi(optarg); @@ -292,9 +279,9 @@ main(int argc, char *argv[]) } } - if (ldpd_di.pathspace && !ctl_sock_used) - snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, - "/", ldpd_di.pathspace); + if (!ctl_sock_used) + snprintf(ctl_sock_path, sizeof(ctl_sock_path), + "%s/" LDPD_SOCK_NAME, frr_runstatedir); strlcpy(init.user, ldpd_privs.user, sizeof(init.user)); strlcpy(init.group, ldpd_privs.group, sizeof(init.group)); diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 1fec5be..81c6ba3 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -341,7 +341,7 @@ struct iface_ldp_sync { struct iface { RB_ENTRY(iface) entry; - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; ifindex_t ifindex; struct if_addr_head addr_list; struct in6_addr linklocal; @@ -447,7 +447,7 @@ struct ldp_entity_stats { struct l2vpn_if { RB_ENTRY(l2vpn_if) entry; struct l2vpn *l2vpn; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int operative; uint8_t mac[ETH_ALEN]; @@ -464,7 +464,7 @@ struct l2vpn_pw { int af; union ldpd_addr addr; uint32_t pwid; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; bool enabled; uint32_t remote_group; @@ -496,7 +496,7 @@ struct l2vpn { int type; int pw_type; int mtu; - char br_ifname[INTERFACE_NAMSIZ]; + char br_ifname[IFNAMSIZ]; ifindex_t br_ifindex; struct l2vpn_if_head if_tree; struct l2vpn_pw_head pw_tree; @@ -618,7 +618,7 @@ struct kroute { }; struct kaddr { - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int af; union ldpd_addr addr; @@ -627,7 +627,7 @@ struct kaddr { }; struct kif { - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int flags; int operative; @@ -645,7 +645,7 @@ struct acl_check { /* control data structures */ struct ctl_iface { int af; - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; ifindex_t ifindex; int state; enum iface_type type; @@ -656,7 +656,7 @@ struct ctl_iface { }; struct ctl_disc_if { - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; int active_v4; int active_v6; int no_adj; @@ -672,7 +672,7 @@ struct ctl_adj { int af; struct in_addr id; enum hello_type type; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; union ldpd_addr src_addr; uint16_t holdtime; uint16_t holdtime_remaining; @@ -712,7 +712,7 @@ struct ctl_rt { struct ctl_pw { uint16_t type; char l2vpn_name[L2VPN_NAME_LEN]; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; uint32_t pwid; struct in_addr lsr_id; uint32_t local_label; @@ -728,7 +728,7 @@ struct ctl_pw { }; struct ctl_ldp_sync { - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; ifindex_t ifindex; bool in_sync; bool timer_running; diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 5209c55..d40728b 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -505,21 +505,12 @@ nbr_start_idtimer(struct nbr *nbr) { int secs; - secs = INIT_DELAY_TMR; - switch(nbr->idtimer_cnt) { - default: + if (nbr->idtimer_cnt > 2) { /* do not further increase the counter */ secs = MAX_DELAY_TMR; - break; - case 2: - secs *= 2; - /* FALLTHROUGH */ - case 1: - secs *= 2; - /* FALLTHROUGH */ - case 0: + } else { + secs = INIT_DELAY_TMR * (1 << nbr->idtimer_cnt); nbr->idtimer_cnt++; - break; } EVENT_OFF(nbr->initdelay_timer); diff --git a/ldpd/socket.c b/ldpd/socket.c index 6b7e475..71d5c21 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -9,6 +9,7 @@ */ #include +#include #include "ldpd.h" #include "ldpe.h" diff --git a/lib/.gitignore b/lib/.gitignore index 6176b30..1c9314b 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -1,3 +1,4 @@ +/config_paths.h /version.c /version.h /gitversion.h @@ -11,3 +12,4 @@ /grammar_sandbox /clippy /defun_lex.c +vtysh_daemons.h diff --git a/lib/affinitymap.c b/lib/affinitymap.c index 17e1b2c..e53d54a 100644 --- a/lib/affinitymap.c +++ b/lib/affinitymap.c @@ -47,7 +47,7 @@ DEFINE_MTYPE_STATIC(LIB, AFFINITY_MAP_INDEX, "Affinity map index"); DEFINE_QOBJ_TYPE(affinity_maps); DEFINE_QOBJ_TYPE(affinity_map); -struct affinity_maps affinity_map_master = {NULL, NULL, NULL, NULL}; +struct affinity_maps affinity_map_master = {NULL, NULL}; static void affinity_map_free(struct affinity_map *map) { @@ -106,36 +106,6 @@ struct affinity_map *affinity_map_get(const char *name) return NULL; } - -char *affinity_map_name_get(int pos) -{ - struct listnode *node; - struct affinity_map *map; - - if (!affinity_map_master.maps) - return NULL; - - for (ALL_LIST_ELEMENTS_RO(affinity_map_master.maps, node, map)) - if (map->bit_position == pos) - return map->name; - return NULL; -} - -bool affinity_map_check_use_hook(const char *affmap_name) -{ - if (affinity_map_master.check_use_hook) - return (*affinity_map_master.check_use_hook)(affmap_name); - return false; -} - -bool affinity_map_check_update_hook(const char *affmap_name, uint16_t new_pos) -{ - if (affinity_map_master.check_update_hook) - return (*affinity_map_master.check_update_hook)(affmap_name, - new_pos); - return true; -} - void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos) { struct affinity_map *map; @@ -153,18 +123,6 @@ void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos) new_pos); } - -void affinity_map_set_check_use_hook(bool (*func)(const char *affmap_name)) -{ - affinity_map_master.check_use_hook = func; -} - -void affinity_map_set_check_update_hook(bool (*func)(const char *affmap_name, - uint16_t new_pos)) -{ - affinity_map_master.check_update_hook = func; -} - void affinity_map_set_update_hook(void (*func)(const char *affmap_name, uint16_t old_pos, uint16_t new_pos)) diff --git a/lib/affinitymap.h b/lib/affinitymap.h index 19edf5a..ebe2659 100644 --- a/lib/affinitymap.h +++ b/lib/affinitymap.h @@ -50,8 +50,6 @@ DECLARE_QOBJ_TYPE(affinity_map); struct affinity_maps { struct list *maps; - bool (*check_use_hook)(const char *affmap_name); - bool (*check_update_hook)(const char *affmap_name, uint16_t new_pos); void (*update_hook)(const char *affmap_name, uint16_t old_pos, uint16_t new_pos); @@ -60,26 +58,18 @@ struct affinity_maps { DECLARE_QOBJ_TYPE(affinity_maps); extern const struct frr_yang_module_info frr_affinity_map_info; +extern const struct frr_yang_module_info frr_affinity_map_cli_info; void affinity_map_set(const char *name, int pos); void affinity_map_unset(const char *name); struct affinity_map *affinity_map_get(const char *name); -char *affinity_map_name_get(const int pos); -bool affinity_map_check_use_hook(const char *affmap_name); -bool affinity_map_check_update_hook(const char *affmap_name, uint16_t new_pos); void affinity_map_update_hook(const char *affmap_name, uint16_t new_pos); -void affinity_map_set_check_use_hook(bool (*func)(const char *affmap_name)); -void affinity_map_set_check_update_hook(bool (*func)(const char *affmap_name, - uint16_t new_pos)); void affinity_map_set_update_hook(void (*func)(const char *affmap_name, uint16_t old_pos, uint16_t new_pos)); -void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults); - void affinity_map_init(void); diff --git a/lib/affinitymap_cli.c b/lib/affinitymap_cli.c index a2d5e8e..73b91e7 100644 --- a/lib/affinitymap_cli.c +++ b/lib/affinitymap_cli.c @@ -30,15 +30,6 @@ #include "lib/affinitymap.h" #include "lib/affinitymap_cli_clippy.c" -/* Route map node structure. */ -static int affinity_map_config_write(struct vty *vty); -static struct cmd_node affinitymap_node = { - .name = "affinity-map", - .node = AFFMAP_NODE, - .prompt = "", - .config_write = affinity_map_config_write, -}; - /* max value is EXT_ADMIN_GROUP_MAX_POSITIONS - 1 */ DEFPY_YANG_NOSH(affinity_map, affinity_map_cmd, "affinity-map NAME$name bit-position (0-1023)$position", @@ -75,33 +66,32 @@ DEFPY_YANG_NOSH(no_affinity_map, no_affinity_map_cmd, return nb_cli_apply_changes(vty, NULL); } -static int affinity_map_config_write(struct vty *vty) -{ - const struct lyd_node *dnode; - int written = 0; - - dnode = yang_dnode_get(running_config->dnode, "/frr-affinity-map:lib"); - if (dnode) { - nb_cli_show_dnode_cmds(vty, dnode, false); - written = 1; - } - - return written; -} - -void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode, +static void cli_show_affinity_map(struct vty *vty, const struct lyd_node *dnode, bool show_defaults __attribute__((__unused__))) { vty_out(vty, "affinity-map %s bit-position %u\n", - yang_dnode_get_string(dnode, "./name"), - yang_dnode_get_uint16(dnode, "./value")); + yang_dnode_get_string(dnode, "name"), + yang_dnode_get_uint16(dnode, "value")); } +const struct frr_yang_module_info frr_affinity_map_cli_info = { + .name = "frr-affinity-map", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-affinity-map:lib/affinity-maps/affinity-map", + .cbs.cli_show = cli_show_affinity_map, + }, + { + .xpath = NULL, + }, + } +}; + /* Initialization of affinity map vector. */ void affinity_map_init(void) { /* CLI commands. */ - install_node(&affinitymap_node); install_element(CONFIG_NODE, &affinity_map_cmd); install_element(CONFIG_NODE, &no_affinity_map_cmd); } diff --git a/lib/affinitymap_northbound.c b/lib/affinitymap_northbound.c index 331075f..003e0c1 100644 --- a/lib/affinitymap_northbound.c +++ b/lib/affinitymap_northbound.c @@ -47,11 +47,6 @@ static int lib_affinity_map_destroy(struct nb_cb_destroy_args *args) switch (args->event) { case NB_EV_VALIDATE: - if (!affinity_map_check_use_hook(name)) - break; - snprintf(args->errmsg, args->errmsg_len, - "affinity-map %s is used", name); - return NB_ERR_VALIDATION; case NB_EV_PREPARE: case NB_EV_ABORT: break; @@ -68,7 +63,6 @@ static int lib_affinity_map_destroy(struct nb_cb_destroy_args *args) static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args) { const char *name; - char *map_name; uint16_t pos; name = yang_dnode_get_string( @@ -79,20 +73,6 @@ static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args) switch (args->event) { case NB_EV_VALIDATE: - map_name = affinity_map_name_get(pos); - if (map_name && - strncmp(map_name, name, AFFINITY_NAME_SIZE) != 0) { - snprintf(args->errmsg, args->errmsg_len, - "bit-position is used by %s.", map_name); - return NB_ERR_VALIDATION; - } - if (!affinity_map_check_update_hook(name, pos)) { - snprintf( - args->errmsg, args->errmsg_len, - "affinity-map new bit-position > 31 but is used with standard admin-groups"); - return NB_ERR_VALIDATION; - } - break; case NB_EV_PREPARE: case NB_EV_ABORT: break; @@ -105,11 +85,6 @@ static int lib_affinity_map_value_modify(struct nb_cb_modify_args *args) return NB_OK; } -static int lib_affinity_map_value_destroy(struct nb_cb_destroy_args *args) -{ - return NB_OK; -} - /* clang-format off */ const struct frr_yang_module_info frr_affinity_map_info = { .name = "frr-affinity-map", @@ -119,14 +94,13 @@ const struct frr_yang_module_info frr_affinity_map_info = { .cbs = { .create = lib_affinity_map_create, .destroy = lib_affinity_map_destroy, - .cli_show = cli_show_affinity_map, - } + }, + .priority = NB_DFLT_PRIORITY - 1, }, { .xpath = "/frr-affinity-map:lib/affinity-maps/affinity-map/value", .cbs = { .modify = lib_affinity_map_value_modify, - .destroy = lib_affinity_map_value_destroy, } }, { diff --git a/lib/agentx.c b/lib/agentx.c index 45f14c2..70ee675 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -4,12 +4,14 @@ */ #include +#include #ifdef SNMP_AGENTX #include #include #include #include +#include #include "command.h" #include "smux.h" @@ -43,7 +45,7 @@ static void agentx_timeout(struct event *t) static void agentx_read(struct event *t) { - fd_set fds; + netsnmp_large_fd_set lfds; int flags, new_flags = 0; int nonblock = false; struct listnode *ln = EVENT_ARG(t); @@ -68,9 +70,9 @@ static void agentx_read(struct event *t) flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)", strerror(errno), errno); - FD_ZERO(&fds); - FD_SET(EVENT_FD(t), &fds); - snmp_read(&fds); + netsnmp_large_fd_set_init(&lfds, FD_SETSIZE); + netsnmp_large_fd_setfd(t->u.fd, &lfds); + snmp_read2(&lfds); /* Reset the flag */ if (!nonblock) { @@ -85,6 +87,7 @@ static void agentx_read(struct event *t) netsnmp_check_outstanding_agent_requests(); agentx_events_update(); + netsnmp_large_fd_set_cleanup(&lfds); } static void agentx_events_update(void) @@ -92,15 +95,15 @@ static void agentx_events_update(void) int maxfd = 0; int block = 1; struct timeval timeout = {.tv_sec = 0, .tv_usec = 0}; - fd_set fds; + netsnmp_large_fd_set lfds; struct listnode *ln; struct event **thr; int fd, thr_fd; event_cancel(&timeout_thr); - FD_ZERO(&fds); - snmp_select_info(&maxfd, &fds, &timeout, &block); + netsnmp_large_fd_set_init(&lfds, FD_SETSIZE); + snmp_select_info2(&maxfd, &lfds, &timeout, &block); if (!block) { event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout, @@ -118,7 +121,7 @@ static void agentx_events_update(void) /* caught up */ if (thr_fd == fd) { struct listnode *nextln = listnextnode(ln); - if (!FD_ISSET(fd, &fds)) { + if (!netsnmp_large_fd_is_set(fd, &lfds)) { event_cancel(thr); XFREE(MTYPE_TMP, thr); list_delete_node(events, ln); @@ -128,7 +131,7 @@ static void agentx_events_update(void) thr_fd = thr ? EVENT_FD(*thr) : -1; } /* need listener, but haven't hit one where it would be */ - else if (FD_ISSET(fd, &fds)) { + else if (netsnmp_large_fd_is_set(fd, &lfds)) { struct listnode *newln; thr = XCALLOC(MTYPE_TMP, sizeof(struct event *)); @@ -147,6 +150,7 @@ static void agentx_events_update(void) list_delete_node(events, ln); ln = nextln; } + netsnmp_large_fd_set_cleanup(&lfds); } /* AgentX node. */ diff --git a/lib/base64.c b/lib/base64.c index 1507b02..ee2e838 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -9,6 +9,7 @@ #endif #include "base64.h" +#include "compiler.h" static const int CHARS_PER_LINE = 72; static const char *ENCODING = @@ -41,6 +42,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, switch (state_in->step) { while (1) { + fallthrough; case step_A: if (plainchar == plaintextend) { state_in->result = result; @@ -51,7 +53,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; - /* fall through */ + fallthrough; case step_B: if (plainchar == plaintextend) { state_in->result = result; @@ -62,7 +64,7 @@ int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; - /* fall through */ + fallthrough; case step_C: if (plainchar == plaintextend) { state_in->result = result; @@ -146,6 +148,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, switch (state_in->step) { while (1) { + fallthrough; case step_a: do { if (codec == code_in+length_in) { @@ -156,7 +159,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, fragmt = base64_decode_value(*codec++); } while (fragmt < 0); *plainc = (fragmt & 0x03f) << 2; - /* fall through */ + fallthrough; case step_b: do { if (codec == code_in+length_in) { @@ -168,7 +171,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, } while (fragmt < 0); *plainc++ |= (fragmt & 0x030) >> 4; *plainc = (fragmt & 0x00f) << 4; - /* fall through */ + fallthrough; case step_c: do { if (codec == code_in+length_in) { @@ -180,7 +183,7 @@ int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, } while (fragmt < 0); *plainc++ |= (fragmt & 0x03c) >> 2; *plainc = (fragmt & 0x003) << 6; - /* fall through */ + fallthrough; case step_d: do { if (codec == code_in+length_in) { diff --git a/lib/bfd.c b/lib/bfd.c index 8c3246f..2222bb9 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -1282,7 +1282,6 @@ static bool bfd_source_cache_update(struct bfd_source_cache *source, const struct zapi_nexthop *nh = &route->nexthops[nh_index]; const struct interface *interface; const struct connected *connected; - const struct listnode *node; interface = if_lookup_by_index(nh->ifindex, nh->vrf_id); if (interface == NULL) { @@ -1291,8 +1290,7 @@ static bool bfd_source_cache_update(struct bfd_source_cache *source, continue; } - for (ALL_LIST_ELEMENTS_RO(interface->connected, node, - connected)) { + frr_each (if_connected_const, interface->connected, connected) { if (source->address.family != connected->address->family) continue; diff --git a/lib/bitfield.h b/lib/bitfield.h index cc8c311..3fda627 100644 --- a/lib/bitfield.h +++ b/lib/bitfield.h @@ -116,6 +116,7 @@ DECLARE_MTYPE(BITFIELD); (v).m = (v).m + 1; \ (v).data = XREALLOC(MTYPE_BITFIELD, (v).data, \ (v).m * sizeof(word_t)); \ + (v).data[(v).m - 1] = 0; \ } \ } while (0) diff --git a/lib/buffer.h b/lib/buffer.h index 5d98c31..a0b82d2 100644 --- a/lib/buffer.h +++ b/lib/buffer.h @@ -14,21 +14,21 @@ extern "C" { /* Create a new buffer. Memory will be allocated in chunks of the given size. If the argument is 0, the library will supply a reasonable default size suitable for buffering socket I/O. */ -extern struct buffer *buffer_new(size_t); +extern struct buffer *buffer_new(size_t size); /* Free all data in the buffer. */ -extern void buffer_reset(struct buffer *); +extern void buffer_reset(struct buffer *b); /* This function first calls buffer_reset to release all buffered data. Then it frees the struct buffer itself. */ -extern void buffer_free(struct buffer *); +extern void buffer_free(struct buffer *b); /* Add the given data to the end of the buffer. */ -extern void buffer_put(struct buffer *, const void *, size_t); +extern void buffer_put(struct buffer *b, const void *p, size_t size); /* Add a single character to the end of the buffer. */ -extern void buffer_putc(struct buffer *, uint8_t); +extern void buffer_putc(struct buffer *b, uint8_t c); /* Add a NUL-terminated string to the end of the buffer. */ -extern void buffer_putstr(struct buffer *, const char *); +extern void buffer_putstr(struct buffer *b, const char *str); /* Add given data, inline-expanding \n to \r\n */ extern void buffer_put_crlf(struct buffer *b, const void *p, size_t size); @@ -36,10 +36,10 @@ extern void buffer_put_crlf(struct buffer *b, const void *p, size_t size); single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note that this function does not alter the state of the buffer, so the data is still inside waiting to be flushed. */ -char *buffer_getstr(struct buffer *); +char *buffer_getstr(struct buffer *b); /* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */ -int buffer_empty(struct buffer *); +int buffer_empty(struct buffer *b); typedef enum { /* An I/O error occurred. The buffer should be destroyed and the @@ -59,12 +59,12 @@ typedef enum { /* Try to write this data to the file descriptor. Any data that cannot be written immediately is added to the buffer queue. */ -extern buffer_status_t buffer_write(struct buffer *, int fd, const void *, - size_t); +extern buffer_status_t buffer_write(struct buffer *b, int fd, const void *p, + size_t size); /* This function attempts to flush some (but perhaps not all) of the queued data to the given file descriptor. */ -extern buffer_status_t buffer_flush_available(struct buffer *, int fd); +extern buffer_status_t buffer_flush_available(struct buffer *b, int fd); /* The following 2 functions (buffer_flush_all and buffer_flush_window) are for use in lib/vty.c only. They should not be used elsewhere. */ @@ -72,7 +72,7 @@ extern buffer_status_t buffer_flush_available(struct buffer *, int fd); /* Call buffer_flush_available repeatedly until either all data has been flushed, or an I/O error has been encountered, or the operation would block. */ -extern buffer_status_t buffer_flush_all(struct buffer *, int fd); +extern buffer_status_t buffer_flush_all(struct buffer *b, int fd); /* Attempt to write enough data to the given fd to fill a window of the given width and height (and remove the data written from the buffer). @@ -85,7 +85,7 @@ extern buffer_status_t buffer_flush_all(struct buffer *, int fd); to return -1 (because the logic for handling the erase and more features is too complicated to retry the write later). */ -extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width, +extern buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, int height, int erase, int no_more); #ifdef __cplusplus diff --git a/lib/command.c b/lib/command.c index 86da488..fa26072 100644 --- a/lib/command.c +++ b/lib/command.c @@ -10,6 +10,10 @@ */ #include +#include +#include +#include + #include #include "command.h" @@ -39,6 +43,8 @@ #include "frrscript.h" +#include "lib/config_paths.h" + DEFINE_MTYPE_STATIC(LIB, HOST, "Host config"); DEFINE_MTYPE(LIB, COMPLETION, "Completion item"); @@ -1629,6 +1635,10 @@ static int vty_write_config(struct vty *vty) return CMD_SUCCESS; } +/* cross-reference frr_daemon_state_save in libfrr.c + * the code there is similar but not identical (state files always use the same + * name for the new write, and don't keep a backup of previous state.) + */ static int file_write_config(struct vty *vty) { int fd, dirfd; diff --git a/lib/command.h b/lib/command.h index 718d34b..04c66ad 100644 --- a/lib/command.h +++ b/lib/command.h @@ -82,12 +82,16 @@ enum node_type { AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ ENABLE_NODE, /* Enable node. */ CONFIG_NODE, /* Config node. Default mode of config file. */ + PREFIX_NODE, /* ip prefix-list node. */ + PREFIX_IPV6_NODE, /* ipv6 prefix-list node. */ DEBUG_NODE, /* Debug node. */ VRF_DEBUG_NODE, /* Vrf Debug node. */ NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */ DEBUG_VNC_NODE, /* Debug VNC node. */ RMAP_DEBUG_NODE, /* Route-map debug node */ RESOLVER_DEBUG_NODE, /* Resolver debug node */ + MGMT_BE_DEBUG_NODE, /* mgmtd backend-client debug node */ + MGMT_FE_DEBUG_NODE, /* mgmtd frontend-client debug node */ AAA_NODE, /* AAA node. */ EXTLOG_NODE, /* RFC5424 & co. extended syslog */ KEYCHAIN_NODE, /* Key-chain node. */ @@ -131,10 +135,8 @@ enum node_type { ISIS_NODE, /* ISIS protocol mode */ ISIS_FLEX_ALGO_NODE, /* ISIS Flex Algo mode */ ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ ACCESS_IPV6_NODE, /* Access list node. */ ACCESS_MAC_NODE, /* MAC access list node*/ - PREFIX_IPV6_NODE, /* Prefix list node. */ AS_LIST_NODE, /* AS list node. */ COMMUNITY_LIST_NODE, /* Community list node. */ COMMUNITY_ALIAS_NODE, /* Community alias node. */ @@ -158,6 +160,7 @@ enum node_type { SRV6_NODE, /* SRv6 node */ SRV6_LOCS_NODE, /* SRv6 locators node */ SRV6_LOC_NODE, /* SRv6 locator node */ + SRV6_ENCAP_NODE, /* SRv6 encapsulation node */ VTY_NODE, /* Vty node. */ FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ @@ -174,6 +177,8 @@ enum node_type { BMP_NODE, /* BMP config under router bgp */ ISIS_SRV6_NODE, /* ISIS SRv6 node */ ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */ + MGMTD_NODE, /* MGMTD node. */ + RPKI_VRF_NODE, /* RPKI node for VRF */ NODE_TYPE_MAX, /* maximum */ }; /* clang-format on */ @@ -286,6 +291,10 @@ struct cmd_node { #define DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) \ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) +#define DEFPY_YANG_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \ + CMD_ATTR_YANG | CMD_ATTR_HIDDEN) + #define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_YANG | CMD_ATTR_NOSH) @@ -310,6 +319,10 @@ struct cmd_node { #define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH) +#define DEFUN_YANG_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \ + CMD_ATTR_YANG | CMD_ATTR_HIDDEN) + #define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_YANG | CMD_ATTR_NOSH) diff --git a/lib/command_match.c b/lib/command_match.c index f740b72..97e6aeb 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -405,10 +405,10 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, listnode_add(next, newstack); break; case partly_match: - trace_matcher("trivial_match\n"); + trace_matcher("partly_match\n"); if (exact_match_exists && !last_token) break; - /* fallthru */ + fallthrough; case exact_match: trace_matcher("exact_match\n"); if (last_token) { diff --git a/lib/compiler.h b/lib/compiler.h index ce67276..0326105 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -32,8 +32,8 @@ extern "C" { #if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5) # define _RET_NONNULL , returns_nonnull #endif -#if __has_attribute(fallthrough) -# define _FALLTHROUGH __attribute__((fallthrough)); +#if __has_attribute(fallthrough) && !defined(__cplusplus) +# define fallthrough __attribute__((fallthrough)); #endif # define _CONSTRUCTOR(x) constructor(x) # define _DEPRECATED(x) deprecated(x) @@ -56,8 +56,8 @@ extern "C" { #if __GNUC__ < 5 # define __has_attribute(x) 0 #endif -#if __GNUC__ >= 7 -# define _FALLTHROUGH __attribute__((fallthrough)); +#if __GNUC__ >= 7 && !defined(__cplusplus) +# define fallthrough __attribute__((fallthrough)); #endif #endif @@ -112,8 +112,8 @@ extern "C" { #ifndef _ALLOC_SIZE # define _ALLOC_SIZE(x) #endif -#ifndef _FALLTHROUGH -#define _FALLTHROUGH +#if !defined(fallthrough) && !defined(__cplusplus) +#define fallthrough #endif #ifndef _DEPRECATED #define _DEPRECATED(x) deprecated @@ -424,10 +424,10 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8, * type.) */ #ifndef __cplusplus -#define prefixtype(uname, typename, fieldname) typename *fieldname; +#define uniontype(uname, typename, fieldname) typename *fieldname; #define TRANSPARENT_UNION __attribute__((transparent_union)) #else -#define prefixtype(uname, typename, fieldname) \ +#define uniontype(uname, typename, fieldname) \ typename *fieldname; \ uname(typename *x) \ { \ diff --git a/lib/config_paths.h.in b/lib/config_paths.h.in new file mode 100644 index 0000000..cc40905 --- /dev/null +++ b/lib/config_paths.h.in @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* autogenerated by configure / config.status */ + +/* IF YOU ARE INCLUDING THIS FILE FROM A DAEMON OR ZEBRA, YOU ARE PROBABLY + * DOING SOMETHING WRONG. Check for / add a library function that retrieves + * the path you need. + * + * Only libfrr and watchfrr should be including this file. + */ + +/* the replacements for these are emitted by AX_SUBST_EXPANDED, which also + * adds the e_ prefix + */ +#define FRR_RUNSTATE_PATH "@e_frr_runstatedir@" +#define FRR_LIBSTATE_PATH "@e_frr_libstatedir@" +#define YANG_MODELS_PATH "@e_yangmodelsdir@" +#define MODULE_PATH "@e_moduledir@" +#define SCRIPT_PATH "@e_scriptdir@" + +/* for extra footgunning, this one has a trailing slash */ +#define SYSCONFDIR "@e_frr_sysconfdir@/" + +#define VTYSH_BIN_PATH "@e_vtysh_bin@" +#define WATCHFRR_SH_PATH "@e_watchfrr_sh@" diff --git a/lib/darr.c b/lib/darr.c index bef51b8..7a01274 100644 --- a/lib/darr.c +++ b/lib/darr.c @@ -9,7 +9,8 @@ #include "darr.h" #include "memory.h" -DEFINE_MTYPE_STATIC(LIB, DARR, "Dynamic Array"); +DEFINE_MTYPE(LIB, DARR, "Dynamic Array"); +DEFINE_MTYPE(LIB, DARR_STR, "Dynamic Array String"); static uint _msb(uint count) { @@ -52,29 +53,75 @@ static size_t darr_size(uint count, size_t esize) return count * esize + sizeof(struct darr_metadata); } -void *__darr_resize(void *a, uint count, size_t esize) +char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap) +{ + size_t inlen = concat ? darr_strlen(*sp) : 0; + size_t capcount = strlen(fmt) + MIN(inlen + 64, 128); + ssize_t len; + va_list ap_copy; + + darr_ensure_cap(*sp, capcount); + + if (!concat) + darr_reset(*sp); + + /* code below counts on having a NUL terminated string */ + if (darr_len(*sp) == 0) + *darr_append(*sp) = 0; +again: + va_copy(ap_copy, ap); + len = vsnprintf(darr_last(*sp), darr_avail(*sp) + 1, fmt, ap_copy); + va_end(ap_copy); + if (len < 0) + darr_in_strcat(*sp, fmt); + else if ((size_t)len <= darr_avail(*sp)) + _darr_len(*sp) += len; + else { + darr_ensure_cap(*sp, darr_len(*sp) + (size_t)len); + goto again; + } + return *sp; +} + +char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)__darr_in_vsprintf(sp, concat, fmt, ap); + va_end(ap); + return *sp; +} + + +void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mtype) { uint ncount = darr_next_count(count, esize); size_t osz = (a == NULL) ? 0 : darr_size(darr_cap(a), esize); size_t sz = darr_size(ncount, esize); - struct darr_metadata *dm = XREALLOC(MTYPE_DARR, - a ? _darr_meta(a) : NULL, sz); + struct darr_metadata *dm; - if (sz > osz) - memset((char *)dm + osz, 0, sz - osz); + if (a) { + dm = XREALLOC(_darr_meta(a)->mtype, _darr_meta(a), sz); + if (sz > osz) + memset((char *)dm + osz, 0, sz - osz); + } else { + dm = XCALLOC(mtype, sz); + dm->mtype = mtype; + } dm->cap = ncount; return (void *)(dm + 1); } -void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero) +void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero, + struct memtype *mtype) { - struct darr_metadata *dm; uint olen, nlen; if (!a) - a = __darr_resize(NULL, at + count, esize); + a = __darr_resize(NULL, at + count, esize, mtype); dm = (struct darr_metadata *)a - 1; olen = dm->len; @@ -89,7 +136,7 @@ void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero) nlen = olen + count; if (nlen > dm->cap) { - a = __darr_resize(a, nlen, esize); + a = __darr_resize(a, nlen, esize, mtype); dm = (struct darr_metadata *)a - 1; } diff --git a/lib/darr.h b/lib/darr.h index ca46fb3..404869d 100644 --- a/lib/darr.h +++ b/lib/darr.h @@ -3,22 +3,37 @@ * June 23 2023, Christian Hopps * * Copyright (c) 2023, LabN Consulting, L.L.C. - * + */ +#ifndef _FRR_DARR_H_ +#define _FRR_DARR_H_ + +/* * API functions: * ============== * - darr_append + * - darr_append_mt * - darr_append_n + * - darr_append_n_mt * - darr_append_nz + * - darr_append_nz_mt * - darr_cap + * - darr_ensure_avail + * - darr_ensure_avail_mt * - darr_ensure_cap + * - darr_ensure_cap_mt * - darr_ensure_i - * - darr_foreach_i - * - darr_foreach_p + * - darr_ensure_i_mt * - darr_free * - darr_insert + * - darr_insert_mt * - darr_insertz + * - darr_insertz_mt * - darr_insert_n + * - darr_insert_n_mt * - darr_insert_nz + * - darr_insert_nz_mt + * - darr_last + * - darr_lasti * - darr_len * - darr_maxi * - darr_pop @@ -28,38 +43,81 @@ * - darr_remove_n * - darr_reset * - darr_setlen + * + * Iteration + * --------- + * - darr_foreach_i + * - darr_foreach_p + * + * String Utilities + * ---------------- + * - darr_in_strcat_tail + * - darr_in_strcatf, darr_in_vstrcatf + * - darr_in_strdup + * - darr_in_strdup_cap + * - darr_in_sprintf, darr_in_vsprintf + * - darr_set_strlen + * - darr_strdup + * - darr_strdup_cap + * - darr_strlen + * - darr_strnul + * - darr_sprintf, darr_vsprintf */ /* * A few assured items * * - DAs will never have capacity 0 unless they are NULL pointers. */ + +/* + * NOTE: valgrind by default enables a "length64" heuristic (among others) which + * identifies "interior-pointer" 8 bytes forward of a "start-pointer" as a + * "start-pointer". This should cause what normally would be "possibly-lost" + * errors to instead be definite for dynamic arrays. This is b/c the header is 8 bytes + */ + #include +#include +#include "memory.h" + +DECLARE_MTYPE(DARR); +DECLARE_MTYPE(DARR_STR); struct darr_metadata { - uint len; - uint cap; + uint32_t len; + uint32_t cap; + struct memtype *mtype; }; -void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero); -void *__darr_resize(void *a, uint count, size_t esize); -#define _darr_esize(A) sizeof((A)[0]) -#define darr_esize(A) sizeof((A)[0]) -#define _darr_len(A) _darr_meta(A)->len -#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1) -#define _darr_resize(A, C) ({ (A) = __darr_resize((A), C, _darr_esize(A)); }) +void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero, + struct memtype *mt); +char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...) + PRINTFRR(3, 4); +char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap) + PRINTFRR(3, 0); +void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt); + + +#define _darr_esize(A) sizeof((A)[0]) +#define darr_esize(A) sizeof((A)[0]) +#define _darr_len(A) _darr_meta(A)->len +#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1) +#define _darr_resize_mt(A, C, MT) \ + ({ (A) = __darr_resize(A, C, _darr_esize(A), MT); }) +#define _darr_resize(A, C) _darr_resize_mt(A, C, MTYPE_DARR) /* Get the current capacity of the array */ #define darr_cap(A) (((A) == NULL) ? 0 : _darr_meta(A)->cap) +/* Get the current available expansion space */ +#define darr_avail(A) (((A) == NULL) ? 0 : (darr_cap(A) - darr_len(A))) + /* Get the largest possible index one can `darr_ensure_i` w/o resizing */ #define darr_maxi(A) ((int)darr_cap(A) - 1) /** - * Get the current length of the array. - * - * As long as `A` is non-NULL, this macro may be used as an L-value to modify - * the length of the array. + * darr_len() - Get the current length of the array as a unsigned int. + * darr_ilen() - Get the current length of the array as an int. * * Args: * A: The dynamic array, can be NULL. @@ -67,7 +125,19 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * The current length of the array. */ -#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len) +#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len) +#define darr_ilen(A) (((A) == NULL) ? 0 : (ssize_t)_darr_meta(A)->len) + +/** + * darr_lasti() - Get the last element's index. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * The current last element index, or -1 for none. + */ +#define darr_lasti(A) (darr_ilen(A) - 1) /** * Set the current length of the array `A` to 0. @@ -96,11 +166,41 @@ void *__darr_resize(void *a, uint count, size_t esize); assert((A) || !(L)); \ if ((A)) { \ /* have to cast to avoid compiler warning for "0" */ \ - assert((long long)darr_cap(A) >= (L)); \ + assert((long long)darr_cap(A) >= (long long)(L)); \ _darr_len(A) = (L); \ } \ } while (0) +/** + * Set the string length of the array `S` to `L`, and NUL + * terminate the string at L. The dynamic array length will be `L` + 1. + * + * Thus after calling: + * + * darr_len(S) == L + 1 + * darr_strlen(S) == L + * S[L] == 0 + * + * This function does *not* guarantee the `L` + 1 memory is allocated to + * the array, use `darr_ensure` or `*_cap` functions for that. + * + * Args: + * S: The dynamic array, cannot be NULL. + * L: The new str length of the array, will set + * + * Return: + * A pointer to the end of S (i.e., pointing to the NUL byte). + */ +#define darr_set_strlen(S, L) \ + ({ \ + assert((S)); \ + /* have to cast to avoid compiler warning for "0" */ \ + assert((long long)darr_cap(S) >= (long long)(L)); \ + _darr_len(S) = (L) + 1; \ + *darr_last(S) = 0; \ + darr_last(S); \ + }) + /** * Free memory allocated for the dynamic array `A` * @@ -111,11 +211,38 @@ void *__darr_resize(void *a, uint count, size_t esize); #define darr_free(A) \ do { \ if ((A)) { \ - free(_darr_meta(A)); \ + struct darr_metadata *__meta = _darr_meta(A); \ + XFREE(__meta->mtype, __meta); \ (A) = NULL; \ } \ } while (0) +/** + * Make sure that there is room in the dynamic array `A` to add `C` elements. + * + * Available space is `darr_cap(a) - darr_len(a)`. + * + * The value `A` may be changed as a result of this call in which case any + * pointers into the previous memory block are no longer valid. The `A` value + * is guaranteed not to change if there is sufficient capacity in the array. + * + * Args: + * A: (IN/OUT) the dynamic array, can be NULL. + * S: Amount of free space to guarantee. + * + * Return: + * A pointer to the (possibly moved) array. + */ +#define darr_ensure_avail_mt(A, S, MT) \ + ({ \ + ssize_t need = (ssize_t)(S) - \ + (ssize_t)(darr_cap(A) - darr_len(A)); \ + if (need > 0) \ + _darr_resize_mt((A), darr_cap(A) + need, MT); \ + (A); \ + }) +#define darr_ensure_avail(A, S) darr_ensure_avail_mt(A, S, MTYPE_DARR) + /** * Make sure that there is room in the dynamic array `A` for `C` elements. * @@ -123,19 +250,26 @@ void *__darr_resize(void *a, uint count, size_t esize); * pointers into the previous memory block are no longer valid. The `A` value * is guaranteed not to change if there is sufficient capacity in the array. * + * The exception to the no-change rule is if @C is passed as 0, it will be + * considered 1 so that an array is always allocated if currently NULL, + * i.e., @A will never be NULL after a call to darr_ensure_cap_mt() + * * Args: * A: (IN/OUT) the dynamic array, can be NULL. - * I: the index to guarantee memory exists for + * C: Total capacity to guarantee. * * Return: * A pointer to the (possibly moved) array. */ -#define darr_ensure_cap(A, C) \ +#define darr_ensure_cap_mt(A, C, MT) \ ({ \ - if (darr_cap(A) < (C)) \ - _darr_resize((A), (C)); \ + /* Cast to avoid warning when C == 0 */ \ + uint _c = (C) > 0 ? (C) : 1; \ + if ((size_t)darr_cap(A) < _c) \ + _darr_resize_mt((A), _c, MT); \ (A); \ }) +#define darr_ensure_cap(A, C) darr_ensure_cap_mt(A, C, MTYPE_DARR) /** * Return a pointer to the (I)th element of array `A`, making sure there is @@ -155,18 +289,22 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the (I)th element in `A` */ -#define darr_ensure_i(A, I) \ +#define darr_ensure_i_mt(A, I, MT) \ ({ \ - if ((int)(I) > darr_maxi(A)) \ - _darr_resize((A), (I) + 1); \ - if ((I) + 1 > _darr_len(A)) \ - _darr_len(A) = (I) + 1; \ - &(A)[I]; \ + assert((int)(I) >= 0 && (uint)(I) <= INT_MAX); \ + int _i = (int)(I); \ + if (_i > darr_maxi(A)) \ + _darr_resize_mt((A), _i + 1, MT); \ + assert((A) != NULL); \ + if ((uint)_i + 1 > _darr_len(A)) \ + _darr_len(A) = _i + 1; \ + &(A)[_i]; \ }) +#define darr_ensure_i(A, I) darr_ensure_i_mt(A, I, MTYPE_DARR) -#define _darr_insert_n(A, I, N, Z) \ +#define _darr_insert_n(A, I, N, Z, MT) \ ({ \ - (A) = __darr_insert_n(A, I, N, _darr_esize(A), Z); \ + (A) = __darr_insert_n(A, I, N, _darr_esize(A), Z, MT); \ &(A)[I]; \ }) /** @@ -187,8 +325,10 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the first inserted element in the array. */ -#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false) -#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true) +#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false, MTYPE_DARR) +#define darr_insert_n_mt(A, I, N) _darr_insert_n(A, I, N, false, MT) +#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true, MTYPE_DARR) +#define darr_insert_nz_mt(A, I, N) _darr_insert_n(A, I, N, true, MT) /** * Insert an uninitialized element in the array at index `I`. @@ -208,8 +348,10 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the element in the array. */ -#define darr_insert(A, I) _darr_insert_n(A, I, 1, false) -#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true) +#define darr_insert(A, I) _darr_insert_n(A, I, 1, false, MTYPE_DARR) +#define darr_insert_mt(A, I) _darr_insert_n(A, I, 1, false, MT) +#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true, MTYPE_DARR) +#define darr_insertz_mt(A, I) _darr_insert_n(A, I, 1, true, MT) /** * Remove `N` elements from the array starting at index `I`. @@ -247,10 +389,10 @@ void *__darr_resize(void *a, uint count, size_t esize); #define darr_remove(A, I) darr_remove_n(A, I, 1) -#define _darr_append_n(A, N, Z) \ +#define _darr_append_n(A, N, Z, MT) \ ({ \ uint __len = darr_len(A); \ - darr_ensure_cap(A, __len + (N)); \ + darr_ensure_cap_mt(A, __len + (N), MT); \ _darr_len(A) = __len + (N); \ if (Z) \ memset(&(A)[__len], 0, (N)*_darr_esize(A)); \ @@ -267,8 +409,10 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the first of the added elements at the end of the array. */ -#define darr_append_n(A, N) _darr_append_n(A, N, false) -#define darr_append_nz(A, N) _darr_append_n(A, N, true) +#define darr_append_n(A, N) _darr_append_n(A, N, false, MTYPE_DARR) +#define darr_append_n_mt(A, N, MT) _darr_append_n(A, N, false, MT) +#define darr_append_nz(A, N) _darr_append_n(A, N, true, MTYPE_DARR) +#define darr_append_nz_mt(A, N, MT) _darr_append_n(A, N, true, MT) /** * Extending the array's length by 1. @@ -281,8 +425,10 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the new element at the end of the array. */ -#define darr_append(A) _darr_append_n(A, 1, false) -#define darr_appendz(A) _darr_append_n(A, 1, true) +#define darr_append(A) _darr_append_n(A, 1, false, MTYPE_DARR) +#define darr_append_mt(A, MT) _darr_append_n(A, 1, false, MT) +#define darr_appendz(A) _darr_append_n(A, 1, true, MTYPE_DARR) +#define darr_appendz_mt(A, MT) _darr_append_n(A, 1, true, MT) /** * Append an element `E` onto the array `A`, extending it's length by 1. @@ -295,8 +441,10 @@ void *__darr_resize(void *a, uint count, size_t esize); * Return: * A pointer to the element in the array. */ -#define darr_push(A, E) (*darr_append(A) = (E)) -#define darr_pushz(A) (darr_appendz(A)) +#define darr_push(A, E) (*darr_append(A) = (E)) +#define darr_push_mt(A, E, MT) (*darr_append_mt(A, MT) = (E)) +#define darr_pushz(A) (darr_appendz(A)) +#define darr_pushz_mt(A, MT) (darr_appendz_mt(A, MT)) /** @@ -344,6 +492,246 @@ void *__darr_resize(void *a, uint count, size_t esize); */ #define darr_end(A) ((A) + darr_len(A)) +/** + * darr_last() - Get a pointer to the last element of the array. + * darr_strnul() - Get a pointer to the NUL byte of the darr string or NULL. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * A pointer to the last element of the array or NULL if the array is + * empty. + */ +#define darr_last(A) \ + ({ \ + uint __len = darr_len(A); \ + ((__len > 0) ? &(A)[__len - 1] : NULL); \ + }) +#define darr_strnul(S) darr_last(S) + +/** + * darr_in_sprintf() - sprintf into D. + * + * Args: + * D: The destination darr, D's value may be NULL. + * F: The format string + * ...: variable arguments for format string. + * + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_in_sprintf(D, F, ...) __darr_in_sprintf(&(D), 0, F, __VA_ARGS__) + + +/** + * darr_in_strcat() - concat a string into a darr string. + * + * Args: + * D: The destination darr, D's value may be NULL. + * S: The string to concat onto D. + * + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_in_strcat(D, S) \ + ({ \ + uint __dlen = darr_strlen(D); \ + uint __slen = strlen(S); \ + darr_ensure_cap_mt(D, __dlen + __slen + 1, MTYPE_DARR_STR); \ + if (darr_len(D) == 0) \ + *darr_append(D) = 0; \ + memcpy(darr_last(D), (S), __slen + 1); \ + _darr_len(D) += __slen; \ + D; \ + }) + +/** + * darr_in_strcatf() - concat a formatted string into a darr string. + * + * Args: + * D: The destination darr, D's value may be NULL. + * F: The format string to concat onto D after adding arguments. + * ...: The arguments for the format string. + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_in_strcatf(D, F, ...) \ + __darr_in_sprintf(&(D), true, (F), __VA_ARGS__) + +/** + * darr_in_strcat_tail() - copies end of one darr str to another. + * + * This is a rather specialized function, it takes 2 darr's, a destination and a + * source. If the source is not longer than the destination nothing is done. + * Otherwise the characters in the source that lie beyond the length of the dest + * are added to the dest. No checking is done to make sure the common prefix + * matches. For example: + * + * D: "/foo" + * S: "/foo/bar" + * -> D: "/foo/bar" + * + * perhaps surprising results: + * D: "/foo" + * S: "/zoo/bar" + * -> D: "/foo/bar" + * + * Args: + * D: The destination darr, D's value may be NULL. + * S: The string to copy the tail from. + * + * Return: + * The dynamic_array D with the extended string content. + */ +#define darr_in_strcat_tail(D, S) \ + ({ \ + int __dsize, __ssize, __extra; \ + \ + if (darr_len(D) == 0) \ + *darr_append(D) = 0; \ + __dsize = darr_ilen(D); \ + __ssize = darr_ilen(S); \ + __extra = __ssize - __dsize; \ + if (__extra > 0) { \ + darr_ensure_cap_mt(D, (uint)__ssize, MTYPE_DARR_STR); \ + memcpy(darr_last(D), (S) + __dsize - 1, __extra + 1); \ + _darr_len(D) += __extra; \ + } \ + D; \ + }) + +/** + * darr_in_strdup_cap() - duplicate the string into a darr reserving capacity. + * darr_in_strdup() - duplicate the string into a darr. + * + * Args: + * D: The destination darr, D's value may be NULL. + * S: The string to duplicate. + * C: The capacity to reserve. + * + * Return: + * The dynamic_array D with the duplicated string. + */ +#define darr_in_strdup_cap(D, S, C) \ + ({ \ + size_t __size = strlen(S) + 1; \ + darr_reset(D); \ + darr_ensure_cap_mt(D, \ + ((size_t)(C) > __size) ? (size_t)(C) \ + : __size, \ + MTYPE_DARR_STR); \ + strlcpy(D, (S), darr_cap(D)); \ + darr_setlen((D), (size_t)__size); \ + D; \ + }) +#define darr_in_strdup(D, S) darr_in_strdup_cap(D, S, 1) + +/** + * darr_in_vsprintf() - vsprintf into D. + * + * Args: + * D: The destination darr, D's value may be NULL. + * F: The format string + * A: Varargs + * + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_in_vsprintf(D, F, A) __darr_in_vsprintf(&(D), 0, F, A) + +/** + * darr_in_vstrcatf() - concat a formatted string into a darr string. + * + * Args: + * D: The destination darr, D's value may be NULL. + * F: The format string to concat onto D after adding arguments. + * A: Varargs + * + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_in_vstrcatf(D, F, A) __darr_in_vsprintf(&(D), true, (F), (A)) + +/** + * darr_sprintf() - sprintf into a new dynamic array. + * + * Args: + * F: The format string + * ...: variable arguments for format string. + * + * Return: + * A char * dynamic_array with the new string content. + */ +#define darr_sprintf(F, ...) \ + ({ \ + char *d = NULL; \ + __darr_in_sprintf(&d, false, F, __VA_ARGS__); \ + d; \ + }) + +/** + * darr_strdup_cap() - duplicate the string reserving capacity. + * darr_strdup() - duplicate the string into a dynamic array. + * + * Args: + * S: The string to duplicate. + * C: The capacity to reserve. + * + * Return: + * The dynamic_array with the duplicated string. + */ +#define darr_strdup_cap(S, C) \ + ({ \ + size_t __size = strlen(S) + 1; \ + char *__s = NULL; \ + /* Cast to ssize_t to avoid warning when C == 0 */ \ + darr_ensure_cap_mt(__s, \ + ((ssize_t)(C) > (ssize_t)__size) \ + ? (size_t)(C) \ + : __size, \ + MTYPE_DARR_STR); \ + strlcpy(__s, (S), darr_cap(__s)); \ + darr_setlen(__s, (size_t)__size); \ + __s; \ + }) +#define darr_strdup(S) darr_strdup_cap(S, 0) + +/** + * darr_strlen() - get the length of the NUL terminated string in a darr. + * + * Args: + * S: The string to measure, value may be NULL. + * + * Return: + * The length of the NUL terminated string in @S + */ +#define darr_strlen(S) \ + ({ \ + uint __size = darr_len(S); \ + if (__size) \ + __size -= 1; \ + assert(!(S) || ((char *)(S))[__size] == 0); \ + __size; \ + }) + +/** + * darr_vsprintf() - vsprintf into a new dynamic array. + * + * Args: + * F: The format string + * A: Varargs + * + * Return: + * The dynamic_array D with the new string content. + */ +#define darr_vsprintf(F, A) \ + ({ \ + char *d = NULL; \ + darr_in_vsprintf(d, F, A); \ + d; \ + }) + /** * Iterate over array `A` using a pointer to each element in `P`. * @@ -361,3 +749,5 @@ void *__darr_resize(void *a, uint count, size_t esize); * I: A uint variable to store the current element index in. */ #define darr_foreach_i(A, I) for ((I) = 0; (I) < darr_len(A); (I)++) + +#endif /* _FRR_DARR_H_ */ diff --git a/lib/defun_lex.l b/lib/defun_lex.l index 124f864..3104e48 100644 --- a/lib/defun_lex.l +++ b/lib/defun_lex.l @@ -140,6 +140,7 @@ SPECIAL [(),] "DEFPY_ATTR" value = strdup(yytext); return DEFUNNY; "DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY; "DEFPY_YANG" value = strdup(yytext); return DEFUNNY; +"DEFPY_YANG_HIDDEN" value = strdup(yytext); return DEFUNNY; "DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY; "ALIAS" value = strdup(yytext); return DEFUNNY; "ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY; diff --git a/lib/distribute.c b/lib/distribute.c index 6548767..ccd1f13 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -17,8 +17,6 @@ DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE, "Distribute list"); DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_IFNAME, "Dist-list ifname"); DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_NAME, "Dist-list name"); -static struct list *dist_ctx_list; - static struct distribute *distribute_new(void) { return XCALLOC(MTYPE_DISTRIBUTE, sizeof(struct distribute)); @@ -244,11 +242,10 @@ static enum distribute_type distribute_direction(const char *dir, bool v4) __builtin_unreachable(); } -int distribute_list_parser(bool prefix, bool v4, const char *dir, - const char *list, const char *ifname) +int distribute_list_parser(struct distribute_ctx *ctx, bool prefix, bool v4, + const char *dir, const char *list, const char *ifname) { enum distribute_type type = distribute_direction(dir, v4); - struct distribute_ctx *ctx = listnode_head(dist_ctx_list); void (*distfn)(struct distribute_ctx *, const char *, enum distribute_type, const char *) = @@ -259,12 +256,12 @@ int distribute_list_parser(bool prefix, bool v4, const char *dir, return CMD_SUCCESS; } -int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4, - const char *dir, const char *list, - const char *ifname) + +int distribute_list_no_parser(struct distribute_ctx *ctx, struct vty *vty, + bool prefix, bool v4, const char *dir, + const char *list, const char *ifname) { enum distribute_type type = distribute_direction(dir, v4); - struct distribute_ctx *ctx = listnode_head(dist_ctx_list); int ret; int (*distfn)(struct distribute_ctx *, const char *, @@ -274,7 +271,8 @@ int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4, ret = distfn(ctx, ifname, type, list); if (!ret) { - vty_out(vty, "distribute list doesn't exist\n"); + if (vty) + vty_out(vty, "distribute list doesn't exist\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -443,17 +441,130 @@ int config_write_distribute(struct vty *vty, return write; } +/* ---------- */ +/* Northbound */ +/* ---------- */ + +int group_distribute_list_create_helper( + struct nb_cb_create_args *args, struct distribute_ctx *ctx) +{ + nb_running_set_entry(args->dnode, ctx); + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/distribute-lists/distribute-list/{in,out}/{access,prefix}-list + */ + +int group_distribute_list_destroy(struct nb_cb_destroy_args *args) +{ + nb_running_unset_entry(args->dnode); + return NB_OK; +} + +static int distribute_list_leaf_update(const struct lyd_node *dnode, + int ip_version, bool no) +{ + struct distribute_ctx *ctx; + struct lyd_node *dir_node = lyd_parent(dnode); + struct lyd_node_inner *list_node = dir_node->parent; + struct lyd_node *intf_key = list_node->child; + bool ipv4 = ip_version == 4 ? true : false; + bool prefix; + + ctx = nb_running_get_entry_non_rec(&list_node->node, NULL, false); + + prefix = dnode->schema->name[0] == 'p' ? true : false; + if (no) + distribute_list_no_parser(ctx, NULL, prefix, ipv4, + dir_node->schema->name, + lyd_get_value(dnode), + lyd_get_value(intf_key)); + else + distribute_list_parser(ctx, prefix, ipv4, + dir_node->schema->name, + lyd_get_value(dnode), + lyd_get_value(intf_key)); + return NB_OK; +} + +static int distribute_list_leaf_modify(struct nb_cb_modify_args *args, + int ip_version) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + return distribute_list_leaf_update(args->dnode, ip_version, false); +} + +static int distribute_list_leaf_destroy(struct nb_cb_destroy_args *args, + int ip_version) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + return distribute_list_leaf_update(args->dnode, ip_version, true); +} + +int group_distribute_list_ipv4_modify(struct nb_cb_modify_args *args) +{ + return distribute_list_leaf_modify(args, 4); +} +int group_distribute_list_ipv4_destroy(struct nb_cb_destroy_args *args) +{ + return distribute_list_leaf_destroy(args, 4); +} +int group_distribute_list_ipv6_modify(struct nb_cb_modify_args *args) +{ + return distribute_list_leaf_modify(args, 6); +} +int group_distribute_list_ipv6_destroy(struct nb_cb_destroy_args *args) +{ + return distribute_list_leaf_destroy(args, 6); +} + +static int distribute_list_leaf_cli_show(struct vty *vty, + const struct lyd_node *dnode, + int ip_version) +{ + struct lyd_node *dir_node = lyd_parent(dnode); + struct lyd_node_inner *list_node = dir_node->parent; + struct lyd_node *intf_key = list_node->child; + bool ipv6 = ip_version == 6 ? true : false; + bool prefix; + + prefix = dnode->schema->name[0] == 'p' ? true : false; + vty_out(vty, + " %sdistribute-list %s%s %s %s\n", + ipv6 ? "ipv6 " : "", + prefix ? "prefix " : "", + lyd_get_value(dnode), + dir_node->schema->name, + lyd_get_value(intf_key)); + + return NB_OK; +} + +void group_distribute_list_ipv4_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + distribute_list_leaf_cli_show(vty, dnode, 4); +} +void group_distribute_list_ipv6_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + distribute_list_leaf_cli_show(vty, dnode, 6); +} + +/* ------------- */ +/* Setup/Cleanup */ +/* ------------- */ + void distribute_list_delete(struct distribute_ctx **ctx) { hash_clean_and_free(&(*ctx)->disthash, (void (*)(void *))distribute_free); - if (dist_ctx_list) { - listnode_delete(dist_ctx_list, *ctx); - if (list_isempty(dist_ctx_list)) - list_delete(&dist_ctx_list); - } - XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx)); } @@ -464,11 +575,9 @@ struct distribute_ctx *distribute_list_ctx_create(struct vrf *vrf) ctx = XCALLOC(MTYPE_DISTRIBUTE_CTX, sizeof(struct distribute_ctx)); ctx->vrf = vrf; - ctx->disthash = hash_create( - distribute_hash_make, - (bool (*)(const void *, const void *))distribute_cmp, NULL); - if (!dist_ctx_list) - dist_ctx_list = list_new(); - listnode_add(dist_ctx_list, ctx); + ctx->disthash = + hash_create(distribute_hash_make, + (bool (*)(const void *, const void *))distribute_cmp, + NULL); return ctx; } diff --git a/lib/distribute.h b/lib/distribute.h index 7578371..a0bc348 100644 --- a/lib/distribute.h +++ b/lib/distribute.h @@ -9,6 +9,7 @@ #include #include "if.h" #include "filter.h" +#include "northbound.h" #ifdef __cplusplus extern "C" { @@ -69,11 +70,43 @@ extern enum filter_type distribute_apply_in(struct interface *, extern enum filter_type distribute_apply_out(struct interface *, struct prefix *); -extern int distribute_list_parser(bool prefix, bool v4, const char *dir, - const char *list, const char *ifname); -extern int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4, +extern int distribute_list_parser(struct distribute_ctx *ctx, bool prefix, + bool v4, const char *dir, const char *list, + const char *ifname); +extern int distribute_list_no_parser(struct distribute_ctx *ctx, + struct vty *vty, bool prefix, bool v4, const char *dir, const char *list, const char *ifname); + +/* + * Northbound + */ + +/* + * Define your own create callback and then call thes helper with your + * distribute list context when a list entry is created. Additionally, plug the + * destroy callback into the frr_module_yang_info struct, or call it if you have + * your own callback destroy function. + */ +extern int group_distribute_list_create_helper(struct nb_cb_create_args *args, + struct distribute_ctx *ctx); +extern int group_distribute_list_destroy(struct nb_cb_destroy_args *args); + +/* + * Plug 3 of these handlers in for your distribute-list for all the northbound + * distribute_list leaf callbacks. If you need multi-protocol then use the + * grouping twice under 2 different containers. + */ +extern int group_distribute_list_ipv4_modify(struct nb_cb_modify_args *args); +extern int group_distribute_list_ipv4_destroy(struct nb_cb_destroy_args *args); +extern void group_distribute_list_ipv4_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +extern int group_distribute_list_ipv6_modify(struct nb_cb_modify_args *args); +extern int group_distribute_list_ipv6_destroy(struct nb_cb_destroy_args *args); +extern void group_distribute_list_ipv6_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); #ifdef __cplusplus } #endif diff --git a/lib/event.c b/lib/event.c index d3bc020..fc46a11 100644 --- a/lib/event.c +++ b/lib/event.c @@ -6,6 +6,8 @@ /* #define DEBUG */ #include + +#include #include #include "frrevent.h" @@ -55,11 +57,6 @@ static int event_timer_cmp(const struct event *a, const struct event *b) DECLARE_HEAP(event_timer_list, struct event, timeritem, event_timer_cmp); -#if defined(__APPLE__) -#include -#include -#endif - #define AWAKEN(m) \ do { \ const unsigned char wakebyte = 0x01; \ @@ -216,7 +213,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_out(vty, - " CPU_Warn Wall_Warn Starv_Warn Type Thread\n"); + " CPU_Warn Wall_Warn Starv_Warn Type Event\n"); if (cpu_records_count(m->cpu_records)) { struct cpu_event_history *rec; @@ -232,13 +229,13 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) } vty_out(vty, "\n"); - vty_out(vty, "Total thread statistics\n"); + vty_out(vty, "Total Event statistics\n"); vty_out(vty, "-------------------------\n"); vty_out(vty, "%30s %18s %18s\n", "", "CPU (user+system):", "Real (wall-clock):"); vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); - vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn"); - vty_out(vty, " Type Thread\n"); + vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn Starv_Warn"); + vty_out(vty, " Type Event\n"); if (tmp.total_calls > 0) vty_out_cpu_event_history(vty, &tmp); @@ -307,13 +304,16 @@ static uint8_t parse_filter(const char *filterstr) return filter; } -DEFUN_NOSH (show_thread_cpu, - show_thread_cpu_cmd, - "show thread cpu [FILTER]", - SHOW_STR - "Thread information\n" - "Thread CPU usage\n" - "Display filter (rwtex)\n") +#if CONFDATE > 20240707 + CPP_NOTICE("Remove `show thread ...` commands") +#endif +DEFUN_NOSH (show_event_cpu, + show_event_cpu_cmd, + "show event cpu [FILTER]", + SHOW_STR + "Event information\n" + "Event CPU usage\n" + "Display filter (rwtexb)\n") { uint8_t filter = (uint8_t)-1U; int idx = 0; @@ -332,6 +332,14 @@ DEFUN_NOSH (show_thread_cpu, return CMD_SUCCESS; } +ALIAS(show_event_cpu, + show_thread_cpu_cmd, + "show thread cpu [FILTER]", + SHOW_STR + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtex)\n") + DEFPY (service_cputime_stats, service_cputime_stats_cmd, "[no] service cputime-stats", @@ -373,7 +381,7 @@ DEFPY (service_walltime_warning, return CMD_SUCCESS; } -static void show_thread_poll_helper(struct vty *vty, struct event_loop *m) +static void show_event_poll_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; @@ -414,24 +422,30 @@ static void show_thread_poll_helper(struct vty *vty, struct event_loop *m) } } -DEFUN_NOSH (show_thread_poll, - show_thread_poll_cmd, - "show thread poll", - SHOW_STR - "Thread information\n" - "Show poll FD's and information\n") +DEFUN_NOSH (show_event_poll, + show_event_poll_cmd, + "show event poll", + SHOW_STR + "Event information\n" + "Event Poll Information\n") { struct listnode *node; struct event_loop *m; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) - show_thread_poll_helper(vty, m); + show_event_poll_helper(vty, m); } return CMD_SUCCESS; } +ALIAS(show_event_poll, + show_thread_poll_cmd, + "show thread poll", + SHOW_STR + "Thread information\n" + "Show poll FD's and information\n") DEFUN (clear_thread_cpu, clear_thread_cpu_cmd, @@ -458,7 +472,7 @@ DEFUN (clear_thread_cpu, return CMD_SUCCESS; } -static void show_thread_timers_helper(struct vty *vty, struct event_loop *m) +static void show_event_timers_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; @@ -475,28 +489,37 @@ static void show_thread_timers_helper(struct vty *vty, struct event_loop *m) } } -DEFPY_NOSH (show_thread_timers, - show_thread_timers_cmd, - "show thread timers", - SHOW_STR - "Thread information\n" - "Show all timers and how long they have in the system\n") +DEFPY_NOSH (show_event_timers, + show_event_timers_cmd, + "show event timers", + SHOW_STR + "Event information\n" + "Show all timers and how long they have in the system\n") { struct listnode *node; struct event_loop *m; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) - show_thread_timers_helper(vty, m); + show_event_timers_helper(vty, m); } return CMD_SUCCESS; } +ALIAS(show_event_timers, + show_thread_timers_cmd, + "show thread timers", + SHOW_STR + "Thread information\n" + "Show all timers and how long they have in the system\n") + void event_cmd_init(void) { install_element(VIEW_NODE, &show_thread_cpu_cmd); + install_element(VIEW_NODE, &show_event_cpu_cmd); install_element(VIEW_NODE, &show_thread_poll_cmd); + install_element(VIEW_NODE, &show_event_poll_cmd); install_element(ENABLE_NODE, &clear_thread_cpu_cmd); install_element(CONFIG_NODE, &service_cputime_stats_cmd); @@ -504,6 +527,7 @@ void event_cmd_init(void) install_element(CONFIG_NODE, &service_walltime_warning_cmd); install_element(VIEW_NODE, &show_thread_timers_cmd); + install_element(VIEW_NODE, &show_event_timers_cmd); } /* CLI end ------------------------------------------------------------------ */ @@ -519,6 +543,7 @@ static void initializer(void) pthread_key_create(&thread_current, NULL); } +#define STUPIDLY_LARGE_FD_SIZE 100000 struct event_loop *event_master_create(const char *name) { struct event_loop *rv; @@ -545,6 +570,13 @@ struct event_loop *event_master_create(const char *name) rv->fd_limit = (int)limit.rlim_cur; } + if (rv->fd_limit > STUPIDLY_LARGE_FD_SIZE) { + zlog_warn("FD Limit set: %u is stupidly large. Is this what you intended? Consider using --limit-fds also limiting size to %u", + rv->fd_limit, STUPIDLY_LARGE_FD_SIZE); + + rv->fd_limit = STUPIDLY_LARGE_FD_SIZE; + } + rv->read = XCALLOC(MTYPE_EVENT_POLL, sizeof(struct event *) * rv->fd_limit); @@ -1594,12 +1626,70 @@ static int thread_process_io_helper(struct event_loop *m, struct event *thread, return 1; } +static inline void thread_process_io_inner_loop(struct event_loop *m, + unsigned int num, + struct pollfd *pfds, nfds_t *i, + uint32_t *ready) +{ + /* no event for current fd? immediately continue */ + if (pfds[*i].revents == 0) + return; + + *ready = *ready + 1; + + /* + * Unless someone has called event_cancel from another + * pthread, the only thing that could have changed in + * m->handler.pfds while we were asleep is the .events + * field in a given pollfd. Barring event_cancel() that + * value should be a superset of the values we have in our + * copy, so there's no need to update it. Similarily, + * barring deletion, the fd should still be a valid index + * into the master's pfds. + * + * We are including POLLERR here to do a READ event + * this is because the read should fail and the + * read function should handle it appropriately + */ + if (pfds[*i].revents & (POLLIN | POLLHUP | POLLERR)) { + thread_process_io_helper(m, m->read[pfds[*i].fd], POLLIN, + pfds[*i].revents, *i); + } + if (pfds[*i].revents & POLLOUT) + thread_process_io_helper(m, m->write[pfds[*i].fd], POLLOUT, + pfds[*i].revents, *i); + + /* + * if one of our file descriptors is garbage, remove the same + * from both pfds + update sizes and index + */ + if (pfds[*i].revents & POLLNVAL) { + memmove(m->handler.pfds + *i, m->handler.pfds + *i + 1, + (m->handler.pfdcount - *i - 1) * sizeof(struct pollfd)); + m->handler.pfdcount--; + m->handler.pfds[m->handler.pfdcount].fd = 0; + m->handler.pfds[m->handler.pfdcount].events = 0; + + memmove(pfds + *i, pfds + *i + 1, + (m->handler.copycount - *i - 1) * sizeof(struct pollfd)); + m->handler.copycount--; + m->handler.copy[m->handler.copycount].fd = 0; + m->handler.copy[m->handler.copycount].events = 0; + + *i = *i - 1; + } +} + /** * Process I/O events. * * Walks through file descriptor array looking for those pollfds whose .revents * field has something interesting. Deletes any invalid file descriptors. * + * Try to impart some impartiality to handling of io. The event + * system will cycle through the fd's available for io + * giving each one a chance to go first. + * * @param m the thread master * @param num the number of active file descriptors (return value of poll()) */ @@ -1607,58 +1697,15 @@ static void thread_process_io(struct event_loop *m, unsigned int num) { unsigned int ready = 0; struct pollfd *pfds = m->handler.copy; + nfds_t i, last_read = m->last_read % m->handler.copycount; - for (nfds_t i = 0; i < m->handler.copycount && ready < num; ++i) { - /* no event for current fd? immediately continue */ - if (pfds[i].revents == 0) - continue; + for (i = last_read; i < m->handler.copycount && ready < num; ++i) + thread_process_io_inner_loop(m, num, pfds, &i, &ready); - ready++; + for (i = 0; i < last_read && ready < num; ++i) + thread_process_io_inner_loop(m, num, pfds, &i, &ready); - /* - * Unless someone has called event_cancel from another - * pthread, the only thing that could have changed in - * m->handler.pfds while we were asleep is the .events - * field in a given pollfd. Barring event_cancel() that - * value should be a superset of the values we have in our - * copy, so there's no need to update it. Similarily, - * barring deletion, the fd should still be a valid index - * into the master's pfds. - * - * We are including POLLERR here to do a READ event - * this is because the read should fail and the - * read function should handle it appropriately - */ - if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { - thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN, - pfds[i].revents, i); - } - if (pfds[i].revents & POLLOUT) - thread_process_io_helper(m, m->write[pfds[i].fd], - POLLOUT, pfds[i].revents, i); - - /* - * if one of our file descriptors is garbage, remove the same - * from both pfds + update sizes and index - */ - if (pfds[i].revents & POLLNVAL) { - memmove(m->handler.pfds + i, m->handler.pfds + i + 1, - (m->handler.pfdcount - i - 1) - * sizeof(struct pollfd)); - m->handler.pfdcount--; - m->handler.pfds[m->handler.pfdcount].fd = 0; - m->handler.pfds[m->handler.pfdcount].events = 0; - - memmove(pfds + i, pfds + i + 1, - (m->handler.copycount - i - 1) - * sizeof(struct pollfd)); - m->handler.copycount--; - m->handler.copy[m->handler.copycount].fd = 0; - m->handler.copy[m->handler.copycount].events = 0; - - i--; - } - } + m->last_read++; } /* Add all timers that have popped to the ready list. */ @@ -1838,12 +1885,6 @@ struct event *event_fetch(struct event_loop *m, struct event *fetch) return fetch; } -static unsigned long timeval_elapsed(struct timeval a, struct timeval b) -{ - return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) - + (a.tv_usec - b.tv_usec)); -} - unsigned long event_consumed_time(RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { diff --git a/lib/filter.c b/lib/filter.c index f86adab..5a0790f 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -410,7 +410,10 @@ void access_list_filter_add(struct access_list *access, filter->prev = access->tail; access->tail = filter; } +} +void access_list_filter_update(struct access_list *access) +{ /* Run hook function. */ if (access->master->add_hook) (*access->master->add_hook)(access); @@ -885,7 +888,7 @@ static void access_list_init_ipv6(void) install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); } -void access_list_init(void) +void access_list_init_new(bool in_backend) { cmd_variable_handler_register(access_list_handlers); @@ -893,7 +896,15 @@ void access_list_init(void) access_list_init_ipv6(); access_list_init_mac(); - filter_cli_init(); + if (!in_backend) { + /* we do not want to handle config commands in the backend */ + filter_cli_init(); + } +} + +void access_list_init(void) +{ + access_list_init_new(false); } void access_list_reset(void) diff --git a/lib/filter.h b/lib/filter.h index e092f07..4fa482b 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -114,6 +114,7 @@ struct access_master { /* Prototypes for access-list. */ extern void access_list_init(void); +extern void access_list_init_new(bool in_backend); extern void access_list_reset(void); extern void access_list_add_hook(void (*func)(struct access_list *)); extern void access_list_delete_hook(void (*func)(struct access_list *)); @@ -124,13 +125,14 @@ extern enum filter_type access_list_apply(struct access_list *access, struct access_list *access_list_get(afi_t afi, const char *name); void access_list_delete(struct access_list *access); struct filter *filter_new(void); -void access_list_filter_add(struct access_list *access, - struct filter *filter); +void access_list_filter_add(struct access_list *access, struct filter *filter); void access_list_filter_delete(struct access_list *access, struct filter *filter); +void access_list_filter_update(struct access_list *access); int64_t filter_new_seq_get(struct access_list *access); extern const struct frr_yang_module_info frr_filter_info; +extern const struct frr_yang_module_info frr_filter_cli_info; /* filter_nb.c */ diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 5c3dc5e..c40c2a7 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -69,59 +69,66 @@ static int64_t acl_get_seq(struct vty *vty, const char *xpath, bool is_remove) return seq; } -static int acl_remove_if_empty(struct vty *vty, const char *iptype, - const char *name) +/** + * Remove main data structure filter list if there are no more entries or + * remark. This fixes compatibility with old CLI and tests. + */ +static int filter_remove_check_empty(struct vty *vty, const char *ftype, + const char *iptype, const char *name, + uint32_t del_seq, bool del_remark) { + const struct lyd_node *remark_dnode = NULL; + const struct lyd_node *entry_dnode = NULL; char xpath[XPATH_MAXLEN]; + uint32_t count; + + /* Count existing entries */ + count = yang_dnode_count(vty->candidate_config->dnode, + "/frr-filter:lib/%s-list[type='%s'][name='%s']/entry", + ftype, iptype, name); + + /* Check entry-to-delete actually exists */ + if (del_seq) { + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/%s-list[type='%s'][name='%s']/entry[sequence='%u']", + ftype, iptype, name, del_seq); + entry_dnode = yang_dnode_get(vty->candidate_config->dnode, + xpath); + + /* If exists, delete and don't count it, we need only remaining entries */ + if (entry_dnode) { + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + count--; + } + } + /* Delete the remark, or check whether it exists if we're keeping it */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='%s'][name='%s']/remark", + "/frr-filter:lib/%s-list[type='%s'][name='%s']/remark", ftype, iptype, name); - /* List is not empty if there is a remark, check that: */ - if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) - return CMD_SUCCESS; - - /* Check if we have any entries: */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='%s'][name='%s']", iptype, - name); - /* - * NOTE: if the list is empty it will return the first sequence - * number: 5. - */ - if (acl_get_seq(vty, xpath, true) != 5) - return CMD_SUCCESS; + if (del_remark) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + remark_dnode = yang_dnode_get(vty->candidate_config->dnode, + xpath); + + /* If there are no entries left and no remark, delete the whole list */ + if (count == 0 && !remark_dnode) { + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/%s-list[type='%s'][name='%s']", ftype, + iptype, name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + } - /* Nobody is using this list, lets remove it. */ - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -static int acl_remove(struct vty *vty, const char *iptype, const char *name, - int64_t sseq) -{ - char xpath[XPATH_MAXLEN]; - int rv; - - snprintfrr( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", - iptype, name, sseq); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return acl_remove_if_empty(vty, iptype, name); - - return rv; -} - /* * Cisco (legacy) access lists. */ DEFPY_YANG( access_list_std, access_list_std_cmd, - "access-list WORD$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR @@ -197,7 +204,7 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_std, no_access_list_std_cmd, - "no access-list WORD$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -213,7 +220,8 @@ DEFPY_YANG( /* If the user provided sequence number, then just go for it. */ if (seq_str != NULL) - return acl_remove(vty, "ipv4", name, seq); + return filter_remove_check_empty(vty, "access", "ipv4", name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ ada.ada_type = "ipv4"; @@ -237,12 +245,13 @@ DEFPY_YANG( else return CMD_WARNING_CONFIG_FAILED; - return acl_remove(vty, "ipv4", name, sseq); + return filter_remove_check_empty(vty, "access", "ipv4", name, sseq, + false); } DEFPY_YANG( access_list_ext, access_list_ext_cmd, - "access-list WORD$name [seq (1-4294967295)$seq] $action ip ", + "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action ip ", ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR @@ -360,7 +369,7 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_ext, no_access_list_ext_cmd, - "no access-list WORD$name [seq (1-4294967295)$seq] $action ip ", + "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action ip ", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -384,7 +393,8 @@ DEFPY_YANG( /* If the user provided sequence number, then just go for it. */ if (seq_str != NULL) - return acl_remove(vty, "ipv4", name, seq); + return filter_remove_check_empty(vty, "access", "ipv4", name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ ada.ada_type = "ipv4"; @@ -429,7 +439,8 @@ DEFPY_YANG( else return CMD_WARNING_CONFIG_FAILED; - return acl_remove(vty, "ipv4", name, sseq); + return filter_remove_check_empty(vty, "access", "ipv4", name, sseq, + false); } /* @@ -437,7 +448,7 @@ DEFPY_YANG( */ DEFPY_YANG( access_list, access_list_cmd, - "access-list WORD$name [seq (1-4294967295)$seq] $action ", + "access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action ", ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_SEQ_STR @@ -510,7 +521,7 @@ DEFPY_YANG( DEFPY_YANG( no_access_list, no_access_list_cmd, - "no access-list WORD$name [seq (1-4294967295)$seq] $action ", + "no access-list ACCESSLIST4_NAME$name [seq (1-4294967295)$seq] $action ", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -525,7 +536,8 @@ DEFPY_YANG( /* If the user provided sequence number, then just go for it. */ if (seq_str != NULL) - return acl_remove(vty, "ipv4", name, seq); + return filter_remove_check_empty(vty, "access", "ipv4", name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ ada.ada_type = "ipv4"; @@ -549,12 +561,13 @@ DEFPY_YANG( else return CMD_WARNING_CONFIG_FAILED; - return acl_remove(vty, "ipv4", name, sseq); + return filter_remove_check_empty(vty, "access", "ipv4", name, sseq, + false); } DEFPY_YANG( no_access_list_all, no_access_list_all_cmd, - "no access-list WORD$name", + "no access-list ACCESSLIST4_NAME$name", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR) @@ -570,7 +583,7 @@ DEFPY_YANG( DEFPY_YANG( access_list_remark, access_list_remark_cmd, - "access-list WORD$name remark LINE...", + "access-list ACCESSLIST4_NAME$name remark LINE...", ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_REMARK_STR @@ -594,30 +607,18 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_remark, no_access_list_remark_cmd, - "no access-list WORD$name remark", + "no access-list ACCESSLIST4_NAME$name remark", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_REMARK_STR) { - char xpath[XPATH_MAXLEN]; - int rv; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark", - name); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return acl_remove_if_empty(vty, "ipv4", name); - - return rv; + return filter_remove_check_empty(vty, "access", "ipv4", name, 0, true); } ALIAS( no_access_list_remark, no_access_list_remark_line_cmd, - "no access-list WORD$name remark LINE...", + "no access-list ACCESSLIST4_NAME$name remark LINE...", NO_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -626,7 +627,7 @@ ALIAS( DEFPY_YANG( ipv6_access_list, ipv6_access_list_cmd, - "ipv6 access-list WORD$name [seq (1-4294967295)$seq] $action ", + "ipv6 access-list ACCESSLIST6_NAME$name [seq (1-4294967295)$seq] $action ", IPV6_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -700,7 +701,7 @@ DEFPY_YANG( DEFPY_YANG( no_ipv6_access_list, no_ipv6_access_list_cmd, - "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] $action ", + "no ipv6 access-list ACCESSLIST6_NAME$name [seq (1-4294967295)$seq] $action ", NO_STR IPV6_STR ACCESS_LIST_STR @@ -716,7 +717,8 @@ DEFPY_YANG( /* If the user provided sequence number, then just go for it. */ if (seq_str != NULL) - return acl_remove(vty, "ipv6", name, seq); + return filter_remove_check_empty(vty, "access", "ipv6", name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ ada.ada_type = "ipv6"; @@ -740,12 +742,13 @@ DEFPY_YANG( else return CMD_WARNING_CONFIG_FAILED; - return acl_remove(vty, "ipv6", name, sseq); + return filter_remove_check_empty(vty, "access", "ipv6", name, sseq, + false); } DEFPY_YANG( no_ipv6_access_list_all, no_ipv6_access_list_all_cmd, - "no ipv6 access-list WORD$name", + "no ipv6 access-list ACCESSLIST6_NAME$name", NO_STR IPV6_STR ACCESS_LIST_STR @@ -762,7 +765,7 @@ DEFPY_YANG( DEFPY_YANG( ipv6_access_list_remark, ipv6_access_list_remark_cmd, - "ipv6 access-list WORD$name remark LINE...", + "ipv6 access-list ACCESSLIST6_NAME$name remark LINE...", IPV6_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR @@ -787,26 +790,14 @@ DEFPY_YANG( DEFPY_YANG( no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd, - "no ipv6 access-list WORD$name remark", + "no ipv6 access-list ACCESSLIST6_NAME$name remark", NO_STR IPV6_STR ACCESS_LIST_STR ACCESS_LIST_ZEBRA_STR ACCESS_LIST_REMARK_STR) { - char xpath[XPATH_MAXLEN]; - int rv; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark", - name); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return acl_remove_if_empty(vty, "ipv6", name); - - return rv; + return filter_remove_check_empty(vty, "access", "ipv6", name, 0, true); } ALIAS( @@ -902,7 +893,8 @@ DEFPY_YANG( /* If the user provided sequence number, then just go for it. */ if (seq_str != NULL) - return acl_remove(vty, "mac", name, seq); + return filter_remove_check_empty(vty, "access", "mac", name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ ada.ada_type = "mac"; @@ -922,7 +914,8 @@ DEFPY_YANG( else return CMD_WARNING_CONFIG_FAILED; - return acl_remove(vty, "mac", name, sseq); + return filter_remove_check_empty(vty, "access", "mac", name, sseq, + false); } DEFPY_YANG( @@ -976,19 +969,7 @@ DEFPY_YANG( ACCESS_LIST_ZEBRA_STR ACCESS_LIST_REMARK_STR) { - char xpath[XPATH_MAXLEN]; - int rv; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='mac'][name='%s']/remark", - name); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return acl_remove_if_empty(vty, "mac", name); - - return rv; + return filter_remove_check_empty(vty, "access", "mac", name, 0, true); } ALIAS( @@ -1004,8 +985,8 @@ ALIAS( int access_list_cmp(const struct lyd_node *dnode1, const struct lyd_node *dnode2) { - uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); - uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "sequence"); return seq1 - seq2; } @@ -1022,23 +1003,23 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode, struct in_addr addr, mask; char macstr[PREFIX2STR_BUFFER]; - is_any = yang_dnode_exists(dnode, "./any"); + is_any = yang_dnode_exists(dnode, "any"); switch (type) { case YALT_IPV4: if (is_any) break; - if (yang_dnode_exists(dnode, "./host") - || yang_dnode_exists(dnode, "./network/address") - || yang_dnode_exists(dnode, "./source-any")) { + if (yang_dnode_exists(dnode, "host") + || yang_dnode_exists(dnode, "network/address") + || yang_dnode_exists(dnode, "source-any")) { cisco_style = true; - if (yang_dnode_exists(dnode, "./destination-host") + if (yang_dnode_exists(dnode, "destination-host") || yang_dnode_exists( dnode, "./destination-network/address") - || yang_dnode_exists(dnode, "./destination-any")) + || yang_dnode_exists(dnode, "destination-any")) cisco_extended = true; } else { - yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + yang_dnode_get_prefix(&p, dnode, "ipv4-prefix"); is_exact = yang_dnode_get_bool(dnode, "./ipv4-exact-match"); } @@ -1048,39 +1029,39 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode, if (is_any) break; - yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix"); - is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match"); + yang_dnode_get_prefix(&p, dnode, "ipv6-prefix"); + is_exact = yang_dnode_get_bool(dnode, "ipv6-exact-match"); break; case YALT_MAC: /* mac */ vty_out(vty, "mac "); if (is_any) break; - yang_dnode_get_prefix(&p, dnode, "./mac"); + yang_dnode_get_prefix(&p, dnode, "mac"); break; } vty_out(vty, "access-list %s seq %s %s", yang_dnode_get_string(dnode, "../name"), - yang_dnode_get_string(dnode, "./sequence"), - yang_dnode_get_string(dnode, "./action")); + yang_dnode_get_string(dnode, "sequence"), + yang_dnode_get_string(dnode, "action")); /* Handle Cisco style access lists. */ if (cisco_style) { if (cisco_extended) vty_out(vty, " ip"); - if (yang_dnode_exists(dnode, "./network")) { - yang_dnode_get_ipv4(&addr, dnode, "./network/address"); - yang_dnode_get_ipv4(&mask, dnode, "./network/mask"); + if (yang_dnode_exists(dnode, "network")) { + yang_dnode_get_ipv4(&addr, dnode, "network/address"); + yang_dnode_get_ipv4(&mask, dnode, "network/mask"); vty_out(vty, " %pI4 %pI4", &addr, &mask); - } else if (yang_dnode_exists(dnode, "./host")) { + } else if (yang_dnode_exists(dnode, "host")) { if (cisco_extended) vty_out(vty, " host"); vty_out(vty, " %s", - yang_dnode_get_string(dnode, "./host")); - } else if (yang_dnode_exists(dnode, "./source-any")) + yang_dnode_get_string(dnode, "host")); + } else if (yang_dnode_exists(dnode, "source-any")) vty_out(vty, " any"); /* Not extended, exit earlier. */ @@ -1090,17 +1071,17 @@ void access_list_show(struct vty *vty, const struct lyd_node *dnode, } /* Handle destination address. */ - if (yang_dnode_exists(dnode, "./destination-network")) { + if (yang_dnode_exists(dnode, "destination-network")) { yang_dnode_get_ipv4(&addr, dnode, "./destination-network/address"); yang_dnode_get_ipv4(&mask, dnode, "./destination-network/mask"); vty_out(vty, " %pI4 %pI4", &addr, &mask); - } else if (yang_dnode_exists(dnode, "./destination-host")) + } else if (yang_dnode_exists(dnode, "destination-host")) vty_out(vty, " host %s", yang_dnode_get_string(dnode, "./destination-host")); - else if (yang_dnode_exists(dnode, "./destination-any")) + else if (yang_dnode_exists(dnode, "destination-any")) vty_out(vty, " any"); vty_out(vty, "\n"); @@ -1149,62 +1130,17 @@ void access_list_remark_show(struct vty *vty, const struct lyd_node *dnode, * Prefix lists. */ -/** - * Remove main data structure prefix list if there are no more entries or - * remark. This fixes compatibility with old CLI and tests. - */ -static int plist_remove_if_empty(struct vty *vty, const char *iptype, - const char *name) -{ - char xpath[XPATH_MAXLEN]; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark", - iptype, name); - /* List is not empty if there is a remark, check that: */ - if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) - return CMD_SUCCESS; - - /* Check if we have any entries: */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, - name); - /* - * NOTE: if the list is empty it will return the first sequence - * number: 5. - */ - if (acl_get_seq(vty, xpath, true) != 5) - return CMD_SUCCESS; - - /* Nobody is using this list, lets remove it. */ - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); -} - static int plist_remove(struct vty *vty, const char *iptype, const char *name, - const char *seq, const char *action, + uint32_t seq, const char *action, union prefixconstptr prefix, int ge, int le) { int64_t sseq; struct plist_dup_args pda = {}; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; - int rv; /* If the user provided sequence number, then just go for it. */ - if (seq != NULL) { - snprintf( - xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']", - iptype, name, seq); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return plist_remove_if_empty(vty, iptype, name); - - return rv; - } + if (seq != 0) + return filter_remove_check_empty(vty, "prefix", iptype, name, + seq, false); /* Otherwise, to keep compatibility, we need to figure it out. */ pda.pda_type = iptype; @@ -1224,22 +1160,13 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name, else return CMD_WARNING_CONFIG_FAILED; - snprintfrr( - xpath_entry, sizeof(xpath_entry), - "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", - iptype, name, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return plist_remove_if_empty(vty, iptype, name); - - return rv; + return filter_remove_check_empty(vty, "prefix", iptype, name, sseq, + false); } DEFPY_YANG( ip_prefix_list, ip_prefix_list_cmd, - "ip prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + "ip prefix-list PREFIXLIST4_NAME$name [seq (1-4294967295)$seq] $action ", IP_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR @@ -1333,7 +1260,7 @@ DEFPY_YANG( DEFPY_YANG( no_ip_prefix_list, no_ip_prefix_list_cmd, - "no ip prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + "no ip prefix-list PREFIXLIST4_NAME$name [seq (1-4294967295)$seq] $action ", NO_STR IP_STR PREFIX_LIST_STR @@ -1347,25 +1274,25 @@ DEFPY_YANG( "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return plist_remove(vty, "ipv4", name, seq_str, action, + return plist_remove(vty, "ipv4", name, seq, action, prefix_str ? prefix : NULL, ge, le); } DEFPY_YANG( no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, - "no ip prefix-list WORD$name seq (1-4294967295)$seq", + "no ip prefix-list PREFIXLIST4_NAME$name seq (1-4294967295)$seq", NO_STR IP_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); + return plist_remove(vty, "ipv4", name, seq, NULL, NULL, 0, 0); } DEFPY_YANG( no_ip_prefix_list_all, no_ip_prefix_list_all_cmd, - "no ip prefix-list WORD$name", + "no ip prefix-list PREFIXLIST4_NAME$name", NO_STR IP_STR PREFIX_LIST_STR @@ -1382,7 +1309,7 @@ DEFPY_YANG( DEFPY_YANG( ip_prefix_list_remark, ip_prefix_list_remark_cmd, - "ip prefix-list WORD$name description LINE...", + "ip prefix-list PREFIXLIST4_NAME$name description LINE...", IP_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR @@ -1407,31 +1334,19 @@ DEFPY_YANG( DEFPY_YANG( no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd, - "no ip prefix-list WORD$name description", + "no ip prefix-list PREFIXLIST4_NAME$name description", NO_STR IP_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR ACCESS_LIST_REMARK_STR) { - char xpath[XPATH_MAXLEN]; - int rv; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark", - name); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return plist_remove_if_empty(vty, "ipv4", name); - - return rv; + return filter_remove_check_empty(vty, "prefix", "ipv4", name, 0, true); } ALIAS( no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd, - "no ip prefix-list WORD$name description LINE...", + "no ip prefix-list PREFIXLIST4_NAME$name description LINE...", NO_STR IP_STR PREFIX_LIST_STR @@ -1441,7 +1356,7 @@ ALIAS( DEFPY_YANG( ipv6_prefix_list, ipv6_prefix_list_cmd, - "ipv6 prefix-list WORD$name [seq (1-4294967295)] $action ", + "ipv6 prefix-list PREFIXLIST6_NAME$name [seq (1-4294967295)] $action ", IPV6_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR @@ -1535,7 +1450,7 @@ DEFPY_YANG( DEFPY_YANG( no_ipv6_prefix_list, no_ipv6_prefix_list_cmd, - "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + "no ipv6 prefix-list PREFIXLIST6_NAME$name [seq (1-4294967295)$seq] $action ", NO_STR IPV6_STR PREFIX_LIST_STR @@ -1549,25 +1464,25 @@ DEFPY_YANG( "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return plist_remove(vty, "ipv6", name, seq_str, action, + return plist_remove(vty, "ipv6", name, seq, action, prefix_str ? prefix : NULL, ge, le); } DEFPY_YANG( no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd, - "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq", + "no ipv6 prefix-list PREFIXLIST6_NAME$name seq (1-4294967295)$seq", NO_STR IPV6_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); + return plist_remove(vty, "ipv6", name, seq, NULL, NULL, 0, 0); } DEFPY_YANG( no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd, - "no ipv6 prefix-list WORD$name", + "no ipv6 prefix-list PREFIXLIST6_NAME$name", NO_STR IPV6_STR PREFIX_LIST_STR @@ -1584,7 +1499,7 @@ DEFPY_YANG( DEFPY_YANG( ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd, - "ipv6 prefix-list WORD$name description LINE...", + "ipv6 prefix-list PREFIXLIST6_NAME$name description LINE...", IPV6_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR @@ -1609,31 +1524,19 @@ DEFPY_YANG( DEFPY_YANG( no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd, - "no ipv6 prefix-list WORD$name description", + "no ipv6 prefix-list PREFIXLIST6_NAME$name description", NO_STR IPV6_STR PREFIX_LIST_STR PREFIX_LIST_NAME_STR ACCESS_LIST_REMARK_STR) { - char xpath[XPATH_MAXLEN]; - int rv; - - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark", - name); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) - return plist_remove_if_empty(vty, "ipv6", name); - - return rv; + return filter_remove_check_empty(vty, "prefix", "ipv6", name, 0, true); } ALIAS( no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd, - "no ipv6 prefix-list WORD$name description LINE...", + "no ipv6 prefix-list PREFIXLIST6_NAME$name description LINE...", NO_STR IPV6_STR PREFIX_LIST_STR @@ -1644,8 +1547,8 @@ ALIAS( int prefix_list_cmp(const struct lyd_node *dnode1, const struct lyd_node *dnode2) { - uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); - uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "sequence"); return seq1 - seq2; } @@ -1658,11 +1561,11 @@ void prefix_list_show(struct vty *vty, const struct lyd_node *dnode, bool is_any; struct prefix p; - is_any = yang_dnode_exists(dnode, "./any"); + is_any = yang_dnode_exists(dnode, "any"); switch (type) { case YPLT_IPV4: if (!is_any) - yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + yang_dnode_get_prefix(&p, dnode, "ipv4-prefix"); if (yang_dnode_exists(dnode, "./ipv4-prefix-length-greater-or-equal")) ge_str = yang_dnode_get_string( @@ -1692,8 +1595,8 @@ void prefix_list_show(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "prefix-list %s seq %s %s", yang_dnode_get_string(dnode, "../name"), - yang_dnode_get_string(dnode, "./sequence"), - yang_dnode_get_string(dnode, "./action")); + yang_dnode_get_string(dnode, "sequence"), + yang_dnode_get_string(dnode, "action")); if (is_any) { vty_out(vty, " any\n"); diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 9511b8f..39042d3 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -17,23 +17,6 @@ #include "lib/plist_int.h" #include "lib/routemap.h" -/* Helper function. */ -static void acl_notify_route_map(struct access_list *acl, int route_map_event) -{ - switch (route_map_event) { - case RMAP_EVENT_FILTER_ADDED: - if (acl->master->add_hook) - (*acl->master->add_hook)(acl); - break; - case RMAP_EVENT_FILTER_DELETED: - if (acl->master->delete_hook) - (*acl->master->delete_hook)(acl); - break; - } - - route_map_notify_dependencies(acl->name, route_map_event); -} - static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args) { int type = yang_dnode_get_enum(args->dnode, "../../type"); @@ -112,7 +95,7 @@ prefix_list_nb_validate_v4_af_type(const struct lyd_node *plist_dnode, { int af_type; - af_type = yang_dnode_get_enum(plist_dnode, "./type"); + af_type = yang_dnode_get_enum(plist_dnode, "type"); if (af_type != YPLT_IPV4) { snprintf(errmsg, errmsg_len, "prefix-list type %u is mismatched.", af_type); @@ -128,7 +111,7 @@ prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode, { int af_type; - af_type = yang_dnode_get_enum(plist_dnode, "./type"); + af_type = yang_dnode_get_enum(plist_dnode, "type"); if (af_type != YPLT_IPV6) { snprintf(errmsg, errmsg_len, "prefix-list type %u is mismatched.", af_type); @@ -153,9 +136,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_modify( ple->ge = yang_dnode_get_uint8(args->dnode, NULL); - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -174,9 +154,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( ple->le = yang_dnode_get_uint8(args->dnode, NULL); - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -195,9 +172,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( ple->ge = 0; - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -216,9 +190,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( ple->le = 0; - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -381,14 +352,14 @@ static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any, *ge = 0; *le = 0; - if (yang_dnode_exists(dnode, "./any")) { + if (yang_dnode_exists(dnode, "any")) { *any = true; return; } switch (yang_dnode_get_enum(dnode, "../type")) { case YPLT_IPV4: - yang_dnode_get_prefix(p, dnode, "./ipv4-prefix"); + yang_dnode_get_prefix(p, dnode, "ipv4-prefix"); if (yang_dnode_exists(dnode, "./ipv4-prefix-length-greater-or-equal")) *ge = yang_dnode_get_uint8( @@ -399,7 +370,7 @@ static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any, dnode, "./ipv4-prefix-length-lesser-or-equal"); break; case YPLT_IPV6: - yang_dnode_get_prefix(p, dnode, "./ipv6-prefix"); + yang_dnode_get_prefix(p, dnode, "ipv6-prefix"); if (yang_dnode_exists(dnode, "./ipv6-prefix-length-greater-or-equal")) *ge = yang_dnode_get_uint8( @@ -468,8 +439,8 @@ static int lib_access_list_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - type = yang_dnode_get_enum(args->dnode, "./type"); - acl_name = yang_dnode_get_string(args->dnode, "./name"); + type = yang_dnode_get_enum(args->dnode, "type"); + acl_name = yang_dnode_get_string(args->dnode, "name"); switch (type) { case YALT_IPV4: @@ -550,7 +521,7 @@ static int lib_access_list_entry_create(struct nb_cb_create_args *args) return NB_OK; f = filter_new(); - f->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + f->seq = yang_dnode_get_uint32(args->dnode, "sequence"); acl = nb_running_get_entry(args->dnode, NULL, true); f->acl = acl; @@ -575,6 +546,15 @@ static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +static void +lib_access_list_entry_apply_finish(struct nb_cb_apply_finish_args *args) +{ + struct filter *f; + + f = nb_running_get_entry(args->dnode, NULL, true); + access_list_filter_update(f->acl); +} + /* * XPath: /frr-filter:lib/access-list/entry/action */ @@ -594,8 +574,6 @@ lib_access_list_entry_action_modify(struct nb_cb_modify_args *args) else f->type = FILTER_DENY; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -629,8 +607,6 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -647,8 +623,6 @@ lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; memset(&fz->prefix, 0, sizeof(fz->prefix)); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); - return NB_OK; } @@ -681,8 +655,6 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; fz->exact = yang_dnode_get_bool(args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -699,8 +671,6 @@ lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->exact = 0; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); - return NB_OK; } @@ -733,8 +703,6 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args) yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -751,7 +719,29 @@ lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args) fc = &f->u.cfilter; cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/network + */ +static int lib_access_list_entry_network_create(struct nb_cb_create_args *args) +{ + /* Nothing to do here, everything is done in children callbacks */ + return NB_OK; +} + +static int lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); return NB_OK; } @@ -784,8 +774,6 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args) fc = &f->u.cfilter; yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -817,8 +805,6 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args) fc = &f->u.cfilter; yang_dnode_get_ipv4(&fc->addr_mask, args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -851,8 +837,6 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args) fc->addr.s_addr = INADDR_ANY; fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -869,8 +853,6 @@ lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args) fc = &f->u.cfilter; cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); - return NB_OK; } @@ -903,8 +885,6 @@ static int lib_access_list_entry_destination_host_modify( yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -922,7 +902,32 @@ static int lib_access_list_entry_destination_host_destroy( fc->extended = 0; cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-network + */ +static int +lib_access_list_entry_destination_network_create(struct nb_cb_create_args *args) +{ + /* Nothing to do here, everything is done in children callbacks */ + return NB_OK; +} + +static int lib_access_list_entry_destination_network_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 0; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); return NB_OK; } @@ -955,8 +960,6 @@ static int lib_access_list_entry_destination_network_address_modify( fc->extended = 1; yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -988,8 +991,6 @@ static int lib_access_list_entry_destination_network_mask_modify( fc->extended = 1; yang_dnode_get_ipv4(&fc->mask_mask, args->dnode, NULL); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -1022,8 +1023,6 @@ static int lib_access_list_entry_destination_any_create( fc->mask.s_addr = INADDR_ANY; fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -1041,8 +1040,6 @@ static int lib_access_list_entry_destination_any_destroy( fc->extended = 0; cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); - return NB_OK; } @@ -1089,8 +1086,6 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) break; } - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); - return NB_OK; } @@ -1106,8 +1101,6 @@ static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->prefix.family = AF_UNSPEC; - acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); - return NB_OK; } @@ -1123,8 +1116,8 @@ static int lib_prefix_list_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - type = yang_dnode_get_enum(args->dnode, "./type"); - name = yang_dnode_get_string(args->dnode, "./name"); + type = yang_dnode_get_enum(args->dnode, "type"); + name = yang_dnode_get_string(args->dnode, "name"); switch (type) { case 0: /* ipv4 */ pl = prefix_list_get(AFI_IP, 0, name); @@ -1201,7 +1194,7 @@ static int lib_prefix_list_entry_create(struct nb_cb_create_args *args) pl = nb_running_get_entry(args->dnode, NULL, true); ple = prefix_list_entry_new(); ple->pl = pl; - ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + ple->seq = yang_dnode_get_uint32(args->dnode, "sequence"); prefix_list_entry_set_empty(ple); nb_running_set_entry(args->dnode, ple); @@ -1224,6 +1217,22 @@ static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +static void +lib_prefix_list_entry_apply_finish(struct nb_cb_apply_finish_args *args) +{ + struct prefix_list_entry *ple; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* + * Finish prefix entry update procedure. The procedure is started in + * children callbacks. `prefix_list_entry_update_start` can be called + * multiple times if multiple children are modified, but it is actually + * executed only once because of the protection by `ple->installed`. + */ + prefix_list_entry_update_finish(ple); +} + /* * XPath: /frr-filter:lib/prefix-list/entry/action */ @@ -1246,9 +1255,6 @@ static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args) else ple->type = PREFIX_DENY; - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -1276,10 +1282,6 @@ static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args) prefix_copy(&ple->prefix, &p); } - - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -1297,9 +1299,6 @@ static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args) memset(&ple->prefix, 0, sizeof(ple->prefix)); - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -1547,9 +1546,6 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) break; } - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -1567,9 +1563,6 @@ static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args) ple->any = false; - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - return NB_OK; } @@ -1597,6 +1590,7 @@ const struct frr_yang_module_info frr_filter_info = { .cbs = { .create = lib_access_list_entry_create, .destroy = lib_access_list_entry_destroy, + .apply_finish = lib_access_list_entry_apply_finish, .cli_cmp = access_list_cmp, .cli_show = access_list_show, } @@ -1628,6 +1622,13 @@ const struct frr_yang_module_info frr_filter_info = { .destroy = lib_access_list_entry_host_destroy, } }, + { + .xpath = "/frr-filter:lib/access-list/entry/network", + .cbs = { + .create = lib_access_list_entry_network_create, + .destroy = lib_access_list_entry_network_destroy, + } + }, { .xpath = "/frr-filter:lib/access-list/entry/network/address", .cbs = { @@ -1654,6 +1655,13 @@ const struct frr_yang_module_info frr_filter_info = { .destroy = lib_access_list_entry_destination_host_destroy, } }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-network", + .cbs = { + .create = lib_access_list_entry_destination_network_create, + .destroy = lib_access_list_entry_destination_network_destroy, + } + }, { .xpath = "/frr-filter:lib/access-list/entry/destination-network/address", .cbs = { @@ -1721,6 +1729,7 @@ const struct frr_yang_module_info frr_filter_info = { .cbs = { .create = lib_prefix_list_entry_create, .destroy = lib_prefix_list_entry_destroy, + .apply_finish = lib_prefix_list_entry_apply_finish, .cli_cmp = prefix_list_cmp, .cli_show = prefix_list_show, } @@ -1785,3 +1794,35 @@ const struct frr_yang_module_info frr_filter_info = { }, } }; + +const struct frr_yang_module_info frr_filter_cli_info = { + .name = "frr-filter", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-filter:lib/access-list/remark", + .cbs.cli_show = access_list_remark_show, + }, + { + .xpath = "/frr-filter:lib/access-list/entry", + .cbs = { + .cli_cmp = access_list_cmp, + .cli_show = access_list_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/remark", + .cbs.cli_show = prefix_list_remark_show, + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry", + .cbs = { + .cli_cmp = prefix_list_cmp, + .cli_show = prefix_list_show, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index c4ead01..1ffa593 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -5,6 +5,9 @@ */ #include + +#include + #include #ifdef HAVE_PTHREAD_NP_H #include @@ -217,6 +220,41 @@ void frr_pthread_stop_all(void) } } +static void *frr_pthread_attr_non_controlled_start(void *arg) +{ + struct frr_pthread *fpt = arg; + + fpt->running = true; + + return NULL; +} + +/* Create a FRR pthread context from a non FRR pthread initialized from an + * external library in order to allow logging */ +int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, + const char *os_name) +{ + struct rcu_thread *rcu_thread = rcu_thread_new(NULL); + + rcu_thread_start(rcu_thread); + + struct frr_pthread_attr attr = { + .start = frr_pthread_attr_non_controlled_start, + .stop = frr_pthread_attr_default.stop, + }; + struct frr_pthread *fpt; + + fpt = frr_pthread_new(&attr, name, os_name); + if (!fpt) + return -1; + + fpt->thread = thread; + fpt->rcu_thread = rcu_thread; + frr_pthread_inner(fpt); + + return 0; +} + /* * ---------------------------------------------------------------------------- * Default Event Loop diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index f91044d..1e1b8d7 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -202,6 +202,9 @@ void frr_pthread_stop_all(void); #define pthread_condattr_setclock(A, B) #endif +int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, + const char *os_name); + /* mutex auto-lock/unlock */ /* variant 1: diff --git a/lib/frrcu.c b/lib/frrcu.c index c7cc655..b85c525 100644 --- a/lib/frrcu.c +++ b/lib/frrcu.c @@ -149,20 +149,9 @@ static struct rcu_thread *rcu_self(void) return (struct rcu_thread *)pthread_getspecific(rcu_thread_key); } -/* - * thread management (for the non-main thread) - */ -struct rcu_thread *rcu_thread_prepare(void) +struct rcu_thread *rcu_thread_new(void *arg) { - struct rcu_thread *rt, *cur; - - rcu_assert_read_locked(); - - if (!rcu_active) - rcu_start(); - - cur = rcu_self(); - assert(cur->depth); + struct rcu_thread *rt, *cur = arg; /* new thread always starts with rcu_read_lock held at depth 1, and * holding the same epoch as the parent (this makes it possible to @@ -172,13 +161,32 @@ struct rcu_thread *rcu_thread_prepare(void) rt->depth = 1; seqlock_init(&rt->rcu); - seqlock_acquire(&rt->rcu, &cur->rcu); + if (cur) + seqlock_acquire(&rt->rcu, &cur->rcu); rcu_threads_add_tail(&rcu_threads, rt); return rt; } +/* + * thread management (for the non-main thread) + */ +struct rcu_thread *rcu_thread_prepare(void) +{ + struct rcu_thread *cur; + + rcu_assert_read_locked(); + + if (!rcu_active) + rcu_start(); + + cur = rcu_self(); + assert(cur->depth); + + return rcu_thread_new(cur); +} + void rcu_thread_start(struct rcu_thread *rt) { pthread_setspecific(rcu_thread_key, rt); diff --git a/lib/frrcu.h b/lib/frrcu.h index e7a54dc..9f07a69 100644 --- a/lib/frrcu.h +++ b/lib/frrcu.h @@ -40,6 +40,12 @@ extern "C" { /* opaque */ struct rcu_thread; +/* sets up rcu thread info + * + * return value must be passed into the thread's call to rcu_thread_start() + */ +extern struct rcu_thread *rcu_thread_new(void *arg); + /* called before new thread creation, sets up rcu thread info for new thread * before it actually exits. This ensures possible RCU references are held * for thread startup. diff --git a/lib/frrdistance.h b/lib/frrdistance.h new file mode 100644 index 0000000..d2fa76e --- /dev/null +++ b/lib/frrdistance.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra distance header + * Copyright (C) 2023 NVIDIA Corporation + * Donald Sharp + * + * Distance related defines. FRR needs a common set + * of values for distance. + */ +#ifndef __FRRDISTANCE_H__ +#define __FRRDISTANCE_H__ + +/* Default Administrative Distance of each protocol. */ +#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 +#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0 +#define ZEBRA_STATIC_DISTANCE_DEFAULT 1 +#define ZEBRA_RIP_DISTANCE_DEFAULT 120 +#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120 +#define ZEBRA_OSPF_DISTANCE_DEFAULT 110 +#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110 +#define ZEBRA_ISIS_DISTANCE_DEFAULT 115 +#define ZEBRA_IBGP_DISTANCE_DEFAULT 200 +#define ZEBRA_EBGP_DISTANCE_DEFAULT 20 +#define ZEBRA_TABLE_DISTANCE_DEFAULT 15 +#define ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT 14 +#define ZEBRA_EIGRP_DISTANCE_DEFAULT 90 +#define ZEBRA_NHRP_DISTANCE_DEFAULT 10 +#define ZEBRA_LDP_DISTANCE_DEFAULT 150 +#define ZEBRA_BABEL_DISTANCE_DEFAULT 100 +#define ZEBRA_SHARP_DISTANCE_DEFAULT 150 +#define ZEBRA_PBR_DISTANCE_DEFAULT 200 +#define ZEBRA_OPENFABRIC_DISTANCE_DEFAULT 115 +#define ZEBRA_MAX_DISTANCE_DEFAULT 255 + +#endif diff --git a/lib/frrevent.h b/lib/frrevent.h index 3f74df3..94640a7 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -6,6 +6,7 @@ #ifndef _ZEBRA_THREAD_H #define _ZEBRA_THREAD_H +#include #include #include #include @@ -91,6 +92,8 @@ struct event_loop { pthread_mutex_t mtx; pthread_t owner; + nfds_t last_read; + bool ready_run_loop; RUSAGE_T last_getrusage; }; @@ -153,6 +156,12 @@ struct cpu_event_history { /* Struct timeval's tv_usec one second value. */ #define TIMER_SECOND_MICRO 1000000L +static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) +{ + return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec)); +} + /* Event yield time. */ #define EVENT_YIELD_TIME_SLOT 10 * 1000L /* 10ms */ diff --git a/lib/frrlua.c b/lib/frrlua.c index e626efe..2cab1a5 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -259,12 +259,12 @@ void *lua_tosockunion(lua_State *L, int idx) return su; } -void lua_pushintegerp(lua_State *L, const long long *num) +void lua_pushintegerp(lua_State *L, const int *num) { lua_pushinteger(L, *num); } -void lua_decode_integerp(lua_State *L, int idx, long long *num) +void lua_decode_integerp(lua_State *L, int idx, int *num) { int isnum; *num = lua_tonumberx(L, idx, &isnum); @@ -274,7 +274,7 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num) void *lua_tointegerp(lua_State *L, int idx) { - long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long)); + int *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(int)); lua_decode_integerp(L, idx, num); return num; @@ -332,32 +332,39 @@ void lua_pushnexthop_group(lua_State *L, const struct nexthop_group *ng) } } -void lua_decode_stringp(lua_State *L, int idx, char *str) +void lua_pushlonglongp(lua_State *L, const long long *num) { - strlcpy(str, lua_tostring(L, idx), strlen(str) + 1); + /* lua library function; this can take a long long */ + lua_pushinteger(L, *num); +} + +void lua_decode_longlongp(lua_State *L, int idx, long long *num) +{ + int isnum; + *num = lua_tonumberx(L, idx, &isnum); lua_pop(L, 1); + assert(isnum); } -void *lua_tostringp(lua_State *L, int idx) +void *lua_tolonglongp(lua_State *L, int idx) { - char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx)); + long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long)); - return string; + lua_decode_longlongp(L, idx, num); + return num; } -/* - * Decoder for const values, since we cannot modify them. - */ -void lua_decode_noop(lua_State *L, int idx, const void *ptr) +void lua_decode_stringp(lua_State *L, int idx, char *str) { + strlcpy(str, lua_tostring(L, idx), strlen(str) + 1); + lua_pop(L, 1); } - -/* - * Noop decoder for int. - */ -void lua_decode_integer_noop(lua_State *L, int idx, int i) +void *lua_tostringp(lua_State *L, int idx) { + char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx)); + + return string; } /* diff --git a/lib/frrlua.h b/lib/frrlua.h index d248312..dc0f4d9 100644 --- a/lib/frrlua.h +++ b/lib/frrlua.h @@ -121,9 +121,9 @@ void lua_pushnexthop(lua_State *L, const struct nexthop *nexthop); /* * Converts an int to a Lua value and pushes it on the stack. */ -void lua_pushintegerp(lua_State *L, const long long *num); +void lua_pushintegerp(lua_State *L, const int *num); -void lua_decode_integerp(lua_State *L, int idx, long long *num); +void lua_decode_integerp(lua_State *L, int idx, int *num); /* * Converts the Lua value at idx to an int. @@ -133,6 +133,21 @@ void lua_decode_integerp(lua_State *L, int idx, long long *num); */ void *lua_tointegerp(lua_State *L, int idx); +/* + * Converts a long long to a Lua value and pushes it on the stack. + */ +void lua_pushlonglongp(lua_State *L, const long long *num); + +void lua_decode_longlongp(lua_State *L, int idx, long long *num); + +/* + * Converts the Lua value at idx to a long long. + * + * Returns: + * long long allocated with MTYPE_TMP. + */ +void *lua_tolonglongp(lua_State *L, int idx); + void lua_decode_stringp(lua_State *L, int idx, char *str); /* @@ -143,13 +158,6 @@ void lua_decode_stringp(lua_State *L, int idx, char *str); */ void *lua_tostringp(lua_State *L, int idx); -/* - * No-op decoders - */ -void lua_decode_noop(lua_State *L, int idx, const void *ptr); - -void lua_decode_integer_noop(lua_State *L, int idx, int i); - /* * Retrieve an integer from table on the top of the stack. * diff --git a/lib/frrscript.c b/lib/frrscript.c index 50410fb..acdd1df 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -25,6 +25,8 @@ DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting"); struct frrscript_names_head frrscript_names_hash; +void _lua_decode_noop(lua_State *L, ...) {} + /* * Wrapper for frrscript_names_add * Use this to register hook calls when a daemon starts up diff --git a/lib/frrscript.h b/lib/frrscript.h index df49b57..ce313a1 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -180,6 +180,11 @@ void frrscript_fini(void); assert(lua_gettop(lfs->L) == 1); \ } while (0) +/* + * Noop function. Used below where we need a noop decoder for any type. + */ +void _lua_decode_noop(lua_State *, ...); + /* * Maps the type of value to its encoder/decoder. * Add new mappings here. @@ -192,7 +197,9 @@ void frrscript_fini(void); #define ENCODE_ARGS_WITH_STATE(L, value) \ _Generic((value), \ int : lua_pushinteger, \ -long long * : lua_pushintegerp, \ +int * : lua_pushintegerp, \ +long long : lua_pushinteger, \ +long long * : lua_pushlonglongp, \ struct prefix * : lua_pushprefix, \ struct interface * : lua_pushinterface, \ struct in_addr * : lua_pushinaddr, \ @@ -211,8 +218,8 @@ struct zebra_dplane_ctx * : lua_pushzebra_dplane_ctx \ #define DECODE_ARGS_WITH_STATE(L, value) \ _Generic((value), \ -int : lua_decode_integer_noop, \ -long long * : lua_decode_integerp, \ +int * : lua_decode_integerp, \ +long long * : lua_decode_longlongp, \ struct prefix * : lua_decode_prefix, \ struct interface * : lua_decode_interface, \ struct in_addr * : lua_decode_inaddr, \ @@ -220,13 +227,7 @@ struct in6_addr * : lua_decode_in6addr, \ union sockunion * : lua_decode_sockunion, \ char * : lua_decode_stringp, \ struct attr * : lua_decode_attr, \ -struct peer * : lua_decode_noop, \ -const struct prefix * : lua_decode_noop, \ -const struct ipaddr * : lua_decode_noop, \ -const struct ethaddr * : lua_decode_noop, \ -const struct nexthop_group * : lua_decode_noop, \ -const struct nexthop * : lua_decode_noop, \ -struct zebra_dplane_ctx * : lua_decode_noop \ +default : _lua_decode_noop \ )((L), -1, (value)) /* diff --git a/lib/frrsendmmsg.h b/lib/frrsendmmsg.h new file mode 100644 index 0000000..ea63d13 --- /dev/null +++ b/lib/frrsendmmsg.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * FRR sendmmsg wrapper + * Copyright (C) 2024 by Nvidia, Inc. + * Donald Sharp + */ +#ifndef __FRRSENDMMSG_H__ +#define __FRRSENDMMSG_H__ + +#if !defined(HAVE_STRUCT_MMSGHDR_MSG_HDR) || !defined(HAVE_SENDMMSG) +/* avoid conflicts in case we have partial support */ +#define mmsghdr frr_mmsghdr +#define sendmmsg frr_sendmmsg + +struct mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; + +/* just go 1 at a time here, the loop this is used in will handle the rest */ +static inline int sendmmsg(int fd, struct mmsghdr *mmh, unsigned int len, + int flags) +{ + int rv = sendmsg(fd, &mmh->msg_hdr, 0); + + return rv > 0 ? 1 : rv; +} +#endif + +#endif diff --git a/lib/frrstr.c b/lib/frrstr.c index e5440c5..1e743d4 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -3,6 +3,7 @@ * FRR string processing utilities. * Copyright (C) 2018 Cumulus Networks, Inc. * Quentin Young + * Copyright (c) 2023, LabN Consulting, L.L.C. */ #include "zebra.h" @@ -225,3 +226,47 @@ char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num) return buff; } + +const char *frrstr_skip_over_char(const char *s, int skipc) +{ + int c, quote = 0; + + while ((c = *s++)) { + if (c == '\\') { + if (!*s++) + return NULL; + continue; + } + if (quote) { + if (c == quote) + quote = 0; + continue; + } + if (c == skipc) + return s; + if (c == '"' || c == '\'') + quote = c; + } + return NULL; +} + +/* + * Advance backward in string until reaching the char `toc` + * if beginning of string is reached w/o finding char return NULL + * + * /foo/bar'baz/booz'/foo + */ +const char *frrstr_back_to_char(const char *s, int toc) +{ + const char *next = s; + const char *prev = NULL; + + if (s[0] == 0) + return NULL; + if (!strpbrk(s, "'\"\\")) + return strrchr(s, toc); + while ((next = frrstr_skip_over_char(next, toc))) + prev = next - 1; + return prev; +} + diff --git a/lib/frrstr.h b/lib/frrstr.h index 19ba09e..33a4992 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -3,6 +3,7 @@ * FRR string processing utilities. * Copyright (C) 2018 Cumulus Networks, Inc. * Quentin Young + * Copyright (c) 2023, LabN Consulting, L.L.C. */ #ifndef _FRRSTR_H_ @@ -166,6 +167,19 @@ int all_digit(const char *str); */ char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num); +/* + * Advance past a given char `skipc` in a string, while honoring quoting and + * backslash escapes (i.e., ignore `skipc` which occur in quoted sections). + */ +const char *frrstr_skip_over_char(const char *s, int skipc); + +/* + * Advance back from end to a given char `toc` in a string, while honoring + * quoting and backslash escapes. `toc` chars inside quote or escaped are + * ignored. + */ +const char *frrstr_back_to_char(const char *s, int toc); + #ifdef __cplusplus } #endif diff --git a/lib/hash.h b/lib/hash.h index 810faf9..2d00a33 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -28,12 +28,12 @@ struct hash_bucket { */ int len; - /* Linked list. */ - struct hash_bucket *next; - /* Hash key. */ unsigned int key; + /* Linked list. */ + struct hash_bucket *next; + /* Data. */ void *data; }; diff --git a/lib/hook.h b/lib/hook.h index 19e0f1f..58aa200 100644 --- a/lib/hook.h +++ b/lib/hook.h @@ -163,6 +163,8 @@ extern void _hook_unregister(struct hook *hook, void *funcptr, void *arg, _hook_unregister(&_hook_##hookname, \ _hook_typecheck_arg_##hookname(func), arg, true) +#define hook_have_hooks(hookname) (_hook_##hookname.entries != NULL) + /* invoke hooks * this is private (static) to the file that has the DEFINE_HOOK statement */ diff --git a/lib/if.c b/lib/if.c index 48c01af..a8ceac7 100644 --- a/lib/if.c +++ b/lib/if.c @@ -42,15 +42,14 @@ RB_GENERATE(if_index_head, interface, index_entry, if_cmp_index_func); DEFINE_QOBJ_TYPE(interface); -DEFINE_HOOK(if_add, (struct interface * ifp), (ifp)); -DEFINE_KOOH(if_del, (struct interface * ifp), (ifp)); +DEFINE_HOOK(if_add, (struct interface *ifp), (ifp)); +DEFINE_KOOH(if_del, (struct interface *ifp), (ifp)); -static struct interface_master{ - int (*create_hook)(struct interface *ifp); - int (*up_hook)(struct interface *ifp); - int (*down_hook)(struct interface *ifp); - int (*destroy_hook)(struct interface *ifp); -} ifp_master = { 0, }; +DEFINE_HOOK(if_real, (struct interface *ifp), (ifp)); +DEFINE_KOOH(if_unreal, (struct interface *ifp), (ifp)); + +DEFINE_HOOK(if_up, (struct interface *ifp), (ifp)); +DEFINE_KOOH(if_down, (struct interface *ifp), (ifp)); /* Compare interface names, returning an integer greater than, equal to, or * less than 0, (following the strcmp convention), according to the @@ -165,8 +164,7 @@ static struct interface *if_new(struct vrf *vrf) ifp->vrf = vrf; - ifp->connected = list_new(); - ifp->connected->del = ifp_connected_free; + if_connected_init(ifp->connected); ifp->nbr_connected = list_new(); ifp->nbr_connected->del = (void (*)(void *))nbr_connected_free; @@ -180,14 +178,12 @@ static struct interface *if_new(struct vrf *vrf) void if_new_via_zapi(struct interface *ifp) { - if (ifp_master.create_hook) - (*ifp_master.create_hook)(ifp); + hook_call(if_real, ifp); } void if_destroy_via_zapi(struct interface *ifp) { - if (ifp_master.destroy_hook) - (*ifp_master.destroy_hook)(ifp); + hook_call(if_unreal, ifp); ifp->oldifindex = ifp->ifindex; if_set_index(ifp, IFINDEX_INTERNAL); @@ -198,14 +194,12 @@ void if_destroy_via_zapi(struct interface *ifp) void if_up_via_zapi(struct interface *ifp) { - if (ifp_master.up_hook) - (*ifp_master.up_hook)(ifp); + hook_call(if_up, ifp); } void if_down_via_zapi(struct interface *ifp) { - if (ifp_master.down_hook) - (*ifp_master.down_hook)(ifp); + hook_call(if_down, ifp); } static struct interface *if_create_name(const char *name, struct vrf *vrf) @@ -248,11 +242,14 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) /* Delete interface structure. */ void if_delete_retain(struct interface *ifp) { + struct connected *ifc; + hook_call(if_del, ifp); QOBJ_UNREG(ifp); /* Free connected address list */ - list_delete_all_node(ifp->connected); + while ((ifc = if_connected_pop(ifp->connected))) + ifp_connected_free(ifc); /* Free connected nbr address list */ list_delete_all_node(ifp->nbr_connected); @@ -270,7 +267,7 @@ void if_delete(struct interface **ifp) if_delete_retain(ptr); - list_delete(&ptr->connected); + if_connected_fini(ptr->connected); list_delete(&ptr->nbr_connected); if_link_params_free(ptr); @@ -367,8 +364,7 @@ struct interface *if_lookup_by_name(const char *name, vrf_id_t vrf_id) struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct interface if_tmp; - if (!vrf || !name - || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ) + if (!vrf || !name || strnlen(name, IFNAMSIZ) == IFNAMSIZ) return NULL; strlcpy(if_tmp.name, name, sizeof(if_tmp.name)); @@ -379,7 +375,7 @@ struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf) { struct interface if_tmp; - if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ) + if (!name || strnlen(name, IFNAMSIZ) == IFNAMSIZ) return NULL; strlcpy(if_tmp.name, name, sizeof(if_tmp.name)); @@ -391,7 +387,7 @@ static struct interface *if_lookup_by_name_all_vrf(const char *name) struct vrf *vrf; struct interface *ifp; - if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ) + if (!name || strnlen(name, IFNAMSIZ) == IFNAMSIZ) return NULL; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { @@ -433,7 +429,6 @@ struct interface *if_lookup_address_local(const void *src, int family, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); - struct listnode *cnode; struct interface *ifp, *best_down = NULL; struct prefix *p; struct connected *c; @@ -442,7 +437,7 @@ struct interface *if_lookup_address_local(const void *src, int family, return NULL; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { p = c->address; if (!p || p->family != family) @@ -474,7 +469,6 @@ struct connected *if_lookup_address(const void *matchaddr, int family, struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct prefix addr; int bestlen = 0; - struct listnode *cnode; struct interface *ifp; struct connected *c; struct connected *match; @@ -493,7 +487,7 @@ struct connected *if_lookup_address(const void *matchaddr, int family, match = NULL; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (c->address && (c->address->family == AF_INET) && prefix_match(CONNECTED_PREFIX(c), &addr) && (c->address->prefixlen > bestlen)) { @@ -509,12 +503,11 @@ struct connected *if_lookup_address(const void *matchaddr, int family, struct interface *if_lookup_prefix(const struct prefix *prefix, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); - struct listnode *cnode; struct interface *ifp; struct connected *c; FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (prefix_cmp(c->address, prefix) == 0) { return ifp; } @@ -781,10 +774,9 @@ const char *if_flag_dump(unsigned long flag) /* For debugging */ static void if_dump(const struct interface *ifp) { - struct listnode *node; - struct connected *c __attribute__((unused)); + const struct connected *c; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c)) + frr_each (if_connected_const, ifp->connected, c) zlog_info( "Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s", ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, @@ -911,11 +903,10 @@ static int connected_same_prefix(const struct prefix *p1, /* count the number of connected addresses that are in the given family */ unsigned int connected_count_by_family(struct interface *ifp, int family) { - struct listnode *cnode; struct connected *connected; unsigned int cnt = 0; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) + frr_each (if_connected, ifp->connected, connected) if (connected->address->family == family) cnt++; @@ -925,14 +916,9 @@ unsigned int connected_count_by_family(struct interface *ifp, int family) struct connected *connected_lookup_prefix_exact(struct interface *ifp, const struct prefix *p) { - struct listnode *node; - struct listnode *next; struct connected *ifc; - for (node = listhead(ifp->connected); node; node = next) { - ifc = listgetdata(node); - next = node->next; - + frr_each (if_connected, ifp->connected, ifc) { if (connected_same_prefix(ifc->address, p)) return ifc; } @@ -942,17 +928,12 @@ struct connected *connected_lookup_prefix_exact(struct interface *ifp, struct connected *connected_delete_by_prefix(struct interface *ifp, struct prefix *p) { - struct listnode *node; - struct listnode *next; struct connected *ifc; /* In case of same prefix come, replace it with new one. */ - for (node = listhead(ifp->connected); node; node = next) { - ifc = listgetdata(node); - next = node->next; - + frr_each_safe (if_connected, ifp->connected, ifc) { if (connected_same_prefix(ifc->address, p)) { - listnode_delete(ifp->connected, ifc); + if_connected_del(ifp->connected, ifc); return ifc; } } @@ -964,13 +945,12 @@ struct connected *connected_delete_by_prefix(struct interface *ifp, struct connected *connected_lookup_prefix(struct interface *ifp, const struct prefix *addr) { - struct listnode *cnode; struct connected *c; struct connected *match; match = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (c->address && (c->address->family == addr->family) && prefix_match(CONNECTED_PREFIX(c), addr) && (!match @@ -1001,16 +981,15 @@ struct connected *connected_add_by_prefix(struct interface *ifp, } /* Add connected address to the interface. */ - listnode_add(ifp->connected, ifc); + if_connected_add_tail(ifp->connected, ifc); return ifc; } struct connected *connected_get_linklocal(struct interface *ifp) { - struct listnode *n; struct connected *c = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + frr_each (if_connected, ifp->connected, c) { if (c->address->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) break; @@ -1340,14 +1319,14 @@ static void cli_show_interface(struct vty *vty, const struct lyd_node *dnode, char ifname[XPATH_MAXLEN]; char vrfname[XPATH_MAXLEN]; - netns_ifname_split(yang_dnode_get_string(dnode, "./name"), + netns_ifname_split(yang_dnode_get_string(dnode, "name"), ifname, vrfname); vty_out(vty, "interface %s", ifname); if (!strmatch(vrfname, VRF_DEFAULT_NAME)) vty_out(vty, " vrf %s", vrfname); } else { - const char *ifname = yang_dnode_get_string(dnode, "./name"); + const char *ifname = yang_dnode_get_string(dnode, "name"); vty_out(vty, "interface %s", ifname); } @@ -1361,6 +1340,15 @@ static void cli_show_interface_end(struct vty *vty, vty_out(vty, "exit\n"); } +static int cli_cmp_interface(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) +{ + const char *ifname1 = yang_dnode_get_string(dnode1, "name"); + const char *ifname2 = yang_dnode_get_string(dnode2, "name"); + + return if_cmp_name_func(ifname1, ifname2); +} + void if_vty_config_start(struct vty *vty, struct interface *ifp) { vty_frame(vty, "!\n"); @@ -1477,17 +1465,6 @@ void if_cmd_init_default(void) if_cmd_init(if_nb_config_write); } -void if_zapi_callbacks(int (*create)(struct interface *ifp), - int (*up)(struct interface *ifp), - int (*down)(struct interface *ifp), - int (*destroy)(struct interface *ifp)) -{ - ifp_master.create_hook = create; - ifp_master.up_hook = up; - ifp_master.down_hook = down; - ifp_master.destroy_hook = destroy; -} - /* ------- Northbound callbacks ------- */ /* @@ -1498,7 +1475,7 @@ static int lib_interface_create(struct nb_cb_create_args *args) const char *ifname; struct interface *ifp; - ifname = yang_dnode_get_string(args->dnode, "./name"); + ifname = yang_dnode_get_string(args->dnode, "name"); switch (args->event) { case NB_EV_VALIDATE: @@ -1709,7 +1686,7 @@ lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args) { const struct interface *ifp = args->list_entry; - return yang_data_new_uint16(args->xpath, ifp->mtu); + return yang_data_new_uint32(args->xpath, ifp->mtu); } /* @@ -1780,6 +1757,8 @@ lib_interface_state_phy_address_get_elem(struct nb_cb_get_elem_args *args) } /* clang-format off */ + +/* cli_show callbacks are kept here for daemons not yet converted to mgmtd */ const struct frr_yang_module_info frr_interface_info = { .name = "frr-interface", .nodes = { @@ -1790,6 +1769,7 @@ const struct frr_yang_module_info frr_interface_info = { .destroy = lib_interface_destroy, .cli_show = cli_show_interface, .cli_show_end = cli_show_interface_end, + .cli_cmp = cli_cmp_interface, .get_next = lib_interface_get_next, .get_keys = lib_interface_get_keys, .lookup_entry = lib_interface_lookup_entry, @@ -1862,3 +1842,27 @@ const struct frr_yang_module_info frr_interface_info = { }, } }; + +const struct frr_yang_module_info frr_interface_cli_info = { + .name = "frr-interface", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-interface:lib/interface", + .cbs = { + .cli_show = cli_show_interface, + .cli_show_end = cli_show_interface_end, + .cli_cmp = cli_cmp_interface, + }, + }, + { + .xpath = "/frr-interface:lib/interface/description", + .cbs = { + .cli_show = cli_show_interface_desc, + }, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/if.h b/lib/if.h index 7b4415d..0dc56bd 100644 --- a/lib/if.h +++ b/lib/if.h @@ -88,8 +88,6 @@ enum zebra_link_type { FreeBSD define value in /usr/include/net/if.h. #define IFNAMSIZ 16 */ - -#define INTERFACE_NAMSIZ IFNAMSIZ #define INTERFACE_HWADDR_MAX 20 typedef signed int ifindex_t; @@ -197,7 +195,7 @@ struct if_link_params { uint32_t min_delay; /* Link Min Delay */ uint32_t max_delay; /* Link Max Delay */ uint32_t delay_var; /* Link Delay Variation */ - float pkt_loss; /* Link Packet Loss */ + uint32_t pkt_loss; /* Link Packet Loss */ float res_bw; /* Residual Bandwidth */ float ava_bw; /* Available Bandwidth */ float use_bw; /* Utilized Bandwidth */ @@ -206,6 +204,8 @@ struct if_link_params { #define INTERFACE_LINK_PARAMS_SIZE sizeof(struct if_link_params) #define HAS_LINK_PARAMS(ifp) ((ifp)->link_params != NULL) +PREDECL_DLIST(if_connected); + /* Interface structure */ struct interface { RB_ENTRY(interface) name_entry, index_entry; @@ -218,7 +218,7 @@ struct interface { To delete, just set ifindex to IFINDEX_INTERNAL to indicate that the interface does not exist in the kernel. */ - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; /* Interface index (should be IFINDEX_INTERNAL for non-kernel or deleted interfaces). @@ -275,12 +275,8 @@ struct interface { /* description of the interface. */ char *desc; - /* Distribute list. */ - void *distribute_in; - void *distribute_out; - /* Connected address list. */ - struct list *connected; + struct if_connected_head connected[1]; /* Neighbor connected address list. */ struct list *nbr_connected; @@ -375,9 +371,6 @@ DECLARE_QOBJ_TYPE(interface); if (vrf) \ RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) -#define FOR_ALL_INTERFACES_ADDRESSES(ifp, connected, node) \ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) - /* called from the library code whenever interfaces are created/deleted * note: interfaces may not be fully realized at that point; also they * may not exist in the system (ifindex = IFINDEX_INTERNAL) @@ -386,13 +379,34 @@ DECLARE_QOBJ_TYPE(interface); * can use 1000+ so they run after the daemon has initialised daemon-specific * interface data */ -DECLARE_HOOK(if_add, (struct interface * ifp), (ifp)); -DECLARE_KOOH(if_del, (struct interface * ifp), (ifp)); +DECLARE_HOOK(if_add, (struct interface *ifp), (ifp)); +DECLARE_KOOH(if_del, (struct interface *ifp), (ifp)); + +/* called (in daemons) when ZAPI tells us the interface actually exists + * (ifindex != IFINDEX_INTERNAL) + * + * WARNING: these 2 hooks NEVER CALLED inside zebra! + */ +DECLARE_HOOK(if_real, (struct interface *ifp), (ifp)); +DECLARE_KOOH(if_unreal, (struct interface *ifp), (ifp)); + +/* called (in daemons) on state changes on interfaces. Whether this is admin + * state (= pure config) or carrier state (= hardware link plugged) depends on + * zebra's "link-detect" configuration. By default, it's carrier state, so + * this won't happen until the interface actually has a link. + * + * WARNING: these 2 hooks NEVER CALLED inside zebra! + */ +DECLARE_HOOK(if_up, (struct interface *ifp), (ifp)); +DECLARE_KOOH(if_down, (struct interface *ifp), (ifp)); + #define METRIC_MAX (~0) /* Connected address structure. */ struct connected { + struct if_connected_item item; + /* Attached interface. */ struct interface *ifp; @@ -420,6 +434,8 @@ struct connected { #define ZEBRA_IFA_SECONDARY (1 << 0) #define ZEBRA_IFA_PEER (1 << 1) #define ZEBRA_IFA_UNNUMBERED (1 << 2) +#define ZEBRA_IFA_NOPREFIXROUTE (1 << 3) + /* N.B. the ZEBRA_IFA_PEER flag should be set if and only if a peer address has been configured. If this flag is set, the destination field must contain the peer address. @@ -442,6 +458,8 @@ struct connected { uint32_t metric; }; +DECLARE_DLIST(if_connected, struct connected, item); + /* Nbr Connected address structure. */ struct nbr_connected { /* Attached interface. */ @@ -512,9 +530,9 @@ extern int if_cmp_name_func(const char *p1, const char *p2); * This is useful for vrf route-leaking. So more than anything * else think before you use VRF_UNKNOWN */ -extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id); +extern void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id); -extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); +extern struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_address_local(const void *matchaddr, @@ -545,7 +563,7 @@ extern int if_set_index(struct interface *ifp, ifindex_t ifindex); /* Delete the interface, but do not free the structure, and leave it in the interface list. It is often advisable to leave the pseudo interface structure because there may be configuration information attached. */ -extern void if_delete_retain(struct interface *); +extern void if_delete_retain(struct interface *ifp); /* Delete and free the interface structure: calls if_delete_retain and then deletes it from the interface list and frees the structure. */ @@ -563,13 +581,13 @@ extern int if_is_pointopoint(const struct interface *ifp); extern int if_is_multicast(const struct interface *ifp); extern void if_terminate(struct vrf *vrf); extern void if_dump_all(void); -extern const char *if_flag_dump(unsigned long); -extern const char *if_link_type_str(enum zebra_link_type); +extern const char *if_flag_dump(unsigned long flags); +extern const char *if_link_type_str(enum zebra_link_type zlt); /* Please use ifindex2ifname instead of if_indextoname where possible; ifindex2ifname uses internal interface info, whereas if_indextoname must make a system call. */ -extern const char *ifindex2ifname(ifindex_t, vrf_id_t vrf_id); +extern const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id); /* Please use ifname2ifindex instead of if_nametoindex where possible; ifname2ifindex uses internal interface info, whereas if_nametoindex must @@ -579,19 +597,20 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id); /* Connected address functions. */ extern struct connected *connected_new(void); extern void connected_free(struct connected **connected); -extern void connected_add(struct interface *, struct connected *); -extern struct connected * -connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *); -extern struct connected *connected_delete_by_prefix(struct interface *, - struct prefix *); -extern struct connected *connected_lookup_prefix(struct interface *, - const struct prefix *); -extern struct connected *connected_lookup_prefix_exact(struct interface *, - const struct prefix *); -extern unsigned int connected_count_by_family(struct interface *, int family); +extern struct connected *connected_add_by_prefix(struct interface *ifp, + struct prefix *p, + struct prefix *dest); +extern struct connected *connected_delete_by_prefix(struct interface *ifp, + struct prefix *p); +extern struct connected *connected_lookup_prefix(struct interface *ifp, + const struct prefix *p); +extern struct connected *connected_lookup_prefix_exact(struct interface *ifp, + const struct prefix *p); +extern unsigned int connected_count_by_family(struct interface *ifp, int family); extern struct nbr_connected *nbr_connected_new(void); -extern void nbr_connected_free(struct nbr_connected *); -struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *); +extern void nbr_connected_free(struct nbr_connected *connected); +struct nbr_connected *nbr_connected_check(struct interface *ifp, + struct prefix *p); struct connected *connected_get_linklocal(struct interface *ifp); /* link parameters */ @@ -599,10 +618,10 @@ bool if_link_params_cmp(struct if_link_params *iflp1, struct if_link_params *iflp2); void if_link_params_copy(struct if_link_params *dst, struct if_link_params *src); -struct if_link_params *if_link_params_get(struct interface *); +struct if_link_params *if_link_params_get(struct interface *ifp); struct if_link_params *if_link_params_enable(struct interface *ifp); struct if_link_params *if_link_params_init(struct interface *ifp); -void if_link_params_free(struct interface *); +void if_link_params_free(struct interface *ifp); /* Northbound. */ struct vty; @@ -610,10 +629,6 @@ extern void if_vty_config_start(struct vty *vty, struct interface *ifp); extern void if_vty_config_end(struct vty *vty); extern void if_cmd_init(int (*config_write)(struct vty *)); extern void if_cmd_init_default(void); -extern void if_zapi_callbacks(int (*create)(struct interface *ifp), - int (*up)(struct interface *ifp), - int (*down)(struct interface *ifp), - int (*destroy)(struct interface *ifp)); extern void if_new_via_zapi(struct interface *ifp); extern void if_up_via_zapi(struct interface *ifp); @@ -621,6 +636,7 @@ extern void if_down_via_zapi(struct interface *ifp); extern void if_destroy_via_zapi(struct interface *ifp); extern const struct frr_yang_module_info frr_interface_info; +extern const struct frr_yang_module_info frr_interface_cli_info; #ifdef __cplusplus } diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 42e1620..5fe5061 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -243,14 +243,14 @@ DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd, void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (yang_dnode_exists(dnode, "./in-route-map")) + if (yang_dnode_exists(dnode, "in-route-map")) vty_out(vty, " route-map %s in %s\n", - yang_dnode_get_string(dnode, "./in-route-map"), - yang_dnode_get_string(dnode, "./interface")); - if (yang_dnode_exists(dnode, "./out-route-map")) + yang_dnode_get_string(dnode, "in-route-map"), + yang_dnode_get_string(dnode, "interface")); + if (yang_dnode_exists(dnode, "out-route-map")) vty_out(vty, " route-map %s out %s\n", - yang_dnode_get_string(dnode, "./out-route-map"), - yang_dnode_get_string(dnode, "./interface")); + yang_dnode_get_string(dnode, "out-route-map"), + yang_dnode_get_string(dnode, "interface")); } void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c index 4f041ff..556e0cf 100644 --- a/lib/imsg-buffer.c +++ b/lib/imsg-buffer.c @@ -6,6 +6,7 @@ */ #include +#include #include "queue.h" #include "imsg.h" diff --git a/lib/jhash.c b/lib/jhash.c index 0d561ef..4e02112 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -86,34 +86,34 @@ uint32_t jhash(const void *key, uint32_t length, uint32_t initval) switch (len) { case 11: c += ((uint32_t)k[10] << 24); - /* fallthru */ + fallthrough; case 10: c += ((uint32_t)k[9] << 16); - /* fallthru */ + fallthrough; case 9: c += ((uint32_t)k[8] << 8); - /* fallthru */ + fallthrough; case 8: b += ((uint32_t)k[7] << 24); - /* fallthru */ + fallthrough; case 7: b += ((uint32_t)k[6] << 16); - /* fallthru */ + fallthrough; case 6: b += ((uint32_t)k[5] << 8); - /* fallthru */ + fallthrough; case 5: b += k[4]; - /* fallthru */ + fallthrough; case 4: a += ((uint32_t)k[3] << 24); - /* fallthru */ + fallthrough; case 3: a += ((uint32_t)k[2] << 16); - /* fallthru */ + fallthrough; case 2: a += ((uint32_t)k[1] << 8); - /* fallthru */ + fallthrough; case 1: a += k[0]; } @@ -148,7 +148,7 @@ uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval) switch (len) { case 2: b += k[1]; - /* fallthru */ + fallthrough; case 1: a += k[0]; } diff --git a/lib/keychain.c b/lib/keychain.c index 640746b..5ff0d1e 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -37,6 +37,7 @@ static void keychain_free(struct keychain *keychain) static struct key *key_new(void) { struct key *key = XCALLOC(MTYPE_KEY, sizeof(struct key)); + QOBJ_REG(key, key); return key; } @@ -77,7 +78,7 @@ static int key_cmp_func(void *arg1, void *arg2) static void key_delete_func(struct key *key) { if (key->string) - free(key->string); + XFREE(MTYPE_KEY, key->string); key_free(key); } @@ -1187,6 +1188,20 @@ static const struct cmd_variable_handler keychain_var_handlers[] = { {.completions = NULL} }; +void keychain_terminate(void) +{ + struct keychain *keychain; + + while (listcount(keychain_list)) { + keychain = listgetdata(listhead(keychain_list)); + + listnode_delete(keychain_list, keychain); + keychain_delete(keychain); + } + + list_delete(&keychain_list); +} + void keychain_init(void) { keychain_list = list_new(); diff --git a/lib/keychain.h b/lib/keychain.h index be93275..c96b74e 100644 --- a/lib/keychain.h +++ b/lib/keychain.h @@ -82,6 +82,7 @@ struct key { DECLARE_QOBJ_TYPE(key); extern void keychain_init(void); +extern void keychain_terminate(void); extern struct keychain *keychain_lookup(const char *); extern struct key *key_lookup_for_accept(const struct keychain *, uint32_t); extern struct key *key_match_for_accept(const struct keychain *, const char *); diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h index f7601eb..3a6ae5b 100644 --- a/lib/ldp_sync.h +++ b/lib/ldp_sync.h @@ -59,7 +59,7 @@ struct ldp_igp_sync_if_state { struct ldp_igp_sync_if_state_req { int proto; ifindex_t ifindex; - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; }; #ifdef __cplusplus diff --git a/lib/libfrr.c b/lib/libfrr.c index 33237df..9e47205 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -6,7 +6,11 @@ */ #include + +#include +#include #include +#include #include #include @@ -33,6 +37,8 @@ #include "frrscript.h" #include "systemd.h" +#include "lib/config_paths.h" + DEFINE_HOOK(frr_early_init, (struct event_loop * tm), (tm)); DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm)); DEFINE_HOOK(frr_config_pre, (struct event_loop * tm), (tm)); @@ -41,10 +47,8 @@ DEFINE_KOOH(frr_early_fini, (), ()); DEFINE_KOOH(frr_fini, (), ()); const char frr_sysconfdir[] = SYSCONFDIR; -char frr_vtydir[256]; -#ifdef HAVE_SQLITE3 -const char frr_dbdir[] = DAEMON_DB_DIR; -#endif +char frr_runstatedir[256] = FRR_RUNSTATE_PATH; +char frr_libstatedir[256] = FRR_LIBSTATE_PATH; const char frr_moduledir[] = MODULE_PATH; const char frr_scriptdir[] = SCRIPT_PATH; @@ -52,7 +56,7 @@ char frr_protoname[256] = "NONE"; char frr_protonameinst[256] = "NONE"; char config_default[512]; -char frr_zclientpath[256]; +char frr_zclientpath[512]; static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 static char dbfile_default[512]; @@ -204,7 +208,7 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { /* note: this functionality is disabled at bottom */ int af; - int port = ZEBRA_PORT; + int port = ZEBRA_TCP_PORT; char *err = NULL; struct sockaddr_in *sin = NULL; struct sockaddr_in6 *sin6 = NULL; @@ -218,7 +222,8 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, break; case '6': path++; - /* fallthrough */ + af = AF_INET6; + break; default: af = AF_INET6; break; @@ -305,11 +310,6 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; -void frr_init_vtydir(void) -{ - snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", ""); -} - void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; @@ -339,16 +339,14 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) if (di->flags & FRR_DETACH_LATER) nodetach_daemon = true; - frr_init_vtydir(); snprintf(config_default, sizeof(config_default), "%s/%s.conf", frr_sysconfdir, di->name); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", - frr_vtydir, di->name); - snprintf(frr_zclientpath, sizeof(frr_zclientpath), - ZEBRA_SERV_PATH, "", ""); + frr_runstatedir, di->name); + snprintf(frr_zclientpath, sizeof(frr_zclientpath), ZAPI_SOCK_NAME); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db", - frr_dbdir, di->name); + frr_libstatedir, di->name); #endif strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); @@ -497,13 +495,15 @@ static int frr_opt(int opt) } di->pathspace = optarg; + snprintf(frr_runstatedir, sizeof(frr_runstatedir), + FRR_RUNSTATE_PATH "/%s", di->pathspace); + snprintf(frr_libstatedir, sizeof(frr_libstatedir), + FRR_LIBSTATE_PATH "/%s", di->pathspace); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_runstatedir, di->name); if (!di->zpathspace) snprintf(frr_zclientpath, sizeof(frr_zclientpath), - ZEBRA_SERV_PATH, "/", di->pathspace); - snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/", - di->pathspace); - snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", - frr_vtydir, di->name); + ZAPI_SOCK_NAME); break; case 'o': vrf_set_default_name(optarg); @@ -724,10 +724,10 @@ struct event_loop *frr_init(void) snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid", - frr_vtydir, di->name, p_instance); + frr_runstatedir, di->name, p_instance); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", - frr_dbdir, p_pathspace, di->name, p_instance); + frr_libstatedir, p_pathspace, di->name, p_instance); #endif zprivs_preinit(di->privs); @@ -756,8 +756,9 @@ struct event_loop *frr_init(void) /* don't mkdir these as root... */ if (!(di->flags & FRR_NO_PRIVSEP)) { + frr_mkdir(frr_libstatedir, false); if (!di->pid_file || !di->vty_path) - frr_mkdir(frr_vtydir, false); + frr_mkdir(frr_runstatedir, false); if (di->pid_file) frr_mkdir(di->pid_file, true); if (di->vty_path) @@ -1044,7 +1045,7 @@ void frr_vty_serv_start(void) const char *dir; char defvtydir[256]; - snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir); + snprintf(defvtydir, sizeof(defvtydir), "%s", frr_runstatedir); dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; @@ -1270,6 +1271,161 @@ void frr_fini(void) } } +struct json_object *frr_daemon_state_load(void) +{ + struct json_object *state; + char **state_path; + + assertf(di->state_paths, + "CODE BUG: daemon trying to load state, but no state path in frr_daemon_info"); + + for (state_path = di->state_paths; *state_path; state_path++) { + state = json_object_from_file(*state_path); + if (state) + return state; + } + + return json_object_new_object(); +} + +/* cross-reference file_write_config() in command.c + * the code there is similar but not identical (configs use a unique temporary + * name for writing and keep a backup of the previous config.) + */ +void frr_daemon_state_save(struct json_object **statep) +{ + struct json_object *state = *statep; + char *state_path, *slash, *temp_name, **other; + size_t name_len, json_len; + const char *json_str; + int dirfd, fd; + + assertf(di->state_paths, + "CODE BUG: daemon trying to save state, but no state path in frr_daemon_info"); + + state_path = di->state_paths[0]; + json_str = json_object_to_json_string_ext(state, + JSON_C_TO_STRING_PRETTY); + json_len = strlen(json_str); + + /* To correctly fsync() and ensure we have either consistent old state + * or consistent new state but no fs-damage garbage inbetween, we need + * to work with a directory fd. If we need that anyway we might as + * well use the dirfd with openat() & co in fd-relative operations. + */ + + slash = strrchr(state_path, '/'); + if (slash) { + char *state_dir; + + state_dir = XSTRDUP(MTYPE_TMP, state_path); + state_dir[slash - state_path] = '\0'; + dirfd = open(state_dir, O_DIRECTORY | O_RDONLY); + XFREE(MTYPE_TMP, state_dir); + + if (dirfd < 0) { + zlog_err("failed to open directory %pSQq for saving daemon state: %m", + state_dir); + return; + } + + /* skip to file name */ + slash++; + } else { + dirfd = open(".", O_DIRECTORY | O_RDONLY); + if (dirfd < 0) { + zlog_err( + "failed to open current directory for saving daemon state: %m"); + return; + } + + /* file name = path */ + slash = state_path; + } + + /* unlike saving configs, a temporary unique filename is unhelpful + * here as it might litter files on limited write-heavy storage + * (think switch with small NOR flash for frequently written data.) + * + * => always use filename with .sav suffix, worst case it litters one + * file. + */ + name_len = strlen(slash); + temp_name = XMALLOC(MTYPE_TMP, name_len + 5); + memcpy(temp_name, slash, name_len); + memcpy(temp_name + name_len, ".sav", 5); + + /* state file is always 0600, it's by and for FRR itself only */ + fd = openat(dirfd, temp_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + zlog_err("failed to open temporary daemon state save file for %pSQq: %m", + state_path); + goto out_closedir_free; + } + + while (json_len) { + ssize_t nwr = write(fd, json_str, json_len); + + if (nwr <= 0) { + zlog_err("failed to write temporary daemon state to %pSQq: %m", + state_path); + + close(fd); + unlinkat(dirfd, temp_name, 0); + goto out_closedir_free; + } + + json_str += nwr; + json_len -= nwr; + } + + /* fsync is theoretically implicit in close(), but... */ + if (fsync(fd) < 0) + zlog_warn("fsync for daemon state %pSQq failed: %m", state_path); + close(fd); + + /* this is the *actual* fsync that ensures we're consistent. The + * file fsync only syncs the inode, but not the directory entry + * referring to it. + */ + if (fsync(dirfd) < 0) + zlog_warn("directory fsync for daemon state %pSQq failed: %m", + state_path); + + /* atomic, hopefully. */ + if (renameat(dirfd, temp_name, dirfd, slash) < 0) { + zlog_err("renaming daemon state %pSQq to %pSQq failed: %m", + temp_name, state_path); + /* no unlink here, give the user a chance to investigate */ + goto out_closedir_free; + } + + /* and the rename needs to be synced too */ + if (fsync(dirfd) < 0) + zlog_warn("directory fsync for daemon state %pSQq failed after rename: %m", + state_path); + + /* daemon may specify other deprecated paths to load from; since we + * just saved successfully we should delete those. + */ + for (other = di->state_paths + 1; *other; other++) { + if (unlink(*other) == 0) + continue; + if (errno == ENOENT || errno == ENOTDIR) + continue; + + zlog_warn("failed to remove deprecated daemon state file %pSQq: %m", + *other); + } + +out_closedir_free: + XFREE(MTYPE_TMP, temp_name); + close(dirfd); + + json_object_free(state); + *statep = NULL; +} + #ifdef INTERP static const char interp[] __attribute__((section(".interp"), used)) = INTERP; diff --git a/lib/libfrr.h b/lib/libfrr.h index b260a54..ee436d9 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -22,6 +22,8 @@ extern "C" { #endif +#define ZAPI_SOCK_NAME "%s/zserv.api", frr_runstatedir + /* The following options disable specific command line options that * are not applicable for a particular daemon. */ @@ -53,6 +55,49 @@ struct log_arg { }; DECLARE_DLIST(log_args, struct log_arg, itm); +/* Registry of daemons' port defaults. Many of these are VTY ports; some + * daemons have default ports for other features such as ospfapi, or zebra's + * FPM. + */ + +/* default zebra TCP port for zapi; this is currently disabled for security + * reasons. + */ +#define ZEBRA_TCP_PORT 2600 + +#define ZEBRA_VTY_PORT 2601 +#define RIP_VTY_PORT 2602 +#define RIPNG_VTY_PORT 2603 +#define OSPF_VTY_PORT 2604 +#define BGP_VTY_PORT 2605 +#define OSPF6_VTY_PORT 2606 + +/* Default API server port to accept connection request from client-side. + * This value could be overridden by "ospfapi" entry in "/etc/services". + */ +#define OSPF_API_SYNC_PORT 2607 + +#define ISISD_VTY_PORT 2608 +#define BABEL_VTY_PORT 2609 +#define NHRP_VTY_PORT 2610 +#define PIMD_VTY_PORT 2611 +#define LDP_VTY_PORT 2612 +#define EIGRP_VTY_PORT 2613 +#define SHARP_VTY_PORT 2614 +#define PBR_VTY_PORT 2615 +#define STATIC_VTY_PORT 2616 +#define BFDD_VTY_PORT 2617 +#define FABRICD_VTY_PORT 2618 +#define VRRP_VTY_PORT 2619 + +/* default port for FPM connections */ +#define FPM_DEFAULT_PORT 2620 + +#define PATH_VTY_PORT 2621 +#define PIM6D_VTY_PORT 2622 +#define MGMTD_VTY_PORT 2623 +/* Registry of daemons' port defaults */ + enum frr_cli_mode { FRR_CLI_CLASSIC = 0, FRR_CLI_TRANSACTIONAL, @@ -85,6 +130,7 @@ struct frr_daemon_info { const char *vty_path; const char *module_path; const char *script_path; + char **state_paths; const char *pathspace; bool zpathspace; @@ -130,7 +176,6 @@ struct frr_daemon_info { .version = FRR_VERSION, ); \ MACRO_REQUIRE_SEMICOLON() /* end */ -extern void frr_init_vtydir(void); extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv); extern void frr_opt_add(const char *optstr, const struct option *longopts, const char *helpstr); @@ -161,6 +206,10 @@ extern void frr_vty_serv_stop(void); extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, const char *path); +struct json_object; +extern struct json_object *frr_daemon_state_load(void); +extern void frr_daemon_state_save(struct json_object **statep); + /* these two are before the protocol daemon does its own shutdown * it's named this way being the counterpart to frr_late_init */ DECLARE_KOOH(frr_early_fini, (), ()); @@ -170,9 +219,10 @@ DECLARE_KOOH(frr_fini, (), ()); extern void frr_fini(void); extern char config_default[512]; -extern char frr_zclientpath[256]; +extern char frr_zclientpath[512]; extern const char frr_sysconfdir[]; -extern char frr_vtydir[256]; +extern char frr_runstatedir[256]; +extern char frr_libstatedir[256]; extern const char frr_moduledir[]; extern const char frr_scriptdir[]; diff --git a/lib/libospf.h b/lib/libospf.h index 9a64325..45e7fb1 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -61,7 +61,7 @@ extern "C" { #define OSPF_TRANSMIT_DELAY_DEFAULT 1 #define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */ -#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Mbps */ +#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */ #define OSPF_POLL_INTERVAL_DEFAULT 60 #define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0 diff --git a/lib/log.c b/lib/log.c index ac31faa..969ca79 100644 --- a/lib/log.c +++ b/lib/log.c @@ -8,6 +8,10 @@ #include +#ifdef HAVE_GLIBC_BACKTRACE +#include +#endif /* HAVE_GLIBC_BACKTRACE */ + #include "zclient.h" #include "log.h" #include "memory.h" @@ -440,11 +444,11 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_NEIGH_DISCOVER), DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST), DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY), - DESC_ENTRY(ZEBRA_NHRP_NEIGH_ADDED), - DESC_ENTRY(ZEBRA_NHRP_NEIGH_REMOVED), - DESC_ENTRY(ZEBRA_NHRP_NEIGH_GET), - DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER), - DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_ADDED), + DESC_ENTRY(ZEBRA_NEIGH_REMOVED), + DESC_ENTRY(ZEBRA_NEIGH_GET), + DESC_ENTRY(ZEBRA_NEIGH_REGISTER), + DESC_ENTRY(ZEBRA_NEIGH_UNREGISTER), DESC_ENTRY(ZEBRA_NEIGH_IP_ADD), DESC_ENTRY(ZEBRA_NEIGH_IP_DEL), DESC_ENTRY(ZEBRA_CONFIGURE_ARP), @@ -547,6 +551,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_KERNEL; else if (strmatch(s, "connected")) return ZEBRA_ROUTE_CONNECT; + else if (strmatch(s, "local")) + return ZEBRA_ROUTE_LOCAL; else if (strmatch(s, "static")) return ZEBRA_ROUTE_STATIC; else if (strmatch(s, "rip")) @@ -573,12 +579,16 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_SHARP; else if (strmatch(s, "openfabric")) return ZEBRA_ROUTE_OPENFABRIC; + else if (strmatch(s, "table-direct")) + return ZEBRA_ROUTE_TABLE_DIRECT; } if (afi == AFI_IP6) { if (strmatch(s, "kernel")) return ZEBRA_ROUTE_KERNEL; else if (strmatch(s, "connected")) return ZEBRA_ROUTE_CONNECT; + else if (strmatch(s, "local")) + return ZEBRA_ROUTE_LOCAL; else if (strmatch(s, "static")) return ZEBRA_ROUTE_STATIC; else if (strmatch(s, "ripng")) @@ -603,6 +613,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_SHARP; else if (strmatch(s, "openfabric")) return ZEBRA_ROUTE_OPENFABRIC; + else if (strmatch(s, "table-direct")) + return ZEBRA_ROUTE_TABLE_DIRECT; } return -1; } diff --git a/lib/log_vty.c b/lib/log_vty.c index 26e608d..323b1b1 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -15,6 +15,7 @@ #include "lib/lib_errors.h" #include "lib/printfrr.h" #include "lib/systemd.h" +#include "lib/vtysh_daemons.h" #include "lib/log_vty_clippy.c" @@ -459,6 +460,70 @@ DEFUN (clear_log_cmdline, return CMD_SUCCESS; } +/* Per-daemon log file config */ +DEFUN (config_log_dmn_file, + config_log_dmn_file_cmd, + "log daemon " DAEMONS_LIST " file FILENAME [$levelarg]", + "Logging control\n" + "Specific daemon\n" + DAEMONS_STR + "Logging to file\n" + "Logging filename\n" + LOG_LEVEL_DESC) +{ + int level = log_default_lvl; + int idx = 0; + const char *d_str; + const char *filename; + const char *levelarg = NULL; + + d_str = argv[2]->text; + + /* Ignore if not for this daemon */ + if (!strmatch(d_str, frr_get_progname())) + return CMD_SUCCESS; + + if (argv_find(argv, argc, "file", &idx)) + filename = argv[idx + 1]->arg; + else + return CMD_SUCCESS; + + if (argc > 5) + levelarg = argv[5]->text; + + if (levelarg) { + level = log_level_match(levelarg); + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } + return set_log_file(&zt_file, vty, filename, level); +} + +/* Per-daemon no log file */ +DEFUN (no_config_log_dmn_file, + no_config_log_dmn_file_cmd, + "no log daemon " DAEMONS_LIST " file [FILENAME [LEVEL]]", + NO_STR + "Logging control\n" + "Specific daemon\n" + DAEMONS_STR + "Cancel logging to file\n" + "Logging file name\n" + "Logging level\n") +{ + const char *d_str; + + d_str = argv[3]->text; + + /* Ignore if not for this daemon */ + if (!strmatch(d_str, frr_get_progname())) + return CMD_SUCCESS; + + zt_file.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_file); + return CMD_SUCCESS; +} + DEFPY (config_log_file, config_log_file_cmd, "log file FILENAME [$levelarg]", @@ -837,6 +902,8 @@ void log_config_write(struct vty *vty) vty_out(vty, "no log error-category\n"); if (!zlog_get_prefix_xid()) vty_out(vty, "no log unique-id\n"); + if (zlog_get_immediate_mode()) + vty_out(vty, "log immediate-mode\n"); if (logmsgs_with_persist_bt) { struct xrefdata *xrd; @@ -857,12 +924,24 @@ void log_config_write(struct vty *vty) } } +static int log_vty_fini(void) +{ + if (zt_file_cmdline.filename) + zlog_file_fini(&zt_file_cmdline); + if (zt_file.filename) + zlog_file_fini(&zt_file); + return 0; +} + + static int log_vty_init(const char *progname, const char *protoname, unsigned short instance, uid_t uid, gid_t gid) { zlog_progname = progname; zlog_protoname = protoname; + hook_register(zlog_fini, log_vty_fini); + zlog_set_prefix_ec(true); zlog_set_prefix_xid(true); @@ -892,6 +971,8 @@ void log_cmd_init(void) install_element(CONFIG_NODE, &config_log_monitor_cmd); install_element(CONFIG_NODE, &no_config_log_monitor_cmd); install_element(CONFIG_NODE, &config_log_file_cmd); + install_element(CONFIG_NODE, &config_log_dmn_file_cmd); + install_element(CONFIG_NODE, &no_config_log_dmn_file_cmd); install_element(CONFIG_NODE, &no_config_log_file_cmd); install_element(CONFIG_NODE, &config_log_syslog_cmd); install_element(CONFIG_NODE, &no_config_log_syslog_cmd); diff --git a/lib/mgmt.proto b/lib/mgmt.proto index 9e4b39a..01a99ab 100644 --- a/lib/mgmt.proto +++ b/lib/mgmt.proto @@ -55,7 +55,10 @@ message YangData { enum CfgDataReqType { REQ_TYPE_NONE = 0; SET_DATA = 1; - DELETE_DATA = 2; + REMOVE_DATA = 2; + CREATE_DATA = 3; + DELETE_DATA = 4; + REPLACE_DATA = 5; } message YangCfgDataReq { @@ -73,8 +76,9 @@ message YangGetDataReq { // message BeSubscribeReq { required string client_name = 1; - required bool subscribe_xpaths = 2; - repeated string xpath_reg = 3; + repeated string config_xpaths = 2; + repeated string oper_xpaths = 3; + repeated string notif_xpaths = 4; } message BeSubscribeReply { @@ -94,16 +98,14 @@ message BeTxnReply { message BeCfgDataCreateReq { required uint64 txn_id = 1; - required uint64 batch_id = 2; - repeated YangCfgDataReq data_req = 3; - required bool end_of_data = 4; + repeated YangCfgDataReq data_req = 2; + required bool end_of_data = 3; } message BeCfgDataCreateReply { required uint64 txn_id = 1; - required uint64 batch_id = 2; - required bool success = 3; - optional string error_if_any = 4; + required bool success = 2; + optional string error_if_any = 3; } message BeCfgDataApplyReq { @@ -112,15 +114,8 @@ message BeCfgDataApplyReq { message BeCfgDataApplyReply { required uint64 txn_id = 1; - repeated uint64 batch_ids = 2; - required bool success = 3; - optional string error_if_any = 4; -} - -message BeOperDataGetReq { - required uint64 txn_id = 1; - required uint64 batch_id = 2; - repeated YangGetDataReq data = 3; + required bool success = 2; + optional string error_if_any = 3; } message YangDataReply { @@ -128,14 +123,6 @@ message YangDataReply { required int64 next_indx = 2; } -message BeOperDataGetReply { - required uint64 txn_id = 1; - required uint64 batch_id = 2; - required bool success = 3; - optional string error = 4; - optional YangDataReply data = 5; -} - // // Any message on the MGMTD Backend Interface. // @@ -149,8 +136,6 @@ message BeMessage { BeCfgDataCreateReply cfg_data_reply = 7; BeCfgDataApplyReq cfg_apply_req = 8; BeCfgDataApplyReply cfg_apply_reply = 9; - BeOperDataGetReq get_req = 10; - BeOperDataGetReply get_reply = 11; } } diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 7bd9980..f483d48 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -8,15 +8,18 @@ #include #include "debug.h" #include "compiler.h" +#include "darr.h" #include "libfrr.h" -#include "mgmtd/mgmt.h" +#include "lib_errors.h" #include "mgmt_be_client.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "network.h" #include "northbound.h" #include "stream.h" #include "sockopt.h" +#include "northbound_cli.h" #include "lib/mgmt_be_client_clippy.c" @@ -24,11 +27,11 @@ DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT, "backend client"); DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT_NAME, "backend client name"); DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data"); DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_GT_CB_ARGS, "backend get-tree cb args"); enum mgmt_be_txn_event { MGMTD_BE_TXN_PROC_SETCFG = 1, MGMTD_BE_TXN_PROC_GETCFG, - MGMTD_BE_TXN_PROC_GETDATA }; struct mgmt_be_set_cfg_req { @@ -36,24 +39,20 @@ struct mgmt_be_set_cfg_req { uint16_t num_cfg_changes; }; -struct mgmt_be_get_data_req { - char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH]; - uint16_t num_xpaths; -}; - struct mgmt_be_txn_req { enum mgmt_be_txn_event event; union { struct mgmt_be_set_cfg_req set_cfg; - struct mgmt_be_get_data_req get_data; } req; }; +struct be_oper_iter_arg { + struct lyd_node *root; /* the tree we are building */ + struct lyd_node *hint; /* last node added */ +}; + PREDECL_LIST(mgmt_be_batches); struct mgmt_be_batch_ctx { - /* Batch-Id as assigned by MGMTD */ - uint64_t batch_id; - struct mgmt_be_txn_req txn_req; uint32_t flags; @@ -116,15 +115,22 @@ struct mgmt_be_client { #define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \ frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn)) -struct debug mgmt_dbg_be_client = {0, "Management backend client operations"}; - -const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = { -#ifdef HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = "staticd", -#endif - [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid", +struct debug mgmt_dbg_be_client = { + .desc = "Management backend client operations" }; +/* NOTE: only one client per proc for now. */ +static struct mgmt_be_client *__be_client; + +static int be_client_send_native_msg(struct mgmt_be_client *client_ctx, + void *msg, size_t len, + bool short_circuit_ok) +{ + return msg_conn_send_msg(&client_ctx->client.conn, + MGMT_MSG_VERSION_NATIVE, msg, len, NULL, + short_circuit_ok); +} + static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx, Mgmtd__BeMessage *be_msg) { @@ -135,37 +141,15 @@ static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx, } static struct mgmt_be_batch_ctx * -mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn, - uint64_t batch_id) -{ - struct mgmt_be_batch_ctx *batch = NULL; - - FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { - if (batch->batch_id == batch_id) - return batch; - } - - return NULL; -} - -static struct mgmt_be_batch_ctx * -mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id) +mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn) { struct mgmt_be_batch_ctx *batch = NULL; - batch = mgmt_be_find_batch_by_id(txn, batch_id); - if (!batch) { - batch = XCALLOC(MTYPE_MGMTD_BE_BATCH, - sizeof(struct mgmt_be_batch_ctx)); - assert(batch); + batch = XCALLOC(MTYPE_MGMTD_BE_BATCH, sizeof(struct mgmt_be_batch_ctx)); - batch->batch_id = batch_id; - mgmt_be_batches_add_tail(&txn->cfg_batches, batch); + mgmt_be_batches_add_tail(&txn->cfg_batches, batch); - MGMTD_BE_CLIENT_DBG("Added new batch-id: %" PRIu64 - " to transaction", - batch_id); - } + debug_be_client("Added new batch to transaction"); return batch; } @@ -218,7 +202,8 @@ mgmt_be_find_txn_by_id(struct mgmt_be_client *client_ctx, uint64_t txn_id, if (txn->txn_id == txn_id) return txn; if (warn) - MGMTD_BE_CLIENT_ERR("Unknown txn-id: %" PRIu64, txn_id); + log_err_be_client("client %s unkonwn txn-id: %" PRIu64, + client_ctx->name, txn_id); return NULL; } @@ -230,8 +215,8 @@ mgmt_be_txn_create(struct mgmt_be_client *client_ctx, uint64_t txn_id) txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false); if (txn) { - MGMTD_BE_CLIENT_ERR("Can't create existing txn-id: %" PRIu64, - txn_id); + log_err_be_client("Can't create existing txn-id: %" PRIu64, + txn_id); return NULL; } @@ -242,7 +227,7 @@ mgmt_be_txn_create(struct mgmt_be_client *client_ctx, uint64_t txn_id) mgmt_be_batches_init(&txn->apply_cfgs); mgmt_be_txns_add_tail(&client_ctx->txn_head, txn); - MGMTD_BE_CLIENT_DBG("Created new txn-id: %" PRIu64, txn_id); + debug_be_client("Created new txn-id: %" PRIu64, txn_id); return txn; } @@ -291,6 +276,84 @@ static void mgmt_be_cleanup_all_txns(struct mgmt_be_client *client_ctx) } } + +/** + * Send an error back to MGMTD using native messaging. + * + * Args: + * client: the BE client. + * txn_id: the txn_id this error pertains to. + * short_circuit_ok: True if OK to short-circuit the call. + * error: An integer error value. + * errfmt: An error format string (i.e., printfrr) + * ...: args for use by the `errfmt` format string. + * + * Return: + * the return value from the underlying send message function. + */ +static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) + PRINTFRR(6, 7); + +static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, errfmt); + ret = vmgmt_msg_native_send_error(&client->client.conn, txn_id, req_id, + short_circuit_ok, error, errfmt, ap); + va_end(ap); + + return ret; +} + +static int mgmt_be_send_notification(void *__be_client, const char *xpath, + const struct lyd_node *tree) +{ + struct mgmt_be_client *client = __be_client; + struct mgmt_msg_notify_data *msg = NULL; + LYD_FORMAT format = LYD_JSON; + uint8_t **darrp; + LY_ERR err; + int ret = 0; + + assert(tree); + + debug_be_client("%s: sending YANG notification: %s", __func__, + tree->schema->name); + /* + * Allocate a message and append the data to it using `format` + */ + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0, + MTYPE_MSG_NATIVE_NOTIFY); + msg->code = MGMT_MSG_CODE_NOTIFY; + msg->result_type = format; + + mgmt_msg_native_xpath_encode(msg, xpath); + + darrp = mgmt_msg_native_get_darrp(msg); + err = yang_print_tree_append(darrp, tree, format, + (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT | + LYD_PRINT_WITHSIBLINGS)); + if (err) { + flog_err(EC_LIB_LIBYANG, + "%s: error creating notification data: %s", __func__, + ly_strerrcode(err)); + ret = 1; + goto done; + } + + (void)be_client_send_native_msg(client, msg, + mgmt_msg_native_get_msg_len(msg), false); +done: + mgmt_msg_native_free_msg(msg); + return ret; +} + static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx, uint64_t txn_id, bool create) { @@ -306,7 +369,7 @@ static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY; be_msg.txn_reply = &txn_reply; - MGMTD_BE_CLIENT_DBG("Sending TXN_REPLY txn-id %" PRIu64, txn_id); + debug_be_client("Sending TXN_REPLY txn-id %" PRIu64, txn_id); return mgmt_be_client_send_msg(client_ctx, &be_msg); } @@ -317,7 +380,7 @@ static int mgmt_be_process_txn_req(struct mgmt_be_client *client_ctx, struct mgmt_be_txn_ctx *txn; if (create) { - MGMTD_BE_CLIENT_DBG("Creating new txn-id %" PRIu64, txn_id); + debug_be_client("Creating new txn-id %" PRIu64, txn_id); txn = mgmt_be_txn_create(client_ctx, txn_id); if (!txn) @@ -328,7 +391,7 @@ static int mgmt_be_process_txn_req(struct mgmt_be_client *client_ctx, client_ctx->user_data, &txn->client_data, false); } else { - MGMTD_BE_CLIENT_DBG("Deleting txn-id: %" PRIu64, txn_id); + debug_be_client("Deleting txn-id: %" PRIu64, txn_id); txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false); if (txn) mgmt_be_txn_delete(client_ctx, &txn); @@ -342,8 +405,7 @@ failed: } static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx, - uint64_t txn_id, uint64_t batch_id, - bool success, + uint64_t txn_id, bool success, const char *error_if_any) { Mgmtd__BeMessage be_msg; @@ -351,7 +413,6 @@ static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx, mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply); cfgdata_reply.txn_id = (uint64_t)txn_id; - cfgdata_reply.batch_id = (uint64_t)batch_id; cfgdata_reply.success = success; if (error_if_any) cfgdata_reply.error_if_any = (char *)error_if_any; @@ -360,9 +421,7 @@ static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY; be_msg.cfg_data_reply = &cfgdata_reply; - MGMTD_BE_CLIENT_DBG("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64 - " batch-id: %" PRIu64, - txn_id, batch_id); + debug_be_client("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64, txn_id); return mgmt_be_client_send_msg(client_ctx, &be_msg); } @@ -373,9 +432,8 @@ static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn) assert(txn && txn->client); if (txn->nb_txn) { - MGMTD_BE_CLIENT_ERR( - "Aborting configs after prep for txn-id: %" PRIu64, - txn->txn_id); + log_err_be_client("Aborting configs after prep for txn-id: %" PRIu64, + txn->txn_id); nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg)); txn->nb_txn = 0; } @@ -386,9 +444,8 @@ static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn) * This is one txn ctx but the candidate_config is per client ctx, how * does that work? */ - MGMTD_BE_CLIENT_DBG( - "Reset candidate configurations after abort of txn-id: %" PRIu64, - txn->txn_id); + debug_be_client("Reset candidate configurations after abort of txn-id: %" PRIu64, + txn->txn_id); nb_config_replace(txn->client->candidate_config, txn->client->running_config, true); } @@ -433,15 +490,12 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) client_ctx->candidate_config, txn_req->req.set_cfg.cfg_changes, (size_t)txn_req->req.set_cfg.num_cfg_changes, - NULL, NULL, 0, err_buf, sizeof(err_buf), - &error); + NULL, true, err_buf, sizeof(err_buf), &error); if (error) { err_buf[sizeof(err_buf) - 1] = 0; - MGMTD_BE_CLIENT_ERR( - "Failed to update configs for txn-id: %" PRIu64 - " batch-id: %" PRIu64 - " to candidate, err: '%s'", - txn->txn_id, batch->batch_id, err_buf); + log_err_be_client("Failed to update configs for txn-id: %" PRIu64 + " to candidate, err: '%s'", + txn->txn_id, err_buf); return -1; } gettimeofday(&edit_nb_cfg_end, NULL); @@ -479,21 +533,19 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) if (err != NB_OK) { err_buf[sizeof(err_buf) - 1] = 0; if (err == NB_ERR_VALIDATION) - MGMTD_BE_CLIENT_ERR( - "Failed to validate configs txn-id: %" PRIu64 - " %zu batches, err: '%s'", - txn->txn_id, num_processed, err_buf); + log_err_be_client("Failed to validate configs txn-id: %" PRIu64 + " %zu batches, err: '%s'", + txn->txn_id, num_processed, err_buf); else - MGMTD_BE_CLIENT_ERR( - "Failed to prepare configs for txn-id: %" PRIu64 - " %zu batches, err: '%s'", - txn->txn_id, num_processed, err_buf); + log_err_be_client("Failed to prepare configs for txn-id: %" PRIu64 + " %zu batches, err: '%s'", + txn->txn_id, num_processed, err_buf); error = true; SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED); } else - MGMTD_BE_CLIENT_DBG("Prepared configs for txn-id: %" PRIu64 - " %zu batches", - txn->txn_id, num_processed); + debug_be_client("Prepared configs for txn-id: %" PRIu64 + " %zu batches", + txn->txn_id, num_processed); gettimeofday(&prep_nb_cfg_end, NULL); prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start); @@ -504,9 +556,6 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) client_ctx->num_prep_nb_cfg++; FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { - mgmt_be_send_cfgdata_create_reply( - client_ctx, txn->txn_id, batch->batch_id, - error ? false : true, error ? err_buf : NULL); if (!error) { SET_FLAG(batch->flags, MGMTD_BE_BATCH_FLAGS_CFG_PREPARED); @@ -515,10 +564,12 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) } } - MGMTD_BE_CLIENT_DBG( - "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", - client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm, - client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); + mgmt_be_send_cfgdata_create_reply(client_ctx, txn->txn_id, + error ? false : true, error ? err_buf : NULL); + + debug_be_client("Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", + client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm, + client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); if (error) mgmt_be_txn_cfg_abort(txn); @@ -531,7 +582,6 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) */ static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx, struct mgmt_be_txn_ctx *txn, - uint64_t batch_id, Mgmtd__YangCfgDataReq *cfg_req[], int num_req) { @@ -540,27 +590,39 @@ static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx, int index; struct nb_cfg_change *cfg_chg; - batch = mgmt_be_batch_create(txn, batch_id); - if (!batch) { - MGMTD_BE_CLIENT_ERR("Batch create failed!"); - return -1; - } + batch = mgmt_be_batch_create(txn); + assert(batch); txn_req = &batch->txn_req; txn_req->event = MGMTD_BE_TXN_PROC_SETCFG; - MGMTD_BE_CLIENT_DBG("Created SETCFG request for batch-id: %" PRIu64 - " txn-id: %" PRIu64 " cfg-items:%d", - batch_id, txn->txn_id, num_req); + debug_be_client("Created SETCFG request for txn-id: %" PRIu64 + " cfg-items:%d", + txn->txn_id, num_req); txn_req->req.set_cfg.num_cfg_changes = num_req; for (index = 0; index < num_req; index++) { cfg_chg = &txn_req->req.set_cfg.cfg_changes[index]; - if (cfg_req[index]->req_type - == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) + /* + * Treat all operations as destroy or modify, because we don't + * need additional existence checks on the backend. Everything + * is already checked by mgmtd. + */ + switch (cfg_req[index]->req_type) { + case MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA: + case MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA: cfg_chg->operation = NB_OP_DESTROY; - else - cfg_chg->operation = NB_OP_CREATE; + break; + case MGMTD__CFG_DATA_REQ_TYPE__SET_DATA: + case MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA: + case MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA: + cfg_chg->operation = NB_OP_MODIFY; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REQ_TYPE_NONE: + case _MGMTD__CFG_DATA_REQ_TYPE_IS_INT_SIZE: + default: + continue; + } strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath, sizeof(cfg_chg->xpath)); @@ -584,7 +646,7 @@ static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx, } static int mgmt_be_process_cfgdata_req(struct mgmt_be_client *client_ctx, - uint64_t txn_id, uint64_t batch_id, + uint64_t txn_id, Mgmtd__YangCfgDataReq *cfg_req[], int num_req, bool end_of_data) { @@ -594,11 +656,10 @@ static int mgmt_be_process_cfgdata_req(struct mgmt_be_client *client_ctx, if (!txn) goto failed; - mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id, cfg_req, - num_req); + mgmt_be_update_setcfg_in_batch(client_ctx, txn, cfg_req, num_req); if (txn && end_of_data) { - MGMTD_BE_CLIENT_DBG("End of data; CFG_PREPARE_REQ processing"); + debug_be_client("End of data; CFG_PREPARE_REQ processing"); if (mgmt_be_txn_cfg_prepare(txn)) goto failed; } @@ -610,8 +671,7 @@ failed: } static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx, - uint64_t txn_id, uint64_t batch_ids[], - size_t num_batch_ids, bool success, + uint64_t txn_id, bool success, const char *error_if_any) { Mgmtd__BeMessage be_msg; @@ -620,8 +680,6 @@ static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx, mgmtd__be_cfg_data_apply_reply__init(&apply_reply); apply_reply.success = success; apply_reply.txn_id = txn_id; - apply_reply.batch_ids = (uint64_t *)batch_ids; - apply_reply.n_batch_ids = num_batch_ids; if (error_if_any) apply_reply.error_if_any = (char *)error_if_any; @@ -630,12 +688,7 @@ static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY; be_msg.cfg_apply_reply = &apply_reply; - MGMTD_BE_CLIENT_DBG( - "Sending CFG_APPLY_REPLY txn-id %" PRIu64 - " %zu batch ids %" PRIu64 " - %" PRIu64, - txn_id, num_batch_ids, - success && num_batch_ids ? batch_ids[0] : 0, - success && num_batch_ids ? batch_ids[num_batch_ids - 1] : 0); + debug_be_client("Sending CFG_APPLY_REPLY txn-id %" PRIu64, txn_id); return mgmt_be_client_send_msg(client_ctx, &be_msg); } @@ -648,14 +701,11 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn) unsigned long apply_nb_cfg_tm; struct mgmt_be_batch_ctx *batch; char err_buf[BUFSIZ]; - size_t num_processed; - static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ]; assert(txn && txn->client); client_ctx = txn->client; assert(txn->nb_txn); - num_processed = 0; /* * Now apply all the batches we have applied in one go. @@ -673,9 +723,6 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn) client_ctx->num_apply_nb_cfg++; txn->nb_txn = NULL; - /* - * Send back CFG_APPLY_REPLY for all batches applied. - */ FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) { /* * No need to delete the batch yet. Will be deleted during @@ -684,22 +731,12 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn) SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED); mgmt_be_batches_del(&txn->apply_cfgs, batch); mgmt_be_batches_add_tail(&txn->cfg_batches, batch); - - batch_ids[num_processed] = batch->batch_id; - num_processed++; - if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) { - mgmt_be_send_apply_reply(client_ctx, txn->txn_id, - batch_ids, num_processed, - true, NULL); - num_processed = 0; - } } - mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids, - num_processed, true, NULL); + mgmt_be_send_apply_reply(client_ctx, txn->txn_id, true, NULL); - MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec", - apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm); + debug_be_client("Nb-apply-duration %lu (avg: %lu) uSec", + apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm); return 0; } @@ -713,7 +750,7 @@ static int mgmt_be_process_cfg_apply(struct mgmt_be_client *client_ctx, if (!txn) goto failed; - MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing"); + debug_be_client("Trigger CFG_APPLY_REQ processing"); if (mgmt_be_txn_proc_cfgapply(txn)) goto failed; @@ -736,50 +773,45 @@ static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx, */ switch ((int)be_msg->message_case) { case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: - MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u", - be_msg->subscr_reply->success); + debug_be_client("Got SUBSCR_REPLY success %u", + be_msg->subscr_reply->success); + + if (client_ctx->cbs.subscr_done) + (*client_ctx->cbs.subscr_done)(client_ctx, + client_ctx->user_data, + be_msg->subscr_reply + ->success); break; case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: - MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64, - be_msg->txn_req->create ? "Create" - : "Delete", - be_msg->txn_req->txn_id); + debug_be_client("Got TXN_REQ %s txn-id: %" PRIu64, + be_msg->txn_req->create ? "Create" : "Delete", + be_msg->txn_req->txn_id); mgmt_be_process_txn_req(client_ctx, be_msg->txn_req->txn_id, be_msg->txn_req->create); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: - MGMTD_BE_CLIENT_DBG("Got CFG_DATA_REQ txn-id: %" PRIu64 - " batch-id: %" PRIu64 " end-of-data %u", - be_msg->cfg_data_req->txn_id, - be_msg->cfg_data_req->batch_id, - be_msg->cfg_data_req->end_of_data); + debug_be_client("Got CFG_DATA_REQ txn-id: %" PRIu64 + " end-of-data %u", + be_msg->cfg_data_req->txn_id, + be_msg->cfg_data_req->end_of_data); mgmt_be_process_cfgdata_req( client_ctx, be_msg->cfg_data_req->txn_id, - be_msg->cfg_data_req->batch_id, be_msg->cfg_data_req->data_req, be_msg->cfg_data_req->n_data_req, be_msg->cfg_data_req->end_of_data); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: - MGMTD_BE_CLIENT_DBG("Got CFG_APPLY_REQ txn-id: %" PRIu64, - be_msg->cfg_data_req->txn_id); + debug_be_client("Got CFG_APPLY_REQ txn-id: %" PRIu64, + be_msg->cfg_data_req->txn_id); mgmt_be_process_cfg_apply( client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id); break; - case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: - MGMTD_BE_CLIENT_ERR("Got unhandled message type %u", - be_msg->message_case); - /* - * TODO: Add handling code in future. - */ - break; /* * NOTE: The following messages are always sent from Backend * clients to MGMTd only and/or need not be handled here. */ case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: - case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: @@ -797,6 +829,167 @@ static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx, return 0; } +struct be_client_tree_data_batch_args { + struct mgmt_be_client *client; + uint64_t txn_id; + uint64_t req_id; + LYD_FORMAT result_type; +}; + +/* + * Process the get-tree request on our local oper state + */ +static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree, + void *arg, enum nb_error ret) +{ + struct be_client_tree_data_batch_args *args = arg; + struct mgmt_be_client *client = args->client; + struct mgmt_msg_tree_data *tree_msg = NULL; + bool more = false; + uint8_t **darrp; + LY_ERR err; + + if (ret == NB_YIELD) { + more = true; + ret = NB_OK; + } + if (ret != NB_OK) + goto done; + + tree_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0, + MTYPE_MSG_NATIVE_TREE_DATA); + tree_msg->refer_id = args->txn_id; + tree_msg->req_id = args->req_id; + tree_msg->code = MGMT_MSG_CODE_TREE_DATA; + tree_msg->result_type = args->result_type; + tree_msg->more = more; + + darrp = mgmt_msg_native_get_darrp(tree_msg); + err = yang_print_tree_append(darrp, tree, args->result_type, + (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT | + LYD_PRINT_WITHSIBLINGS)); + if (err) { + ret = NB_ERR; + goto done; + } + (void)be_client_send_native_msg(client, tree_msg, + mgmt_msg_native_get_msg_len(tree_msg), + false); +done: + mgmt_msg_native_free_msg(tree_msg); + if (ret) + be_client_send_error(client, args->txn_id, args->req_id, false, + -EINVAL, + "BE client %s txn-id %" PRIu64 + " error fetching oper state %d", + client->name, args->txn_id, ret); + if (ret != NB_OK || !more) + XFREE(MTYPE_MGMTD_BE_GT_CB_ARGS, args); + return ret; +} + +/* + * Process the get-tree request on our local oper state + */ +static void be_client_handle_get_tree(struct mgmt_be_client *client, + uint64_t txn_id, void *msgbuf, + size_t msg_len) +{ + struct mgmt_msg_get_tree *get_tree_msg = msgbuf; + struct be_client_tree_data_batch_args *args; + + debug_be_client("Received get-tree request for client %s txn-id %" PRIu64 + " req-id %" PRIu64, + client->name, txn_id, get_tree_msg->req_id); + + /* NOTE: removed the translator, if put back merge with northbound_cli + * code + */ + + args = XMALLOC(MTYPE_MGMTD_BE_GT_CB_ARGS, sizeof(*args)); + args->client = client; + args->txn_id = get_tree_msg->refer_id; + args->req_id = get_tree_msg->req_id; + args->result_type = get_tree_msg->result_type; + nb_oper_walk(get_tree_msg->xpath, NULL, 0, true, NULL, NULL, + be_client_send_tree_data_batch, args); +} + +/* + * Process the notification. + */ +static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf, + size_t msg_len) +{ + struct mgmt_msg_notify_data *notif_msg = msgbuf; + struct nb_node *nb_node; + struct lyd_node *dnode; + const char *data; + const char *notif; + LY_ERR err; + + debug_be_client("Received notification for client %s", client->name); + + notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data); + if (!notif || !data) { + log_err_be_client("Corrupt notify msg"); + return; + } + + nb_node = nb_node_find(notif); + if (!nb_node) { + log_err_be_client("No schema found for notification: %s", notif); + return; + } + + if (!nb_node->cbs.notify) { + debug_be_client("No notification callback for: %s", notif); + return; + } + + err = yang_parse_notification(notif, notif_msg->result_type, data, + &dnode); + if (err) { + log_err_be_client("Can't parse notification data for: %s", + notif); + return; + } + + nb_callback_notify(nb_node, notif, dnode); + + lyd_free_all(dnode); +} + +/* + * Handle a native encoded message + * + * We don't create transactions with native messaging. + */ +static void be_client_handle_native_msg(struct mgmt_be_client *client, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + uint64_t txn_id = msg->refer_id; + + switch (msg->code) { + case MGMT_MSG_CODE_GET_TREE: + be_client_handle_get_tree(client, txn_id, msg, msg_len); + break; + case MGMT_MSG_CODE_NOTIFY: + be_client_handle_notify(client, msg, msg_len); + break; + default: + log_err_be_client("unknown native message txn-id %" PRIu64 + " req-id %" PRIu64 " code %u to client %s", + txn_id, msg->req_id, msg->code, client->name); + be_client_send_error(client, msg->refer_id, msg->req_id, false, + -1, + "BE client %s recv msg unknown txn-id %" PRIu64, + client->name, txn_id); + break; + } +} + static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { @@ -807,43 +1000,53 @@ static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data, client = container_of(conn, struct msg_client, conn); client_ctx = container_of(client, struct mgmt_be_client, client); + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + + if (len >= sizeof(*msg)) + be_client_handle_native_msg(client_ctx, msg, len); + else + log_err_be_client("native message to client %s too short %zu", + client_ctx->name, len); + return; + } + be_msg = mgmtd__be_message__unpack(NULL, len, data); if (!be_msg) { - MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server", - len); + debug_be_client("Failed to decode %zu bytes from server", len); return; } - MGMTD_BE_CLIENT_DBG( - "Decoded %zu bytes of message(msg: %u/%u) from server", len, - be_msg->message_case, be_msg->message_case); + debug_be_client("Decoded %zu bytes of message(msg: %u/%u) from server", + len, be_msg->message_case, be_msg->message_case); (void)mgmt_be_client_handle_msg(client_ctx, be_msg); mgmtd__be_message__free_unpacked(be_msg, NULL); } int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx, - bool subscr_xpaths, int num_xpaths, - char **reg_xpaths) + int n_config_xpaths, char **config_xpaths, + int n_oper_xpaths, char **oper_xpaths) { Mgmtd__BeMessage be_msg; Mgmtd__BeSubscribeReq subscr_req; mgmtd__be_subscribe_req__init(&subscr_req); subscr_req.client_name = client_ctx->name; - subscr_req.n_xpath_reg = num_xpaths; - if (num_xpaths) - subscr_req.xpath_reg = reg_xpaths; - else - subscr_req.xpath_reg = NULL; - subscr_req.subscribe_xpaths = subscr_xpaths; + subscr_req.n_config_xpaths = n_config_xpaths; + subscr_req.config_xpaths = config_xpaths; + subscr_req.n_oper_xpaths = n_oper_xpaths; + subscr_req.oper_xpaths = oper_xpaths; + + /* See if we should register for notifications */ + subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths; + subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths; mgmtd__be_message__init(&be_msg); be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; be_msg.subscr_req = &subscr_req; - MGMTD_FE_CLIENT_DBG( - "Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu", - subscr_req.client_name, subscr_req.subscribe_xpaths, - subscr_req.n_xpath_reg); + debug_be_client("Sending SUBSCR_REQ name: %s xpaths: config %zu oper: %zu notif: %zu", + subscr_req.client_name, subscr_req.n_config_xpaths, + subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths); return mgmt_be_client_send_msg(client_ctx, &be_msg); } @@ -857,15 +1060,16 @@ static int _notify_conenct_disconnect(struct msg_client *msg_client, if (connected) { assert(msg_client->conn.fd != -1); - ret = mgmt_be_send_subscr_req(client, false, 0, NULL); + ret = mgmt_be_send_subscr_req(client, 0, NULL, 0, NULL); if (ret) return ret; } /* Notify BE client through registered callback (if any) */ if (client->cbs.client_connect_notify) - (void)(*client->cbs.client_connect_notify)( - client, client->user_data, connected); + (void)(*client->cbs.client_connect_notify)(client, + client->user_data, + connected); /* Cleanup any in-progress TXN on disconnect */ if (!connected) @@ -890,44 +1094,48 @@ static int mgmt_be_client_notify_disconenct(struct msg_conn *conn) * Debug Flags */ -DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd, - "[no] debug mgmt client backend", - NO_STR DEBUG_STR MGMTD_STR - "client\n" - "backend\n") +static void mgmt_debug_client_be_set(uint32_t flags, bool set) { - uint32_t mode = DEBUG_NODE2MODE(vty->node); + DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set); - DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no); + if (!__be_client) + return; - return CMD_SUCCESS; + __be_client->client.conn.debug = DEBUG_MODE_CHECK(&mgmt_dbg_be_client, + DEBUG_MODE_ALL); } -static void mgmt_debug_client_be_set_all(uint32_t flags, bool set) +DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd, + "[no] debug mgmt client backend", + NO_STR DEBUG_STR MGMTD_STR "client\n" + "backend\n") { - DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set); + mgmt_debug_client_be_set(DEBUG_NODE2MODE(vty->node), !no); + + return CMD_SUCCESS; } static int mgmt_debug_be_client_config_write(struct vty *vty) { if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF)) - vty_out(vty, "debug mgmt client frontend\n"); + vty_out(vty, "debug mgmt client backend\n"); return 1; } void mgmt_debug_be_client_show_debug(struct vty *vty) { - if (MGMTD_DBG_BE_CLIENT_CHECK()) + if (debug_check_be_client()) vty_out(vty, "debug mgmt client backend\n"); } static struct debug_callbacks mgmt_dbg_be_client_cbs = { - .debug_set_all = mgmt_debug_client_be_set_all}; + .debug_set_all = mgmt_debug_client_be_set +}; static struct cmd_node mgmt_dbg_node = { - .name = "mgmt backend client", - .node = DEBUG_NODE, + .name = "debug mgmt client backend", + .node = MGMT_BE_DEBUG_NODE, .prompt = "", .config_write = mgmt_debug_be_client_config_write, }; @@ -937,26 +1145,39 @@ struct mgmt_be_client *mgmt_be_client_create(const char *client_name, uintptr_t user_data, struct event_loop *event_loop) { - struct mgmt_be_client *client = - XCALLOC(MTYPE_MGMTD_BE_CLIENT, sizeof(*client)); + struct mgmt_be_client *client; + char server_path[MAXPATHLEN]; + + if (__be_client) + return NULL; + + client = XCALLOC(MTYPE_MGMTD_BE_CLIENT, sizeof(*client)); + __be_client = client; /* Only call after frr_init() */ assert(running_config); client->name = XSTRDUP(MTYPE_MGMTD_BE_CLIENT_NAME, client_name); client->running_config = running_config; - client->candidate_config = nb_config_new(NULL); + client->candidate_config = vty_shared_candidate_config; if (cbs) client->cbs = *cbs; mgmt_be_txns_init(&client->txn_head); - msg_client_init(&client->client, event_loop, MGMTD_BE_SERVER_PATH, + + snprintf(server_path, sizeof(server_path), MGMTD_BE_SOCK_NAME); + + msg_client_init(&client->client, event_loop, server_path, mgmt_be_client_notify_conenct, mgmt_be_client_notify_disconenct, mgmt_be_client_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, - MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, false, - "BE-client", MGMTD_DBG_BE_CLIENT_CHECK()); + MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MAX_MSG_LEN, false, + "BE-client", debug_check_be_client()); + + /* Hook to receive notifications */ + hook_register_arg(nb_notification_tree_send, mgmt_be_send_notification, + client); - MGMTD_BE_CLIENT_DBG("Initialized client '%s'", client_name); + debug_be_client("Initialized client '%s'", client_name); return client; } @@ -972,14 +1193,17 @@ void mgmt_be_client_lib_vty_init(void) void mgmt_be_client_destroy(struct mgmt_be_client *client) { - MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'", - client->name); + assert(client == __be_client); + debug_be_client("Destroying MGMTD Backend Client '%s'", client->name); + + nb_oper_cancel_all_walks(); msg_client_cleanup(&client->client); mgmt_be_cleanup_all_txns(client); mgmt_be_txns_fini(&client->txn_head); - nb_config_free(client->candidate_config); XFREE(MTYPE_MGMTD_BE_CLIENT_NAME, client->name); XFREE(MTYPE_MGMTD_BE_CLIENT, client); + + __be_client = NULL; } diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h index 4ad5ca5..cd8b237 100644 --- a/lib/mgmt_be_client.h +++ b/lib/mgmt_be_client.h @@ -14,52 +14,18 @@ extern "C" { #include "northbound.h" #include "mgmt_pb.h" -#include "mgmtd/mgmt_defines.h" - -/*************************************************************** - * Client IDs - ***************************************************************/ - -/* - * Add enum value for each supported component, wrap with - * #ifdef HAVE_COMPONENT - */ -enum mgmt_be_client_id { - MGMTD_BE_CLIENT_ID_MIN = 0, - MGMTD_BE_CLIENT_ID_INIT = -1, -#ifdef HAVE_STATICD - MGMTD_BE_CLIENT_ID_STATICD, -#endif - MGMTD_BE_CLIENT_ID_MAX -}; - -#define FOREACH_MGMTD_BE_CLIENT_ID(id) \ - for ((id) = MGMTD_BE_CLIENT_ID_MIN; \ - (id) < MGMTD_BE_CLIENT_ID_MAX; (id)++) +#include "mgmt_defines.h" /*************************************************************** * Constants ***************************************************************/ -#define MGMTD_BE_CLIENT_ERROR_STRING_MAX_LEN 32 - -#define MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC 5 - -#define MGMTD_BE_MSG_PROC_DELAY_USEC 10 -#define MGMTD_BE_MAX_NUM_MSG_PROC 500 - -#define MGMTD_BE_MSG_WRITE_DELAY_MSEC 1 +#define MGMTD_BE_MAX_NUM_MSG_PROC 500 #define MGMTD_BE_MAX_NUM_MSG_WRITE 1000 +#define MGMTD_BE_MAX_MSG_LEN (64 * 1024) -#define GMGD_BE_MAX_NUM_REQ_ITEMS 64 - -#define MGMTD_BE_MSG_MAX_LEN 16384 - -#define MGMTD_SOCKET_BE_SEND_BUF_SIZE 65535 -#define MGMTD_SOCKET_BE_RECV_BUF_SIZE MGMTD_SOCKET_BE_SEND_BUF_SIZE - -#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \ - ((10 * MGMTD_BE_MSG_MAX_LEN) / \ +#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \ + ((10 * MGMTD_BE_MAX_MSG_LEN) / \ (MGMTD_MAX_XPATH_LEN + MGMTD_MAX_YANG_VALUE_LEN)) /* @@ -68,11 +34,11 @@ enum mgmt_be_client_id { * that gets added to sent message */ #define MGMTD_BE_CFGDATA_PACKING_EFFICIENCY 0.8 -#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \ - (MGMTD_BE_MSG_MAX_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY) +#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \ + (MGMTD_BE_MAX_MSG_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY) -#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \ - (MGMTD_BE_MSG_MAX_LEN - 128) / sizeof(uint64_t) +#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \ + (MGMTD_BE_MAX_MSG_LEN - 128) / sizeof(uint64_t) #define MGMTD_BE_CONTAINER_NODE_VAL "<>" @@ -94,55 +60,39 @@ struct mgmt_be_client_txn_ctx { * Callbacks: * client_connect_notify: called when connection is made/lost to mgmtd. * txn_notify: called when a txn has been created + * notify_cbs: callbacks for notifications. + * nnotify_cbs: number of notification callbacks. + * */ struct mgmt_be_client_cbs { void (*client_connect_notify)(struct mgmt_be_client *client, uintptr_t usr_data, bool connected); - + void (*subscr_done)(struct mgmt_be_client *client, uintptr_t usr_data, + bool success); void (*txn_notify)(struct mgmt_be_client *client, uintptr_t usr_data, struct mgmt_be_client_txn_ctx *txn_ctx, bool destroyed); + + const char **notif_xpaths; + uint nnotif_xpaths; }; /*************************************************************** * Global data exported ***************************************************************/ -extern const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1]; - -static inline const char *mgmt_be_client_id2name(enum mgmt_be_client_id id) -{ - if (id > MGMTD_BE_CLIENT_ID_MAX) - id = MGMTD_BE_CLIENT_ID_MAX; - return mgmt_be_client_names[id]; -} - -static inline enum mgmt_be_client_id -mgmt_be_client_name2id(const char *name) -{ - enum mgmt_be_client_id id; - - FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (!strncmp(mgmt_be_client_names[id], name, - MGMTD_CLIENT_NAME_MAX_LEN)) - return id; - } - - return MGMTD_BE_CLIENT_ID_MAX; -} - extern struct debug mgmt_dbg_be_client; /*************************************************************** * API prototypes ***************************************************************/ -#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ +#define debug_be_client(fmt, ...) \ DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s: " fmt, __func__, \ ##__VA_ARGS__) -#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ +#define log_err_be_client(fmt, ...) \ zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_DBG_BE_CLIENT_CHECK() \ +#define debug_check_be_client() \ DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL) /** @@ -181,7 +131,7 @@ extern void mgmt_debug_be_client_show_debug(struct vty *vty); * The client object. * * reg_yang_xpaths - * Yang xpath(s) that needs to be [un]-subscribed from/to + * Yang xpath(s) that needs to be subscribed to * * num_xpaths * Number of xpaths @@ -189,9 +139,9 @@ extern void mgmt_debug_be_client_show_debug(struct vty *vty); * Returns: * MGMTD_SUCCESS on success, MGMTD_* otherwise. */ -extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client, - bool subscr_xpaths, int num_xpaths, - char **reg_xpaths); +extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx, + int n_config_xpaths, char **config_xpaths, + int n_oper_xpaths, char **oper_xpaths); /* * Destroy backend client and cleanup everything. diff --git a/lib/mgmt_defines.h b/lib/mgmt_defines.h new file mode 100644 index 0000000..b02341e --- /dev/null +++ b/lib/mgmt_defines.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD public defines. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar + */ + +#ifndef _FRR_MGMTD_DEFINES_H +#define _FRR_MGMTD_DEFINES_H + +#include "yang.h" + +#define MGMTD_FE_SOCK_NAME "%s/mgmtd_fe.sock", frr_runstatedir +#define MGMTD_BE_SOCK_NAME "%s/mgmtd_be.sock", frr_runstatedir + +#define MGMTD_CLIENT_NAME_MAX_LEN 32 + +#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN + +#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN + +#define MGMTD_MAX_NUM_XPATH_REG 128 + +#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32 +#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8 + +enum mgmt_result { + MGMTD_SUCCESS = 0, + MGMTD_INVALID_PARAM, + MGMTD_INTERNAL_ERROR, + MGMTD_NO_CFG_CHANGES, + MGMTD_DS_LOCK_FAILED, + MGMTD_DS_UNLOCK_FAILED, + MGMTD_UNKNOWN_FAILURE +}; + +#endif /* _FRR_MGMTD_DEFINES_H */ diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index 7e42e1c..a107582 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -12,6 +12,7 @@ #include "libfrr.h" #include "mgmt_fe_client.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "network.h" #include "stream.h" @@ -47,8 +48,12 @@ struct mgmt_fe_client { #define FOREACH_SESSION_IN_LIST(client, session) \ frr_each_safe (mgmt_sessions, &(client)->sessions, (session)) -struct debug mgmt_dbg_fe_client = {0, "Management frontend client operations"}; +struct debug mgmt_dbg_fe_client = { + .desc = "Management frontend client operations" +}; +/* NOTE: only one client per proc for now. */ +static struct mgmt_fe_client *__fe_client; static inline const char *dsid2name(Mgmtd__DatastoreId id) { @@ -74,14 +79,13 @@ mgmt_fe_find_session_by_client_id(struct mgmt_fe_client *client, FOREACH_SESSION_IN_LIST (client, session) { if (session->client_id == client_id) { - MGMTD_FE_CLIENT_DBG("Found session-id %" PRIu64 - " using client-id %" PRIu64, - session->session_id, client_id); + debug_fe_client("Found session-id %" PRIu64 + " using client-id %" PRIu64, + session->session_id, client_id); return session; } } - MGMTD_FE_CLIENT_DBG("Session not found using client-id %" PRIu64, - client_id); + debug_fe_client("Session not found using client-id %" PRIu64, client_id); return NULL; } @@ -93,15 +97,14 @@ mgmt_fe_find_session_by_session_id(struct mgmt_fe_client *client, FOREACH_SESSION_IN_LIST (client, session) { if (session->session_id == session_id) { - MGMTD_FE_CLIENT_DBG( - "Found session of client-id %" PRIu64 - " using session-id %" PRIu64, - session->client_id, session_id); + debug_fe_client("Found session of client-id %" PRIu64 + " using session-id %" PRIu64, + session->client_id, session_id); return session; } } - MGMTD_FE_CLIENT_DBG("Session not found using session-id %" PRIu64, - session_id); + debug_fe_client("Session not found using session-id %" PRIu64, + session_id); return NULL; } @@ -128,8 +131,7 @@ static int mgmt_fe_send_register_req(struct mgmt_fe_client *client) fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ; fe_msg.register_req = &rgstr_req; - MGMTD_FE_CLIENT_DBG( - "Sending REGISTER_REQ message to MGMTD Frontend server"); + debug_fe_client("Sending REGISTER_REQ message to MGMTD Frontend server"); return mgmt_fe_client_send_msg(client, &fe_msg, true); } @@ -155,9 +157,8 @@ static int mgmt_fe_send_session_req(struct mgmt_fe_client *client, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ; fe_msg.session_req = &sess_req; - MGMTD_FE_CLIENT_DBG( - "Sending SESSION_REQ %s message for client-id %" PRIu64, - create ? "create" : "destroy", session->client_id); + debug_fe_client("Sending SESSION_REQ %s message for client-id %" PRIu64, + create ? "create" : "destroy", session->client_id); return mgmt_fe_client_send_msg(client, &fe_msg, true); } @@ -180,9 +181,8 @@ int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client, uint64_t session_id, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ; fe_msg.lockds_req = &lockds_req; - MGMTD_FE_CLIENT_DBG( - "Sending LOCKDS_REQ (%sLOCK) message for DS:%s session-id %" PRIu64, - lock ? "" : "UN", dsid2name(ds_id), session_id); + debug_fe_client("Sending LOCKDS_REQ (%sLOCK) message for DS:%s session-id %" PRIu64, + lock ? "" : "UN", dsid2name(ds_id), session_id); return mgmt_fe_client_send_msg(client, &fe_msg, scok); @@ -210,10 +210,9 @@ int mgmt_fe_send_setcfg_req(struct mgmt_fe_client *client, uint64_t session_id, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ; fe_msg.setcfg_req = &setcfg_req; - MGMTD_FE_CLIENT_DBG( - "Sending SET_CONFIG_REQ message for DS:%s session-id %" PRIu64 - " (#xpaths:%d)", - dsid2name(ds_id), session_id, num_data_reqs); + debug_fe_client("Sending SET_CONFIG_REQ message for DS:%s session-id %" PRIu64 + " (#xpaths:%d)", + dsid2name(ds_id), session_id, num_data_reqs); return mgmt_fe_client_send_msg(client, &fe_msg, false); } @@ -240,9 +239,8 @@ int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client *client, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ; fe_msg.commcfg_req = &commitcfg_req; - MGMTD_FE_CLIENT_DBG( - "Sending COMMIT_CONFIG_REQ message for Src-DS:%s, Dst-DS:%s session-id %" PRIu64, - dsid2name(src_ds_id), dsid2name(dest_ds_id), session_id); + debug_fe_client("Sending COMMIT_CONFIG_REQ message for Src-DS:%s, Dst-DS:%s session-id %" PRIu64, + dsid2name(src_ds_id), dsid2name(dest_ds_id), session_id); return mgmt_fe_client_send_msg(client, &fe_msg, false); } @@ -268,10 +266,9 @@ int mgmt_fe_send_get_req(struct mgmt_fe_client *client, uint64_t session_id, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REQ; fe_msg.get_req = &getcfg_req; - MGMTD_FE_CLIENT_DBG("Sending GET_REQ (iscfg %d) message for DS:%s session-id %" PRIu64 - " (#xpaths:%d)", - is_config, dsid2name(ds_id), session_id, - num_data_reqs); + debug_fe_client("Sending GET_REQ (iscfg %d) message for DS:%s session-id %" PRIu64 + " (#xpaths:%d)", + is_config, dsid2name(ds_id), session_id, num_data_reqs); return mgmt_fe_client_send_msg(client, &fe_msg, false); } @@ -300,6 +297,39 @@ int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, return mgmt_fe_client_send_msg(client, &fe_msg, false); } +/* + * Send get-data request. + */ +int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + uint8_t datastore, LYD_FORMAT result_type, + uint8_t flags, uint8_t defaults, const char *xpath) +{ + struct mgmt_msg_get_data *msg; + size_t xplen = strlen(xpath); + int ret; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_data, xplen + 1, + MTYPE_MSG_NATIVE_GET_DATA); + msg->refer_id = session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_GET_DATA; + msg->result_type = result_type; + msg->flags = flags; + msg->defaults = defaults; + msg->datastore = datastore; + strlcpy(msg->xpath, xpath, xplen + 1); + + debug_fe_client("Sending GET_DATA_REQ session-id %" PRIu64 + " req-id %" PRIu64 " xpath: %s", + session_id, req_id, xpath); + + ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false); + mgmt_msg_native_free_msg(msg); + return ret; +} + + static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, Mgmtd__FeMessage *fe_msg) { @@ -313,30 +343,28 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY: if (fe_msg->session_reply->create && fe_msg->session_reply->has_client_conn_id) { - MGMTD_FE_CLIENT_DBG( - "Got SESSION_REPLY (create) for client-id %" PRIu64 - " with session-id: %" PRIu64, - fe_msg->session_reply->client_conn_id, - fe_msg->session_reply->session_id); + debug_fe_client("Got SESSION_REPLY (create) for client-id %" PRIu64 + " with session-id: %" PRIu64, + fe_msg->session_reply->client_conn_id, + fe_msg->session_reply->session_id); session = mgmt_fe_find_session_by_client_id( client, fe_msg->session_reply->client_conn_id); if (session && fe_msg->session_reply->success) { - MGMTD_FE_CLIENT_DBG( - "Session Created for client-id %" PRIu64, - fe_msg->session_reply->client_conn_id); + debug_fe_client("Session Created for client-id %" PRIu64, + fe_msg->session_reply + ->client_conn_id); session->session_id = fe_msg->session_reply->session_id; } else { - MGMTD_FE_CLIENT_ERR( + log_err_fe_client( "Session Create failed for client-id %" PRIu64, fe_msg->session_reply->client_conn_id); } } else if (!fe_msg->session_reply->create) { - MGMTD_FE_CLIENT_DBG( - "Got SESSION_REPLY (destroy) for session-id %" PRIu64, - fe_msg->session_reply->session_id); + debug_fe_client("Got SESSION_REPLY (destroy) for session-id %" PRIu64, + fe_msg->session_reply->session_id); session = mgmt_fe_find_session_by_session_id( client, fe_msg->session_req->session_id); @@ -353,8 +381,8 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, session->user_ctx); break; case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY: - MGMTD_FE_CLIENT_DBG("Got LOCKDS_REPLY for session-id %" PRIu64, - fe_msg->lockds_reply->session_id); + debug_fe_client("Got LOCKDS_REPLY for session-id %" PRIu64, + fe_msg->lockds_reply->session_id); session = mgmt_fe_find_session_by_session_id( client, fe_msg->lockds_reply->session_id); @@ -370,8 +398,8 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, fe_msg->lockds_reply->error_if_any); break; case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY: - MGMTD_FE_CLIENT_DBG("Got SETCFG_REPLY for session-id %" PRIu64, - fe_msg->setcfg_reply->session_id); + debug_fe_client("Got SETCFG_REPLY for session-id %" PRIu64, + fe_msg->setcfg_reply->session_id); session = mgmt_fe_find_session_by_session_id( client, fe_msg->setcfg_reply->session_id); @@ -388,8 +416,8 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, fe_msg->setcfg_reply->error_if_any); break; case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY: - MGMTD_FE_CLIENT_DBG("Got COMMCFG_REPLY for session-id %" PRIu64, - fe_msg->commcfg_reply->session_id); + debug_fe_client("Got COMMCFG_REPLY for session-id %" PRIu64, + fe_msg->commcfg_reply->session_id); session = mgmt_fe_find_session_by_session_id( client, fe_msg->commcfg_reply->session_id); @@ -408,8 +436,8 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, fe_msg->commcfg_reply->error_if_any); break; case MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY: - MGMTD_FE_CLIENT_DBG("Got GET_REPLY for session-id %" PRIu64, - fe_msg->get_reply->session_id); + debug_fe_client("Got GET_REPLY for session-id %" PRIu64, + fe_msg->get_reply->session_id); session = mgmt_fe_find_session_by_session_id(client, @@ -465,6 +493,110 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, return 0; } +/* + * Handle a native encoded message + */ +static void fe_client_handle_native_msg(struct mgmt_fe_client *client, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_fe_client_session *session = NULL; + struct mgmt_msg_notify_data *notify_msg; + struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_error *err_msg; + const char *data = NULL; + size_t dlen; + + debug_fe_client("Got native message for session-id %" PRIu64, + msg->refer_id); + + session = mgmt_fe_find_session_by_session_id(client, msg->refer_id); + if (!session || !session->client) { + log_err_fe_client("No session for received native msg session-id %" PRIu64, + msg->refer_id); + return; + } + + switch (msg->code) { + case MGMT_MSG_CODE_ERROR: + if (!session->client->cbs.error_notify) + return; + + err_msg = (typeof(err_msg))msg; + if (!MGMT_MSG_VALIDATE_NUL_TERM(err_msg, msg_len)) { + log_err_fe_client("Corrupt error msg recv"); + return; + } + session->client->cbs.error_notify(client, client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, + msg->req_id, err_msg->error, + err_msg->errstr); + break; + case MGMT_MSG_CODE_TREE_DATA: + if (!session->client->cbs.get_tree_notify) + return; + + tree_msg = (typeof(tree_msg))msg; + if (msg_len < sizeof(*tree_msg)) { + log_err_fe_client("Corrupt tree-data msg recv"); + return; + } + session->client->cbs.get_tree_notify(client, client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, + msg->req_id, + MGMTD_DS_OPERATIONAL, + tree_msg->result_type, + tree_msg->result, + msg_len - sizeof(*tree_msg), + tree_msg->partial_error); + break; + case MGMT_MSG_CODE_NOTIFY: + if (!session->client->cbs.async_notification) + return; + + notify_msg = (typeof(notify_msg))msg; + if (msg_len < sizeof(*notify_msg)) { + log_err_fe_client("Corrupt notify-data msg recv"); + return; + } + + data = mgmt_msg_native_data_decode(notify_msg, msg_len); + if (!data) { + log_err_fe_client("Corrupt error msg recv"); + return; + } + dlen = mgmt_msg_native_data_len_decode(notify_msg, msg_len); + if (notify_msg->result_type != LYD_JSON) + data = yang_convert_lyd_format(data, dlen, + notify_msg->result_type, + LYD_JSON, true); + if (!data) { + log_err_fe_client("Can't convert format %d to JSON", + notify_msg->result_type); + return; + } + + session->client->cbs.async_notification(client, + client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, data); + + if (notify_msg->result_type != LYD_JSON) + darr_free(data); + break; + default: + log_err_fe_client("unknown native message session-id %" PRIu64 + " req-id %" PRIu64 " code %u", + msg->refer_id, msg->req_id, msg->code); + break; + } +} + static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { @@ -475,15 +607,24 @@ static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data, msg_client = container_of(conn, struct msg_client, conn); client = container_of(msg_client, struct mgmt_fe_client, client); + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + + if (len >= sizeof(*msg)) + fe_client_handle_native_msg(client, msg, len); + else + log_err_fe_client("native message to FE client %s too short %zu", + client->name, len); + return; + } + fe_msg = mgmtd__fe_message__unpack(NULL, len, data); if (!fe_msg) { - MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.", - len); + debug_fe_client("Failed to decode %zu bytes from server.", len); return; } - MGMTD_FE_CLIENT_DBG( - "Decoded %zu bytes of message(msg: %u/%u) from server", len, - fe_msg->message_case, fe_msg->message_case); + debug_fe_client("Decoded %zu bytes of message(msg: %u/%u) from server", + len, fe_msg->message_case, fe_msg->message_case); (void)mgmt_fe_client_handle_msg(client, fe_msg); mgmtd__fe_message__free_unpacked(fe_msg, NULL); } @@ -504,7 +645,7 @@ static int _notify_connect_disconnect(struct msg_client *msg_client, /* Walk list of sessions for this FE client deleting them */ if (!connected && mgmt_sessions_count(&client->sessions)) { - MGMTD_FE_CLIENT_DBG("Cleaning up existing sessions"); + debug_fe_client("Cleaning up existing sessions"); FOREACH_SESSION_IN_LIST (client, session) { assert(session->client); @@ -543,6 +684,16 @@ static int mgmt_fe_client_notify_disconnect(struct msg_conn *conn) return _notify_connect_disconnect(client, false); } +static void mgmt_debug_client_fe_set(uint32_t mode, bool set) +{ + DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, mode, set); + + if (!__fe_client) + return; + + __fe_client->client.conn.debug = DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, + DEBUG_MODE_ALL); +} DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd, "[no] debug mgmt client frontend", @@ -550,18 +701,11 @@ DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd, "client\n" "frontend\n") { - uint32_t mode = DEBUG_NODE2MODE(vty->node); - - DEBUG_MODE_SET(&mgmt_dbg_fe_client, mode, !no); + mgmt_debug_client_fe_set(DEBUG_NODE2MODE(vty->node), !no); return CMD_SUCCESS; } -static void mgmt_debug_client_fe_set_all(uint32_t flags, bool set) -{ - DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, flags, set); -} - static int mgmt_debug_fe_client_config_write(struct vty *vty) { if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF)) @@ -572,16 +716,17 @@ static int mgmt_debug_fe_client_config_write(struct vty *vty) void mgmt_debug_fe_client_show_debug(struct vty *vty) { - if (MGMTD_DBG_FE_CLIENT_CHECK()) + if (debug_check_fe_client()) vty_out(vty, "debug mgmt client frontend\n"); } static struct debug_callbacks mgmt_dbg_fe_client_cbs = { - .debug_set_all = mgmt_debug_client_fe_set_all}; + .debug_set_all = mgmt_debug_client_fe_set +}; static struct cmd_node mgmt_dbg_node = { - .name = "mgmt client frontend", - .node = DEBUG_NODE, + .name = "debug mgmt client frontend", + .node = MGMT_FE_DEBUG_NODE, .prompt = "", .config_write = mgmt_debug_fe_client_config_write, }; @@ -594,8 +739,14 @@ struct mgmt_fe_client *mgmt_fe_client_create(const char *client_name, uintptr_t user_data, struct event_loop *event_loop) { - struct mgmt_fe_client *client = - XCALLOC(MTYPE_MGMTD_FE_CLIENT, sizeof(*client)); + struct mgmt_fe_client *client; + char server_path[MAXPATHLEN]; + + if (__fe_client) + return NULL; + + client = XCALLOC(MTYPE_MGMTD_FE_CLIENT, sizeof(*client)); + __fe_client = client; client->name = XSTRDUP(MTYPE_MGMTD_FE_CLIENT_NAME, client_name); client->user_data = user_data; @@ -604,14 +755,16 @@ struct mgmt_fe_client *mgmt_fe_client_create(const char *client_name, mgmt_sessions_init(&client->sessions); - msg_client_init(&client->client, event_loop, MGMTD_FE_SERVER_PATH, + snprintf(server_path, sizeof(server_path), MGMTD_FE_SOCK_NAME); + + msg_client_init(&client->client, event_loop, server_path, mgmt_fe_client_notify_connect, mgmt_fe_client_notify_disconnect, mgmt_fe_client_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC, - MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, true, - "FE-client", MGMTD_DBG_FE_CLIENT_CHECK()); + MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MAX_MSG_LEN, true, + "FE-client", debug_check_fe_client()); - MGMTD_FE_CLIENT_DBG("Initialized client '%s'", client_name); + debug_fe_client("Initialized client '%s'", client_name); return client; } @@ -634,6 +787,11 @@ bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client) return client->client.conn.is_short_circuit; } +const char *mgmt_fe_client_name(struct mgmt_fe_client *client) +{ + return client->name; +} + /* * Create a new Session for a Frontend Client connection. */ @@ -675,9 +833,8 @@ enum mgmt_result mgmt_fe_destroy_client_session(struct mgmt_fe_client *client, if (session->session_id && mgmt_fe_send_session_req(client, session, false) != 0) - MGMTD_FE_CLIENT_ERR( - "Failed to send session destroy request for the session-id %" PRIu64, - session->session_id); + log_err_fe_client("Failed to send session destroy request for the session-id %" PRIu64, + session->session_id); mgmt_sessions_del(&client->sessions, session); XFREE(MTYPE_MGMTD_FE_SESSION, session); @@ -692,8 +849,9 @@ void mgmt_fe_client_destroy(struct mgmt_fe_client *client) { struct mgmt_fe_client_session *session; - MGMTD_FE_CLIENT_DBG("Destroying MGMTD Frontend Client '%s'", - client->name); + assert(client == __fe_client); + + debug_fe_client("Destroying MGMTD Frontend Client '%s'", client->name); FOREACH_SESSION_IN_LIST (client, session) mgmt_fe_destroy_client_session(client, session->client_id); @@ -702,4 +860,6 @@ void mgmt_fe_client_destroy(struct mgmt_fe_client *client) XFREE(MTYPE_MGMTD_FE_CLIENT_NAME, client->name); XFREE(MTYPE_MGMTD_FE_CLIENT, client); + + __fe_client = NULL; } diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h index 349b7e4..eee4594 100644 --- a/lib/mgmt_fe_client.h +++ b/lib/mgmt_fe_client.h @@ -14,7 +14,8 @@ extern "C" { #include "mgmt_pb.h" #include "frrevent.h" -#include "mgmtd/mgmt_defines.h" +#include "mgmt_defines.h" +#include "mgmt_msg_native.h" /*************************************************************** * Macros @@ -25,22 +26,11 @@ extern "C" { * connections. */ -#define MGMTD_FE_CLIENT_ERROR_STRING_MAX_LEN 32 - -#define MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC 5 - #define MGMTD_FE_MSG_PROC_DELAY_USEC 10 -#define MGMTD_FE_MAX_NUM_MSG_PROC 500 -#define MGMTD_FE_MSG_WRITE_DELAY_MSEC 1 +#define MGMTD_FE_MAX_NUM_MSG_PROC 500 #define MGMTD_FE_MAX_NUM_MSG_WRITE 100 - -#define GMGD_FE_MAX_NUM_REQ_ITEMS 64 - -#define MGMTD_FE_MSG_MAX_LEN 9000 - -#define MGMTD_SOCKET_FE_SEND_BUF_SIZE 65535 -#define MGMTD_SOCKET_FE_RECV_BUF_SIZE MGMTD_SOCKET_FE_SEND_BUF_SIZE +#define MGMTD_FE_MAX_MSG_LEN (64 * 1024) /*************************************************************** * Data-structures @@ -115,6 +105,26 @@ struct mgmt_fe_client_cbs { uintptr_t user_data, uint64_t req_id, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data, size_t num_data); + + /* Called when get-tree result is returned */ + int (*get_tree_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uintptr_t session_ctx, + uint64_t req_id, Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, void *result, size_t len, + int partial_error); + + /* Called with asynchronous notifications from backends */ + int (*async_notification)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uintptr_t session_ctx, + const char *result); + + /* Called when new native error is returned */ + int (*error_notify)(struct mgmt_fe_client *client, uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, int error, + const char *errstr); }; extern struct debug mgmt_dbg_fe_client; @@ -123,12 +133,12 @@ extern struct debug mgmt_dbg_fe_client; * API prototypes ***************************************************************/ -#define MGMTD_FE_CLIENT_DBG(fmt, ...) \ +#define debug_fe_client(fmt, ...) \ DEBUGD(&mgmt_dbg_fe_client, "FE-CLIENT: %s: " fmt, __func__, \ ##__VA_ARGS__) -#define MGMTD_FE_CLIENT_ERR(fmt, ...) \ +#define log_err_fe_client(fmt, ...) \ zlog_err("FE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_DBG_FE_CLIENT_CHECK() \ +#define debug_check_fe_client() \ DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_ALL) /* @@ -363,6 +373,42 @@ extern int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, Mgmtd__YangDataXPath **data_req, int num_reqs); +/* + * Send GET-DATA to MGMTD daemon. + * + * client + * Client object. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * datastore + * Datastore for getting data. + * + * result_type + * The LYD_FORMAT of the result. + * + * flags + * Flags to control the behavior of the request. + * + * defaults + * Options to control the reporting of default values. + * + * xpath + * the xpath to get. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + uint8_t datastore, LYD_FORMAT result_type, + uint8_t flags, uint8_t defaults, + const char *xpath); + /* * Destroy library and cleanup everything. */ @@ -379,6 +425,17 @@ extern uint mgmt_fe_client_session_count(struct mgmt_fe_client *client); extern bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client); +/** + * Get the name of the client + * + * Args: + * The client object. + * + * Return: + * The name of the client. + */ +extern const char *mgmt_fe_client_name(struct mgmt_fe_client *client); + #ifdef __cplusplus } #endif diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c index 1cd3240..aff9af7 100644 --- a/lib/mgmt_msg.c +++ b/lib/mgmt_msg.c @@ -7,12 +7,15 @@ * Copyright (c) 2023, LabN Consulting, L.L.C. */ #include +#include + #include "debug.h" #include "network.h" #include "sockopt.h" #include "stream.h" #include "frrevent.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #define MGMT_MSG_DBG(dbgtag, fmt, ...) \ @@ -84,7 +87,7 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd, */ assert(stream_get_getp(ms->ins) == 0); left = stream_get_endp(ms->ins); - while (left > (long)sizeof(struct mgmt_msg_hdr)) { + while (left > (ssize_t)sizeof(struct mgmt_msg_hdr)) { mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total); if (!MGMT_MSG_IS_MARKER(mhdr->marker)) { MGMT_MSG_DBG(dbgtag, "recv corrupt buffer, disconnect"); @@ -99,8 +102,30 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd, mcount++; } - if (!mcount) + if (!mcount) { + /* Didn't manage to read a full message */ + if (mhdr && avail == 0) { + struct stream *news; + /* + * Message was longer than what was left and we have no + * available space to read more in. B/c mcount == 0 the + * message starts at the beginning of the stream so + * therefor the stream is too small to fit the message.. + * Resize the stream to fit. + */ + if (mhdr->len > MGMT_MSG_MAX_MSG_ALLOC_LEN) { + MGMT_MSG_ERR(ms, "corrupt msg len rcvd %u", + mhdr->len); + return MSR_DISCONNECT; + } + news = stream_new(mhdr->len); + stream_put(news, mhdr, left); + stream_set_endp(news, left); + stream_free(ms->ins); + ms->ins = news; + } return MSR_SCHED_STREAM; + } /* * We have read at least one message into the stream, queue it up. @@ -108,7 +133,11 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd, mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total); stream_set_endp(ms->ins, total); stream_fifo_push(&ms->inq, ms->ins); - ms->ins = stream_new(ms->max_msg_sz); + if (left < (ssize_t)sizeof(struct mgmt_msg_hdr)) + ms->ins = stream_new(ms->max_msg_sz); + else + /* handle case where message is greater than max */ + ms->ins = stream_new(MAX(ms->max_msg_sz, mhdr->len)); if (left) { stream_put(ms->ins, mhdr, left); stream_set_endp(ms->ins, left); @@ -180,7 +209,7 @@ bool mgmt_msg_procbufs(struct mgmt_msg_state *ms, } /** - * Write data from a onto the socket, using streams that have been queued for + * Write data onto the socket, using streams that have been queued for * sending by mgmt_msg_send_msg. This function should be reschedulable. * * Args: @@ -292,23 +321,26 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg, size_t endp, n; size_t mlen = len + sizeof(*mhdr); - if (mlen > ms->max_msg_sz) { - MGMT_MSG_ERR(ms, "Message %zu > max size %zu, dropping", mlen, - ms->max_msg_sz); - return -1; - } + if (mlen > ms->max_msg_sz) + MGMT_MSG_DBG(dbgtag, "Sending large msg size %zu > max size %zu", + mlen, ms->max_msg_sz); if (!ms->outs) { - MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu", - len); - ms->outs = stream_new(ms->max_msg_sz); + MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu", mlen); + ms->outs = stream_new(MAX(ms->max_msg_sz, mlen)); + } else if (mlen > ms->max_msg_sz && ms->outs->endp == 0) { + /* msg is larger than stream max size get a fit-to-size stream */ + MGMT_MSG_DBG(dbgtag, + "replacing old stream with fit-to-size for msg len %zu", + mlen); + stream_free(ms->outs); + ms->outs = stream_new(mlen); } else if (STREAM_WRITEABLE(ms->outs) < mlen) { - MGMT_MSG_DBG( - dbgtag, - "enq existing stream len %zu and creating new stream for msg len %zu", - STREAM_WRITEABLE(ms->outs), mlen); + MGMT_MSG_DBG(dbgtag, + "enq existing stream len %zu and creating new stream for msg len %zu", + STREAM_WRITEABLE(ms->outs), mlen); stream_fifo_push(&ms->outq, ms->outs); - ms->outs = stream_new(ms->max_msg_sz); + ms->outs = stream_new(MAX(ms->max_msg_sz, mlen)); } else { MGMT_MSG_DBG( dbgtag, @@ -317,6 +349,16 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg, } s = ms->outs; + if (dbgtag && version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *native_msg = msg; + + MGMT_MSG_DBG( + dbgtag, + "Sending native msg sess/txn-id %"PRIu64" req-id %"PRIu64" code %u", + native_msg->refer_id, native_msg->req_id, native_msg->code); + + } + /* We have a stream with space, pack the message into it. */ mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(s) + s->endp); mhdr->marker = MGMT_MSG_MARKER(version); @@ -636,13 +678,13 @@ static void msg_client_sched_connect(struct msg_client *client, &client->conn_retry_tmr); } -static bool msg_client_connect_short_circuit(struct msg_client *client) +static int msg_client_connect_short_circuit(struct msg_client *client) { struct msg_conn *server_conn; struct msg_server *server; const char *dbgtag = client->conn.debug ? client->conn.mstate.idtag : NULL; - union sockunion su = {0}; + union sockunion su = {}; int sockets[2]; frr_each (msg_server_list, &msg_servers, server) @@ -650,10 +692,9 @@ static bool msg_client_connect_short_circuit(struct msg_client *client) break; if (!server) { MGMT_MSG_DBG(dbgtag, - "no short-circuit connection available for %s", + "no short-circuit server available yet for %s", client->sopath); - - return false; + return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { @@ -661,7 +702,7 @@ static bool msg_client_connect_short_circuit(struct msg_client *client) &client->conn.mstate, "socketpair failed trying to short-circuit connection on %s: %s", client->sopath, safe_strerror(errno)); - return false; + return -1; } /* client side */ @@ -673,6 +714,9 @@ static bool msg_client_connect_short_circuit(struct msg_client *client) /* server side */ memset(&su, 0, sizeof(union sockunion)); server_conn = server->create(sockets[1], &su); + server_conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL) + ? true + : false; client->conn.remote_conn = server_conn; server_conn->remote_conn = &client->conn; @@ -689,7 +733,7 @@ static bool msg_client_connect_short_circuit(struct msg_client *client) client->sopath, client->conn.mstate.idtag, client->conn.fd, server_conn->mstate.idtag, server_conn->fd); - return true; + return 0; } @@ -699,11 +743,12 @@ static void msg_client_connect(struct msg_client *client) struct msg_conn *conn = &client->conn; const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL; - if (!client->short_circuit_ok || - !msg_client_connect_short_circuit(client)) + if (!client->short_circuit_ok) conn->fd = mgmt_msg_connect(client->sopath, MSG_CONN_SEND_BUF_SIZE, MSG_CONN_RECV_BUF_SIZE, dbgtag); + else if (msg_client_connect_short_circuit(client)) + conn->fd = -1; if (conn->fd == -1) /* retry the connection */ @@ -743,7 +788,6 @@ void msg_client_init(struct msg_client *client, struct event_loop *tm, mgmt_msg_init(&conn->mstate, max_read_buf, max_write_buf, max_msg_sz, idtag); - /* XXX maybe just have client kick this off */ /* Start trying to connect to server */ msg_client_sched_connect(client, 0); } @@ -766,8 +810,9 @@ void msg_client_cleanup(struct msg_client *client) static void msg_server_accept(struct event *event) { struct msg_server *server = EVENT_ARG(event); - int fd; + struct msg_conn *conn; union sockunion su; + int fd; if (server->fd < 0) return; @@ -790,7 +835,11 @@ static void msg_server_accept(struct event *event) DEBUGD(server->debug, "Accepted new %s connection", server->idtag); - server->create(fd, &su); + conn = server->create(fd, &su); + if (conn) + conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL) + ? true + : false; } int msg_server_init(struct msg_server *server, const char *sopath, diff --git a/lib/mgmt_msg.h b/lib/mgmt_msg.h index dd7ae59..6bdc9a6 100644 --- a/lib/mgmt_msg.h +++ b/lib/mgmt_msg.h @@ -24,6 +24,8 @@ DECLARE_MTYPE(MSG_CONN); #define MGMT_MSG_VERSION_PROTOBUF 0 #define MGMT_MSG_VERSION_NATIVE 1 +/* The absolute maximum message size (16MB) */ +#define MGMT_MSG_MAX_MSG_ALLOC_LEN (16 * 1024 * 1024) struct mgmt_msg_state { struct stream *ins; @@ -136,6 +138,10 @@ struct msg_client { extern void msg_client_cleanup(struct msg_client *client); /* + * If `short_circuit_ok` is true, then the client-server connection will use a + * socketpair() rather than a unix-domain socket. This must be passed true if + * you wish to send messages short-circuit later. + * * `notify_disconnect` is not called when the user `msg_client_cleanup` is * called for a client which is currently connected. The socket is closed * but there is no notification. diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c new file mode 100644 index 0000000..d27c5d3 --- /dev/null +++ b/lib/mgmt_msg_native.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * June 29 2023, Christian Hopps + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ +#include +#include "mgmt_msg_native.h" + +DEFINE_MGROUP(MSG_NATIVE, "Native message allocations"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_MSG, "native mgmt msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_ERROR, "native error msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg"); + +int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, va_list ap) +{ + struct mgmt_msg_error *msg; + char *errstr; + ssize_t slen; + int ret; + + errstr = darr_vsprintf(errfmt, ap); + slen = strlen(errstr); + + msg = mgmt_msg_native_alloc_msg(typeof(*msg), slen + 1, + MTYPE_MSG_NATIVE_ERROR); + msg->refer_id = sess_or_txn_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_ERROR; + msg->error = error; + strlcpy(msg->errstr, errstr, slen + 1); + darr_free(errstr); + + if (conn->debug) + zlog_debug("Sending error %d session-id %" PRIu64 + " req-id %" PRIu64 " scok %d errstr: %s", + error, sess_or_txn_id, req_id, short_circuit_ok, + msg->errstr); + + ret = mgmt_msg_native_send_msg(conn, msg, short_circuit_ok); + mgmt_msg_native_free_msg(msg); + return ret; +} diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h new file mode 100644 index 0000000..53bb81b --- /dev/null +++ b/lib/mgmt_msg_native.h @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * June 29 2023, Christian Hopps + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ + +#ifndef _FRR_MGMT_MSG_NATIVE_H_ +#define _FRR_MGMT_MSG_NATIVE_H_ + +#ifdef __cplusplus +extern "C" { +#elif 0 +} +#endif + +#include +#include "compiler.h" +#include "darr.h" +#include "memory.h" +#include "mgmt_msg.h" +#include "mgmt_defines.h" + +#include + +/* + * ================== + * Native Message API + * ================== + * + * ----------------------- + * Defining A New Message: + * ----------------------- + * + * 1) Start with `struct mgmt_msg_header` as the first (unnamed) field. + * + * 2) Add fixed-width fields. Add on natural aligned boundaries (*) + * + * 3) [Optional] Add a zero-length variable field. Add aligned on a 64-bit + * boundary, this is done so that: `value = (HDR + 1)` works. + * + * 4) Define a new MTYPE for the new message type (see DECLARE_MTYPE below + * as well as the paired DEFINE_MTYPE in mgmt_msg_native.c) + * + * These rules are so the messages may be read from and written directly to + * "the wire", easily, using common programming languages (e.g., C, rust, go, + * python, ...) + * + * (*) Natrual aligned boundaries, i.e., uint16_t on 2-byte boundary, uint64_t + * on 8-byte boundaries, ...) + * + * ------------------------------ + * Allocating New Native Messages + * ------------------------------ + * + * For fixed-length and variable length messages one should allocate new + * messages with the mgmt_msg_native_alloc_msg() passing in the newly defined + * MTYPE. Likewise, to free the message one should use + * mgmt_msg_native_free_msg(). + * + * Unknown Variable Length Messages: + * --------------------------------- + * + * If using a zero-length variable length field and the length is not known at + * message creation time, you can use the `native` API function + * mgmt_msg_native_append() to add data to the end of the message, or if a more + * full set of operations are required, the darr_xxxx() API is also available as + * in the Advanced section below. + * + * Notable API Functions: + * --------------------------------- + * + * mgmt_msg_native_alloc_msg() - Allocate a native msg. + * mgmt_msg_native_free_msg() - Free a native msg. + * mgmt_msg_native_append() - Append data to the end of the msg. + * mgmt_msg_native_get_msg_len() - Get the total length of the msg. + * mgmt_msg_native_send_msg() - Send the message. + * + * mgmt_msg_native_xpath_encode() - Encode xpath in xpath, data format message. + * mgmt_msg_native_xpath_data_decode() - Decode xpath, data format message. + * mgmt_msg_native_xpath_decode() - Get the xpath, from xpath, data format message. + * mgmt_msg_native_data_decode() - Get the secondary data from xpath, data message. + * mgmt_msg_native_data_len_decode() - Get length of secondary data. + * + * ------------------------------------- + * [Advanced Use] Dynamic Array Messages + * ------------------------------------- + * + * NOTE: Most users can simply use mgmt_msg_native_append() and skip this + * section. + * + * This section is only important to understand if you wish to utilize the fact + * that native messages allocated with mgmt_msg_native_alloc_msg are + * actually allocated as uint8_t dynamic arrays (`darr`). + * + * You can utilize all the darr_xxxx() API to manipulate the variable length + * message data in a native message. To do so you simply need to understand that + * the native message is actually a `uint8_t *` darr. So, for example, to append + * data to the end of a message one could do the following: + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * msg = (struct mggm_msg_my_msg *) + * darr_strcat((uint8_t *)msg, "/metric"); + * + * // ... + * } + * + * NOTE: If reallocs happen the original passed in pointer will be updated; + * however, any other pointers into the message will become invalid, and so they + * should always be discarded or reinitialized after using any reallocating + * darr_xxx() API functions. + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * char *xpath = msg->xpath; // pointer into message + * + * darr_in_strcat((uint8_t *)msg, "/metric"); + * // msg may have been updated to point at new memory + * + * xpath = NULL; // now invalid + * xpath = msg->xpath; // reinitialize + * // ... + * } + * + * Rather than worry about this, it's typical when using dynamic arrays to always + * work from the main pointer to the dynamic array, rather than caching multiple + * pointers into the data. Modern compilers will optimize the code so that it + * adds no extra execution cost. + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * darr_in_strcat((uint8_t *)msg, "/metric"); + * + * // Use `msg->xpath` directly rather creating and using an + * // `xpath = msg->xpath` local variable. + * + * if (strcmp(msg->xpath, "foobar/metric")) { + * // ... + * } + * } + * + */ + +DECLARE_MTYPE(MSG_NATIVE_MSG); +DECLARE_MTYPE(MSG_NATIVE_ERROR); +DECLARE_MTYPE(MSG_NATIVE_GET_TREE); +DECLARE_MTYPE(MSG_NATIVE_TREE_DATA); +DECLARE_MTYPE(MSG_NATIVE_GET_DATA); +DECLARE_MTYPE(MSG_NATIVE_NOTIFY); + +/* + * Native message codes + */ +#define MGMT_MSG_CODE_ERROR 0 +#define MGMT_MSG_CODE_GET_TREE 1 +#define MGMT_MSG_CODE_TREE_DATA 2 +#define MGMT_MSG_CODE_GET_DATA 3 +#define MGMT_MSG_CODE_NOTIFY 4 + +/* + * Datastores + */ +#define MGMT_MSG_DATASTORE_STARTUP 0 +#define MGMT_MSG_DATASTORE_CANDIDATE 1 +#define MGMT_MSG_DATASTORE_RUNNING 2 +#define MGMT_MSG_DATASTORE_OPERATIONAL 3 + +/* + * Formats + */ +#define MGMT_MSG_FORMAT_XML 1 +#define MGMT_MSG_FORMAT_JSON 2 +#define MGMT_MSG_FORMAT_BINARY 3 /* non-standard libyang internal format */ + +/* + * Now we're using LYD_FORMAT directly to avoid mapping code, but having our + * own definitions allows us to create such a mapping in the future if libyang + * makes a backwards incompatible change. + */ +_Static_assert(MGMT_MSG_FORMAT_XML == LYD_XML, "Format mismatch"); +_Static_assert(MGMT_MSG_FORMAT_JSON == LYD_JSON, "Format mismatch"); +_Static_assert(MGMT_MSG_FORMAT_BINARY == LYD_LYB, "Format mismatch"); + +/** + * struct mgmt_msg_header - Header common to all native messages. + * + * @code: the actual type of the message. + * @resv: Set to zero, ignore on receive. + * @vsplit: If a variable section is split in 2, the length of first part. + * @refer_id: the session, txn, conn, etc, this message is associated with. + * @req_id: the request this message is for. + */ +struct mgmt_msg_header { + uint16_t code; + uint16_t resv; + uint32_t vsplit; + uint64_t refer_id; + uint64_t req_id; +}; +_Static_assert(sizeof(struct mgmt_msg_header) == 3 * 8, "Bad padding"); +_Static_assert(sizeof(struct mgmt_msg_header) == + offsetof(struct mgmt_msg_header, req_id) + + sizeof(((struct mgmt_msg_header *)0)->req_id), + "Size mismatch"); + +/** + * struct mgmt_msg_error - Common error message. + * + * @error: An error value. + * @errst: Description of error can be 0 length. + * + * This common error message can be used for replies for many msg requests + * (req_id). + */ +struct mgmt_msg_error { + struct mgmt_msg_header; + int16_t error; + uint8_t resv2[6]; + + alignas(8) char errstr[]; +}; +_Static_assert(sizeof(struct mgmt_msg_error) == + offsetof(struct mgmt_msg_error, errstr), + "Size mismatch"); + +/** + * struct mgmt_msg_get_tree - backend oper data request. + * + * @result_type: ``LYD_FORMAT`` for the returned result. + * @xpath: the query for the data to return. + */ +struct mgmt_msg_get_tree { + struct mgmt_msg_header; + uint8_t result_type; + uint8_t resv2[7]; + + alignas(8) char xpath[]; +}; +_Static_assert(sizeof(struct mgmt_msg_get_tree) == + offsetof(struct mgmt_msg_get_tree, xpath), + "Size mismatch"); + +/** + * struct mgmt_msg_tree_data - Message carrying tree data. + * + * @partial_error: If the full result could not be returned do to this error. + * @result_type: ``LYD_FORMAT`` for format of the @result value. + * @more: if this is a partial return and there will be more coming. + * @result: The tree data in @result_type format. + * + */ +struct mgmt_msg_tree_data { + struct mgmt_msg_header; + int8_t partial_error; + uint8_t result_type; + uint8_t more; + uint8_t resv2[5]; + + alignas(8) uint8_t result[]; +}; +_Static_assert(sizeof(struct mgmt_msg_tree_data) == + offsetof(struct mgmt_msg_tree_data, result), + "Size mismatch"); + +/* Flags for get-data request */ +#define GET_DATA_FLAG_STATE 0x01 /* include "config false" data */ +#define GET_DATA_FLAG_CONFIG 0x02 /* include "config true" data */ +#define GET_DATA_FLAG_EXACT 0x04 /* get exact data node instead of the full tree */ + +/* + * Modes of reporting default values. Non-default values are always reported. + * These options reflect "with-defaults" modes as defined in RFC 6243. + */ +#define GET_DATA_DEFAULTS_EXPLICIT 0 /* "explicit" */ +#define GET_DATA_DEFAULTS_TRIM 1 /* "trim" */ +#define GET_DATA_DEFAULTS_ALL 2 /* "report-all" */ +#define GET_DATA_DEFAULTS_ALL_ADD_TAG 3 /* "report-all-tagged" */ + +/** + * struct mgmt_msg_get_data - frontend get-data request. + * + * @result_type: ``LYD_FORMAT`` for the returned result. + * @flags: combination of ``GET_DATA_FLAG_*`` flags. + * @defaults: one of ``GET_DATA_DEFAULTS_*`` values. + * @xpath: the query for the data to return. + */ +struct mgmt_msg_get_data { + struct mgmt_msg_header; + uint8_t result_type; + uint8_t flags; + uint8_t defaults; + uint8_t datastore; + uint8_t resv2[4]; + + alignas(8) char xpath[]; +}; +_Static_assert(sizeof(struct mgmt_msg_get_data) == + offsetof(struct mgmt_msg_get_data, xpath), + "Size mismatch"); + +/** + * struct mgmt_msg_notify_data - Message carrying notification data. + * + * @result_type: ``LYD_FORMAT`` for format of the @result value. + * @data: The xpath string of the notification followed by the tree data in + * @result_type format. + */ +struct mgmt_msg_notify_data { + struct mgmt_msg_header; + uint8_t result_type; + uint8_t resv2[7]; + + alignas(8) char data[]; +}; +_Static_assert(sizeof(struct mgmt_msg_notify_data) == + offsetof(struct mgmt_msg_notify_data, data), + "Size mismatch"); + +/* + * Validate that the message ends in a NUL terminating byte + */ +#define MGMT_MSG_VALIDATE_NUL_TERM(msgp, len) \ + ((len) >= sizeof(*msgp) + 1 && ((char *)msgp)[(len)-1] == 0) + + +/** + * Send a native message error to the other end of the connection. + * + * This function is normally used by the server-side to indicate a failure to + * process a client request. For this server side handling of client messages + * which expect a reply, either that reply or this error should be returned, as + * closing the connection is not allowed during message handling. + * + * Args: + * conn: the connection. + * sess_or_txn_id: Session ID (to FE client) or Txn ID (from BE client) + * req_id: which req_id this error is associated with. + * short_circuit_ok: if short circuit sending is OK. + * error: the error value + * errfmt: vprintfrr style format string + * ap: the variable args for errfmt. + * + * Return: + * The return value of ``msg_conn_send_msg``. + */ +extern int vmgmt_msg_native_send_error(struct msg_conn *conn, + uint64_t sess_or_txn_id, uint64_t req_id, + bool short_circuit_ok, int16_t error, + const char *errfmt, va_list ap) + PRINTFRR(6, 0); + +/** + * mgmt_msg_native_alloc_msg() - Create a native appendable msg. + * @msg_type: The message structure type. + * @var_len: The initial additional length to add to the message. + * @mem_type: The initial additional length to add to the message. + * + * This function takes a C type (e.g., `struct mgmt_msg_get_tree`) as an + * argument and returns a new native message. The newly allocated message + * can be used with the other `native` functions. + * + * Importantly the mgmt_msg_native_append() function can be used to add data + * to the end of the message, and mgmt_msg_get_native_msg_len() can be used + * to obtain the total length of the message (i.e., the fixed sized header plus + * the variable length data that has been appended). + * + * Additionally, a dynamic array (darr) pointer can be obtained using + * mgmt_msg_get_native_darr() which allows adding and manipulating the + * variable data that follows the fixed sized header. + * + * Return: A `msg_type` object created using a dynamic_array. + */ +#define mgmt_msg_native_alloc_msg(msg_type, var_len, mem_type) \ + ({ \ + uint8_t *buf = NULL; \ + (msg_type *)darr_append_nz_mt(buf, \ + sizeof(msg_type) + (var_len), \ + mem_type); \ + }) + +/** + * mgmt_msg_free_native_msg() - Free a native msg. + * @msg - pointer to message allocated by mgmt_msg_create_native_msg(). + */ +#define mgmt_msg_native_free_msg(msg) darr_free(msg) + +/** + * mgmt_msg_native_get_msg_len() - Get the total length of the msg. + * @msg: the native message. + * + * Return: the total length of the message, fixed + variable length. + */ +#define mgmt_msg_native_get_msg_len(msg) (darr_len((uint8_t *)(msg))) + +/** + * mgmt_msg_native_append() - Append data to the end of the msg. + * @msg: (IN/OUT) Pointer to the native message, variable may be updated. + * @data: data to append. + * @len: length of data to append. + * + * Append @data of length @len to the native message @msg. + * + * NOTE: Be aware @msg pointer may change as a result of reallocating the + * message to fit the new data. Any other pointers into the old message should + * be discarded. + * + * Return: a pointer to the newly appended data. + */ +#define mgmt_msg_native_append(msg, data, len) \ + ({ \ + uint8_t **darrp = mgmt_msg_native_get_darrp(msg); \ + uint8_t *p = darr_append_n(*darrp, len); \ + memcpy(p, data, len); \ + p; \ + }) + +/** + * mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg. + * @conn: the mgmt_msg connection. + * @msg: the native message. + * @short_circuit_ok: True if short-circuit sending is required. + * + * Return: The error return value of msg_conn_send_msg(). + */ +#define mgmt_msg_native_send_msg(conn, msg, short_circuit_ok) \ + msg_conn_send_msg(conn, MGMT_MSG_VERSION_NATIVE, msg, \ + mgmt_msg_native_get_msg_len(msg), NULL, \ + short_circuit_ok) + +/** + * mgmt_msg_native_get_darrp() - Return a ptr to the dynamic array ptr. + * @msg: Pointer to the native message. + * + * NOTE: Most users can simply use mgmt_msg_native_append() instead of this. + * + * This function obtains a pointer to the dynamic byte array for this message, + * this array actually includes the message header if one is going to look at + * the length value. With that in mind any of the `darr_*()` functions/API may + * be used to manipulate the variable data at the end of the message. + * + * NOTE: The pointer returned is actually a pointer to the message pointer + * passed in to this function. This pointer to pointer is required so that + * realloc can be done inside the darr API. + * + * NOTE: If reallocs happen the original passed in pointer will be updated; + * however, any other pointers into the message will become invalid and so they + * should always be discarded after using the returned value. + * + * Example: + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * char *xpath = msg->xpath; // pointer into message + * uint8_t **darp; + * + * darrp = mgmt_msg_native_get_darrp(msg); + * darr_in_strcat(*darrp, "/metric"); + * + * xpath = NULL; // now invalid + * xpath = msg->xpath; + * } + * + * + * Return: A pointer to the first argument -- which is a pointer to a pointer to + * a dynamic array. + */ +#define mgmt_msg_native_get_darrp(msg) ((uint8_t **)&(msg)) + +/* ------------------------- */ +/* Encode and Decode Helpers */ +/* ------------------------- */ + +/** + * mgmt_msg_native_xpath_encode() - encode an xpath in a xpath, data message. + * @msg: Pointer to the native message. + * @xpath: The xpath string to encode. + * + * This function starts the encoding of a message that can be decoded with + * `mgmt_msg_native_xpath_data_decode()`. The variable length data is comprised + * of a NUL terminated string followed by some data of any format. This starts + * the first half of the encoding, after which one can simply append the + * secondary data to the message. + */ +#define mgmt_msg_native_xpath_encode(msg, xpath) \ + do { \ + size_t __slen = strlen(xpath) + 1; \ + mgmt_msg_native_append(msg, xpath, __slen); \ + (msg)->vsplit = __slen; \ + } while (0) + +/** + * mgmt_msg_native_xpath_data_decode() - decode an xpath, data format message. + * @msg: Pointer to the native message. + * @msglen: Length of the message. + * @data: [OUT] Pointer to the data section of the variable data + * + * This function decodes a message that was encoded with + * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a + * NUL terminated string followed by some data of any format. + * + * Return: + * The xpath string or NULL if there was an error decoding (i.e., the + * message is corrupt). + */ +#define mgmt_msg_native_xpath_data_decode(msg, msglen, data) \ + ({ \ + size_t __len = (msglen) - sizeof(*msg); \ + const char *__s = NULL; \ + if (msg->vsplit && msg->vsplit <= __len && \ + msg->data[msg->vsplit - 1] == 0) { \ + (data) = msg->data + msg->vsplit; \ + __s = msg->data; \ + } \ + __s; \ + }) + +/** + * mgmt_msg_native_xpath_decode() - return the xpath from xpath, data message. + * @msg: Pointer to the native message. + * @msglen: Length of the message. + * + * This function decodes the xpath from a message that was encoded with + * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a + * NUL terminated string followed by some data of any format. + * + * Return: + * The xpath string or NULL if there was an error decoding (i.e., the + * message is corrupt). + */ +#define mgmt_msg_native_xpath_decode(msg, msglen) \ + ({ \ + size_t __len = (msglen) - sizeof(*msg); \ + const char *__s = msg->data; \ + if (!msg->vsplit || msg->vsplit > __len || \ + __s[msg->vsplit - 1] != 0) \ + __s = NULL; \ + __s; \ + }) + +/** + * mgmt_msg_native_data_decode() - return the data from xpath, data message. + * @msg: Pointer to the native message. + * @msglen: Length of the message. + * + * This function decodes the secondary data from a message that was encoded with + * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a + * NUL terminated string followed by some data of any format. + * + * Return: + * The secondary data or NULL if there was an error decoding (i.e., the + * message is corrupt). + */ +#define mgmt_msg_native_data_decode(msg, msglen) \ + ({ \ + size_t __len = (msglen) - sizeof(*msg); \ + const char *__data = msg->data + msg->vsplit; \ + if (!msg->vsplit || msg->vsplit > __len || __data[-1] != 0) \ + __data = NULL; \ + __data; \ + }) + +/** + * mgmt_msg_native_data_len_decode() - len of data in xpath, data format message. + * @msg: Pointer to the native message. + * @msglen: Length of the message. + * + * This function returns the length of the secondary variable data from a + * message that was encoded with `mgmt_msg_native_xpath_encode()`. The variable + * length data is comprised of a NUL terminated string followed by some data of + * any format. + * + * Return: + * The length of the secondary variable data. The message is assumed to be + * validated as not corrupt already. + */ +#define mgmt_msg_native_data_len_decode(msg, msglen) \ + ((msglen) - sizeof(*msg) - msg->vsplit) + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_MGMT_MSG_NATIVE_H_ */ diff --git a/lib/mlag.c b/lib/mlag.c index a370bf8..62d00ff 100644 --- a/lib/mlag.c +++ b/lib/mlag.c @@ -92,7 +92,7 @@ stream_failure: } #define MLAG_MROUTE_ADD_LENGTH \ - (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4 + 1 + 1 + 4) + (VRF_NAMSIZ + IFNAMSIZ + 4 + 4 + 4 + 4 + 1 + 1 + 4) int mlag_lib_decode_mroute_add(struct stream *s, struct mlag_mroute_add *msg, size_t *length) @@ -108,14 +108,14 @@ int mlag_lib_decode_mroute_add(struct stream *s, struct mlag_mroute_add *msg, STREAM_GETC(s, msg->am_i_dr); STREAM_GETC(s, msg->am_i_dual_active); STREAM_GETL(s, msg->vrf_id); - STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + STREAM_GET(msg->intf_name, s, IFNAMSIZ); return 0; stream_failure: return -1; } -#define MLAG_MROUTE_DEL_LENGTH (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4) +#define MLAG_MROUTE_DEL_LENGTH (VRF_NAMSIZ + IFNAMSIZ + 4 + 4 + 4 + 4) int mlag_lib_decode_mroute_del(struct stream *s, struct mlag_mroute_del *msg, size_t *length) @@ -128,7 +128,7 @@ int mlag_lib_decode_mroute_del(struct stream *s, struct mlag_mroute_del *msg, STREAM_GETL(s, msg->group_ip); STREAM_GETL(s, msg->owner_id); STREAM_GETL(s, msg->vrf_id); - STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + STREAM_GET(msg->intf_name, s, IFNAMSIZ); return 0; stream_failure: @@ -140,7 +140,7 @@ int mlag_lib_decode_mlag_status(struct stream *s, struct mlag_status *msg) if (s == NULL || msg == NULL) return -1; - STREAM_GET(msg->peerlink_rif, s, INTERFACE_NAMSIZ); + STREAM_GET(msg->peerlink_rif, s, IFNAMSIZ); STREAM_GETL(s, msg->my_role); STREAM_GETL(s, msg->peer_state); return 0; diff --git a/lib/mlag.h b/lib/mlag.h index 3aef0d7..91c550b 100644 --- a/lib/mlag.h +++ b/lib/mlag.h @@ -65,7 +65,7 @@ struct mlag_frr_status { }; struct mlag_status { - char peerlink_rif[INTERFACE_NAMSIZ]; + char peerlink_rif[IFNAMSIZ]; enum mlag_role my_role; enum mlag_state peer_state; }; @@ -86,7 +86,7 @@ struct mlag_mroute_add { bool am_i_dr; bool am_i_dual_active; vrf_id_t vrf_id; - char intf_name[INTERFACE_NAMSIZ]; + char intf_name[IFNAMSIZ]; }; struct mlag_mroute_del { @@ -95,7 +95,7 @@ struct mlag_mroute_del { uint32_t group_ip; enum mlag_owner owner_id; vrf_id_t vrf_id; - char intf_name[INTERFACE_NAMSIZ]; + char intf_name[IFNAMSIZ]; }; struct mlag_msg { diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 297e2c5..8fa4bc6 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -5,6 +5,7 @@ */ #include +#include #ifdef HAVE_NETNS #undef _GNU_SOURCE @@ -502,12 +503,19 @@ void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns) void ns_terminate(void) { struct ns *ns; + struct ns_map_nsid *ns_map; while (!RB_EMPTY(ns_head, &ns_tree)) { ns = RB_ROOT(ns_head, &ns_tree); ns_delete(ns); } + + while (!RB_EMPTY(ns_map_nsid_head, &ns_map_nsid_list)) { + ns_map = RB_ROOT(ns_map_nsid_head, &ns_map_nsid_list); + RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map); + XFREE(MTYPE_NS, ns_map); + } } int ns_switch_to_netns(const char *name) diff --git a/lib/network.c b/lib/network.c index af1c7db..b768693 100644 --- a/lib/network.c +++ b/lib/network.c @@ -5,6 +5,7 @@ */ #include +#include #include "log.h" #include "network.h" #include "lib_errors.h" diff --git a/lib/nexthop.c b/lib/nexthop.c index 8df57e3..4ddb53c 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -173,7 +173,7 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, ret = _nexthop_gateway_cmp(next1, next2); if (ret != 0) return ret; - /* Intentional Fall-Through */ + fallthrough; case NEXTHOP_TYPE_IFINDEX: if (next1->ifindex < next2->ifindex) return -1; @@ -295,7 +295,7 @@ int nexthop_cmp_basic(const struct nexthop *nh1, ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); if (ret != 0) return ret; - /* Intentional Fall-Through */ + fallthrough; case NEXTHOP_TYPE_IFINDEX: if (nh1->ifindex < nh2->ifindex) return -1; diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 25370eb..3f408e0 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -81,7 +81,8 @@ uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg) return num; } -uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg) +static uint8_t +nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg) { struct nexthop *nhop; uint8_t num = 0; @@ -105,20 +106,6 @@ uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg) return num; } -uint8_t -nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg) -{ - struct nexthop *nhop; - uint8_t num = 0; - - for (nhop = nhg->nexthop; nhop; nhop = nhop->next) { - if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE)) - num++; - } - - return num; -} - bool nexthop_group_has_label(const struct nexthop_group *nhg) { struct nexthop *nhop; @@ -180,7 +167,7 @@ static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg, &nexthop->gate, &nh->gate); if (ret != 0) continue; - /* Intentional Fall-Through */ + fallthrough; case NEXTHOP_TYPE_IFINDEX: if (nexthop->ifindex != nh->ifindex) continue; @@ -1248,9 +1235,9 @@ void nexthop_group_disable_vrf(struct vrf *vrf) struct nexthop_hold *nhh; RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { - struct listnode *node; + struct listnode *node, *nnode; - for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + for (ALL_LIST_ELEMENTS(nhgc->nhg_list, node, nnode, nhh)) { struct nexthop nhop; struct nexthop *nh; @@ -1271,6 +1258,10 @@ void nexthop_group_disable_vrf(struct vrf *vrf) nhg_hooks.del_nexthop(nhgc, nh); nexthop_free(nh); + + list_delete_node(nhgc->nhg_list, node); + + nhgl_delete(nhh); } } } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 78237e4..822a354 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -151,11 +151,7 @@ extern void nexthop_group_json_nexthop(json_object *j, /* Return the number of nexthops in this nhg */ extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg); extern uint8_t -nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg); -extern uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg); -extern uint8_t -nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg); extern bool nexthop_group_has_label(const struct nexthop_group *nhg); diff --git a/lib/northbound.c b/lib/northbound.c index 6ff5c24..487f225 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -6,6 +6,7 @@ #include +#include "darr.h" #include "libfrr.h" #include "log.h" #include "lib_errors.h" @@ -70,12 +71,6 @@ static int nb_transaction_process(enum nb_event event, char *errmsg, size_t errmsg_len); static void nb_transaction_apply_finish(struct nb_transaction *transaction, char *errmsg, size_t errmsg_len); -static int nb_oper_data_iter_node(const struct lysc_node *snode, - const char *xpath, const void *list_entry, - const struct yang_list_keys *list_keys, - struct yang_translator *translator, - bool first, uint32_t flags, - nb_oper_data_cb cb, void *arg); static int nb_node_check_config_only(const struct lysc_node *snode, void *arg) { @@ -130,8 +125,8 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg) assert(snode->priv == NULL); ((struct lysc_node *)snode)->priv = nb_node; - if (module && module->ignore_cbs) - SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS); + if (module && module->ignore_cfg_cbs) + SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS); return YANG_ITER_CONTINUE; } @@ -162,18 +157,45 @@ void nb_nodes_delete(void) struct nb_node *nb_node_find(const char *path) { const struct lysc_node *snode; + uint32_t llopts = 0; /* * Use libyang to find the schema node associated to the path and get - * the northbound node from there (snode private pointer). + * the northbound node from there (snode private pointer). We need to + * disable logging temporarily to avoid libyang from logging an error + * message when the node is not found. */ + ly_temp_log_options(&llopts); + snode = yang_find_snode(ly_native_ctx, path, 0); + + ly_temp_log_options(NULL); if (!snode) return NULL; return snode->priv; } +struct nb_node **nb_nodes_find(const char *xpath) +{ + struct lysc_node **snodes = NULL; + struct nb_node **nb_nodes = NULL; + bool simple; + LY_ERR err; + uint i; + + err = yang_resolve_snode_xpath(ly_native_ctx, xpath, &snodes, &simple); + if (err) + return NULL; + + darr_ensure_i(nb_nodes, darr_lasti(snodes)); + darr_foreach_i (snodes, i) + nb_nodes[i] = snodes[i]->priv; + darr_free(snodes); + return nb_nodes; +} + + void nb_node_set_dependency_cbs(const char *dependency_xpath, const char *dependant_xpath, struct nb_dependency_callbacks *cbs) @@ -194,12 +216,12 @@ bool nb_node_has_dependency(struct nb_node *node) } static int nb_node_validate_cb(const struct nb_node *nb_node, - enum nb_operation operation, + enum nb_cb_operation operation, int callback_implemented, bool optional) { bool valid; - valid = nb_operation_is_valid(operation, nb_node->snode); + valid = nb_cb_operation_is_valid(operation, nb_node->snode); /* * Add an exception for operational data callbacks. A rw list usually @@ -211,15 +233,15 @@ static int nb_node_validate_cb(const struct nb_node *nb_node, * depends on context (e.g. some daemons might augment "frr-interface" * while others don't). */ - if (!valid && callback_implemented && operation != NB_OP_GET_NEXT - && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY) + if (!valid && callback_implemented && operation != NB_CB_GET_NEXT + && operation != NB_CB_GET_KEYS && operation != NB_CB_LOOKUP_ENTRY) flog_warn(EC_LIB_NB_CB_UNNEEDED, "unneeded '%s' callback for '%s'", - nb_operation_name(operation), nb_node->xpath); + nb_cb_operation_name(operation), nb_node->xpath); if (!optional && valid && !callback_implemented) { flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'", - nb_operation_name(operation), nb_node->xpath); + nb_cb_operation_name(operation), nb_node->xpath); return 1; } @@ -235,31 +257,33 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node) { unsigned int error = 0; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return error; - error += nb_node_validate_cb(nb_node, NB_OP_CREATE, + error += nb_node_validate_cb(nb_node, NB_CB_CREATE, !!nb_node->cbs.create, false); - error += nb_node_validate_cb(nb_node, NB_OP_MODIFY, + error += nb_node_validate_cb(nb_node, NB_CB_MODIFY, !!nb_node->cbs.modify, false); - error += nb_node_validate_cb(nb_node, NB_OP_DESTROY, + error += nb_node_validate_cb(nb_node, NB_CB_DESTROY, !!nb_node->cbs.destroy, false); - error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move, + error += nb_node_validate_cb(nb_node, NB_CB_MOVE, !!nb_node->cbs.move, false); - error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE, + error += nb_node_validate_cb(nb_node, NB_CB_PRE_VALIDATE, !!nb_node->cbs.pre_validate, true); - error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH, + error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH, !!nb_node->cbs.apply_finish, true); - error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM, + error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM, !!nb_node->cbs.get_elem, false); - error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT, + error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT, !!nb_node->cbs.get_next, false); - error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS, + error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS, !!nb_node->cbs.get_keys, false); - error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY, + error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY, !!nb_node->cbs.lookup_entry, false); - error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc, + error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc, false); + error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY, + !!nb_node->cbs.notify, true); return error; } @@ -305,8 +329,6 @@ struct nb_config *nb_config_new(struct lyd_node *dnode) config->dnode = yang_dnode_new(ly_native_ctx, true); config->version = 0; - RB_INIT(nb_config_cbs, &config->cfg_chgs); - return config; } @@ -314,7 +336,7 @@ void nb_config_free(struct nb_config *config) { if (config->dnode) yang_dnode_free(config->dnode); - nb_config_diff_del_changes(&config->cfg_chgs); + XFREE(MTYPE_NB_CONFIG, config); } @@ -326,8 +348,6 @@ struct nb_config *nb_config_dup(const struct nb_config *config) dup->dnode = yang_dnode_dup(config->dnode); dup->version = config->version; - RB_INIT(nb_config_cbs, &dup->cfg_chgs); - return dup; } @@ -369,12 +389,28 @@ void nb_config_replace(struct nb_config *config_dst, static inline int nb_config_cb_compare(const struct nb_config_cb *a, const struct nb_config_cb *b) { - /* Sort by priority first. */ - if (a->nb_node->priority < b->nb_node->priority) + bool a_destroy = a->operation == NB_CB_DESTROY; + bool b_destroy = b->operation == NB_CB_DESTROY; + + /* + * Sort by operation first. All "destroys" must come first, to correctly + * process the change of a "case" inside a "choice". The old "case" must + * be deleted before the new "case" is created. + */ + if (a_destroy && !b_destroy) return -1; - if (a->nb_node->priority > b->nb_node->priority) + if (!a_destroy && b_destroy) return 1; + /* + * Then sort by priority. If the operation is "destroy", reverse the + * order, so that the dependants are deleted before the dependencies. + */ + if (a->nb_node->priority < b->nb_node->priority) + return !a_destroy ? -1 : 1; + if (a->nb_node->priority > b->nb_node->priority) + return !a_destroy ? 1 : -1; + /* * Preserve the order of the configuration changes as told by libyang. */ @@ -397,10 +433,9 @@ static inline int nb_config_cb_compare(const struct nb_config_cb *a, } RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare); -static void nb_config_diff_add_change(struct nb_config_cbs *changes, - enum nb_operation operation, - uint32_t *seq, - const struct lyd_node *dnode) +void nb_config_diff_add_change(struct nb_config_cbs *changes, + enum nb_cb_operation operation, uint32_t *seq, + const struct lyd_node *dnode) { struct nb_config_change *change; @@ -438,7 +473,7 @@ void nb_config_diff_del_changes(struct nb_config_cbs *changes) void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { - enum nb_operation operation; + enum nb_cb_operation operation; struct lyd_node *child; /* Ignore unimplemented nodes. */ @@ -451,10 +486,10 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, if (lyd_is_default(dnode)) break; - if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema)) - operation = NB_OP_CREATE; - else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema)) - operation = NB_OP_MODIFY; + if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema)) + operation = NB_CB_CREATE; + else if (nb_cb_operation_is_valid(NB_CB_MODIFY, dnode->schema)) + operation = NB_CB_MODIFY; else return; @@ -462,8 +497,8 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, break; case LYS_CONTAINER: case LYS_LIST: - if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema)) - nb_config_diff_add_change(changes, NB_OP_CREATE, seq, + if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema)) + nb_config_diff_add_change(changes, NB_CB_CREATE, seq, dnode); /* Process child nodes recursively. */ @@ -483,8 +518,8 @@ static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq, if (!dnode->schema->priv) return; - if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema)) - nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode); + if (nb_cb_operation_is_valid(NB_CB_DESTROY, dnode->schema)) + nb_config_diff_add_change(changes, NB_CB_DESTROY, seq, dnode); else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) { struct lyd_node *child; @@ -633,7 +668,7 @@ void nb_config_diff(const struct nb_config *config1, /* either moving an entry or changing a value */ target = yang_dnode_get(config2->dnode, path); assert(target); - nb_config_diff_add_change(changes, NB_OP_MODIFY, + nb_config_diff_add_change(changes, NB_CB_MODIFY, &seq, target); break; case 'n': /* none */ @@ -648,44 +683,54 @@ void nb_config_diff(const struct nb_config *config1, lyd_free_all(diff); } -int nb_candidate_edit(struct nb_config *candidate, - const struct nb_node *nb_node, +static int dnode_create(struct nb_config *candidate, const char *xpath, + const char *value, uint32_t options, + struct lyd_node **new_dnode) +{ + struct lyd_node *dnode; + LY_ERR err; + + err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value, + options, &dnode); + if (err) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d", + __func__, xpath, err); + return NB_ERR; + } else if (dnode) { + err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL); + if (err) { + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_new_implicit_all failed: %d", + __func__, err); + } + } + if (new_dnode) + *new_dnode = dnode; + return NB_OK; +} + +int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node, enum nb_operation operation, const char *xpath, const struct yang_data *previous, const struct yang_data *data) { - struct lyd_node *dnode, *dep_dnode; - char xpath_edit[XPATH_MAXLEN]; + struct lyd_node *dnode, *dep_dnode, *old_dnode; char dep_xpath[XPATH_MAXLEN]; + struct lyd_node *parent = NULL; + uint32_t options = 0; LY_ERR err; - /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */ - if (nb_node->snode->nodetype == LYS_LEAFLIST) - snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath, - data->value); - else - strlcpy(xpath_edit, xpath, sizeof(xpath_edit)); - switch (operation) { case NB_OP_CREATE: case NB_OP_MODIFY: - err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit, - (void *)data->value, LYD_NEW_PATH_UPDATE, + options = LYD_NEW_PATH_UPDATE; + fallthrough; + case NB_OP_CREATE_EXCL: + err = dnode_create(candidate, xpath, data->value, options, &dnode); if (err) { - flog_warn(EC_LIB_LIBYANG, - "%s: lyd_new_path(%s) failed: %d", __func__, - xpath_edit, err); - return NB_ERR; + return err; } else if (dnode) { - /* Create default nodes */ - LY_ERR err = lyd_new_implicit_tree( - dnode, LYD_IMPLICIT_NO_STATE, NULL); - if (err) { - flog_warn(EC_LIB_LIBYANG, - "%s: lyd_new_implicit_all failed: %d", - __func__, err); - } /* * create dependency * @@ -697,33 +742,24 @@ int nb_candidate_edit(struct nb_config *candidate, nb_node->dep_cbs.get_dependency_xpath( dnode, dep_xpath); - err = lyd_new_path(candidate->dnode, - ly_native_ctx, dep_xpath, - NULL, LYD_NEW_PATH_UPDATE, - &dep_dnode); - /* Create default nodes */ - if (!err && dep_dnode) - err = lyd_new_implicit_tree( - dep_dnode, - LYD_IMPLICIT_NO_STATE, NULL); + err = dnode_create(candidate, dep_xpath, NULL, + LYD_NEW_PATH_UPDATE, NULL); if (err) { - flog_warn( - EC_LIB_LIBYANG, - "%s: dependency: lyd_new_path(%s) failed: %d", - __func__, dep_xpath, err); - return NB_ERR; + lyd_free_tree(dnode); + return err; } } } break; case NB_OP_DESTROY: - dnode = yang_dnode_get(candidate->dnode, xpath_edit); - if (!dnode) - /* - * Return a special error code so the caller can choose - * whether to ignore it or not. - */ - return NB_ERR_NOT_FOUND; + case NB_OP_DELETE: + dnode = yang_dnode_get(candidate->dnode, xpath); + if (!dnode) { + if (operation == NB_OP_DELETE) + return NB_ERR; + else + return NB_OK; + } /* destroy dependant */ if (nb_node->dep_cbs.get_dependant_xpath) { nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); @@ -734,103 +770,88 @@ int nb_candidate_edit(struct nb_config *candidate, } lyd_free_tree(dnode); break; + case NB_OP_REPLACE: + old_dnode = yang_dnode_get(candidate->dnode, xpath); + if (old_dnode) { + parent = lyd_parent(old_dnode); + lyd_unlink_tree(old_dnode); + } + err = dnode_create(candidate, xpath, data->value, options, + &dnode); + if (!err && dnode && !old_dnode) { + /* create dependency if the node didn't exist */ + nb_node = dnode->schema->priv; + if (nb_node->dep_cbs.get_dependency_xpath) { + nb_node->dep_cbs.get_dependency_xpath( + dnode, dep_xpath); + + err = dnode_create(candidate, dep_xpath, NULL, + LYD_NEW_PATH_UPDATE, NULL); + if (err) + lyd_free_tree(dnode); + } + } + if (old_dnode) { + /* restore original node on error, free it otherwise */ + if (err) { + if (parent) + lyd_insert_child(parent, old_dnode); + else + lyd_insert_sibling(candidate->dnode, + old_dnode, NULL); + return err; + } + + lyd_free_tree(old_dnode); + } + break; case NB_OP_MOVE: /* TODO: update configuration. */ break; - case NB_OP_PRE_VALIDATE: - case NB_OP_APPLY_FINISH: - case NB_OP_GET_ELEM: - case NB_OP_GET_NEXT: - case NB_OP_GET_KEYS: - case NB_OP_LOOKUP_ENTRY: - case NB_OP_RPC: - flog_warn(EC_LIB_DEVELOPMENT, - "%s: unknown operation (%u) [xpath %s]", __func__, - operation, xpath_edit); - return NB_ERR; } return NB_OK; } -static void nb_update_candidate_changes(struct nb_config *candidate, - struct nb_cfg_change *change, - uint32_t *seq) +const char *nb_operation_name(enum nb_operation operation) { - enum nb_operation oper = change->operation; - char *xpath = change->xpath; - struct lyd_node *root = NULL; - struct lyd_node *dnode; - struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs; - int op; - - switch (oper) { + switch (operation) { + case NB_OP_CREATE_EXCL: + return "create exclusive"; case NB_OP_CREATE: + return "create"; case NB_OP_MODIFY: - root = yang_dnode_get(candidate->dnode, xpath); - break; + return "modify"; case NB_OP_DESTROY: - root = yang_dnode_get(running_config->dnode, xpath); - /* code */ - break; + return "destroy"; + case NB_OP_DELETE: + return "delete"; + case NB_OP_REPLACE: + return "replace"; case NB_OP_MOVE: - case NB_OP_PRE_VALIDATE: - case NB_OP_APPLY_FINISH: - case NB_OP_GET_ELEM: - case NB_OP_GET_NEXT: - case NB_OP_GET_KEYS: - case NB_OP_LOOKUP_ENTRY: - case NB_OP_RPC: - break; - default: - assert(!"non-enum value, invalid"); + return "move"; } - if (!root) - return; - - LYD_TREE_DFS_BEGIN (root, dnode) { - op = nb_lyd_diff_get_op(dnode); - switch (op) { - case 'c': /* create */ - nb_config_diff_created(dnode, seq, cfg_chgs); - LYD_TREE_DFS_continue = 1; - break; - case 'd': /* delete */ - nb_config_diff_deleted(dnode, seq, cfg_chgs); - LYD_TREE_DFS_continue = 1; - break; - case 'r': /* replace */ - nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq, - dnode); - break; - case 'n': /* none */ - default: - break; - } - LYD_TREE_DFS_END(root, dnode); - } + assert(!"Reached end of function we should never hit"); } -static bool nb_is_operation_allowed(struct nb_node *nb_node, - struct nb_cfg_change *change) +bool nb_is_operation_allowed(struct nb_node *nb_node, enum nb_operation oper) { - enum nb_operation oper = change->operation; - if (lysc_is_key(nb_node->snode)) { - if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY) + if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY + || oper == NB_OP_DELETE || oper == NB_OP_REPLACE) return false; } return true; } -void nb_candidate_edit_config_changes( - struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[], - size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath, - int xpath_index, char *err_buf, int err_bufsize, bool *error) +void nb_candidate_edit_config_changes(struct nb_config *candidate_config, + struct nb_cfg_change cfg_changes[], + size_t num_cfg_changes, + const char *xpath_base, bool in_backend, + char *err_buf, int err_bufsize, + bool *error) { - uint32_t seq = 0; - if (error) *error = false; @@ -841,38 +862,38 @@ void nb_candidate_edit_config_changes( for (size_t i = 0; i < num_cfg_changes; i++) { struct nb_cfg_change *change = &cfg_changes[i]; struct nb_node *nb_node; + char *change_xpath = change->xpath; char xpath[XPATH_MAXLEN]; const char *value; struct yang_data *data; int ret; - /* Handle relative XPaths. */ memset(xpath, 0, sizeof(xpath)); - if (xpath_index > 0 && - (xpath_base[0] == '.' || change->xpath[0] == '.')) - strlcpy(xpath, curr_xpath, sizeof(xpath)); - if (xpath_base[0]) { - if (xpath_base[0] == '.') - strlcat(xpath, xpath_base + 1, sizeof(xpath)); - else - strlcat(xpath, xpath_base, sizeof(xpath)); + /* If change xpath is relative, prepend base xpath. */ + if (change_xpath[0] == '.') { + strlcpy(xpath, xpath_base, sizeof(xpath)); + change_xpath++; /* skip '.' */ } - if (change->xpath[0] == '.') - strlcat(xpath, change->xpath + 1, sizeof(xpath)); - else - strlcpy(xpath, change->xpath, sizeof(xpath)); + strlcat(xpath, change_xpath, sizeof(xpath)); /* Find the northbound node associated to the data path. */ nb_node = nb_node_find(xpath); if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - if (error) - *error = true; + if (in_backend) + DEBUGD(&nb_dbg_cbs_config, + "%s: ignoring non-handled path: %s", + __func__, xpath); + else { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, + xpath); + if (error) + *error = true; + } continue; } /* Find if the node to be edited is not a key node */ - if (!nb_is_operation_allowed(nb_node, change)) { + if (!nb_is_operation_allowed(nb_node, change->operation)) { zlog_err(" Xpath %s points to key node", xpath); if (error) *error = true; @@ -892,7 +913,7 @@ void nb_candidate_edit_config_changes( ret = nb_candidate_edit(candidate_config, nb_node, change->operation, xpath, NULL, data); yang_data_free(data); - if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { + if (ret != NB_OK) { flog_warn( EC_LIB_NB_CANDIDATE_EDIT_ERROR, "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", @@ -902,17 +923,11 @@ void nb_candidate_edit_config_changes( *error = true; continue; } - nb_update_candidate_changes(candidate_config, change, &seq); } if (error && *error) { char buf[BUFSIZ]; - /* - * Failure to edit the candidate configuration should never - * happen in practice, unless there's a bug in the code. When - * that happens, log the error but otherwise ignore it. - */ snprintf(err_buf, err_bufsize, "%% Failed to edit configuration.\n\n%s", yang_print_errors(ly_native_ctx, buf, sizeof(buf))); @@ -949,9 +964,19 @@ int nb_candidate_update(struct nb_config *candidate) int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state, char *errmsg, size_t errmsg_len) { - if (lyd_validate_all(&candidate->dnode, ly_native_ctx, - no_state ? LYD_VALIDATE_NO_STATE - : LYD_VALIDATE_PRESENT, + uint32_t options = 0; + +#ifdef LYD_VALIDATE_MULTI_ERROR + /* libyang 2.1.36+ */ + options |= LYD_VALIDATE_MULTI_ERROR; +#endif + + if (no_state) + SET_FLAG(options, LYD_VALIDATE_NO_STATE); + else + SET_FLAG(options, LYD_VALIDATE_PRESENT); + + if (lyd_validate_all(&candidate->dnode, ly_native_ctx, options, NULL) != 0) { yang_print_errors(ly_native_ctx, errmsg, errmsg_len); return NB_ERR_VALIDATION; @@ -1193,7 +1218,7 @@ int nb_running_lock_check(enum nb_client client, const void *user) } static void nb_log_config_callback(const enum nb_event event, - enum nb_operation operation, + enum nb_cb_operation operation, const struct lyd_node *dnode) { const char *value; @@ -1210,7 +1235,7 @@ static void nb_log_config_callback(const enum nb_event event, zlog_debug( "northbound callback: event [%s] op [%s] xpath [%s] value [%s]", - nb_event_name(event), nb_operation_name(operation), xpath, + nb_event_name(event), nb_cb_operation_name(operation), xpath, value); } @@ -1224,9 +1249,9 @@ static int nb_callback_create(struct nb_context *context, bool unexpected_error = false; int ret; - assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)); - nb_log_config_callback(event, NB_OP_CREATE, dnode); + nb_log_config_callback(event, NB_CB_CREATE, dnode); args.context = context; args.event = event; @@ -1275,9 +1300,9 @@ static int nb_callback_modify(struct nb_context *context, bool unexpected_error = false; int ret; - assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)); - nb_log_config_callback(event, NB_OP_MODIFY, dnode); + nb_log_config_callback(event, NB_CB_MODIFY, dnode); args.context = context; args.event = event; @@ -1326,9 +1351,9 @@ static int nb_callback_destroy(struct nb_context *context, bool unexpected_error = false; int ret; - assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)); - nb_log_config_callback(event, NB_OP_DESTROY, dnode); + nb_log_config_callback(event, NB_CB_DESTROY, dnode); args.context = context; args.event = event; @@ -1371,9 +1396,9 @@ static int nb_callback_move(struct nb_context *context, bool unexpected_error = false; int ret; - assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)); - nb_log_config_callback(event, NB_OP_MOVE, dnode); + nb_log_config_callback(event, NB_CB_MOVE, dnode); args.context = context; args.event = event; @@ -1416,10 +1441,10 @@ static int nb_callback_pre_validate(struct nb_context *context, bool unexpected_error = false; int ret; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return 0; - nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode); + nb_log_config_callback(NB_EV_VALIDATE, NB_CB_PRE_VALIDATE, dnode); args.dnode = dnode; args.errmsg = errmsg; @@ -1450,10 +1475,10 @@ static void nb_callback_apply_finish(struct nb_context *context, { struct nb_cb_apply_finish_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return; - nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode); + nb_log_config_callback(NB_EV_APPLY, NB_CB_APPLY_FINISH, dnode); args.context = context; args.dnode = dnode; @@ -1468,7 +1493,7 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, { struct nb_cb_get_elem_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return NULL; DEBUGD(&nb_dbg_cbs_state, @@ -1486,7 +1511,7 @@ const void *nb_callback_get_next(const struct nb_node *nb_node, { struct nb_cb_get_next_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return NULL; DEBUGD(&nb_dbg_cbs_state, @@ -1503,7 +1528,7 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry, { struct nb_cb_get_keys_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return 0; DEBUGD(&nb_dbg_cbs_state, @@ -1521,7 +1546,7 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node, { struct nb_cb_lookup_entry_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return NULL; DEBUGD(&nb_dbg_cbs_state, @@ -1533,13 +1558,57 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node, return nb_node->cbs.lookup_entry(&args); } +const void *nb_callback_lookup_node_entry(struct lyd_node *node, + const void *parent_list_entry) +{ + struct yang_list_keys keys; + struct nb_cb_lookup_entry_args args = {}; + const struct nb_node *nb_node = node->schema->priv; + + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) + return NULL; + + if (yang_get_node_keys(node, &keys)) { + flog_warn(EC_LIB_LIBYANG, + "%s: can't get keys for lookup from existing data node %s", + __func__, node->schema->name); + return NULL; + } + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (lookup_node_entry): node [%s] parent_list_entry [%p]", + nb_node->xpath, parent_list_entry); + + args.parent_list_entry = parent_list_entry; + args.keys = &keys; + return nb_node->cbs.lookup_entry(&args); +} + +const void *nb_callback_lookup_next(const struct nb_node *nb_node, + const void *parent_list_entry, + const struct yang_list_keys *keys) +{ + struct nb_cb_lookup_entry_args args = {}; + + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) + return NULL; + + DEBUGD(&nb_dbg_cbs_state, + "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]", + nb_node->xpath, parent_list_entry); + + args.parent_list_entry = parent_list_entry; + args.keys = keys; + return nb_node->cbs.lookup_next(&args); +} + int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, const struct list *input, struct list *output, char *errmsg, size_t errmsg_len) { struct nb_cb_rpc_args args = {}; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return 0; DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); @@ -1552,6 +1621,18 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, return nb_node->cbs.rpc(&args); } +void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, + struct lyd_node *dnode) +{ + struct nb_cb_notify_args args = {}; + + DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath); + + args.xpath = xpath; + args.dnode = dnode; + nb_node->cbs.notify(&args); +} + /* * Call the northbound configuration callback associated to a given * configuration change. @@ -1561,14 +1642,14 @@ static int nb_callback_configuration(struct nb_context *context, struct nb_config_change *change, char *errmsg, size_t errmsg_len) { - enum nb_operation operation = change->cb.operation; + enum nb_cb_operation operation = change->cb.operation; char xpath[XPATH_MAXLEN]; const struct nb_node *nb_node = change->cb.nb_node; const struct lyd_node *dnode = change->cb.dnode; union nb_resource *resource; int ret = NB_ERR; - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return NB_OK; if (event == NB_EV_VALIDATE) @@ -1577,29 +1658,30 @@ static int nb_callback_configuration(struct nb_context *context, resource = &change->resource; switch (operation) { - case NB_OP_CREATE: + case NB_CB_CREATE: ret = nb_callback_create(context, nb_node, event, dnode, resource, errmsg, errmsg_len); break; - case NB_OP_MODIFY: + case NB_CB_MODIFY: ret = nb_callback_modify(context, nb_node, event, dnode, resource, errmsg, errmsg_len); break; - case NB_OP_DESTROY: + case NB_CB_DESTROY: ret = nb_callback_destroy(context, nb_node, event, dnode, errmsg, errmsg_len); break; - case NB_OP_MOVE: + case NB_CB_MOVE: ret = nb_callback_move(context, nb_node, event, dnode, errmsg, errmsg_len); break; - case NB_OP_PRE_VALIDATE: - case NB_OP_APPLY_FINISH: - case NB_OP_GET_ELEM: - case NB_OP_GET_NEXT: - case NB_OP_GET_KEYS: - case NB_OP_LOOKUP_ENTRY: - case NB_OP_RPC: + case NB_CB_PRE_VALIDATE: + case NB_CB_APPLY_FINISH: + case NB_CB_GET_ELEM: + case NB_CB_GET_NEXT: + case NB_CB_GET_KEYS: + case NB_CB_LOOKUP_ENTRY: + case NB_CB_RPC: + case NB_CB_NOTIFY: yang_dnode_get_path(dnode, xpath, sizeof(xpath)); flog_err(EC_LIB_DEVELOPMENT, "%s: unknown operation (%u) [xpath %s]", __func__, @@ -1615,28 +1697,28 @@ static int nb_callback_configuration(struct nb_context *context, flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath, + nb_cb_operation_name(operation), xpath, errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_PREPARE: flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE, "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath, + nb_cb_operation_name(operation), xpath, errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_ABORT: flog_warn(EC_LIB_NB_CB_CONFIG_ABORT, "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath, + nb_cb_operation_name(operation), xpath, errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_APPLY: flog_err(EC_LIB_NB_CB_CONFIG_APPLY, "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath, + nb_cb_operation_name(operation), xpath, errmsg[0] ? " message: " : "", errmsg); break; default: @@ -1743,6 +1825,7 @@ nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node, struct nb_config_cb *cb; cb = XCALLOC(MTYPE_TMP, sizeof(*cb)); + cb->operation = NB_CB_APPLY_FINISH; cb->nb_node = nb_node; cb->dnode = dnode; RB_INSERT(nb_config_cbs, cbs, cb); @@ -1757,6 +1840,7 @@ nb_apply_finish_cb_find(struct nb_config_cbs *cbs, { struct nb_config_cb s; + s.operation = NB_CB_APPLY_FINISH; s.seq = 0; s.nb_node = nb_node; s.dnode = dnode; @@ -1784,12 +1868,12 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction, * (the 'apply_finish' callbacks from the node ancestors should * be called though). */ - if (change->cb.operation == NB_OP_DESTROY) { + if (change->cb.operation == NB_CB_DESTROY) { char xpath[XPATH_MAXLEN]; dnode = lyd_parent(dnode); if (!dnode) - break; + continue; /* * The dnode from 'delete' callbacks point to elements @@ -1835,394 +1919,15 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction, } } -static int nb_oper_data_iter_children(const struct lysc_node *snode, - const char *xpath, const void *list_entry, - const struct yang_list_keys *list_keys, - struct yang_translator *translator, - bool first, uint32_t flags, - nb_oper_data_cb cb, void *arg) -{ - const struct lysc_node *child; - - LY_LIST_FOR (lysc_node_child(snode), child) { - int ret; - - ret = nb_oper_data_iter_node(child, xpath, list_entry, - list_keys, translator, false, - flags, cb, arg); - if (ret != NB_OK) - return ret; - } - - return NB_OK; -} - -static int nb_oper_data_iter_leaf(const struct nb_node *nb_node, - const char *xpath, const void *list_entry, - const struct yang_list_keys *list_keys, - struct yang_translator *translator, - uint32_t flags, nb_oper_data_cb cb, void *arg) -{ - struct yang_data *data; - - if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W)) - return NB_OK; - - /* Ignore list keys. */ - if (lysc_is_key(nb_node->snode)) - return NB_OK; - - data = nb_callback_get_elem(nb_node, xpath, list_entry); - if (data == NULL) - /* Leaf of type "empty" is not present. */ - return NB_OK; - - return (*cb)(nb_node->snode, translator, data, arg); -} - -static int nb_oper_data_iter_container(const struct nb_node *nb_node, - const char *xpath, - const void *list_entry, - const struct yang_list_keys *list_keys, - struct yang_translator *translator, - uint32_t flags, nb_oper_data_cb cb, - void *arg) -{ - const struct lysc_node *snode = nb_node->snode; - - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) - return NB_OK; - - /* Read-only presence containers. */ - if (nb_node->cbs.get_elem) { - struct yang_data *data; - int ret; - - data = nb_callback_get_elem(nb_node, xpath, list_entry); - if (data == NULL) - /* Presence container is not present. */ - return NB_OK; - - ret = (*cb)(snode, translator, data, arg); - if (ret != NB_OK) - return ret; - } - - /* Read-write presence containers. */ - if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) { - struct lysc_node_container *scontainer; - - scontainer = (struct lysc_node_container *)snode; - if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE) - && !yang_dnode_get(running_config->dnode, xpath)) - return NB_OK; - } - - /* Iterate over the child nodes. */ - return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys, - translator, false, flags, cb, arg); -} - -static int -nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath, - const void *parent_list_entry, - const struct yang_list_keys *parent_list_keys, - struct yang_translator *translator, uint32_t flags, - nb_oper_data_cb cb, void *arg) -{ - const void *list_entry = NULL; - - if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W)) - return NB_OK; - - do { - struct yang_data *data; - int ret; - - list_entry = nb_callback_get_next(nb_node, parent_list_entry, - list_entry); - if (!list_entry) - /* End of the list. */ - break; - - data = nb_callback_get_elem(nb_node, xpath, list_entry); - if (data == NULL) - continue; - - ret = (*cb)(nb_node->snode, translator, data, arg); - if (ret != NB_OK) - return ret; - } while (list_entry); - - return NB_OK; -} - -static int nb_oper_data_iter_list(const struct nb_node *nb_node, - const char *xpath_list, - const void *parent_list_entry, - const struct yang_list_keys *parent_list_keys, - struct yang_translator *translator, - uint32_t flags, nb_oper_data_cb cb, void *arg) -{ - const struct lysc_node *snode = nb_node->snode; - const void *list_entry = NULL; - uint32_t position = 1; - - if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) - return NB_OK; - - /* Iterate over all list entries. */ - do { - const struct lysc_node_leaf *skey; - struct yang_list_keys list_keys = {}; - char xpath[XPATH_MAXLEN * 2]; - int ret; - - /* Obtain list entry. */ - list_entry = nb_callback_get_next(nb_node, parent_list_entry, - list_entry); - if (!list_entry) - /* End of the list. */ - break; - - if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) { - /* Obtain the list entry keys. */ - if (nb_callback_get_keys(nb_node, list_entry, - &list_keys) - != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to get list keys", - __func__); - return NB_ERR; - } - - /* Build XPath of the list entry. */ - strlcpy(xpath, xpath_list, sizeof(xpath)); - unsigned int i = 0; - LY_FOR_KEYS (snode, skey) { - assert(i < list_keys.num); - snprintf(xpath + strlen(xpath), - sizeof(xpath) - strlen(xpath), - "[%s='%s']", skey->name, - list_keys.key[i]); - i++; - } - assert(i == list_keys.num); - } else { - /* - * Keyless list - build XPath using a positional index. - */ - snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list, - position); - position++; - } - - /* Iterate over the child nodes. */ - ret = nb_oper_data_iter_children( - nb_node->snode, xpath, list_entry, &list_keys, - translator, false, flags, cb, arg); - if (ret != NB_OK) - return ret; - } while (list_entry); - - return NB_OK; -} - -static int nb_oper_data_iter_node(const struct lysc_node *snode, - const char *xpath_parent, - const void *list_entry, - const struct yang_list_keys *list_keys, - struct yang_translator *translator, - bool first, uint32_t flags, - nb_oper_data_cb cb, void *arg) -{ - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - int ret = NB_OK; - - if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE) - && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) - return NB_OK; - - /* Update XPath. */ - strlcpy(xpath, xpath_parent, sizeof(xpath)); - if (!first && snode->nodetype != LYS_USES) { - struct lysc_node *parent; - - /* Get the real parent. */ - parent = snode->parent; - - /* - * When necessary, include the namespace of the augmenting - * module. - */ - if (parent && parent->module != snode->module) - snprintf(xpath + strlen(xpath), - sizeof(xpath) - strlen(xpath), "/%s:%s", - snode->module->name, snode->name); - else - snprintf(xpath + strlen(xpath), - sizeof(xpath) - strlen(xpath), "/%s", - snode->name); - } - - nb_node = snode->priv; - switch (snode->nodetype) { - case LYS_CONTAINER: - ret = nb_oper_data_iter_container(nb_node, xpath, list_entry, - list_keys, translator, flags, - cb, arg); - break; - case LYS_LEAF: - ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry, - list_keys, translator, flags, cb, - arg); - break; - case LYS_LEAFLIST: - ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry, - list_keys, translator, flags, - cb, arg); - break; - case LYS_LIST: - ret = nb_oper_data_iter_list(nb_node, xpath, list_entry, - list_keys, translator, flags, cb, - arg); - break; - case LYS_USES: - ret = nb_oper_data_iter_children(snode, xpath, list_entry, - list_keys, translator, false, - flags, cb, arg); - break; - default: - break; - } - - return ret; -} - -int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator, - uint32_t flags, nb_oper_data_cb cb, void *arg) -{ - struct nb_node *nb_node; - const void *list_entry = NULL; - struct yang_list_keys list_keys; - struct list *list_dnodes; - struct lyd_node *dnode, *dn; - struct listnode *ln; - int ret; - - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - return NB_ERR; - } - - /* For now this function works only with containers and lists. */ - if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) { - flog_warn( - EC_LIB_NB_OPERATIONAL_DATA, - "%s: can't iterate over YANG leaf or leaf-list [xpath %s]", - __func__, xpath); - return NB_ERR; - } - - /* - * Create a data tree from the XPath so that we can parse the keys of - * all YANG lists (if any). - */ - - LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, - LYD_NEW_PATH_UPDATE, NULL, &dnode); - if (err || !dnode) { - const char *errmsg = - err ? ly_errmsg(ly_native_ctx) : "node not found"; - flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s", - __func__, errmsg); - return NB_ERR; - } - - /* - * Create a linked list to sort the data nodes starting from the root. - */ - list_dnodes = list_new(); - for (dn = dnode; dn; dn = lyd_parent(dn)) { - if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn)) - continue; - listnode_add_head(list_dnodes, dn); - } - /* - * Use the northbound callbacks to find list entry pointer corresponding - * to the given XPath. - */ - for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) { - struct lyd_node *child; - struct nb_node *nn; - unsigned int n = 0; - - /* Obtain the list entry keys. */ - memset(&list_keys, 0, sizeof(list_keys)); - LY_LIST_FOR (lyd_child(dn), child) { - if (!lysc_is_key(child->schema)) - break; - strlcpy(list_keys.key[n], - yang_dnode_get_string(child, NULL), - sizeof(list_keys.key[n])); - n++; - } - list_keys.num = n; - if (list_keys.num != yang_snode_num_keys(dn->schema)) { - list_delete(&list_dnodes); - yang_dnode_free(dnode); - return NB_ERR_NOT_FOUND; - } - - /* Find the list entry pointer. */ - nn = dn->schema->priv; - if (!nn->cbs.lookup_entry) { - flog_warn( - EC_LIB_NB_OPERATIONAL_DATA, - "%s: data path doesn't support iteration over operational data: %s", - __func__, xpath); - list_delete(&list_dnodes); - yang_dnode_free(dnode); - return NB_ERR; - } - - list_entry = - nb_callback_lookup_entry(nn, list_entry, &list_keys); - if (list_entry == NULL) { - list_delete(&list_dnodes); - yang_dnode_free(dnode); - return NB_ERR_NOT_FOUND; - } - } - - /* If a list entry was given, iterate over that list entry only. */ - if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode)) - ret = nb_oper_data_iter_children( - nb_node->snode, xpath, list_entry, &list_keys, - translator, true, flags, cb, arg); - else - ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry, - &list_keys, translator, true, - flags, cb, arg); - - list_delete(&list_dnodes); - yang_dnode_free(dnode); - - return ret; -} - -bool nb_operation_is_valid(enum nb_operation operation, - const struct lysc_node *snode) +bool nb_cb_operation_is_valid(enum nb_cb_operation operation, + const struct lysc_node *snode) { struct nb_node *nb_node = snode->priv; struct lysc_node_container *scontainer; struct lysc_node_leaf *sleaf; switch (operation) { - case NB_OP_CREATE: + case NB_CB_CREATE: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; @@ -2233,6 +1938,8 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; break; case LYS_CONTAINER: + if (snode->parent && snode->parent->nodetype == LYS_CASE) + return true; scontainer = (struct lysc_node_container *)snode; if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE)) return false; @@ -2244,7 +1951,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_MODIFY: + case NB_CB_MODIFY: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; @@ -2262,7 +1969,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_DESTROY: + case NB_CB_DESTROY: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; @@ -2287,6 +1994,8 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; break; case LYS_CONTAINER: + if (snode->parent && snode->parent->nodetype == LYS_CASE) + return true; scontainer = (struct lysc_node_container *)snode; if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE)) return false; @@ -2298,7 +2007,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_MOVE: + case NB_CB_MOVE: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; @@ -2312,12 +2021,12 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_PRE_VALIDATE: - case NB_OP_APPLY_FINISH: + case NB_CB_PRE_VALIDATE: + case NB_CB_APPLY_FINISH: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; return true; - case NB_OP_GET_ELEM: + case NB_CB_GET_ELEM: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) return false; @@ -2334,7 +2043,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_GET_NEXT: + case NB_CB_GET_NEXT: switch (snode->nodetype) { case LYS_LIST: if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) @@ -2348,8 +2057,8 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_GET_KEYS: - case NB_OP_LOOKUP_ENTRY: + case NB_CB_GET_KEYS: + case NB_CB_LOOKUP_ENTRY: switch (snode->nodetype) { case LYS_LIST: if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) @@ -2361,7 +2070,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; - case NB_OP_RPC: + case NB_CB_RPC: if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R)) return false; @@ -2373,6 +2082,10 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; + case NB_CB_NOTIFY: + if (snode->nodetype != LYS_NOTIF) + return false; + return true; default: return false; } @@ -2383,17 +2096,81 @@ DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments), int nb_notification_send(const char *xpath, struct list *arguments) { + struct lyd_node *root = NULL; + struct lyd_node *dnode; + struct yang_data *data; + struct listnode *ln; + LY_ERR err; int ret; DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath); + /* + * Call old hook functions + */ ret = hook_call(nb_notification_send, xpath, arguments); + + if (!hook_have_hooks(nb_notification_tree_send)) + goto done; + /* + * Convert yang data arguments list to a libyang data tree for new hook + * functions. + */ + for (ALL_LIST_ELEMENTS_RO(arguments, ln, data)) { + err = lyd_new_path(root, ly_native_ctx, data->xpath, + data->value, LYD_NEW_PATH_UPDATE, &dnode); + if (err != LY_SUCCESS) + goto lyerr; + if (!root) { + root = dnode; + while (root->parent) + root = lyd_parent(root); + } + } + + if (!root) { + err = lyd_new_path(NULL, ly_native_ctx, xpath, "", 0, &root); + if (err) { +lyerr: + flog_err(EC_LIB_LIBYANG, + "%s: error creating notification data: %s", + __func__, ly_strerrcode(err)); + ret += 1; + goto done; + } + } + + /* + * Call new hook functions + */ + ret += nb_notification_tree_send(xpath, root); + +done: + if (root) + lyd_free_all(root); if (arguments) list_delete(&arguments); return ret; } +DEFINE_HOOK(nb_notification_tree_send, + (const char *xpath, const struct lyd_node *tree), (xpath, tree)); + +int nb_notification_tree_send(const char *xpath, const struct lyd_node *tree) +{ + int ret; + + assert(tree); + + DEBUGD(&nb_dbg_notif, "northbound tree notification: %s", + tree->schema->name); + + ret = hook_call(nb_notification_tree_send, xpath, tree); + + return ret; +} + /* Running configuration user pointers management. */ struct nb_config_entry { char xpath[XPATH_MAXLEN]; @@ -2563,31 +2340,33 @@ const char *nb_event_name(enum nb_event event) assert(!"Reached end of function we should never hit"); } -const char *nb_operation_name(enum nb_operation operation) +const char *nb_cb_operation_name(enum nb_cb_operation operation) { switch (operation) { - case NB_OP_CREATE: + case NB_CB_CREATE: return "create"; - case NB_OP_MODIFY: + case NB_CB_MODIFY: return "modify"; - case NB_OP_DESTROY: + case NB_CB_DESTROY: return "destroy"; - case NB_OP_MOVE: + case NB_CB_MOVE: return "move"; - case NB_OP_PRE_VALIDATE: + case NB_CB_PRE_VALIDATE: return "pre_validate"; - case NB_OP_APPLY_FINISH: + case NB_CB_APPLY_FINISH: return "apply_finish"; - case NB_OP_GET_ELEM: + case NB_CB_GET_ELEM: return "get_elem"; - case NB_OP_GET_NEXT: + case NB_CB_GET_NEXT: return "get_next"; - case NB_OP_GET_KEYS: + case NB_CB_GET_KEYS: return "get_keys"; - case NB_OP_LOOKUP_ENTRY: + case NB_CB_LOOKUP_ENTRY: return "lookup_entry"; - case NB_OP_RPC: + case NB_CB_RPC: return "rpc"; + case NB_CB_NOTIFY: + return "notify"; } assert(!"Reached end of function we should never hit"); @@ -2612,6 +2391,8 @@ const char *nb_err_name(enum nb_error error) return "failed to allocate resource"; case NB_ERR_INCONSISTENCY: return "internal inconsistency"; + case NB_YIELD: + return "should yield"; } assert(!"Reached end of function we should never hit"); @@ -2643,10 +2424,6 @@ const char *nb_client_name(enum nb_client client) static void nb_load_callbacks(const struct frr_yang_module_info *module) { - - if (module->ignore_cbs) - return; - for (size_t i = 0; module->nodes[i].xpath; i++) { struct nb_node *nb_node; uint32_t priority; @@ -2712,7 +2489,8 @@ void nb_init(struct event_loop *tm, for (size_t i = 0; i < nmodules; i++) { DEBUGD(&nb_dbg_events, "northbound: loading %s.yang", modules[i]->name); - *loadedp++ = yang_module_load(modules[i]->name); + *loadedp++ = yang_module_load(modules[i]->name, + modules[i]->features); } if (explicit_compile) @@ -2737,10 +2515,15 @@ void nb_init(struct event_loop *tm, /* Initialize the northbound CLI. */ nb_cli_init(tm); + + /* Initialize oper-state */ + nb_oper_init(tm); } void nb_terminate(void) { + nb_oper_terminate(); + /* Terminate the northbound CLI. */ nb_cli_terminate(); diff --git a/lib/northbound.h b/lib/northbound.h index 1723a87..5be111c 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -83,28 +83,23 @@ enum nb_event { }; /* - * Northbound operations. + * Northbound callback operations. * * Refer to the documentation comments of nb_callbacks for more details. */ -enum nb_operation { - NB_OP_CREATE, - NB_OP_MODIFY, - NB_OP_DESTROY, - NB_OP_MOVE, - NB_OP_PRE_VALIDATE, - NB_OP_APPLY_FINISH, - NB_OP_GET_ELEM, - NB_OP_GET_NEXT, - NB_OP_GET_KEYS, - NB_OP_LOOKUP_ENTRY, - NB_OP_RPC, -}; - -struct nb_cfg_change { - char xpath[XPATH_MAXLEN]; - enum nb_operation operation; - const char *value; +enum nb_cb_operation { + NB_CB_CREATE, + NB_CB_MODIFY, + NB_CB_DESTROY, + NB_CB_MOVE, + NB_CB_PRE_VALIDATE, + NB_CB_APPLY_FINISH, + NB_CB_GET_ELEM, + NB_CB_GET_NEXT, + NB_CB_GET_KEYS, + NB_CB_LOOKUP_ENTRY, + NB_CB_RPC, + NB_CB_NOTIFY, }; union nb_resource { @@ -292,6 +287,18 @@ struct nb_cb_rpc_args { size_t errmsg_len; }; +struct nb_cb_notify_args { + /* XPath of the notification. */ + const char *xpath; + + /* + * libyang data node representing the notification. If the notification + * is not top-level, it still points to the notification node, but it's + * part of the full data tree with all its parents. + */ + struct lyd_node *dnode; +}; + /* * Set of configuration callbacks that can be associated to a northbound node. */ @@ -484,6 +491,22 @@ struct nb_callbacks { */ const void *(*lookup_entry)(struct nb_cb_lookup_entry_args *args); + /* + * Operational data callback for YANG lists. + * + * The callback function should return the next list entry that would + * follow a list entry with the keys given as a parameter. Keyless + * lists don't need to implement this callback. + * + * args + * Refer to the documentation comments of nb_cb_lookup_entry_args for + * details. + * + * Returns: + * Pointer to the list entry if found, or NULL if not found. + */ + const void *(*lookup_next)(struct nb_cb_lookup_entry_args *args); + /* * RPC and action callback. * @@ -499,6 +522,17 @@ struct nb_callbacks { */ int (*rpc)(struct nb_cb_rpc_args *args); + /* + * Notification callback. + * + * The callback is called when a YANG notification is received. + * + * args + * Refer to the documentation comments of nb_cb_notify_args for + * details. + */ + void (*notify)(struct nb_cb_notify_args *args); + /* * Optional callback to compare the data nodes when printing * the CLI commands associated with them. @@ -597,8 +631,8 @@ struct nb_node { #define F_NB_NODE_CONFIG_ONLY 0x01 /* The YANG list doesn't contain key leafs. */ #define F_NB_NODE_KEYLESS_LIST 0x02 -/* Ignore callbacks for this node */ -#define F_NB_NODE_IGNORE_CBS 0x04 +/* Ignore config callbacks for this node */ +#define F_NB_NODE_IGNORE_CFG_CBS 0x04 /* * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays @@ -612,10 +646,19 @@ struct frr_yang_module_info { const char *name; /* - * Ignore callbacks for this module. Set this to true to - * load module without any callbacks. + * Ignore configuration callbacks for this module. Set this to true to + * load module with only CLI-related callbacks. This is useful for + * modules loaded in mgmtd. */ - bool ignore_cbs; + bool ignore_cfg_cbs; + + /* + * The NULL-terminated list of supported features. + * Features are defined with "feature" statements in the YANG model. + * Use ["*", NULL] to enable all features. + * Use NULL to disable all features. + */ + const char **features; /* Northbound callbacks. */ const struct { @@ -644,6 +687,7 @@ enum nb_error { NB_ERR_VALIDATION, NB_ERR_RESOURCE, NB_ERR_INCONSISTENCY, + NB_YIELD, }; /* Default priority. */ @@ -676,7 +720,7 @@ struct nb_context { /* Northbound configuration callback. */ struct nb_config_cb { RB_ENTRY(nb_config_cb) entry; - enum nb_operation operation; + enum nb_cb_operation operation; uint32_t seq; const struct nb_node *nb_node; const struct lyd_node *dnode; @@ -703,7 +747,26 @@ struct nb_transaction { struct nb_config { struct lyd_node *dnode; uint32_t version; - struct nb_config_cbs cfg_chgs; +}; + +/* + * Northbound operations. The semantics of operations is explained in RFC 8072, + * section 2.5: https://datatracker.ietf.org/doc/html/rfc8072#section-2.5. + */ +enum nb_operation { + NB_OP_CREATE_EXCL, /* "create" */ + NB_OP_CREATE, /* "merge" - kept for backward compatibility */ + NB_OP_MODIFY, /* "merge" */ + NB_OP_DESTROY, /* "remove" */ + NB_OP_DELETE, /* "delete" */ + NB_OP_REPLACE, /* "replace" */ + NB_OP_MOVE, /* "move" */ +}; + +struct nb_cfg_change { + char xpath[XPATH_MAXLEN]; + enum nb_operation operation; + const char *value; }; /* Callback function used by nb_oper_data_iterate(). */ @@ -711,12 +774,37 @@ typedef int (*nb_oper_data_cb)(const struct lysc_node *snode, struct yang_translator *translator, struct yang_data *data, void *arg); +/** + * nb_oper_data_finish_cb() - finish a portion or all of a oper data walk. + * @tree - r/o copy of the tree created during this portion of the walk. + * @arg - finish arg passed to nb_op_iterate_yielding. + * @ret - NB_OK if done with walk, NB_YIELD if done with portion, otherwise an + * error. + * + * If nb_op_iterate_yielding() was passed with @should_batch set then this + * callback will be invoked during each portion (batch) of the walk. + * + * The @tree is read-only and should not be modified or freed. + * + * If this function returns anything but NB_OK then the walk will be terminated. + * and this function will not be called again regardless of if @ret was + * `NB_YIELD` or not. + * + * Return: NB_OK to continue or complete the walk normally, otherwise an error + * to immediately terminate the walk. + */ +/* Callback function used by nb_oper_data_iter_yielding(). */ +typedef enum nb_error (*nb_oper_data_finish_cb)(const struct lyd_node *tree, + void *arg, enum nb_error ret); + /* Iterate over direct child nodes only. */ #define NB_OPER_DATA_ITER_NORECURSE 0x0001 /* Hooks. */ DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments), (xpath, arguments)); +DECLARE_HOOK(nb_notification_tree_send, + (const char *xpath, const struct lyd_node *tree), (xpath, tree)); DECLARE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty)); DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set)); @@ -724,6 +812,7 @@ DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set)); extern struct debug nb_dbg_cbs_config; extern struct debug nb_dbg_cbs_state; extern struct debug nb_dbg_cbs_rpc; +extern struct debug nb_dbg_cbs_notify; extern struct debug nb_dbg_notif; extern struct debug nb_dbg_events; extern struct debug nb_dbg_libyang; @@ -744,9 +833,16 @@ extern int nb_callback_get_keys(const struct nb_node *nb_node, extern const void *nb_callback_lookup_entry(const struct nb_node *nb_node, const void *parent_list_entry, const struct yang_list_keys *keys); +extern const void *nb_callback_lookup_node_entry(struct lyd_node *node, + const void *parent_list_entry); +extern const void *nb_callback_lookup_next(const struct nb_node *nb_node, + const void *parent_list_entry, + const struct yang_list_keys *keys); extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, const struct list *input, struct list *output, char *errmsg, size_t errmsg_len); +extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, + struct lyd_node *dnode); /* * Create a northbound node for all YANG schema nodes. @@ -769,6 +865,14 @@ void nb_nodes_delete(void); */ extern struct nb_node *nb_node_find(const char *xpath); +/** + * nb_nodes_find() - find the NB nodes corresponding to complex xpath. + * @xpath: XPath to search for (with or without predicates). + * + * Return: a dynamic array (darr) of `struct nb_node *`s. + */ +extern struct nb_node **nb_nodes_find(const char *xpath); + extern void nb_node_set_dependency_cbs(const char *dependency_xpath, const char *dependant_xpath, struct nb_dependency_callbacks *cbs); @@ -842,6 +946,32 @@ extern void nb_config_replace(struct nb_config *config_dst, struct nb_config *config_src, bool preserve_source); +/* + * Return a human-readable string representing a northbound operation. + * + * operation + * Northbound operation. + * + * Returns: + * String representation of the given northbound operation. + */ +extern const char *nb_operation_name(enum nb_operation operation); + +/* + * Validate if the northbound operation is allowed for the given node. + * + * nb_node + * Northbound node. + * + * operation + * Operation we want to check. + * + * Returns: + * true if the operation is allowed, false otherwise. + */ +extern bool nb_is_operation_allowed(struct nb_node *nb_node, + enum nb_operation oper); + /* * Edit a candidate configuration. * @@ -866,7 +996,6 @@ extern void nb_config_replace(struct nb_config *config_dst, * * Returns: * - NB_OK on success. - * - NB_ERR_NOT_FOUND when the element to be deleted was not found. * - NB_ERR for other errors. */ extern int nb_candidate_edit(struct nb_config *candidate, @@ -917,11 +1046,8 @@ extern bool nb_candidate_needs_update(const struct nb_config *candidate); * xpath_base * Base xpath for config. * - * curr_xpath - * Current xpath for config. - * - * xpath_index - * Index of xpath being processed. + * in_backend + * Specify whether the changes are being applied in the backend or not. * * err_buf * Buffer to store human-readable error message in case of error. @@ -932,11 +1058,18 @@ extern bool nb_candidate_needs_update(const struct nb_config *candidate); * error * TRUE on error, FALSE on success */ -extern void nb_candidate_edit_config_changes( - struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[], - size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath, - int xpath_index, char *err_buf, int err_bufsize, bool *error); - +extern void nb_candidate_edit_config_changes(struct nb_config *candidate_config, + struct nb_cfg_change cfg_changes[], + size_t num_cfg_changes, + const char *xpath_base, + bool in_backend, char *err_buf, + int err_bufsize, bool *error); + + +extern void nb_config_diff_add_change(struct nb_config_cbs *changes, + enum nb_cb_operation operation, + uint32_t *seq, + const struct lyd_node *dnode); /* * Delete candidate configuration changes. * @@ -1258,7 +1391,7 @@ extern int nb_running_unlock(enum nb_client client, const void *user); extern int nb_running_lock_check(enum nb_client client, const void *user); /* - * Iterate over operational data. + * Iterate over operational data -- deprecated. * * xpath * Data path of the YANG data we want to iterate over. @@ -1269,21 +1402,60 @@ extern int nb_running_lock_check(enum nb_client client, const void *user); * flags * NB_OPER_DATA_ITER_ flags to control how the iteration is performed. * + * should_batch + * Should call finish cb with partial results (i.e., creating batches) + * * cb * Function to call with each data node. * * arg * Arbitrary argument passed as the fourth parameter in each call to 'cb'. * + * tree + * If non-NULL will contain the data tree built from the walk. + * * Returns: * NB_OK on success, NB_ERR otherwise. */ -extern int nb_oper_data_iterate(const char *xpath, - struct yang_translator *translator, - uint32_t flags, nb_oper_data_cb cb, void *arg); +extern enum nb_error nb_oper_iterate_legacy(const char *xpath, + struct yang_translator *translator, + uint32_t flags, nb_oper_data_cb cb, + void *arg, struct lyd_node **tree); + +/** + * nb_oper_walk() - walk the schema building operational state. + * @xpath - + * @translator - + * @flags - + * @should_batch - should allow yielding and processing portions of the tree. + * @cb - callback invoked for each non-list, non-container node. + * @arg - arg to pass to @cb. + * @finish - function to call when done with portion or all of walk. + * @finish_arg - arg to pass to @finish. + * + * Return: walk - a cookie that can be used to cancel the walk. + */ +extern void *nb_oper_walk(const char *xpath, struct yang_translator *translator, + uint32_t flags, bool should_batch, nb_oper_data_cb cb, + void *arg, nb_oper_data_finish_cb finish, + void *finish_arg); + +/** + * nb_oper_cancel_walk() - cancel the in progress walk. + * @walk - value returned from nb_op_iterate_yielding() + * + * Should only be called on an in-progress walk. It is invalid to cancel and + * already finished walk. The walks `finish` callback will not be called. + */ +extern void nb_oper_cancel_walk(void *walk); + +/** + * nb_op_cancel_all_walks() - cancel all in progress walks. + */ +extern void nb_oper_cancel_all_walks(void); /* - * Validate if the northbound operation is valid for the given node. + * Validate if the northbound callback operation is valid for the given node. * * operation * Operation we want to check. @@ -1294,10 +1466,14 @@ extern int nb_oper_data_iterate(const char *xpath, * Returns: * true if the operation is valid, false otherwise. */ -extern bool nb_operation_is_valid(enum nb_operation operation, - const struct lysc_node *snode); +extern bool nb_cb_operation_is_valid(enum nb_cb_operation operation, + const struct lysc_node *snode); /* + * DEPRECATED: This call and infra should no longer be used. Instead, + * the mgmtd supported tree based call `nb_notification_tree_send` should be + * used instead + * * Send a YANG notification. This is a no-op unless the 'nb_notification_send' * hook was registered by a northbound plugin. * @@ -1313,6 +1489,22 @@ extern bool nb_operation_is_valid(enum nb_operation operation, */ extern int nb_notification_send(const char *xpath, struct list *arguments); +/* + * Send a YANG notification from a backend . This is a no-op unless th + * 'nb_notification_tree_send' hook was registered by a northbound plugin. + * + * xpath + * XPath of the YANG notification. + * + * tree + * The libyang tree for the notification. + * + * Returns: + * NB_OK on success, NB_ERR otherwise. + */ +extern int nb_notification_tree_send(const char *xpath, + const struct lyd_node *tree); + /* * Associate a user pointer to a configuration node. * @@ -1419,15 +1611,15 @@ extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, extern const char *nb_event_name(enum nb_event event); /* - * Return a human-readable string representing a northbound operation. + * Return a human-readable string representing a northbound callback operation. * * operation - * Northbound operation. + * Northbound callback operation. * * Returns: - * String representation of the given northbound operation. + * String representation of the given northbound callback operation. */ -extern const char *nb_operation_name(enum nb_operation operation); +extern const char *nb_cb_operation_name(enum nb_cb_operation operation); /* * Return a human-readable string representing a northbound error. @@ -1488,6 +1680,9 @@ extern void nb_init(struct event_loop *tm, */ extern void nb_terminate(void); +extern void nb_oper_init(struct event_loop *loop); +extern void nb_oper_terminate(void); + #ifdef __cplusplus } #endif diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 8003679..8809ec2 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -5,6 +5,7 @@ */ #include +#include #include "libfrr.h" #include "lib/version.h" @@ -24,6 +25,7 @@ struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"}; struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"}; struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"}; +struct debug nb_dbg_cbs_notify = {0, "Northbound callbacks: notifications"}; struct debug nb_dbg_notif = {0, "Northbound notifications"}; struct debug nb_dbg_events = {0, "Northbound events"}; struct debug nb_dbg_libyang = {0, "libyang debugging"}; @@ -145,10 +147,9 @@ static int nb_cli_apply_changes_internal(struct vty *vty, VTY_CHECK_XPATH; - nb_candidate_edit_config_changes( - vty->candidate_config, vty->cfg_changes, vty->num_cfg_changes, - xpath_base, VTY_CURR_XPATH, vty->xpath_index, buf, sizeof(buf), - &error); + nb_candidate_edit_config_changes(vty->candidate_config, vty->cfg_changes, + vty->num_cfg_changes, xpath_base, + false, buf, sizeof(buf), &error); if (error) { /* * Failure to edit the candidate configuration should never @@ -180,8 +181,26 @@ static int nb_cli_apply_changes_internal(struct vty *vty, return CMD_SUCCESS; } +static void create_xpath_base_abs(struct vty *vty, char *xpath_base_abs, + size_t xpath_base_abs_size, + const char *xpath_base) +{ + memset(xpath_base_abs, 0, xpath_base_abs_size); + + if (xpath_base[0] == 0) + xpath_base = "."; + + /* If base xpath is relative, prepend current vty xpath. */ + if (vty->xpath_index > 0 && xpath_base[0] == '.') { + strlcpy(xpath_base_abs, VTY_CURR_XPATH, xpath_base_abs_size); + xpath_base++; /* skip '.' */ + } + strlcat(xpath_base_abs, xpath_base, xpath_base_abs_size); +} + int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) { + char xpath_base_abs[XPATH_MAXLEN] = {}; char xpath_base[XPATH_MAXLEN] = {}; bool implicit_commit; int ret; @@ -195,6 +214,9 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) va_end(ap); } + create_xpath_base_abs(vty, xpath_base_abs, sizeof(xpath_base_abs), + xpath_base); + if (vty_mgmt_should_process_cli_apply_changes(vty)) { VTY_CHECK_XPATH; @@ -202,18 +224,20 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) return CMD_SUCCESS; implicit_commit = vty_needs_implicit_commit(vty); - ret = vty_mgmt_send_config_data(vty, implicit_commit); + ret = vty_mgmt_send_config_data(vty, xpath_base_abs, + implicit_commit); if (ret >= 0 && !implicit_commit) vty->mgmt_num_pending_setcfg++; return ret; } - return nb_cli_apply_changes_internal(vty, xpath_base, false); + return nb_cli_apply_changes_internal(vty, xpath_base_abs, false); } int nb_cli_apply_changes_clear_pending(struct vty *vty, const char *xpath_base_fmt, ...) { + char xpath_base_abs[XPATH_MAXLEN] = {}; char xpath_base[XPATH_MAXLEN] = {}; bool implicit_commit; int ret; @@ -227,6 +251,9 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty, va_end(ap); } + create_xpath_base_abs(vty, xpath_base_abs, sizeof(xpath_base_abs), + xpath_base); + if (vty_mgmt_should_process_cli_apply_changes(vty)) { VTY_CHECK_XPATH; /* @@ -238,13 +265,14 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty, * conversions to mgmtd require full proper implementations. */ implicit_commit = vty_needs_implicit_commit(vty); - ret = vty_mgmt_send_config_data(vty, implicit_commit); + ret = vty_mgmt_send_config_data(vty, xpath_base_abs, + implicit_commit); if (ret >= 0 && !implicit_commit) vty->mgmt_num_pending_setcfg++; return ret; } - return nb_cli_apply_changes_internal(vty, xpath_base, true); + return nb_cli_apply_changes_internal(vty, xpath_base_abs, true); } int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, @@ -1411,11 +1439,9 @@ static int nb_cli_oper_data_cb(const struct lysc_node *snode, } exit: - yang_data_free(data); return NB_OK; error: - yang_data_free(data); return NB_ERR; } @@ -1464,9 +1490,14 @@ DEFPY (show_yang_operational_data, ly_ctx = ly_native_ctx; /* Obtain data. */ - dnode = yang_dnode_new(ly_ctx, false); - ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, - dnode); + if (translator) { + dnode = yang_dnode_new(ly_ctx, false); + ret = nb_oper_iterate_legacy(xpath, translator, 0, + nb_cli_oper_data_cb, dnode, NULL); + } else { + dnode = NULL; + ret = nb_oper_iterate_legacy(xpath, NULL, 0, NULL, NULL, &dnode); + } if (ret != NB_OK) { if (format == LYD_JSON) vty_out(vty, "{}\n"); @@ -1474,7 +1505,8 @@ DEFPY (show_yang_operational_data, /* embed ly_last_errmsg() when we get newer libyang */ vty_out(vty, "\n"); } - yang_dnode_free(dnode); + if (dnode) + yang_dnode_free(dnode); return CMD_WARNING; } @@ -1741,13 +1773,15 @@ DEFPY (rollback_config, /* Debug CLI commands. */ static struct debug *nb_debugs[] = { &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc, - &nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang, + &nb_dbg_cbs_notify, &nb_dbg_notif, &nb_dbg_events, + &nb_dbg_libyang, }; static const char *const nb_debugs_conflines[] = { "debug northbound callbacks configuration", "debug northbound callbacks state", "debug northbound callbacks rpc", + "debug northbound callbacks notify", "debug northbound notifications", "debug northbound events", "debug northbound libyang", @@ -1772,7 +1806,7 @@ DEFPY (debug_nb, debug_nb_cmd, "[no] debug northbound\ [<\ - callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\ + callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc|notify$cbs_notify}]\ |notifications$notifications\ |events$events\ |libyang$libyang\ @@ -1785,13 +1819,14 @@ DEFPY (debug_nb, "State\n" "RPC\n" "Notifications\n" + "Notifications\n" "Events\n" "libyang debugging\n") { uint32_t mode = DEBUG_NODE2MODE(vty->node); if (cbs) { - bool none = (!cbs_cfg && !cbs_state && !cbs_rpc); + bool none = (!cbs_cfg && !cbs_state && !cbs_rpc && !cbs_notify); if (none || cbs_cfg) DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no); @@ -1799,6 +1834,8 @@ DEFPY (debug_nb, DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no); if (none || cbs_rpc) DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no); + if (none || cbs_notify) + DEBUG_MODE_SET(&nb_dbg_cbs_notify, mode, !no); } if (notifications) DEBUG_MODE_SET(&nb_dbg_notif, mode, !no); diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index c8f8a84..1a45794 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -32,7 +32,7 @@ extern struct nb_config *vty_shared_candidate_config; * XPath (absolute or relative) of the configuration option being edited. * * operation - * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or NB_OP_DELETE). + * Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or NB_OP_DESTROY). * * value * New value of the configuration option. Should be NULL for typeless YANG diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 34406a1..8503d18 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -221,7 +221,7 @@ frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, nb_op = NB_OP_DESTROY; break; case MOP_VALUE_SET: - if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) + if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY)) nb_op = NB_OP_MODIFY; else /* Ignore list keys modifications. */ diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 6c33351..7957752 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -427,25 +427,11 @@ static struct lyd_node *get_dnode_config(const std::string &path) return dnode; } -static int get_oper_data_cb(const struct lysc_node *snode, - struct yang_translator *translator, - struct yang_data *data, void *arg) -{ - struct lyd_node *dnode = static_cast(arg); - int ret = yang_dnode_edit(dnode, data->xpath, data->value); - yang_data_free(data); - - return (ret == 0) ? NB_OK : NB_ERR; -} - static struct lyd_node *get_dnode_state(const std::string &path) { - struct lyd_node *dnode = yang_dnode_new(ly_native_ctx, false); - if (nb_oper_data_iterate(path.c_str(), NULL, 0, get_oper_data_cb, dnode) - != NB_OK) { - yang_dnode_free(dnode); - return NULL; - } + struct lyd_node *dnode = NULL; + + (void)nb_oper_iterate_legacy(path.c_str(), NULL, 0, NULL, NULL, &dnode); return dnode; } diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c new file mode 100644 index 0000000..2394b5e --- /dev/null +++ b/lib/northbound_oper.c @@ -0,0 +1,1857 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * October 14 2023, Christian Hopps + * + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ + +#include +#include "darr.h" +#include "debug.h" +#include "frrevent.h" +#include "frrstr.h" +#include "lib_errors.h" +#include "monotime.h" +#include "northbound.h" + +/* + * YANG model yielding design restrictions: + * + * In order to be able to yield and guarantee we have a valid data tree at the + * point of yielding we must know that each parent has all it's siblings + * collected to represent a complete element. + * + * Basically, there should be a only single branch in the schema tree that + * supports yielding. In practice this means: + * + * list node schema with lookup next: + * - must not have any lookup-next list node sibling schema + * - must not have any list or container node siblings with lookup-next descendants. + * - any parent list nodes must also be lookup-next list nodes + * + * We must also process containers with lookup-next descendants last. + */ + +DEFINE_MTYPE_STATIC(LIB, NB_YIELD_STATE, "NB Yield State"); +DEFINE_MTYPE_STATIC(LIB, NB_NODE_INFOS, "NB Node Infos"); + +/* Amount of time allowed to spend constructing oper-state prior to yielding */ +#define NB_OP_WALK_INTERVAL_MS 50 +#define NB_OP_WALK_INTERVAL_US (NB_OP_WALK_INTERVAL_MS * 1000) + +/* ---------- */ +/* Data Types */ +/* ---------- */ +PREDECL_LIST(nb_op_walks); + +/* + * This is our information about a node on the branch we are looking at + */ +struct nb_op_node_info { + struct lyd_node *inner; + const struct lysc_node *schema; /* inner schema in case we rm inner */ + struct yang_list_keys keys; /* if list, keys to locate element */ + const void *list_entry; /* opaque entry from user or NULL */ + uint xpath_len; /* length of the xpath string for this node */ + uint niters; /* # list elems create this iteration */ + uint nents; /* # list elems create so far */ + bool query_specific_entry : 1; /* this info is specific specified */ + bool has_lookup_next : 1; /* if this node support lookup next */ + bool lookup_next_ok : 1; /* if this and all previous support */ +}; + +/** + * struct nb_op_yield_state - tracking required state for yielding. + * + * @xpath: current xpath representing the node_info stack. + * @xpath_orig: the original query string from the user + * @node_infos: the container stack for the walk from root to current + * @schema_path: the schema nodes along the path indicated by the query string. + * this will include the choice and case nodes which are not + * present in the query string. + * @query_tokstr: the query string tokenized with NUL bytes. + * @query_tokens: the string pointers to each query token (node). + * @non_specific_predicate: tracks if a query_token is non-specific predicate. + * @walk_root_level: The topmost specific node, +1 is where we start walking. + * @walk_start_level: @walk_root_level + 1. + * @query_base_level: the level the query string stops at and full walks + * commence below that. + */ +struct nb_op_yield_state { + /* Walking state */ + char *xpath; + char *xpath_orig; + struct nb_op_node_info *node_infos; + const struct lysc_node **schema_path; + char *query_tokstr; + char **query_tokens; + uint8_t *non_specific_predicate; + int walk_root_level; + int walk_start_level; + int query_base_level; + bool query_list_entry; /* XXX query was for a specific list entry */ + + /* Yielding state */ + bool query_did_entry; /* currently processing the entry */ + bool should_batch; + struct timeval start_time; + struct yang_translator *translator; + uint32_t flags; + nb_oper_data_cb cb; + void *cb_arg; + nb_oper_data_finish_cb finish; + void *finish_arg; + struct event *walk_ev; + struct nb_op_walks_item link; +}; + +DECLARE_LIST(nb_op_walks, struct nb_op_yield_state, link); + +/* ---------------- */ +/* Global Variables */ +/* ---------------- */ + +static struct event_loop *event_loop; +static struct nb_op_walks_head nb_op_walks; + +/* --------------------- */ +/* Function Declarations */ +/* --------------------- */ + +static enum nb_error nb_op_yield(struct nb_op_yield_state *ys); +static struct lyd_node *ys_root_node(struct nb_op_yield_state *ys); + +/* -------------------- */ +/* Function Definitions */ +/* -------------------- */ + +static inline struct nb_op_yield_state * +nb_op_create_yield_state(const char *xpath, struct yang_translator *translator, + uint32_t flags, bool should_batch, nb_oper_data_cb cb, + void *cb_arg, nb_oper_data_finish_cb finish, + void *finish_arg) +{ + struct nb_op_yield_state *ys; + + ys = XCALLOC(MTYPE_NB_YIELD_STATE, sizeof(*ys)); + ys->xpath = darr_strdup_cap(xpath, (size_t)XPATH_MAXLEN); + ys->xpath_orig = darr_strdup(xpath); + ys->translator = translator; + ys->flags = flags; + ys->should_batch = should_batch; + ys->cb = cb; + ys->cb_arg = cb_arg; + ys->finish = finish; + ys->finish_arg = finish_arg; + + nb_op_walks_add_tail(&nb_op_walks, ys); + + return ys; +} + +static inline void nb_op_free_yield_state(struct nb_op_yield_state *ys, + bool nofree_tree) +{ + if (ys) { + EVENT_OFF(ys->walk_ev); + nb_op_walks_del(&nb_op_walks, ys); + /* if we have a branch then free up it's libyang tree */ + if (!nofree_tree && ys_root_node(ys)) + lyd_free_all(ys_root_node(ys)); + darr_free(ys->query_tokens); + darr_free(ys->non_specific_predicate); + darr_free(ys->query_tokstr); + darr_free(ys->schema_path); + darr_free(ys->node_infos); + darr_free(ys->xpath_orig); + darr_free(ys->xpath); + XFREE(MTYPE_NB_YIELD_STATE, ys); + } +} + +static const struct lysc_node *ys_get_walk_stem_tip(struct nb_op_yield_state *ys) +{ + if (ys->walk_start_level <= 0) + return NULL; + return ys->node_infos[ys->walk_start_level - 1].schema; +} + +static struct lyd_node *ys_root_node(struct nb_op_yield_state *ys) +{ + if (!darr_len(ys->node_infos)) + return NULL; + return ys->node_infos[0].inner; +} + +static void ys_trim_xpath(struct nb_op_yield_state *ys) +{ + uint len = darr_len(ys->node_infos); + + if (len == 0) + darr_setlen(ys->xpath, 1); + else + darr_setlen(ys->xpath, darr_last(ys->node_infos)->xpath_len + 1); + ys->xpath[darr_len(ys->xpath) - 1] = 0; +} + +static void ys_pop_inner(struct nb_op_yield_state *ys) +{ + uint len = darr_len(ys->node_infos); + + assert(len); + darr_setlen(ys->node_infos, len - 1); + ys_trim_xpath(ys); +} + +static void ys_free_inner(struct nb_op_yield_state *ys, + struct nb_op_node_info *ni) +{ + if (!CHECK_FLAG(ni->schema->nodetype, LYS_CASE | LYS_CHOICE)) + lyd_free_tree(ni->inner); + ni->inner = NULL; +} + +static void nb_op_get_keys(struct lyd_node_inner *list_node, + struct yang_list_keys *keys) +{ + struct lyd_node *child; + uint n = 0; + + keys->num = 0; + LY_LIST_FOR (list_node->child, child) { + if (!lysc_is_key(child->schema)) + break; + strlcpy(keys->key[n], yang_dnode_get_string(child, NULL), + sizeof(keys->key[n])); + n++; + } + + keys->num = n; +} + +/** + * __move_back_to_next() - move back to the next lookup-next schema + */ +static bool __move_back_to_next(struct nb_op_yield_state *ys, int i) +{ + struct nb_op_node_info *ni; + int j; + + /* + * We will free the subtree we are trimming back to, or we will be done + * with the walk and will free the root on cleanup. + */ + + /* pop any node_info we dropped below on entry */ + for (j = darr_ilen(ys->node_infos) - 1; j > i; j--) + ys_pop_inner(ys); + + for (; i >= ys->walk_root_level; i--) { + if (ys->node_infos[i].has_lookup_next) + break; + ys_pop_inner(ys); + } + + if (i < ys->walk_root_level) + return false; + + ni = &ys->node_infos[i]; + + /* + * The i'th node has been lost after a yield so trim it from the tree + * now. + */ + ys_free_inner(ys, ni); + ni->list_entry = NULL; + + /* + * Leave the empty-of-data node_info on top, __walk will deal with + * this, by doing a lookup-next with the keys which we still have. + */ + + return true; +} + +static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) +{ + struct nb_op_node_info *ni; + struct nb_node *nn; + const void *parent_entry; + const void *list_entry; + uint i; + + /* + * IMPORTANT: On yielding: we always yield during list iteration and + * after the initial list element has been created and handled, so the + * top of the yield stack will always point at a list node. + * + * Additionally, that list node has been processed and was in the + * process of being "get_next"d when we yielded. We process the + * lookup-next list node last so all the rest of the data (to the left) + * has been gotten. NOTE: To keep this simple we will require only a + * single lookup-next sibling in any parents list of children. + * + * Walk the rightmost branch (the node info stack) from base to tip + * verifying all list nodes are still present. If not we backup to the + * node which has a lookup next, and we prune the branch to this node. + * If the list node that went away is the topmost we will be using + * lookup_next, but if it's a parent then the list_entry will have been + * restored. + */ + darr_foreach_i (ys->node_infos, i) { + ni = &ys->node_infos[i]; + nn = ni->schema->priv; + + if (!CHECK_FLAG(ni->schema->nodetype, LYS_LIST)) + continue; + + assert(ni->list_entry != NULL || + ni == darr_last(ys->node_infos)); + + /* Verify the entry is still present */ + parent_entry = (i == 0 ? NULL : ni[-1].list_entry); + list_entry = nb_callback_lookup_entry(nn, parent_entry, + &ni->keys); + if (!list_entry || list_entry != ni->list_entry) { + /* May be NULL or a different pointer + * move back to first of + * container with last lookup_next list node + * (which may be this one) and get next. + */ + if (!__move_back_to_next(ys, i)) + DEBUGD(&nb_dbg_events, + "%s: Nothing to resume after delete during walk (yield)", + __func__); + return; + } + } +} + +/* + * Can only yield if all list nodes to root have lookup_next() callbacks + * + * In order to support lookup_next() the list_node get_next() callback + * needs to return ordered (i.e., sorted) results. + */ + +/* ======================= */ +/* Start of walk init code */ +/* ======================= */ + +/** + * __xpath_pop_node() - remove the last node from xpath string + * @xpath: an xpath string + * + * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop. + */ +static int __xpath_pop_node(char *xpath) +{ + int len = strlen(xpath); + bool abs = xpath[0] == '/'; + char *slash; + + /* "//" or "/" => NULL */ + if (abs && (len == 1 || (len == 2 && xpath[1] == '/'))) + return NB_ERR_NOT_FOUND; + + slash = (char *)frrstr_back_to_char(xpath, '/'); + /* "/foo/bar/" or "/foo/bar//" => "/foo " */ + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + } + } + if (!slash) + return NB_ERR_NOT_FOUND; + *slash = 0; + return NB_OK; +} + +/** + * nb_op_xpath_to_trunk() - generate a lyd_node tree (trunk) using an xpath. + * @xpath_in: xpath query string to build trunk from. + * @dnode: resulting tree (trunk) + * + * Use the longest prefix of @xpath_in as possible to resolve to a tree (trunk). + * This is logically as if we walked along the xpath string resolving each + * nodename reference (in particular list nodes) until we could not. + * + * Return: error if any, if no error then @dnode contains the tree (trunk). + */ +static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, + struct lyd_node **trunk) +{ + char *xpath = NULL; + enum nb_error ret = NB_OK; + LY_ERR err; + + darr_in_strdup(xpath, xpath_in); + for (;;) { + err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, + LYD_NEW_PATH_UPDATE, NULL, trunk); + if (err == LY_SUCCESS) + break; + + ret = __xpath_pop_node(xpath); + if (ret != NB_OK) + break; + } + darr_free(xpath); + return ret; +} + +/* + * Finish initializing the node info based on the xpath string, and previous + * node_infos on the stack. If this node is a list node, obtain the specific + * list-entry object. + */ +static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, + uint index) +{ + struct nb_op_node_info *ni = &ys->node_infos[index]; + struct lyd_node *inner = ni->inner; + struct nb_node *nn = ni->schema->priv; + bool yield_ok = ys->finish != NULL; + + ni->has_lookup_next = nn->cbs.lookup_next != NULL; + + /* track the last list_entry until updated by new list node */ + ni->list_entry = index == 0 ? NULL : ni[-1].list_entry; + + /* Assert that we are walking the rightmost branch */ + assert(!inner->parent || inner == inner->parent->child->prev); + + if (CHECK_FLAG(inner->schema->nodetype, + LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) { + /* containers have only zero or one child on a branch of a tree */ + inner = ((struct lyd_node_inner *)inner)->child; + assert(!inner || inner->prev == inner); + ni->lookup_next_ok = yield_ok && + (index == 0 || ni[-1].lookup_next_ok); + return NB_OK; + } + + assert(CHECK_FLAG(inner->schema->nodetype, LYS_LIST)); + + ni->lookup_next_ok = yield_ok && ni->has_lookup_next && + (index == 0 || ni[-1].lookup_next_ok); + + nb_op_get_keys((struct lyd_node_inner *)inner, &ni->keys); + + /* A list entry cannot be present in a tree w/o it's keys */ + assert(ni->keys.num == yang_snode_num_keys(inner->schema)); + + /* + * Get this nodes opaque list_entry object + */ + + if (!nn->cbs.lookup_entry) { + flog_warn(EC_LIB_NB_OPERATIONAL_DATA, + "%s: data path doesn't support iteration over operational data: %s", + __func__, ys->xpath); + return NB_ERR_NOT_FOUND; + } + + /* ni->list_entry starts as the parent entry of this node */ + ni->list_entry = nb_callback_lookup_entry(nn, ni->list_entry, &ni->keys); + if (ni->list_entry == NULL) { + flog_warn(EC_LIB_NB_OPERATIONAL_DATA, + "%s: list entry lookup failed", __func__); + return NB_ERR_NOT_FOUND; + } + + /* + * By definition any list element we can get a specific list_entry for + * is specific. + */ + ni->query_specific_entry = true; + + return NB_OK; +} + +/** + * nb_op_ys_init_node_infos() - initialize the node info stack from the query. + * @ys: the yield state for this tree walk. + * + * On starting a walk we initialize the node_info stack as deeply as possible + * based on specific node references in the query string. We will stop at the + * point in the query string that is not specific (e.g., a list element without + * it's keys predicate) + * + * Return: northbound return value (enum nb_error) + */ +static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) +{ + struct nb_op_node_info *ni; + struct lyd_node *inner; + struct lyd_node *node = NULL; + enum nb_error ret; + uint i, len; + char *tmp; + + /* + * Obtain the trunk of the data node tree of the query. + * + * These are the nodes from the root that could be specifically + * identified with the query string. The trunk ends when a no specific + * node could be identified (e.g., a list-node name with no keys). + */ + + ret = nb_op_xpath_to_trunk(ys->xpath, &node); + if (ret || !node) { + flog_warn(EC_LIB_LIBYANG, + "%s: can't instantiate concrete path using xpath: %s", + __func__, ys->xpath); + if (!ret) + ret = NB_ERR_NOT_FOUND; + return ret; + } + + /* Move up to the container if on a leaf currently. */ + if (node && + !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) { + struct lyd_node *leaf = node; + + node = &node->parent->node; + + /* + * If the leaf is not a key, delete it, because it has a wrong + * empty value. + */ + if (!lysc_is_key(leaf->schema)) + lyd_free_tree(leaf); + } + assert(!node || + CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)); + if (!node) + return NB_ERR_NOT_FOUND; + + inner = node; + for (len = 1; inner->parent; len++) + inner = &inner->parent->node; + + darr_append_nz_mt(ys->node_infos, len, MTYPE_NB_NODE_INFOS); + + /* + * For each node find the prefix of the xpath query that identified it + * -- save the prefix length. + */ + inner = node; + for (i = len; i > 0; i--, inner = &inner->parent->node) { + ni = &ys->node_infos[i - 1]; + ni->inner = inner; + ni->schema = inner->schema; + /* + * NOTE: we could build this by hand with a litte more effort, + * but this simple implementation works and won't be expensive + * since the number of nodes is small and only done once per + * query. + */ + tmp = yang_dnode_get_path(inner, NULL, 0); + ni->xpath_len = strlen(tmp); + + /* Replace users supplied xpath with the libyang returned value */ + if (i == len) + darr_in_strdup(ys->xpath, tmp); + + /* The prefix must match the prefix of the stored xpath */ + assert(!strncmp(tmp, ys->xpath, ni->xpath_len)); + free(tmp); + } + + /* + * Obtain the specific list-entry objects for each list node on the + * trunk and finish initializing the node_info structs. + */ + + darr_foreach_i (ys->node_infos, i) { + ret = nb_op_ys_finalize_node_info(ys, i); + if (ret != NB_OK) { + if (ys->node_infos[0].inner) + lyd_free_all(ys->node_infos[0].inner); + darr_free(ys->node_infos); + return ret; + } + } + + ys->walk_start_level = darr_len(ys->node_infos); + + ys->walk_root_level = (int)ys->walk_start_level - 1; + + return NB_OK; +} + +/* ================ */ +/* End of init code */ +/* ================ */ + +/** + * nb_op_add_leaf() - Add leaf data to the get tree results + * @ys - the yield state for this tree walk. + * @nb_node - the northbound node representing this leaf. + * @xpath - the xpath (with key predicates) to this leaf value. + * + * Return: northbound return value (enum nb_error) + */ +static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys, + const struct nb_node *nb_node, + const char *xpath) +{ + const struct lysc_node *snode = nb_node->snode; + struct nb_op_node_info *ni = darr_last(ys->node_infos); + struct yang_data *data; + enum nb_error ret = NB_OK; + LY_ERR err; + + if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) + return NB_OK; + + /* Ignore list keys. */ + if (lysc_is_key(snode)) + return NB_OK; + + data = nb_callback_get_elem(nb_node, xpath, ni->list_entry); + if (data == NULL) + return NB_OK; + + /* Add a dnode to our tree */ + err = lyd_new_term(ni->inner, snode->module, snode->name, data->value, + false, NULL); + if (err) { + yang_data_free(data); + return NB_ERR_RESOURCE; + } + + if (ys->cb) + ret = (*ys->cb)(nb_node->snode, ys->translator, data, + ys->cb_arg); + yang_data_free(data); + + return ret; +} + +static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys, + const struct nb_node *nb_node, + const char *xpath) +{ + const struct lysc_node *snode = nb_node->snode; + struct nb_op_node_info *ni = darr_last(ys->node_infos); + const void *list_entry = NULL; + enum nb_error ret = NB_OK; + LY_ERR err; + + if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) + return NB_OK; + + do { + struct yang_data *data; + + list_entry = nb_callback_get_next(nb_node, ni->list_entry, + list_entry); + if (!list_entry) + /* End of the list. */ + break; + + data = nb_callback_get_elem(nb_node, xpath, list_entry); + if (data == NULL) + continue; + + /* Add a dnode to our tree */ + err = lyd_new_term(ni->inner, snode->module, snode->name, + data->value, false, NULL); + if (err) { + yang_data_free(data); + return NB_ERR_RESOURCE; + } + + if (ys->cb) + ret = (*ys->cb)(nb_node->snode, ys->translator, data, + ys->cb_arg); + yang_data_free(data); + } while (ret == NB_OK && list_entry); + + return ret; +} + + +static bool nb_op_schema_path_has_predicate(struct nb_op_yield_state *ys, + int level) +{ + if (level > darr_lasti(ys->query_tokens)) + return false; + return strchr(ys->query_tokens[level], '[') != NULL; +} + +/** + * nb_op_empty_container_ok() - determine if should keep empty container node. + * + * Return: true if the empty container should be kept. + */ +static bool nb_op_empty_container_ok(const struct lysc_node *snode, + const char *xpath, const void *list_entry) +{ + struct nb_node *nn = snode->priv; + struct yang_data *data; + + if (!CHECK_FLAG(snode->flags, LYS_PRESENCE)) + return false; + + if (!nn->cbs.get_elem) + return false; + + data = nb_callback_get_elem(nn, xpath, list_entry); + if (data) { + yang_data_free(data); + return true; + } + return false; +} + +/** + * nb_op_get_child_path() - add child node name to the xpath. + * @xpath_parent - a darr string for the parent node. + * @schild - the child schema node. + * @xpath_child - a previous return value from this function to reuse. + */ +static char *nb_op_get_child_path(const char *xpath_parent, + const struct lysc_node *schild, + char *xpath_child) +{ + /* "/childname" */ + uint space, extra = strlen(schild->name) + 1; + bool new_mod = (!schild->parent || + schild->parent->module != schild->module); + int n; + + if (new_mod) + /* "modulename:" */ + extra += strlen(schild->module->name) + 1; + space = darr_len(xpath_parent) + extra; + + if (xpath_parent == xpath_child) + darr_ensure_cap(xpath_child, space); + else + darr_in_strdup_cap(xpath_child, xpath_parent, space); + if (new_mod) + n = snprintf(darr_strnul(xpath_child), extra + 1, "/%s:%s", + schild->module->name, schild->name); + else + n = snprintf(darr_strnul(xpath_child), extra + 1, "/%s", + schild->name); + assert(n == (int)extra); + _darr_len(xpath_child) += extra; + return xpath_child; +} + +static bool __is_yielding_node(const struct lysc_node *snode) +{ + struct nb_node *nn = snode->priv; + + return nn->cbs.lookup_next != NULL; +} + +static const struct lysc_node *__sib_next(bool yn, const struct lysc_node *sib) +{ + for (; sib; sib = sib->next) { + /* Always skip keys. */ + if (lysc_is_key(sib)) + continue; + if (yn == __is_yielding_node(sib)) + return sib; + } + return NULL; +} + +/** + * nb_op_sib_next() - Return the next sibling to walk to + * @ys: the yield state for this tree walk. + * @sib: the currently being visited sibling + * + * Return: the next sibling to walk to, walking non-yielding before yielding. + */ +static const struct lysc_node *nb_op_sib_next(struct nb_op_yield_state *ys, + const struct lysc_node *sib) +{ + struct lysc_node *parent = sib->parent; + bool yn = __is_yielding_node(sib); + + /* + * If the node info stack is shorter than the schema path then we are + * doign specific query still on the node from the schema path (should + * match) so just return NULL (i.e., don't process siblings) + */ + if (darr_len(ys->schema_path) > darr_len(ys->node_infos)) + return NULL; + /* + * If sib is on top of the node info stack then + * 1) it's a container node -or- + * 2) it's a list node that we were walking and we've reach the last entry + * 3) if sib is a list and the list was empty we never would have + * pushed sib on the stack so the top of the stack is the parent + * + * If the query string included this node then we do not process any + * siblings as we are not walking all the parent's children just this + * specified one give by the query string. + */ + if (sib == darr_last(ys->node_infos)->schema && + darr_len(ys->schema_path) >= darr_len(ys->node_infos)) + return NULL; + /* case (3) */ + else if (sib->nodetype == LYS_LIST && + parent == darr_last(ys->node_infos)->schema && + darr_len(ys->schema_path) > darr_len(ys->node_infos)) + return NULL; + + sib = __sib_next(yn, sib->next); + if (sib) + return sib; + if (yn) + return NULL; + return __sib_next(true, lysc_node_child(parent)); +} +/* + * sib_walk((struct lyd_node *)ni->inner->node.parent->parent->parent->parent->parent->parent->parent) + */ + +/** + * nb_op_sib_first() - obtain the first child to walk to + * @ys: the yield state for this tree walk. + * @parent: the parent whose child we seek + * @skip_keys: if should skip over keys + * + * Return: the first child to continue the walk to, starting with non-yielding + * siblings then yielding ones. There should be no more than 1 yielding sibling. + */ +static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys, + const struct lysc_node *parent) +{ + const struct lysc_node *sib = lysc_node_child(parent); + const struct lysc_node *first_sib; + + /* + * NOTE: when we want to handle root level walks we will need to use + * lys_getnext() to walk root level of each module and + * ly_ctx_get_module_iter() to walk the modules. + */ + assert(darr_len(ys->node_infos) > 0); + + /* + * The top of the node stack points at @parent. + * + * If the schema path (original query) is longer than our current node + * info stack (current xpath location), we are building back up to the + * base of the user query, return the next schema node from the query + * string (schema_path). + */ + if (darr_last(ys->node_infos) != NULL && + !CHECK_FLAG(darr_last(ys->node_infos)->schema->nodetype, + LYS_CASE | LYS_CHOICE)) + assert(darr_last(ys->node_infos)->schema == parent); + if (darr_lasti(ys->node_infos) < ys->query_base_level) + return ys->schema_path[darr_lasti(ys->node_infos) + 1]; + + /* We always skip keys. */ + while (sib && lysc_is_key(sib)) + sib = sib->next; + if (!sib) + return NULL; + + /* Return non-yielding node's first */ + first_sib = sib; + if (__is_yielding_node(sib)) { + sib = __sib_next(false, sib); + if (sib) + return sib; + } + return first_sib; +} + +/* + * "3-dimensional" walk from base of the tree to the tip in-order. + * + * The actual tree is only 2-dimensional as list nodes are organized as adjacent + * siblings under a common parent perhaps with other siblings to each side; + * however, using 3d view here is easier to diagram. + * + * - A list node is yielding if it has a lookup_next callback. + * - All other node types are not yielding. + * - There's only one yielding node in a list of children (i.e., siblings). + * + * We visit all non-yielding children prior to the yielding child. + * That way we have the fullest tree possible even when something is deleted + * during a yield. + * --- child/parent descendant poinilnters + * ... next/prev sibling pointers + * o.o list entries pointers + * ~~~ diagram extension connector + * 1 + * / \ + * / \ o~~~~12 + * / \ . / \ + * 2.......5 o~~~9 13...14 + * / \ | . / \ + * 3...4 6 10...11 Cont Nodes: 1,2,5 + * / \ List Nodes: 6,9,12 + * 7...8 Leaf Nodes: 3,4,7,8,10,11,13,14 + * Schema Leaf A: 3 + * Schema Leaf B: 4 + * Schema Leaf C: 7,10,13 + * Schema Leaf D: 8,11,14 + */ +static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) +{ + const struct lysc_node *walk_stem_tip = ys_get_walk_stem_tip(ys); + const struct lysc_node *sib; + const void *parent_list_entry = NULL; + const void *list_entry = NULL; + struct nb_op_node_info *ni, *pni; + struct lyd_node *node; + struct nb_node *nn; + char *xpath_child = NULL; + // bool at_query_base; + bool at_root_level, list_start, is_specific_node; + enum nb_error ret = NB_OK; + LY_ERR err; + int at_clevel; + uint len; + + + monotime(&ys->start_time); + + /* Don't currently support walking all root nodes */ + if (!walk_stem_tip) + return NB_ERR_NOT_FOUND; + + if (ys->schema_path[0]->nodetype == LYS_CHOICE) { + flog_err(EC_LIB_NB_OPERATIONAL_DATA, + "%s: unable to walk root level choice node from module: %s", + __func__, ys->schema_path[0]->module->name); + return NB_ERR; + } + + /* + * If we are resuming then start with the list container on top. + * Otherwise get the first child of the container we are walking, + * starting with non-yielding children. + */ + if (is_resume) + sib = darr_last(ys->node_infos)->schema; + else { + /* + * Start with non-yielding children first. + * + * When adding root level walks, the sibling list are the root + * level nodes of all modules + */ + sib = nb_op_sib_first(ys, walk_stem_tip); + if (!sib) + return NB_ERR_NOT_FOUND; + } + + + while (true) { + /* Grab the top container/list node info on the stack */ + at_clevel = darr_lasti(ys->node_infos); + ni = &ys->node_infos[at_clevel]; + + /* + * This is the level of the last specific node at init + * time. +1 would be the first non-specific list or + * non-container if present in the container node. + */ + at_root_level = at_clevel == ys->walk_root_level; + + if (!sib) { + /* + * We've reached the end of the siblings inside a + * containing node; either a container, case, choice, or + * a specific list node entry. + * + * We handle case/choice/container node inline; however, + * for lists we are only done with a specific entry and + * need to move to the next element on the list so we + * drop down into the switch for that case. + */ + + /* Grab the containing node. */ + sib = ni->schema; + + if (CHECK_FLAG(sib->nodetype, + LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) { + /* If we added an empty container node (no + * children) and it's not a presence container + * or it's not backed by the get_elem callback, + * remove the node from the tree. + */ + if (sib->nodetype == LYS_CONTAINER && + !lyd_child(ni->inner) && + !nb_op_empty_container_ok(sib, ys->xpath, + ni->list_entry)) + ys_free_inner(ys, ni); + + /* If we have returned to our original walk base, + * then we are done with the walk. + */ + if (at_root_level) { + ret = NB_OK; + goto done; + } + /* + * Grab the sibling of the container we are + * about to pop, so we will be mid-walk on the + * parent containers children. + */ + sib = nb_op_sib_next(ys, sib); + + /* Pop container node to the parent container */ + ys_pop_inner(ys); + + /* + * If are were working on a user narrowed path + * then we are done with these siblings. + */ + if (darr_len(ys->schema_path) > + darr_len(ys->node_infos)) + sib = NULL; + + /* Start over */ + continue; + } + /* + * If we are here we have reached the end of the + * children of a list entry node. sib points + * at the list node info. + */ + } + + if (CHECK_FLAG(sib->nodetype, + LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER)) + xpath_child = nb_op_get_child_path(ys->xpath, sib, + xpath_child); + else if (CHECK_FLAG(sib->nodetype, LYS_CASE | LYS_CHOICE)) + darr_in_strdup(xpath_child, ys->xpath); + + nn = sib->priv; + + switch (sib->nodetype) { + case LYS_LEAF: + /* + * If we have a non-specific walk to a specific leaf + * (e.g., "..../route-entry/metric") and the leaf value + * is not present, then we are left with the data nodes + * of the stem of the branch to the missing leaf data. + * For containers this will get cleaned up by the + * container code above that looks for no children; + * however, this doesn't work for lists. + * + * (FN:A) We need a similar check for empty list + * elements. Empty list elements below the + * query_base_level (i.e., the schema path length) + * should be cleaned up as they don't support anything + * the user is querying for, if they are above the + * query_base_level then they are part of the walk and + * should be kept. + */ + ret = nb_op_iter_leaf(ys, nn, xpath_child); + if (ret != NB_OK) + goto done; + sib = nb_op_sib_next(ys, sib); + continue; + case LYS_LEAFLIST: + ret = nb_op_iter_leaflist(ys, nn, xpath_child); + if (ret != NB_OK) + goto done; + sib = nb_op_sib_next(ys, sib); + continue; + case LYS_CASE: + case LYS_CHOICE: + case LYS_CONTAINER: + if (CHECK_FLAG(nn->flags, F_NB_NODE_CONFIG_ONLY)) { + sib = nb_op_sib_next(ys, sib); + continue; + } + + if (sib->nodetype != LYS_CONTAINER) { + /* Case/choice use parent inner. */ + /* TODO: thus we don't support root level choice */ + node = ni->inner; + } else { + err = lyd_new_inner(ni->inner, sib->module, + sib->name, false, &node); + if (err) { + ret = NB_ERR_RESOURCE; + goto done; + } + } + + /* push this choice/container node on top of the stack */ + ni = darr_appendz(ys->node_infos); + ni->inner = node; + ni->schema = sib; + ni->lookup_next_ok = ni[-1].lookup_next_ok; + ni->list_entry = ni[-1].list_entry; + + darr_in_strdup(ys->xpath, xpath_child); + ni->xpath_len = darr_strlen(ys->xpath); + + sib = nb_op_sib_first(ys, sib); + continue; + case LYS_LIST: + + /* + * Notes: + * + * NOTE: ni->inner may be NULL here if we resumed and it + * was gone. ni->schema and ni->keys will still be + * valid. + * + * NOTE: At this point sib is never NULL; however, if it + * was NULL at the top of the loop, then we were done + * working on a list element's children and will be + * attempting to get the next list element here so sib + * == ni->schema (i.e., !list_start). + * + * (FN:A): Before doing this let's remove empty list + * elements that are "inside" the query string as they + * represent a stem which didn't lead to actual data + * being requested by the user -- for example, + * ".../route-entry/metric" if metric is not present we + * don't want to return an empty route-entry to the + * user. + */ + + node = NULL; + list_start = ni->schema != sib; + if (list_start) { + /* + * List iteration: First Element + * ----------------------------- + * + * Our node info wasn't on top (wasn't an entry + * for sib) so this is a new list iteration, we + * will push our node info below. The top is our + * parent. + */ + if (CHECK_FLAG(nn->flags, + F_NB_NODE_CONFIG_ONLY)) { + sib = nb_op_sib_next(ys, sib); + continue; + } + /* we are now at one level higher */ + at_clevel += 1; + pni = ni; + ni = NULL; + } else { + /* + * List iteration: Next Element + * ---------------------------- + * + * This is the case where `sib == NULL` at the + * top of the loop, so, we just completed the + * walking the children of a list entry, i.e., + * we are done with that list entry. + * + * `sib` was reset to point at the our list node + * at the top of node_infos. + * + * Within this node_info, `ys->xpath`, `inner`, + * `list_entry`, and `xpath_len` are for the + * previous list entry, and need to be updated. + */ + pni = darr_len(ys->node_infos) > 1 ? &ni[-1] + : NULL; + } + + parent_list_entry = pni ? pni->list_entry : NULL; + list_entry = ni ? ni->list_entry : NULL; + + /* + * Before yielding we check to see if we are doing a + * specific list entry instead of a full list iteration. + * We do not want to yield during specific list entry + * processing. + */ + + /* + * If we are at a list start check to see if the node + * has a predicate. If so we will try and fetch the data + * node now that we've built part of the tree, if the + * predicates are keys or only depend on the tree already + * built, it should create the element for us. + */ + is_specific_node = false; + if (list_start && + at_clevel <= darr_lasti(ys->query_tokens) && + !ys->non_specific_predicate[at_clevel] && + nb_op_schema_path_has_predicate(ys, at_clevel)) { + err = lyd_new_path(pni->inner, NULL, + ys->query_tokens[at_clevel], + NULL, 0, &node); + if (!err) + is_specific_node = true; + else if (err == LY_EVALID) + ys->non_specific_predicate[at_clevel] = true; + else { + flog_err(EC_LIB_NB_OPERATIONAL_DATA, + "%s: unable to create node for specific query string: %s: %s", + __func__, + ys->query_tokens[at_clevel], + yang_ly_strerrcode(err)); + ret = NB_ERR; + goto done; + } + } + + if (list_entry && ni->query_specific_entry) { + /* + * Ending specific list entry processing. + */ + assert(!list_start); + is_specific_node = true; + list_entry = NULL; + } + + /* + * Should we yield? + * + * Don't yield if we have a specific entry. + */ + if (!is_specific_node && ni && ni->lookup_next_ok && + // make sure we advance, if the interval is + // fast and we are very slow. + ((monotime_since(&ys->start_time, NULL) > + NB_OP_WALK_INTERVAL_US && + ni->niters) || + (ni->niters + 1) % 10000 == 0)) { + /* This is a yield supporting list node and + * we've been running at least our yield + * interval, so yield. + * + * NOTE: we never yield on list_start, and we + * are always about to be doing a get_next. + */ + DEBUGD(&nb_dbg_events, + "%s: yielding after %u iterations", + __func__, ni->niters); + + ni->niters = 0; + ret = NB_YIELD; + goto done; + } + + /* + * Now get the backend list_entry opaque object for + * this list entry from the backend. + */ + + if (is_specific_node) { + /* + * Specific List Entry: + * -------------------- + */ + if (list_start) { + list_entry = + nb_callback_lookup_node_entry( + node, parent_list_entry); + /* + * If the node we created from a + * specific predicate entry is not + * actually there we need to delete the + * node from our data tree + */ + if (!list_entry) { + lyd_free_tree(node); + node = NULL; + } + } + } else if (!list_start && !list_entry && + ni->has_lookup_next) { + /* + * After Yield: + * ------------ + * After a yield the list_entry may have become + * invalid, so use lookup_next callback with + * parent and keys instead to find next element. + */ + list_entry = + nb_callback_lookup_next(nn, + parent_list_entry, + &ni->keys); + } else { + /* + * Normal List Iteration: + * ---------------------- + * Start (list_entry == NULL) or continue + * (list_entry != NULL) the list iteration. + */ + /* Obtain [next] list entry. */ + list_entry = + nb_callback_get_next(nn, + parent_list_entry, + list_entry); + } + + /* + * (FN:A) Reap empty list element? Check to see if we + * should reap an empty list element. We do this if the + * empty list element exists at or below the query base + * (i.e., it's not part of the walk, but a failed find + * on a more specific query e.g., for below the + * `route-entry` element for a query + * `.../route-entry/metric` where the list element had + * no metric value. + * + * However, if the user query is for a key of a list + * element, then when we reach that list element it will + * have no non-key children, check for this condition + * and do not reap if true. + */ + if (!list_start && ni->inner && + !lyd_child_no_keys(ni->inner) && + /* not the top element with a key match */ + !((darr_ilen(ys->node_infos) == + darr_ilen(ys->schema_path) - 1) && + lysc_is_key((*darr_last(ys->schema_path)))) && + /* is this at or below the base? */ + darr_ilen(ys->node_infos) <= ys->query_base_level) + ys_free_inner(ys, ni); + + + if (!list_entry) { + /* + * List Iteration Done + * ------------------- + */ + + /* + * Grab next sibling of the list node + */ + if (is_specific_node) + sib = NULL; + else + sib = nb_op_sib_next(ys, sib); + + /* + * If we are at the walk root (base) level then + * that specifies a list and we are done iterating + * the list, so we are done with the walk entirely. + */ + if (!sib && at_clevel == ys->walk_root_level) { + ret = NB_OK; + goto done; + } + + /* + * Pop the our list node info back to our + * parent. + * + * We only do this if we've already pushed a + * node for the current list schema. For + * `list_start` this hasn't happened yet, as + * would have happened below. So when list_start + * is true but list_entry if NULL we + * are processing an empty list. + */ + if (!list_start) + ys_pop_inner(ys); + + /* + * We should never be below the walk root + */ + assert(darr_lasti(ys->node_infos) >= + ys->walk_root_level); + + /* Move on to the sibling of the list node */ + continue; + } + + /* + * From here on, we have selected a new top node_info + * list entry (either newly pushed or replacing the + * previous entry in the walk), and we are filling in + * the details. + */ + + if (list_start) { + /* + * Starting iteration of a list type or + * processing a specific entry, push the list + * node_info on stack. + */ + ni = darr_appendz(ys->node_infos); + pni = &ni[-1]; /* memory may have moved */ + ni->has_lookup_next = nn->cbs.lookup_next != + NULL; + ni->lookup_next_ok = ((!pni && ys->finish) || + pni->lookup_next_ok) && + ni->has_lookup_next; + ni->query_specific_entry = is_specific_node; + ni->niters = 0; + ni->nents = 0; + + /* this will be our predicate-less xpath */ + ys->xpath = nb_op_get_child_path(ys->xpath, sib, + ys->xpath); + } else { + /* + * Reset our xpath to the list node (i.e., + * remove the entry predicates) + */ + if (ni->query_specific_entry) { + flog_warn(EC_LIB_NB_OPERATIONAL_DATA, + "%s: unexpected state", + __func__); + } + assert(!ni->query_specific_entry); + len = strlen(sib->name) + 1; /* "/sibname" */ + if (pni) + len += pni->xpath_len; + darr_setlen(ys->xpath, len + 1); + ys->xpath[len] = 0; + ni->xpath_len = len; + } + + /* Need to get keys. */ + + if (!CHECK_FLAG(nn->flags, F_NB_NODE_KEYLESS_LIST)) { + ret = nb_callback_get_keys(nn, list_entry, + &ni->keys); + if (ret) { + darr_pop(ys->node_infos); + ret = NB_ERR_RESOURCE; + goto done; + } + } + /* + * Append predicates to xpath. + */ + len = darr_strlen(ys->xpath); + if (ni->keys.num) { + yang_get_key_preds(ys->xpath + len, sib, + &ni->keys, + darr_cap(ys->xpath) - len); + } else { + /* add a position predicate (1s based?) */ + darr_ensure_avail(ys->xpath, 10); + snprintf(ys->xpath + len, + darr_cap(ys->xpath) - len + 1, "[%u]", + ni->nents + 1); + } + darr_setlen(ys->xpath, + strlen(ys->xpath + len) + len + 1); + ni->xpath_len = darr_strlen(ys->xpath); + + /* + * Create the new list entry node. + */ + + if (!node) { + err = yang_lyd_new_list((struct lyd_node_inner *) + ni[-1] + .inner, + sib, &ni->keys, &node); + if (err) { + darr_pop(ys->node_infos); + ret = NB_ERR_RESOURCE; + goto done; + } + } + + /* + * Save the new list entry with the list node info + */ + ni->inner = node; + ni->schema = node->schema; + ni->list_entry = list_entry; + ni->niters += 1; + ni->nents += 1; + + /* Skip over the key children, they've been created. */ + sib = nb_op_sib_first(ys, sib); + continue; + + default: + /*FALLTHROUGH*/ + case LYS_ANYXML: + case LYS_ANYDATA: + /* These schema types are not currently handled */ + flog_warn(EC_LIB_NB_OPERATIONAL_DATA, + "%s: unsupported schema node type: %s", + __func__, lys_nodetype2str(sib->nodetype)); + sib = nb_op_sib_next(ys, sib); + continue; + } + } + +done: + darr_free(xpath_child); + return ret; +} + +static void nb_op_walk_continue(struct event *thread) +{ + struct nb_op_yield_state *ys = EVENT_ARG(thread); + enum nb_error ret = NB_OK; + + DEBUGD(&nb_dbg_cbs_state, "northbound oper-state: resuming %s", + ys->xpath); + + nb_op_resume_data_tree(ys); + + /* if we've popped past the walk start level we're done */ + if (darr_lasti(ys->node_infos) < ys->walk_root_level) + goto finish; + + /* otherwise we are at a resumable node */ + assert(darr_last(ys->node_infos)->has_lookup_next); + + ret = __walk(ys, true); + if (ret == NB_YIELD) { + if (nb_op_yield(ys) != NB_OK) { + if (ys->should_batch) + goto stopped; + else + goto finish; + } + return; + } +finish: + (*ys->finish)(ys_root_node(ys), ys->finish_arg, ret); +stopped: + nb_op_free_yield_state(ys, false); +} + +static void __free_siblings(struct lyd_node *this) +{ + struct lyd_node *next, *sib; + uint count = 0; + + LY_LIST_FOR_SAFE(lyd_first_sibling(this), next, sib) + { + if (lysc_is_key(sib->schema)) + continue; + if (sib == this) + continue; + lyd_free_tree(sib); + count++; + } + DEBUGD(&nb_dbg_events, "NB oper-state: deleted %u siblings", count); +} + +/* + * Trim Algorithm: + * + * Delete final lookup-next list node and subtree, leave stack slot with keys. + * + * Then walking up the stack, delete all siblings except: + * 1. right-most container or list node (must be lookup-next by design) + * 2. keys supporting existing parent list node. + * + * NOTE the topmost node on the stack will be the final lookup-nexxt list node, + * as we only yield on lookup-next list nodes. + * + */ +static void nb_op_trim_yield_state(struct nb_op_yield_state *ys) +{ + struct nb_op_node_info *ni; + int i = darr_lasti(ys->node_infos); + + assert(i >= 0); + + DEBUGD(&nb_dbg_events, "NB oper-state: start trimming: top: %d", i); + + ni = &ys->node_infos[i]; + assert(ni->has_lookup_next); + + DEBUGD(&nb_dbg_events, "NB oper-state: deleting tree at level %d", i); + __free_siblings(ni->inner); + ys_free_inner(ys, ni); + + while (--i > 0) { + DEBUGD(&nb_dbg_events, + "NB oper-state: deleting siblings at level: %d", i); + __free_siblings(ys->node_infos[i].inner); + } + DEBUGD(&nb_dbg_events, "NB oper-state: stop trimming: new top: %d", + (int)darr_lasti(ys->node_infos)); +} + +static enum nb_error nb_op_yield(struct nb_op_yield_state *ys) +{ + enum nb_error ret; + unsigned long min_us = MAX(1, NB_OP_WALK_INTERVAL_US / 50000); + struct timeval tv = { .tv_sec = 0, .tv_usec = min_us }; + + DEBUGD(&nb_dbg_events, "NB oper-state: yielding %s for %lus (should_batch %d)", + ys->xpath, tv.tv_usec, ys->should_batch); + + if (ys->should_batch) { + /* + * TODO: add ability of finish to influence the timer. + * This will allow, for example, flow control based on how long + * it takes finish to process the batch. + */ + ret = (*ys->finish)(ys_root_node(ys), ys->finish_arg, NB_YIELD); + if (ret != NB_OK) + return ret; + /* now trim out that data we just "finished" */ + nb_op_trim_yield_state(ys); + + } + + event_add_timer_tv(event_loop, nb_op_walk_continue, ys, &tv, + &ys->walk_ev); + return NB_OK; +} + +static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys, + struct nb_node **last) +{ + struct nb_node **nb_nodes = NULL; + const struct lysc_node *sn; + struct nb_node *nblast; + char *s, *s2; + int count; + uint i; + + /* + * Get the schema node stack for the entire query string + * + * The user might pass in something like "//metric" which may resolve to + * more than one schema node ("trunks"). nb_node_find() returns a single + * node though. We should expand the functionality to get the set of + * nodes that matches the xpath (not path) query and save that set in + * the yield state. Then we should do a walk using the users query + * string over each schema trunk in the set. + */ + nblast = nb_node_find(ys->xpath); + if (!nblast) { + nb_nodes = nb_nodes_find(ys->xpath); + nblast = darr_len(nb_nodes) ? nb_nodes[0] : NULL; + darr_free(nb_nodes); + } + if (!nblast) { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, ys->xpath); + return NB_ERR; + } + *last = nblast; + + /* + * Create a stack of schema nodes one element per node in the query + * path, only the top (last) element may be a non-container type. + * + * NOTE: appears to be a bug in nb_node linkage where parent can be NULL, + * or I'm misunderstanding the code, in any case we use the libyang + * linkage to walk which works fine. + * + * XXX: we don't actually support choice/case yet, they are container + * types in the libyang schema, but won't be in data so our length + * checking gets messed up. + */ + for (sn = nblast->snode, count = 0; sn; count++, sn = sn->parent) + if (sn != nblast->snode) + assert(CHECK_FLAG(sn->nodetype, + LYS_CONTAINER | LYS_LIST | + LYS_CHOICE | LYS_CASE)); + /* create our arrays */ + darr_append_n(ys->schema_path, count); + darr_append_n(ys->query_tokens, count); + darr_append_nz(ys->non_specific_predicate, count); + for (sn = nblast->snode; sn; sn = sn->parent) + ys->schema_path[--count] = sn; + + /* + * Now tokenize the query string and get pointers to each token + */ + + /* Get copy of query string start after initial '/'s */ + s = ys->xpath; + while (*s && *s == '/') + s++; + ys->query_tokstr = darr_strdup(s); + s = ys->query_tokstr; + + darr_foreach_i (ys->schema_path, i) { + const char *modname = ys->schema_path[i]->module->name; + const char *name = ys->schema_path[i]->name; + int nlen = strlen(name); + int mnlen = 0; + + /* + * Technically the query_token for choice/case should probably be pointing at + * the child (leaf) rather than the parent (container), however, + * we only use these for processing list nodes so KISS. + */ + if (CHECK_FLAG(ys->schema_path[i]->nodetype, + LYS_CASE | LYS_CHOICE)) { + ys->query_tokens[i] = ys->query_tokens[i - 1]; + continue; + } + + while (true) { + s2 = strstr(s, name); + if (!s2) + goto error; + + if (s2[-1] == ':') { + mnlen = strlen(modname) + 1; + if (ys->query_tokstr > s2 - mnlen || + strncmp(s2 - mnlen, modname, mnlen - 1)) + goto error; + s2 -= mnlen; + nlen += mnlen; + } + + s = s2; + if ((i == 0 || s[-1] == '/') && + (s[nlen] == 0 || s[nlen] == '[' || s[nlen] == '/')) + break; + /* + * Advance past the incorrect match, must have been + * part of previous predicate. + */ + s += nlen; + } + + /* NUL terminate previous token and save this one */ + if (i > 0) + s[-1] = 0; + ys->query_tokens[i] = s; + s += nlen; + } + + /* NOTE: need to subtract choice/case nodes when these are supported */ + ys->query_base_level = darr_lasti(ys->schema_path); + + return NB_OK; + +error: + darr_free(ys->query_tokstr); + darr_free(ys->schema_path); + darr_free(ys->query_tokens); + darr_free(ys->non_specific_predicate); + return NB_ERR; +} + + +/** + * nb_op_walk_start() - Start walking oper-state directed by query string. + * @ys: partially initialized yield state for this walk. + * + */ +static enum nb_error nb_op_walk_start(struct nb_op_yield_state *ys) +{ + struct nb_node *nblast; + enum nb_error ret; + + /* + * Get nb_node path (stack) corresponding to the xpath query + */ + ret = nb_op_ys_init_schema_path(ys, &nblast); + if (ret != NB_OK) + return ret; + + + /* + * Get the node_info path (stack) corresponding to the uniquely + * resolvable data nodes from the beginning of the xpath query. + */ + ret = nb_op_ys_init_node_infos(ys); + if (ret != NB_OK) + return ret; + + return __walk(ys, false); +} + + +void *nb_oper_walk(const char *xpath, struct yang_translator *translator, + uint32_t flags, bool should_batch, nb_oper_data_cb cb, + void *cb_arg, nb_oper_data_finish_cb finish, void *finish_arg) +{ + struct nb_op_yield_state *ys; + enum nb_error ret; + + ys = nb_op_create_yield_state(xpath, translator, flags, should_batch, + cb, cb_arg, finish, finish_arg); + + ret = nb_op_walk_start(ys); + if (ret == NB_YIELD) { + if (nb_op_yield(ys) != NB_OK) { + if (ys->should_batch) + goto stopped; + else + goto finish; + } + return ys; + } +finish: + (void)(*ys->finish)(ys_root_node(ys), ys->finish_arg, ret); +stopped: + nb_op_free_yield_state(ys, false); + return NULL; +} + + +void nb_oper_cancel_walk(void *walk) +{ + if (walk) + nb_op_free_yield_state(walk, false); +} + + +void nb_oper_cancel_all_walks(void) +{ + struct nb_op_yield_state *ys; + + frr_each_safe (nb_op_walks, &nb_op_walks, ys) + nb_oper_cancel_walk(ys); +} + + +/* + * The old API -- remove when we've update the users to yielding. + */ +enum nb_error nb_oper_iterate_legacy(const char *xpath, + struct yang_translator *translator, + uint32_t flags, nb_oper_data_cb cb, + void *cb_arg, struct lyd_node **tree) +{ + struct nb_op_yield_state *ys; + enum nb_error ret; + + ys = nb_op_create_yield_state(xpath, translator, flags, false, cb, + cb_arg, NULL, NULL); + + ret = nb_op_walk_start(ys); + assert(ret != NB_YIELD); + + if (tree && ret == NB_OK) + *tree = ys_root_node(ys); + else { + if (ys_root_node(ys)) + yang_dnode_free(ys_root_node(ys)); + if (tree) + *tree = NULL; + } + + nb_op_free_yield_state(ys, true); + return ret; +} + +void nb_oper_init(struct event_loop *loop) +{ + event_loop = loop; + nb_op_walks_init(&nb_op_walks); +} + +void nb_oper_terminate(void) +{ + nb_oper_cancel_all_walks(); +} diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 7fd4af8..4942d66 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -118,6 +118,9 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data) sr_data->type = SR_INT64_T; sr_data->data.int64_val = yang_str2int64(frr_data->value); break; + case LY_TYPE_LEAFREF: + sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value); + break; case LY_TYPE_STRING: sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value); break; @@ -137,6 +140,11 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data) sr_data->type = SR_UINT64_T; sr_data->data.uint64_val = yang_str2uint64(frr_data->value); break; + case LY_TYPE_UNION: + /* No way to deal with this using un-typed yang_data object */ + sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value); + break; + case LY_TYPE_UNKNOWN: default: return -1; } @@ -178,12 +186,12 @@ static int frr_sr_process_change(struct nb_config *candidate, /* Map operation values. */ switch (sr_op) { case SR_OP_CREATED: + nb_op = NB_OP_CREATE; + break; case SR_OP_MODIFIED: - if (nb_operation_is_valid(NB_OP_CREATE, nb_node->snode)) - nb_op = NB_OP_CREATE; - else if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) { + if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY)) nb_op = NB_OP_MODIFY; - } else + else /* Ignore list keys modifications. */ return NB_OK; break; @@ -193,7 +201,7 @@ static int frr_sr_process_change(struct nb_config *candidate, * notified about the removal of all of its leafs, even the ones * that are non-optional. We need to ignore these notifications. */ - if (!nb_operation_is_valid(NB_OP_DESTROY, nb_node->snode)) + if (!nb_is_operation_allowed(nb_node, NB_OP_DESTROY)) return NB_OK; nb_op = NB_OP_DESTROY; @@ -213,7 +221,7 @@ static int frr_sr_process_change(struct nb_config *candidate, ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data); yang_data_free(data); - if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { + if (ret != NB_OK) { flog_warn( EC_LIB_NB_CANDIDATE_EDIT_ERROR, "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", @@ -271,11 +279,12 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, ret = nb_candidate_commit_prepare(context, candidate, NULL, &transaction, false, false, errmsg, sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - flog_warn( - EC_LIB_LIBSYSREPO, - "%s: failed to prepare configuration transaction: %s (%s)", - __func__, nb_err_name(ret), errmsg); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { + flog_warn(EC_LIB_LIBSYSREPO, + "%s: failed to prepare configuration transaction: %s (%s)", + __func__, nb_err_name(ret), errmsg); + sr_session_set_error_message(session, errmsg); + } if (!transaction) nb_config_free(candidate); @@ -340,6 +349,8 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id, return frr_sr_config_change_cb_apply(session, module_name); case SR_EV_ABORT: return frr_sr_config_change_cb_abort(session, module_name); + case SR_EV_RPC: + case SR_EV_UPDATE: default: flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u", __func__, sr_ev); @@ -347,39 +358,16 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id, } } -static int frr_sr_state_data_iter_cb(const struct lysc_node *snode, - struct yang_translator *translator, - struct yang_data *data, void *arg) -{ - struct lyd_node *dnode = arg; - LY_ERR ly_errno; - - ly_errno = 0; - ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value, - 0, &dnode); - if (ly_errno) { - flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", - __func__); - yang_data_free(data); - return NB_ERR; - } - - yang_data_free(data); - return NB_OK; -} - /* Callback for state retrieval. */ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_ctx) { - struct lyd_node *dnode; + struct lyd_node *dnode = NULL; dnode = *parent; - if (nb_oper_data_iterate(request_xpath, NULL, 0, - frr_sr_state_data_iter_cb, dnode) - != NB_OK) { + if (nb_oper_iterate_legacy(request_xpath, NULL, 0, NULL, NULL, &dnode)) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: failed to obtain operational data [xpath %s]", __func__, xpath); diff --git a/lib/ntop.c b/lib/ntop.c index 49e1b15..89b4d5e 100644 --- a/lib/ntop.c +++ b/lib/ntop.c @@ -158,7 +158,7 @@ inet4: return dst; } -#if !defined(INET_NTOP_NO_OVERRIDE) && !defined(__APPLE__) +#if !defined(INET_NTOP_NO_OVERRIDE) /* we want to override libc inet_ntop, but make sure it shows up in backtraces * as frr_inet_ntop (to avoid confusion while debugging) */ diff --git a/lib/pbr.h b/lib/pbr.h index d8c06e7..fe2d32a 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -138,7 +138,7 @@ struct pbr_rule { struct pbr_filter filter; struct pbr_action action; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; }; /* TCP flags value shared diff --git a/lib/pid_output.c b/lib/pid_output.c index 064a7bb..ce1b7d1 100644 --- a/lib/pid_output.c +++ b/lib/pid_output.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include "lib/version.h" diff --git a/lib/plist.c b/lib/plist.c index 2f5827c..618d92b 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1229,7 +1229,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, DEFPY (show_ip_prefix_list, show_ip_prefix_list_cmd, - "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]", + "show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR @@ -1242,13 +1242,13 @@ DEFPY (show_ip_prefix_list, if (dseq) dtype = sequential_display; - return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype, + return vty_show_prefix_list(vty, AFI_IP, name, arg_str, dtype, !!uj); } DEFPY (show_ip_prefix_list_prefix, show_ip_prefix_list_prefix_cmd, - "show ip prefix-list WORD A.B.C.D/M$prefix [longer$dl|first-match$dfm]", + "show ip prefix-list PREFIXLIST4_NAME$name A.B.C.D/M$prefix [longer$dl|first-match$dfm]", SHOW_STR IP_STR PREFIX_LIST_STR @@ -1263,13 +1263,13 @@ DEFPY (show_ip_prefix_list_prefix, else if (dfm) dtype = first_match_display; - return vty_show_prefix_list_prefix(vty, AFI_IP, prefix_list, prefix_str, + return vty_show_prefix_list_prefix(vty, AFI_IP, name, prefix_str, dtype); } DEFPY (show_ip_prefix_list_summary, show_ip_prefix_list_summary_cmd, - "show ip prefix-list summary [WORD$prefix_list] [json$uj]", + "show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR @@ -1277,13 +1277,13 @@ DEFPY (show_ip_prefix_list_summary, "Name of a prefix list\n" JSON_STR) { - return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL, + return vty_show_prefix_list(vty, AFI_IP, name, NULL, summary_display, !!uj); } DEFPY (show_ip_prefix_list_detail, show_ip_prefix_list_detail_cmd, - "show ip prefix-list detail [WORD$prefix_list] [json$uj]", + "show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR @@ -1291,25 +1291,25 @@ DEFPY (show_ip_prefix_list_detail, "Name of a prefix list\n" JSON_STR) { - return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL, + return vty_show_prefix_list(vty, AFI_IP, name, NULL, detail_display, !!uj); } DEFPY (clear_ip_prefix_list, clear_ip_prefix_list_cmd, - "clear ip prefix-list [WORD [A.B.C.D/M$prefix]]", + "clear ip prefix-list [PREFIXLIST4_NAME$name [A.B.C.D/M$prefix]]", CLEAR_STR IP_STR PREFIX_LIST_STR "Name of a prefix list\n" "IP prefix /, e.g., 35.0.0.0/8\n") { - return vty_clear_prefix_list(vty, AFI_IP, prefix_list, prefix_str); + return vty_clear_prefix_list(vty, AFI_IP, name, prefix_str); } DEFPY (show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, - "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]", + "show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR @@ -1322,13 +1322,13 @@ DEFPY (show_ipv6_prefix_list, if (dseq) dtype = sequential_display; - return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype, + return vty_show_prefix_list(vty, AFI_IP6, name, arg_str, dtype, !!uj); } DEFPY (show_ipv6_prefix_list_prefix, show_ipv6_prefix_list_prefix_cmd, - "show ipv6 prefix-list WORD X:X::X:X/M$prefix [longer$dl|first-match$dfm]", + "show ipv6 prefix-list PREFIXLIST6_NAME$name X:X::X:X/M$prefix [longer$dl|first-match$dfm]", SHOW_STR IPV6_STR PREFIX_LIST_STR @@ -1343,13 +1343,13 @@ DEFPY (show_ipv6_prefix_list_prefix, else if (dfm) dtype = first_match_display; - return vty_show_prefix_list_prefix(vty, AFI_IP6, prefix_list, + return vty_show_prefix_list_prefix(vty, AFI_IP6, name, prefix_str, dtype); } DEFPY (show_ipv6_prefix_list_summary, show_ipv6_prefix_list_summary_cmd, - "show ipv6 prefix-list summary [WORD$prefix-list] [json$uj]", + "show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR @@ -1357,13 +1357,13 @@ DEFPY (show_ipv6_prefix_list_summary, "Name of a prefix list\n" JSON_STR) { - return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL, + return vty_show_prefix_list(vty, AFI_IP6, name, NULL, summary_display, !!uj); } DEFPY (show_ipv6_prefix_list_detail, show_ipv6_prefix_list_detail_cmd, - "show ipv6 prefix-list detail [WORD$prefix-list] [json$uj]", + "show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR @@ -1371,20 +1371,20 @@ DEFPY (show_ipv6_prefix_list_detail, "Name of a prefix list\n" JSON_STR) { - return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL, + return vty_show_prefix_list(vty, AFI_IP6, name, NULL, detail_display, !!uj); } DEFPY (clear_ipv6_prefix_list, clear_ipv6_prefix_list_cmd, - "clear ipv6 prefix-list [WORD [X:X::X:X/M$prefix]]", + "clear ipv6 prefix-list [PREFIXLIST6_NAME$name [X:X::X:X/M$prefix]]", CLEAR_STR IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" "IPv6 prefix /, e.g., 3ffe::/16\n") { - return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str); + return vty_clear_prefix_list(vty, AFI_IP6, name, prefix_str); } DEFPY (debug_prefix_list_match, @@ -1632,12 +1632,26 @@ static void plist_autocomplete(vector comps, struct cmd_token *token) plist_autocomplete_afi(AFI_IP6, comps, token); } +static void plist4_autocomplete(vector comps, struct cmd_token *token) +{ + plist_autocomplete_afi(AFI_IP, comps, token); +} + +static void plist6_autocomplete(vector comps, struct cmd_token *token) +{ + plist_autocomplete_afi(AFI_IP6, comps, token); +} + static const struct cmd_variable_handler plist_var_handlers[] = { {/* "prefix-list WORD" */ .varname = "prefix_list", .completions = plist_autocomplete}, {.tokenname = "PREFIXLIST_NAME", .completions = plist_autocomplete}, + {.tokenname = "PREFIXLIST4_NAME", + .completions = plist4_autocomplete}, + {.tokenname = "PREFIXLIST6_NAME", + .completions = plist6_autocomplete}, {.completions = NULL}}; diff --git a/lib/prefix.h b/lib/prefix.h index fc6e32d..14f2695 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -286,23 +286,25 @@ struct prefix_sg { struct in_addr grp; }; +/* clang-format off */ union prefixptr { - prefixtype(prefixptr, struct prefix, p) - prefixtype(prefixptr, struct prefix_ipv4, p4) - prefixtype(prefixptr, struct prefix_ipv6, p6) - prefixtype(prefixptr, struct prefix_evpn, evp) - prefixtype(prefixptr, struct prefix_fs, fs) - prefixtype(prefixptr, struct prefix_rd, rd) + uniontype(prefixptr, struct prefix, p) + uniontype(prefixptr, struct prefix_ipv4, p4) + uniontype(prefixptr, struct prefix_ipv6, p6) + uniontype(prefixptr, struct prefix_evpn, evp) + uniontype(prefixptr, struct prefix_fs, fs) + uniontype(prefixptr, struct prefix_rd, rd) } TRANSPARENT_UNION; union prefixconstptr { - prefixtype(prefixconstptr, const struct prefix, p) - prefixtype(prefixconstptr, const struct prefix_ipv4, p4) - prefixtype(prefixconstptr, const struct prefix_ipv6, p6) - prefixtype(prefixconstptr, const struct prefix_evpn, evp) - prefixtype(prefixconstptr, const struct prefix_fs, fs) - prefixtype(prefixconstptr, const struct prefix_rd, rd) + uniontype(prefixconstptr, const struct prefix, p) + uniontype(prefixconstptr, const struct prefix_ipv4, p4) + uniontype(prefixconstptr, const struct prefix_ipv6, p6) + uniontype(prefixconstptr, const struct prefix_evpn, evp) + uniontype(prefixconstptr, const struct prefix_fs, fs) + uniontype(prefixconstptr, const struct prefix_rd, rd) } TRANSPARENT_UNION; +/* clang-format on */ #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c index ac775be..b2ba1a7 100644 --- a/lib/printf/printf-pos.c +++ b/lib/printf/printf-pos.c @@ -355,7 +355,7 @@ reswitch: switch (ch) { goto rflag; case 'C': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'c': error = addtype(&types, (flags & LONGINT) ? T_WINT : T_INT); @@ -364,7 +364,7 @@ reswitch: switch (ch) { break; case 'D': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'd': case 'i': if ((error = addsarg(&types, flags))) @@ -408,7 +408,7 @@ reswitch: switch (ch) { #endif case 'O': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'o': if ((error = adduarg(&types, flags))) goto error; @@ -419,7 +419,7 @@ reswitch: switch (ch) { break; case 'S': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 's': error = addtype(&types, (flags & LONGINT) ? TP_WCHAR : TP_CHAR); @@ -428,7 +428,7 @@ reswitch: switch (ch) { break; case 'U': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'u': case 'X': case 'x': @@ -549,7 +549,7 @@ reswitch: switch (ch) { goto rflag; case 'C': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'c': error = addtype(&types, (flags & LONGINT) ? T_WINT : T_INT); @@ -558,7 +558,7 @@ reswitch: switch (ch) { break; case 'D': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'd': case 'i': if ((error = addsarg(&types, flags))) @@ -602,7 +602,7 @@ reswitch: switch (ch) { #endif case 'O': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'o': if ((error = adduarg(&types, flags))) goto error; @@ -613,7 +613,7 @@ reswitch: switch (ch) { break; case 'S': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 's': error = addtype(&types, (flags & LONGINT) ? TP_WCHAR : TP_CHAR); @@ -622,7 +622,7 @@ reswitch: switch (ch) { break; case 'U': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'u': case 'X': case 'x': diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index 78f8be0..2083642 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -340,7 +340,7 @@ reswitch: switch (ch) { if (width >= 0) goto rflag; width = -width; - /* FALLTHROUGH */ + fallthrough; case '-': flags |= LADJUST; goto rflag; @@ -434,7 +434,7 @@ reswitch: switch (ch) { break; case 'C': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'c': #ifdef WCHAR_SUPPORT if (flags & LONGINT) { @@ -460,7 +460,7 @@ reswitch: switch (ch) { break; case 'D': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'd': case 'i': if (flags & INTMAX_SIZE) @@ -551,7 +551,7 @@ reswitch: switch (ch) { break; case 'O': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'o': if (flags & INTMAX_SIZE) ujval = UJARG(); @@ -595,7 +595,7 @@ reswitch: switch (ch) { goto nosign; case 'S': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 's': #ifdef WCHAR_SUPPORT if (flags & LONGINT) { @@ -621,7 +621,7 @@ reswitch: switch (ch) { break; case 'U': flags |= LONGINT; - /*FALLTHROUGH*/ + fallthrough; case 'u': if (flags & INTMAX_SIZE) ujval = UJARG(); diff --git a/lib/privs.c b/lib/privs.c index accd989..717a2e4 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -6,6 +6,15 @@ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. */ #include + +#include +#include + +#ifdef HAVE_LCAPS +#include +#include +#endif /* HAVE_LCAPS */ + #include "log.h" #include "privs.h" #include "memory.h" diff --git a/lib/pullwr.c b/lib/pullwr.c index 3967eb5..919a663 100644 --- a/lib/pullwr.c +++ b/lib/pullwr.c @@ -6,6 +6,8 @@ #include "zebra.h" +#include + #include "pullwr.h" #include "memory.h" #include "monotime.h" diff --git a/lib/resolver.c b/lib/resolver.c index 99bf356..d8245e3 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -335,3 +335,8 @@ void resolver_init(struct event_loop *tm) install_element(CONFIG_NODE, &debug_resolver_cmd); install_element(ENABLE_NODE, &debug_resolver_cmd); } + +void resolver_terminate(void) +{ + ares_destroy(state.channel); +} diff --git a/lib/resolver.h b/lib/resolver.h index 87e8ecd..882f960 100644 --- a/lib/resolver.h +++ b/lib/resolver.h @@ -23,6 +23,7 @@ struct resolver_query { }; void resolver_init(struct event_loop *tm); +void resolver_terminate(void); void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, const char *hostname, void (*cb)(struct resolver_query *, const char *, int, diff --git a/lib/route_types.txt b/lib/route_types.txt index a82273a..93cbc36 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -49,6 +49,7 @@ ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, 0, "Reserved", none ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, 1, "kernel route", zebra ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, 1, "connected", zebra +ZEBRA_ROUTE_LOCAL, local, zebra, 'L', 1, 1, 1, "local", zebra ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, 1, "static", zebra ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, 1, "RIP", ripd ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, 1, "RIPng", ripngd @@ -86,6 +87,7 @@ ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric", fa ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP", vrrpd ZEBRA_ROUTE_NHG, zebra, none, '-', 0, 0, 0, "Nexthop Group", none ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE", none +ZEBRA_ROUTE_TABLE_DIRECT, table-direct, zebra, 't', 1, 1, 1, "Table-Direct", zebra ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-", none @@ -93,6 +95,7 @@ ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-", no ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" ZEBRA_ROUTE_KERNEL, "Kernel routes (not installed via the zebra RIB)" ZEBRA_ROUTE_CONNECT,"Connected routes (directly attached subnet or host)" +ZEBRA_ROUTE_LOCAL, "Local routes (directly attached host route)" ZEBRA_ROUTE_STATIC, "Statically configured routes" ZEBRA_ROUTE_RIP, "Routing Information Protocol (RIP)" ZEBRA_ROUTE_RIPNG, "Routing Information Protocol next-generation (IPv6) (RIPng)" @@ -116,3 +119,4 @@ ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)" ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)" ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol" ZEBRA_ROUTE_NHG, "Zebra Nexthop Groups (NHG)" +ZEBRA_ROUTE_TABLE_DIRECT, "Non-main Kernel Routing Table - Direct" diff --git a/lib/routemap.c b/lib/routemap.c index e0b0eb7..ea917eb 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1070,20 +1070,17 @@ static int vty_show_route_map(struct vty *vty, const char *name, bool use_json) { struct route_map *map; json_object *json = NULL; - json_object *json_proto = NULL; - if (use_json) { + if (use_json) json = json_object_new_object(); - json_proto = json_object_new_object(); - json_object_object_add(json, frr_protonameinst, json_proto); - } else + else vty_out(vty, "%s:\n", frr_protonameinst); if (name) { map = route_map_lookup_by_name(name); if (map) { - vty_show_route_map_entry(vty, map, json_proto); + vty_show_route_map_entry(vty, map, json); } else if (!use_json) { vty_out(vty, "%s: 'route-map %s' not found\n", frr_protonameinst, name); @@ -1099,7 +1096,7 @@ static int vty_show_route_map(struct vty *vty, const char *name, bool use_json) list_sort(maplist, sort_route_map); for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) - vty_show_route_map_entry(vty, map, json_proto); + vty_show_route_map_entry(vty, map, json); list_delete(&maplist); } @@ -2551,7 +2548,6 @@ route_map_result_t route_map_apply_ext(struct route_map *map, struct route_map_index *index = NULL; struct route_map_rule *set = NULL; bool skip_match_clause = false; - struct prefix conv; if (recursion > RMAP_RECURSION_LIMIT) { if (map) @@ -2574,31 +2570,14 @@ route_map_result_t route_map_apply_ext(struct route_map *map, map->applied++; - /* - * Handling for matching evpn_routes in the prefix table. - * - * We convert type2/5 prefix to ipv4/6 prefix to do longest - * prefix matching on. - */ if (prefix->family == AF_EVPN) { - if (evpn_prefix2prefix(prefix, &conv) != 0) { - if (unlikely(CHECK_FLAG(rmap_debug, - DEBUG_ROUTEMAP_DETAIL))) - zlog_debug( - "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup", - prefix); - } else { - if (unlikely(CHECK_FLAG(rmap_debug, - DEBUG_ROUTEMAP_DETAIL))) - zlog_debug( - "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup", - prefix, &conv); - - prefix = &conv; - } + index = map->head; + } else { + skip_match_clause = true; + index = route_map_get_index(map, prefix, match_object, + &match_ret); } - index = route_map_get_index(map, prefix, match_object, &match_ret); if (index) { index->applied++; if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) @@ -2622,7 +2601,6 @@ route_map_result_t route_map_apply_ext(struct route_map *map, ret = RMAP_DENYMATCH; goto route_map_apply_end; } - skip_match_clause = true; for (; index; index = index->next) { if (!skip_match_clause) { @@ -3145,13 +3123,13 @@ DEFPY (rmap_clear_counters, } -DEFUN (rmap_show_name, - rmap_show_name_cmd, - "show route-map [WORD] [json]", - SHOW_STR - "route-map information\n" - "route-map name\n" - JSON_STR) +DEFUN_NOSH (rmap_show_name, + rmap_show_name_cmd, + "show route-map [WORD] [json]", + SHOW_STR + "route-map information\n" + "route-map name\n" + JSON_STR) { bool uj = use_json(argc, argv); int idx = 0; @@ -3412,7 +3390,7 @@ DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd, } /* Initialization of route map vector. */ -void route_map_init(void) +void route_map_init_new(bool in_backend) { int i; @@ -3427,7 +3405,10 @@ void route_map_init(void) UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP); - route_map_cli_init(); + if (!in_backend) { + /* we do not want to handle config commands in the backend */ + route_map_cli_init(); + } /* Install route map top node. */ install_node(&rmap_debug_node); @@ -3447,3 +3428,8 @@ void route_map_init(void) install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd); } + +void route_map_init(void) +{ + route_map_init_new(false); +} diff --git a/lib/routemap.h b/lib/routemap.h index 08e3412..dfb84ce 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -401,6 +401,7 @@ enum ecommunity_lb_type { /* Prototypes. */ extern void route_map_init(void); +extern void route_map_init_new(bool in_backend); /* * This should only be called on shutdown @@ -1024,6 +1025,7 @@ routemap_hook_context_insert(struct route_map_index *rmi); void routemap_hook_context_free(struct routemap_hook_context *rhc); extern const struct frr_yang_module_info frr_route_map_info; +extern const struct frr_yang_module_info frr_route_map_cli_info; /* routemap_cli.c */ extern int route_map_instance_cmp(const struct lyd_node *dnode1, diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 20a157e..88b341c 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -90,8 +90,8 @@ DEFPY_YANG( int route_map_instance_cmp(const struct lyd_node *dnode1, const struct lyd_node *dnode2) { - uint16_t seq1 = yang_dnode_get_uint16(dnode1, "./sequence"); - uint16_t seq2 = yang_dnode_get_uint16(dnode2, "./sequence"); + uint16_t seq1 = yang_dnode_get_uint16(dnode1, "sequence"); + uint16_t seq2 = yang_dnode_get_uint16(dnode2, "sequence"); return seq1 - seq2; } @@ -100,8 +100,8 @@ void route_map_instance_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { const char *name = yang_dnode_get_string(dnode, "../name"); - const char *action = yang_dnode_get_string(dnode, "./action"); - const char *sequence = yang_dnode_get_string(dnode, "./sequence"); + const char *action = yang_dnode_get_string(dnode, "action"); + const char *sequence = yang_dnode_get_string(dnode, "sequence"); vty_out(vty, "route-map %s %s %s\n", name, action, sequence); @@ -188,7 +188,7 @@ DEFPY_YANG( DEFPY_YANG( match_ip_address_prefix_list, match_ip_address_prefix_list_cmd, - "match ip address prefix-list PREFIXLIST_NAME$name", + "match ip address prefix-list PREFIXLIST4_NAME$name", MATCH_STR IP_STR "Match address of route\n" @@ -209,7 +209,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd, - "no match ip address prefix-list [PREFIXLIST_NAME]", + "no match ip address prefix-list [PREFIXLIST4_NAME]", NO_STR MATCH_STR IP_STR @@ -265,7 +265,7 @@ DEFPY_YANG( DEFPY_YANG( match_ip_next_hop_prefix_list, match_ip_next_hop_prefix_list_cmd, - "match ip next-hop prefix-list PREFIXLIST_NAME$name", + "match ip next-hop prefix-list PREFIXLIST4_NAME$name", MATCH_STR IP_STR "Match next-hop address of route\n" @@ -287,7 +287,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ip_next_hop_prefix_list, no_match_ip_next_hop_prefix_list_cmd, - "no match ip next-hop prefix-list [PREFIXLIST_NAME]", + "no match ip next-hop prefix-list [PREFIXLIST4_NAME]", NO_STR MATCH_STR IP_STR @@ -379,7 +379,7 @@ DEFPY_YANG( DEFPY_YANG( match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd, - "match ipv6 address prefix-list PREFIXLIST_NAME$name", + "match ipv6 address prefix-list PREFIXLIST6_NAME$name", MATCH_STR IPV6_STR "Match address of route\n" @@ -401,7 +401,7 @@ DEFPY_YANG( DEFPY_YANG( no_match_ipv6_address_prefix_list, no_match_ipv6_address_prefix_list_cmd, - "no match ipv6 address prefix-list [PREFIXLIST_NAME]", + "no match ipv6 address prefix-list [PREFIXLIST6_NAME]", NO_STR MATCH_STR IPV6_STR @@ -526,7 +526,7 @@ DEFPY_YANG( void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *condition = yang_dnode_get_string(dnode, "./condition"); + const char *condition = yang_dnode_get_string(dnode, "condition"); const struct lyd_node *ln; const char *acl; @@ -1050,7 +1050,7 @@ DEFUN_YANG (no_set_srte_color, void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *action = yang_dnode_get_string(dnode, "./action"); + const char *action = yang_dnode_get_string(dnode, "action"); const struct lyd_node *ln; const char *acl; @@ -1102,7 +1102,7 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, "./rmap-set-action/max-metric")); } else if (IS_SET_TAG(action)) { vty_out(vty, " set tag %s\n", - yang_dnode_get_string(dnode, "./rmap-set-action/tag")); + yang_dnode_get_string(dnode, "rmap-set-action/tag")); } else if (IS_SET_SR_TE_COLOR(action)) { vty_out(vty, " set sr-te color %s\n", yang_dnode_get_string(dnode, @@ -1196,7 +1196,7 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, " set large-comm-list %s delete\n", acl); } else if (IS_SET_EXTCOMM_LIST_DEL(action)) { acl = NULL; - ln = yang_dnode_get(dnode, "./rmap-set-action/frr-bgp-route-map:comm-list-name"); + ln = yang_dnode_get(dnode, "rmap-set-action/frr-bgp-route-map:comm-list-name"); if (ln) acl = yang_dnode_get_string(ln, NULL); diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index 5767e0a..1bba4da 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -98,7 +98,7 @@ static int lib_route_map_create(struct nb_cb_create_args *args) /* NOTHING */ break; case NB_EV_APPLY: - rm_name = yang_dnode_get_string(args->dnode, "./name"); + rm_name = yang_dnode_get_string(args->dnode, "name"); rm = route_map_get(rm_name); nb_running_set_entry(args->dnode, rm); break; @@ -167,8 +167,8 @@ static int lib_route_map_entry_create(struct nb_cb_create_args *args) /* NOTHING */ break; case NB_EV_APPLY: - sequence = yang_dnode_get_uint16(args->dnode, "./sequence"); - action = yang_dnode_get_enum(args->dnode, "./action") == 0 + sequence = yang_dnode_get_uint16(args->dnode, "sequence"); + action = yang_dnode_get_enum(args->dnode, "action") == 0 ? RMAP_PERMIT : RMAP_DENY; rm = nb_running_get_entry(args->dnode, NULL, true); @@ -364,7 +364,6 @@ lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args) case 0: /* permit-or-deny */ break; case 1: /* next */ - /* FALLTHROUGH */ case 2: /* goto */ rm_action = yang_dnode_get_enum(args->dnode, "../action"); @@ -885,7 +884,7 @@ static int lib_route_map_entry_set_action_ipv4_address_modify( yang_dnode_get_ipv4(&ia, args->dnode, NULL); if (ia.s_addr == INADDR_ANY || !ipv4_unicast_valid(&ia)) return NB_ERR_VALIDATION; - /* FALLTHROUGH */ + return NB_OK; case NB_EV_PREPARE: case NB_EV_ABORT: return NB_OK; @@ -944,7 +943,7 @@ static int lib_route_map_entry_set_action_ipv6_address_modify( yang_dnode_get_ipv6(&i6a, args->dnode, NULL); if (!IN6_IS_ADDR_LINKLOCAL(&i6a)) return NB_ERR_VALIDATION; - /* FALLTHROUGH */ + return NB_OK; case NB_EV_PREPARE: case NB_EV_ABORT: return NB_OK; @@ -1551,3 +1550,45 @@ const struct frr_yang_module_info frr_route_map_info = { }, } }; + +const struct frr_yang_module_info frr_route_map_cli_info = { + .name = "frr-route-map", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/optimization-disabled", + .cbs.cli_show = route_map_optimization_disabled_show, + }, + { + .xpath = "/frr-route-map:lib/route-map/entry", + .cbs = { + .cli_cmp = route_map_instance_cmp, + .cli_show = route_map_instance_show, + .cli_show_end = route_map_instance_show_end, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/description", + .cbs.cli_show = route_map_description_show, + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/call", + .cbs.cli_show = route_map_call_show, + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/exit-policy", + .cbs.cli_show = route_map_exit_policy_show, + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition", + .cbs.cli_show = route_map_condition_show, + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action", + .cbs.cli_show = route_map_action_show, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/routing_nb.c b/lib/routing_nb.c index 3d837bc..33372d1 100644 --- a/lib/routing_nb.c +++ b/lib/routing_nb.c @@ -27,3 +27,13 @@ const struct frr_yang_module_info frr_routing_info = { }, } }; + +const struct frr_yang_module_info frr_routing_cli_info = { + .name = "frr-routing", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = NULL, + }, + } +}; diff --git a/lib/routing_nb.h b/lib/routing_nb.h index c185091..e805e1c 100644 --- a/lib/routing_nb.h +++ b/lib/routing_nb.h @@ -6,6 +6,7 @@ extern "C" { #endif extern const struct frr_yang_module_info frr_routing_info; +extern const struct frr_yang_module_info frr_routing_cli_info; /* Mandatory callbacks. */ int routing_control_plane_protocols_control_plane_protocol_create( @@ -29,6 +30,8 @@ int routing_control_plane_protocols_control_plane_protocol_destroy( * based on the control plane protocol */ DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args)); +DECLARE_HOOK(routing_create, (struct nb_cb_create_args *args), (args)); +DECLARE_KOOH(routing_destroy, (struct nb_cb_destroy_args *args), (args)); void routing_control_plane_protocols_register_vrf_dependency(void); diff --git a/lib/routing_nb_config.c b/lib/routing_nb_config.c index f4fe48f..d532279 100644 --- a/lib/routing_nb_config.c +++ b/lib/routing_nb_config.c @@ -14,6 +14,8 @@ DEFINE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args)); +DEFINE_HOOK(routing_create, (struct nb_cb_create_args *args), (args)); +DEFINE_KOOH(routing_destroy, (struct nb_cb_destroy_args *args), (args)); /* * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol @@ -44,11 +46,12 @@ int routing_control_plane_protocols_control_plane_protocol_create( * find the vrf and store the pointer. */ if (nb_node_has_dependency(args->dnode->schema->priv)) { - vrfname = yang_dnode_get_string(args->dnode, "./vrf"); + vrfname = yang_dnode_get_string(args->dnode, "vrf"); vrf = vrf_lookup_by_name(vrfname); assert(vrf); nb_running_set_entry(args->dnode, vrf); } + hook_call(routing_create, args); break; }; @@ -61,6 +64,8 @@ int routing_control_plane_protocols_control_plane_protocol_destroy( if (args->event != NB_EV_APPLY) return NB_OK; + hook_call(routing_destroy, args); + /* * If dependency on VRF module is registered, then VRF * pointer was stored and must be cleared. @@ -76,7 +81,7 @@ static void vrf_to_control_plane_protocol(const struct lyd_node *dnode, { const char *vrf; - vrf = yang_dnode_get_string(dnode, "./name"); + vrf = yang_dnode_get_string(dnode, "name"); snprintf(xpath, XPATH_MAXLEN, FRR_ROUTING_KEY_XPATH_VRF, vrf); } @@ -86,7 +91,7 @@ static void control_plane_protocol_to_vrf(const struct lyd_node *dnode, { const char *vrf; - vrf = yang_dnode_get_string(dnode, "./vrf"); + vrf = yang_dnode_get_string(dnode, "vrf"); snprintf(xpath, XPATH_MAXLEN, FRR_VRF_KEY_XPATH, vrf); } diff --git a/lib/sigevent.c b/lib/sigevent.c index 3cd65eb..06f80db 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -4,6 +4,8 @@ */ #include + +#include #include #include #include diff --git a/lib/sockopt.c b/lib/sockopt.c index 41fcc41..74bc034 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -10,13 +10,12 @@ #include "sockunion.h" #include "lib_errors.h" -#if (defined(__FreeBSD__) \ - && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) \ - || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) \ - || (defined(__NetBSD__) && defined(__NetBSD_Version__) \ - && __NetBSD_Version__ >= 106010000) \ - || defined(__OpenBSD__) || defined(__APPLE__) \ - || defined(__DragonFly__) || defined(__sun) +#if (defined(__FreeBSD__) && \ + ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || \ + (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || \ + (defined(__NetBSD__) && defined(__NetBSD_Version__) && \ + __NetBSD_Version__ >= 106010000) || \ + defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) #define HAVE_BSD_STRUCT_IP_MREQ_HACK #endif @@ -673,6 +672,9 @@ int sockopt_tcp_mss_get(int sock) int tcp_maxseg = 0; socklen_t tcp_maxseg_len = sizeof(tcp_maxseg); + if (sock < 0) + return 0; + ret = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, &tcp_maxseg_len); if (ret != 0) { diff --git a/lib/sockunion.h b/lib/sockunion.h index e507255..1466512 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -7,10 +7,13 @@ #ifndef _ZEBRA_SOCKUNION_H #define _ZEBRA_SOCKUNION_H +#include "compiler.h" + #include "privs.h" #include "if.h" #include #ifdef __OpenBSD__ +#include #include #endif @@ -27,8 +30,40 @@ union sockunion { struct sockaddr_mpls smpls; struct sockaddr_rtlabel rtlabel; #endif + + /* sockaddr_storage is guaranteed to be larger than the others */ + struct sockaddr_storage sa_storage; }; +/* clang-format off */ +/* for functions that want to accept any sockaddr pointer without casts */ +union sockaddrptr { + uniontype(sockaddrptr, union sockunion, su) + uniontype(sockaddrptr, struct sockaddr, sa) + uniontype(sockaddrptr, struct sockaddr_in, sin) + uniontype(sockaddrptr, struct sockaddr_in6, sin6) + uniontype(sockaddrptr, struct sockaddr_un, sun) +#ifdef __OpenBSD__ + uniontype(sockaddrptr, struct sockaddr_mpls, smpls) + uniontype(sockaddrptr, struct sockaddr_rtlabel, rtlabel) +#endif + uniontype(sockaddrptr, struct sockaddr_storage, sa_storage) +} TRANSPARENT_UNION; + +union sockaddrconstptr { + uniontype(sockaddrconstptr, const union sockunion, su) + uniontype(sockaddrconstptr, const struct sockaddr, sa) + uniontype(sockaddrconstptr, const struct sockaddr_in, sin) + uniontype(sockaddrconstptr, const struct sockaddr_in6, sin6) + uniontype(sockaddrconstptr, const struct sockaddr_un, sun) +#ifdef __OpenBSD__ + uniontype(sockaddrconstptr, const struct sockaddr_mpls, smpls) + uniontype(sockaddrconstptr, const struct sockaddr_rtlabel, rtlabel) +#endif + uniontype(sockaddrconstptr, const struct sockaddr_storage, sa_storage) +} TRANSPARENT_UNION; +/* clang-format on */ + enum connect_result { connect_error, connect_success, connect_in_progress }; /* Default address family. */ diff --git a/lib/srv6.c b/lib/srv6.c index 09835f3..dceb6ab 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -108,7 +108,7 @@ const char *seg6local_context2str(char *str, size_t size, } } -static void srv6_locator_chunk_list_free(void *data) +void srv6_locator_chunk_list_free(void *data) { struct srv6_locator_chunk *chunk = data; diff --git a/lib/srv6.h b/lib/srv6.h index fb34f86..433c5c1 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -252,6 +252,7 @@ int snprintf_seg6_segs(char *str, extern struct srv6_locator *srv6_locator_alloc(const char *name); extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void); extern void srv6_locator_free(struct srv6_locator *locator); +extern void srv6_locator_chunk_list_free(void *data); extern void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk); json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk); json_object *srv6_locator_json(const struct srv6_locator *loc); diff --git a/lib/subdir.am b/lib/subdir.am index d7b28ff..6893049 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -68,6 +68,7 @@ lib_libfrr_la_SOURCES = \ lib/mgmt_be_client.c \ lib/mgmt_fe_client.c \ lib/mgmt_msg.c \ + lib/mgmt_msg_native.c \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ @@ -80,6 +81,7 @@ lib_libfrr_la_SOURCES = \ lib/northbound.c \ lib/northbound_cli.c \ lib/northbound_db.c \ + lib/northbound_oper.c \ lib/ntop.c \ lib/openbsd-tree.c \ lib/pid_output.c \ @@ -219,11 +221,13 @@ pkginclude_HEADERS += \ lib/filter.h \ lib/flex_algo.h \ lib/freebsd-queue.h \ + lib/frrdistance.h \ lib/frrlua.h \ lib/frrscript.h \ lib/frr_pthread.h \ lib/frratomic.h \ lib/frrcu.h \ + lib/frrsendmmsg.h \ lib/frrstr.h \ lib/graph.h \ lib/hash.h \ @@ -252,8 +256,10 @@ pkginclude_HEADERS += \ lib/memory.h \ lib/mgmt.pb-c.h \ lib/mgmt_be_client.h \ + lib/mgmt_defines.h \ lib/mgmt_fe_client.h \ lib/mgmt_msg.h \ + lib/mgmt_msg_native.h \ lib/mgmt_pb.h \ lib/module.h \ lib/monotime.h \ @@ -534,12 +540,22 @@ EXTRA_DIST += \ BUILT_SOURCES += \ lib/gitversion.h \ lib/route_types.h \ + lib/vtysh_daemons.h \ # end ## force route_types.h $(lib_clippy_OBJECTS): lib/route_types.h $(lib_libfrr_la_OBJECTS): lib/route_types.h +# force lib_daemons.h +$(lib_libfrr_la_OBJECTS): lib/vtysh_daemons.h + +CLEANFILES += lib/vtysh_daemons.h +lib/vtysh_daemons.h: + @$(MKDIR_P) lib + $(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > lib/vtysh_daemons.h + + AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ @BISON_VERBOSE@ lib/command_lex.h: lib/command_lex.c diff --git a/lib/systemd.c b/lib/systemd.c index 56a53a6..a82c376 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -5,6 +5,7 @@ */ #include +#include #include #include "frrevent.h" diff --git a/lib/vector.c b/lib/vector.c index bbea67c..60d3831 100644 --- a/lib/vector.c +++ b/lib/vector.c @@ -4,6 +4,7 @@ */ #include +#include #include "vector.h" #include "memory.h" diff --git a/lib/vrf.c b/lib/vrf.c index b279e8b..e907626 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -5,6 +5,7 @@ */ #include +#include #include "if.h" #include "vrf.h" @@ -325,6 +326,33 @@ void vrf_disable(struct vrf *vrf) (*vrf_master.vrf_disable_hook)(vrf); } +void vrf_iterate(vrf_iter_func fnc) +{ + struct vrf *vrf, *tmp; + + if (debug_vrf) + zlog_debug("%s: vrf subsystem iteration", __func__); + + RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) { + if (vrf->vrf_id == VRF_DEFAULT) + continue; + + fnc(vrf); + } + + RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) { + if (vrf->vrf_id == VRF_DEFAULT) + continue; + + fnc(vrf); + } + + /* Finally process default VRF */ + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (vrf) + fnc(vrf); +} + const char *vrf_id_to_name(vrf_id_t vrf_id) { struct vrf *vrf; @@ -541,32 +569,12 @@ static void vrf_terminate_single(struct vrf *vrf) vrf_delete(vrf); } -/* Terminate VRF module. */ void vrf_terminate(void) { - struct vrf *vrf, *tmp; - if (debug_vrf) zlog_debug("%s: Shutting down vrf subsystem", __func__); - RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) { - if (vrf->vrf_id == VRF_DEFAULT) - continue; - - vrf_terminate_single(vrf); - } - - RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) { - if (vrf->vrf_id == VRF_DEFAULT) - continue; - - vrf_terminate_single(vrf); - } - - /* Finally terminate default VRF */ - vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (vrf) - vrf_terminate_single(vrf); + vrf_iterate(vrf_terminate_single); } int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, @@ -680,18 +688,6 @@ DEFUN_YANG (no_vrf, const char *vrfname = argv[2]->arg; char xpath_list[XPATH_MAXLEN]; - struct vrf *vrfp; - - vrfp = vrf_lookup_by_name(vrfname); - - if (vrfp == NULL) - return CMD_SUCCESS; - - if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) { - vty_out(vty, "%% Only inactive VRFs can be deleted\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { /* * Remove the VRF interface config when removing the VRF. @@ -914,7 +910,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args) const char *vrfname; struct vrf *vrfp; - vrfname = yang_dnode_get_string(args->dnode, "./name"); + vrfname = yang_dnode_get_string(args->dnode, "name"); if (args->event != NB_EV_APPLY) return NB_OK; @@ -955,6 +951,25 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +static void lib_vrf_cli_write(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + const char *name = yang_dnode_get_string(dnode, "name"); + + if (strcmp(name, VRF_DEFAULT_NAME)) { + vty_out(vty, "!\n"); + vty_out(vty, "vrf %s\n", name); + } +} + +static void lib_vrf_cli_write_end(struct vty *vty, const struct lyd_node *dnode) +{ + const char *name = yang_dnode_get_string(dnode, "name"); + + if (strcmp(name, VRF_DEFAULT_NAME)) + vty_out(vty, "exit-vrf\n"); +} + static const void *lib_vrf_get_next(struct nb_cb_get_next_args *args) { struct vrf *vrfp = (struct vrf *)args->list_entry; @@ -987,6 +1002,19 @@ static const void *lib_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args) return vrf; } +static const void *lib_vrf_lookup_next(struct nb_cb_lookup_entry_args *args) +{ + const char *vrfname = args->keys->key[0]; + struct vrf vrfkey, *vrf; + + strlcpy(vrfkey.name, vrfname, sizeof(vrfkey.name)); + vrf = RB_FIND(vrf_name_head, &vrfs_by_name, &vrfkey); + if (!strcmp(vrf->name, vrfname)) + vrf = RB_NEXT(vrf_name_head, vrf); + + return vrf; +} + /* * XPath: /frr-vrf:lib/vrf/id */ @@ -1013,6 +1041,8 @@ lib_vrf_state_active_get_elem(struct nb_cb_get_elem_args *args) } /* clang-format off */ + +/* cli_show callbacks are kept here for daemons not yet converted to mgmtd */ const struct frr_yang_module_info frr_vrf_info = { .name = "frr-vrf", .nodes = { @@ -1021,9 +1051,12 @@ const struct frr_yang_module_info frr_vrf_info = { .cbs = { .create = lib_vrf_create, .destroy = lib_vrf_destroy, + .cli_show = lib_vrf_cli_write, + .cli_show_end = lib_vrf_cli_write_end, .get_next = lib_vrf_get_next, .get_keys = lib_vrf_get_keys, .lookup_entry = lib_vrf_lookup_entry, + .lookup_next = lib_vrf_lookup_next, }, .priority = NB_DFLT_PRIORITY - 2, }, @@ -1045,3 +1078,19 @@ const struct frr_yang_module_info frr_vrf_info = { } }; +const struct frr_yang_module_info frr_vrf_cli_info = { + .name = "frr-vrf", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-vrf:lib/vrf", + .cbs = { + .cli_show = lib_vrf_cli_write, + .cli_show_end = lib_vrf_cli_write_end, + }, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/vrf.h b/lib/vrf.h index f66a9e6..3ebb6dd 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -201,6 +201,12 @@ extern void vrf_init(int (*create)(struct vrf *vrf), int (*disable)(struct vrf *vrf), int (*destroy)(struct vrf *vrf)); +/* + * Iterate over custom VRFs and round up by processing the default VRF. + */ +typedef void (*vrf_iter_func)(struct vrf *vrf); +extern void vrf_iterate(vrf_iter_func fnc); + /* * Call vrf_terminate when the protocol is being shutdown */ @@ -294,6 +300,7 @@ extern int vrf_enable(struct vrf *vrf); extern void vrf_delete(struct vrf *vrf); extern const struct frr_yang_module_info frr_vrf_info; +extern const struct frr_yang_module_info frr_vrf_cli_info; #ifdef __cplusplus } diff --git a/lib/vty.c b/lib/vty.c index ed8b71e..1c9cff4 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -6,6 +6,8 @@ #include +#include +#include #include #include #include @@ -44,6 +46,8 @@ #include #include +#include "lib/config_paths.h" + #include "lib/vty_clippy.c" DEFINE_MTYPE_STATIC(LIB, VTY, "VTY"); @@ -122,6 +126,13 @@ bool vty_log_commands; static bool vty_log_commands_perm; char const *const mgmt_daemons[] = { + "zebra", +#ifdef HAVE_RIPD + "ripd", +#endif +#ifdef HAVE_RIPNGD + "ripngd", +#endif #ifdef HAVE_STATICD "staticd", #endif @@ -157,10 +168,9 @@ static int vty_mgmt_unlock_running_inline(struct vty *vty) return vty->mgmt_locked_running_ds ? -1 : 0; } -void vty_mgmt_resume_response(struct vty *vty, bool success) +void vty_mgmt_resume_response(struct vty *vty, int ret) { uint8_t header[4] = {0, 0, 0, 0}; - int ret = CMD_SUCCESS; if (!vty->mgmt_req_pending_cmd) { zlog_err( @@ -168,14 +178,10 @@ void vty_mgmt_resume_response(struct vty *vty, bool success) return; } - if (!success) - ret = CMD_WARNING_CONFIG_FAILED; - - MGMTD_FE_CLIENT_DBG( - "resuming CLI cmd after %s on vty session-id: %" PRIu64 - " with '%s'", - vty->mgmt_req_pending_cmd, vty->mgmt_session_id, - success ? "succeeded" : "failed"); + debug_fe_client("resuming CLI cmd after %s on vty session-id: %" PRIu64 + " with '%s'", + vty->mgmt_req_pending_cmd, vty->mgmt_session_id, + ret == CMD_SUCCESS ? "success" : "failed"); vty->mgmt_req_pending_cmd = NULL; @@ -383,11 +389,14 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json) return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE); } -void vty_json_empty(struct vty *vty) +void vty_json_empty(struct vty *vty, struct json_object *json) { - json_object *json = json_object_new_object(); + json_object *jsonobj = json; + + if (!json) + jsonobj = json_object_new_object(); - vty_json(vty, json); + vty_json(vty, jsonobj); } /* Output current time to the vty. */ @@ -1565,7 +1574,7 @@ static void vty_read(struct event *thread) break; case '\r': vty->escape = VTY_CR; - /* fallthru */ + fallthrough; case '\n': vty_out(vty, "\n"); buffer_flush_available(vty->obuf, vty->wfd); @@ -2256,19 +2265,6 @@ bool mgmt_vty_read_configs(void) snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir); confp = vty_open_config(path, config_default); - if (!confp) { - char *orig; - - snprintf(path, sizeof(path), "%s/zebra.conf", frr_sysconfdir); - orig = XSTRDUP(MTYPE_TMP, host_config_get()); - - zlog_info("mgmtd: trying backup config file: %s", path); - confp = vty_open_config(path, config_default); - - host_config_set(path); - XFREE(MTYPE_TMP, orig); - } - if (confp) { zlog_info("mgmtd: reading config file: %s", path); @@ -2406,10 +2402,9 @@ static void vtysh_read(struct event *thread) * we get response through callback. */ if (vty->mgmt_req_pending_cmd) { - MGMTD_FE_CLIENT_DBG( - "postpone CLI response pending mgmtd %s on vty session-id %" PRIu64, - vty->mgmt_req_pending_cmd, - vty->mgmt_session_id); + debug_fe_client("postpone CLI response pending mgmtd %s on vty session-id %" PRIu64, + vty->mgmt_req_pending_cmd, + vty->mgmt_session_id); return; } @@ -2490,14 +2485,14 @@ void vty_close(struct vty *vty) * so warn the user. */ if (vty->mgmt_num_pending_setcfg) - MGMTD_FE_CLIENT_ERR( + log_err_fe_client( "vty closed, uncommitted config will be lost."); /* Drop out of configure / transaction if needed. */ vty_config_exit(vty); if (mgmt_fe_client && vty->mgmt_session_id) { - MGMTD_FE_CLIENT_DBG("closing vty session"); + debug_fe_client("closing vty session"); mgmt_fe_destroy_client_session(mgmt_fe_client, vty->mgmt_client_id); vty->mgmt_session_id = 0; @@ -3477,9 +3472,8 @@ void vty_init_vtysh(void) static void vty_mgmt_server_connected(struct mgmt_fe_client *client, uintptr_t usr_data, bool connected) { - MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server", - !connected ? "dis: " : "", - !connected ? "from" : "to"); + debug_fe_client("Got %sconnected %s MGMTD Frontend Server", + !connected ? "dis: " : "", !connected ? "from" : "to"); /* * We should not have any sessions for connecting or disconnecting case. @@ -3515,8 +3509,8 @@ static void vty_mgmt_session_notify(struct mgmt_fe_client *client, return; } - MGMTD_FE_CLIENT_DBG("%s session for client %" PRIu64 " successfully", - create ? "Created" : "Destroyed", client_id); + debug_fe_client("%s session for client %" PRIu64 " successfully", + create ? "Created" : "Destroyed", client_id); if (create) { assert(session_id != 0); @@ -3547,8 +3541,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client, zlog_err("%socking for DS %u failed, Err: '%s' vty %p", lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty); else { - MGMTD_FE_CLIENT_DBG("%socked DS %u successfully", - lock_ds ? "L" : "Unl", ds_id); + debug_fe_client("%socked DS %u successfully", + lock_ds ? "L" : "Unl", ds_id); if (ds_id == MGMTD_DS_CANDIDATE) vty->mgmt_locked_candidate_ds = lock_ds; else @@ -3557,7 +3551,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client, if (!is_short_circuit && vty->mgmt_req_pending_cmd) { assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ")); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, + success ? CMD_SUCCESS : CMD_WARNING); } } @@ -3578,9 +3573,9 @@ static void vty_mgmt_set_config_result_notified( vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n", errmsg_if_any ? errmsg_if_any : "Unknown"); } else { - MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64 - " req-id %" PRIu64 " was successfull", - client_id, req_id); + debug_fe_client("SET_CONFIG request for client 0x%" PRIx64 + " req-id %" PRIu64 " was successfull", + client_id, req_id); } if (implicit_commit) { @@ -3589,7 +3584,8 @@ static void vty_mgmt_set_config_result_notified( vty_mgmt_unlock_running_inline(vty); } - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); } static void vty_mgmt_commit_config_result_notified( @@ -3609,15 +3605,15 @@ static void vty_mgmt_commit_config_result_notified( vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n", errmsg_if_any ? errmsg_if_any : "Unknown"); } else { - MGMTD_FE_CLIENT_DBG( - "COMMIT_CONFIG request for client 0x%" PRIx64 - " req-id %" PRIu64 " was successfull", - client_id, req_id); + debug_fe_client("COMMIT_CONFIG request for client 0x%" PRIx64 + " req-id %" PRIu64 " was successfull", + client_id, req_id); if (errmsg_if_any) vty_out(vty, "MGMTD: %s\n", errmsg_if_any); } - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); } static int vty_mgmt_get_data_result_notified( @@ -3637,13 +3633,13 @@ static int vty_mgmt_get_data_result_notified( client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n", errmsg_if_any ? errmsg_if_any : "Unknown"); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, CMD_WARNING); return -1; } - MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64 - " req-id %" PRIu64, - client_id, req_id); + debug_fe_client("GET_DATA request succeeded, client 0x%" PRIx64 + " req-id %" PRIu64, + client_id, req_id); if (req_id != mgmt_last_req_id) { mgmt_last_req_id = req_id; @@ -3656,12 +3652,211 @@ static int vty_mgmt_get_data_result_notified( } if (next_key < 0) { vty_out(vty, "]\n"); - vty_mgmt_resume_response(vty, success); + vty_mgmt_resume_response(vty, CMD_SUCCESS); } return 0; } +static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf, + size_t count) +{ + struct vty *vty = user_data; + + vty_out(vty, "%.*s", (int)count, (const char *)buf); + return count; +} + +static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format, + struct ly_err_item *ei) +{ + bool have_apptag = ei->apptag && ei->apptag[0] != 0; + bool have_path = ei->path && ei->path[0] != 0; + bool have_msg = ei->msg && ei->msg[0] != 0; + const char *severity = NULL; + const char *evalid = NULL; + const char *ecode = NULL; + LY_ERR err = ei->no; + + if (ei->level == LY_LLERR) + severity = "error"; + else if (ei->level == LY_LLWRN) + severity = "warning"; + + ecode = yang_ly_strerrcode(err); + if (err == LY_EVALID && ei->vecode != LYVE_SUCCESS) + evalid = yang_ly_strvecode(ei->vecode); + + switch (format) { + case LYD_XML: + vty_out(vty, + ""); + vty_out(vty, "application"); + if (severity) + vty_out(vty, "%s", + severity); + if (ecode) + vty_out(vty, "%s", ecode); + if (evalid) + vty_out(vty, "%s\n", + evalid); + if (have_path) + vty_out(vty, "%s\n", ei->path); + if (have_apptag) + vty_out(vty, "%s\n", + ei->apptag); + if (have_msg) + vty_out(vty, "%s\n", + ei->msg); + + vty_out(vty, ""); + break; + case LYD_JSON: + vty_out(vty, "{ \"error-type\": \"application\""); + if (severity) + vty_out(vty, ", \"error-severity\": \"%s\"", severity); + if (ecode) + vty_out(vty, ", \"error-code\": \"%s\"", ecode); + if (evalid) + vty_out(vty, ", \"error-validation\": \"%s\"", evalid); + if (have_path) + vty_out(vty, ", \"error-path\": \"%s\"", ei->path); + if (have_apptag) + vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag); + if (have_msg) + vty_out(vty, ", \"error-message\": \"%s\"", ei->msg); + + vty_out(vty, "}"); + break; + case LYD_UNKNOWN: + case LYD_LYB: + default: + vty_out(vty, "%% error"); + if (severity) + vty_out(vty, " severity: %s", severity); + if (evalid) + vty_out(vty, " invalid: %s", evalid); + if (have_path) + vty_out(vty, " path: %s", ei->path); + if (have_apptag) + vty_out(vty, " app-tag: %s", ei->apptag); + if (have_msg) + vty_out(vty, " msg: %s", ei->msg); + break; + } +} + +static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format) +{ + struct ly_err_item *ei = ly_err_first(ly_native_ctx); + uint count; + + if (!ei) + return 0; + + if (format == LYD_JSON) + vty_out(vty, "\"ietf-restconf:errors\": [ "); + + for (count = 0; ei; count++, ei = ei->next) { + if (count) + vty_out(vty, ", "); + vty_out_yang_error(vty, format, ei); + } + + if (format == LYD_JSON) + vty_out(vty, " ]"); + + ly_err_clean(ly_native_ctx, NULL); + + return count; +} + + +static int vty_mgmt_get_tree_result_notified( + struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uintptr_t session_ctx, uint64_t req_id, + Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result, + size_t len, int partial_error) +{ + struct vty *vty; + struct lyd_node *dnode; + int ret = CMD_SUCCESS; + LY_ERR err; + + vty = (struct vty *)session_ctx; + + debug_fe_client("GET_TREE request %ssucceeded, client 0x%" PRIx64 + " req-id %" PRIu64, + partial_error ? "partially " : "", client_id, req_id); + + assert(result_type == LYD_LYB || + result_type == vty->mgmt_req_pending_data); + + if (vty->mgmt_req_pending_data == LYD_XML && partial_error) + vty_out(vty, + "\n"); + + if (result_type == LYD_LYB) { + /* + * parse binary into tree and print in the specified format + */ + result_type = vty->mgmt_req_pending_data; + + err = lyd_parse_data_mem(ly_native_ctx, result, LYD_LYB, 0, 0, + &dnode); + if (!err) + err = lyd_print_clb(vty_mgmt_libyang_print, vty, dnode, + result_type, LYD_PRINT_WITHSIBLINGS); + lyd_free_all(dnode); + + if (vty_out_yang_errors(vty, result_type) || err) + ret = CMD_WARNING; + } else { + /* + * Print the in-format result + */ + assert(result_type == LYD_XML || result_type == LYD_JSON); + vty_out(vty, "%.*s\n", (int)len - 1, (const char *)result); + } + + vty_mgmt_resume_response(vty, ret); + + return 0; +} + +static int vty_mgmt_error_notified(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uint64_t session_id, uintptr_t session_ctx, + uint64_t req_id, int error, + const char *errstr) +{ + struct vty *vty = (struct vty *)session_ctx; + const char *cname = mgmt_fe_client_name(client); + + if (!vty->mgmt_req_pending_cmd) { + debug_fe_client("Erorr with no pending command: %d returned for client %s 0x%" PRIx64 + " session-id %" PRIu64 " req-id %" PRIu64 + "error-str %s", + error, cname, client_id, session_id, req_id, + errstr); + vty_out(vty, + "%% Error %d from MGMTD for %s with no pending command: %s\n", + error, cname, errstr); + return CMD_WARNING; + } + + debug_fe_client("Erorr %d returned for client %s 0x%" PRIx64 + " session-id %" PRIu64 " req-id %" PRIu64 "error-str %s", + error, cname, client_id, session_id, req_id, errstr); + + vty_out(vty, "%% %s (for %s, client %s)\n", errstr, + vty->mgmt_req_pending_cmd, cname); + + vty_mgmt_resume_response(vty, error ? CMD_WARNING : CMD_SUCCESS); + + return 0; +} + static struct mgmt_fe_client_cbs mgmt_cbs = { .client_connect_notify = vty_mgmt_server_connected, .client_session_notify = vty_mgmt_session_notify, @@ -3669,6 +3864,9 @@ static struct mgmt_fe_client_cbs mgmt_cbs = { .set_config_notify = vty_mgmt_set_config_result_notified, .commit_config_notify = vty_mgmt_commit_config_result_notified, .get_data_notify = vty_mgmt_get_data_result_notified, + .get_tree_notify = vty_mgmt_get_tree_result_notified, + .error_notify = vty_mgmt_error_notified, + }; void vty_init_mgmt_fe(void) @@ -3715,12 +3913,15 @@ int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, return 0; } -int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit) +int vty_mgmt_send_config_data(struct vty *vty, const char *xpath_base, + bool implicit_commit) { Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES]; Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES]; Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES]; Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0}; + char xpath[VTY_MAXCFGCHANGES][XPATH_MAXLEN]; + char *change_xpath; size_t indx; if (vty->type == VTY_FILE) { @@ -3756,6 +3957,9 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit) } } + if (xpath_base == NULL) + xpath_base = ""; + for (indx = 0; indx < vty->num_cfg_changes; indx++) { mgmt_yang_data_init(&cfg_data[indx]); @@ -3768,29 +3972,47 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit) cfg_data[indx].value = &value[indx]; } - cfg_data[indx].xpath = vty->cfg_changes[indx].xpath; + change_xpath = vty->cfg_changes[indx].xpath; + + memset(xpath[indx], 0, sizeof(xpath[indx])); + /* If change xpath is relative, prepend base xpath. */ + if (change_xpath[0] == '.') { + strlcpy(xpath[indx], xpath_base, sizeof(xpath[indx])); + change_xpath++; /* skip '.' */ + } + strlcat(xpath[indx], change_xpath, sizeof(xpath[indx])); + + cfg_data[indx].xpath = xpath[indx]; mgmt_yang_cfg_data_req_init(&cfg_req[indx]); cfg_req[indx].data = &cfg_data[indx]; switch (vty->cfg_changes[indx].operation) { - case NB_OP_DESTROY: + case NB_OP_DELETE: cfg_req[indx].req_type = MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; break; + case NB_OP_DESTROY: + cfg_req[indx].req_type = + MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA; + break; + + case NB_OP_CREATE_EXCL: + cfg_req[indx].req_type = + MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA; + break; + + case NB_OP_REPLACE: + cfg_req[indx].req_type = + MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA; + break; + case NB_OP_CREATE: case NB_OP_MODIFY: case NB_OP_MOVE: - case NB_OP_PRE_VALIDATE: - case NB_OP_APPLY_FINISH: cfg_req[indx].req_type = MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; break; - case NB_OP_GET_ELEM: - case NB_OP_GET_NEXT: - case NB_OP_GET_KEYS: - case NB_OP_LOOKUP_ENTRY: - case NB_OP_RPC: default: assertf(false, "Invalid operation type for send config: %d", @@ -3874,6 +4096,30 @@ int vty_mgmt_send_get_req(struct vty *vty, bool is_config, return 0; } +int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT result_type, uint8_t flags, + uint8_t defaults, const char *xpath) +{ + LYD_FORMAT intern_format = result_type; + + vty->mgmt_req_id++; + + if (mgmt_fe_send_get_data_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, datastore, + intern_format, flags, defaults, xpath)) { + zlog_err("Failed to send GET-DATA to MGMTD session-id: %" PRIu64 + " req-id %" PRIu64 ".", + vty->mgmt_session_id, vty->mgmt_req_id); + vty_out(vty, "Failed to send GET-DATA to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_GET_DATA_REQ"; + vty->mgmt_req_pending_data = result_type; + + return 0; +} + /* Install vty's own commands like `who' command. */ void vty_init(struct event_loop *master_thread, bool do_command_logging) { diff --git a/lib/vty.h b/lib/vty.h index a8654f8..a59ac7a 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -229,12 +229,9 @@ struct vty { * CLI command and we are waiting on the reply so we can respond to the * vty user. */ const char *mgmt_req_pending_cmd; + uintptr_t mgmt_req_pending_data; bool mgmt_locked_candidate_ds; bool mgmt_locked_running_ds; - /* Need to track when we file-lock in vtysh to re-lock on end/conf t - * workaround - */ - bool vtysh_file_locked; }; static inline void vty_push_context(struct vty *vty, int node, uint64_t id) @@ -377,7 +374,7 @@ extern bool vty_set_include(struct vty *vty, const char *regexp); */ extern int vty_json(struct vty *vty, struct json_object *json); extern int vty_json_no_pretty(struct vty *vty, struct json_object *json); -extern void vty_json_empty(struct vty *vty); +extern void vty_json_empty(struct vty *vty, struct json_object *json); /* post fd to be passed to the vtysh client * fd is owned by the VTY code after this and will be closed when done */ @@ -412,15 +409,19 @@ extern bool vty_mgmt_fe_enabled(void); extern bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty); extern bool mgmt_vty_read_configs(void); -extern int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit); +extern int vty_mgmt_send_config_data(struct vty *vty, const char *xpath_base, + bool implicit_commit); extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort); extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config, Mgmtd__DatastoreId datastore, const char **xpath_list, int num_req); +extern int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT result_type, uint8_t flags, + uint8_t defaults, const char *xpath); extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, bool lock, bool scok); -extern void vty_mgmt_resume_response(struct vty *vty, bool success); +extern void vty_mgmt_resume_response(struct vty *vty, int ret); static inline bool vty_needs_implicit_commit(struct vty *vty) { diff --git a/lib/workqueue.c b/lib/workqueue.c index fa5d585..d630af1 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -42,6 +42,15 @@ static void work_queue_item_free(struct work_queue_item *item) return; } +static inline void work_queue_item_dequeue(struct work_queue *wq, + struct work_queue_item *item) +{ + assert(wq->item_count > 0); + + wq->item_count--; + STAILQ_REMOVE(&wq->items, item, work_queue_item, wq); +} + static void work_queue_item_remove(struct work_queue *wq, struct work_queue_item *item) { @@ -133,6 +142,13 @@ static int work_queue_schedule(struct work_queue *wq, unsigned int delay) return 0; } +static inline void work_queue_item_enqueue(struct work_queue *wq, + struct work_queue_item *item) +{ + STAILQ_INSERT_TAIL(&wq->items, item, wq); + wq->item_count++; +} + void work_queue_add(struct work_queue *wq, void *data) { struct work_queue_item *item; @@ -265,17 +281,14 @@ void work_queue_run(struct event *thread) do { ret = wq->spec.workfunc(wq, item->data); item->ran++; - } while ((ret == WQ_RETRY_NOW) - && (item->ran < wq->spec.max_retries)); + } while (item->ran < wq->spec.max_retries); switch (ret) { case WQ_QUEUE_BLOCKED: { /* decrement item->ran again, cause this isn't an item - * specific error, and fall through to WQ_RETRY_LATER + * specific error, and retry later */ item->ran--; - } - case WQ_RETRY_LATER: { goto stats; } case WQ_REQUEUE: { @@ -295,10 +308,6 @@ void work_queue_run(struct event *thread) titem = item; break; } - case WQ_RETRY_NOW: - /* a RETRY_NOW that gets here has exceeded max_tries, same as - * ERROR */ - /* fallthru */ case WQ_SUCCESS: default: { work_queue_item_remove(wq, item); @@ -350,8 +359,7 @@ stats: /* Is the queue done yet? If it is, call the completion callback. */ if (!work_queue_empty(wq)) { - if (ret == WQ_RETRY_LATER || - ret == WQ_QUEUE_BLOCKED) + if (ret == WQ_QUEUE_BLOCKED) work_queue_schedule(wq, wq->spec.retry); else work_queue_schedule(wq, 0); diff --git a/lib/workqueue.h b/lib/workqueue.h index 5d84739..a495fe8 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -26,9 +26,7 @@ DECLARE_MTYPE(WORK_QUEUE); /* action value, for use by item processor and item error handlers */ typedef enum { WQ_SUCCESS = 0, - WQ_RETRY_NOW, /* retry immediately */ - WQ_RETRY_LATER, /* retry later, cease processing work queue */ - WQ_REQUEUE, /* requeue item, continue processing work queue */ + WQ_REQUEUE, /* requeue item, continue processing work queue */ WQ_QUEUE_BLOCKED, /* Queue cant be processed at this time. * Similar to WQ_RETRY_LATER, but doesn't penalise * the particular item.. */ @@ -117,22 +115,6 @@ work_queue_last_item(struct work_queue *wq) return STAILQ_LAST(&wq->items, work_queue_item, wq); } -static inline void work_queue_item_enqueue(struct work_queue *wq, - struct work_queue_item *item) -{ - STAILQ_INSERT_TAIL(&wq->items, item, wq); - wq->item_count++; -} - -static inline void work_queue_item_dequeue(struct work_queue *wq, - struct work_queue_item *item) -{ - assert(wq->item_count > 0); - - wq->item_count--; - STAILQ_REMOVE(&wq->items, item, work_queue_item, wq); -} - /* create a new work queue, of given name. * user must fill in the spec of the returned work queue before adding * anything to it @@ -160,6 +142,7 @@ bool work_queue_is_scheduled(struct work_queue *wq); /* Helpers, exported for thread.c and command.c */ extern void work_queue_run(struct event *thread); +/* Function to initialize the workqueue cli */ extern void workqueue_cmd_init(void); #ifdef __cplusplus diff --git a/lib/yang.c b/lib/yang.c index 4dd8654..03044fc 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -6,12 +6,15 @@ #include +#include "darr.h" #include "log.h" #include "lib_errors.h" #include "yang.h" #include "yang_translator.h" #include "northbound.h" +#include "lib/config_paths.h" + DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module"); DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure"); @@ -37,7 +40,8 @@ static LY_ERR yang_module_imp_clb(const char *mod_name, const char *mod_rev, struct yang_module_embed *e; if (!strcmp(mod_name, "ietf-inet-types") || - !strcmp(mod_name, "ietf-yang-types")) + !strcmp(mod_name, "ietf-yang-types") || + !strcmp(mod_name, "ietf-yang-metadata")) /* libyang has these built in, don't try finding them here */ return LY_ENOTFOUND; @@ -97,13 +101,14 @@ RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare) struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules); -struct yang_module *yang_module_load(const char *module_name) +struct yang_module *yang_module_load(const char *module_name, + const char **features) { struct yang_module *module; const struct lys_module *module_info; - module_info = - ly_ctx_load_module(ly_native_ctx, module_name, NULL, NULL); + module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL, + features); if (!module_info) { flog_err(EC_LIB_YANG_MODULE_LOAD, "%s: failed to load data model: %s", __func__, @@ -127,8 +132,10 @@ struct yang_module *yang_module_load(const char *module_name) void yang_module_load_all(void) { + static const char * const all_features[] = { "*", NULL }; + for (size_t i = 0; i < array_size(frr_native_modules); i++) - yang_module_load(frr_native_modules[i]); + yang_module_load(frr_native_modules[i], (const char **)all_features); } struct yang_module *yang_module_find(const char *module_name) @@ -250,19 +257,44 @@ void yang_snode_get_path(const struct lysc_node *snode, } } -struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath, - uint32_t options) +LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath, + struct lysc_node ***snodes, bool *simple) { struct lysc_node *snode; struct ly_set *set; LY_ERR err; - err = lys_find_xpath(ly_native_ctx, NULL, xpath, options, &set); - if (err || !set->count) - return NULL; + /* lys_find_path will not resolve complex xpaths */ + snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0); + if (snode) { + *darr_append(*snodes) = snode; + *simple = true; + return LY_SUCCESS; + } + + /* Try again to catch complex query cases */ + err = lys_find_xpath(ly_native_ctx, NULL, xpath, 0, &set); + if (err) + return err; + if (!set->count) { + ly_set_free(set, NULL); + return LY_ENOTFOUND; + } - snode = set->snodes[0]; + *simple = false; + darr_ensure_i(*snodes, set->count - 1); + memcpy(*snodes, set->snodes, set->count * sizeof(set->snodes[0])); ly_set_free(set, NULL); + return LY_SUCCESS; +} + + +struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath, + uint32_t options) +{ + struct lysc_node *snode; + + snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0); return snode; } @@ -370,33 +402,10 @@ unsigned int yang_snode_num_keys(const struct lysc_node *snode) return count; } -void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath, - size_t xpath_len) -{ - lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len); -} - -const char *yang_dnode_get_schema_name(const struct lyd_node *dnode, - const char *xpath_fmt, ...) +char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath, + size_t xpath_len) { - if (xpath_fmt) { - va_list ap; - char xpath[XPATH_MAXLEN]; - - va_start(ap, xpath_fmt); - vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); - va_end(ap); - - dnode = yang_dnode_get(dnode, xpath); - if (!dnode) { - flog_err(EC_LIB_YANG_DNODE_NOT_FOUND, - "%s: couldn't find %s", __func__, xpath); - zlog_backtrace(LOG_ERR); - abort(); - } - } - - return dnode->schema->name; + return lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len); } struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath) @@ -505,6 +514,30 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, ly_set_free(set, NULL); } +uint32_t yang_dnode_count(const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + struct ly_set *set; + uint32_t count; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + if (lyd_find_xpath(dnode, xpath, &set)) { + assert(0); + return 0; + } + + count = set->count; + + ly_set_free(set, NULL); + + return count; +} + bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath) { const struct lysc_node *snode; @@ -596,7 +629,8 @@ struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode) { struct lyd_node *dup = NULL; LY_ERR err; - err = lyd_dup_siblings(dnode, NULL, LYD_DUP_RECURSIVE, &dup); + err = lyd_dup_siblings(dnode, NULL, + LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup); assert(!err); return dup; } @@ -680,10 +714,122 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) zlog(priority, "libyang: %s", msg); } +LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, + const char *data, struct lyd_node **notif) +{ + struct lyd_node *tree; + struct ly_set *set = NULL; + struct ly_in *in = NULL; + LY_ERR err; + + err = ly_in_new_memory(data, &in); + if (err) { + zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg()); + return err; + } + + err = lyd_parse_op(ly_native_ctx, NULL, in, format, LYD_TYPE_NOTIF_YANG, + &tree, NULL); + ly_in_free(in, 0); + if (err) { + zlog_err("Failed to parse notification: %s", ly_last_errmsg()); + return err; + } + + err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set); + if (err) { + zlog_err("Failed to parse notification: %s", ly_last_errmsg()); + lyd_free_all(tree); + return err; + } + if (set->count == 0) { + zlog_err("Notification not found in the parsed tree: %s", xpath); + ly_set_free(set, NULL); + lyd_free_all(tree); + return LY_ENOTFOUND; + } + *notif = set->dnodes[0]; + ly_set_free(set, NULL); + return LY_SUCCESS; +} + +static ssize_t yang_print_darr(void *arg, const void *buf, size_t count) +{ + uint8_t *dst = darr_append_n(*(uint8_t **)arg, count); + + memcpy(dst, buf, count); + return count; +} + +LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root, + LYD_FORMAT format, uint32_t options) +{ + LY_ERR err; + + err = lyd_print_clb(yang_print_darr, darr, root, format, options); + if (err) + zlog_err("Failed to save yang tree: %s", ly_last_errmsg()); + else if (format != LYD_LYB) + *darr_append(*darr) = 0; + return err; +} + +uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format, + uint32_t options) +{ + uint8_t *darr = NULL; + + if (yang_print_tree_append(&darr, root, format, options)) + return NULL; + return darr; +} + +char *yang_convert_lyd_format(const char *data, size_t data_len, + LYD_FORMAT in_format, LYD_FORMAT out_format, + bool shrink) +{ + struct lyd_node *tree = NULL; + uint32_t options = LYD_PRINT_WD_EXPLICIT | LYD_PRINT_WITHSIBLINGS; + uint8_t *result = NULL; + LY_ERR err; + + assert(out_format != LYD_LYB); + + if (in_format != LYD_LYB && !MGMT_MSG_VALIDATE_NUL_TERM(data, data_len)) { + zlog_err("Corrupt input data, no NUL terminating byte"); + return NULL; + } + + if (in_format == out_format) + return darr_strdup((const char *)data); + + err = lyd_parse_data_mem(ly_native_ctx, (const char *)data, in_format, + LYD_PARSE_ONLY, 0, &tree); + + if (err) { + flog_err_sys(EC_LIB_LIBYANG, + "cannot parse input data to convert: %s", + ly_last_errmsg()); + return NULL; + } + + if (shrink) + options |= LYD_PRINT_SHRINK; + + /* Take a guess at the initial capacity based on input data size */ + darr_ensure_cap(result, data_len); + err = yang_print_tree_append(&result, tree, out_format, options); + lyd_free_all(tree); + if (err) { + darr_free(result); + return NULL; + } + return (char *)result; +} + const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len) { struct ly_err_item *ei; - const char *path; ei = ly_err_first(ly_ctx); if (!ei) @@ -691,18 +837,16 @@ const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len) strlcpy(buf, "YANG error(s):\n", buf_len); for (; ei; ei = ei->next) { - strlcat(buf, " ", buf_len); + if (ei->path) { + strlcat(buf, " Path: ", buf_len); + strlcat(buf, ei->path, buf_len); + strlcat(buf, "\n", buf_len); + } + strlcat(buf, " Error: ", buf_len); strlcat(buf, ei->msg, buf_len); strlcat(buf, "\n", buf_len); } - path = ly_errpath(ly_ctx); - if (path) { - strlcat(buf, " YANG path: ", buf_len); - strlcat(buf, path, buf_len); - strlcat(buf, "\n", buf_len); - } - ly_err_clean(ly_ctx, NULL); return buf; @@ -723,6 +867,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile) { struct ly_ctx *ctx = NULL; const char *yang_models_path = YANG_MODELS_PATH; + uint options; LY_ERR err; if (access(yang_models_path, R_OK | X_OK)) { @@ -736,7 +881,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile) YANG_MODELS_PATH); } - uint options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD; + options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD; if (explicit_compile) options |= LY_CTX_EXPLICIT_COMPILE; err = ly_ctx_new(yang_models_path, options, &ctx); @@ -927,3 +1072,274 @@ uint32_t yang_get_list_elements_count(const struct lyd_node *node) } while (node); return count; } + +int yang_get_key_preds(char *s, const struct lysc_node *snode, + struct yang_list_keys *keys, ssize_t space) +{ + const struct lysc_node_leaf *skey; + ssize_t len2, len = 0; + ssize_t i = 0; + + LY_FOR_KEYS (snode, skey) { + assert(i < keys->num); + len2 = snprintf(s + len, space - len, "[%s='%s']", skey->name, + keys->key[i]); + if (len2 > space - len) + len = space; + else + len += len2; + i++; + } + + assert(i == keys->num); + return i; +} + +int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys) +{ + struct lyd_node *child = lyd_child(node); + + keys->num = 0; + for (; child && lysc_is_key(child->schema); child = child->next) { + const char *value = lyd_get_value(child); + + if (!value) + return NB_ERR; + strlcpy(keys->key[keys->num], value, + sizeof(keys->key[keys->num])); + keys->num++; + } + return NB_OK; +} + +/* + * ------------------------ + * Libyang Future Functions + * ------------------------ + * + * All these functions are implemented in libyang versions (perhaps unreleased) + * beyond what we require currently so we must supply the functionality. + */ + +/* + * Safe to remove after libyang v2.1.xxx is required (.144 has a bug so + * something > .144) https://github.com/CESNET/libyang/issues/2149 + */ +LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent, + const struct lysc_node *snode, + const struct yang_list_keys *list_keys, + struct lyd_node **node) +{ +#if defined(HAVE_LYD_NEW_LIST3) && 0 + LY_ERR err; + const char *keys[LIST_MAXKEYS]; + + assert(list_keys->num <= LIST_MAXKEYS); + for (int i = 0; i < list_keys->num; i++) + keys[i] = list_keys->key[i]; + + err = lyd_new_list3(&parent->node, snode->module, snode->name, keys, + NULL, 0, node); + return err; +#else + struct lyd_node *pnode = &parent->node; + const char(*keys)[LIST_MAXKEYLEN] = list_keys->key; + + assert(list_keys->num <= 8); + switch (list_keys->num) { + case 0: + return lyd_new_list(pnode, snode->module, snode->name, false, + node); + case 1: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0]); + case 2: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1]); + case 3: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2]); + case 4: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2], keys[3]); + case 5: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2], keys[3], + keys[4]); + case 6: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2], keys[3], + keys[4], keys[5]); + case 7: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2], keys[3], + keys[4], keys[5], keys[6]); + case 8: + return lyd_new_list(pnode, snode->module, snode->name, false, + node, keys[0], keys[1], keys[2], keys[3], + keys[4], keys[5], keys[6], keys[7]); + } + _Static_assert(LIST_MAXKEYS == 8, "max key mismatch in switch unroll"); + /*NOTREACHED*/ + return LY_EINVAL; +#endif +} + + +/* + * Safe to remove after libyang v2.1.144 is required + */ +LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath) +{ + LY_ERR err; +#ifdef HAVE_LYD_TRIM_XPATH + err = lyd_trim_xpath(root, xpath, NULL); + if (err) { + flog_err_sys(EC_LIB_LIBYANG, + "cannot obtain specific result for xpath \"%s\": %s", + xpath, yang_ly_strerrcode(err)); + return err; + } + return LY_SUCCESS; +#else + struct lyd_node *node, *sib; + struct lyd_node **remove = NULL; + struct ly_set *set = NULL; + uint32_t i; + + *root = lyd_first_sibling(*root); + + err = lyd_find_xpath3(NULL, *root, xpath, NULL, &set); + if (err) { + flog_err_sys(EC_LIB_LIBYANG, + "cannot obtain specific result for xpath \"%s\": %s", + xpath, yang_ly_strerrcode(err)); + return err; + } + /* + * Mark keepers and sweep deleting non-keepers. + * + * NOTE: We assume the data-nodes have NULL priv pointers and use that + * for our mark. + */ + + /* Mark */ + for (i = 0; i < set->count; i++) { + for (node = set->dnodes[i]; node; node = &node->parent->node) { + if (node->priv) + break; + if (node == set->dnodes[i]) + node->priv = (void *)2; + else + node->priv = (void *)1; + } + } + + darr_ensure_cap(remove, 128); + LY_LIST_FOR(*root, sib) { + LYD_TREE_DFS_BEGIN (sib, node) { + /* + * If this is a direct matching node then include its + * subtree which won't be marked and would otherwise + * be removed. + */ + if (node->priv == (void *)2) + LYD_TREE_DFS_continue = 1; + else if (!node->priv) { + *darr_append(remove) = node; + LYD_TREE_DFS_continue = 1; + } + LYD_TREE_DFS_END(sib, node); + } + } + darr_foreach_i (remove, i) { + if (remove[i] == *root) + *root = (*root)->next; + lyd_free_tree(remove[i]); + } + darr_free(remove); + + ly_set_free(set, NULL); + + return LY_SUCCESS; +#endif +} + +/* + * Safe to remove after libyang v2.1.128 is required + */ +const char *yang_ly_strerrcode(LY_ERR err) +{ +#ifdef HAVE_LY_STRERRCODE + return ly_strerrcode(err); +#else + switch (err) { + case LY_SUCCESS: + return "ok"; + case LY_EMEM: + return "out of memory"; + case LY_ESYS: + return "system error"; + case LY_EINVAL: + return "invalid value given"; + case LY_EEXIST: + return "item exists"; + case LY_ENOTFOUND: + return "item not found"; + case LY_EINT: + return "operation interrupted"; + case LY_EVALID: + return "validation failed"; + case LY_EDENIED: + return "access denied"; + case LY_EINCOMPLETE: + return "incomplete"; + case LY_ERECOMPILE: + return "compile error"; + case LY_ENOT: + return "not"; + case LY_EPLUGIN: + case LY_EOTHER: + return "other"; + default: + return "unknown"; + } +#endif +} + +/* + * Safe to remove after libyang v2.1.128 is required + */ +const char *yang_ly_strvecode(LY_VECODE vecode) +{ +#ifdef HAVE_LY_STRVECODE + return ly_strvecode(vecode); +#else + switch (vecode) { + case LYVE_SUCCESS: + return ""; + case LYVE_SYNTAX: + return "syntax"; + case LYVE_SYNTAX_YANG: + return "yang-syntax"; + case LYVE_SYNTAX_YIN: + return "yin-syntax"; + case LYVE_REFERENCE: + return "reference"; + case LYVE_XPATH: + return "xpath"; + case LYVE_SEMANTICS: + return "semantics"; + case LYVE_SYNTAX_XML: + return "xml-syntax"; + case LYVE_SYNTAX_JSON: + return "json-syntax"; + case LYVE_DATA: + return "data"; + case LYVE_OTHER: + return "other"; + default: + return "unknown"; + } +#endif +} diff --git a/lib/yang.h b/lib/yang.h index 37369c0..65f6a73 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -112,10 +112,16 @@ extern struct yang_modules yang_modules; * module_name * Name of the YANG module. * + * features + * NULL-terminated array of feature names to enable. + * If NULL, all features are disabled. + * To enable all features, use ["*", NULL]. + * * Returns: * Pointer to newly created YANG module. */ -extern struct yang_module *yang_module_load(const char *module_name); +extern struct yang_module *yang_module_load(const char *module_name, + const char **features); /* * Load all FRR native YANG models. @@ -317,30 +323,16 @@ extern unsigned int yang_snode_num_keys(const struct lysc_node *snode); * libyang data node to be processed. * * xpath - * Pointer to previously allocated buffer. + * Pointer to previously allocated buffer or NULL. * * xpath_len - * Size of the xpath buffer. - */ -extern void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath, - size_t xpath_len); - -/* - * Return the schema name of the given libyang data node. + * Size of the xpath buffer if xpath non-NULL. * - * dnode - * libyang data node. - * - * xpath_fmt - * Optional XPath expression (absolute or relative) to specify a different - * data node to operate on in the same data tree. - * - * Returns: - * Schema name of the libyang data node. + * If xpath is NULL, the returned string (if non-NULL) needs to be free()d by + * the caller. */ -extern const char *yang_dnode_get_schema_name(const struct lyd_node *dnode, - const char *xpath_fmt, ...) - PRINTFRR(2, 3); +extern char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath, + size_t xpath_len); /* * Find a libyang data node by its YANG data path. @@ -434,6 +426,21 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, const struct lyd_node *dnode, const char *xpath_fmt, ...) PRINTFRR(4, 5); +/* + * Count the number of data nodes that satisfy an XPath query. + * + * dnode + * Base libyang data node to operate on. + * + * xpath_fmt + * XPath expression (absolute or relative). + * + * ... + * any parameters for xpath_fmt. + */ +uint32_t yang_dnode_count(const struct lyd_node *dnode, const char *xpath_fmt, + ...) PRINTFRR(2, 3); + /* * Check if the libyang data node contains a default value. Non-presence * containers are assumed to always contain a default value. @@ -600,6 +607,69 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, */ extern void yang_debugging_set(bool enable); +/* + * Parse a YANG notification. + * + * Args: + * xpath: xpath of notification. + * format: LYD_FORMAT of input data. + * data: input data. + * notif: pointer to the libyang data tree to store the parsed notification. + * If the notification is not on the top level of the yang model, + * the pointer to the notification node is still returned, but it's + * part of the full data tree with all its parents. + */ +extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, + const char *data, struct lyd_node **notif); + +/* + * "Print" the yang tree in `root` into dynamic sized array. + * + * Args: + * root: root of the subtree to "print" along with siblings. + * format: LYD_FORMAT of output (see lyd_print_mem) + * options: printing options (see lyd_print_mem) + * + * Return: + * A darr dynamic array with the "printed" output or NULL on failure. + */ +extern uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format, + uint32_t options); + + +/** + * yang_convert_lyd_format() - convert one libyang format to darr string. + * @data: data to convert. + * @data_len: length of the data. + * @in_format: format of the data. + * @out_format: format to return. + * @shrink: true to avoid pretty printing. + * + * Return: + * A darr based string or NULL for error. + */ +extern char *yang_convert_lyd_format(const char *data, size_t msg_len, + LYD_FORMAT in_format, + LYD_FORMAT out_format, bool shrink); + +/* + * "Print" the yang tree in `root` into an existing dynamic sized array. + * + * This function does not initialize or free the dynamic array, the array can + * already existing data, the tree will be appended to this data. + * + * Args: + * darr: existing `uint8_t *`, dynamic array. + * root: root of the subtree to "print" along with siblings. + * format: LYD_FORMAT of output (see lyd_print_mem) + * options: printing options (see lyd_print_mem) + * + * Return: + * LY_ERR from underlying calls. + */ +extern LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root, + LYD_FORMAT format, uint32_t options); + /* * Print libyang error messages into the provided buffer. * @@ -693,6 +763,44 @@ bool yang_is_last_list_dnode(const struct lyd_node *dnode); /* API to check if the given node is last node in the data tree level */ bool yang_is_last_level_dnode(const struct lyd_node *dnode); +/* Create a YANG predicate string based on the keys */ +extern int yang_get_key_preds(char *s, const struct lysc_node *snode, + struct yang_list_keys *keys, ssize_t space); + +/* Get YANG keys from an existing dnode */ +extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys); + +/** + * yang_resolve_snodes() - Resolve an XPath to matching schema nodes. + * @ly_ctx: libyang context to operate on. + * @xpath: the path or XPath to resolve. + * @snodes: [OUT] pointer for resulting dynamic array (darr) of schema node + * pointers. + * @simple: [OUT] indicates if @xpath was resolvable simply or not. Non-simple + * means that the @xpath is not a simple path and utilizes XPath 1.0 + * functionality beyond simple key predicates. + * + * This function can be used to find the schema node (or nodes) that correspond + * to a given @xpath. If the @xpath includes non-key predicates (e.g., using + * functions) then @simple will be set to false, and @snodes may contain more + * than a single schema node. + * + * Return: a libyang error or LY_SUCCESS. + */ +extern LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath, + struct lysc_node ***snodes, bool *simple); + +/* + * Libyang future functions + */ +extern const char *yang_ly_strerrcode(LY_ERR err); +extern const char *yang_ly_strvecode(LY_VECODE vecode); +extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent, + const struct lysc_node *snode, + const struct yang_list_keys *keys, + struct lyd_node **nodes); +extern LY_ERR yang_lyd_trim_xpath(struct lyd_node **rootp, const char *xpath); + #ifdef __cplusplus } #endif diff --git a/lib/yang_translator.c b/lib/yang_translator.c index eae7577..005f642 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -146,7 +146,7 @@ struct yang_translator *yang_translator_load(const char *path) */ assert(dnode); - family = yang_dnode_get_string(dnode, "./family"); + family = yang_dnode_get_string(dnode, "family"); translator = yang_translator_find(family); if (translator != NULL) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, @@ -182,7 +182,7 @@ struct yang_translator *yang_translator_load(const char *path) tmodule = XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule)); - module_name = yang_dnode_get_string(set->dnodes[i], "./name"); + module_name = yang_dnode_get_string(set->dnodes[i], "name"); tmodule->module = ly_ctx_load_module(translator->ly_ctx, module_name, NULL, NULL); if (!tmodule->module) { @@ -233,7 +233,7 @@ struct yang_translator *yang_translator_load(const char *path) const struct lysc_node *snode_custom, *snode_native; xpath_custom = - yang_dnode_get_string(set->dnodes[i], "./custom"); + yang_dnode_get_string(set->dnodes[i], "custom"); snode_custom = yang_find_snode(translator->ly_ctx, xpath_custom, 0); @@ -246,7 +246,7 @@ struct yang_translator *yang_translator_load(const char *path) } xpath_native = - yang_dnode_get_string(set->dnodes[i], "./native"); + yang_dnode_get_string(set->dnodes[i], "native"); snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0); if (!snode_native) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index dc049a3..a013395 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -168,10 +168,9 @@ struct yang_data *yang_data_new_dec64(const char *xpath, double value) double yang_dnode_get_dec64(const struct lyd_node *dnode, const char *xpath_fmt, ...) { - const double denom[19] = {1e0, 1e-1, 1e-2, 1e-3, 1e-4, - 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, - 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, - 1e-15, 1e-16, 1e-17, 1e-18}; + const double denom[19] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, + 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, + 1e14, 1e15, 1e16, 1e17, 1e18 }; const struct lysc_type_dec *dectype; const struct lyd_value *dvalue; @@ -179,7 +178,7 @@ double yang_dnode_get_dec64(const struct lyd_node *dnode, const char *xpath_fmt, dectype = (const struct lysc_type_dec *)dvalue->realtype; assert(dectype->basetype == LY_TYPE_DEC64); assert(dectype->fraction_digits < sizeof(denom) / sizeof(*denom)); - return (double)dvalue->dec64 * denom[dectype->fraction_digits]; + return (double)dvalue->dec64 / denom[dectype->fraction_digits]; } double yang_get_default_dec64(const char *xpath_fmt, ...) @@ -1020,6 +1019,13 @@ void yang_str2mac(const char *value, struct ethaddr *mac) (void)prefix_str2mac(value, mac); } +void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + (void)prefix_str2mac(canon, mac); +} + struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) { struct tm tm; @@ -1046,6 +1052,17 @@ struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) return yang_data_new(xpath, timebuf); } +float yang_dnode_get_bandwidth_ieee_float32(const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + float value; + + assert(sscanf(canon, "%a", &value) == 1); + + return value; +} + const char *yang_nexthop_type2str(uint32_t ntype) { switch (ntype) { diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 06e0587..59b5b13 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -195,11 +195,18 @@ extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...) extern struct yang_data *yang_data_new_mac(const char *xpath, const struct ethaddr *mac); extern void yang_str2mac(const char *value, struct ethaddr *mac); +extern void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode, + const char *xpath_fmt, ...) PRINTFRR(3, 4); /*data-and-time */ extern struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time); +/* rt-types:bandwidth-ieee-float32 */ +extern float yang_dnode_get_bandwidth_ieee_float32(const struct lyd_node *dnode, + const char *xpath_fmt, ...) + PRINTFRR(2, 3); + /* nexthop enum2str */ extern const char *yang_nexthop_type2str(uint32_t ntype); diff --git a/lib/zclient.c b/lib/zclient.c index f8f9cf7..51ebb56 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -41,8 +41,20 @@ static void zclient_event(enum zclient_event, struct zclient *); static void zebra_interface_if_set_value(struct stream *s, struct interface *ifp); -struct zclient_options zclient_options_default = {.receive_notify = false, - .synchronous = false}; +const struct zclient_options zclient_options_default = { + .synchronous = false, + .auxiliary = false, +}; + +const struct zclient_options zclient_options_sync = { + .synchronous = true, + .auxiliary = true, +}; + +const struct zclient_options zclient_options_auxiliary = { + .synchronous = false, + .auxiliary = true, +}; struct sockaddr_storage zclient_addr; socklen_t zclient_addr_len; @@ -52,7 +64,7 @@ static int zclient_debug; /* Allocate zclient structure. */ struct zclient *zclient_new(struct event_loop *master, - struct zclient_options *opt, + const struct zclient_options *opt, zclient_handler *const *handlers, size_t n_handlers) { struct zclient *zclient; @@ -69,8 +81,8 @@ struct zclient *zclient_new(struct event_loop *master, zclient->handlers = handlers; zclient->n_handlers = n_handlers; - zclient->receive_notify = opt->receive_notify; zclient->synchronous = opt->synchronous; + zclient->auxiliary = opt->auxiliary; return zclient; } @@ -392,10 +404,6 @@ enum zclient_send_status zclient_send_hello(struct zclient *zclient) stream_putc(s, zclient->redist_default); stream_putw(s, zclient->instance); stream_putl(s, zclient->session_id); - if (zclient->receive_notify) - stream_putc(s, 1); - else - stream_putc(s, 0); if (zclient->synchronous) stream_putc(s, 1); else @@ -891,7 +899,7 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, &next2->gate); if (ret != 0) return ret; - /* Intentional Fall-Through */ + fallthrough; case NEXTHOP_TYPE_IFINDEX: if (next1->ifindex < next2->ifindex) return -1; @@ -1851,7 +1859,7 @@ int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r) zapi_pbr_rule_filter_encode(s, &(r->filter)); zapi_pbr_rule_action_encode(s, &(r->action)); - stream_put(s, r->ifname, INTERFACE_NAMSIZ); + stream_put(s, r->ifname, IFNAMSIZ); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1875,7 +1883,7 @@ bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r) if (!zapi_pbr_rule_action_decode(s, &(r->action))) goto stream_failure; - STREAM_GET(r->ifname, s, INTERFACE_NAMSIZ); + STREAM_GET(r->ifname, s, IFNAMSIZ); return true; stream_failure: @@ -2034,7 +2042,7 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, STREAM_GETL(s, seq); STREAM_GETL(s, prio); STREAM_GETL(s, uni); - STREAM_GET(ifname, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname, s, IFNAMSIZ); if (zclient_debug) zlog_debug("%s: %u %u %u %s", __func__, seq, prio, uni, ifname); @@ -2269,8 +2277,8 @@ const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, /* * Decode the nexthop-tracking update message */ -bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, - struct zapi_route *nhr) +static bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, + struct zapi_route *nhr) { uint32_t i; @@ -2526,12 +2534,12 @@ static int zclient_vrf_delete(ZAPI_CALLBACK_ARGS) static int zclient_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - char ifname_tmp[INTERFACE_NAMSIZ + 1] = {}; + char ifname_tmp[IFNAMSIZ + 1] = {}; struct stream *s = zclient->ibuf; struct vrf *vrf; /* Read interface name. */ - STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname_tmp, s, IFNAMSIZ); /* Lookup/create interface by name. */ vrf = vrf_lookup_by_id(vrf_id); @@ -2562,10 +2570,10 @@ stream_failure: struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id) { struct interface *ifp; - char ifname_tmp[INTERFACE_NAMSIZ + 1] = {}; + char ifname_tmp[IFNAMSIZ + 1] = {}; /* Read interface name. */ - STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname_tmp, s, IFNAMSIZ); /* Lookup this by interface index. */ ifp = if_lookup_by_name(ifname_tmp, vrf_id); @@ -3051,36 +3059,6 @@ stream_failure: return NULL; } -struct interface *zebra_interface_vrf_update_read(struct stream *s, - vrf_id_t vrf_id, - vrf_id_t *new_vrf_id) -{ - char ifname[INTERFACE_NAMSIZ + 1] = {}; - struct interface *ifp; - vrf_id_t new_id; - - /* Read interface name. */ - STREAM_GET(ifname, s, INTERFACE_NAMSIZ); - - /* Lookup interface. */ - ifp = if_lookup_by_name(ifname, vrf_id); - if (ifp == NULL) { - flog_err(EC_LIB_ZAPI_ENCODE, - "INTERFACE_VRF_UPDATE: Cannot find IF %s in VRF %d", - ifname, vrf_id); - return NULL; - } - - /* Fetch new VRF Id. */ - STREAM_GETL(s, new_id); - - *new_vrf_id = new_id; - return ifp; - -stream_failure: - return NULL; -} - /* filter unwanted messages until the expected one arrives */ static int zclient_read_sync_response(struct zclient *zclient, uint16_t expected_cmd) @@ -3946,7 +3924,7 @@ enum zclient_send_status zebra_send_pw(struct zclient *zclient, int command, stream_reset(s); zclient_create_header(s, command, VRF_DEFAULT); - stream_write(s, pw->ifname, INTERFACE_NAMSIZ); + stream_write(s, pw->ifname, IFNAMSIZ); stream_putl(s, pw->ifindex); /* Put type */ @@ -3993,7 +3971,7 @@ int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw) s = zclient->ibuf; /* Get data. */ - stream_get(pw->ifname, s, INTERFACE_NAMSIZ); + stream_get(pw->ifname, s, IFNAMSIZ); STREAM_GETL(s, pw->ifindex); STREAM_GETL(s, pw->status); @@ -4298,6 +4276,28 @@ stream_failure: return -1; } +static int zclient_nexthop_update(ZAPI_CALLBACK_ARGS) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct prefix match; + struct zapi_route route; + + if (!vrf) { + zlog_warn("nexthop update for unknown VRF ID %u", vrf_id); + return 0; + } + + if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &route)) { + zlog_err("failed to decode nexthop update"); + return -1; + } + + if (zclient->nexthop_update) + zclient->nexthop_update(vrf, &match, &route); + + return 0; +} + static zclient_handler *const lib_handlers[] = { /* fundamentals */ [ZEBRA_CAPABILITIES] = zclient_capability_decode, @@ -4311,6 +4311,9 @@ static zclient_handler *const lib_handlers[] = { [ZEBRA_INTERFACE_UP] = zclient_interface_up, [ZEBRA_INTERFACE_DOWN] = zclient_interface_down, + /* NHT pre-decode */ + [ZEBRA_NEXTHOP_UPDATE] = zclient_nexthop_update, + /* BFD */ [ZEBRA_BFD_DEST_REPLAY] = zclient_bfd_session_replay, [ZEBRA_INTERFACE_BFD_DEST_UPDATE] = zclient_bfd_session_update, @@ -4419,7 +4422,8 @@ static void zclient_read(struct event *thread) zlog_debug("zclient %p command %s VRF %u", zclient, zserv_command_string(command), vrf_id); - if (command < array_size(lib_handlers) && lib_handlers[command]) + if (!zclient->auxiliary && command < array_size(lib_handlers) && + lib_handlers[command]) lib_handlers[command](command, zclient, length, vrf_id); if (command < zclient->n_handlers && zclient->handlers[command]) zclient->handlers[command](command, zclient, length, vrf_id); @@ -4516,6 +4520,24 @@ static void zclient_event(enum zclient_event event, struct zclient *zclient) } } +enum zclient_send_status zclient_interface_set_arp(struct zclient *client, + struct interface *ifp, + bool arp_enable) +{ + struct stream *s; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_INTERFACE_SET_ARP, ifp->vrf->vrf_id); + + stream_putl(s, ifp->ifindex); + stream_putc(s, arp_enable); + + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(client); +} + enum zclient_send_status zclient_interface_set_master(struct zclient *client, struct interface *master, struct interface *slave) @@ -4705,7 +4727,7 @@ static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add) int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, union sockunion *out, struct interface *ifp, - int ndm_state) + int ndm_state, int ip_len) { int ret = 0; @@ -4718,6 +4740,7 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, sockunion_get_addrlen(out)); } else stream_putc(s, AF_UNSPEC); + stream_putl(s, ip_len); stream_putl(s, ifp->ifindex); if (out) stream_putl(s, ndm_state); @@ -4735,6 +4758,7 @@ int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api) return -1; zclient_neigh_ip_read_entry(s, &api->ip_out); + STREAM_GETL(s, api->ip_len); STREAM_GETL(s, api->index); STREAM_GETL(s, api->ndm_state); return 0; @@ -4881,3 +4905,23 @@ enum zclient_send_status zclient_opaque_drop_notify(struct zclient *zclient, return zclient_send_message(zclient); } + +void zclient_register_neigh(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, + bool reg) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + reg ? ZEBRA_NEIGH_REGISTER + : ZEBRA_NEIGH_UNREGISTER, + vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index f18fc05..1bf9106 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -98,6 +98,7 @@ typedef enum { ZEBRA_INTERFACE_UP, ZEBRA_INTERFACE_DOWN, ZEBRA_INTERFACE_SET_MASTER, + ZEBRA_INTERFACE_SET_ARP, ZEBRA_INTERFACE_SET_PROTODOWN, ZEBRA_ROUTE_ADD, ZEBRA_ROUTE_DELETE, @@ -216,11 +217,11 @@ typedef enum { ZEBRA_NEIGH_DISCOVER, ZEBRA_ROUTE_NOTIFY_REQUEST, ZEBRA_CLIENT_CLOSE_NOTIFY, - ZEBRA_NHRP_NEIGH_ADDED, - ZEBRA_NHRP_NEIGH_REMOVED, - ZEBRA_NHRP_NEIGH_GET, - ZEBRA_NHRP_NEIGH_REGISTER, - ZEBRA_NHRP_NEIGH_UNREGISTER, + ZEBRA_NEIGH_ADDED, + ZEBRA_NEIGH_REMOVED, + ZEBRA_NEIGH_GET, + ZEBRA_NEIGH_REGISTER, + ZEBRA_NEIGH_UNREGISTER, ZEBRA_NEIGH_IP_ADD, ZEBRA_NEIGH_IP_DEL, ZEBRA_CONFIGURE_ARP, @@ -293,6 +294,8 @@ struct zapi_cap { typedef int (zclient_handler)(ZAPI_CALLBACK_ARGS); /* clang-format on */ +struct zapi_route; + /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ @@ -301,12 +304,14 @@ struct zclient { /* Privileges to change socket values */ struct zebra_privs_t *privs; - /* Do we care about failure events for route install? */ - bool receive_notify; - /* Is this a synchronous client? */ bool synchronous; + /* Auxiliary clients don't execute standard library handlers + * (which otherwise would duplicate VRF/interface add/delete/etc. + */ + bool auxiliary; + /* BFD enabled with bfd_protocol_integration_init() */ bool bfd_integration; @@ -348,6 +353,19 @@ struct zclient { void (*zebra_connected)(struct zclient *); void (*zebra_capabilities)(struct zclient_capabilities *cap); + /* + * match -> is the prefix that the calling daemon asked to be matched + * against. + * nhr->prefix -> is the actual prefix that was matched against in the + * rib itself. + * + * This distinction is made because a LPM can be made if there is a + * covering route. This way the upper level protocol can make a + * decision point about whether or not it wants to use the match or not. + */ + void (*nexthop_update)(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr); + int (*handle_error)(enum zebra_error_types error); /* @@ -629,7 +647,7 @@ struct zapi_sr_policy { }; struct zapi_pw { - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int type; int af; @@ -642,7 +660,7 @@ struct zapi_pw { }; struct zapi_pw_status { - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; uint32_t status; }; @@ -819,11 +837,18 @@ extern char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; struct zclient_options { - bool receive_notify; bool synchronous; + + /* auxiliary = don't call common lib/ handlers that manage bits. + * Those should only run once, on the "main" zclient, which this is + * not. (This is also set for synchronous clients.) + */ + bool auxiliary; }; -extern struct zclient_options zclient_options_default; +extern const struct zclient_options zclient_options_default; +extern const struct zclient_options zclient_options_sync; +extern const struct zclient_options zclient_options_auxiliary; /* link layer representation for GRE like interfaces * ip_in is the underlay IP, ip_out is the tunnel dest @@ -843,6 +868,7 @@ extern struct zclient_options zclient_options_default; struct zapi_neigh_ip { int cmd; + int ip_len; struct ipaddr ip_in; struct ipaddr ip_out; ifindex_t index; @@ -851,7 +877,7 @@ struct zapi_neigh_ip { int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api); int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, union sockunion *out, struct interface *ifp, - int ndm_state); + int ndm_state, int ip_len); /* * We reserve the top 4 bits for l2-NHG, everything else @@ -865,12 +891,12 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, ((uint32_t)250000000) /* Bottom 28 bits then rounded down */ #define ZEBRA_NHG_PROTO_SPACING (ZEBRA_NHG_PROTO_UPPER / ZEBRA_ROUTE_MAX) #define ZEBRA_NHG_PROTO_LOWER \ - (ZEBRA_NHG_PROTO_SPACING * (ZEBRA_ROUTE_CONNECT + 1)) + (ZEBRA_NHG_PROTO_SPACING * (ZEBRA_ROUTE_LOCAL + 1)) extern uint32_t zclient_get_nhg_start(uint32_t proto); extern struct zclient *zclient_new(struct event_loop *m, - struct zclient_options *opt, + const struct zclient_options *opt, zclient_handler *const *handlers, size_t n_handlers); @@ -1011,6 +1037,9 @@ extern int zclient_read_header(struct stream *s, int sock, uint16_t *size, */ extern bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr); +extern enum zclient_send_status zclient_interface_set_arp(struct zclient *client, + struct interface *ifp, + bool arp_enable); extern enum zclient_send_status zclient_interface_set_master(struct zclient *client, struct interface *master, struct interface *slave); @@ -1019,9 +1048,6 @@ extern struct connected *zebra_interface_address_read(int, struct stream *, vrf_id_t); extern struct nbr_connected * zebra_interface_nbr_address_read(int, struct stream *, vrf_id_t); -extern struct interface *zebra_interface_vrf_update_read(struct stream *s, - vrf_id_t vrf_id, - vrf_id_t *new_vrf_id); extern int zebra_router_id_update_read(struct stream *s, struct prefix *rid); extern struct interface *zebra_interface_link_params_read(struct stream *s, @@ -1123,18 +1149,6 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); -/* - * match -> is the prefix that the calling daemon asked to be matched - * against. - * nhr->prefix -> is the actual prefix that was matched against in the - * rib itself. - * - * This distinction is made because a LPM can be made if there is a - * covering route. This way the upper level protocol can make a decision - * point about whether or not it wants to use the match or not. - */ -extern bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, - struct zapi_route *nhr); const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, int bufsize); @@ -1157,6 +1171,15 @@ static inline void zapi_route_set_blackhole(struct zapi_route *api, SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP); }; +static inline void zapi_route_set_nhg_id(struct zapi_route *api, + uint32_t *nhg_id) +{ + api->nexthop_num = 0; + api->nhgid = *nhg_id; + if (api->nhgid) + SET_FLAG(api->message, ZAPI_MESSAGE_NHG); +}; + extern enum zclient_send_status zclient_send_mlag_register(struct zclient *client, uint32_t bit_map); extern enum zclient_send_status @@ -1304,6 +1327,9 @@ enum zapi_opaque_registry { */ extern enum zclient_send_status zclient_send_hello(struct zclient *client); +extern void zclient_register_neigh(struct zclient *zclient, vrf_id_t vrf_id, + afi_t afi, bool reg); + extern enum zclient_send_status zclient_send_neigh_discovery_req(struct zclient *zclient, const struct interface *ifp, diff --git a/lib/zebra.h b/lib/zebra.h index ecc87f5..15a54f6 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -17,17 +17,9 @@ #include #include #include -#include -#include -#include -#include -#include -#include #ifdef HAVE_STROPTS_H #include #endif /* HAVE_STROPTS_H */ -#include -#include #include #include #ifdef HAVE_SYS_SYSCTL_H @@ -37,22 +29,15 @@ #include #endif #endif /* HAVE_SYS_SYSCTL_H */ -#include #ifdef HAVE_SYS_CONF_H #include #endif /* HAVE_SYS_CONF_H */ #ifdef HAVE_SYS_KSYM_H #include #endif /* HAVE_SYS_KSYM_H */ -#include #include #include -#include -#include -#include -#include #include -#include #ifdef HAVE_SYS_ENDIAN_H #include #endif @@ -63,11 +48,6 @@ /* misc include group */ #include -#ifdef HAVE_LCAPS -#include -#include -#endif /* HAVE_LCAPS */ - /* network include group */ #include @@ -76,10 +56,6 @@ #include #endif /* HAVE_SYS_SOCKIO_H */ -#ifdef __APPLE__ -#define __APPLE_USE_RFC_3542 -#endif - #ifndef HAVE_LIBCRYPT #ifdef HAVE_LIBCRYPTO #include @@ -87,15 +63,9 @@ #endif #endif -#ifdef CRYPTO_OPENSSL -#include -#include -#endif - #include "openbsd-tree.h" #include -#include #include #include @@ -113,14 +83,9 @@ #include #endif /* HAVE_NET_IF_VAR_H */ -#include - -#ifdef HAVE_NETLINK -#include -#include -#include -#else +#ifndef HAVE_NETLINK #define RT_TABLE_MAIN 0 +#define RT_TABLE_LOCAL RT_TABLE_MAIN #endif /* HAVE_NETLINK */ #include @@ -146,37 +111,14 @@ #include #endif /* HAVE_NETINET6_IN_H */ - #ifdef HAVE_NETINET6_IP6_H #include #endif /* HAVE_NETINET6_IP6_H */ -#include - #ifdef HAVE_NETINET6_ND6_H #include #endif /* HAVE_NETINET6_ND6_H */ -/* Some systems do not define UINT32_MAX, etc.. from inttypes.h - * e.g. this makes life easier for FBSD 4.11 users. - */ -#ifndef INT16_MAX -#define INT16_MAX (32767) -#endif -#ifndef INT32_MAX -#define INT32_MAX (2147483647) -#endif -#ifndef UINT16_MAX -#define UINT16_MAX (65535U) -#endif -#ifndef UINT32_MAX -#define UINT32_MAX (4294967295U) -#endif - -#ifdef HAVE_GLIBC_BACKTRACE -#include -#endif /* HAVE_GLIBC_BACKTRACE */ - /* Local includes: */ #if !defined(__GNUC__) #define __attribute__(x) @@ -210,26 +152,6 @@ size_t strlcpy(char *__restrict dest, void explicit_bzero(void *buf, size_t len); #endif -#if !defined(HAVE_STRUCT_MMSGHDR_MSG_HDR) || !defined(HAVE_SENDMMSG) -/* avoid conflicts in case we have partial support */ -#define mmsghdr frr_mmsghdr -#define sendmmsg frr_sendmmsg - -struct mmsghdr { - struct msghdr msg_hdr; - unsigned int msg_len; -}; - -/* just go 1 at a time here, the loop this is used in will handle the rest */ -static inline int sendmmsg(int fd, struct mmsghdr *mmh, unsigned int len, - int flags) -{ - int rv = sendmsg(fd, &mmh->msg_hdr, 0); - - return rv > 0 ? 1 : rv; -} -#endif - /* * RFC 3542 defines several macros for using struct cmsghdr. * Here, we define those that are not present @@ -283,10 +205,9 @@ struct in_pktinfo { * OpenBSD: network byte order, apart from older versions which are as per * *BSD */ -#if defined(__NetBSD__) \ - || (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) \ - || (defined(__OpenBSD__) && (OpenBSD < 200311)) \ - || (defined(__APPLE__)) +#if defined(__NetBSD__) || \ + (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) || \ + (defined(__OpenBSD__) && (OpenBSD < 200311)) #define HAVE_IP_HDRINCL_BSD_ORDER #endif @@ -300,15 +221,6 @@ struct in_pktinfo { #define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL #endif /* IN6_ARE_ADDR_EQUAL */ -/* default zebra TCP port for zclient */ -#define ZEBRA_PORT 2600 - -/* - * The compiler.h header is used for anyone using the CPP_NOTICE - * since this is universally needed, let's add it to zebra.h - */ -#include "compiler.h" - /* Zebra route's types are defined in route_types.h */ #include "lib/route_types.h" @@ -358,27 +270,6 @@ typedef enum { for (afi = AFI_IP; afi < AFI_MAX; afi++) \ for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) -/* Default Administrative Distance of each protocol. */ -#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 -#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0 -#define ZEBRA_STATIC_DISTANCE_DEFAULT 1 -#define ZEBRA_RIP_DISTANCE_DEFAULT 120 -#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120 -#define ZEBRA_OSPF_DISTANCE_DEFAULT 110 -#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110 -#define ZEBRA_ISIS_DISTANCE_DEFAULT 115 -#define ZEBRA_IBGP_DISTANCE_DEFAULT 200 -#define ZEBRA_EBGP_DISTANCE_DEFAULT 20 -#define ZEBRA_TABLE_DISTANCE_DEFAULT 15 -#define ZEBRA_EIGRP_DISTANCE_DEFAULT 90 -#define ZEBRA_NHRP_DISTANCE_DEFAULT 10 -#define ZEBRA_LDP_DISTANCE_DEFAULT 150 -#define ZEBRA_BABEL_DISTANCE_DEFAULT 100 -#define ZEBRA_SHARP_DISTANCE_DEFAULT 150 -#define ZEBRA_PBR_DISTANCE_DEFAULT 200 -#define ZEBRA_OPENFABRIC_DISTANCE_DEFAULT 115 -#define ZEBRA_MAX_DISTANCE_DEFAULT 255 - /* Flag manipulation macros. */ #define CHECK_FLAG(V,F) ((V) & (F)) #define SET_FLAG(V,F) (V) |= (F) diff --git a/lib/zlog.c b/lib/zlog.c index 309a955..8734fd5 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -4,6 +4,12 @@ */ #include "zebra.h" +#include +#include + +#ifdef HAVE_GLIBC_BACKTRACE +#include +#endif /* HAVE_GLIBC_BACKTRACE */ #include #include @@ -32,9 +38,6 @@ #ifdef __DragonFly__ #include #endif -#ifdef __APPLE__ -#include -#endif #ifdef HAVE_LIBUNWIND #define UNW_LOCAL_ONLY @@ -81,7 +84,7 @@ static struct zlog_targets_head zlog_targets; /* Global setting for buffered vs immediate output. The default is * per-pthread buffering. */ -static bool default_immediate; +static bool zlog_default_immediate; /* cf. zlog.h for additional comments on this struct. * @@ -442,7 +445,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, struct zlog_msg *msg; char *buf; bool ignoremsg = true; - bool immediate = default_immediate; + bool immediate = zlog_default_immediate; /* avoid further processing cost if no target wants this message */ rcu_read_lock(); @@ -963,7 +966,12 @@ struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, */ void zlog_set_immediate(bool set_p) { - default_immediate = set_p; + zlog_default_immediate = set_p; +} + +bool zlog_get_immediate_mode(void) +{ + return zlog_default_immediate; } /* common init */ diff --git a/lib/zlog.h b/lib/zlog.h index a207b29..27401d5 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -276,6 +276,7 @@ extern void zlog_tls_buffer_fini(void); /* Enable or disable 'immediate' output - default is to buffer messages. */ extern void zlog_set_immediate(bool set_p); +bool zlog_get_immediate_mode(void); extern const char *zlog_priority_str(int priority); diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c index 9bc1c81..4c60d4b 100644 --- a/lib/zlog_5424.c +++ b/lib/zlog_5424.c @@ -13,6 +13,9 @@ */ #include "zebra.h" +#include + +#include "frrsendmmsg.h" #include "zlog_5424.h" @@ -913,7 +916,7 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type) } flags = O_NONBLOCK; - /* fallthru */ + fallthrough; case ZLOG_5424_DST_FILE: if (!zcf->filename) diff --git a/lib/zlog_live.c b/lib/zlog_live.c index 4d3c350..1b7696c 100644 --- a/lib/zlog_live.c +++ b/lib/zlog_live.c @@ -5,6 +5,8 @@ #include "zebra.h" +#include "frrsendmmsg.h" + #include "zlog_live.h" #include "memory.h" diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c index b0f7571..bbd228f 100644 --- a/lib/zlog_targets.c +++ b/lib/zlog_targets.c @@ -5,6 +5,7 @@ #include "zebra.h" +#include #include #include diff --git a/m4/.gitignore b/m4/.gitignore index 63f9fa7..37888dd 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -7,6 +7,7 @@ !ax_prog_perl_modules.m4 !ax_pthread.m4 !ax_python.m4 +!ax_recursive_eval.m4 !ax_sys_weak_alias.m4 !ax_sys_weak_alias.m4 !pkg.m4 diff --git a/m4/ax_recursive_eval.m4 b/m4/ax_recursive_eval.m4 new file mode 100644 index 0000000..0283602 --- /dev/null +++ b/m4/ax_recursive_eval.m4 @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later WITH Autoconf-exception-2.0 +# +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_recursive_eval.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_RECURSIVE_EVAL(VALUE, RESULT) +# +# DESCRIPTION +# +# Interpolate the VALUE in loop until it doesn't change, and set the +# result to $RESULT. This version has a recursion limit (10). +# +# LICENSE +# +# Copyright (c) 2008 Alexandre Duret-Lutz +# Copyright (c) 2024 David Lamparter + +AC_DEFUN([AX_RECURSIVE_EVAL], +[_lcl_receval="$1" +$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" + _lcl_receval_old='' + for _rec_limit in 1 2 3 4 5 6 7 8 9 10; do + test "[$]_lcl_receval_old" = "[$]_lcl_receval" && break + _lcl_receval_old="[$]_lcl_receval" + eval _lcl_receval="\"[$]_lcl_receval\"" + done + echo "[$]_lcl_receval")`]) diff --git a/mgmtd/mgmt.c b/mgmtd/mgmt.c index 77c4473..8d41643 100644 --- a/mgmtd/mgmt.c +++ b/mgmtd/mgmt.c @@ -52,17 +52,26 @@ void mgmt_init(void) /* Initialize the MGMTD Frontend Adapter Module */ mgmt_fe_adapter_init(mm->master); - /* Initialize the CLI frontend client */ + /* + * Initialize the CLI frontend client -- this queues an event for the + * client to short-circuit connect to the server (ourselves). + */ vty_init_mgmt_fe(); - /* MGMTD VTY commands installation. */ + /* + * MGMTD VTY commands installation -- the frr lib code will queue an + * event to read the config files which needs to happen after the + * connect from above is made. + */ mgmt_vty_init(); /* * Initialize the MGMTD Backend Adapter Module * - * We do this after the FE stuff so that we always read our config file - * prior to any BE connection. + * We do this after the FE stuff so that we have read our config file + * prior to any BE connection. Setting up the server will queue a + * "socket read" event to accept BE connections. So the code is counting + * on the above 2 events to run prior to any `accept` event from here. */ mgmt_be_adapter_init(mm->master); } diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h index d89d76f..665e8d8 100644 --- a/mgmtd/mgmt.h +++ b/mgmtd/mgmt.h @@ -13,14 +13,13 @@ #include "vrf.h" #include "defaults.h" #include "stream.h" +#include "mgmt_defines.h" #include "mgmtd/mgmt_memory.h" -#include "mgmtd/mgmt_defines.h" #include "mgmtd/mgmt_history.h" #include "mgmtd/mgmt_txn.h" #include "mgmtd/mgmt_ds.h" -#define MGMTD_VTY_PORT 2623 #define MGMTD_SOCKET_BUF_SIZE 65535 #define MGMTD_MAX_COMMIT_LIST 10 @@ -70,11 +69,6 @@ struct mgmt_master { extern struct mgmt_master *mm; /* Inline functions */ -static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) -{ - return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) - + (a.tv_usec - b.tv_usec)); -} /* * Remove trailing separator from a string. diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index 399fdaf..b311bf4 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -10,79 +10,141 @@ #include #include "darr.h" #include "frrevent.h" +#include "frrstr.h" #include "sockopt.h" #include "network.h" #include "libfrr.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_memory.h" #include "mgmt_be_client.h" #include "mgmtd/mgmt_be_adapter.h" -#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \ +#define __log_err(fmt, ...) \ zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define FOREACH_ADAPTER_IN_LIST(adapter) \ frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter)) +/* ---------- */ +/* Client IDs */ +/* ---------- */ + +const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = { + [MGMTD_BE_CLIENT_ID_TESTC] = "mgmtd-testc", /* always first */ + [MGMTD_BE_CLIENT_ID_ZEBRA] = "zebra", +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = "ripd", +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = "ripngd", +#endif +#ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = "staticd", +#endif + [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid", +}; + +/* ------------- */ +/* XPATH MAPPING */ +/* ------------- */ + /* - * Mapping of YANG XPath regular expressions to - * their corresponding backend clients. + * Mapping of YANG XPath prefixes to their corresponding backend clients. */ struct mgmt_be_xpath_map { - char *xpath_regexp; - uint subscr_info[MGMTD_BE_CLIENT_ID_MAX]; + char *xpath_prefix; + uint64_t clients; }; -struct mgmt_be_client_xpath { - const char *xpath; - uint subscribed; +/* + * Each client gets their own map, but also union all the strings into the + * above map as well. + */ + +static const char *const zebra_config_xpaths[] = { + "/frr-affinity-map:lib", + "/frr-filter:lib", + "/frr-route-map:lib", + "/frr-zebra:zebra", + "/frr-interface:lib", + "/frr-vrf:lib", + NULL, }; -struct mgmt_be_client_xpath_map { - struct mgmt_be_client_xpath *xpaths; - uint nxpaths; +static const char *const zebra_oper_xpaths[] = { + "/frr-interface:lib/interface", + "/frr-vrf:lib/vrf/frr-zebra:zebra", + "/frr-zebra:zebra", + NULL, }; -struct mgmt_be_get_adapter_config_params { - struct mgmt_be_client_adapter *adapter; - struct nb_config_cbs *cfg_chgs; - uint32_t seq; +#if HAVE_RIPD +static const char *const ripd_config_xpaths[] = { + "/frr-filter:lib", + "/frr-interface:lib/interface", + "/frr-ripd:ripd", + "/frr-route-map:lib", + "/frr-vrf:lib", + NULL, +}; +static const char *const ripd_oper_xpaths[] = { + "/frr-ripd:ripd", + NULL, }; +#endif + +#if HAVE_RIPNGD +static const char *const ripngd_config_xpaths[] = { + "/frr-filter:lib", + "/frr-interface:lib/interface", + "/frr-ripngd:ripngd", + "/frr-route-map:lib", + "/frr-vrf:lib", + NULL, +}; +static const char *const ripngd_oper_xpaths[] = { + "/frr-ripngd:ripngd", + NULL, +}; +#endif -/* - * Each client gets their own map, but also union all the strings into the - * above map as well. - */ #if HAVE_STATICD -static struct mgmt_be_client_xpath staticd_xpaths[] = { - { - .xpath = "/frr-vrf:lib/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - { - .xpath = "/frr-interface:lib/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - { - .xpath = - "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, +static const char *const staticd_config_xpaths[] = { + "/frr-vrf:lib", + "/frr-interface:lib", + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + NULL, }; #endif -static struct mgmt_be_client_xpath_map - mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +static const char *const *be_client_config_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_config_xpaths, +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = ripd_config_xpaths, +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_config_xpaths, +#endif #ifdef HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, - array_size(staticd_xpaths)}, + [MGMTD_BE_CLIENT_ID_STATICD] = staticd_config_xpaths, #endif }; +static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = ripd_oper_xpaths, +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_oper_xpaths, +#endif + [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths, +}; + /* * We would like to have a better ADT than one with O(n) comparisons * @@ -91,7 +153,10 @@ static struct mgmt_be_client_xpath_map * says this probably involves exact match/no-match on a stem in the map array * or something like that. */ -static struct mgmt_be_xpath_map *mgmt_xpath_map; + +static struct mgmt_be_xpath_map *be_cfg_xpath_map; +static struct mgmt_be_xpath_map *be_oper_xpath_map; +static struct mgmt_be_xpath_map *be_notif_xpath_map; static struct event_loop *mgmt_loop; static struct msg_server mgmt_be_server = {.fd = -1}; @@ -101,12 +166,33 @@ static struct mgmt_be_adapters_head mgmt_be_adapters; static struct mgmt_be_client_adapter *mgmt_be_adapters_by_id[MGMTD_BE_CLIENT_ID_MAX]; + /* Forward declarations */ static void mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter); -static uint mgmt_be_get_subscr_for_xpath_and_client( - const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask); +static bool be_is_client_interested(const char *xpath, + enum mgmt_be_client_id id, bool config); + +const char *mgmt_be_client_id2name(enum mgmt_be_client_id id) +{ + if (id > MGMTD_BE_CLIENT_ID_MAX) + return "invalid client id"; + return mgmt_be_client_names[id]; +} + +static enum mgmt_be_client_id mgmt_be_client_name2id(const char *name) +{ + enum mgmt_be_client_id id; + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (!strncmp(mgmt_be_client_names[id], name, + MGMTD_CLIENT_NAME_MAX_LEN)) + return id; + } + + return MGMTD_BE_CLIENT_ID_MAX; +} static struct mgmt_be_client_adapter * mgmt_be_find_adapter_by_fd(int conn_fd) @@ -135,183 +221,103 @@ mgmt_be_find_adapter_by_name(const char *name) } static void mgmt_register_client_xpath(enum mgmt_be_client_id id, - const char *xpath, uint subscribed) + const char *xpath, bool config, bool oper) { - struct mgmt_be_xpath_map *map; + struct mgmt_be_xpath_map **maps, *map; - darr_foreach_p (mgmt_xpath_map, map) - if (!strcmp(xpath, map->xpath_regexp)) { - map->subscr_info[id] = subscribed; + if (config) + maps = &be_cfg_xpath_map; + else if (oper) + maps = &be_oper_xpath_map; + else + maps = &be_notif_xpath_map; + + darr_foreach_p (*maps, map) { + if (!strcmp(xpath, map->xpath_prefix)) { + map->clients |= (1u << id); return; } + } /* we didn't find a matching entry */ - map = darr_append(mgmt_xpath_map); - map->xpath_regexp = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); - map->subscr_info[id] = subscribed; + map = darr_append(*maps); + map->xpath_prefix = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); + map->clients = (1ul << id); } /* - * Load the initial mapping from static init map + * initial the combined maps from per client maps */ static void mgmt_be_xpath_map_init(void) { - struct mgmt_be_client_xpath *init, *end; enum mgmt_be_client_id id; + const char *const *init; - MGMTD_BE_ADAPTER_DBG("Init XPath Maps"); + __dbg("Init XPath Maps"); FOREACH_MGMTD_BE_CLIENT_ID (id) { - init = mgmt_client_xpaths[id].xpaths; - end = init + mgmt_client_xpaths[id].nxpaths; - for (; init < end; init++) { - MGMTD_BE_ADAPTER_DBG(" - XPATH: '%s'", init->xpath); - mgmt_register_client_xpath(id, init->xpath, - init->subscribed); + /* Initialize the common config init map */ + for (init = be_client_config_xpaths[id]; init && *init; init++) { + __dbg(" - CFG XPATH: '%s'", *init); + mgmt_register_client_xpath(id, *init, true, false); + } + + /* Initialize the common oper init map */ + for (init = be_client_oper_xpaths[id]; init && *init; init++) { + __dbg(" - OPER XPATH: '%s'", *init); + mgmt_register_client_xpath(id, *init, false, true); } } - MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", darr_len(mgmt_xpath_map)); + __dbg("Total Cfg XPath Maps: %u", darr_len(be_cfg_xpath_map)); + __dbg("Total Oper XPath Maps: %u", darr_len(be_oper_xpath_map)); } static void mgmt_be_xpath_map_cleanup(void) { struct mgmt_be_xpath_map *map; - darr_foreach_p (mgmt_xpath_map, map) - XFREE(MTYPE_MGMTD_XPATH, map->xpath_regexp); - darr_free(mgmt_xpath_map); -} - -static int mgmt_be_eval_regexp_match(const char *xpath_regexp, - const char *xpath) -{ - int match_len = 0, re_indx = 0, xp_indx = 0; - int rexp_len, xpath_len; - bool match = true, re_wild = false, xp_wild = false; - bool delim = false, enter_wild_match = false; - char wild_delim = 0; + darr_foreach_p (be_cfg_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_cfg_xpath_map); - rexp_len = strlen(xpath_regexp); - xpath_len = strlen(xpath); - - /* - * Remove the trailing wildcard from the regexp and Xpath. - */ - if (rexp_len && xpath_regexp[rexp_len-1] == '*') - rexp_len--; - if (xpath_len && xpath[xpath_len-1] == '*') - xpath_len--; + darr_foreach_p (be_oper_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_oper_xpath_map); - if (!rexp_len || !xpath_len) - return 0; - - for (re_indx = 0, xp_indx = 0; - match && re_indx < rexp_len && xp_indx < xpath_len;) { - match = (xpath_regexp[re_indx] == xpath[xp_indx]); - - /* - * Check if we need to enter wildcard matching. - */ - if (!enter_wild_match && !match && - (xpath_regexp[re_indx] == '*' - || xpath[xp_indx] == '*')) { - /* - * Found wildcard - */ - enter_wild_match = - (xpath_regexp[re_indx-1] == '/' - || xpath_regexp[re_indx-1] == '\'' - || xpath[xp_indx-1] == '/' - || xpath[xp_indx-1] == '\''); - if (enter_wild_match) { - if (xpath_regexp[re_indx] == '*') { - /* - * Begin RE wildcard match. - */ - re_wild = true; - wild_delim = xpath_regexp[re_indx-1]; - } else if (xpath[xp_indx] == '*') { - /* - * Begin XP wildcard match. - */ - xp_wild = true; - wild_delim = xpath[xp_indx-1]; - } - } - } + darr_foreach_p (be_notif_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_notif_xpath_map); +} - /* - * Check if we need to exit wildcard matching. - */ - if (enter_wild_match) { - if (re_wild && xpath[xp_indx] == wild_delim) { - /* - * End RE wildcard matching. - */ - re_wild = false; - if (re_indx < rexp_len-1) - re_indx++; - enter_wild_match = false; - } else if (xp_wild - && xpath_regexp[re_indx] == wild_delim) { - /* - * End XP wildcard matching. - */ - xp_wild = false; - if (xp_indx < xpath_len-1) - xp_indx++; - enter_wild_match = false; - } - } - match = (xp_wild || re_wild - || xpath_regexp[re_indx] == xpath[xp_indx]); +/* + * Check if either path or xpath is a prefix of the other. Before checking the + * xpath is converted to a regular path string (e..g, removing key value + * specifiers). + */ +static bool mgmt_be_xpath_prefix(const char *path, const char *xpath) +{ + int xc, pc; - /* - * Check if we found a delimiter in both the Xpaths - */ - if ((xpath_regexp[re_indx] == '/' - && xpath[xp_indx] == '/') - || (xpath_regexp[re_indx] == ']' - && xpath[xp_indx] == ']') - || (xpath_regexp[re_indx] == '[' - && xpath[xp_indx] == '[')) { - /* - * Increment the match count if we have a - * new delimiter. - */ - if (match && re_indx && xp_indx && !delim) - match_len++; - delim = true; - } else { - delim = false; + while ((xc = *xpath++)) { + if (xc == '[') { + xpath = frrstr_skip_over_char(xpath, ']'); + if (!xpath) + return false; + continue; } - - /* - * Proceed to the next character in the RE/XP string as - * necessary. - */ - if (!re_wild) - re_indx++; - if (!xp_wild) - xp_indx++; + pc = *path++; + if (!pc) + return true; + if (pc != xc) + return false; } - - /* - * If we finished matching and the last token was a full match - * increment the match count appropriately. - */ - if (match && !delim && - (xpath_regexp[re_indx] == '/' - || xpath_regexp[re_indx] == ']')) - match_len++; - - return match_len; + return true; } static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter) { - MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + __dbg("deleting client adapter '%s'", adapter->name); /* * Notify about disconnect for appropriate cleanup @@ -330,8 +336,7 @@ static int mgmt_be_adapter_notify_disconnect(struct msg_conn *conn) { struct mgmt_be_client_adapter *adapter = conn->user; - MGMTD_BE_ADAPTER_DBG("notify disconnect for client adapter '%s'", - adapter->name); + __dbg("notify disconnect for client adapter '%s'", adapter->name); mgmt_be_adapter_delete(adapter); @@ -349,17 +354,14 @@ mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter) /* * We have a Zombie lingering around */ - MGMTD_BE_ADAPTER_DBG( - "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", - adapter->name, adapter->conn->fd, - old->conn->fd); + __dbg("Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", + adapter->name, adapter->conn->fd, old->conn->fd); /* this will/should delete old */ msg_conn_disconnect(old->conn, false); } } } - static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter, Mgmtd__BeMessage *be_msg) { @@ -382,8 +384,8 @@ static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY; be_msg.subscr_reply = &reply; - MGMTD_FE_CLIENT_DBG("Sending SUBSCR_REPLY client: %s sucess: %u", - adapter->name, success); + __dbg("Sending SUBSCR_REPLY client: %s sucess: %u", adapter->name, + success); return mgmt_be_adapter_send_msg(adapter, &be_msg); } @@ -392,32 +394,30 @@ static int mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, Mgmtd__BeMessage *be_msg) { + const char *xpath; + uint i, num; + /* * protobuf-c adds a max size enum with an internal, and changing by * version, name; cast to an int to avoid unhandled enum warnings */ switch ((int)be_msg->message_case) { case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: - MGMTD_BE_ADAPTER_DBG( - "Got SUBSCR_REQ from '%s' to %sregister %zu xpaths", - be_msg->subscr_req->client_name, - !be_msg->subscr_req->subscribe_xpaths && - be_msg->subscr_req->n_xpath_reg - ? "de" - : "", - be_msg->subscr_req->n_xpath_reg); + __dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %zu", + be_msg->subscr_req->client_name, + be_msg->subscr_req->n_config_xpaths, + be_msg->subscr_req->n_oper_xpaths, + be_msg->subscr_req->n_notif_xpaths); if (strlen(be_msg->subscr_req->client_name)) { strlcpy(adapter->name, be_msg->subscr_req->client_name, sizeof(adapter->name)); adapter->id = mgmt_be_client_name2id(adapter->name); if (adapter->id >= MGMTD_BE_CLIENT_ID_MAX) { - MGMTD_BE_ADAPTER_ERR( - "Unable to resolve adapter '%s' to a valid ID. Disconnecting!", - adapter->name); + __log_err("Unable to resolve adapter '%s' to a valid ID. Disconnecting!", + adapter->name); /* this will/should delete old */ msg_conn_disconnect(adapter->conn, false); - zlog_err("XXX different from original code"); break; } mgmt_be_adapters_by_id[adapter->id] = adapter; @@ -427,19 +427,34 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, mgmt_be_adapter_sched_init_event(adapter); } - if (be_msg->subscr_req->n_xpath_reg) - /* we aren't handling dynamic xpaths yet */ - mgmt_be_send_subscr_reply(adapter, false); - else - mgmt_be_send_subscr_reply(adapter, true); + num = be_msg->subscr_req->n_config_xpaths; + for (i = 0; i < num; i++) { + xpath = be_msg->subscr_req->config_xpaths[i]; + mgmt_register_client_xpath(adapter->id, xpath, true, + false); + } + + num = be_msg->subscr_req->n_oper_xpaths; + for (i = 0; i < num; i++) { + xpath = be_msg->subscr_req->oper_xpaths[i]; + mgmt_register_client_xpath(adapter->id, xpath, false, + true); + } + + num = be_msg->subscr_req->n_notif_xpaths; + for (i = 0; i < num; i++) { + xpath = be_msg->subscr_req->notif_xpaths[i]; + mgmt_register_client_xpath(adapter->id, xpath, false, + false); + } + + mgmt_be_send_subscr_reply(adapter, true); break; case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got %s TXN_REPLY from '%s' txn-id %" PRIx64 - " with '%s'", - be_msg->txn_reply->create ? "Create" : "Delete", - adapter->name, be_msg->txn_reply->txn_id, - be_msg->txn_reply->success ? "success" : "failure"); + __dbg("Got %s TXN_REPLY from '%s' txn-id %" PRIx64 " with '%s'", + be_msg->txn_reply->create ? "Create" : "Delete", + adapter->name, be_msg->txn_reply->txn_id, + be_msg->txn_reply->success ? "success" : "failure"); /* * Forward the TXN_REPLY to txn module. */ @@ -449,58 +464,40 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, be_msg->txn_reply->success, adapter); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got CFGDATA_REPLY from '%s' txn-id %" PRIx64 - " batch-id %" PRIu64 " err:'%s'", - adapter->name, be_msg->cfg_data_reply->txn_id, - be_msg->cfg_data_reply->batch_id, - be_msg->cfg_data_reply->error_if_any - ? be_msg->cfg_data_reply->error_if_any - : "None"); + __dbg("Got CFGDATA_REPLY from '%s' txn-id %" PRIx64 " err:'%s'", + adapter->name, be_msg->cfg_data_reply->txn_id, + be_msg->cfg_data_reply->error_if_any + ? be_msg->cfg_data_reply->error_if_any + : "None"); /* * Forward the CGFData-create reply to txn module. */ mgmt_txn_notify_be_cfgdata_reply( be_msg->cfg_data_reply->txn_id, - be_msg->cfg_data_reply->batch_id, be_msg->cfg_data_reply->success, be_msg->cfg_data_reply->error_if_any, adapter); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64 - " for %zu batches id %" PRIu64 "-%" PRIu64 " err:'%s'", - be_msg->cfg_apply_reply->success ? "successful" - : "failed", - adapter->name, be_msg->cfg_apply_reply->txn_id, - be_msg->cfg_apply_reply->n_batch_ids, - be_msg->cfg_apply_reply->batch_ids[0], - be_msg->cfg_apply_reply->batch_ids - [be_msg->cfg_apply_reply->n_batch_ids - 1], - be_msg->cfg_apply_reply->error_if_any - ? be_msg->cfg_apply_reply->error_if_any - : "None"); + __dbg("Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64 + " err:'%s'", + be_msg->cfg_apply_reply->success ? "successful" : "failed", + adapter->name, be_msg->cfg_apply_reply->txn_id, + be_msg->cfg_apply_reply->error_if_any + ? be_msg->cfg_apply_reply->error_if_any + : "None"); /* * Forward the CGFData-apply reply to txn module. */ mgmt_txn_notify_be_cfg_apply_reply( be_msg->cfg_apply_reply->txn_id, be_msg->cfg_apply_reply->success, - (uint64_t *)be_msg->cfg_apply_reply->batch_ids, - be_msg->cfg_apply_reply->n_batch_ids, be_msg->cfg_apply_reply->error_if_any, adapter); break; - case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: - /* - * TODO: Add handling code in future. - */ - break; /* * NOTE: The following messages are always sent from MGMTD to * Backend clients only and/or need not be handled on MGMTd. */ case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: - case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: @@ -532,14 +529,13 @@ int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ; be_msg.txn_req = &txn_req; - MGMTD_BE_ADAPTER_DBG("Sending TXN_REQ to '%s' txn-id: %" PRIu64, - adapter->name, txn_id); + __dbg("Sending TXN_REQ to '%s' txn-id: %" PRIu64, adapter->name, txn_id); return mgmt_be_adapter_send_msg(adapter, &be_msg); } int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, - uint64_t txn_id, uint64_t batch_id, + uint64_t txn_id, Mgmtd__YangCfgDataReq **cfgdata_reqs, size_t num_reqs, bool end_of_data) { @@ -547,7 +543,6 @@ int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, Mgmtd__BeCfgDataCreateReq cfgdata_req; mgmtd__be_cfg_data_create_req__init(&cfgdata_req); - cfgdata_req.batch_id = batch_id; cfgdata_req.txn_id = txn_id; cfgdata_req.data_req = cfgdata_reqs; cfgdata_req.n_data_req = num_reqs; @@ -557,10 +552,8 @@ int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ; be_msg.cfg_data_req = &cfgdata_req; - MGMTD_BE_ADAPTER_DBG( - "Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64 - " batch-id: %" PRIu64, - adapter->name, txn_id, batch_id); + __dbg("Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64 " last: %s", + adapter->name, txn_id, end_of_data ? "yes" : "no"); return mgmt_be_adapter_send_msg(adapter, &be_msg); } @@ -578,42 +571,148 @@ int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ; be_msg.cfg_apply_req = &apply_req; - MGMTD_BE_ADAPTER_DBG("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64, - adapter->name, txn_id); + __dbg("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64, adapter->name, + txn_id); return mgmt_be_adapter_send_msg(adapter, &be_msg); } +int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg) +{ + struct mgmt_be_client_adapter *adapter = mgmt_be_get_adapter_by_id(id); + + if (!adapter) + return -1; + + return mgmt_msg_native_send_msg(adapter->conn, msg, false); +} + +static void mgmt_be_adapter_send_notify(struct mgmt_msg_notify_data *msg, + size_t msglen) +{ + struct mgmt_be_client_adapter *adapter; + struct mgmt_be_xpath_map *map; + struct nb_node *nb_node; + const char *notif; + uint id, len; + + if (!darr_len(be_notif_xpath_map)) + return; + + notif = mgmt_msg_native_xpath_decode(msg, msglen); + if (!notif) { + __log_err("Corrupt notify msg"); + return; + } + + nb_node = nb_node_find(notif); + if (!nb_node) { + __log_err("No schema found for notification: %s", notif); + return; + } + + darr_foreach_p (be_notif_xpath_map, map) { + len = strlen(map->xpath_prefix); + if (strncmp(map->xpath_prefix, nb_node->xpath, len) && + strncmp(map->xpath_prefix, notif, len)) + continue; + + FOREACH_BE_CLIENT_BITS (id, map->clients) { + adapter = mgmt_be_get_adapter_by_id(id); + if (!adapter) + continue; + msg_conn_send_msg(adapter->conn, MGMT_MSG_VERSION_NATIVE, + msg, msglen, NULL, false); + } + } +} + +/* + * Handle a native encoded message + */ +static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_msg_notify_data *notify_msg; + struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_error *error_msg; + + /* get the transaction */ + + switch (msg->code) { + case MGMT_MSG_CODE_ERROR: + error_msg = (typeof(error_msg))msg; + __dbg("Got ERROR from '%s' txn-id %" PRIx64, adapter->name, + msg->refer_id); + + /* Forward the reply to the txn module */ + mgmt_txn_notify_error(adapter, msg->refer_id, msg->req_id, + error_msg->error, error_msg->errstr); + + break; + case MGMT_MSG_CODE_TREE_DATA: + /* tree data from a backend client */ + tree_msg = (typeof(tree_msg))msg; + __dbg("Got TREE_DATA from '%s' txn-id %" PRIx64, adapter->name, + msg->refer_id); + + /* Forward the reply to the txn module */ + mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len); + break; + case MGMT_MSG_CODE_NOTIFY: + notify_msg = (typeof(notify_msg))msg; + __dbg("Got NOTIFY from '%s'", adapter->name); + mgmt_be_adapter_send_notify(notify_msg, msg_len); + mgmt_fe_adapter_send_notify(notify_msg, msg_len); + break; + default: + __log_err("unknown native message txn-id %" PRIu64 + " req-id %" PRIu64 + " code %u from BE client for adapter %s", + msg->refer_id, msg->req_id, msg->code, adapter->name); + break; + } +} + + static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { struct mgmt_be_client_adapter *adapter = conn->user; - Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data); + Mgmtd__BeMessage *be_msg; + + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + + if (len >= sizeof(*msg)) + be_adapter_handle_native_msg(adapter, msg, len); + else + __log_err("native message to adapter %s too short %zu", + adapter->name, len); + return; + } + be_msg = mgmtd__be_message__unpack(NULL, len, data); if (!be_msg) { - MGMTD_BE_ADAPTER_DBG( - "Failed to decode %zu bytes for adapter: %s", len, - adapter->name); + __dbg("Failed to decode %zu bytes for adapter: %s", len, + adapter->name); return; } - MGMTD_BE_ADAPTER_DBG("Decoded %zu bytes of message: %u for adapter: %s", - len, be_msg->message_case, adapter->name); + __dbg("Decoded %zu bytes of message: %u for adapter: %s", len, + be_msg->message_case, adapter->name); (void)mgmt_be_adapter_handle_msg(adapter, be_msg); mgmtd__be_message__free_unpacked(be_msg, NULL); } -static void mgmt_be_iter_and_get_cfg(const char *xpath, struct lyd_node *node, - struct nb_node *nb_node, void *ctx) -{ - struct mgmt_be_get_adapter_config_params *parms = ctx; - struct mgmt_be_client_adapter *adapter = parms->adapter; - uint subscr; - - subscr = mgmt_be_get_subscr_for_xpath_and_client( - xpath, adapter->id, MGMT_SUBSCR_NOTIFY_CFG); - if (subscr) - nb_config_diff_created(node, &parms->seq, parms->cfg_chgs); -} +/* + * Args for callback + */ +struct mgmt_be_get_adapter_config_params { + struct mgmt_be_client_adapter *adapter; + struct nb_config_cbs *cfg_chgs; + uint32_t seq; +}; /* * Initialize a BE client over a new connection @@ -625,28 +724,17 @@ static void mgmt_be_adapter_conn_init(struct event *thread) adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread); assert(adapter && adapter->conn->fd >= 0); - /* - * Check first if the current session can run a CONFIG - * transaction or not. Reschedule if a CONFIG transaction - * from another session is already in progress. - */ - if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) { - zlog_err("XXX txn in progress, retry init"); - mgmt_be_adapter_sched_init_event(adapter); - return; - } - /* * Notify TXN module to create a CONFIG transaction and * download the CONFIGs identified for this new client. * If the TXN module fails to initiate the CONFIG transaction - * disconnect from the client forcing a reconnect later. - * That should also take care of destroying the adapter. + * retry a bit later. It only fails if there's an existing config + * transaction in progress. */ if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) { - zlog_err("XXX notify be adapter conn fail"); - msg_conn_disconnect(adapter->conn, false); - adapter = NULL; + zlog_err("XXX txn in progress, retry init"); + mgmt_be_adapter_sched_init_event(adapter); + return; } } @@ -686,15 +774,18 @@ extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter) */ void mgmt_be_adapter_init(struct event_loop *tm) { + char server_path[MAXPATHLEN]; + assert(!mgmt_loop); mgmt_loop = tm; mgmt_be_adapters_init(&mgmt_be_adapters); mgmt_be_xpath_map_init(); - if (msg_server_init(&mgmt_be_server, MGMTD_BE_SERVER_PATH, tm, - mgmt_be_create_adapter, "backend", - &mgmt_debug_be)) { + snprintf(server_path, sizeof(server_path), MGMTD_BE_SOCK_NAME); + + if (msg_server_init(&mgmt_be_server, server_path, tm, + mgmt_be_create_adapter, "backend", &mgmt_debug_be)) { zlog_err("cannot initialize backend server"); exit(1); } @@ -733,14 +824,17 @@ struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter); RB_INIT(nb_config_cbs, &adapter->cfg_chgs); - adapter->conn = msg_server_conn_create( - mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect, - mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, - MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter, - "BE-adapter"); + adapter->conn = msg_server_conn_create(mgmt_loop, conn_fd, + mgmt_be_adapter_notify_disconnect, + mgmt_be_adapter_process_msg, + MGMTD_BE_MAX_NUM_MSG_PROC, + MGMTD_BE_MAX_NUM_MSG_WRITE, + MGMTD_BE_MAX_MSG_LEN, adapter, + "BE-adapter"); - MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'", - adapter->name); + adapter->conn->debug = DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL); + + __dbg("Added new MGMTD Backend adapter '%s'", adapter->name); return adapter->conn; } @@ -748,8 +842,7 @@ struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) struct mgmt_be_client_adapter * mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id) { - return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id] - : NULL); + return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id] : NULL); } struct mgmt_be_client_adapter * @@ -758,100 +851,99 @@ mgmt_be_get_adapter_by_name(const char *name) return mgmt_be_find_adapter_by_name(name); } -int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, - struct nb_config_cbs **cfg_chgs) +void mgmt_be_adapter_toggle_client_debug(bool set) { - struct mgmt_be_get_adapter_config_params parms; - struct nb_config *cfg_root = mgmt_ds_get_nb_config(mm->running_ds); - - assert(cfg_chgs); + struct mgmt_be_client_adapter *adapter; - /* - * TODO: we should consider making this an assertable condition and - * guaranteeing it be true when this function is called. B/c what is - * going to happen if there are some changes being sent, and we don't - * gather a new snapshot, what new changes that came after the previous - * snapshot will then be lost? - */ - if (RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)) { - parms.adapter = adapter; - parms.cfg_chgs = &adapter->cfg_chgs; - parms.seq = 0; + FOREACH_ADAPTER_IN_LIST (adapter) + adapter->conn->debug = set; +} - mgmt_ds_iter_data(MGMTD_DS_RUNNING, cfg_root, "", - mgmt_be_iter_and_get_cfg, (void *)&parms); +/* + * Get a full set of changes for all the config that an adapter is subscribed to + * receive. + */ +void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **changes) +{ + const struct lyd_node *root, *dnode; + uint32_t seq = 0; + char *xpath; + + /* We can't be in the middle of sending other chgs when here. */ + assert(RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)); + + *changes = &adapter->cfg_chgs; + LY_LIST_FOR (running_config->dnode, root) { + LYD_TREE_DFS_BEGIN (root, dnode) { + if (lysc_is_key(dnode->schema)) + goto walk_cont; + + xpath = lyd_path(dnode, LYD_PATH_STD, NULL, 0); + if (be_is_client_interested(xpath, adapter->id, true)) + nb_config_diff_add_change(*changes, NB_CB_CREATE, &seq, dnode); + else + LYD_TREE_DFS_continue = 1; /* skip any subtree */ + free(xpath); + walk_cont: + LYD_TREE_DFS_END(root, dnode); + } } - - *cfg_chgs = &adapter->cfg_chgs; - return 0; } -void mgmt_be_get_subscr_info_for_xpath( - const char *xpath, struct mgmt_be_client_subscr_info *subscr_info) +uint64_t mgmt_be_interested_clients(const char *xpath, bool config) { - struct mgmt_be_xpath_map *map; + struct mgmt_be_xpath_map *maps, *map; enum mgmt_be_client_id id; + uint64_t clients; - memset(subscr_info, 0, sizeof(*subscr_info)); + maps = config ? be_cfg_xpath_map : be_oper_xpath_map; - MGMTD_BE_ADAPTER_DBG("XPATH: '%s'", xpath); - darr_foreach_p (mgmt_xpath_map, map) { - if (!mgmt_be_eval_regexp_match(map->xpath_regexp, xpath)) - continue; - FOREACH_MGMTD_BE_CLIENT_ID (id) { - subscr_info->xpath_subscr[id] |= map->subscr_info[id]; - } - } + clients = 0; + + __dbg("XPATH: '%s'", xpath); + darr_foreach_p (maps, map) + if (mgmt_be_xpath_prefix(map->xpath_prefix, xpath)) + clients |= map->clients; if (DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)) { - FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (!subscr_info->xpath_subscr[id]) - continue; - MGMTD_BE_ADAPTER_DBG("Cient: %s: subscribed: 0x%x", - mgmt_be_client_id2name(id), - subscr_info->xpath_subscr[id]); - } + FOREACH_BE_CLIENT_BITS (id, clients) + __dbg("Cient: %s: subscribed", + mgmt_be_client_id2name(id)); } + return clients; } /** - * Return the subscription info bits for a given `xpath` for a given - * `client_id`. + * Return true if `client_id` is interested in `xpath` for `config` + * or oper (!`config`). * * Args: - * xpath - the xpath to check for subscription information. + * xpath - the xpath to check for interest. * client_id - the BE client being checked for. - * subscr_mask - The subscr bits the caller is interested in seeing - * if set. + * bool - check for config (vs oper) subscription. * * Returns: - * The subscription info bits. + * Interested or not. */ -static uint mgmt_be_get_subscr_for_xpath_and_client( - const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask) +static bool be_is_client_interested(const char *xpath, + enum mgmt_be_client_id id, bool config) { - struct mgmt_be_client_xpath_map *map; - uint subscr = 0; - uint i; + uint64_t clients; - assert(client_id < MGMTD_BE_CLIENT_ID_MAX); + assert(id < MGMTD_BE_CLIENT_ID_MAX); - MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'", - mgmt_be_client_id2name(client_id), xpath); + __dbg("Checking client: %s for xpath: '%s'", mgmt_be_client_id2name(id), + xpath); - map = &mgmt_client_xpaths[client_id]; - for (i = 0; i < map->nxpaths; i++) { - if (!mgmt_be_eval_regexp_match(map->xpaths[i].xpath, xpath)) - continue; - MGMTD_BE_ADAPTER_DBG("xpath: %s: matched: %s", - map->xpaths[i].xpath, xpath); - subscr |= map->xpaths[i].subscribed; - if ((subscr & subscr_mask) == subscr_mask) - break; + clients = mgmt_be_interested_clients(xpath, config); + if (IS_IDBIT_SET(clients, id)) { + __dbg("client: %s: interested", mgmt_be_client_id2name(id)); + return true; } - MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x", - mgmt_be_client_id2name(client_id), subscr); - return subscr; + + __dbg("client: %s: not interested", mgmt_be_client_id2name(id)); + return false; } void mgmt_be_adapter_status_write(struct vty *vty) @@ -878,56 +970,49 @@ void mgmt_be_adapter_status_write(struct vty *vty) (int)mgmt_be_adapters_count(&mgmt_be_adapters)); } -void mgmt_be_xpath_register_write(struct vty *vty) +static void be_show_xpath_register(struct vty *vty, + struct mgmt_be_xpath_map *map) { - struct mgmt_be_xpath_map *map; enum mgmt_be_client_id id; - struct mgmt_be_client_adapter *adapter; - uint info; - - vty_out(vty, "MGMTD Backend XPath Registry\n"); + const char *astr; - darr_foreach_p (mgmt_xpath_map, map) { - vty_out(vty, " - XPATH: '%s'\n", map->xpath_regexp); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - info = map->subscr_info[id]; - if (!info) - continue; - vty_out(vty, - " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", - mgmt_be_client_id2name(id), - (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, - (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, - (info & MGMT_SUBSCR_OPER_OWN) != 0); - adapter = mgmt_be_get_adapter_by_id(id); - if (adapter) - vty_out(vty, " -- Adapter: %p\n", adapter); - } + vty_out(vty, " - xpath: '%s'\n", map->xpath_prefix); + FOREACH_BE_CLIENT_BITS (id, map->clients) { + astr = mgmt_be_get_adapter_by_id(id) ? "active" : "inactive"; + vty_out(vty, " -- %s-client: '%s'\n", astr, + mgmt_be_client_id2name(id)); } +} +void mgmt_be_xpath_register_write(struct vty *vty) +{ + struct mgmt_be_xpath_map *map; - vty_out(vty, "Total XPath Registries: %u\n", darr_len(mgmt_xpath_map)); + vty_out(vty, "MGMTD Backend CFG XPath Registry: Count: %u\n", + darr_len(be_oper_xpath_map)); + darr_foreach_p (be_cfg_xpath_map, map) + be_show_xpath_register(vty, map); + + vty_out(vty, "\nMGMTD Backend OPER XPath Registry: Count: %u\n", + darr_len(be_oper_xpath_map)); + darr_foreach_p (be_oper_xpath_map, map) + be_show_xpath_register(vty, map); } -void mgmt_be_xpath_subscr_info_write(struct vty *vty, const char *xpath) +void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath) { - struct mgmt_be_client_subscr_info subscr; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; - uint info; + uint64_t cclients, oclients, combined; - mgmt_be_get_subscr_info_for_xpath(xpath, &subscr); + cclients = mgmt_be_interested_clients(xpath, true); + oclients = mgmt_be_interested_clients(xpath, false); + combined = cclients | oclients; vty_out(vty, "XPath: '%s'\n", xpath); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - info = subscr.xpath_subscr[id]; - if (!info) - continue; - vty_out(vty, - " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", - mgmt_be_client_id2name(id), - (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, - (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, - (info & MGMT_SUBSCR_OPER_OWN) != 0); + FOREACH_BE_CLIENT_BITS (id, combined) { + vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d\n", + mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id), + IS_IDBIT_SET(oclients, id)); adapter = mgmt_be_get_adapter_by_id(id); if (adapter) vty_out(vty, " -- Adapter: %p\n", adapter); diff --git a/mgmtd/mgmt_be_adapter.h b/mgmtd/mgmt_be_adapter.h index ca8f55c..491410a 100644 --- a/mgmtd/mgmt_be_adapter.h +++ b/mgmtd/mgmt_be_adapter.h @@ -12,14 +12,37 @@ #include "mgmt_be_client.h" #include "mgmt_msg.h" -#include "mgmtd/mgmt_defines.h" +#include "mgmt_defines.h" #include "mgmtd/mgmt_ds.h" #define MGMTD_BE_CONN_INIT_DELAY_MSEC 50 -#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \ +#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \ mgmt_adaptr_ref[adapter_index] +/** + * CLIENT-ID + * + * Add enum value for each supported component, wrap with + * #ifdef HAVE_COMPONENT + */ +enum mgmt_be_client_id { + MGMTD_BE_CLIENT_ID_TESTC, /* always first */ + MGMTD_BE_CLIENT_ID_ZEBRA, +#ifdef HAVE_RIPD + MGMTD_BE_CLIENT_ID_RIPD, +#endif +#ifdef HAVE_RIPNGD + MGMTD_BE_CLIENT_ID_RIPNGD, +#endif +#ifdef HAVE_STATICD + MGMTD_BE_CLIENT_ID_STATICD, +#endif + MGMTD_BE_CLIENT_ID_MAX +}; +#define MGMTD_BE_CLIENT_ID_MIN 0 + + enum mgmt_be_req_type { MGMTD_BE_REQ_NONE = 0, MGMTD_BE_REQ_CFG_VALIDATE, @@ -49,8 +72,6 @@ struct mgmt_be_client_adapter { enum mgmt_be_client_id id; uint32_t flags; char name[MGMTD_CLIENT_NAME_MAX_LEN]; - uint8_t num_xpath_reg; - char xpath_reg[MGMTD_MAX_NUM_XPATH_REG][MGMTD_MAX_XPATH_LEN]; int refcount; @@ -81,9 +102,36 @@ DECLARE_LIST(mgmt_be_adapters, struct mgmt_be_client_adapter, list_linkage); #define MGMT_SUBSCR_OPER_OWN 0x4 #define MGMT_SUBSCR_ALL 0x7 -struct mgmt_be_client_subscr_info { - uint xpath_subscr[MGMTD_BE_CLIENT_ID_MAX]; -}; +/* --------- */ +/* CLIENT-ID */ +/* --------- */ + +#define FOREACH_MGMTD_BE_CLIENT_ID(id) \ + for ((id) = MGMTD_BE_CLIENT_ID_MIN; (id) < MGMTD_BE_CLIENT_ID_MAX; \ + (id)++) + +#define IS_IDBIT_SET(v, id) (!IS_IDBIT_UNSET(v, id)) +#define IS_IDBIT_UNSET(v, id) (!((v) & (1ull << (id)))) + +#define __GET_NEXT_SET(id, bits) \ + ({ \ + enum mgmt_be_client_id __id = (id); \ + \ + for (; __id < MGMTD_BE_CLIENT_ID_MAX && \ + IS_IDBIT_UNSET(bits, __id); \ + __id++) \ + ; \ + __id; \ + }) + +#define FOREACH_BE_CLIENT_BITS(id, bits) \ + for ((id) = __GET_NEXT_SET(MGMTD_BE_CLIENT_ID_MIN, bits); \ + (id) < MGMTD_BE_CLIENT_ID_MAX; \ + (id) = __GET_NEXT_SET((id) + 1, bits)) + +/* ---------- */ +/* Prototypes */ +/* ---------- */ /* Initialise backend adapter module. */ extern void mgmt_be_adapter_init(struct event_loop *tm); @@ -109,9 +157,15 @@ mgmt_be_get_adapter_by_name(const char *name); extern struct mgmt_be_client_adapter * mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id); +/* Get the client name given a client ID */ +extern const char *mgmt_be_client_id2name(enum mgmt_be_client_id id); + +/* Toggle debug on or off for connected clients. */ +extern void mgmt_be_adapter_toggle_client_debug(bool set); + /* Fetch backend adapter config. */ -extern int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, - struct nb_config_cbs **cfg_chgs); +extern void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **changes); /* Create/destroy a transaction. */ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, @@ -126,9 +180,6 @@ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, * txn_id * Unique transaction identifier. * - * batch_id - * Request batch ID. - * * cfgdata_reqs * An array of pointer to Mgmtd__YangCfgDataReq. * @@ -142,7 +193,7 @@ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, * 0 on success, -1 on failure. */ extern int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, - uint64_t txn_id, uint64_t batch_id, + uint64_t txn_id, Mgmtd__YangCfgDataReq **cfgdata_reqs, size_t num_reqs, bool end_of_data); @@ -171,23 +222,39 @@ extern void mgmt_be_adapter_status_write(struct vty *vty); */ extern void mgmt_be_xpath_register_write(struct vty *vty); + +/** + * Send a native message to a backend client + * + * Args: + * adapter: the client to send the message to. + * msg: a native message from mgmt_msg_native_alloc_msg() + * + * Return: + * Any return value from msg_conn_send_msg(). + */ +extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg); + /** * Lookup the clients which are subscribed to a given `xpath` * and the way they are subscribed. * * Args: * xpath - the xpath to check for subscription information. - * subscr_info - An array of uint indexed by client id - * each eleemnt holds the subscription info - * for that client. + * config - true for config interest false for oper interest. */ -extern void mgmt_be_get_subscr_info_for_xpath( - const char *xpath, struct mgmt_be_client_subscr_info *subscr_info); +extern uint64_t mgmt_be_interested_clients(const char *xpath, bool config); +/** + * mgmt_fe_adapter_send_notify() - notify FE clients of a notification. + * @msg: the notify message from the backend client. + * @msglen: the length of the notify message. + */ +extern void mgmt_fe_adapter_send_notify(struct mgmt_msg_notify_data *msg, + size_t msglen); /* * Dump backend client information for a given xpath to vty. */ -extern void mgmt_be_xpath_subscr_info_write(struct vty *vty, - const char *xpath); +extern void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath); #endif /* _FRR_MGMTD_BE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_defines.h b/mgmtd/mgmt_defines.h deleted file mode 100644 index 40fa670..0000000 --- a/mgmtd/mgmt_defines.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * MGMTD public defines. - * - * Copyright (C) 2021 Vmware, Inc. - * Pushpasis Sarkar - */ - -#ifndef _FRR_MGMTD_DEFINES_H -#define _FRR_MGMTD_DEFINES_H - -#include "yang.h" - -#define MGMTD_CLIENT_NAME_MAX_LEN 32 - -#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN - -#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN - -#define MGMTD_MAX_NUM_XPATH_REG 128 - -#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32 -#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8 - -enum mgmt_result { - MGMTD_SUCCESS = 0, - MGMTD_INVALID_PARAM, - MGMTD_INTERNAL_ERROR, - MGMTD_NO_CFG_CHANGES, - MGMTD_DS_LOCK_FAILED, - MGMTD_DS_UNLOCK_FAILED, - MGMTD_UNKNOWN_FAILURE -}; - -enum mgmt_fe_event { - MGMTD_FE_SERVER = 1, - MGMTD_FE_CONN_READ, - MGMTD_FE_CONN_WRITE, - MGMTD_FE_PROC_MSG -}; - -enum mgmt_be_event { - MGMTD_BE_SERVER = 1, - MGMTD_BE_CONN_INIT, - MGMTD_BE_CONN_READ, - MGMTD_BE_CONN_WRITE, - MGMTD_BE_PROC_MSG, - MGMTD_BE_SCHED_CFG_PREPARE, - MGMTD_BE_RESCHED_CFG_PREPARE, - MGMTD_BE_SCHED_CFG_APPLY, - MGMTD_BE_RESCHED_CFG_APPLY, -}; - -#define MGMTD_TXN_ID_NONE 0 - -#define MGMTD_TXN_BATCH_ID_NONE 0 - -#endif /* _FRR_MGMTD_DEFINES_H */ diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c index a0e610c..eaf52df 100644 --- a/mgmtd/mgmt_ds.c +++ b/mgmtd/mgmt_ds.c @@ -15,10 +15,9 @@ #include "mgmtd/mgmt_txn.h" #include "libyang/libyang.h" -#define MGMTD_DS_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_ds, "DS: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_DS_ERR(fmt, ...) \ - zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define __log_err(fmt, ...) zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) struct mgmt_ds_ctx { Mgmtd__DatastoreId ds_id; @@ -81,8 +80,8 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, if (!src || !dst) return -1; - MGMTD_DS_DBG("Replacing %s with %s", mgmt_ds_id2name(dst->ds_id), - mgmt_ds_id2name(src->ds_id)); + __dbg("Replacing %s with %s", mgmt_ds_id2name(dst->ds_id), + mgmt_ds_id2name(src->ds_id)); if (src->config_ds && dst->config_ds) nb_config_replace(dst->root.cfg_root, src->root.cfg_root, true); @@ -93,14 +92,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, dst->root.dnode_root = yang_dnode_dup(src->root.dnode_root); } - if (src->ds_id == MGMTD_DS_CANDIDATE) { - /* - * Drop the changes in scratch-buffer. - */ - MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); - nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); - } - return 0; } @@ -112,7 +103,7 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, if (!src || !dst) return -1; - MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id); + __dbg("Merging DS %d with %d", dst->ds_id, src->ds_id); if (src->config_ds && dst->config_ds) ret = nb_config_merge(dst->root.cfg_root, src->root.cfg_root, true); @@ -122,18 +113,10 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, src->root.dnode_root, 0); } if (ret != 0) { - MGMTD_DS_ERR("merge failed with err: %d", ret); + __log_err("merge failed with err: %d", ret); return ret; } - if (src->ds_id == MGMTD_DS_CANDIDATE) { - /* - * Drop the changes in scratch-buffer. - */ - MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); - nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); - } - return 0; } @@ -315,7 +298,7 @@ static int mgmt_walk_ds_nodes( assert(mgmt_ds_node_iter_fn); - MGMTD_DS_DBG(" -- START: base xpath: '%s'", base_xpath); + __dbg(" -- START: base xpath: '%s'", base_xpath); if (!base_dnode) /* @@ -326,9 +309,9 @@ static int mgmt_walk_ds_nodes( if (!base_dnode) return -1; - MGMTD_DS_DBG(" search base schema: '%s'", - lysc_path(base_dnode->schema, LYSC_PATH_LOG, xpath, - sizeof(xpath))); + __dbg(" search base schema: '%s'", + lysc_path(base_dnode->schema, LYSC_PATH_LOG, xpath, + sizeof(xpath))); nbnode = (struct nb_node *)base_dnode->schema->priv; (*mgmt_ds_node_iter_fn)(base_xpath, base_dnode, nbnode, ctx); @@ -351,7 +334,7 @@ static int mgmt_walk_ds_nodes( (void)lyd_path(dnode, LYD_PATH_STD, xpath, sizeof(xpath)); - MGMTD_DS_DBG(" -- Child xpath: %s", xpath); + __dbg(" -- Child xpath: %s", xpath); ret = mgmt_walk_ds_nodes(root, xpath, dnode, mgmt_ds_node_iter_fn, ctx); @@ -359,7 +342,7 @@ static int mgmt_walk_ds_nodes( break; } - MGMTD_DS_DBG(" -- END: base xpath: '%s'", base_xpath); + __dbg(" -- END: base xpath: '%s'", base_xpath); return ret; } @@ -423,8 +406,7 @@ int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst, return -1; if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) { - MGMTD_DS_ERR("Failed to load config from the file %s", - file_path); + __log_err("Failed to load config from the file %s", file_path); return -1; } @@ -467,7 +449,7 @@ int mgmt_ds_iter_data(Mgmtd__DatastoreId ds_id, struct nb_config *root, * Oper-state should be kept in mind though for the prefix walk */ - MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_id); + __dbg(" -- START DS walk for DSid: %d", ds_id); /* If the base_xpath is empty then crawl the sibblings */ if (xpath[0] == 0) { diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h index 1cf4816..b8e77e3 100644 --- a/mgmtd/mgmt_ds.h +++ b/mgmtd/mgmt_ds.h @@ -11,8 +11,8 @@ #include "mgmt_fe_client.h" #include "northbound.h" +#include "mgmt_defines.h" -#include "mgmtd/mgmt_defines.h" #include "mgmtd/mgmt_be_adapter.h" #include "mgmtd/mgmt_fe_adapter.h" @@ -29,8 +29,8 @@ #define MGMTD_MAX_COMMIT_LIST 10 -#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json" -#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat" +#define MGMTD_COMMIT_FILE_PATH(id) "%s/commit-%s.json", frr_libstatedir, id +#define MGMTD_COMMIT_INDEX_FILE_PATH "%s/commit-index.dat", frr_libstatedir extern struct nb_config *running_config; diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 2b2471c..ec8e773 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -8,11 +8,13 @@ */ #include +#include "darr.h" #include "sockopt.h" #include "network.h" #include "libfrr.h" #include "mgmt_fe_client.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "hash.h" #include "jhash.h" @@ -21,9 +23,9 @@ #include "mgmtd/mgmt_memory.h" #include "mgmtd/mgmt_fe_adapter.h" -#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_fe, "FE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ +#define __log_err(fmt, ...) \ zlog_err("FE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define FOREACH_ADAPTER_IN_LIST(adapter) \ @@ -76,20 +78,18 @@ mgmt_fe_session_write_lock_ds(Mgmtd__DatastoreId ds_id, session->session_id, mgmt_ds_id2name(ds_id)); else { if (mgmt_ds_lock(ds_ctx, session->session_id)) { - MGMTD_FE_ADAPTER_DBG( - "Failed to lock the DS:%s for session-id: %" PRIu64 - " from %s!", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Failed to lock the DS:%s for session-id: %" PRIu64 + " from %s!", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); return -1; } session->ds_locked[ds_id] = true; - MGMTD_FE_ADAPTER_DBG( - "Write-Locked the DS:%s for session-id: %" PRIu64 - " from %s", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Write-Locked the DS:%s for session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); } return 0; @@ -105,11 +105,10 @@ static void mgmt_fe_session_unlock_ds(Mgmtd__DatastoreId ds_id, session->ds_locked[ds_id] = false; mgmt_ds_unlock(ds_ctx); - MGMTD_FE_ADAPTER_DBG( - "Unlocked DS:%s write-locked earlier by session-id: %" PRIu64 - " from %s", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Unlocked DS:%s write-locked earlier by session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); } static void @@ -204,14 +203,13 @@ mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_adapter *adapter, FOREACH_SESSION_IN_LIST (adapter, session) { if (session->client_id == client_id) { - MGMTD_FE_ADAPTER_DBG("Found session-id %" PRIu64 - " using client-id %" PRIu64, - session->session_id, client_id); + __dbg("Found session-id %" PRIu64 + " using client-id %" PRIu64, + session->session_id, client_id); return session; } } - MGMTD_FE_ADAPTER_DBG("Session not found using client-id %" PRIu64, - client_id); + __dbg("Session not found using client-id %" PRIu64, client_id); return NULL; } @@ -246,6 +244,23 @@ mgmt_session_id2ctx(uint64_t session_id) return session; } +void mgmt_fe_adapter_toggle_client_debug(bool set) +{ + struct mgmt_fe_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) + adapter->conn->debug = set; +} + +static struct mgmt_fe_session_ctx *fe_adapter_session_by_txn_id(uint64_t txn_id) +{ + uint64_t session_id = mgmt_txn_get_session_id(txn_id); + + if (session_id == MGMTD_SESSION_ID_NONE) + return NULL; + return mgmt_session_id2ctx(session_id); +} + static struct mgmt_fe_session_ctx * mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, uint64_t client_id) @@ -273,6 +288,14 @@ mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, return session; } +static int fe_adapter_send_native_msg(struct mgmt_fe_client_adapter *adapter, + void *msg, size_t len, + bool short_circuit_ok) +{ + return msg_conn_send_msg(adapter->conn, MGMT_MSG_VERSION_NATIVE, msg, + len, NULL, short_circuit_ok); +} + static int fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter, Mgmtd__FeMessage *fe_msg, bool short_circuit_ok) { @@ -303,9 +326,8 @@ static int fe_adapter_send_session_reply(struct mgmt_fe_client_adapter *adapter, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY; fe_msg.session_reply = &session_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending SESSION_REPLY message to MGMTD Frontend client '%s'", - adapter->name); + __dbg("Sending SESSION_REPLY message to MGMTD Frontend client '%s'", + adapter->name); return fe_adapter_send_msg(adapter, &fe_msg, true); } @@ -334,9 +356,8 @@ static int fe_adapter_send_lockds_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY; fe_msg.lockds_reply = &lockds_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s' scok: %d", - session->adapter->name, scok); + __dbg("Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s' scok: %d", + session->adapter->name, scok); return fe_adapter_send_msg(session->adapter, &fe_msg, scok); } @@ -369,9 +390,8 @@ static int fe_adapter_send_set_cfg_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY; fe_msg.setcfg_reply = &setcfg_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending SETCFG_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending SETCFG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); if (implicit_commit) { if (mm->perf_stats_en) @@ -415,9 +435,8 @@ static int fe_adapter_send_commit_cfg_reply( fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY; fe_msg.commcfg_reply = &commcfg_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); /* * Cleanup the CONFIG transaction associated with this session. @@ -457,8 +476,8 @@ static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY; fe_msg.get_reply = &get_reply; - MGMTD_FE_ADAPTER_DBG("Sending GET_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending GET_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); /* * Cleanup the SHOW transaction associated with this session. @@ -470,6 +489,28 @@ static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session, return fe_adapter_send_msg(session->adapter, &fe_msg, false); } +static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) + PRINTFRR(5, 6); + +static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, errfmt); + ret = vmgmt_msg_native_send_error(session->adapter->conn, + session->session_id, req_id, + short_circuit_ok, error, errfmt, ap); + va_end(ap); + + return ret; +} + + static void mgmt_fe_session_cfg_txn_clnup(struct event *thread) { struct mgmt_fe_session_ctx *session; @@ -523,7 +564,7 @@ mgmt_fe_find_adapter_by_fd(int conn_fd) static void mgmt_fe_adapter_delete(struct mgmt_fe_client_adapter *adapter) { struct mgmt_fe_session_ctx *session; - MGMTD_FE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + __dbg("deleting client adapter '%s'", adapter->name); /* TODO: notify about client disconnect for appropriate cleanup */ FOREACH_SESSION_IN_LIST (adapter, session) @@ -538,8 +579,7 @@ static int mgmt_fe_adapter_notify_disconnect(struct msg_conn *conn) { struct mgmt_fe_client_adapter *adapter = conn->user; - MGMTD_FE_ADAPTER_DBG("notify disconnect for client adapter '%s'", - adapter->name); + __dbg("notify disconnect for client adapter '%s'", adapter->name); mgmt_fe_adapter_delete(adapter); @@ -560,10 +600,8 @@ mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter) if (strncmp(adapter->name, old->name, sizeof(adapter->name))) continue; - MGMTD_FE_ADAPTER_DBG( - "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)", - adapter->name, adapter->conn->fd, - old->conn->fd); + __dbg("Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)", + adapter->name, adapter->conn->fd, old->conn->fd); msg_conn_disconnect(old->conn, false); } } @@ -616,11 +654,10 @@ mgmt_fe_session_handle_lockds_req_msg(struct mgmt_fe_session_ctx *session, if (fe_adapter_send_lockds_reply(session, lockds_req->ds_id, lockds_req->req_id, lockds_req->lock, true, NULL) != 0) { - MGMTD_FE_ADAPTER_DBG( - "Failed to send LOCK_DS_REPLY for DS %u session-id: %" PRIu64 - " from %s", - lockds_req->ds_id, session->session_id, - session->adapter->name); + __dbg("Failed to send LOCK_DS_REPLY for DS %u session-id: %" PRIu64 + " from %s", + lockds_req->ds_id, session->session_id, + session->adapter->name); } return 0; @@ -674,9 +711,6 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, } if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { - /* as we have the lock no-one else should have a config txn */ - assert(mgmt_config_txn_in_progress() == MGMTD_SESSION_ID_NONE); - /* Start a CONFIG Transaction (if not started already) */ session->cfg_txn_id = mgmt_create_txn(session->session_id, MGMTD_TXN_TYPE_CONFIG); @@ -690,14 +724,13 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, } txn_created = true; - MGMTD_FE_ADAPTER_DBG("Created new Config txn-id: %" PRIu64 - " for session-id %" PRIu64, - session->cfg_txn_id, session->session_id); + __dbg("Created new Config txn-id: %" PRIu64 + " for session-id %" PRIu64, + session->cfg_txn_id, session->session_id); } else { - MGMTD_FE_ADAPTER_DBG("Config txn-id: %" PRIu64 - " for session-id: %" PRIu64 - " already created", - session->cfg_txn_id, session->session_id); + __dbg("Config txn-id: %" PRIu64 " for session-id: %" PRIu64 + " already created", + session->cfg_txn_id, session->session_id); if (setcfg_req->implicit_commit) { /* @@ -740,14 +773,8 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio struct nb_config *cfg_root = NULL; Mgmtd__DatastoreId ds_id = get_req->ds_id; uint64_t req_id = get_req->req_id; - bool is_cfg = get_req->config; - bool ds_ok = true; - - if (is_cfg && ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) - ds_ok = false; - else if (!is_cfg && ds_id != MGMTD_DS_OPERATIONAL) - ds_ok = false; - if (!ds_ok) { + + if (ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) { fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, "get-req on unsupported datastore"); return 0; @@ -768,23 +795,22 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio return -1; } - MGMTD_FE_ADAPTER_DBG("Created new show txn-id: %" PRIu64 - " for session-id: %" PRIu64, - session->txn_id, session->session_id); + __dbg("Created new show txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); } else { fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, "Request processing for GET failed!"); - MGMTD_FE_ADAPTER_DBG("Transaction in progress txn-id: %" PRIu64 - " for session-id: %" PRIu64, - session->txn_id, session->session_id); + __dbg("Transaction in progress txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); return -1; } /* * Get a copy of the datastore config root, avoids locking. */ - if (is_cfg) - cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx)); + cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx)); /* * Create a GET request under the transaction. @@ -848,6 +874,9 @@ static int mgmt_fe_session_handle_commit_config_req_msg( } if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { + /* as we have the lock no-one else should have a config txn */ + assert(!mgmt_config_txn_in_progress()); + /* * Start a CONFIG Transaction (if not started already) */ @@ -861,10 +890,9 @@ static int mgmt_fe_session_handle_commit_config_req_msg( "Failed to create a Configuration session!"); return 0; } - MGMTD_FE_ADAPTER_DBG("Created txn-id: %" PRIu64 - " for session-id %" PRIu64 - " for COMMIT-CFG-REQ", - session->cfg_txn_id, session->session_id); + __dbg("Created txn-id: %" PRIu64 " for session-id %" PRIu64 + " for COMMIT-CFG-REQ", + session->cfg_txn_id, session->session_id); } /* @@ -898,8 +926,8 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, */ switch ((int)fe_msg->message_case) { case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ: - MGMTD_FE_ADAPTER_DBG("Got REGISTER_REQ from '%s'", - fe_msg->register_req->client_name); + __dbg("Got REGISTER_REQ from '%s'", + fe_msg->register_req->client_name); if (strlen(fe_msg->register_req->client_name)) { strlcpy(adapter->name, @@ -912,11 +940,10 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, if (fe_msg->session_req->create && fe_msg->session_req->id_case == MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID) { - MGMTD_FE_ADAPTER_DBG( - "Got SESSION_REQ (create) for client-id %" PRIu64 - " from '%s'", - fe_msg->session_req->client_conn_id, - adapter->name); + __dbg("Got SESSION_REQ (create) for client-id %" PRIu64 + " from '%s'", + fe_msg->session_req->client_conn_id, + adapter->name); session = mgmt_fe_create_session( adapter, fe_msg->session_req->client_conn_id); @@ -926,10 +953,9 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, !fe_msg->session_req->create && fe_msg->session_req->id_case == MGMTD__FE_SESSION_REQ__ID_SESSION_ID) { - MGMTD_FE_ADAPTER_DBG( - "Got SESSION_REQ (destroy) for session-id %" PRIu64 - "from '%s'", - fe_msg->session_req->session_id, adapter->name); + __dbg("Got SESSION_REQ (destroy) for session-id %" PRIu64 + "from '%s'", + fe_msg->session_req->session_id, adapter->name); session = mgmt_session_id2ctx( fe_msg->session_req->session_id); @@ -941,12 +967,11 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ: session = mgmt_session_id2ctx( fe_msg->lockds_req->session_id); - MGMTD_FE_ADAPTER_DBG( - "Got LOCKDS_REQ (%sLOCK) for DS:%s for session-id %" PRIu64 - " from '%s'", - fe_msg->lockds_req->lock ? "" : "UN", - mgmt_ds_id2name(fe_msg->lockds_req->ds_id), - fe_msg->lockds_req->session_id, adapter->name); + __dbg("Got LOCKDS_REQ (%sLOCK) for DS:%s for session-id %" PRIu64 + " from '%s'", + fe_msg->lockds_req->lock ? "" : "UN", + mgmt_ds_id2name(fe_msg->lockds_req->ds_id), + fe_msg->lockds_req->session_id, adapter->name); mgmt_fe_session_handle_lockds_req_msg( session, fe_msg->lockds_req); break; @@ -954,13 +979,12 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, session = mgmt_session_id2ctx( fe_msg->setcfg_req->session_id); session->adapter->setcfg_stats.set_cfg_count++; - MGMTD_FE_ADAPTER_DBG( - "Got SETCFG_REQ (%d Xpaths, Implicit:%c) on DS:%s for session-id %" PRIu64 - " from '%s'", - (int)fe_msg->setcfg_req->n_data, - fe_msg->setcfg_req->implicit_commit ? 'T' : 'F', - mgmt_ds_id2name(fe_msg->setcfg_req->ds_id), - fe_msg->setcfg_req->session_id, adapter->name); + __dbg("Got SETCFG_REQ (%d Xpaths, Implicit:%c) on DS:%s for session-id %" PRIu64 + " from '%s'", + (int)fe_msg->setcfg_req->n_data, + fe_msg->setcfg_req->implicit_commit ? 'T' : 'F', + mgmt_ds_id2name(fe_msg->setcfg_req->ds_id), + fe_msg->setcfg_req->session_id, adapter->name); mgmt_fe_session_handle_setcfg_req_msg( session, fe_msg->setcfg_req); @@ -968,31 +992,28 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ: session = mgmt_session_id2ctx( fe_msg->commcfg_req->session_id); - MGMTD_FE_ADAPTER_DBG( - "Got COMMCFG_REQ for src-DS:%s dst-DS:%s (Abort:%c) on session-id %" PRIu64 - " from '%s'", - mgmt_ds_id2name(fe_msg->commcfg_req->src_ds_id), - mgmt_ds_id2name(fe_msg->commcfg_req->dst_ds_id), - fe_msg->commcfg_req->abort ? 'T' : 'F', - fe_msg->commcfg_req->session_id, adapter->name); + __dbg("Got COMMCFG_REQ for src-DS:%s dst-DS:%s (Abort:%c) on session-id %" PRIu64 + " from '%s'", + mgmt_ds_id2name(fe_msg->commcfg_req->src_ds_id), + mgmt_ds_id2name(fe_msg->commcfg_req->dst_ds_id), + fe_msg->commcfg_req->abort ? 'T' : 'F', + fe_msg->commcfg_req->session_id, adapter->name); mgmt_fe_session_handle_commit_config_req_msg( session, fe_msg->commcfg_req); break; case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ: session = mgmt_session_id2ctx(fe_msg->get_req->session_id); - MGMTD_FE_ADAPTER_DBG("Got GET_REQ (iscfg %d) for DS:%s (xpaths: %d) on session-id %" PRIu64 - " from '%s'", - (int)fe_msg->get_req->config, - mgmt_ds_id2name(fe_msg->get_req->ds_id), - (int)fe_msg->get_req->n_data, - fe_msg->get_req->session_id, adapter->name); + __dbg("Got GET_REQ for DS:%s (xpaths: %d) on session-id %" PRIu64 + " from '%s'", + mgmt_ds_id2name(fe_msg->get_req->ds_id), + (int)fe_msg->get_req->n_data, fe_msg->get_req->session_id, + adapter->name); mgmt_fe_session_handle_get_req_msg(session, fe_msg->get_req); break; case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ: case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ: - MGMTD_FE_ADAPTER_ERR( - "Got unhandled message of type %u from '%s'", - fe_msg->message_case, adapter->name); + __log_err("Got unhandled message of type %u from '%s'", + fe_msg->message_case, adapter->name); /* * TODO: Add handling code in future. */ @@ -1020,25 +1041,265 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, return 0; } +/** + * Send result of get-tree request back to the FE client. + * + * Args: + * session: the session. + * req_id: the request ID. + * short_circuit_ok: if allowed to short circuit the message. + * result_format: LYD_FORMAT for the sent output. + * tree: the tree to send, can be NULL which will send an empty tree. + * partial_error: if an error occurred during gathering results. + * + * Return: + * Any error that occurs -- the message is likely not sent if non-zero. + */ +static int fe_adapter_send_tree_data(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + uint8_t result_type, uint32_t wd_options, + const struct lyd_node *tree, + int partial_error) + +{ + struct mgmt_msg_tree_data *msg; + uint8_t **darrp = NULL; + int ret = 0; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0, + MTYPE_MSG_NATIVE_TREE_DATA); + msg->refer_id = session->session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_TREE_DATA; + msg->partial_error = partial_error; + msg->result_type = result_type; + + darrp = mgmt_msg_native_get_darrp(msg); + ret = yang_print_tree_append(darrp, tree, result_type, + (wd_options | LYD_PRINT_WITHSIBLINGS)); + if (ret != LY_SUCCESS) { + __log_err("Error building get-tree result for client %s session-id %" PRIu64 + " req-id %" PRIu64 " scok %d result type %u", + session->adapter->name, session->session_id, req_id, + short_circuit_ok, result_type); + goto done; + } + + __dbg("Sending get-tree result from adapter %s to session-id %" PRIu64 + " req-id %" PRIu64 " scok %d result type %u len %u", + session->adapter->name, session->session_id, req_id, + short_circuit_ok, result_type, mgmt_msg_native_get_msg_len(msg)); + + ret = fe_adapter_send_native_msg(session->adapter, msg, + mgmt_msg_native_get_msg_len(msg), + short_circuit_ok); +done: + mgmt_msg_native_free_msg(msg); + + return ret; +} + +/** + * fe_adapter_handle_get_data() - Handle a get-tree message from a FE client. + * @session: the client session. + * @msg_raw: the message data. + * @msg_len: the length of the message data. + */ +static void fe_adapter_handle_get_data(struct mgmt_fe_session_ctx *session, + void *__msg, size_t msg_len) +{ + struct mgmt_msg_get_data *msg = __msg; + struct lysc_node **snodes = NULL; + char *xpath_resolved = NULL; + uint64_t req_id = msg->req_id; + Mgmtd__DatastoreId ds_id; + uint64_t clients; + uint32_t wd_options; + bool simple_xpath; + LY_ERR err; + int ret; + + __dbg("Received get-data request from client %s for session-id %" PRIu64 + " req-id %" PRIu64, + session->adapter->name, session->session_id, msg->req_id); + + if (!MGMT_MSG_VALIDATE_NUL_TERM(msg, msg_len)) { + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Invalid message rcvd from session-id: %" PRIu64, + session->session_id); + goto done; + } + + if (session->txn_id != MGMTD_TXN_ID_NONE) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "Transaction in progress txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); + goto done; + } + + switch (msg->defaults) { + case GET_DATA_DEFAULTS_EXPLICIT: + wd_options = LYD_PRINT_WD_EXPLICIT; + break; + case GET_DATA_DEFAULTS_TRIM: + wd_options = LYD_PRINT_WD_TRIM; + break; + case GET_DATA_DEFAULTS_ALL: + wd_options = LYD_PRINT_WD_ALL; + break; + case GET_DATA_DEFAULTS_ALL_ADD_TAG: + wd_options = LYD_PRINT_WD_IMPL_TAG; + break; + default: + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Invalid defaults value %u for session-id: %" PRIu64, + msg->defaults, session->session_id); + goto done; + } + + switch (msg->datastore) { + case MGMT_MSG_DATASTORE_CANDIDATE: + ds_id = MGMTD_DS_CANDIDATE; + break; + case MGMT_MSG_DATASTORE_RUNNING: + ds_id = MGMTD_DS_RUNNING; + break; + case MGMT_MSG_DATASTORE_OPERATIONAL: + ds_id = MGMTD_DS_OPERATIONAL; + break; + default: + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Unsupported datastore %" PRIu8 + " requested from session-id: %" PRIu64, + msg->datastore, session->session_id); + goto done; + } + + err = yang_resolve_snode_xpath(ly_native_ctx, msg->xpath, &snodes, + &simple_xpath); + if (err) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "XPath doesn't resolve for session-id: %" PRIu64, + session->session_id); + goto done; + } + darr_free(snodes); + + clients = mgmt_be_interested_clients(msg->xpath, false); + if (!clients && !CHECK_FLAG(msg->flags, GET_DATA_FLAG_CONFIG)) { + __dbg("No backends provide xpath: %s for txn-id: %" PRIu64 + " session-id: %" PRIu64, + msg->xpath, session->txn_id, session->session_id); + + fe_adapter_send_tree_data(session, req_id, false, + msg->result_type, wd_options, NULL, 0); + goto done; + } + + /* Start a SHOW Transaction */ + session->txn_id = mgmt_create_txn(session->session_id, + MGMTD_TXN_TYPE_SHOW); + if (session->txn_id == MGMTD_SESSION_ID_NONE) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "failed to create a 'show' txn"); + goto done; + } + + __dbg("Created new show txn-id: %" PRIu64 " for session-id: %" PRIu64, + session->txn_id, session->session_id); + + /* Create a GET-TREE request under the transaction */ + ret = mgmt_txn_send_get_tree_oper(session->txn_id, req_id, clients, + ds_id, msg->result_type, msg->flags, + wd_options, simple_xpath, msg->xpath); + if (ret) { + /* destroy the just created txn */ + mgmt_destroy_txn(&session->txn_id); + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "failed to create a 'show' txn"); + } +done: + darr_free(snodes); + darr_free(xpath_resolved); +} + +/** + * Handle a native encoded message from the FE client. + */ +static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(msg->refer_id); + if (!session) { + __log_err("adapter %s: recv msg unknown session-id %" PRIu64, + adapter->name, msg->refer_id); + return; + } + assert(session->adapter == adapter); + + switch (msg->code) { + case MGMT_MSG_CODE_GET_DATA: + fe_adapter_handle_get_data(session, msg, msg_len); + break; + default: + __log_err("unknown native message session-id %" PRIu64 + " req-id %" PRIu64 " code %u to FE adapter %s", + msg->refer_id, msg->req_id, msg->code, adapter->name); + break; + } +} + + static void mgmt_fe_adapter_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { struct mgmt_fe_client_adapter *adapter = conn->user; - Mgmtd__FeMessage *fe_msg = mgmtd__fe_message__unpack(NULL, len, data); + Mgmtd__FeMessage *fe_msg; + + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + if (len >= sizeof(*msg)) + fe_adapter_handle_native_msg(adapter, msg, len); + else + __log_err("native message to adapter %s too short %zu", + adapter->name, len); + return; + } + + fe_msg = mgmtd__fe_message__unpack(NULL, len, data); if (!fe_msg) { - MGMTD_FE_ADAPTER_DBG( - "Failed to decode %zu bytes for adapter: %s", len, - adapter->name); + __dbg("Failed to decode %zu bytes for adapter: %s", len, + adapter->name); return; } - MGMTD_FE_ADAPTER_DBG( - "Decoded %zu bytes of message: %u from adapter: %s", len, - fe_msg->message_case, adapter->name); + __dbg("Decoded %zu bytes of message: %u from adapter: %s", len, + fe_msg->message_case, adapter->name); (void)mgmt_fe_adapter_handle_msg(adapter, fe_msg); mgmtd__fe_message__free_unpacked(fe_msg, NULL); } +void mgmt_fe_adapter_send_notify(struct mgmt_msg_notify_data *msg, size_t msglen) +{ + struct mgmt_fe_client_adapter *adapter; + struct mgmt_fe_session_ctx *session; + + assert(msg->refer_id == 0); + + FOREACH_ADAPTER_IN_LIST (adapter) { + FOREACH_SESSION_IN_LIST (adapter, session) { + msg->refer_id = session->session_id; + (void)fe_adapter_send_native_msg(adapter, msg, msglen, + false); + } + } + msg->refer_id = 0; +} + void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter) { adapter->refcount++; @@ -1062,6 +1323,8 @@ extern void mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) */ void mgmt_fe_adapter_init(struct event_loop *tm) { + char server_path[MAXPATHLEN]; + assert(!mgmt_loop); mgmt_loop = tm; @@ -1072,9 +1335,10 @@ void mgmt_fe_adapter_init(struct event_loop *tm) hash_create(mgmt_fe_session_hash_key, mgmt_fe_session_hash_cmp, "MGMT Frontend Sessions"); - if (msg_server_init(&mgmt_fe_server, MGMTD_FE_SERVER_PATH, tm, - mgmt_fe_create_adapter, "frontend", - &mgmt_debug_fe)) { + snprintf(server_path, sizeof(server_path), MGMTD_FE_SOCK_NAME); + + if (msg_server_init(&mgmt_fe_server, server_path, tm, + mgmt_fe_create_adapter, "frontend", &mgmt_debug_fe)) { zlog_err("cannot initialize frontend server"); exit(1); } @@ -1084,11 +1348,10 @@ static void mgmt_fe_abort_if_session(void *data) { struct mgmt_fe_session_ctx *session = data; - MGMTD_FE_ADAPTER_ERR("found orphaned session id %" PRIu64 - " client id %" PRIu64 " adapter %s", - session->session_id, session->client_id, - session->adapter ? session->adapter->name - : "NULL"); + __log_err("found orphaned session id %" PRIu64 " client id %" PRIu64 + " adapter %s", + session->session_id, session->client_id, + session->adapter ? session->adapter->name : "NULL"); abort(); } @@ -1129,13 +1392,15 @@ struct msg_conn *mgmt_fe_create_adapter(int conn_fd, union sockunion *from) adapter->conn = msg_server_conn_create( mgmt_loop, conn_fd, mgmt_fe_adapter_notify_disconnect, mgmt_fe_adapter_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC, - MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, + MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MAX_MSG_LEN, adapter, "FE-adapter"); + adapter->conn->debug = DEBUG_MODE_CHECK(&mgmt_debug_fe, + DEBUG_MODE_ALL); + adapter->setcfg_stats.min_tm = ULONG_MAX; adapter->cmt_stats.min_tm = ULONG_MAX; - MGMTD_FE_ADAPTER_DBG("Added new MGMTD Frontend adapter '%s'", - adapter->name); + __dbg("Added new MGMTD Frontend adapter '%s'", adapter->name); } return adapter->conn; } @@ -1151,10 +1416,9 @@ int mgmt_fe_send_set_cfg_reply(uint64_t session_id, uint64_t txn_id, session = mgmt_session_id2ctx(session_id); if (!session || session->cfg_txn_id != txn_id) { if (session) - MGMTD_FE_ADAPTER_ERR( - "txn-id doesn't match, session txn-id is %" PRIu64 - " current txnid: %" PRIu64, - session->cfg_txn_id, txn_id); + __log_err("txn-id doesn't match, session txn-id is %" PRIu64 + " current txnid: %" PRIu64, + session->cfg_txn_id, txn_id); return -1; } @@ -1198,8 +1462,56 @@ int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, error_if_any); } -struct mgmt_setcfg_stats * -mgmt_fe_get_session_setcfg_stats(uint64_t session_id) +int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id, + uint64_t req_id, LYD_FORMAT result_type, + uint32_t wd_options, + const struct lyd_node *tree, + int partial_error, bool short_circuit_ok) +{ + struct mgmt_fe_session_ctx *session; + int ret; + + session = mgmt_session_id2ctx(session_id); + if (!session || session->txn_id != txn_id) + return -1; + + ret = fe_adapter_send_tree_data(session, req_id, short_circuit_ok, + result_type, wd_options, tree, + partial_error); + + mgmt_destroy_txn(&session->txn_id); + + return ret; +} + +/** + * Send an error back to the FE client and cleanup any in-progress txn. + */ +int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id, + bool short_circuit_ok, int16_t error, + const char *errstr) +{ + struct mgmt_fe_session_ctx *session; + int ret; + + session = fe_adapter_session_by_txn_id(txn_id); + if (!session) { + __log_err("failed sending error for txn-id %" PRIu64 + " session not found", + txn_id); + return -ENOENT; + } + + + ret = fe_adapter_send_error(session, req_id, false, error, "%s", errstr); + + mgmt_destroy_txn(&session->txn_id); + + return ret; +} + + +struct mgmt_setcfg_stats *mgmt_fe_get_session_setcfg_stats(uint64_t session_id) { struct mgmt_fe_session_ctx *session; diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h index d2991ec..2150f86 100644 --- a/mgmtd/mgmt_fe_adapter.h +++ b/mgmtd/mgmt_fe_adapter.h @@ -12,7 +12,7 @@ #include "mgmt_fe_client.h" #include "mgmt_msg.h" -#include "mgmtd/mgmt_defines.h" +#include "mgmt_defines.h" struct mgmt_fe_client_adapter; struct mgmt_master; @@ -138,6 +138,52 @@ extern int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, Mgmtd__YangDataReply *data_resp, const char *error_if_any); +/** + * Send get-tree data reply back to client. + * + * This also cleans up and frees the transaction. + * + * Args: + * session_id: the session. + * txn_id: the txn_id this data pertains to + * req_id: the req id for the get_tree message + * result_type: the format of the result data. + * wd_options: with-defaults options. + * tree: the results. + * partial_error: if there were errors while gather results. + * short_circuit_ok: True if OK to short-circuit the call. + * + * Return: + * the return value from the underlying send function. + * + */ +extern int +mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id, + uint64_t req_id, LYD_FORMAT result_type, + uint32_t wd_options, const struct lyd_node *tree, + int partial_error, bool short_circuit_ok); + +/** + * Send an error back to the FE client using native messaging. + * + * This also cleans up and frees the transaction. + * + * Args: + * txn_id: the txn_id this error pertains to. + * short_circuit_ok: True if OK to short-circuit the call. + * error: An integer error value. + * errfmt: An error format string (i.e., printfrr) + * ...: args for use by the `errfmt` format string. + * + * Return: + * the return value from the underlying send function. + * + */ +extern int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id, + bool short_circuit_ok, int16_t error, + const char *errstr); + + /* Fetch frontend client session set-config stats */ extern struct mgmt_setcfg_stats * mgmt_fe_get_session_setcfg_stats(uint64_t session_id); @@ -149,4 +195,8 @@ mgmt_fe_get_session_commit_stats(uint64_t session_id); extern void mgmt_fe_adapter_status_write(struct vty *vty, bool detail); extern void mgmt_fe_adapter_perf_measurement(struct vty *vty, bool config); extern void mgmt_fe_adapter_reset_perf_stats(struct vty *vty); + +/* Toggle debug on or off for connected clients. */ +extern void mgmt_fe_adapter_toggle_client_debug(bool set); + #endif /* _FRR_MGMTD_FE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c index d406932..c97cb7f 100644 --- a/mgmtd/mgmt_history.c +++ b/mgmtd/mgmt_history.c @@ -63,7 +63,7 @@ static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void) mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str)); mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str)); snprintf(new->cmt_json_file, sizeof(new->cmt_json_file), - MGMTD_COMMIT_FILE_PATH, new->cmtid_str); + MGMTD_COMMIT_FILE_PATH(new->cmtid_str)); return new; } @@ -104,18 +104,21 @@ mgmt_history_find_cmt_record(const char *cmtid_str) static bool mgmt_history_read_cmt_record_index(void) { + char index_path[MAXPATHLEN]; FILE *fp; struct mgmt_cmt_info_t cmt_info; struct mgmt_cmt_info_t *new; int cnt = 0; - if (!file_exists(MGMTD_COMMIT_FILE_PATH)) - return false; + snprintf(index_path, sizeof(index_path), MGMTD_COMMIT_INDEX_FILE_PATH); - fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "rb"); + fp = fopen(index_path, "rb"); if (!fp) { - zlog_err("Failed to open commit history %s for reading: %s", - MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + if (errno == ENOENT || errno == ENOTDIR) + return false; + + zlog_err("Failed to open commit history %pSQq for reading: %m", + index_path); return false; } @@ -132,9 +135,8 @@ static bool mgmt_history_read_cmt_record_index(void) memcpy(new, &cmt_info, sizeof(struct mgmt_cmt_info_t)); mgmt_cmt_infos_add_tail(&mm->cmts, new); } else { - zlog_warn( - "More records found in commit history file %s than expected", - MGMTD_COMMIT_INDEX_FILE_NAME); + zlog_warn("More records found in commit history file %pSQq than expected", + index_path); fclose(fp); return false; } @@ -148,16 +150,19 @@ static bool mgmt_history_read_cmt_record_index(void) static bool mgmt_history_dump_cmt_record_index(void) { + char index_path[MAXPATHLEN]; FILE *fp; int ret = 0; struct mgmt_cmt_info_t *cmt_info; struct mgmt_cmt_info_t cmt_info_set[10]; int cnt = 0; - fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "wb"); + snprintf(index_path, sizeof(index_path), MGMTD_COMMIT_INDEX_FILE_PATH); + + fp = fopen(index_path, "wb"); if (!fp) { - zlog_err("Failed to open commit history %s for writing: %s", - MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + zlog_err("Failed to open commit history %pSQq for writing: %m", + index_path); return false; } @@ -176,7 +181,7 @@ static bool mgmt_history_dump_cmt_record_index(void) fclose(fp); if (ret != cnt) { zlog_err("Failed to write full commit history, removing file"); - remove_file(MGMTD_COMMIT_INDEX_FILE_NAME); + remove_file(index_path); return false; } return true; @@ -261,7 +266,9 @@ failed_unlock: void mgmt_history_rollback_complete(bool success) { - vty_mgmt_resume_response(rollback_vty, success); + vty_mgmt_resume_response(rollback_vty, + success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); rollback_vty = NULL; } diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c index 39362fa..cce16f5 100644 --- a/mgmtd/mgmt_main.c +++ b/mgmtd/mgmt_main.c @@ -14,18 +14,21 @@ #include "frr_pthread.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_ds.h" +#include "ripd/rip_nb.h" +#include "ripngd/ripng_nb.h" #include "routing_nb.h" - +#include "affinitymap.h" +#include "zebra/zebra_cli.h" /* mgmt options, we use GNU getopt library. */ static const struct option longopts[] = { {"skip_runas", no_argument, NULL, 'S'}, {"no_zebra", no_argument, NULL, 'Z'}, {"socket_size", required_argument, NULL, 's'}, + {"vrfwnetns", no_argument, NULL, 'n'}, {0}}; static void mgmt_exit(int); -static void mgmt_vrf_terminate(void); /* privileges */ static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW, @@ -45,7 +48,6 @@ struct zebra_privs_t mgmt_privs = { }; static struct frr_daemon_info mgmtd_di; -char backup_config_file[256]; /* SIGHUP handler. */ static void sighup(void) @@ -114,8 +116,6 @@ static __attribute__((__noreturn__)) void mgmt_exit(int status) /* stop pthreads (if any) */ frr_pthread_stop_all(); - mgmt_vrf_terminate(); - frr_fini(); exit(status); } @@ -139,87 +139,81 @@ static struct frr_signal_t mgmt_signals[] = { }, }; -static int mgmt_vrf_new(struct vrf *vrf) -{ - zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_delete(struct vrf *vrf) -{ - zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_enable(struct vrf *vrf) -{ - zlog_debug("VRF Enable: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_disable(struct vrf *vrf) -{ - zlog_debug("VRF Disable: %s(%u)", vrf->name, vrf->vrf_id); - - /* Note: This is a callback, the VRF will be deleted by the caller. */ - return 0; -} - -static int mgmt_vrf_config_write(struct vty *vty) -{ - return 0; -} +#ifdef HAVE_STATICD +extern const struct frr_yang_module_info frr_staticd_cli_info; +#endif -static void mgmt_vrf_init(void) -{ - vrf_init(mgmt_vrf_new, mgmt_vrf_enable, mgmt_vrf_disable, - mgmt_vrf_delete); - vrf_cmd_init(mgmt_vrf_config_write); -} +/* + * These are modules that are only needed by mgmtd and hence not included into + * the lib and backend daemons. + */ +const struct frr_yang_module_info ietf_netconf_with_defaults_info = { + .name = "ietf-netconf-with-defaults", + .ignore_cfg_cbs = true, + .nodes = { { .xpath = NULL } }, +}; -static void mgmt_vrf_terminate(void) -{ - vrf_terminate(); -} +/* + * These are stub info structs that are used to load the modules used by backend + * clients into mgmtd. The modules are used by libyang in order to support + * parsing binary data returns from the backend. + */ +const struct frr_yang_module_info zebra_route_map_info = { + .name = "frr-zebra-route-map", + .ignore_cfg_cbs = true, + .nodes = { { .xpath = NULL } }, +}; /* * List of YANG modules to be loaded in the process context of * MGMTd. - * - * NOTE: In future this will also include the YANG modules of - * all individual Backend clients. */ static const struct frr_yang_module_info *const mgmt_yang_modules[] = { - &frr_filter_info, - &frr_interface_info, - &frr_route_map_info, - &frr_routing_info, - &frr_vrf_info, -/* - * YANG module info supported by backend clients get added here. - * NOTE: Always set .ignore_cbs true for to avoid validating - * backend northbound callbacks during loading. - */ + &frr_filter_cli_info, + &frr_interface_cli_info, + &frr_route_map_cli_info, + &frr_routing_cli_info, + &frr_vrf_cli_info, + &frr_affinity_map_cli_info, + + /* mgmtd-only modules */ + &ietf_netconf_with_defaults_info, + + /* + * YANG module info used by backend clients get added here. + */ + + &frr_zebra_cli_info, + &zebra_route_map_info, + +#ifdef HAVE_RIPD + &frr_ripd_cli_info, +#endif +#ifdef HAVE_RIPNGD + &frr_ripngd_cli_info, +#endif #ifdef HAVE_STATICD - &(struct frr_yang_module_info){.name = "frr-staticd", - .ignore_cbs = true}, + &frr_staticd_cli_info, #endif }; -FRR_DAEMON_INFO(mgmtd, MGMTD, .vty_port = MGMTD_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(mgmtd, MGMTD, + .vty_port = MGMTD_VTY_PORT, + .proghelp = "FRR Management Daemon.", - .proghelp = "FRR Management Daemon.", + .signals = mgmt_signals, + .n_signals = array_size(mgmt_signals), - .signals = mgmt_signals, .n_signals = array_size(mgmt_signals), + .privs = &mgmt_privs, - .privs = &mgmt_privs, .yang_modules = mgmt_yang_modules, - .n_yang_modules = array_size(mgmt_yang_modules), + .yang_modules = mgmt_yang_modules, + .n_yang_modules = array_size(mgmt_yang_modules), - /* avoid libfrr trying to read our config file for us */ - .flags = FRR_MANUAL_VTY_START); + /* avoid libfrr trying to read our config file for us */ + .flags = FRR_MANUAL_VTY_START | FRR_NO_SPLIT_CONFIG, +); +/* clang-format on */ #define DEPRECATED_OPTIONS "" @@ -235,8 +229,9 @@ int main(int argc, char **argv) frr_preinit(&mgmtd_di, argc, argv); frr_opt_add( - "s:" DEPRECATED_OPTIONS, longopts, - " -s, --socket_size Set MGMTD peer socket send buffer size\n"); + "s:n" DEPRECATED_OPTIONS, longopts, + " -s, --socket_size Set MGMTD peer socket send buffer size\n" + " -n, --vrfwnetns Use NetNS as VRF backend\n"); /* Command line argument treatment. */ while (1) { @@ -258,6 +253,9 @@ int main(int argc, char **argv) case 's': buffer_size = atoi(optarg); break; + case 'n': + vrf_configure_backend(VRF_BACKEND_NETNS); + break; default: frr_help_exit(1); break; @@ -267,17 +265,15 @@ int main(int argc, char **argv) /* MGMTD master init. */ mgmt_master_init(frr_init(), buffer_size); - /* VRF Initializations. */ - mgmt_vrf_init(); + /* VRF commands initialization. */ + vrf_cmd_init(NULL); + + /* Interface commands initialization. */ + if_cmd_init(NULL); /* MGMTD related initialization. */ mgmt_init(); - snprintf(backup_config_file, sizeof(backup_config_file), - "%s/zebra.conf", frr_sysconfdir); - mgmtd_di.backup_config_file = backup_config_file; - - /* this will queue a read configs event */ frr_config_fork(); frr_run(mm->master); diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c index b2a0f0e..0fce61a 100644 --- a/mgmtd/mgmt_memory.c +++ b/mgmtd/mgmt_memory.c @@ -29,5 +29,6 @@ DEFINE_MTYPE(MGMTD, MGMTD_TXN_SETCFG_REQ, "txn set-config requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ, "txn commit-config requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETTREE_REQ, "txn get-tree requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches"); DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info"); diff --git a/mgmtd/mgmt_memory.h b/mgmtd/mgmt_memory.h index 06518e3..d5b6aa6 100644 --- a/mgmtd/mgmt_memory.h +++ b/mgmtd/mgmt_memory.h @@ -23,6 +23,7 @@ DECLARE_MTYPE(MGMTD_TXN_SETCFG_REQ); DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ); DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ); DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY); +DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ); DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH); DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF); DECLARE_MTYPE(MGMTD_CMT_INFO); diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c new file mode 100644 index 0000000..7e3ded8 --- /dev/null +++ b/mgmtd/mgmt_testc.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * January 29 2024, Christian Hopps + * + * Copyright (c) 2024, LabN Consulting, L.L.C. + * + */ + +#include +#include +#include "darr.h" +#include "libfrr.h" +#include "mgmt_be_client.h" +#include "northbound.h" + +/* ---------------- */ +/* Local Prototypes */ +/* ---------------- */ + +static void async_notification(struct nb_cb_notify_args *args); + +static void sigusr1(void); +static void sigint(void); + +/* ----------- */ +/* Global Data */ +/* ----------- */ + +/* privileges */ +static zebra_capabilities_t _caps_p[] = {}; + +struct zebra_privs_t __privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, +}; + +#define OPTION_LISTEN 2000 +#define OPTION_NOTIF_COUNT 2001 +#define OPTION_TIMEOUT 2002 +const struct option longopts[] = { + { "listen", no_argument, NULL, OPTION_LISTEN }, + { "notif-count", required_argument, NULL, OPTION_NOTIF_COUNT }, + { "timeout", required_argument, NULL, OPTION_TIMEOUT }, + { 0 } +}; + + +/* Master of threads. */ +struct event_loop *master; + +struct mgmt_be_client *mgmt_be_client; + +static struct frr_daemon_info mgmtd_testc_di; + +struct frr_signal_t __signals[] = { + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +#define MGMTD_TESTC_VTY_PORT 2624 + +/* clang-format off */ +static const struct frr_yang_module_info frr_ripd_info = { + .name = "frr-ripd", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-ripd:authentication-failure", + .cbs.notify = async_notification, + }, + { + .xpath = NULL, + } + } +}; + +static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_ripd_info, +}; + +FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC, + .proghelp = "FRR Management Daemon Test Client.", + + .signals = __signals, + .n_signals = array_size(__signals), + + .privs = &__privs, + + .yang_modules = mgmt_yang_modules, + .n_yang_modules = array_size(mgmt_yang_modules), + + /* avoid libfrr trying to read our config file for us */ + .flags = FRR_MANUAL_VTY_START, + ); +/* clang-format on */ + +const char **__notif_xpaths; + +struct mgmt_be_client_cbs __client_cbs = {}; +struct event *event_timeout; + +int o_notif_count = 1; +int o_timeout; + +/* --------- */ +/* Functions */ +/* --------- */ + + +static void sigusr1(void) +{ + zlog_rotate(); +} + +static void quit(int exit_code) +{ + EVENT_OFF(event_timeout); + frr_fini(); + darr_free(__client_cbs.notif_xpaths); + exit(exit_code); +} + +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + quit(0); +} + +static void timeout(struct event *event) +{ + zlog_notice("Timeout, exiting"); + quit(1); +} + +static void async_notification(struct nb_cb_notify_args *args) +{ + zlog_notice("Received YANG notification"); + + printf("{\"frr-ripd:authentication-failure\": {\"interface-name\": \"%s\"}}\n", + yang_dnode_get_string(args->dnode, "interface-name")); + + if (o_notif_count && !--o_notif_count) + quit(0); +} + +int main(int argc, char **argv) +{ + int f_listen = 0; + int i; + + frr_preinit(&mgmtd_testc_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case OPTION_LISTEN: + f_listen = 1; + break; + case OPTION_NOTIF_COUNT: + o_notif_count = atoi(optarg); + break; + case OPTION_TIMEOUT: + o_timeout = atoi(optarg); + break; + case 0: + break; + default: + frr_help_exit(1); + } + } + + master = frr_init(); + + /* + * Setup notification listen + */ + argv += optind; + argc -= optind; + if (!argc && f_listen) { + fprintf(stderr, + "Must specify at least one notification xpath to listen to\n"); + exit(1); + } + if (argc && f_listen) { + for (i = 0; i < argc; i++) { + zlog_notice("Listen on xpath: %s", argv[i]); + darr_push(__notif_xpaths, argv[i]); + } + __client_cbs.notif_xpaths = __notif_xpaths; + __client_cbs.nnotif_xpaths = darr_len(__notif_xpaths); + } + + mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0, + master); + + frr_config_fork(); + + if (o_timeout) + event_add_timer(master, timeout, NULL, o_timeout, &event_timeout); + + frr_run(master); + + /* Reached. */ + return 0; +} diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c index 452f9c8..c1cb33f 100644 --- a/mgmtd/mgmt_txn.c +++ b/mgmtd/mgmt_txn.c @@ -7,17 +7,19 @@ */ #include +#include "darr.h" #include "hash.h" #include "jhash.h" #include "libfrr.h" +#include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_memory.h" #include "mgmtd/mgmt_txn.h" -#define MGMTD_TXN_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_txn, "TXN: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_TXN_ERR(fmt, ...) \ - zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define __log_err(fmt, ...) zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__) #define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__) @@ -26,9 +28,9 @@ enum mgmt_txn_event { MGMTD_TXN_PROC_SETCFG = 1, MGMTD_TXN_PROC_COMMITCFG, MGMTD_TXN_PROC_GETCFG, - MGMTD_TXN_PROC_GETDATA, + MGMTD_TXN_PROC_GETTREE, MGMTD_TXN_COMMITCFG_TIMEOUT, - MGMTD_TXN_CLEANUP + MGMTD_TXN_GETTREE_TIMEOUT, }; PREDECL_LIST(mgmt_txn_reqs); @@ -77,17 +79,14 @@ PREDECL_LIST(mgmt_txn_batches); struct mgmt_txn_be_cfg_batch { struct mgmt_txn_ctx *txn; - uint64_t batch_id; enum mgmt_be_client_id be_id; struct mgmt_be_client_adapter *be_adapter; - uint xp_subscr[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangCfgDataReq cfg_data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangCfgDataReq *cfg_datap[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangData data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangDataValue value[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; size_t num_cfg_data; int buf_space_left; - enum mgmt_commit_phase comm_phase; struct mgmt_txn_batches_item list_linkage; }; @@ -106,10 +105,12 @@ struct mgmt_commit_cfg_req { uint8_t abort : 1; uint8_t implicit : 1; uint8_t rollback : 1; + uint8_t init : 1; /* Track commit phases */ - enum mgmt_commit_phase curr_phase; - enum mgmt_commit_phase next_phase; + enum mgmt_commit_phase phase; + + enum mgmt_commit_phase be_phase[MGMTD_BE_CLIENT_ID_MAX]; /* * Set of config changes to commit. This is used only @@ -125,26 +126,17 @@ struct mgmt_commit_cfg_req { * Details on all the Backend Clients associated with * this commit. */ - struct mgmt_be_client_subscr_info subscr_info; + uint64_t clients; /* * List of backend batches for this commit to be validated * and applied at the backend. - * - * FIXME: Need to re-think this design for the case set of - * validators for a given YANG data item is different from - * the set of notifiers for the same. We may need to have - * separate list of batches for VALIDATE and APPLY. */ - struct mgmt_txn_batches_head curr_batches[MGMTD_BE_CLIENT_ID_MAX]; - struct mgmt_txn_batches_head next_batches[MGMTD_BE_CLIENT_ID_MAX]; + struct mgmt_txn_batches_head batches[MGMTD_BE_CLIENT_ID_MAX]; /* - * The last batch added for any backend client. This is always on - * 'curr_batches' + * The last batch added for any backend client. */ struct mgmt_txn_be_cfg_batch *last_be_cfg_batch[MGMTD_BE_CLIENT_ID_MAX]; - struct hash *batches; - uint64_t next_batch_id; struct mgmt_commit_stats *cmt_stats; }; @@ -176,6 +168,19 @@ struct mgmt_get_data_req { int total_reply; }; + +struct txn_req_get_tree { + char *xpath; /* xpath of tree to get */ + uint64_t sent_clients; /* Bitmask of clients sent req to */ + uint64_t recv_clients; /* Bitmask of clients recv reply from */ + int32_t partial_error; /* an error while gather results */ + uint8_t result_type; /* LYD_FORMAT for results */ + uint8_t wd_options; /* LYD_PRINT_WD_* flags for results */ + uint8_t exact; /* if exact node is requested */ + uint8_t simple_xpath; /* if xpath is simple */ + struct lyd_node *client_results; /* result tree from clients */ +}; + struct mgmt_txn_req { struct mgmt_txn_ctx *txn; enum mgmt_txn_event req_event; @@ -183,10 +188,10 @@ struct mgmt_txn_req { union { struct mgmt_set_cfg_req *set_cfg; struct mgmt_get_data_req *get_data; + struct txn_req_get_tree *get_tree; struct mgmt_commit_cfg_req commit_cfg; } req; - bool pending_be_proc; struct mgmt_txn_reqs_item list_linkage; }; @@ -206,7 +211,9 @@ struct mgmt_txn_ctx { struct event *proc_comm_cfg; struct event *proc_get_cfg; struct event *proc_get_data; + struct event *proc_get_tree; struct event *comm_cfg_timeout; + struct event *get_tree_timeout; struct event *clnup; /* List of backend adapters involved in this transaction */ @@ -216,6 +223,10 @@ struct mgmt_txn_ctx { struct mgmt_txns_item list_linkage; + /* TODO: why do we need unique lists for each type of transaction since + * a transaction is of only 1 type? + */ + /* * List of pending set-config requests for a given * transaction/session. Just one list for requests @@ -231,13 +242,9 @@ struct mgmt_txn_ctx { */ struct mgmt_txn_reqs_head get_cfg_reqs; /* - * List of pending get-data requests for a given - * transaction/session Two lists, one for requests - * not processed at all, and one for requests that - * has been sent to backend for processing. + * List of pending get-tree requests. */ - struct mgmt_txn_reqs_head get_data_reqs; - struct mgmt_txn_reqs_head pending_get_datas; + struct mgmt_txn_reqs_head get_tree_reqs; /* * There will always be one commit-config allowed for a given * transaction/session. No need to maintain lists for it. @@ -254,15 +261,12 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, enum mgmt_result result, const char *error_if_any); -static inline const char *mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn, - bool curr) +static inline const char *mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn) { if (!txn->commit_cfg_req) return "None"; - return (mgmt_commit_phase2str( - curr ? txn->commit_cfg_req->req.commit_cfg.curr_phase - : txn->commit_cfg_req->req.commit_cfg.next_phase)); + return mgmt_commit_phase2str(txn->commit_cfg_req->req.commit_cfg.phase); } static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line); @@ -277,9 +281,7 @@ static struct mgmt_master *mgmt_txn_mm; static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, enum mgmt_txn_event event); -static int -mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, - struct mgmt_be_client_adapter *adapter); +static void mgmt_txn_cleanup_txn(struct mgmt_txn_ctx **txn); static struct mgmt_txn_be_cfg_batch * mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, @@ -296,7 +298,7 @@ mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, MGMTD_TXN_LOCK(txn); assert(txn->commit_cfg_req); mgmt_txn_batches_add_tail(&txn->commit_cfg_req->req.commit_cfg - .curr_batches[id], + .batches[id], batch); batch->be_adapter = be_adapter; batch->buf_space_left = MGMTD_BE_CFGDATA_MAX_MSG_LEN; @@ -304,11 +306,6 @@ mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, mgmt_be_adapter_lock(be_adapter); txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] = batch; - if (!txn->commit_cfg_req->req.commit_cfg.next_batch_id) - txn->commit_cfg_req->req.commit_cfg.next_batch_id++; - batch->batch_id = txn->commit_cfg_req->req.commit_cfg.next_batch_id++; - hash_get(txn->commit_cfg_req->req.commit_cfg.batches, batch, - hash_alloc_intern); return batch; } @@ -318,15 +315,12 @@ static void mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **batch) size_t indx; struct mgmt_commit_cfg_req *cmtcfg_req; - MGMTD_TXN_DBG(" freeing batch-id: %" PRIu64 " txn-id %" PRIu64, - (*batch)->batch_id, (*batch)->txn->txn_id); + __dbg(" freeing batch txn-id %" PRIu64, (*batch)->txn->txn_id); assert((*batch)->txn && (*batch)->txn->type == MGMTD_TXN_TYPE_CONFIG); cmtcfg_req = &(*batch)->txn->commit_cfg_req->req.commit_cfg; - hash_release(cmtcfg_req->batches, *batch); - mgmt_txn_batches_del(&cmtcfg_req->curr_batches[(*batch)->be_id], *batch); - mgmt_txn_batches_del(&cmtcfg_req->next_batches[(*batch)->be_id], *batch); + mgmt_txn_batches_del(&cmtcfg_req->batches[(*batch)->be_id], *batch); if ((*batch)->be_adapter) mgmt_be_adapter_unlock(&(*batch)->be_adapter); @@ -344,57 +338,13 @@ static void mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **batch) *batch = NULL; } -static unsigned int mgmt_txn_cfgbatch_hash_key(const void *data) -{ - const struct mgmt_txn_be_cfg_batch *batch = data; - - return jhash2((uint32_t *)&batch->batch_id, - sizeof(batch->batch_id) / sizeof(uint32_t), 0); -} - -static bool mgmt_txn_cfgbatch_hash_cmp(const void *d1, const void *d2) -{ - const struct mgmt_txn_be_cfg_batch *batch1 = d1; - const struct mgmt_txn_be_cfg_batch *batch2 = d2; - - return (batch1->batch_id == batch2->batch_id); -} - -static void mgmt_txn_cfgbatch_hash_free(void *data) -{ - struct mgmt_txn_be_cfg_batch *batch = data; - - mgmt_txn_cfg_batch_free(&batch); -} - -static inline struct mgmt_txn_be_cfg_batch * -mgmt_txn_cfgbatch_id2ctx(struct mgmt_txn_ctx *txn, uint64_t batch_id) -{ - struct mgmt_txn_be_cfg_batch key = { 0 }; - struct mgmt_txn_be_cfg_batch *batch; - - if (!txn->commit_cfg_req) - return NULL; - - key.batch_id = batch_id; - batch = hash_lookup(txn->commit_cfg_req->req.commit_cfg.batches, &key); - - return batch; -} - static void mgmt_txn_cleanup_be_cfg_batches(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id) { struct mgmt_txn_be_cfg_batch *batch; struct mgmt_txn_batches_head *list; - list = &txn->commit_cfg_req->req.commit_cfg.curr_batches[id]; - FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) - mgmt_txn_cfg_batch_free(&batch); - - mgmt_txn_batches_fini(list); - - list = &txn->commit_cfg_req->req.commit_cfg.next_batches[id]; + list = &txn->commit_cfg_req->req.commit_cfg.batches[id]; FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) mgmt_txn_cfg_batch_free(&batch); @@ -415,7 +365,6 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, txn_req->txn = txn; txn_req->req_id = req_id; txn_req->req_event = req_event; - txn_req->pending_be_proc = false; switch (txn_req->req_event) { case MGMTD_TXN_PROC_SETCFG: @@ -423,27 +372,24 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, sizeof(struct mgmt_set_cfg_req)); assert(txn_req->req.set_cfg); mgmt_txn_reqs_add_tail(&txn->set_cfg_reqs, txn_req); - MGMTD_TXN_DBG("Added a new SETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 ", session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new SETCFG req-id: %" PRIu64 " txn-id: %" PRIu64 + ", session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); break; case MGMTD_TXN_PROC_COMMITCFG: txn->commit_cfg_req = txn_req; - MGMTD_TXN_DBG("Added a new COMMITCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new COMMITCFG req-id: %" PRIu64 + " txn-id: %" PRIu64 " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); FOREACH_MGMTD_BE_CLIENT_ID (id) { + txn_req->req.commit_cfg.be_phase[id] = + MGMTD_COMMIT_PHASE_PREPARE_CFG; mgmt_txn_batches_init( - &txn_req->req.commit_cfg.curr_batches[id]); - mgmt_txn_batches_init( - &txn_req->req.commit_cfg.next_batches[id]); + &txn_req->req.commit_cfg.batches[id]); } - txn_req->req.commit_cfg.batches = - hash_create(mgmt_txn_cfgbatch_hash_key, - mgmt_txn_cfgbatch_hash_cmp, - "MGMT Config Batches"); + txn_req->req.commit_cfg.phase = MGMTD_COMMIT_PHASE_PREPARE_CFG; break; case MGMTD_TXN_PROC_GETCFG: txn_req->req.get_data = @@ -451,22 +397,20 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, sizeof(struct mgmt_get_data_req)); assert(txn_req->req.get_data); mgmt_txn_reqs_add_tail(&txn->get_cfg_reqs, txn_req); - MGMTD_TXN_DBG("Added a new GETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new GETCFG req-id: %" PRIu64 " txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); break; - case MGMTD_TXN_PROC_GETDATA: - txn_req->req.get_data = - XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ, - sizeof(struct mgmt_get_data_req)); - assert(txn_req->req.get_data); - mgmt_txn_reqs_add_tail(&txn->get_data_reqs, txn_req); - MGMTD_TXN_DBG("Added a new GETDATA req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + case MGMTD_TXN_PROC_GETTREE: + txn_req->req.get_tree = XCALLOC(MTYPE_MGMTD_TXN_GETTREE_REQ, + sizeof(struct txn_req_get_tree)); + mgmt_txn_reqs_add_tail(&txn->get_tree_reqs, txn_req); + __dbg("Added a new GETTREE req-id: %" PRIu64 " txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); break; case MGMTD_TXN_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: + case MGMTD_TXN_GETTREE_TIMEOUT: break; } @@ -479,49 +423,31 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) { int indx; struct mgmt_txn_reqs_head *req_list = NULL; - struct mgmt_txn_reqs_head *pending_list = NULL; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *ccreq; + struct mgmt_set_cfg_req *set_cfg; bool cleanup; switch ((*txn_req)->req_event) { case MGMTD_TXN_PROC_SETCFG: - for (indx = 0; indx < (*txn_req)->req.set_cfg->num_cfg_changes; - indx++) { - if ((*txn_req)->req.set_cfg->cfg_changes[indx].value) { - MGMTD_TXN_DBG("Freeing value for %s at %p ==> '%s'", - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .xpath, - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .value, - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .value); - free((void *)(*txn_req) - ->req.set_cfg->cfg_changes[indx] - .value); - } + set_cfg = (*txn_req)->req.set_cfg; + for (indx = 0; indx < set_cfg->num_cfg_changes; indx++) { + if (set_cfg->cfg_changes[indx].value) + free((void *)set_cfg->cfg_changes[indx].value); } req_list = &(*txn_req)->txn->set_cfg_reqs; - MGMTD_TXN_DBG("Deleting SETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting SETCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); XFREE(MTYPE_MGMTD_TXN_SETCFG_REQ, (*txn_req)->req.set_cfg); break; case MGMTD_TXN_PROC_COMMITCFG: - MGMTD_TXN_DBG("Deleting COMMITCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting COMMITCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); ccreq = &(*txn_req)->req.commit_cfg; - cleanup = (ccreq->curr_phase >= MGMTD_COMMIT_PHASE_TXN_CREATE && - ccreq->curr_phase < MGMTD_COMMIT_PHASE_TXN_DELETE); + cleanup = (ccreq->phase >= MGMTD_COMMIT_PHASE_TXN_CREATE && + ccreq->phase < MGMTD_COMMIT_PHASE_TXN_DELETE); FOREACH_MGMTD_BE_CLIENT_ID (id) { /* @@ -534,20 +460,13 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) * anything more with them */ mgmt_txn_cleanup_be_cfg_batches((*txn_req)->txn, id); - if (ccreq->batches) { - hash_clean(ccreq->batches, - mgmt_txn_cfgbatch_hash_free); - hash_free(ccreq->batches); - ccreq->batches = NULL; - } /* * If we were in the middle of the state machine then * send a txn delete message */ adapter = mgmt_be_get_adapter_by_id(id); - if (adapter && cleanup && - ccreq->subscr_info.xpath_subscr[id]) + if (adapter && cleanup && IS_IDBIT_SET(ccreq->clients, id)) mgmt_txn_send_be_txn_delete((*txn_req)->txn, adapter); } @@ -560,9 +479,8 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) ->req.get_data->xpaths[indx]); } req_list = &(*txn_req)->txn->get_cfg_reqs; - MGMTD_TXN_DBG("Deleting GETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting GETCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); if ((*txn_req)->req.get_data->reply) XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, (*txn_req)->req.get_data->reply); @@ -572,42 +490,25 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); break; - case MGMTD_TXN_PROC_GETDATA: - for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths; - indx++) { - if ((*txn_req)->req.get_data->xpaths[indx]) - free((void *)(*txn_req) - ->req.get_data->xpaths[indx]); - } - pending_list = &(*txn_req)->txn->pending_get_datas; - req_list = &(*txn_req)->txn->get_data_reqs; - MGMTD_TXN_DBG("Deleting GETDATA req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); - if ((*txn_req)->req.get_data->reply) - XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, - (*txn_req)->req.get_data->reply); - XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); + case MGMTD_TXN_PROC_GETTREE: + __dbg("Deleting GETTREE req-id: %" PRIu64 " of txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + req_list = &(*txn_req)->txn->get_tree_reqs; + lyd_free_all((*txn_req)->req.get_tree->client_results); + XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.get_tree->xpath); + XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree); break; case MGMTD_TXN_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: + case MGMTD_TXN_GETTREE_TIMEOUT: break; } - if ((*txn_req)->pending_be_proc && pending_list) { - mgmt_txn_reqs_del(pending_list, *txn_req); - MGMTD_TXN_DBG("Removed req-id: %" PRIu64 - " from pending-list (left:%zu)", - (*txn_req)->req_id, - mgmt_txn_reqs_count(pending_list)); - } else if (req_list) { + if (req_list) { mgmt_txn_reqs_del(req_list, *txn_req); - MGMTD_TXN_DBG("Removed req-id: %" PRIu64 - " from request-list (left:%zu)", - (*txn_req)->req_id, mgmt_txn_reqs_count(req_list)); + __dbg("Removed req-id: %" PRIu64 " from request-list (left:%zu)", + (*txn_req)->req_id, mgmt_txn_reqs_count(req_list)); } - (*txn_req)->pending_be_proc = false; MGMTD_TXN_UNLOCK(&(*txn_req)->txn); XFREE(MTYPE_MGMTD_TXN_REQ, (*txn_req)); *txn_req = NULL; @@ -630,10 +531,10 @@ static void mgmt_txn_process_set_cfg(struct event *thread) assert(txn); cmt_stats = mgmt_fe_get_session_commit_stats(txn->session_id); - MGMTD_TXN_DBG("Processing %zu SET_CONFIG requests txn-id:%" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id, - txn->session_id); + __dbg("Processing %zu SET_CONFIG requests txn-id:%" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id, + txn->session_id); FOREACH_TXN_REQ_IN_LIST (&txn->set_cfg_reqs, txn_req) { assert(txn_req->req_event == MGMTD_TXN_PROC_SETCFG); @@ -666,7 +567,7 @@ static void mgmt_txn_process_set_cfg(struct event *thread) txn_req->req.set_cfg->cfg_changes, (size_t)txn_req->req.set_cfg ->num_cfg_changes, - NULL, NULL, 0, err_buf, + NULL, false, err_buf, sizeof(err_buf), &error); if (error) { mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, @@ -685,14 +586,17 @@ static void mgmt_txn_process_set_cfg(struct event *thread) /* We expect the user to have locked the DST DS */ if (!mgmt_ds_is_locked(txn_req->req.set_cfg->dst_ds_ctx, txn->session_id)) { - MGMTD_TXN_ERR("DS %u not locked for implicit commit txn-id: %" PRIu64 - " session-id: %" PRIu64 " err: %s", - txn_req->req.set_cfg->dst_ds_id, - txn->txn_id, txn->session_id, - strerror(ret)); - mgmt_txn_send_commit_cfg_reply( - txn, MGMTD_DS_LOCK_FAILED, - "running DS not locked for implicit commit"); + __log_err("DS %u not locked for implicit commit txn-id: %" PRIu64 + " session-id: %" PRIu64 " err: %s", + txn_req->req.set_cfg->dst_ds_id, + txn->txn_id, txn->session_id, + strerror(ret)); + mgmt_fe_send_set_cfg_reply( + txn->session_id, txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, MGMTD_DS_LOCK_FAILED, + "running DS not locked for implicit commit", + txn_req->req.set_cfg->implicit_commit); goto mgmt_txn_process_set_cfg_done; } @@ -717,9 +621,9 @@ static void mgmt_txn_process_set_cfg(struct event *thread) txn_req->req_id, MGMTD_SUCCESS, NULL, false) != 0) { - MGMTD_TXN_ERR("Failed to send SET_CONFIG_REPLY txn-id %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send SET_CONFIG_REPLY txn-id %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } mgmt_txn_process_set_cfg_done: @@ -736,9 +640,8 @@ mgmt_txn_process_set_cfg_done: left = mgmt_txn_reqs_count(&txn->set_cfg_reqs); if (left) { - MGMTD_TXN_DBG("Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC, - (int)left); + __dbg("Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC, (int)left); mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG); } } @@ -769,9 +672,9 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, txn->commit_cfg_req->req.commit_cfg .validate_only, result, error_if_any) != 0) { - MGMTD_TXN_ERR("Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id && @@ -783,9 +686,9 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, success ? MGMTD_SUCCESS : MGMTD_INTERNAL_ERROR, error_if_any, true) != 0) { - MGMTD_TXN_ERR("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } if (success) { @@ -848,6 +751,14 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, mgmt_history_rollback_complete(success); } + if (txn->commit_cfg_req->req.commit_cfg.init) { + /* + * This is the backend init request. + * We need to unlock the running datastore. + */ + mgmt_ds_unlock(txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx); + } + txn->commit_cfg_req->req.commit_cfg.cmt_stats = NULL; mgmt_txn_req_free(&txn->commit_cfg_req); @@ -857,66 +768,26 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, * we need to cleanup by itself. */ if (!txn->session_id) - mgmt_txn_register_event(txn, MGMTD_TXN_CLEANUP); + mgmt_txn_cleanup_txn(&txn); return 0; } -static void -mgmt_move_txn_cfg_batch_to_next(struct mgmt_commit_cfg_req *cmtcfg_req, - struct mgmt_txn_be_cfg_batch *batch, - struct mgmt_txn_batches_head *src_list, - struct mgmt_txn_batches_head *dst_list, - bool update_commit_phase, - enum mgmt_commit_phase to_phase) -{ - mgmt_txn_batches_del(src_list, batch); - - if (update_commit_phase) { - MGMTD_TXN_DBG("Move txn-id %" PRIu64 " batch-id: %" PRIu64 - " from '%s' --> '%s'", - batch->txn->txn_id, batch->batch_id, - mgmt_commit_phase2str(batch->comm_phase), - mgmt_txn_commit_phase_str(batch->txn, false)); - batch->comm_phase = to_phase; - } - - mgmt_txn_batches_add_tail(dst_list, batch); -} - -static void mgmt_move_txn_cfg_batches(struct mgmt_txn_ctx *txn, - struct mgmt_commit_cfg_req *cmtcfg_req, - struct mgmt_txn_batches_head *src_list, - struct mgmt_txn_batches_head *dst_list, - bool update_commit_phase, - enum mgmt_commit_phase to_phase) -{ - struct mgmt_txn_be_cfg_batch *batch; - - FOREACH_TXN_CFG_BATCH_IN_LIST (src_list, batch) { - mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, src_list, - dst_list, update_commit_phase, - to_phase); - } -} - static int mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn, struct mgmt_commit_cfg_req *cmtcfg_req) { - struct mgmt_txn_batches_head *curr_list, *next_list; enum mgmt_be_client_id id; - MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("txn-id: %" PRIu64 ", Phase '%s'", txn->txn_id, + mgmt_txn_commit_phase_str(txn)); /* * Check if all clients has moved to next phase or not. */ FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id] && - mgmt_txn_batches_count(&cmtcfg_req->curr_batches[id])) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id) && + cmtcfg_req->be_phase[id] == cmtcfg_req->phase) { /* * There's atleast once client who hasn't moved to * next phase. @@ -929,83 +800,36 @@ mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn, } } - MGMTD_TXN_DBG("Move entire txn-id: %" PRIu64 " from '%s' to '%s'", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); - /* * If we are here, it means all the clients has moved to next phase. * So we can move the whole commit to next phase. */ - cmtcfg_req->curr_phase = cmtcfg_req->next_phase; - cmtcfg_req->next_phase++; - MGMTD_TXN_DBG("Move back all config batches for txn-id: %" PRIu64 - " from next to current branch", - txn->txn_id); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - curr_list = &cmtcfg_req->curr_batches[id]; - next_list = &cmtcfg_req->next_batches[id]; - mgmt_move_txn_cfg_batches(txn, cmtcfg_req, next_list, curr_list, - false, 0); - } - - mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); - - return 0; -} - -static int -mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, - struct mgmt_be_client_adapter *adapter) -{ - struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_batches_head *curr_list, *next_list; - - if (txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) - return -1; - - cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - - MGMTD_TXN_DBG("Move txn-id: %" PRIu64 - " for '%s' Phase(current: '%s' next:'%s')", - txn->txn_id, adapter->name, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); - - MGMTD_TXN_DBG("Move all config batches for '%s' from current to next list", - adapter->name); - curr_list = &cmtcfg_req->curr_batches[adapter->id]; - next_list = &cmtcfg_req->next_batches[adapter->id]; - mgmt_move_txn_cfg_batches(txn, cmtcfg_req, curr_list, next_list, true, - cmtcfg_req->next_phase); + cmtcfg_req->phase++; - MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("Move entire txn-id: %" PRIu64 " to phase '%s'", txn->txn_id, + mgmt_txn_commit_phase_str(txn)); - /* - * Check if all clients has moved to next phase or not. - */ - mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); return 0; } +/* + * This is the real workhorse + */ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, struct nb_config_cbs *changes) { struct nb_config_cb *cb, *nxt; struct nb_config_change *chg; struct mgmt_txn_be_cfg_batch *batch; - struct mgmt_be_client_subscr_info subscr_info; char *xpath = NULL, *value = NULL; - char err_buf[1024]; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - bool found_validator; int num_chgs = 0; int xpath_len, value_len; + uint64_t clients, chg_clients; cmtcfg_req = &txn_req->req.commit_cfg; @@ -1029,24 +853,21 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, if (!value) value = (char *)MGMTD_BE_CONTAINER_NODE_VAL; - MGMTD_TXN_DBG("XPATH: %s, Value: '%s'", xpath, - value ? value : "NIL"); + __dbg("XPATH: %s, Value: '%s'", xpath, value ? value : "NIL"); + + clients = mgmt_be_interested_clients(xpath, true); - mgmt_be_get_subscr_info_for_xpath(xpath, &subscr_info); + chg_clients = 0; xpath_len = strlen(xpath) + 1; value_len = strlen(value) + 1; - found_validator = false; - FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (!(subscr_info.xpath_subscr[id] & - (MGMT_SUBSCR_VALIDATE_CFG | - MGMT_SUBSCR_NOTIFY_CFG))) - continue; - + FOREACH_BE_CLIENT_BITS (id, clients) { adapter = mgmt_be_get_adapter_by_id(id); if (!adapter) continue; + chg_clients |= (1ull << id); + batch = cmtcfg_req->last_be_cfg_batch[id]; if (!batch || (batch->num_cfg_data == @@ -1058,18 +879,20 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, } batch->buf_space_left -= (xpath_len + value_len); - memcpy(&batch->xp_subscr[batch->num_cfg_data], - &subscr_info.xpath_subscr[id], - sizeof(batch->xp_subscr[0])); mgmt_yang_cfg_data_req_init( &batch->cfg_data[batch->num_cfg_data]); batch->cfg_datap[batch->num_cfg_data] = &batch->cfg_data[batch->num_cfg_data]; - if (chg->cb.operation == NB_OP_DESTROY) + /* + * On the backend, we don't really care if it's CREATE + * or MODIFY, because the existence was already checked + * on the frontend. Therefore we use SET for both. + */ + if (chg->cb.operation == NB_CB_DESTROY) batch->cfg_data[batch->num_cfg_data].req_type = - MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; + MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA; else batch->cfg_data[batch->num_cfg_data].req_type = MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; @@ -1087,28 +910,19 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; batch->value[batch->num_cfg_data].encoded_str_val = value; - value = NULL; - if (subscr_info.xpath_subscr[id] & - MGMT_SUBSCR_VALIDATE_CFG) - found_validator = true; - - cmtcfg_req->subscr_info.xpath_subscr[id] |= - subscr_info.xpath_subscr[id]; - MGMTD_TXN_DBG(" -- %s, batch-id: %" PRIu64 " item:%d", - adapter->name, batch->batch_id, - (int)batch->num_cfg_data); + __dbg(" -- %s, batch item:%d", adapter->name, + (int)batch->num_cfg_data); batch->num_cfg_data++; num_chgs++; } - if (!found_validator) { - snprintf(err_buf, sizeof(err_buf), - "No validator module found for XPATH: '%s", - xpath); - MGMTD_TXN_ERR("***** %s", err_buf); - } + if (!chg_clients) + __log_err("No connected daemon is interested in XPATH %s", + xpath); + + cmtcfg_req->clients |= chg_clients; free(xpath); } @@ -1121,7 +935,13 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, return -1; } - cmtcfg_req->next_phase = MGMTD_COMMIT_PHASE_TXN_CREATE; + /* Move all BE clients to create phase */ + FOREACH_MGMTD_BE_CLIENT_ID(id) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) + cmtcfg_req->be_phase[id] = + MGMTD_COMMIT_PHASE_TXN_CREATE; + } + return 0; } @@ -1194,25 +1014,28 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) } /* - * Check for diffs from scratch buffer. If found empty - * get the diff from Candidate DS itself. + * Validate YANG contents of the source DS and get the diff + * between source and destination DS contents. */ - cfg_chgs = &nb_config->cfg_chgs; - if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { - /* - * This could be the case when the config is directly - * loaded onto the candidate DS from a file. Get the - * diff from a full comparison of the candidate and - * running DSs. - */ - nb_config_diff(mgmt_ds_get_nb_config( - txn->commit_cfg_req->req.commit_cfg - .dst_ds_ctx), - nb_config, &changes); - cfg_chgs = &changes; - del_cfg_chgs = true; + char err_buf[BUFSIZ] = { 0 }; + + ret = nb_candidate_validate_yang(nb_config, true, err_buf, + sizeof(err_buf) - 1); + if (ret != NB_OK) { + if (strncmp(err_buf, " ", strlen(err_buf)) == 0) + strlcpy(err_buf, "Validation failed", sizeof(err_buf)); + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + err_buf); + ret = -1; + goto mgmt_txn_prepare_config_done; } + nb_config_diff(mgmt_ds_get_nb_config(txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx), + nb_config, &changes); + cfg_chgs = &changes; + del_cfg_chgs = true; + if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { /* * This means there's no changes to commit whatsoever @@ -1229,29 +1052,13 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) gettimeofday(&txn->commit_cfg_req->req.commit_cfg.cmt_stats ->validate_start, NULL); - /* - * Validate YANG contents of the source DS and get the diff - * between source and destination DS contents. - */ - char err_buf[1024] = { 0 }; - nb_ctx.client = NB_CLIENT_MGMTD_SERVER; - nb_ctx.user = (void *)txn; - - ret = nb_candidate_validate_yang(nb_config, true, err_buf, - sizeof(err_buf) - 1); - if (ret != NB_OK) { - if (strncmp(err_buf, " ", strlen(err_buf)) == 0) - strlcpy(err_buf, "Validation failed", sizeof(err_buf)); - (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, - err_buf); - ret = -1; - goto mgmt_txn_prepare_config_done; - } /* * Perform application level validations locally on the MGMTD * process by calling application specific validation routines * loaded onto MGMTD process using libraries. */ + nb_ctx.client = NB_CLIENT_MGMTD_SERVER; + nb_ctx.user = (void *)txn; ret = nb_candidate_validate_code(&nb_ctx, nb_config, &changes, err_buf, sizeof(err_buf) - 1); if (ret != NB_OK) { @@ -1290,7 +1097,7 @@ mgmt_txn_prep_config_validation_done: } /* Move to the Transaction Create Phase */ - txn->commit_cfg_req->req.commit_cfg.curr_phase = + txn->commit_cfg_req->req.commit_cfg.phase = MGMTD_COMMIT_PHASE_TXN_CREATE; mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); @@ -1312,13 +1119,12 @@ static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn) enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_be_cfg_batch *batch; assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id]) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) { adapter = mgmt_be_get_adapter_by_id(id); if (mgmt_be_send_txn_req(adapter, txn->txn_id, true)) { (void)mgmt_txn_send_commit_cfg_reply( @@ -1326,29 +1132,16 @@ static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn) "Could not send TXN_CREATE to backend adapter"); return -1; } - - FOREACH_TXN_CFG_BATCH_IN_LIST (&txn->commit_cfg_req->req - .commit_cfg - .curr_batches[id], - batch) - batch->comm_phase = - MGMTD_COMMIT_PHASE_TXN_CREATE; } } - txn->commit_cfg_req->req.commit_cfg.next_phase = - MGMTD_COMMIT_PHASE_SEND_CFG; - /* * Dont move the commit to next phase yet. Wait for the TXN_REPLY to * come back. */ - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " Phase(Current:'%s', Next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64 " Phase '%s'", + txn->txn_id, txn->session_id, mgmt_txn_commit_phase_str(txn)); return 0; } @@ -1364,41 +1157,34 @@ static int mgmt_txn_send_be_cfg_data(struct mgmt_txn_ctx *txn, assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - assert(cmtcfg_req->subscr_info.xpath_subscr[adapter->id]); + assert(IS_IDBIT_SET(cmtcfg_req->clients, adapter->id)); indx = 0; - num_batches = - mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id]); - FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->curr_batches[adapter->id], + num_batches = mgmt_txn_batches_count(&cmtcfg_req->batches[adapter->id]); + FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->batches[adapter->id], batch) { - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_SEND_CFG); cfg_req.cfgdata_reqs = batch->cfg_datap; cfg_req.num_reqs = batch->num_cfg_data; indx++; if (mgmt_be_send_cfgdata_req(adapter, txn->txn_id, - batch->batch_id, cfg_req.cfgdata_reqs, cfg_req.num_reqs, indx == num_batches)) { (void)mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, "Internal Error! Could not send config data to backend!"); - MGMTD_TXN_ERR("Could not send CFGDATA_CREATE txn-id: %" PRIu64 - " batch-id: %" PRIu64 " to client '%s", - txn->txn_id, batch->batch_id, - adapter->name); + __log_err("Could not send CFGDATA_CREATE txn-id: %" PRIu64 + " to client '%s", + txn->txn_id, adapter->name); return -1; } cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++; - mgmt_move_txn_cfg_batch_to_next( - cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], true, - MGMTD_COMMIT_PHASE_SEND_CFG); } + cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_SEND_CFG; + /* * This could be the last Backend Client to send CFGDATA_CREATE_REQ to. * Try moving the commit to next phase. @@ -1415,9 +1201,8 @@ static int mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn, &txn->commit_cfg_req->req.commit_cfg; assert(txn->type == MGMTD_TXN_TYPE_CONFIG); - assert(!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])); - if (!cmtcfg_req->subscr_info.xpath_subscr[adapter->id]) + if (IS_IDBIT_UNSET(cmtcfg_req->clients, adapter->id)) return 0; return mgmt_be_send_txn_req(adapter, txn->txn_id, false); @@ -1435,8 +1220,8 @@ static void mgmt_txn_cfg_commit_timedout(struct event *thread) if (!txn->commit_cfg_req) return; - MGMTD_TXN_ERR("Backend timeout txn-id: %" PRIu64 " aborting commit", - txn->txn_id); + __log_err("Backend timeout txn-id: %" PRIu64 " aborting commit", + txn->txn_id); /* * Send a COMMIT_CONFIG_REPLY with failure. @@ -1448,6 +1233,84 @@ static void mgmt_txn_cfg_commit_timedout(struct event *thread) "Operation on the backend timed-out. Aborting commit!"); } + +static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn, + struct mgmt_txn_req *txn_req) +{ + struct txn_req_get_tree *get_tree = txn_req->req.get_tree; + uint64_t req_id = txn_req->req_id; + struct lyd_node *result; + int ret = NB_OK; + + /* cancel timer and send reply onward */ + EVENT_OFF(txn->get_tree_timeout); + + if (!get_tree->simple_xpath && get_tree->client_results) { + /* + * We have a complex query so Filter results by the xpath query. + */ + if (yang_lyd_trim_xpath(&get_tree->client_results, + txn_req->req.get_tree->xpath)) + ret = NB_ERR; + } + + result = get_tree->client_results; + + if (ret == NB_OK && result && get_tree->exact) + result = yang_dnode_get(result, get_tree->xpath); + + if (ret == NB_OK) + ret = mgmt_fe_adapter_send_tree_data(txn->session_id, + txn->txn_id, + txn_req->req_id, + get_tree->result_type, + get_tree->wd_options, + result, + get_tree->partial_error, + false); + + /* we're done with the request */ + mgmt_txn_req_free(&txn_req); + + if (ret) { + __log_err("Error sending the results of GETTREE for txn-id %" PRIu64 + " req_id %" PRIu64 " to requested type %u", + txn->txn_id, req_id, get_tree->result_type); + + (void)mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, ret, + "Error converting results of GETTREE"); + } + + return ret; +} + + +static void txn_get_tree_timeout(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + + txn_req = (struct mgmt_txn_req *)EVENT_ARG(thread); + txn = txn_req->txn; + + assert(txn); + assert(txn->type == MGMTD_TXN_TYPE_SHOW); + + + __log_err("Backend timeout txn-id: %" PRIu64 " ending get-tree", + txn->txn_id); + + /* + * Send a get-tree data reply. + * + * NOTE: The transaction cleanup will be triggered from Front-end + * adapter. + */ + + txn_req->req.get_tree->partial_error = -ETIMEDOUT; + txn_get_tree_data_done(txn, txn_req); +} + /* * Send CFG_APPLY_REQs to all the backend client. * @@ -1461,8 +1324,6 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_batches_head *batch_list; - struct mgmt_txn_be_cfg_batch *batch; assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); @@ -1476,13 +1337,11 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) } FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id] & - MGMT_SUBSCR_NOTIFY_CFG) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) { adapter = mgmt_be_get_adapter_by_id(id); if (!adapter) return -1; - batch_list = &cmtcfg_req->curr_batches[id]; if (mgmt_be_send_cfgapply_req(adapter, txn->txn_id)) { (void)mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, @@ -1493,15 +1352,9 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) UNSET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); - - FOREACH_TXN_CFG_BATCH_IN_LIST (batch_list, batch) - batch->comm_phase = MGMTD_COMMIT_PHASE_APPLY_CFG; } } - txn->commit_cfg_req->req.commit_cfg.next_phase = - MGMTD_COMMIT_PHASE_TXN_DELETE; - /* * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES * to come back. @@ -1518,15 +1371,13 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); assert(txn); - MGMTD_TXN_DBG("Processing COMMIT_CONFIG for txn-id: %" PRIu64 - " session-id: %" PRIu64 " Phase(Current:'%s', Next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("Processing COMMIT_CONFIG for txn-id: %" PRIu64 + " session-id: %" PRIu64 " Phase '%s'", + txn->txn_id, txn->session_id, mgmt_txn_commit_phase_str(txn)); assert(txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - switch (cmtcfg_req->curr_phase) { + switch (cmtcfg_req->phase) { case MGMTD_COMMIT_PHASE_PREPARE_CFG: mgmt_txn_prepare_config(txn); break; @@ -1548,15 +1399,13 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) * Backend by now. */ #ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " trigger sending CFG_VALIDATE_REQ to all backend clients", - txn->txn_id, txn->session_id); + __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64 + " trigger sending CFG_VALIDATE_REQ to all backend clients", + txn->txn_id, txn->session_id); #else /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */ - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " trigger sending CFG_APPLY_REQ to all backend clients", - txn->txn_id, txn->session_id); + __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64 + " trigger sending CFG_APPLY_REQ to all backend clients", + txn->txn_id, txn->session_id); #endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */ break; case MGMTD_COMMIT_PHASE_APPLY_CFG: @@ -1587,12 +1436,6 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) case MGMTD_COMMIT_PHASE_MAX: break; } - - MGMTD_TXN_DBG("txn-id:%" PRIu64 " session-id: %" PRIu64 - " phase updated to (current:'%s', next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); } static void mgmt_init_get_data_reply(struct mgmt_get_data_reply *get_reply) @@ -1613,8 +1456,6 @@ static void mgmt_reset_get_data_reply(struct mgmt_get_data_reply *get_reply) get_reply->reply_xpathp[indx] = 0; } if (get_reply->reply_data[indx].xpath) { - zlog_debug("%s free xpath %p", __func__, - get_reply->reply_data[indx].xpath); free(get_reply->reply_data[indx].xpath); get_reply->reply_data[indx].xpath = 0; } @@ -1653,8 +1494,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, data_reply->next_indx = (!get_reply->last_batch ? get_req->total_reply : -1); - MGMTD_TXN_DBG("Sending %zu Get-Config/Data replies next-index:%" PRId64, - data_reply->n_data, data_reply->next_indx); + __dbg("Sending %zu Get-Config/Data replies next-index:%" PRId64, + data_reply->n_data, data_reply->next_indx); switch (txn_req->req_event) { case MGMTD_TXN_PROC_GETCFG: @@ -1662,30 +1503,18 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, txn_req->txn->txn_id, get_req->ds_id, txn_req->req_id, MGMTD_SUCCESS, data_reply, NULL) != 0) { - MGMTD_TXN_ERR("Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->txn->txn_id, - txn_req->txn->session_id, txn_req->req_id); - } - break; - case MGMTD_TXN_PROC_GETDATA: - if (mgmt_fe_send_get_reply(txn_req->txn->session_id, - txn_req->txn->txn_id, get_req->ds_id, - txn_req->req_id, MGMTD_SUCCESS, - data_reply, NULL) != 0) { - MGMTD_TXN_ERR("Failed to send GET-DATA-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->txn->txn_id, - txn_req->txn->session_id, txn_req->req_id); + __log_err("Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64 " req-id: %" PRIu64, + txn_req->txn->txn_id, + txn_req->txn->session_id, txn_req->req_id); } break; case MGMTD_TXN_PROC_SETCFG: case MGMTD_TXN_PROC_COMMITCFG: + case MGMTD_TXN_PROC_GETTREE: + case MGMTD_TXN_GETTREE_TIMEOUT: case MGMTD_TXN_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: - MGMTD_TXN_ERR("Invalid Txn-Req-Event %u", txn_req->req_event); + __log_err("Invalid Txn-Req-Event %u", txn_req->req_event); break; } @@ -1695,10 +1524,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, mgmt_reset_get_data_reply_buf(get_req); } -static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, - struct lyd_node *node, - struct nb_node *nb_node, - void *ctx) +static void txn_iter_get_config_data_cb(const char *xpath, struct lyd_node *node, + struct nb_node *nb_node, void *ctx) { struct mgmt_txn_req *txn_req; struct mgmt_get_data_req *get_req; @@ -1713,8 +1540,7 @@ static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, if (!(node->schema->nodetype & LYD_NODE_TERM)) return; - assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG || - txn_req->req_event == MGMTD_TXN_PROC_GETDATA); + assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG); get_req = txn_req->req.get_data; assert(get_req); @@ -1731,8 +1557,8 @@ static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, get_reply->num_reply++; get_req->total_reply++; - MGMTD_TXN_DBG(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply, - data->xpath, data_value->encoded_str_val); + __dbg(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply, + data->xpath, data_value->encoded_str_val); if (get_reply->num_reply == MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH) mgmt_txn_send_getcfg_reply_data(txn_req, get_req); @@ -1766,8 +1592,8 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, */ get_reply = get_data->reply; for (indx = 0; indx < get_data->num_xpaths; indx++) { - MGMTD_TXN_DBG("Trying to get all data under '%s'", - get_data->xpaths[indx]); + __dbg("Trying to get all data under '%s'", + get_data->xpaths[indx]); mgmt_init_get_data_reply(get_reply); /* * mgmt_ds_iter_data works on path prefixes, but the user may @@ -1776,18 +1602,17 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, */ if (mgmt_ds_iter_data(get_data->ds_id, root, get_data->xpaths[indx], - mgmt_txn_iter_and_send_get_cfg_reply, + txn_iter_get_config_data_cb, (void *)txn_req) == -1) { - MGMTD_TXN_DBG("Invalid Xpath '%s", - get_data->xpaths[indx]); + __dbg("Invalid Xpath '%s", get_data->xpaths[indx]); mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, get_data->ds_id, txn_req->req_id, MGMTD_INTERNAL_ERROR, NULL, "Invalid xpath"); goto mgmt_txn_get_config_failed; } - MGMTD_TXN_DBG("Got %d remaining data-replies for xpath '%s'", - get_reply->num_reply, get_data->xpaths[indx]); + __dbg("Got %d remaining data-replies for xpath '%s'", + get_reply->num_reply, get_data->xpaths[indx]); get_reply->last_batch = true; mgmt_txn_send_getcfg_reply_data(txn_req, get_data); } @@ -1814,10 +1639,10 @@ static void mgmt_txn_process_get_cfg(struct event *thread) txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); assert(txn); - MGMTD_TXN_DBG("Processing %zu GET_CONFIG requests txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id, - txn->session_id); + __dbg("Processing %zu GET_CONFIG requests txn-id: %" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id, + txn->session_id); FOREACH_TXN_REQ_IN_LIST (&txn->get_cfg_reqs, txn_req) { error = false; @@ -1826,11 +1651,10 @@ static void mgmt_txn_process_get_cfg(struct event *thread) assert(cfg_root); if (mgmt_txn_get_config(txn, txn_req, cfg_root) != 0) { - MGMTD_TXN_ERR("Unable to retrieve config from DS %d txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->req.get_data->ds_id, txn->txn_id, - txn->session_id, txn_req->req_id); + __log_err("Unable to retrieve config from DS %d txn-id: %" PRIu64 + " session-id: %" PRIu64 " req-id: %" PRIu64, + txn_req->req.get_data->ds_id, txn->txn_id, + txn->session_id, txn_req->req_id); error = true; } @@ -1853,60 +1677,12 @@ static void mgmt_txn_process_get_cfg(struct event *thread) } if (mgmt_txn_reqs_count(&txn->get_cfg_reqs)) { - MGMTD_TXN_DBG("Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC); + __dbg("Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC); mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETCFG); } } -static void mgmt_txn_process_get_data(struct event *thread) -{ - struct mgmt_txn_ctx *txn; - struct mgmt_txn_req *txn_req; - int num_processed = 0; - - txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); - assert(txn); - - MGMTD_TXN_DBG("Processing %zu GET_DATA requests txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->get_data_reqs), txn->txn_id, - txn->session_id); - - FOREACH_TXN_REQ_IN_LIST (&txn->get_data_reqs, txn_req) { - assert(txn_req->req_event == MGMTD_TXN_PROC_GETDATA); - - /* - * TODO: Trigger GET procedures for Backend - * For now return back error. - */ - mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, - txn_req->req.get_data->ds_id, - txn_req->req_id, MGMTD_INTERNAL_ERROR, - NULL, "GET-DATA is not supported yet!"); - /* - * Delete the txn request. - * Note: The following will remove it from the list - * as well. - */ - mgmt_txn_req_free(&txn_req); - - /* - * Else the transaction would have been already deleted or - * moved to corresponding pending list. No need to delete it. - */ - num_processed++; - if (num_processed == MGMTD_TXN_MAX_NUM_GETDATA_PROC) - break; - } - - if (mgmt_txn_reqs_count(&txn->get_data_reqs)) { - MGMTD_TXN_DBG("Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_GETDATA_PROC); - mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA); - } -} - static struct mgmt_txn_ctx * mgmt_fe_find_txn_by_session_id(struct mgmt_master *cm, uint64_t session_id, enum mgmt_txn_type type) @@ -1926,15 +1702,9 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, { struct mgmt_txn_ctx *txn = NULL; - /* - * For 'CONFIG' transaction check if one is already created - * or not. - */ - if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) { - if (mgmt_config_txn_in_progress() == session_id) - txn = mgmt_txn_mm->cfg_txn; - goto mgmt_create_txn_done; - } + /* Do not allow multiple config transactions */ + if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_config_txn_in_progress()) + return NULL; txn = mgmt_fe_find_txn_by_session_id(mgmt_txn_mm, session_id, type); if (!txn) { @@ -1944,10 +1714,10 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, txn->session_id = session_id; txn->type = type; mgmt_txns_add_tail(&mgmt_txn_mm->txn_list, txn); + /* TODO: why do we need N lists for one transaction */ mgmt_txn_reqs_init(&txn->set_cfg_reqs); mgmt_txn_reqs_init(&txn->get_cfg_reqs); - mgmt_txn_reqs_init(&txn->get_data_reqs); - mgmt_txn_reqs_init(&txn->pending_get_datas); + mgmt_txn_reqs_init(&txn->get_tree_reqs); txn->commit_cfg_req = NULL; txn->refcount = 0; if (!mgmt_txn_mm->next_txn_id) @@ -1955,8 +1725,8 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, txn->txn_id = mgmt_txn_mm->next_txn_id++; hash_get(mgmt_txn_mm->txn_hash, txn, hash_alloc_intern); - MGMTD_TXN_DBG("Added new '%s' txn-id: %" PRIu64, - mgmt_txn_type2str(type), txn->txn_id); + __dbg("Added new '%s' txn-id: %" PRIu64, + mgmt_txn_type2str(type), txn->txn_id); if (type == MGMTD_TXN_TYPE_CONFIG) mgmt_txn_mm->cfg_txn = txn; @@ -1964,7 +1734,6 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, MGMTD_TXN_LOCK(txn); } -mgmt_create_txn_done: return txn; } @@ -2029,12 +1798,18 @@ static inline struct mgmt_txn_ctx *mgmt_txn_id2ctx(uint64_t txn_id) return txn; } +uint64_t mgmt_txn_get_session_id(uint64_t txn_id) +{ + struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id); + + return txn ? txn->session_id : MGMTD_SESSION_ID_NONE; +} + static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line) { txn->refcount++; - MGMTD_TXN_DBG("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file, - line, mgmt_txn_type2str(txn->type), txn->txn_id, - txn->refcount); + __dbg("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file, line, + mgmt_txn_type2str(txn->type), txn->txn_id, txn->refcount); } static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, @@ -2043,9 +1818,8 @@ static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, assert(*txn && (*txn)->refcount); (*txn)->refcount--; - MGMTD_TXN_DBG("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d", - file, line, mgmt_txn_type2str((*txn)->type), - (*txn)->txn_id, (*txn)->refcount); + __dbg("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d", file, line, + mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, (*txn)->refcount); if (!(*txn)->refcount) { if ((*txn)->type == MGMTD_TXN_TYPE_CONFIG) if (mgmt_txn_mm->cfg_txn == *txn) @@ -2054,13 +1828,13 @@ static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, EVENT_OFF((*txn)->proc_get_data); EVENT_OFF((*txn)->proc_comm_cfg); EVENT_OFF((*txn)->comm_cfg_timeout); + EVENT_OFF((*txn)->get_tree_timeout); hash_release(mgmt_txn_mm->txn_hash, *txn); mgmt_txns_del(&mgmt_txn_mm->txn_list, *txn); - MGMTD_TXN_DBG("Deleted %s txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, - (*txn)->session_id); + __dbg("Deleted %s txn-id: %" PRIu64 " session-id: %" PRIu64, + mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, + (*txn)->session_id); XFREE(MTYPE_MGMTD_TXN, *txn); } @@ -2086,16 +1860,6 @@ static void mgmt_txn_cleanup_all_txns(void) mgmt_txn_cleanup_txn(&txn); } -static void mgmt_txn_cleanup(struct event *thread) -{ - struct mgmt_txn_ctx *txn; - - txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); - assert(txn); - - mgmt_txn_cleanup_txn(&txn); -} - static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, enum mgmt_txn_event event) { @@ -2117,19 +1881,19 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_cfg, txn, &tv, &txn->proc_get_cfg); break; - case MGMTD_TXN_PROC_GETDATA: - event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_data, txn, - &tv, &txn->proc_get_data); - break; case MGMTD_TXN_COMMITCFG_TIMEOUT: - event_add_timer_msec(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, - txn, MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC, - &txn->comm_cfg_timeout); + event_add_timer(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, txn, + MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC, + &txn->comm_cfg_timeout); + break; + case MGMTD_TXN_GETTREE_TIMEOUT: + event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn, + MGMTD_TXN_GET_TREE_MAX_DELAY_SEC, + &txn->get_tree_timeout); + break; + case MGMTD_TXN_PROC_GETTREE: + assert(!"code bug do not register this event"); break; - case MGMTD_TXN_CLEANUP: - tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC; - event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv, - &txn->clnup); } } @@ -2154,12 +1918,12 @@ void mgmt_txn_destroy(void) mgmt_txn_hash_destroy(); } -uint64_t mgmt_config_txn_in_progress(void) +bool mgmt_config_txn_in_progress(void) { if (mgmt_txn_mm && mgmt_txn_mm->cfg_txn) - return mgmt_txn_mm->cfg_txn->session_id; + return true; - return MGMTD_SESSION_ID_NONE; + return false; } uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type) @@ -2195,13 +1959,14 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, size_t indx; uint16_t *num_chgs; struct nb_cfg_change *cfg_chg; + struct nb_node *node; txn = mgmt_txn_id2ctx(txn_id); if (!txn) return -1; if (implicit_commit && mgmt_txn_reqs_count(&txn->set_cfg_reqs)) { - MGMTD_TXN_ERR( + __log_err( "For implicit commit config only one SETCFG-REQ can be allowed!"); return -1; } @@ -2213,27 +1978,42 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, for (indx = 0; indx < num_req; indx++) { cfg_chg = &txn_req->req.set_cfg->cfg_changes[*num_chgs]; - if (cfg_req[indx]->req_type == - MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) + switch (cfg_req[indx]->req_type) { + case MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA: + cfg_chg->operation = NB_OP_DELETE; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA: cfg_chg->operation = NB_OP_DESTROY; - else if (cfg_req[indx]->req_type == - MGMTD__CFG_DATA_REQ_TYPE__SET_DATA) - cfg_chg->operation = - mgmt_ds_find_data_node_by_xpath(ds_ctx, - cfg_req[indx] - ->data - ->xpath) - ? NB_OP_MODIFY - : NB_OP_CREATE; - else + break; + case MGMTD__CFG_DATA_REQ_TYPE__SET_DATA: + /* + * For backward compatibility, we need to allow creating + * *new* list keys with SET_DATA operation. NB_OP_MODIFY + * is not allowed for keys, so use NB_OP_CREATE_EXCL. + */ + node = nb_node_find(cfg_req[indx]->data->xpath); + if (node && lysc_is_key(node->snode)) + cfg_chg->operation = NB_OP_CREATE_EXCL; + else + cfg_chg->operation = NB_OP_MODIFY; + break; + case MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA: + cfg_chg->operation = NB_OP_CREATE_EXCL; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA: + cfg_chg->operation = NB_OP_REPLACE; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REQ_TYPE_NONE: + case _MGMTD__CFG_DATA_REQ_TYPE_IS_INT_SIZE: + default: continue; + } - MGMTD_TXN_DBG("XPath: '%s', Value: '%s'", - cfg_req[indx]->data->xpath, - (cfg_req[indx]->data->value && - cfg_req[indx]->data->value->encoded_str_val - ? cfg_req[indx]->data->value->encoded_str_val - : "NULL")); + __dbg("XPath: '%s', Value: '%s'", cfg_req[indx]->data->xpath, + (cfg_req[indx]->data->value && + cfg_req[indx]->data->value->encoded_str_val + ? cfg_req[indx]->data->value->encoded_str_val + : "NULL")); strlcpy(cfg_chg->xpath, cfg_req[indx]->data->xpath, sizeof(cfg_chg->xpath)); cfg_chg->value = @@ -2243,8 +2023,8 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, ->data->value->encoded_str_val) : NULL); if (cfg_chg->value) - MGMTD_TXN_DBG("Allocated value at %p ==> '%s'", - cfg_chg->value, cfg_chg->value); + __dbg("Allocated value at %p ==> '%s'", cfg_chg->value, + cfg_chg->value); (*num_chgs)++; } @@ -2274,9 +2054,9 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id, return -1; if (txn->commit_cfg_req) { - MGMTD_TXN_ERR("Commit already in-progress txn-id: %" PRIu64 - " session-id: %" PRIu64 ". Cannot start another", - txn->txn_id, txn->session_id); + __log_err("Commit already in-progress txn-id: %" PRIu64 + " session-id: %" PRIu64 ". Cannot start another", + txn->txn_id, txn->session_id); return -1; } @@ -2306,15 +2086,26 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, struct mgmt_commit_cfg_req *cmtcfg_req; static struct mgmt_commit_stats dummy_stats; struct nb_config_cbs *adapter_cfgs = NULL; + struct mgmt_ds_ctx *ds_ctx; memset(&dummy_stats, 0, sizeof(dummy_stats)); if (connect) { - /* Get config for this single backend client */ + ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_RUNNING); + assert(ds_ctx); + + /* + * Lock the running datastore to prevent any changes while we + * are initializing the backend. + */ + if (mgmt_ds_lock(ds_ctx, 0) != 0) + return -1; + /* Get config for this single backend client */ mgmt_be_get_adapter_config(adapter, &adapter_cfgs); if (!adapter_cfgs || RB_EMPTY(nb_config_cbs, adapter_cfgs)) { SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + mgmt_ds_unlock(ds_ctx); return 0; } @@ -2324,14 +2115,15 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, */ txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); if (!txn) { - MGMTD_TXN_ERR("Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'", - adapter->name); + __log_err("Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'", + adapter->name); + mgmt_ds_unlock(ds_ctx); + nb_config_diff_del_changes(adapter_cfgs); return -1; } - MGMTD_TXN_DBG("Created initial txn-id: %" PRIu64 - " for BE client '%s'", - txn->txn_id, adapter->name); + __dbg("Created initial txn-id: %" PRIu64 " for BE client '%s'", + txn->txn_id, adapter->name); /* * Set the changeset for transaction to commit and trigger the * commit request. @@ -2339,10 +2131,11 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, txn_req = mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG); txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_NONE; txn_req->req.commit_cfg.src_ds_ctx = 0; - txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_NONE; - txn_req->req.commit_cfg.dst_ds_ctx = 0; + txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_RUNNING; + txn_req->req.commit_cfg.dst_ds_ctx = ds_ctx; txn_req->req.commit_cfg.validate_only = false; txn_req->req.commit_cfg.abort = false; + txn_req->req.commit_cfg.init = true; txn_req->req.commit_cfg.cmt_stats = &dummy_stats; txn_req->req.commit_cfg.cfg_chgs = adapter_cfgs; @@ -2365,9 +2158,8 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, ? &txn->commit_cfg_req->req .commit_cfg : NULL; - if (cmtcfg_req && - cmtcfg_req->subscr_info - .xpath_subscr[adapter->id]) { + if (cmtcfg_req && IS_IDBIT_SET(cmtcfg_req->clients, + adapter->id)) { mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, "Backend daemon disconnected while processing commit!"); @@ -2400,7 +2192,7 @@ int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, * Done with TXN_CREATE. Move the backend client to * next phase. */ - assert(cmtcfg_req->curr_phase == + assert(cmtcfg_req->phase == MGMTD_COMMIT_PHASE_TXN_CREATE); /* @@ -2412,23 +2204,16 @@ int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, txn, MGMTD_INTERNAL_ERROR, "Internal error! Failed to initiate transaction at backend!"); } - } else { - /* - * Done with TXN_DELETE. Move the backend client to next phase. - */ - if (false) - mgmt_move_be_commit_to_next_phase(txn, adapter); } return 0; } -int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, - bool success, char *error_if_any, +int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, bool success, + char *error_if_any, struct mgmt_be_client_adapter *adapter) { struct mgmt_txn_ctx *txn; - struct mgmt_txn_be_cfg_batch *batch; struct mgmt_commit_cfg_req *cmtcfg_req; txn = mgmt_txn_id2ctx(txn_id); @@ -2439,15 +2224,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, return -1; cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_id); - if (!batch || batch->txn != txn) - return -1; - if (!success) { - MGMTD_TXN_ERR("CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64 - " batch-id %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch->batch_id, - error_if_any ? error_if_any : "None"); + __log_err("CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, + error_if_any ? error_if_any : "None"); mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, error_if_any @@ -2456,14 +2237,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, return 0; } - MGMTD_TXN_DBG("CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64 - " batch-id %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch->batch_id, - error_if_any ? error_if_any : "None"); - mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], - true, MGMTD_COMMIT_PHASE_APPLY_CFG); + __dbg("CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, error_if_any ? error_if_any : "None"); + + cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_APPLY_CFG; mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); @@ -2471,14 +2249,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, } int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, - uint64_t batch_ids[], - size_t num_batch_ids, char *error_if_any, + char *error_if_any, struct mgmt_be_client_adapter *adapter) { struct mgmt_txn_ctx *txn; - struct mgmt_txn_be_cfg_batch *batch; struct mgmt_commit_cfg_req *cmtcfg_req = NULL; - size_t indx; txn = mgmt_txn_id2ctx(txn_id); if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) @@ -2487,11 +2262,10 @@ int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; if (!success) { - MGMTD_TXN_ERR("CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64 - " batch ids %" PRIu64 " - %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch_ids[0], - batch_ids[num_batch_ids - 1], - error_if_any ? error_if_any : "None"); + __log_err("CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, + error_if_any ? error_if_any : "None"); mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, error_if_any @@ -2500,25 +2274,14 @@ int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, return 0; } - for (indx = 0; indx < num_batch_ids; indx++) { - batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_ids[indx]); - if (batch->txn != txn) - return -1; - mgmt_move_txn_cfg_batch_to_next( - cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], true, - MGMTD_COMMIT_PHASE_TXN_DELETE); - } + cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_TXN_DELETE; - if (!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])) { - /* - * All configuration for the specific backend has been applied. - * Send TXN-DELETE to wrap up the transaction for this backend. - */ - SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); - mgmt_txn_send_be_txn_delete(txn, adapter); - } + /* + * All configuration for the specific backend has been applied. + * Send TXN-DELETE to wrap up the transaction for this backend. + */ + SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + mgmt_txn_send_be_txn_delete(txn, adapter); mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); if (mm->perf_stats_en) @@ -2540,15 +2303,14 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, if (!txn) return -1; - req_event = cfg_root ? MGMTD_TXN_PROC_GETCFG : MGMTD_TXN_PROC_GETDATA; - + req_event = MGMTD_TXN_PROC_GETCFG; txn_req = mgmt_txn_req_alloc(txn, req_id, req_event); txn_req->req.get_data->ds_id = ds_id; txn_req->req.get_data->cfg_root = cfg_root; for (indx = 0; indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH; indx++) { - MGMTD_TXN_DBG("XPath: '%s'", data_req[indx]->data->xpath); + __dbg("XPath: '%s'", data_req[indx]->data->xpath); txn_req->req.get_data->xpaths[indx] = strdup(data_req[indx]->data->xpath); txn_req->req.get_data->num_xpaths++; @@ -2559,6 +2321,266 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, return 0; } + +/** + * Send get-tree requests to each client indicated in `clients` bitmask, which + * has registered operational state that matches the given `xpath` + */ +int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id, + uint64_t clients, Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, uint8_t flags, + uint32_t wd_options, bool simple_xpath, + const char *xpath) +{ + struct mgmt_msg_get_tree *msg; + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + struct txn_req_get_tree *get_tree; + enum mgmt_be_client_id id; + ssize_t slen = strlen(xpath); + int ret; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn) + return -1; + + /* If error in this function below here, be sure to free the req */ + txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETTREE); + get_tree = txn_req->req.get_tree; + get_tree->result_type = result_type; + get_tree->wd_options = wd_options; + get_tree->exact = CHECK_FLAG(flags, GET_DATA_FLAG_EXACT); + get_tree->simple_xpath = simple_xpath; + get_tree->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); + + if (CHECK_FLAG(flags, GET_DATA_FLAG_CONFIG)) { + /* + * If the requested datastore is operational, get the config + * from running. + */ + struct mgmt_ds_ctx *ds = + mgmt_ds_get_ctx_by_id(mm, ds_id == MGMTD_DS_OPERATIONAL + ? MGMTD_DS_RUNNING + : ds_id); + struct nb_config *config = mgmt_ds_get_nb_config(ds); + + if (config) { + struct ly_set *set = NULL; + LY_ERR err; + + err = lyd_find_xpath(config->dnode, xpath, &set); + if (err) { + get_tree->partial_error = err; + goto state; + } + + /* + * If there's a single result, duplicate the returned + * node. If there are multiple results, duplicate the + * whole config and mark simple_xpath as false so the + * result is trimmed later in txn_get_tree_data_done. + */ + if (set->count == 1) { + err = lyd_dup_single(set->dnodes[0], NULL, + LYD_DUP_WITH_PARENTS | + LYD_DUP_WITH_FLAGS | + LYD_DUP_RECURSIVE, + &get_tree->client_results); + if (!err) + while (get_tree->client_results->parent) + get_tree->client_results = lyd_parent( + get_tree->client_results); + } else if (set->count > 1) { + err = lyd_dup_siblings(config->dnode, NULL, + LYD_DUP_RECURSIVE | + LYD_DUP_WITH_FLAGS, + &get_tree->client_results); + if (!err) + get_tree->simple_xpath = false; + } + + if (err) + get_tree->partial_error = err; + + ly_set_free(set, NULL); + } + } +state: + /* If we are only getting config, we are done */ + if (!CHECK_FLAG(flags, GET_DATA_FLAG_STATE) || + ds_id != MGMTD_DS_OPERATIONAL || !clients) + return txn_get_tree_data_done(txn, txn_req); + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_tree, slen + 1, + MTYPE_MSG_NATIVE_GET_TREE); + msg->refer_id = txn_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_GET_TREE; + /* Always operate with the binary format in the backend */ + msg->result_type = LYD_LYB; + strlcpy(msg->xpath, xpath, slen + 1); + + assert(clients); + FOREACH_BE_CLIENT_BITS (id, clients) { + ret = mgmt_be_send_native(id, msg); + if (ret) { + __log_err("Could not send get-tree message to backend client %s", + mgmt_be_client_id2name(id)); + continue; + } + + __dbg("Sent get-tree req to backend client %s", + mgmt_be_client_id2name(id)); + + /* record that we sent the request to the client */ + get_tree->sent_clients |= (1u << id); + } + + mgmt_msg_native_free_msg(msg); + + /* Return if we didn't send any messages to backends */ + if (!get_tree->sent_clients) + return txn_get_tree_data_done(txn, txn_req); + + /* Start timeout timer - pulled out of register event code so we can + * pass a different arg + */ + event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn_req, + MGMTD_TXN_GET_TREE_MAX_DELAY_SEC, + &txn->get_tree_timeout); + return 0; +} + +/* + * Error reply from the backend client. + */ +int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t req_id, int error, + const char *errstr) +{ + enum mgmt_be_client_id id = adapter->id; + struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id); + struct txn_req_get_tree *get_tree; + struct mgmt_txn_req *txn_req; + + if (!txn) { + __log_err("Error reply from %s cannot find txn-id %" PRIu64, + adapter->name, txn_id); + return -1; + } + + /* Find the request. */ + FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req) + if (txn_req->req_id == req_id) + break; + if (!txn_req) { + __log_err("Error reply from %s for txn-id %" PRIu64 + " cannot find req_id %" PRIu64, + adapter->name, txn_id, req_id); + return -1; + } + + __log_err("Error reply from %s for txn-id %" PRIu64 " req_id %" PRIu64, + adapter->name, txn_id, req_id); + + switch (txn_req->req_event) { + case MGMTD_TXN_PROC_GETTREE: + get_tree = txn_req->req.get_tree; + get_tree->recv_clients |= (1u << id); + get_tree->partial_error = error; + + /* check if done yet */ + if (get_tree->recv_clients != get_tree->sent_clients) + return 0; + return txn_get_tree_data_done(txn, txn_req); + + /* non-native message events */ + case MGMTD_TXN_PROC_SETCFG: + case MGMTD_TXN_PROC_COMMITCFG: + case MGMTD_TXN_PROC_GETCFG: + case MGMTD_TXN_COMMITCFG_TIMEOUT: + case MGMTD_TXN_GETTREE_TIMEOUT: + default: + assert(!"non-native req event in native erorr path"); + return -1; + } +} + +/* + * Get-tree data from the backend client. + */ +int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_tree_data *data_msg, + size_t msg_len) +{ + uint64_t txn_id = data_msg->refer_id; + uint64_t req_id = data_msg->req_id; + + enum mgmt_be_client_id id = adapter->id; + struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id); + struct mgmt_txn_req *txn_req; + struct txn_req_get_tree *get_tree; + struct lyd_node *tree = NULL; + LY_ERR err; + + if (!txn) { + __log_err("GETTREE reply from %s for a missing txn-id %" PRIu64, + adapter->name, txn_id); + return -1; + } + + /* Find the request. */ + FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req) + if (txn_req->req_id == req_id) + break; + if (!txn_req) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " missing req_id %" PRIu64, + adapter->name, txn_id, req_id); + return -1; + } + + get_tree = txn_req->req.get_tree; + + /* store the result */ + err = lyd_parse_data_mem(ly_native_ctx, (const char *)data_msg->result, + data_msg->result_type, + LYD_PARSE_STRICT | LYD_PARSE_ONLY, + 0 /*LYD_VALIDATE_OPERATIONAL*/, &tree); + if (err) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " req_id %" PRIu64 " error parsing result of type %u", + adapter->name, txn_id, req_id, data_msg->result_type); + } + if (!err) { + /* TODO: we could merge ly_errs here if it's not binary */ + + if (!get_tree->client_results) + get_tree->client_results = tree; + else + err = lyd_merge_siblings(&get_tree->client_results, + tree, LYD_MERGE_DESTRUCT); + if (err) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " req_id %" PRIu64 " error merging result", + adapter->name, txn_id, req_id); + } + } + if (!get_tree->partial_error) + get_tree->partial_error = (data_msg->partial_error + ? data_msg->partial_error + : (int)err); + + if (!data_msg->more) + get_tree->recv_clients |= (1u << id); + + /* check if done yet */ + if (get_tree->recv_clients != get_tree->sent_clients) + return 0; + + return txn_get_tree_data_done(txn, txn_req); +} + void mgmt_txn_status_write(struct vty *vty) { struct mgmt_txn_ctx *txn; @@ -2614,12 +2636,12 @@ int mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx, */ txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); if (!txn) { - MGMTD_TXN_ERR( + __log_err( "Failed to create CONFIG Transaction for downloading CONFIGs"); return -1; } - MGMTD_TXN_DBG("Created rollback txn-id: %" PRIu64, txn->txn_id); + __dbg("Created rollback txn-id: %" PRIu64, txn->txn_id); /* * Set the changeset for transaction to commit and trigger the commit diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h index 068f07a..b719832 100644 --- a/mgmtd/mgmt_txn.h +++ b/mgmtd/mgmt_txn.h @@ -9,23 +9,23 @@ #ifndef _FRR_MGMTD_TXN_H_ #define _FRR_MGMTD_TXN_H_ +#include "lib/mgmt_msg_native.h" #include "mgmtd/mgmt_be_adapter.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_ds.h" -#define MGMTD_TXN_PROC_DELAY_MSEC 5 #define MGMTD_TXN_PROC_DELAY_USEC 10 #define MGMTD_TXN_MAX_NUM_SETCFG_PROC 128 #define MGMTD_TXN_MAX_NUM_GETCFG_PROC 128 #define MGMTD_TXN_MAX_NUM_GETDATA_PROC 128 -#define MGMTD_TXN_SEND_CFGVALIDATE_DELAY_MSEC 100 -#define MGMTD_TXN_SEND_CFGAPPLY_DELAY_MSEC 100 -#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */ +#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600 +#define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600 -#define MGMTD_TXN_CLEANUP_DELAY_MSEC 100 #define MGMTD_TXN_CLEANUP_DELAY_USEC 10 +#define MGMTD_TXN_ID_NONE 0 + /* * The following definition enables local validation of config * on the MGMTD process by loading client-defined NB callbacks @@ -71,12 +71,18 @@ extern int mgmt_txn_init(struct mgmt_master *cm, struct event_loop *tm); extern void mgmt_txn_destroy(void); /* - * Check if transaction is in progress. + * Check if configuration transaction is in progress. * * Returns: - * session ID if in-progress, MGMTD_SESSION_ID_NONE otherwise. + * true if in-progress, false otherwise. */ -extern uint64_t mgmt_config_txn_in_progress(void); +extern bool mgmt_config_txn_in_progress(void); + +/** + * Get the session ID associated with the given ``txn-id``. + * + */ +extern uint64_t mgmt_txn_get_session_id(uint64_t txn_id); /* * Create transaction. @@ -188,6 +194,31 @@ extern int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, Mgmtd__YangGetDataReq **data_req, size_t num_reqs); + +/** + * Send get-tree to the backend `clients`. + * + * Args: + * txn_id: Transaction identifier. + * req_id: FE client request identifier. + * clients: Bitmask of clients to send get-tree to. + * ds_id: datastore ID. + * result_type: LYD_FORMAT result format. + * flags: option flags for the request. + * wd_options: LYD_PRINT_WD_* flags for the result. + * simple_xpath: true if xpath is simple (only key predicates). + * xpath: The xpath to get the tree from. + * + * Return: + * 0 on success. + */ +extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id, + uint64_t clients, + Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, uint8_t flags, + uint32_t wd_options, bool simple_xpath, + const char *xpath); + /* * Notifiy backend adapter on connection. */ @@ -206,8 +237,8 @@ mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, * Reply to backend adapater with config data create request. */ extern int -mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, - bool success, char *error_if_any, +mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, bool success, + char *error_if_any, struct mgmt_be_client_adapter *adapter); /* @@ -223,10 +254,37 @@ extern int mgmt_txn_notify_be_cfg_validate_reply( */ extern int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, - uint64_t batch_ids[], - size_t num_batch_ids, char *error_if_any, + char *error_if_any, struct mgmt_be_client_adapter *adapter); + +/** + * Process a reply from a backend client to our get-tree request + * + * Args: + * adapter: The adapter that received the result. + * txn_id: The transaction for this get-tree request. + * req_id: The request ID for this transaction. + * error: the integer error value (negative) + * errstr: the string description of the error. + */ +int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t req_id, int error, + const char *errstr); + +/** + * Process a reply from a backend client to our get-tree request + * + * Args: + * adapter: The adapter that received the result. + * data_msg: The message from the backend. + * msg_len: Total length of the message. + */ + +extern int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_tree_data *data_msg, + size_t msg_len); + /* * Dump transaction status to vty. */ diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c index b49bf80..bbc1077 100644 --- a/mgmtd/mgmt_vty.c +++ b/mgmtd/mgmt_vty.c @@ -8,10 +8,13 @@ #include +#include "affinitymap.h" #include "command.h" +#include "filter.h" #include "json.h" #include "network.h" #include "northbound_cli.h" +#include "routemap.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_be_adapter.h" @@ -20,6 +23,10 @@ #include "mgmtd/mgmt_history.h" #include "mgmtd/mgmt_vty_clippy.c" +#include "ripd/rip_nb.h" +#include "ripngd/ripng_nb.h" +#include "staticd/static_vty.h" +#include "zebra/zebra_cli.h" extern struct frr_daemon_info *mgmt_daemon_info; @@ -144,6 +151,23 @@ DEFPY(mgmt_commit, return CMD_SUCCESS; } +DEFPY(mgmt_create_config_data, mgmt_create_config_data_cmd, + "mgmt create-config WORD$path VALUE", + MGMTD_STR + "Create configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to create\n") +{ + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_CREATE_EXCL; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, "mgmt set-config WORD$path VALUE", MGMTD_STR @@ -154,10 +178,10 @@ DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, strlcpy(vty->cfg_changes[0].xpath, path, sizeof(vty->cfg_changes[0].xpath)); vty->cfg_changes[0].value = value; - vty->cfg_changes[0].operation = NB_OP_CREATE; + vty->cfg_changes[0].operation = NB_OP_MODIFY; vty->num_cfg_changes = 1; - vty_mgmt_send_config_data(vty, false); + vty_mgmt_send_config_data(vty, NULL, false); return CMD_SUCCESS; } @@ -168,13 +192,48 @@ DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd, "XPath expression specifying the YANG data path\n") { + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = NULL; + vty->cfg_changes[0].operation = NB_OP_DELETE; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + +DEFPY(mgmt_remove_config_data, mgmt_remove_config_data_cmd, + "mgmt remove-config WORD$path", + MGMTD_STR + "Remove configuration data\n" + "XPath expression specifying the YANG data path\n") +{ + strlcpy(vty->cfg_changes[0].xpath, path, sizeof(vty->cfg_changes[0].xpath)); vty->cfg_changes[0].value = NULL; vty->cfg_changes[0].operation = NB_OP_DESTROY; vty->num_cfg_changes = 1; - vty_mgmt_send_config_data(vty, false); + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + +DEFPY(mgmt_replace_config_data, mgmt_replace_config_data_cmd, + "mgmt replace-config WORD$path VALUE", + MGMTD_STR + "Replace configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to set\n") +{ + + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_REPLACE; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); return CMD_SUCCESS; } @@ -199,22 +258,69 @@ DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd, } DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd, - "show mgmt get-data [candidate|operational|running]$dsname WORD$path", - SHOW_STR MGMTD_STR - "Get data from a specific datastore\n" + "show mgmt get-data WORD$path [datastore $ds] [with-config|only-config]$content [exact]$exact [with-defaults $wd] [json|xml]$fmt", + SHOW_STR + MGMTD_STR + "Get a data from the operational datastore\n" + "XPath expression specifying the YANG data root\n" + "Specify datastore to get data from (operational by default)\n" "Candidate datastore\n" - "Operational datastore (default)\n" "Running datastore\n" - "XPath expression specifying the YANG data path\n") + "Operational datastore\n" + "Include \"config true\" data\n" + "Get only \"config true\" data\n" + "Get exact node instead of the whole data tree\n" + "Configure 'with-defaults' mode per RFC 6243 (\"explicit\" mode by default)\n" + "Use \"trim\" mode\n" + "Use \"report-all-tagged\" mode\n" + "Use \"report-all\" mode\n" + "JSON output format\n" + "XML output format\n") { - const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; - Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL; + LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON; + int plen = strlen(path); + char *xpath = NULL; + uint8_t flags = content ? GET_DATA_FLAG_CONFIG : GET_DATA_FLAG_STATE; + uint8_t defaults = GET_DATA_DEFAULTS_EXPLICIT; + uint8_t datastore = MGMT_MSG_DATASTORE_OPERATIONAL; + + if (content && content[0] == 'w') + flags |= GET_DATA_FLAG_STATE; + + if (exact) + flags |= GET_DATA_FLAG_EXACT; + + if (wd) { + if (wd[0] == 't') + defaults = GET_DATA_DEFAULTS_TRIM; + else if (wd[3] == '-') + defaults = GET_DATA_DEFAULTS_ALL_ADD_TAG; + else + defaults = GET_DATA_DEFAULTS_ALL; + } - if (dsname) - datastore = mgmt_ds_name2id(dsname); + if (ds) { + if (ds[0] == 'c') + datastore = MGMT_MSG_DATASTORE_CANDIDATE; + else if (ds[0] == 'r') + datastore = MGMT_MSG_DATASTORE_RUNNING; + } + + /* get rid of extraneous trailing slash-* or single '/' unless root */ + if (plen > 2 && ((path[plen - 2] == '/' && path[plen - 1] == '*') || + (path[plen - 2] != '/' && path[plen - 1] == '/'))) { + plen = path[plen - 1] == '/' ? plen - 1 : plen - 2; + xpath = XSTRDUP(MTYPE_TMP, path); + xpath[plen] = 0; + path = xpath; + } + + vty_mgmt_send_get_data_req(vty, datastore, format, flags, defaults, + path); + + if (xpath) + XFREE(MTYPE_TMP, xpath); - xpath_list[0] = path; - vty_mgmt_send_get_req(vty, false, datastore, xpath_list, 1); return CMD_SUCCESS; } @@ -273,7 +379,7 @@ DEFPY(show_mgmt_map_xpath, "Get YANG Backend Subscription\n" "XPath expression specifying the YANG data path\n") { - mgmt_be_xpath_subscr_info_write(vty, path); + mgmt_be_show_xpath_registries(vty, path); return CMD_SUCCESS; } @@ -379,7 +485,7 @@ DEFPY(mgmt_rollback, int config_write_mgmt_debug(struct vty *vty); static struct cmd_node debug_node = { - .name = "debug", + .name = "mgmt debug", .node = DEBUG_NODE, .prompt = "", .config_write = config_write_mgmt_debug, @@ -438,12 +544,18 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, { uint32_t mode = DEBUG_NODE2MODE(vty->node); - if (be) + if (be) { DEBUG_MODE_SET(&mgmt_debug_be, mode, !no); + mgmt_be_adapter_toggle_client_debug( + DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)); + } if (ds) DEBUG_MODE_SET(&mgmt_debug_ds, mode, !no); - if (fe) + if (fe) { DEBUG_MODE_SET(&mgmt_debug_fe, mode, !no); + mgmt_fe_adapter_toggle_client_debug( + DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)); + } if (txn) DEBUG_MODE_SET(&mgmt_debug_txn, mode, !no); @@ -452,19 +564,57 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, static void mgmt_config_read_in(struct event *event) { - mgmt_vty_read_configs(); + if (vty_mgmt_fe_enabled()) + mgmt_vty_read_configs(); + else { + zlog_warn("%s: no connection to front-end server, retry in 1s", + __func__); + event_add_timer(mm->master, mgmt_config_read_in, NULL, 1, + &mgmt_daemon_info->read_in); + } +} + +static int mgmtd_config_write(struct vty *vty) +{ + struct lyd_node *root; + + LY_LIST_FOR (running_config->dnode, root) { + nb_cli_show_dnode_cmds(vty, root, false); + } + + return 1; } +static struct cmd_node mgmtd_node = { + .name = "mgmtd", + .node = MGMTD_NODE, + .prompt = "", + .config_write = mgmtd_config_write, +}; + void mgmt_vty_init(void) { + /* + * Library based CLI handlers + */ + filter_cli_init(); + route_map_cli_init(); + affinity_map_init(); + /* * Initialize command handling from VTYSH connection. * Call command initialization routines defined by * backend components that are moved to new MGMTD infra * here one by one. */ -#if HAVE_STATICD - extern void static_vty_init(void); + zebra_cli_init(); +#ifdef HAVE_RIPD + rip_cli_init(); +#endif +#ifdef HAVE_RIPNGD + ripng_cli_init(); +#endif +#ifdef HAVE_STATICD static_vty_init(); #endif @@ -472,6 +622,7 @@ void mgmt_vty_init(void) &mgmt_daemon_info->read_in); install_node(&debug_node); + install_node(&mgmtd_node); install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd); install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd); @@ -485,8 +636,11 @@ void mgmt_vty_init(void) install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd); install_element(CONFIG_NODE, &mgmt_commit_cmd); + install_element(CONFIG_NODE, &mgmt_create_config_data_cmd); install_element(CONFIG_NODE, &mgmt_set_config_data_cmd); install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd); install_element(CONFIG_NODE, &mgmt_load_config_cmd); install_element(CONFIG_NODE, &mgmt_save_config_cmd); install_element(CONFIG_NODE, &mgmt_rollback_cmd); diff --git a/mgmtd/subdir.am b/mgmtd/subdir.am index 67b45d5..1624c6e 100644 --- a/mgmtd/subdir.am +++ b/mgmtd/subdir.am @@ -15,6 +15,9 @@ clippy_scan += \ # end lib_LTLIBRARIES += mgmtd/libmgmt_be_nb.la +mgmtd_libmgmt_be_nb_la_SOURCES = \ + zebra/zebra_cli.c \ + # end nodist_mgmtd_libmgmt_be_nb_la_SOURCES = \ # end mgmtd_libmgmt_be_nb_la_CFLAGS = $(AM_CFLAGS) -DINCLUDE_MGMTD_CMDDEFS_ONLY @@ -33,11 +36,6 @@ mgmtd_libmgmtd_a_SOURCES = \ mgmtd/mgmt_vty.c \ # end -mgmtdheaderdir = $(pkgincludedir)/mgmtd -mgmtdheader_HEADERS = \ - mgmtd/mgmt_defines.h \ - # end - noinst_HEADERS += \ mgmtd/mgmt.h \ mgmtd/mgmt_be_adapter.h \ @@ -46,23 +44,64 @@ noinst_HEADERS += \ mgmtd/mgmt_history.h \ mgmtd/mgmt_memory.h \ mgmtd/mgmt_txn.h \ + zebra/zebra_cli.h \ # end sbin_PROGRAMS += mgmtd/mgmtd +if MGMTD_TESTC +sbin_PROGRAMS += mgmtd/mgmtd_testc +mgmtd_mgmtd_testc_SOURCES = mgmtd/mgmt_testc.c +mgmtd_mgmtd_testc_LDADD = lib/libfrr.la +endif + mgmtd_mgmtd_SOURCES = \ mgmtd/mgmt_main.c \ # end nodist_mgmtd_mgmtd_SOURCES = \ + yang/frr-zebra.yang.c \ + yang/frr-zebra-route-map.yang.c \ + yang/ietf/ietf-netconf-acm.yang.c \ + yang/ietf/ietf-netconf.yang.c \ + yang/ietf/ietf-netconf-with-defaults.yang.c \ # nothing mgmtd_mgmtd_CFLAGS = $(AM_CFLAGS) -I ./ mgmtd_mgmtd_LDADD = mgmtd/libmgmtd.a lib/libfrr.la $(LIBCAP) $(LIBM) $(LIBYANG_LIBS) $(UST_LIBS) mgmtd_mgmtd_LDADD += mgmtd/libmgmt_be_nb.la + +if STATICD +nodist_mgmtd_mgmtd_SOURCES += yang/frr-bfdd.yang.c +else +if RIPD +nodist_mgmtd_mgmtd_SOURCES += yang/frr-bfdd.yang.c +endif +endif + +if RIPD +nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-ripd.yang.c \ + # end +mgmtd_libmgmt_be_nb_la_SOURCES += \ + ripd/rip_cli.c \ + # end +endif + +if RIPNGD +nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-ripngd.yang.c \ + # end +mgmtd_libmgmt_be_nb_la_SOURCES += \ + ripngd/ripng_cli.c \ + # end +endif + if STATICD nodist_mgmtd_mgmtd_SOURCES += \ yang/frr-staticd.yang.c \ - yang/frr-bfdd.yang.c \ # end -nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c +nodist_mgmtd_libmgmt_be_nb_la_SOURCES += \ + staticd/static_vty.c \ + # end endif + diff --git a/nhrpd/linux.c b/nhrpd/linux.c index eb98166..e4df0dd 100644 --- a/nhrpd/linux.c +++ b/nhrpd/linux.c @@ -5,6 +5,7 @@ #include "zebra.h" +#include #include #include @@ -96,25 +97,6 @@ int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, return 0; } -static int linux_configure_arp(const char *iface, int on) -{ - struct ifreq ifr; - - strlcpy(ifr.ifr_name, iface, IFNAMSIZ); - if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr)) - return -1; - - if (on) - ifr.ifr_flags &= ~IFF_NOARP; - else - ifr.ifr_flags |= IFF_NOARP; - - if (ioctl(nhrp_socket_fd, SIOCSIFFLAGS, &ifr)) - return -1; - - return 0; -} - static int linux_icmp_redirect_off(const char *iface) { char fname[PATH_MAX]; @@ -142,7 +124,6 @@ int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af) ret |= linux_icmp_redirect_off(ifname); break; } - ret |= linux_configure_arp(ifname, 1); return ret; } diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h index 7a3029b..33af1a0 100644 --- a/nhrpd/netlink.h +++ b/nhrpd/netlink.h @@ -11,7 +11,6 @@ extern int netlink_nflog_group; extern int netlink_mcast_nflog_group; -int netlink_configure_arp(unsigned int ifindex, int pf); void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma); void netlink_set_nflog_group(int nlgroup); diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index 2e22f8e..be909c8 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -7,6 +7,10 @@ #include "config.h" #endif +#ifdef GNU_LINUX +#include +#endif + #include #include #include @@ -150,6 +154,10 @@ int nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS) struct zapi_neigh_ip api = {}; zclient_neigh_ip_decode(zclient->ibuf, &api); + + if (api.ip_len != IPV4_MAX_BYTELEN && api.ip_len != 0) + return 0; + if (api.ip_in.ipa_type == AF_UNSPEC) return 0; sockunion_family(&addr) = api.ip_in.ipa_type; @@ -172,26 +180,30 @@ int nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS) return 0; debugf(NHRP_DEBUG_KERNEL, "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", - (cmd == ZEBRA_NHRP_NEIGH_GET) - ? "who-has" - : (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? "new-neigh" - : "del-neigh", + (cmd == ZEBRA_NEIGH_GET) ? "who-has" + : (cmd == ZEBRA_NEIGH_ADDED) ? "new-neigh" + : "del-neigh", &addr, ifp->name, &lladdr, ndm_state, c->used, c->cur.type); - if (cmd == ZEBRA_NHRP_NEIGH_GET) { + if (cmd == ZEBRA_NEIGH_GET) { if (c->cur.type >= NHRP_CACHE_CACHED) { nhrp_cache_set_used(c, 1); debugf(NHRP_DEBUG_KERNEL, "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", &addr, ifp->name, &c->cur.remote_nbma_natoa, &c->cur.peer->vc->remote.nbma, &lladdr); + + if (lladdr.sa.sa_family == AF_UNSPEC) + /* nothing from zebra, so use nhrp peer */ + lladdr = c->cur.peer->vc->remote.nbma; + /* In case of shortcuts, nbma is given by lladdr, not * vc->remote.nbma. */ netlink_update_binding(ifp, &addr, &lladdr); } } else { - state = (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? ndm_state - : ZEBRA_NEIGH_STATE_FAILED; + state = (cmd == ZEBRA_NEIGH_ADDED) ? ndm_state + : ZEBRA_NEIGH_STATE_FAILED; nhrp_cache_set_used(c, state == ZEBRA_NEIGH_STATE_REACHABLE); } return 0; diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 1a11e0d..e0b8f7b 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -70,6 +70,8 @@ static void nhrp_cache_free(struct nhrp_cache *c) notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE); assert(!notifier_active(&c->notifier_list)); hash_release(nifp->cache_hash, c); + if (c->cur.peer) + nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier); nhrp_peer_unref(c->cur.peer); nhrp_peer_unref(c->new.peer); EVENT_OFF(c->t_timeout); diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index 7c84fde..7d0ab97 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -259,13 +259,12 @@ static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, struct nhrp_afi_data *if_ad = &nifp->afi[afi]; struct nhrp_cache *nc; struct connected *c, *best; - struct listnode *cnode; union sockunion addr; char buf[PREFIX_STRLEN]; /* Select new best match preferring primary address */ best = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (PREFIX_FAMILY(c->address) != family) continue; if (best == NULL) { @@ -352,6 +351,7 @@ void nhrp_interface_update(struct interface *ifp) if (!if_ad->configured) { os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi)); + nhrp_interface_update_arp(ifp, true); nhrp_send_zebra_configure_arp(ifp, afi2family(afi)); if_ad->configured = 1; nhrp_interface_update_address(ifp, afi, 1); diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 593498c..adb8be3 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -88,10 +88,12 @@ static void nhrp_request_stop(void) nhrp_zebra_terminate(); vici_terminate(); evmgr_terminate(); - nhrp_vc_terminate(); vrf_terminate(); + nhrp_vc_terminate(); debugf(NHRP_DEBUG_COMMON, "Done."); + + resolver_terminate(); frr_fini(); exit(0); @@ -118,15 +120,20 @@ static const struct frr_yang_module_info *const nhrpd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(nhrpd, NHRP, .vty_port = NHRP_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(nhrpd, NHRP, + .vty_port = NHRP_VTY_PORT, + .proghelp = "Implementation of the NHRP routing protocol.", - .proghelp = "Implementation of the NHRP routing protocol.", + .signals = sighandlers, + .n_signals = array_size(sighandlers), - .signals = sighandlers, .n_signals = array_size(sighandlers), + .privs = &nhrpd_privs, - .privs = &nhrpd_privs, .yang_modules = nhrpd_yang_modules, - .n_yang_modules = array_size(nhrpd_yang_modules), + .yang_modules = nhrpd_yang_modules, + .n_yang_modules = array_size(nhrpd_yang_modules), ); +/* clang-format on */ int main(int argc, char **argv) { @@ -155,8 +162,10 @@ int main(int argc, char **argv) nhrp_vc_init(); nhrp_packet_init(); vici_init(); - if_zapi_callbacks(nhrp_ifp_create, nhrp_ifp_up, - nhrp_ifp_down, nhrp_ifp_destroy); + hook_register_prio(if_real, 0, nhrp_ifp_create); + hook_register_prio(if_up, 0, nhrp_ifp_up); + hook_register_prio(if_down, 0, nhrp_ifp_down); + hook_register_prio(if_unreal, 0, nhrp_ifp_destroy); nhrp_zebra_init(); nhrp_shortcut_init(); diff --git a/nhrpd/nhrp_multicast.c b/nhrpd/nhrp_multicast.c index fdc1a31..aead982 100644 --- a/nhrpd/nhrp_multicast.c +++ b/nhrpd/nhrp_multicast.c @@ -7,6 +7,10 @@ #include "config.h" #endif +#ifdef GNU_LINUX +#include +#endif + #include #include #include diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c index 9d0b30c..c6bd3bb 100644 --- a/nhrpd/nhrp_packet.c +++ b/nhrpd/nhrp_packet.c @@ -270,7 +270,7 @@ int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, default: if (type & NHRP_EXTENSION_FLAG_COMPULSORY) goto err; - /* fallthru */ + fallthrough; case NHRP_EXTENSION_FORWARD_TRANSIT_NHS: case NHRP_EXTENSION_REVERSE_TRANSIT_NHS: /* Supported compulsory extensions, and any diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index ffb6cf7..6e7857c 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -45,7 +45,8 @@ static void nhrp_peer_check_delete(struct nhrp_peer *p) EVENT_OFF(p->t_fallback); EVENT_OFF(p->t_timer); - hash_release(nifp->peer_hash, p); + if (nifp->peer_hash) + hash_release(nifp->peer_hash, p); nhrp_interface_notify_del(p->ifp, &p->ifp_notifier); nhrp_vc_notify_del(p->vc, &p->vc_notifier); XFREE(MTYPE_NHRP_PEER, p); @@ -139,7 +140,7 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd) nhrp_peer_vc_notify); __nhrp_peer_check(p); } - /* fallthru */ /* to post config update */ + fallthrough; /* to post config update */ case NOTIFY_INTERFACE_ADDRESS_CHANGED: notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED); break; @@ -1050,7 +1051,7 @@ static void nhrp_peer_forward(struct nhrp_peer *p, * append our selves to the transit NHS list */ goto err; - /* fallthru */ + fallthrough; case NHRP_EXTENSION_RESPONDER_ADDRESS: /* Supported compulsory extensions, and any * non-compulsory that is not explicitly handled, @@ -1220,7 +1221,7 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) /* FIXME: send error-indication */ } } - /* fallthru */ /* FIXME: double check, is this correct? */ + fallthrough; /* FIXME: double check, is this correct? */ case NHRP_ROUTE_OFF_NBMA: if (packet_types[hdr->type].handler) { packet_types[hdr->type].handler(&pp); diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 060e603..fd9090b 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -75,24 +75,6 @@ static void nhrp_route_update_zebra(const struct prefix *p, } } -static void nhrp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg) -{ - struct stream *s; - - if (!zclient || zclient->sock < 0) - return; - - s = zclient->obuf; - stream_reset(s); - - zclient_create_header(s, reg ? ZEBRA_NHRP_NEIGH_REGISTER : - ZEBRA_NHRP_NEIGH_UNREGISTER, - vrf_id); - stream_putw(s, afi); - stream_putw_at(s, 0, stream_get_endp(s)); - zclient_send_message(zclient); -} - void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp) { struct route_node *rn; @@ -368,8 +350,8 @@ static void nhrp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); - nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, true); - nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, true); + zclient_register_neigh(zclient, VRF_DEFAULT, AFI_IP, true); + zclient_register_neigh(zclient, VRF_DEFAULT, AFI_IP6, true); } static zclient_handler *const nhrp_handlers[] = { @@ -377,9 +359,9 @@ static zclient_handler *const nhrp_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_DELETE] = nhrp_interface_address_delete, [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = nhrp_route_read, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = nhrp_route_read, - [ZEBRA_NHRP_NEIGH_ADDED] = nhrp_neighbor_operation, - [ZEBRA_NHRP_NEIGH_REMOVED] = nhrp_neighbor_operation, - [ZEBRA_NHRP_NEIGH_GET] = nhrp_neighbor_operation, + [ZEBRA_NEIGH_ADDED] = nhrp_neighbor_operation, + [ZEBRA_NEIGH_REMOVED] = nhrp_neighbor_operation, + [ZEBRA_NEIGH_GET] = nhrp_neighbor_operation, [ZEBRA_GRE_UPDATE] = nhrp_gre_update, }; @@ -456,10 +438,11 @@ void nhrp_send_zebra_nbr(union sockunion *in, return; s = zclient->obuf; stream_reset(s); - zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : - ZEBRA_NEIGH_IP_DEL, in, out, - ifp, out ? ZEBRA_NEIGH_STATE_REACHABLE - : ZEBRA_NEIGH_STATE_FAILED); + zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : ZEBRA_NEIGH_IP_DEL, + in, out, ifp, + out ? ZEBRA_NEIGH_STATE_REACHABLE + : ZEBRA_NEIGH_STATE_FAILED, + 0); stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(zclient); } @@ -469,10 +452,16 @@ int nhrp_send_zebra_gre_request(struct interface *ifp) return zclient_send_zebra_gre_request(zclient, ifp); } +void nhrp_interface_update_arp(struct interface *ifp, bool arp_enable) +{ + zclient_interface_set_arp(zclient, ifp, arp_enable); +} + + void nhrp_zebra_terminate(void) { - nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, false); - nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, false); + zclient_register_neigh(zclient, VRF_DEFAULT, AFI_IP, false); + zclient_register_neigh(zclient, VRF_DEFAULT, AFI_IP6, false); zclient_stop(zclient); zclient_free(zclient); diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index 2c32014..6f3346c 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -214,4 +214,5 @@ void nhrp_vc_terminate(void) { nhrp_vc_reset(); hash_clean(nhrp_vc_hash, nhrp_vc_free); + hash_free(nhrp_vc_hash); } diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 1421f0f..e389b74 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -16,7 +16,6 @@ DECLARE_MGROUP(NHRPD); #define NHRPD_DEFAULT_HOLDTIME 7200 -#define NHRP_VTY_PORT 2610 #define NHRP_DEFAULT_CONFIG "nhrpd.conf" extern struct event_loop *master; @@ -362,6 +361,7 @@ int sock_open_unix(const char *path); void nhrp_interface_init(void); void nhrp_interface_update(struct interface *ifp); +void nhrp_interface_update_arp(struct interface *ifp, bool arp_enable); void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); void nhrp_interface_update_nbma(struct interface *ifp, struct nhrp_gre_info *gre_info); diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 2f76362..8162ac0 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "frrevent.h" #include "zbuf.h" diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 69c711b..cf5479e 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -731,7 +731,7 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6) DEFUN (area_filter_list, area_filter_list_cmd, - "area filter-list prefix PREFIXLIST_NAME ", + "area filter-list prefix PREFIXLIST6_NAME ", "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" "OSPF6 area ID as a decimal value\n" @@ -774,7 +774,7 @@ DEFUN (area_filter_list, DEFUN (no_area_filter_list, no_area_filter_list_cmd, - "no area filter-list prefix PREFIXLIST_NAME ", + "no area filter-list prefix PREFIXLIST6_NAME ", NO_STR "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 99c3e16..d1c2b8b 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -481,7 +481,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, static int ospf6_ase_forward_address_check(struct ospf6 *ospf6, struct in6_addr *fwd_addr) { - struct listnode *anode, *node, *cnode; + struct listnode *anode, *node; struct ospf6_interface *oi; struct ospf6_area *oa; struct interface *ifp; @@ -494,7 +494,7 @@ static int ospf6_ase_forward_address_check(struct ospf6 *ospf6, continue; ifp = oi->interface; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (IPV6_ADDR_SAME(&c->address->u.prefix6, fwd_addr)) return 0; @@ -1407,12 +1407,11 @@ static void ospf6_external_lsa_fwd_addr_set(struct ospf6 *ospf6, FOR_ALL_INTERFACES (vrf, ifp) { struct ospf6_interface *oi = ifp->info; struct connected *connected; - struct listnode *node; if (!oi || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) continue; - FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { + frr_each (if_connected, ifp->connected, connected) { if (connected->address->family != AF_INET6) continue; if (IN6_IS_ADDR_LINKLOCAL(&connected->address->u.prefix6)) diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c index 10e0092..8d9eff4 100644 --- a/ospf6d/ospf6_auth_trailer.c +++ b/ospf6d/ospf6_auth_trailer.c @@ -4,6 +4,13 @@ */ #include "zebra.h" +#include + +#ifdef CRYPTO_OPENSSL +#include +#include +#endif + #include "config.h" #include "memory.h" #include "ospf6d.h" @@ -23,9 +30,13 @@ #include "ospf6_zebra.h" #include "lib/keychain.h" +#define OSPF6D_COMPAT_AUTHSEQ_NAME "%s/ospf6d-at-seq-no.dat", frr_runstatedir + unsigned char conf_debug_ospf6_auth[2]; DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH_XOR, "OSPF6 auth hash xor"); +static void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6); + /*Apad is the hexadecimal value 0x878FE1F3. */ const uint8_t ospf6_hash_apad_max[KEYCHAIN_MAX_HASH_SIZE] = { 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, @@ -855,23 +866,11 @@ void install_element_ospf6_clear_intf_auth(void) install_element(ENABLE_NODE, &clear_ipv6_ospf6_intf_auth_cmd); } -enum ospf6_auth_err ospf6_auth_nvm_file_exist(void) -{ - struct stat buffer; - int exist; - - exist = stat(OSPF6_AUTH_SEQ_NUM_FILE, &buffer); - if (exist == 0) - return OSPF6_AUTH_FILE_EXIST; - else - return OSPF6_AUTH_FILE_DO_NOT_EXIST; -} - /* * Record in non-volatile memory the given ospf6 process, * authentication trailer higher order sequence number. */ -void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) +static void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) { const char *inst_name; json_object *json; @@ -883,9 +882,7 @@ void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -905,49 +902,82 @@ void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) */ json_object_int_add(json_instance, "sequence_number", ospf6->seqnum_h); - json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } /* * Delete authentication sequence number for a given OSPF6 process * from non-volatile memory. */ -void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6) +__attribute__((unused)) static void +ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6) { const char *inst_name; json_object *json; json_object *json_instances; + json_object *json_instance; zlog_err("Higher order sequence number delete for %s process", ospf6->name); inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); 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_put(json); + return; + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (json_instance) { + json_object_put(json); + return; } - json_object_object_del(json_instances, inst_name); + json_object_object_del(json_instance, "sequence_number"); - json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } +static struct json_object *ospf6_auth_seqno_compat_read(const char *inst_name) +{ + /* try legacy location */ + char compat_path[512]; + json_object *json; + json_object *json_instances = NULL; + json_object *json_instance = NULL; + json_object *json_seqnum = NULL; + + snprintf(compat_path, sizeof(compat_path), OSPF6D_COMPAT_AUTHSEQ_NAME); + json = json_object_from_file(compat_path); + + if (json) + json_object_object_get_ex(json, "instances", &json_instances); + if (json_instances) + json_object_object_get_ex(json_instances, inst_name, + &json_instance); + if (json_instance) + json_object_object_get_ex(json_instance, "sequence_number", + &json_seqnum); + if (json_seqnum) + /* => free the file-level object and still return this */ + json_seqnum = json_object_get(json_seqnum); + + if (json) { + json_object_free(json); + unlink(compat_path); + } + return json_seqnum; +} + /* * Fetch from non-volatile memory the stored ospf6 process * authentication sequence number. */ -void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) +static void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) { const char *inst_name; json_object *json; @@ -957,9 +987,7 @@ void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -976,13 +1004,34 @@ void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) json_object_object_get_ex(json_instance, "sequence_number", &json_seqnum); - ospf6->seqnum_h = json_object_get_int(json_seqnum); + + if (json_seqnum) + /* cf. reference taken in compat_read above */ + json_seqnum = json_object_get(json_seqnum); + else + json_seqnum = ospf6_auth_seqno_compat_read(inst_name); + + ospf6->seqnum_l = 0; + if (json_seqnum) { + ospf6->seqnum_h = json_object_get_int(json_seqnum); + ospf6->seqnum_h += 1; + } else { + ospf6->seqnum_h = 0; + } + + if (json_seqnum) + json_object_put(json_seqnum); zlog_err("Higher order sequence number %d read for %s process %s", ospf6->seqnum_h, ospf6->name, strerror(errno)); - json_object_object_del(json_instances, inst_name); - json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + json_object_object_del(json_instance, "sequence_number"); + + frr_daemon_state_save(&json); +} + +void ospf6_auth_init(struct ospf6 *o) +{ + ospf6_auth_seqno_nvm_read(o); + ospf6_auth_seqno_nvm_update(o); } diff --git a/ospf6d/ospf6_auth_trailer.h b/ospf6d/ospf6_auth_trailer.h index 3f82a7b..9073ae4 100644 --- a/ospf6d/ospf6_auth_trailer.h +++ b/ospf6d/ospf6_auth_trailer.h @@ -48,10 +48,10 @@ enum ospf6_auth_err { OSPF6_AUTH_VALIDATE_SUCCESS = 0, OSPF6_AUTH_VALIDATE_FAILURE, OSPF6_AUTH_PROCESS_NORMAL, - OSPF6_AUTH_FILE_EXIST, - OSPF6_AUTH_FILE_DO_NOT_EXIST }; +void ospf6_auth_init(struct ospf6 *o); + void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length); void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length, unsigned int lls_len); @@ -73,8 +73,5 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, void install_element_ospf6_debug_auth(void); int config_write_ospf6_debug_auth(struct vty *vty); void install_element_ospf6_clear_intf_auth(void); -enum ospf6_auth_err ospf6_auth_nvm_file_exist(void); -void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6); -void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6); -void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6); + #endif /* __OSPF6_AUTH_TRAILER_H__ */ diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index 69230e5..34cb970 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -561,9 +561,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6D_GR_STATE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -591,9 +589,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) json_object_int_add(json_instance, "timestamp", time(NULL) + ospf6->gr_info.grace_period); - json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } /* @@ -608,9 +604,7 @@ void ospf6_gr_nvm_delete(struct ospf6 *ospf6) inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6D_GR_STATE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -620,9 +614,7 @@ void ospf6_gr_nvm_delete(struct ospf6 *ospf6) json_object_object_del(json_instances, inst_name); - json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } /* @@ -641,9 +633,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; - json = json_object_from_file((char *)OSPF6D_GR_STATE); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -687,11 +677,10 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) ospf6->gr_info.grace_period); } - json_object_object_del(json_instances, inst_name); + json_object_object_del(json_instance, "gracePeriod"); + json_object_object_del(json_instance, "timestamp"); - json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, - JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index a79f993..b9dace2 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -49,8 +49,9 @@ DEFINE_HOOK(ospf6_interface_change, unsigned char conf_debug_ospf6_interface = 0; const char *const ospf6_interface_state_str[] = { - "None", "Down", "Loopback", "Waiting", "PointToPoint", - "DROther", "BDR", "DR", NULL}; + "None", "Down", "Loopback", "Waiting", "PointToPoint", + "PtMultipoint", "DROther", "BDR", "DR", NULL +}; int ospf6_interface_neighbor_count(struct ospf6_interface *oi) { @@ -83,8 +84,7 @@ struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex, } /* schedule routing table recalculation */ -static void ospf6_interface_lsdb_hook(struct ospf6_lsa *lsa, - unsigned int reason) +static void ospf6_interface_lsdb_hook(struct ospf6_lsa *lsa, unsigned int reason) { struct ospf6_interface *oi; @@ -227,9 +227,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) iobuflen = ospf6_iobuf_size(ifp->mtu6); if (oi->ifmtu > iobuflen) { if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", - ifp->name, iobuflen); + zlog_debug("Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); oi->ifmtu = iobuflen; } @@ -242,8 +241,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi->lsdb->hook_remove = ospf6_interface_lsdb_hook_remove; oi->lsdb_self = ospf6_lsdb_create(oi); - oi->route_connected = - OSPF6_ROUTE_TABLE_CREATE(INTERFACE, CONNECTED_ROUTES); + oi->route_connected = OSPF6_ROUTE_TABLE_CREATE(INTERFACE, + CONNECTED_ROUTES); oi->route_connected->scope = oi; /* link both */ @@ -344,12 +343,11 @@ void ospf6_interface_disable(struct ospf6_interface *oi) static struct in6_addr * ospf6_interface_get_linklocal_address(struct interface *ifp) { - struct listnode *n; struct connected *c; struct in6_addr *l = (struct in6_addr *)NULL; /* for each connected address */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + frr_each (if_connected, ifp->connected, c) { /* if family not AF_INET6, ignore */ if (c->address->family != AF_INET6) continue; @@ -380,23 +378,21 @@ void ospf6_interface_state_update(struct interface *ifp) iobuflen = ospf6_iobuf_size(ifp->mtu6); if (oi->ifmtu > iobuflen) { if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", - ifp->name, iobuflen); + zlog_debug("Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); oi->ifmtu = iobuflen; } } else if (oi->c_ifmtu > ifp->mtu6) { oi->ifmtu = ifp->mtu6; - zlog_warn( - "Configured mtu %u on %s overridden by kernel %u", - oi->c_ifmtu, ifp->name, ifp->mtu6); + zlog_warn("Configured mtu %u on %s overridden by kernel %u", + oi->c_ifmtu, ifp->name, ifp->mtu6); } else oi->ifmtu = oi->c_ifmtu; } - if (if_is_operative(ifp) - && (ospf6_interface_get_linklocal_address(oi->interface) - || if_is_loopback(oi->interface))) + if (if_is_operative(ifp) && + (ospf6_interface_get_linklocal_address(oi->interface) || + if_is_loopback(oi->interface))) event_execute(master, interface_up, oi, 0, NULL); else event_execute(master, interface_down, oi, 0, NULL); @@ -407,9 +403,7 @@ void ospf6_interface_state_update(struct interface *ifp) void ospf6_interface_connected_route_update(struct interface *ifp) { struct ospf6_interface *oi; - struct ospf6_route *route; struct connected *c; - struct listnode *node, *nnode; struct in6_addr nh_addr; oi = (struct ospf6_interface *)ifp->info; @@ -429,7 +423,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp) /* update "route to advertise" interface route table */ ospf6_route_remove_all(oi->route_connected); - for (ALL_LIST_ELEMENTS(oi->interface->connected, node, nnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (c->address->family != AF_INET6) continue; @@ -453,14 +447,43 @@ void ospf6_interface_connected_route_update(struct interface *ifp) ret = prefix_list_apply(plist, (void *)c->address); if (ret == PREFIX_DENY) { if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "%pFX on %s filtered by prefix-list %s ", - c->address, oi->interface->name, - oi->plist_name); + zlog_debug("%pFX on %s filtered by prefix-list %s ", + c->address, + oi->interface->name, + oi->plist_name); continue; } } + if (oi->state == OSPF6_INTERFACE_LOOPBACK || + oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT || + oi->state == OSPF6_INTERFACE_POINTTOPOINT) { + struct ospf6_route *la_route; + + la_route = ospf6_route_create(oi->area->ospf6); + la_route->prefix = *c->address; + la_route->prefix.prefixlen = 128; + la_route->prefix_options |= OSPF6_PREFIX_OPTION_LA; + + la_route->type = OSPF6_DEST_TYPE_NETWORK; + la_route->path.area_id = oi->area->area_id; + la_route->path.type = OSPF6_PATH_TYPE_INTRA; + la_route->path.cost = 0; + inet_pton(AF_INET6, "::1", &nh_addr); + ospf6_route_add_nexthop(la_route, oi->interface->ifindex, + &nh_addr); + ospf6_route_add(la_route, oi->route_connected); + } + + if (oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT && + !oi->p2xp_connected_pfx_include) + continue; + if (oi->state == OSPF6_INTERFACE_POINTTOPOINT && + oi->p2xp_connected_pfx_exclude) + continue; + + struct ospf6_route *route; + route = ospf6_route_create(oi->area->ospf6); memcpy(&route->prefix, c->address, sizeof(struct prefix)); apply_mask(&route->prefix); @@ -469,8 +492,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp) route->path.type = OSPF6_PATH_TYPE_INTRA; route->path.cost = oi->cost; inet_pton(AF_INET6, "::1", &nh_addr); - ospf6_route_add_nexthop(route, oi->interface->ifindex, - &nh_addr); + ospf6_route_add_nexthop(route, oi->interface->ifindex, &nh_addr); ospf6_route_add(route, oi->route_connected); } @@ -506,17 +528,17 @@ static int ospf6_interface_state_change(uint8_t next_state, ospf6 = oi->area->ospf6; - if ((prev_state == OSPF6_INTERFACE_DR - || prev_state == OSPF6_INTERFACE_BDR) - && (next_state != OSPF6_INTERFACE_DR - && next_state != OSPF6_INTERFACE_BDR)) + if ((prev_state == OSPF6_INTERFACE_DR || + prev_state == OSPF6_INTERFACE_BDR) && + (next_state != OSPF6_INTERFACE_DR && + next_state != OSPF6_INTERFACE_BDR)) ospf6_sso(oi->interface->ifindex, &alldrouters6, IPV6_LEAVE_GROUP, ospf6->fd); - if ((prev_state != OSPF6_INTERFACE_DR - && prev_state != OSPF6_INTERFACE_BDR) - && (next_state == OSPF6_INTERFACE_DR - || next_state == OSPF6_INTERFACE_BDR)) + if ((prev_state != OSPF6_INTERFACE_DR && + prev_state != OSPF6_INTERFACE_BDR) && + (next_state == OSPF6_INTERFACE_DR || + next_state == OSPF6_INTERFACE_BDR)) ospf6_sso(oi->interface->ifindex, &alldrouters6, IPV6_JOIN_GROUP, ospf6->fd); @@ -526,13 +548,17 @@ static int ospf6_interface_state_change(uint8_t next_state, OSPF6_NETWORK_LSA_EXECUTE(oi); OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi); OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); - } else if (prev_state == OSPF6_INTERFACE_DR - || next_state == OSPF6_INTERFACE_DR) { + } else if (prev_state == OSPF6_INTERFACE_DR || + next_state == OSPF6_INTERFACE_DR) { OSPF6_NETWORK_LSA_SCHEDULE(oi); OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); } + if (next_state == OSPF6_INTERFACE_POINTTOPOINT || + next_state == OSPF6_INTERFACE_POINTTOMULTIPOINT) + ospf6_if_p2xp_up(oi); + hook_call(ospf6_interface_change, oi, next_state, prev_state); return 0; @@ -547,8 +573,8 @@ static int ospf6_interface_state_change(uint8_t next_state, static struct ospf6_neighbor *better_bdrouter(struct ospf6_neighbor *a, struct ospf6_neighbor *b) { - if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter == a->router_id) - && (b == NULL || !IS_ELIGIBLE(b) || b->drouter == b->router_id)) + if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter == a->router_id) && + (b == NULL || !IS_ELIGIBLE(b) || b->drouter == b->router_id)) return NULL; else if (a == NULL || !IS_ELIGIBLE(a) || a->drouter == a->router_id) return b; @@ -577,8 +603,8 @@ static struct ospf6_neighbor *better_bdrouter(struct ospf6_neighbor *a, static struct ospf6_neighbor *better_drouter(struct ospf6_neighbor *a, struct ospf6_neighbor *b) { - if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter != a->router_id) - && (b == NULL || !IS_ELIGIBLE(b) || b->drouter != b->router_id)) + if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter != a->router_id) && + (b == NULL || !IS_ELIGIBLE(b) || b->drouter != b->router_id)) return NULL; else if (a == NULL || !IS_ELIGIBLE(a) || a->drouter != a->router_id) return b; @@ -641,10 +667,10 @@ uint8_t dr_election(struct ospf6_interface *oi) drouter = bdrouter; /* the router itself is newly/no longer DR/BDR (4) */ - if ((drouter == &myself && myself.drouter != myself.router_id) - || (drouter != &myself && myself.drouter == myself.router_id) - || (bdrouter == &myself && myself.bdrouter != myself.router_id) - || (bdrouter != &myself && myself.bdrouter == myself.router_id)) { + if ((drouter == &myself && myself.drouter != myself.router_id) || + (drouter != &myself && myself.drouter == myself.router_id) || + (bdrouter == &myself && myself.bdrouter != myself.router_id) || + (bdrouter != &myself && myself.bdrouter == myself.router_id)) { myself.drouter = (drouter ? drouter->router_id : htonl(0)); myself.bdrouter = (bdrouter ? bdrouter->router_id : htonl(0)); @@ -718,8 +744,8 @@ static bool ifmaddr_check(ifindex_t ifindex, struct in6_addr *addr) 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) { + if (sdl->sdl_index == ifindex && + memcmp(&sin6->sin6_addr, addr, IPV6_MAX_BYTELEN) == 0) { found = true; break; } @@ -759,11 +785,10 @@ void interface_up(struct event *thread) } /* check interface has a link-local address */ - if (!(ospf6_interface_get_linklocal_address(oi->interface) - || if_is_loopback(oi->interface))) { - zlog_warn( - "Interface %s has no link local address, can't execute [InterfaceUp]", - oi->interface->name); + if (!(ospf6_interface_get_linklocal_address(oi->interface) || + if_is_loopback(oi->interface))) { + zlog_warn("Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); return; } @@ -780,9 +805,8 @@ void interface_up(struct event *thread) /* If no area assigned, return */ if (oi->area == NULL) { - zlog_warn( - "%s: Not scheduling Hello for %s as there is no area assigned yet", - __func__, oi->interface->name); + zlog_warn("%s: Not scheduling Hello for %s as there is no area assigned yet", + __func__, oi->interface->name); return; } @@ -807,9 +831,8 @@ void interface_up(struct event *thread) * the interface actually left the group. */ if (ifmaddr_check(oi->interface->ifindex, &allspfrouters6)) { - zlog_info( - "Interface %s is still in all routers group, rescheduling for SSO", - oi->interface->name); + zlog_info("Interface %s is still in all routers group, rescheduling for SSO", + oi->interface->name); event_add_timer(master, interface_up, oi, OSPF6_INTERFACE_SSO_RETRY_INT, &oi->thread_sso); return; @@ -820,12 +843,10 @@ void interface_up(struct event *thread) /* Join AllSPFRouters */ if (ospf6_sso(oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP, - ospf6->fd) - < 0) { + ospf6->fd) < 0) { if (oi->sso_try_cnt++ < OSPF6_INTERFACE_SSO_RETRY_MAX) { - zlog_info( - "Scheduling %s for sso retry, trial count: %d", - oi->interface->name, oi->sso_try_cnt); + zlog_info("Scheduling %s for sso retry, trial count: %d", + oi->interface->name, oi->sso_try_cnt); event_add_timer(master, interface_up, oi, OSPF6_INTERFACE_SSO_RETRY_INT, &oi->thread_sso); @@ -838,8 +859,8 @@ void interface_up(struct event *thread) ospf6_interface_connected_route_update(oi->interface); /* Schedule Hello */ - if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE) - && !if_is_loopback(oi->interface)) { + if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE) && + !if_is_loopback(oi->interface)) { event_add_timer(master, ospf6_hello_send, oi, 0, &oi->thread_send_hello); } @@ -849,6 +870,9 @@ void interface_up(struct event *thread) 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->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOMULTIPOINT, + oi); } else if (oi->priority == 0) ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi); else { @@ -899,9 +923,8 @@ void neighbor_change(struct event *thread) zlog_debug("Interface Event %s: [NeighborChange]", oi->interface->name); - if (oi->state == OSPF6_INTERFACE_DROTHER - || oi->state == OSPF6_INTERFACE_BDR - || oi->state == OSPF6_INTERFACE_DR) + if (oi->state == OSPF6_INTERFACE_DROTHER || + oi->state == OSPF6_INTERFACE_BDR || oi->state == OSPF6_INTERFACE_DR) ospf6_interface_state_change(dr_election(oi), oi); } @@ -977,6 +1000,8 @@ static const char *ospf6_iftype_str(uint8_t iftype) return "BROADCAST"; case OSPF_IFTYPE_POINTOPOINT: return "POINTOPOINT"; + case OSPF_IFTYPE_POINTOMULTIPOINT: + return "POINTOMULTIPOINT"; } return "UNKNOWN"; } @@ -988,7 +1013,6 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, struct ospf6_interface *oi; struct connected *c; struct prefix *p; - struct listnode *i; char strbuf[PREFIX2STR_BUFFER], drouter[32], bdrouter[32]; uint8_t default_iftype; struct timeval res, now; @@ -1035,7 +1059,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, if (use_json) { json_arr = json_object_new_array(); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, i, c)) { + frr_each (if_connected, ifp->connected, c) { json_addr = json_object_new_object(); p = c->address; prefix2str(p, strbuf, sizeof(strbuf)); @@ -1067,7 +1091,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, } else { vty_out(vty, " Internet Address:\n"); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, i, c)) { + frr_each (if_connected, ifp->connected, c) { p = c->address; prefix2str(p, strbuf, sizeof(strbuf)); switch (p->family) { @@ -1086,12 +1110,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, if (use_json) { if (oi->area) { - json_object_boolean_true_add(json_obj, - "attachedToArea"); + json_object_boolean_true_add(json_obj, "attachedToArea"); json_object_int_add(json_obj, "instanceId", oi->instance_id); - json_object_int_add(json_obj, "interfaceMtu", - oi->ifmtu); + json_object_int_add(json_obj, "interfaceMtu", oi->ifmtu); json_object_int_add(json_obj, "autoDetect", ifp->mtu6); json_object_string_add(json_obj, "mtuMismatchDetection", oi->mtu_ignore ? "disabled" @@ -1131,9 +1153,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, oi->dead_interval); json_object_int_add(json_obj, "timerIntervalsConfigRetransmit", oi->rxmt_interval); - json_object_boolean_add( - json_obj, "timerPassiveIface", - !!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)); + json_object_boolean_add(json_obj, "timerPassiveIface", + !!CHECK_FLAG(oi->flag, + OSPF6_INTERFACE_PASSIVE)); } else { vty_out(vty, " State %s, Transmit Delay %d sec, Priority %d\n", ospf6_interface_state_str[oi->state], oi->transdelay, @@ -1166,24 +1188,23 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, if (use_json) { timerclear(&res); if (event_is_scheduled(oi->thread_send_lsupdate)) - timersub(&oi->thread_send_lsupdate->u.sands, &now, - &res); + timersub(&oi->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); json_object_int_add(json_obj, "pendingLsaLsUpdateCount", oi->lsupdate_list->count); json_object_string_add(json_obj, "pendingLsaLsUpdateTime", duration); - json_object_string_add( - json_obj, "lsUpdateSendThread", - (event_is_scheduled(oi->thread_send_lsupdate) ? "on" - : "off")); + json_object_string_add(json_obj, "lsUpdateSendThread", + (event_is_scheduled( + oi->thread_send_lsupdate) + ? "on" + : "off")); json_arr = json_object_new_array(); for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) - json_object_array_add( - json_arr, json_object_new_string(lsa->name)); - json_object_object_add(json_obj, "pendingLsaLsUpdate", - json_arr); + json_object_array_add(json_arr, + json_object_new_string(lsa->name)); + json_object_object_add(json_obj, "pendingLsaLsUpdate", json_arr); timerclear(&res); if (event_is_scheduled(oi->thread_send_lsack)) @@ -1194,15 +1215,15 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, oi->lsack_list->count); json_object_string_add(json_obj, "pendingLsaLsAckTime", duration); - json_object_string_add( - json_obj, "lsAckSendThread", - (event_is_scheduled(oi->thread_send_lsack) ? "on" - : "off")); + json_object_string_add(json_obj, "lsAckSendThread", + (event_is_scheduled(oi->thread_send_lsack) + ? "on" + : "off")); json_arr = json_object_new_array(); for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) - json_object_array_add( - json_arr, json_object_new_string(lsa->name)); + json_object_array_add(json_arr, + json_object_new_string(lsa->name)); json_object_object_add(json_obj, "pendingLsaLsAck", json_arr); if (oi->gr.hello_delay.interval != 0) @@ -1211,8 +1232,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, } else { timerclear(&res); if (event_is_scheduled(oi->thread_send_lsupdate)) - timersub(&oi->thread_send_lsupdate->u.sands, &now, - &res); + timersub(&oi->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", @@ -1244,9 +1264,8 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, if (use_json) { struct json_object *json_bfd = json_object_new_object(); - json_object_int_add( - json_bfd, "detectMultiplier", - oi->bfd_config.detection_multiplier); + json_object_int_add(json_bfd, "detectMultiplier", + oi->bfd_config.detection_multiplier); json_object_int_add(json_bfd, "rxMinInterval", oi->bfd_config.min_rx); json_object_int_add(json_bfd, "txMinInterval", @@ -1269,8 +1288,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, OSPF6_AUTH_TRAILER_KEYCHAIN)) { json_object_string_add(json_auth, "authType", "keychain"); - json_object_string_add(json_auth, - "keychainName", + json_object_string_add(json_auth, "keychainName", oi->at_data.keychain); } else if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) @@ -1310,11 +1328,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, /* Find the global address to be used as a forwarding address in NSSA LSA.*/ struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp) { - struct listnode *n; struct connected *c; /* for each connected address */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + frr_each (if_connected, ifp->connected, c) { /* if family not AF_INET6, ignore */ if (c->address->family != AF_INET6) continue; @@ -1332,7 +1349,6 @@ static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id, int idx_ifname, int intf_idx, int json_idx, bool uj) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct interface *ifp; json_object *json; @@ -1462,18 +1478,15 @@ static int ospf6_interface_show_traffic(struct vty *vty, json_object_int_add(json_interface, "lsReqTx", oi->ls_req_out); json_object_int_add(json_interface, - "lsUpdateRx", - oi->ls_upd_in); - json_object_int_add(json_interface, - "lsUpdateTx", + "lsUpdateRx", oi->ls_upd_in); + json_object_int_add(json_interface, "lsUpdateTx", oi->ls_upd_out); json_object_int_add(json_interface, "lsAckRx", oi->ls_ack_in); json_object_int_add(json_interface, "lsAckTx", oi->ls_ack_out); - json_object_object_add(json, - oi->interface->name, + json_object_object_add(json, oi->interface->name, json_interface); } else vty_out(vty, @@ -1659,8 +1672,8 @@ DEFUN(show_ipv6_ospf6_interface_ifname_prefix, } oi = ifp->info; - if (oi == NULL - || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + if (oi == NULL || + CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { vty_out(vty, "Interface %s not attached to area\n", argv[idx_ifname]->arg); @@ -1687,8 +1700,7 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, | []\ >] [json]", SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR - "All VRFs\n" INTERFACE_STR - "Display connected prefixes to advertise\n" + "All VRFs\n" INTERFACE_STR "Display connected prefixes to advertise\n" "Display details of the prefixes\n" OSPF6_ROUTE_ADDRESS_STR OSPF6_ROUTE_PREFIX_STR OSPF6_ROUTE_MATCH_STR "Display details of the prefixes\n" JSON_STR) @@ -1713,9 +1725,9 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, vrf = vrf_lookup_by_id(ospf6->vrf_id); FOR_ALL_INTERFACES (vrf, ifp) { oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL - || CHECK_FLAG(oi->flag, - OSPF6_INTERFACE_DISABLE)) + if (oi == NULL || + CHECK_FLAG(oi->flag, + OSPF6_INTERFACE_DISABLE)) continue; ospf6_route_table_show(vty, idx_prefix, argc, @@ -1790,14 +1802,11 @@ void ospf6_interface_stop(struct ospf6_interface *oi) } /* interface variable set command */ -DEFUN (ipv6_ospf6_area, - ipv6_ospf6_area_cmd, - "ipv6 ospf6 area ", - IP6_STR - OSPF6_STR - "Specify the OSPF6 area ID\n" - "OSPF6 area ID in IPv4 address notation\n" - "OSPF6 area ID in decimal notation\n") +DEFUN(ipv6_ospf6_area, ipv6_ospf6_area_cmd, + "ipv6 ospf6 area ", + IP6_STR OSPF6_STR "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -1831,15 +1840,11 @@ DEFUN (ipv6_ospf6_area, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_area, - no_ipv6_ospf6_area_cmd, - "no ipv6 ospf6 area []", - NO_STR - IP6_STR - OSPF6_STR - "Specify the OSPF6 area ID\n" - "OSPF6 area ID in IPv4 address notation\n" - "OSPF6 area ID in decimal notation\n") +DEFUN(no_ipv6_ospf6_area, no_ipv6_ospf6_area_cmd, + "no ipv6 ospf6 area []", + NO_STR IP6_STR OSPF6_STR "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -1859,14 +1864,9 @@ DEFUN (no_ipv6_ospf6_area, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_ifmtu, - ipv6_ospf6_ifmtu_cmd, - "ipv6 ospf6 ifmtu (1-65535)", - IP6_STR - OSPF6_STR - "Interface MTU\n" - "OSPFv3 Interface MTU\n" - ) +DEFUN(ipv6_ospf6_ifmtu, ipv6_ospf6_ifmtu_cmd, "ipv6 ospf6 ifmtu (1-65535)", + IP6_STR OSPF6_STR "Interface MTU\n" + "OSPFv3 Interface MTU\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -1915,15 +1915,10 @@ DEFUN (ipv6_ospf6_ifmtu, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_ifmtu, - no_ipv6_ospf6_ifmtu_cmd, - "no ipv6 ospf6 ifmtu [(1-65535)]", - NO_STR - IP6_STR - OSPF6_STR - "Interface MTU\n" - "OSPFv3 Interface MTU\n" - ) +DEFUN(no_ipv6_ospf6_ifmtu, no_ipv6_ospf6_ifmtu_cmd, + "no ipv6 ospf6 ifmtu [(1-65535)]", + NO_STR IP6_STR OSPF6_STR "Interface MTU\n" + "OSPFv3 Interface MTU\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -1961,13 +1956,9 @@ DEFUN (no_ipv6_ospf6_ifmtu, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_cost, - ipv6_ospf6_cost_cmd, - "ipv6 ospf6 cost (1-65535)", - IP6_STR - OSPF6_STR - "Interface cost\n" - "Outgoing metric of this interface\n") +DEFUN(ipv6_ospf6_cost, ipv6_ospf6_cost_cmd, "ipv6 ospf6 cost (1-65535)", + IP6_STR OSPF6_STR "Interface cost\n" + "Outgoing metric of this interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -1998,14 +1989,10 @@ DEFUN (ipv6_ospf6_cost, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_cost, - no_ipv6_ospf6_cost_cmd, - "no ipv6 ospf6 cost [(1-65535)]", - NO_STR - IP6_STR - OSPF6_STR - "Calculate interface cost from bandwidth\n" - "Outgoing metric of this interface\n") +DEFUN(no_ipv6_ospf6_cost, no_ipv6_ospf6_cost_cmd, + "no ipv6 ospf6 cost [(1-65535)]", + NO_STR IP6_STR OSPF6_STR "Calculate interface cost from bandwidth\n" + "Outgoing metric of this interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2023,12 +2010,11 @@ DEFUN (no_ipv6_ospf6_cost, return CMD_SUCCESS; } -DEFUN (auto_cost_reference_bandwidth, - auto_cost_reference_bandwidth_cmd, - "auto-cost reference-bandwidth (1-4294967)", - "Calculate OSPF interface cost according to bandwidth\n" - "Use reference bandwidth method to assign OSPF cost\n" - "The reference bandwidth in terms of Mbits per second\n") +DEFUN(auto_cost_reference_bandwidth, auto_cost_reference_bandwidth_cmd, + "auto-cost reference-bandwidth (1-4294967)", + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") { VTY_DECLVAR_CONTEXT(ospf6, o); int idx_number = 2; @@ -2055,13 +2041,11 @@ DEFUN (auto_cost_reference_bandwidth, return CMD_SUCCESS; } -DEFUN (no_auto_cost_reference_bandwidth, - no_auto_cost_reference_bandwidth_cmd, - "no auto-cost reference-bandwidth [(1-4294967)]", - NO_STR - "Calculate OSPF interface cost according to bandwidth\n" - "Use reference bandwidth method to assign OSPF cost\n" - "The reference bandwidth in terms of Mbits per second\n") +DEFUN(no_auto_cost_reference_bandwidth, no_auto_cost_reference_bandwidth_cmd, + "no auto-cost reference-bandwidth [(1-4294967)]", + NO_STR "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") { VTY_DECLVAR_CONTEXT(ospf6, o); struct ospf6_area *oa; @@ -2080,11 +2064,10 @@ DEFUN (no_auto_cost_reference_bandwidth, } -DEFUN (ospf6_write_multiplier, - ospf6_write_multiplier_cmd, - "write-multiplier (1-100)", - "Write multiplier\n" - "Maximum number of interface serviced per write\n") +DEFUN(ospf6_write_multiplier, ospf6_write_multiplier_cmd, + "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") { VTY_DECLVAR_CONTEXT(ospf6, o); uint32_t write_oi_count; @@ -2099,12 +2082,10 @@ DEFUN (ospf6_write_multiplier, return CMD_SUCCESS; } -DEFUN (no_ospf6_write_multiplier, - no_ospf6_write_multiplier_cmd, - "no write-multiplier (1-100)", - NO_STR - "Write multiplier\n" - "Maximum number of interface serviced per write\n") +DEFUN(no_ospf6_write_multiplier, no_ospf6_write_multiplier_cmd, + "no write-multiplier (1-100)", + NO_STR "Write multiplier\n" + "Maximum number of interface serviced per write\n") { VTY_DECLVAR_CONTEXT(ospf6, o); @@ -2112,13 +2093,9 @@ DEFUN (no_ospf6_write_multiplier, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_hellointerval, - ipv6_ospf6_hellointerval_cmd, - "ipv6 ospf6 hello-interval (1-65535)", - IP6_STR - OSPF6_STR - "Time between HELLO packets\n" - SECONDS_STR) +DEFUN(ipv6_ospf6_hellointerval, ipv6_ospf6_hellointerval_cmd, + "ipv6 ospf6 hello-interval (1-65535)", + IP6_STR OSPF6_STR "Time between HELLO packets\n" SECONDS_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2146,23 +2123,15 @@ DEFUN (ipv6_ospf6_hellointerval, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_hellointerval, - no_ipv6_ospf6_hellointerval_cmd, - "no ipv6 ospf6 hello-interval [(1-65535)]", - NO_STR - IP6_STR - OSPF6_STR - "Time between HELLO packets\n" - SECONDS_STR) +ALIAS(ipv6_ospf6_hellointerval, no_ipv6_ospf6_hellointerval_cmd, + "no ipv6 ospf6 hello-interval [(1-65535)]", + NO_STR IP6_STR OSPF6_STR "Time between HELLO packets\n" SECONDS_STR) /* interface variable set command */ -DEFUN (ipv6_ospf6_deadinterval, - ipv6_ospf6_deadinterval_cmd, - "ipv6 ospf6 dead-interval (1-65535)", - IP6_STR - OSPF6_STR - "Interval time after which a neighbor is declared down\n" - SECONDS_STR) +DEFUN(ipv6_ospf6_deadinterval, ipv6_ospf6_deadinterval_cmd, + "ipv6 ospf6 dead-interval (1-65535)", + IP6_STR OSPF6_STR + "Interval time after which a neighbor is declared down\n" SECONDS_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2180,22 +2149,16 @@ DEFUN (ipv6_ospf6_deadinterval, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_deadinterval, - no_ipv6_ospf6_deadinterval_cmd, - "no ipv6 ospf6 dead-interval [(1-65535)]", - NO_STR - IP6_STR - OSPF6_STR - "Interval time after which a neighbor is declared down\n" - SECONDS_STR) +ALIAS(ipv6_ospf6_deadinterval, no_ipv6_ospf6_deadinterval_cmd, + "no ipv6 ospf6 dead-interval [(1-65535)]", + NO_STR IP6_STR OSPF6_STR + "Interval time after which a neighbor is declared down\n" SECONDS_STR) DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd, "ipv6 ospf6 graceful-restart hello-delay (1-1800)", - IP6_STR - OSPF6_STR - "Graceful Restart parameters\n" - "Delay the sending of the first hello packets.\n" - "Delay in seconds\n") + IP6_STR OSPF6_STR "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2212,12 +2175,9 @@ DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd, DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd, "no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]", - NO_STR - IP6_STR - OSPF6_STR - "Graceful Restart parameters\n" - "Delay the sending of the first hello packets.\n" - "Delay in seconds\n") + NO_STR IP6_STR OSPF6_STR "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2234,13 +2194,9 @@ DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd, } /* interface variable set command */ -DEFUN (ipv6_ospf6_transmitdelay, - ipv6_ospf6_transmitdelay_cmd, - "ipv6 ospf6 transmit-delay (1-3600)", - IP6_STR - OSPF6_STR - "Link state transmit delay\n" - SECONDS_STR) +DEFUN(ipv6_ospf6_transmitdelay, ipv6_ospf6_transmitdelay_cmd, + "ipv6 ospf6 transmit-delay (1-3600)", + IP6_STR OSPF6_STR "Link state transmit delay\n" SECONDS_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2258,23 +2214,15 @@ DEFUN (ipv6_ospf6_transmitdelay, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_transmitdelay, - no_ipv6_ospf6_transmitdelay_cmd, - "no ipv6 ospf6 transmit-delay [(1-3600)]", - NO_STR - IP6_STR - OSPF6_STR - "Link state transmit delay\n" - SECONDS_STR) +ALIAS(ipv6_ospf6_transmitdelay, no_ipv6_ospf6_transmitdelay_cmd, + "no ipv6 ospf6 transmit-delay [(1-3600)]", + NO_STR IP6_STR OSPF6_STR "Link state transmit delay\n" SECONDS_STR) /* interface variable set command */ -DEFUN (ipv6_ospf6_retransmitinterval, - ipv6_ospf6_retransmitinterval_cmd, - "ipv6 ospf6 retransmit-interval (1-65535)", - IP6_STR - OSPF6_STR - "Time between retransmitting lost link state advertisements\n" - SECONDS_STR) +DEFUN(ipv6_ospf6_retransmitinterval, ipv6_ospf6_retransmitinterval_cmd, + "ipv6 ospf6 retransmit-interval (1-65535)", + IP6_STR OSPF6_STR + "Time between retransmitting lost link state advertisements\n" SECONDS_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2292,23 +2240,16 @@ DEFUN (ipv6_ospf6_retransmitinterval, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_retransmitinterval, - no_ipv6_ospf6_retransmitinterval_cmd, - "no ipv6 ospf6 retransmit-interval [(1-65535)]", - NO_STR - IP6_STR - OSPF6_STR - "Time between retransmitting lost link state advertisements\n" - SECONDS_STR) +ALIAS(ipv6_ospf6_retransmitinterval, no_ipv6_ospf6_retransmitinterval_cmd, + "no ipv6 ospf6 retransmit-interval [(1-65535)]", + NO_STR IP6_STR OSPF6_STR + "Time between retransmitting lost link state advertisements\n" SECONDS_STR) /* interface variable set command */ -DEFUN (ipv6_ospf6_priority, - ipv6_ospf6_priority_cmd, - "ipv6 ospf6 priority (0-255)", - IP6_STR - OSPF6_STR - "Router priority\n" - "Priority value\n") +DEFUN(ipv6_ospf6_priority, ipv6_ospf6_priority_cmd, + "ipv6 ospf6 priority (0-255)", + IP6_STR OSPF6_STR "Router priority\n" + "Priority value\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2324,10 +2265,9 @@ DEFUN (ipv6_ospf6_priority, ? OSPF6_INTERFACE_PRIORITY : strtoul(argv[idx_number]->arg, NULL, 10); - if (oi->area - && (oi->state == OSPF6_INTERFACE_DROTHER - || oi->state == OSPF6_INTERFACE_BDR - || oi->state == OSPF6_INTERFACE_DR)) { + if (oi->area && (oi->state == OSPF6_INTERFACE_DROTHER || + oi->state == OSPF6_INTERFACE_BDR || + oi->state == OSPF6_INTERFACE_DR)) { if (ospf6_interface_state_change(dr_election(oi), oi) == -1) OSPF6_LINK_LSA_SCHEDULE(oi); } @@ -2335,22 +2275,15 @@ DEFUN (ipv6_ospf6_priority, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_priority, - no_ipv6_ospf6_priority_cmd, - "no ipv6 ospf6 priority [(0-255)]", - NO_STR - IP6_STR - OSPF6_STR - "Router priority\n" - "Priority value\n") +ALIAS(ipv6_ospf6_priority, no_ipv6_ospf6_priority_cmd, + "no ipv6 ospf6 priority [(0-255)]", + NO_STR IP6_STR OSPF6_STR "Router priority\n" + "Priority value\n") -DEFUN (ipv6_ospf6_instance, - ipv6_ospf6_instance_cmd, - "ipv6 ospf6 instance-id (0-255)", - IP6_STR - OSPF6_STR - "Instance ID for this interface\n" - "Instance ID value\n") +DEFUN(ipv6_ospf6_instance, ipv6_ospf6_instance_cmd, + "ipv6 ospf6 instance-id (0-255)", + IP6_STR OSPF6_STR "Instance ID for this interface\n" + "Instance ID value\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -2368,22 +2301,14 @@ DEFUN (ipv6_ospf6_instance, return CMD_SUCCESS; } -ALIAS (ipv6_ospf6_instance, - no_ipv6_ospf6_instance_cmd, - "no ipv6 ospf6 instance-id [(0-255)]", - NO_STR - IP6_STR - OSPF6_STR - "Instance ID for this interface\n" - "Instance ID value\n") +ALIAS(ipv6_ospf6_instance, no_ipv6_ospf6_instance_cmd, + "no ipv6 ospf6 instance-id [(0-255)]", + NO_STR IP6_STR OSPF6_STR "Instance ID for this interface\n" + "Instance ID value\n") -DEFUN (ipv6_ospf6_passive, - ipv6_ospf6_passive_cmd, - "ipv6 ospf6 passive", - IP6_STR - OSPF6_STR - "Passive interface; no adjacency will be formed on this interface\n" - ) +DEFUN(ipv6_ospf6_passive, ipv6_ospf6_passive_cmd, "ipv6 ospf6 passive", + IP6_STR OSPF6_STR + "Passive interface; no adjacency will be formed on this interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2409,14 +2334,9 @@ DEFUN (ipv6_ospf6_passive, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_passive, - no_ipv6_ospf6_passive_cmd, - "no ipv6 ospf6 passive", - NO_STR - IP6_STR - OSPF6_STR - "passive interface: No Adjacency will be formed on this I/F\n" - ) +DEFUN(no_ipv6_ospf6_passive, no_ipv6_ospf6_passive_cmd, "no ipv6 ospf6 passive", + NO_STR IP6_STR OSPF6_STR + "passive interface: No Adjacency will be formed on this I/F\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2439,13 +2359,8 @@ DEFUN (no_ipv6_ospf6_passive, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_mtu_ignore, - ipv6_ospf6_mtu_ignore_cmd, - "ipv6 ospf6 mtu-ignore", - IP6_STR - OSPF6_STR - "Disable MTU mismatch detection on this interface\n" - ) +DEFUN(ipv6_ospf6_mtu_ignore, ipv6_ospf6_mtu_ignore_cmd, "ipv6 ospf6 mtu-ignore", + IP6_STR OSPF6_STR "Disable MTU mismatch detection on this interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2461,14 +2376,10 @@ DEFUN (ipv6_ospf6_mtu_ignore, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_mtu_ignore, - no_ipv6_ospf6_mtu_ignore_cmd, - "no ipv6 ospf6 mtu-ignore", - NO_STR - IP6_STR - OSPF6_STR - "Disable MTU mismatch detection on this interface\n" - ) +DEFUN(no_ipv6_ospf6_mtu_ignore, no_ipv6_ospf6_mtu_ignore_cmd, + "no ipv6 ospf6 mtu-ignore", + NO_STR IP6_STR OSPF6_STR + "Disable MTU mismatch detection on this interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2484,15 +2395,11 @@ DEFUN (no_ipv6_ospf6_mtu_ignore, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_advertise_prefix_list, - ipv6_ospf6_advertise_prefix_list_cmd, - "ipv6 ospf6 advertise prefix-list WORD", - IP6_STR - OSPF6_STR - "Advertising options\n" - "Filter prefix using prefix-list\n" - "Prefix list name\n" - ) +DEFUN(ipv6_ospf6_advertise_prefix_list, ipv6_ospf6_advertise_prefix_list_cmd, + "ipv6 ospf6 advertise prefix-list PREFIXLIST6_NAME", + IP6_STR OSPF6_STR "Advertising options\n" + "Filter prefix using prefix-list\n" + "Prefix list name\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_word = 4; @@ -2522,15 +2429,12 @@ DEFUN (ipv6_ospf6_advertise_prefix_list, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_advertise_prefix_list, - no_ipv6_ospf6_advertise_prefix_list_cmd, - "no ipv6 ospf6 advertise prefix-list [WORD]", - NO_STR - IP6_STR - OSPF6_STR - "Advertising options\n" - "Filter prefix using prefix-list\n" - "Prefix list name\n") +DEFUN(no_ipv6_ospf6_advertise_prefix_list, + no_ipv6_ospf6_advertise_prefix_list_cmd, + "no ipv6 ospf6 advertise prefix-list [PREFIXLIST6_NAME]", + NO_STR IP6_STR OSPF6_STR "Advertising options\n" + "Filter prefix using prefix-list\n" + "Prefix list name\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2558,15 +2462,12 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_list, return CMD_SUCCESS; } -DEFUN (ipv6_ospf6_network, - ipv6_ospf6_network_cmd, - "ipv6 ospf6 network ", - IP6_STR - OSPF6_STR - "Network type\n" - "Specify OSPF6 broadcast network\n" - "Specify OSPF6 point-to-point network\n" - ) +DEFUN(ipv6_ospf6_network, ipv6_ospf6_network_cmd, + "ipv6 ospf6 network ", + IP6_STR OSPF6_STR "Network type\n" + "Specify OSPF6 broadcast network\n" + "Specify OSPF6 point-to-point network\n" + "Specify OSPF6 point-to-multipoint network\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_network = 3; @@ -2591,6 +2492,11 @@ DEFUN (ipv6_ospf6_network, return CMD_SUCCESS; } oi->type = OSPF_IFTYPE_POINTOPOINT; + } else if (strncmp(argv[idx_network]->arg, "point-to-m", 10) == 0) { + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + return CMD_SUCCESS; + } + oi->type = OSPF_IFTYPE_POINTOMULTIPOINT; } /* Reset the interface */ @@ -2600,15 +2506,11 @@ DEFUN (ipv6_ospf6_network, return CMD_SUCCESS; } -DEFUN (no_ipv6_ospf6_network, - no_ipv6_ospf6_network_cmd, - "no ipv6 ospf6 network []", - NO_STR - IP6_STR - OSPF6_STR - "Set default network type\n" - "Specify OSPF6 broadcast network\n" - "Specify OSPF6 point-to-point network\n") +DEFUN(no_ipv6_ospf6_network, no_ipv6_ospf6_network_cmd, + "no ipv6 ospf6 network []", + NO_STR IP6_STR OSPF6_STR "Set default network type\n" + "Specify OSPF6 broadcast network\n" + "Specify OSPF6 point-to-point network\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -2617,9 +2519,8 @@ DEFUN (no_ipv6_ospf6_network, assert(ifp); oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) { + if (oi == NULL) return CMD_SUCCESS; - } oi->type_cfg = false; @@ -2636,6 +2537,95 @@ DEFUN (no_ipv6_ospf6_network, return CMD_SUCCESS; } +DEFPY(ipv6_ospf6_p2xp_only_cfg_neigh, ipv6_ospf6_p2xp_only_cfg_neigh_cmd, + "[no] ipv6 ospf6 p2p-p2mp config-neighbors-only", + NO_STR IP6_STR OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Only form adjacencies with explicitly configured neighbors\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + + if (no) { + if (!oi) + return CMD_SUCCESS; + + oi->p2xp_only_cfg_neigh = false; + return CMD_SUCCESS; + } + + if (!oi) + oi = ospf6_interface_create(ifp); + + oi->p2xp_only_cfg_neigh = true; + return CMD_SUCCESS; +} + +DEFPY(ipv6_ospf6_p2xp_no_multicast_hello, ipv6_ospf6_p2xp_no_multicast_hello_cmd, + "[no] ipv6 ospf6 p2p-p2mp disable-multicast-hello", + NO_STR IP6_STR OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Do not send multicast hellos\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + + if (no) { + if (!oi) + return CMD_SUCCESS; + + oi->p2xp_no_multicast_hello = false; + return CMD_SUCCESS; + } + + if (!oi) + oi = ospf6_interface_create(ifp); + + oi->p2xp_no_multicast_hello = true; + return CMD_SUCCESS; +} + +DEFPY(ipv6_ospf6_p2xp_connected_pfx, ipv6_ospf6_p2xp_connected_pfx_cmd, + "[no] ipv6 ospf6 p2p-p2mp connected-prefixes ", + NO_STR IP6_STR OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Adjust handling of directly connected prefixes\n" + "Advertise prefixes and own /128 (default for PtP)\n" + "Ignore, only advertise own /128 (default for PtMP)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + bool old_incl, old_excl; + + if (no && !oi) + return CMD_SUCCESS; + + if (!oi) + oi = ospf6_interface_create(ifp); + + old_incl = oi->p2xp_connected_pfx_include; + old_excl = oi->p2xp_connected_pfx_exclude; + oi->p2xp_connected_pfx_include = false; + oi->p2xp_connected_pfx_exclude = false; + + if (incl && !no) + oi->p2xp_connected_pfx_include = true; + if (excl && !no) + oi->p2xp_connected_pfx_exclude = true; + + if (oi->p2xp_connected_pfx_include != old_incl || + oi->p2xp_connected_pfx_exclude != old_excl) + ospf6_interface_connected_route_update(ifp); + return CMD_SUCCESS; +} + +ALIAS(ipv6_ospf6_p2xp_connected_pfx, no_ipv6_ospf6_p2xp_connected_pfx_cmd, + "no ipv6 ospf6 p2p-p2mp connected-prefixes", + NO_STR IP6_STR OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Adjust handling of directly connected prefixes\n") + + static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { struct ospf6_interface *oi; @@ -2695,7 +2685,10 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) if (oi->mtu_ignore) vty_out(vty, " ipv6 ospf6 mtu-ignore\n"); - if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) + if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + vty_out(vty, + " ipv6 ospf6 network point-to-multipoint\n"); + else if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) vty_out(vty, " ipv6 ospf6 network point-to-point\n"); else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) vty_out(vty, " ipv6 ospf6 network broadcast\n"); @@ -2704,7 +2697,22 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) vty_out(vty, " ipv6 ospf6 graceful-restart hello-delay %u\n", oi->gr.hello_delay.interval); + if (oi->p2xp_only_cfg_neigh) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp config-neighbors-only\n"); + if (oi->p2xp_no_multicast_hello) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp disable-multicast-hello\n"); + + if (oi->p2xp_connected_pfx_include) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp connected-prefixes include\n"); + else if (oi->p2xp_connected_pfx_exclude) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp connected-prefixes exclude\n"); + + config_write_ospf6_p2xp_neighbor(vty, oi); ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); @@ -2742,10 +2750,10 @@ static int ospf6_ifp_create(struct interface *ifp) static int ospf6_ifp_up(struct interface *ifp) { if (IS_OSPF6_DEBUG_ZEBRA(RECV)) - zlog_debug( - "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu6, ifp->bandwidth); + zlog_debug("Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, + ifp->mtu6, ifp->bandwidth); ospf6_interface_state_update(ifp); @@ -2755,10 +2763,10 @@ static int ospf6_ifp_up(struct interface *ifp) static int ospf6_ifp_down(struct interface *ifp) { if (IS_OSPF6_DEBUG_ZEBRA(RECV)) - zlog_debug( - "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu6, ifp->bandwidth); + zlog_debug("Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, + ifp->mtu6, ifp->bandwidth); ospf6_interface_state_update(ifp); @@ -2785,13 +2793,14 @@ void ospf6_interface_init(void) { /* Install interface node. */ if_cmd_init(config_write_interface); - if_zapi_callbacks(ospf6_ifp_create, ospf6_ifp_up, - ospf6_ifp_down, ospf6_ifp_destroy); + hook_register_prio(if_real, 0, ospf6_ifp_create); + hook_register_prio(if_up, 0, ospf6_ifp_up); + hook_register_prio(if_down, 0, ospf6_ifp_down); + hook_register_prio(if_unreal, 0, ospf6_ifp_destroy); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd); - install_element(VIEW_NODE, - &show_ipv6_ospf6_interface_ifname_prefix_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_traffic_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_area_cmd); @@ -2829,6 +2838,11 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_network_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_only_cfg_neigh_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_no_multicast_hello_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_connected_pfx_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_p2xp_connected_pfx_cmd); + /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); @@ -2859,16 +2873,9 @@ void ospf6_interface_clear(struct interface *ifp) } /* Clear interface */ -DEFUN (clear_ipv6_ospf6_interface, - clear_ipv6_ospf6_interface_cmd, - "clear ipv6 ospf6 [vrf NAME] interface [IFNAME]", - CLEAR_STR - IP6_STR - OSPF6_STR - VRF_CMD_HELP_STR - INTERFACE_STR - IFNAME_STR - ) +DEFUN(clear_ipv6_ospf6_interface, clear_ipv6_ospf6_interface_cmd, + "clear ipv6 ospf6 [vrf NAME] interface [IFNAME]", + CLEAR_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR INTERFACE_STR IFNAME_STR) { struct vrf *vrf; int idx_vrf = 3; @@ -2909,26 +2916,16 @@ void install_element_ospf6_clear_interface(void) install_element(ENABLE_NODE, &clear_ipv6_ospf6_interface_cmd); } -DEFUN (debug_ospf6_interface, - debug_ospf6_interface_cmd, - "debug ospf6 interface", - DEBUG_STR - OSPF6_STR - "Debug OSPFv3 Interface\n" - ) +DEFUN(debug_ospf6_interface, debug_ospf6_interface_cmd, "debug ospf6 interface", + DEBUG_STR OSPF6_STR "Debug OSPFv3 Interface\n") { OSPF6_DEBUG_INTERFACE_ON(); return CMD_SUCCESS; } -DEFUN (no_debug_ospf6_interface, - no_debug_ospf6_interface_cmd, - "no debug ospf6 interface", - NO_STR - DEBUG_STR - OSPF6_STR - "Debug OSPFv3 Interface\n" - ) +DEFUN(no_debug_ospf6_interface, no_debug_ospf6_interface_cmd, + "no debug ospf6 interface", + NO_STR DEBUG_STR OSPF6_STR "Debug OSPFv3 Interface\n") { OSPF6_DEBUG_INTERFACE_OFF(); return CMD_SUCCESS; @@ -2965,10 +2962,9 @@ void ospf6_auth_write_config(struct vty *vty, struct ospf6_auth_data *at_data) DEFUN(ipv6_ospf6_intf_auth_trailer_keychain, ipv6_ospf6_intf_auth_trailer_keychain_cmd, "ipv6 ospf6 authentication keychain KEYCHAIN_NAME", - IP6_STR OSPF6_STR - "Enable authentication on this interface\n" - "Keychain\n" - "Keychain name\n") + IP6_STR OSPF6_STR "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int keychain_idx = 4; @@ -2989,8 +2985,8 @@ DEFUN(ipv6_ospf6_intf_auth_trailer_keychain, if (oi->at_data.keychain) XFREE(MTYPE_OSPF6_AUTH_KEYCHAIN, oi->at_data.keychain); - oi->at_data.keychain = - XSTRDUP(MTYPE_OSPF6_AUTH_KEYCHAIN, argv[keychain_idx]->arg); + oi->at_data.keychain = XSTRDUP(MTYPE_OSPF6_AUTH_KEYCHAIN, + argv[keychain_idx]->arg); return CMD_SUCCESS; } @@ -2998,10 +2994,9 @@ DEFUN(ipv6_ospf6_intf_auth_trailer_keychain, DEFUN(no_ipv6_ospf6_intf_auth_trailer_keychain, no_ipv6_ospf6_intf_auth_trailer_keychain_cmd, "no ipv6 ospf6 authentication keychain [KEYCHAIN_NAME]", - NO_STR IP6_STR OSPF6_STR - "Enable authentication on this interface\n" - "Keychain\n" - "Keychain name\n") + NO_STR IP6_STR OSPF6_STR "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; @@ -3027,18 +3022,17 @@ DEFUN(ipv6_ospf6_intf_auth_trailer_key, ipv6_ospf6_intf_auth_trailer_key_cmd, "ipv6 ospf6 authentication key-id (1-65535) hash-algo " " " "key WORD", - IP6_STR OSPF6_STR - "Authentication\n" - "Key ID\n" - "Key ID value\n" - "Cryptographic-algorithm\n" - "Use MD5 algorithm\n" - "Use HMAC-SHA-1 algorithm\n" - "Use HMAC-SHA-256 algorithm\n" - "Use HMAC-SHA-384 algorithm\n" - "Use HMAC-SHA-512 algorithm\n" - "Password\n" - "Password string (key)\n") + IP6_STR OSPF6_STR "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int key_id_idx = 4; @@ -3072,8 +3066,8 @@ DEFUN(ipv6_ospf6_intf_auth_trailer_key, ipv6_ospf6_intf_auth_trailer_key_cmd, oi->at_data.key_id = (uint16_t)strtol(argv[key_id_idx]->arg, NULL, 10); if (oi->at_data.auth_key) XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); - oi->at_data.auth_key = - XSTRDUP(MTYPE_OSPF6_AUTH_MANUAL_KEY, argv[password_idx]->arg); + oi->at_data.auth_key = XSTRDUP(MTYPE_OSPF6_AUTH_MANUAL_KEY, + argv[password_idx]->arg); return CMD_SUCCESS; } @@ -3083,18 +3077,17 @@ DEFUN(no_ipv6_ospf6_intf_auth_trailer_key, "no ipv6 ospf6 authentication key-id [(1-65535) hash-algo " " " "key WORD]", - NO_STR IP6_STR OSPF6_STR - "Authentication\n" - "Key ID\n" - "Key ID value\n" - "Cryptographic-algorithm\n" - "Use MD5 algorithm\n" - "Use HMAC-SHA-1 algorithm\n" - "Use HMAC-SHA-256 algorithm\n" - "Use HMAC-SHA-384 algorithm\n" - "Use HMAC-SHA-512 algorithm\n" - "Password\n" - "Password string (key)\n") + NO_STR IP6_STR OSPF6_STR "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf6_interface *oi; diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 5942df0..46a7c90 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -34,6 +34,25 @@ struct ospf6_auth_data { uint32_t rx_drop; /* Pkt drop due to auth fail while reading */ }; +PREDECL_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs); + +struct ospf6_if_p2xp_neighcfg { + struct ospf6_if_p2xp_neighcfgs_item item; + + struct ospf6_interface *ospf6_if; + struct in6_addr addr; + + bool cfg_cost : 1; + + uint32_t cost; + uint16_t poll_interval; + + /* NULL if down */ + struct ospf6_neighbor *active; + + struct event *t_unicast_hello; +}; + /* Interface structure */ struct ospf6_interface { /* IF info from zebra */ @@ -66,6 +85,20 @@ struct ospf6_interface { uint8_t type; bool type_cfg; + /* P2P/P2MP behavior: */ + + /* disable hellos on standard multicast? */ + bool p2xp_no_multicast_hello; + /* only allow explicitly configured neighbors? */ + bool p2xp_only_cfg_neigh; + /* override mode default for advertising connected prefixes. + * both false by default (= do include for PtP, exclude for PtMP) + */ + bool p2xp_connected_pfx_include; + bool p2xp_connected_pfx_exclude; + + struct ospf6_if_p2xp_neighcfgs_head p2xp_neighs; + /* Router Priority */ uint8_t priority; @@ -171,15 +204,16 @@ struct ospf6_interface { DECLARE_QOBJ_TYPE(ospf6_interface); /* interface state */ -#define OSPF6_INTERFACE_NONE 0 -#define OSPF6_INTERFACE_DOWN 1 -#define OSPF6_INTERFACE_LOOPBACK 2 -#define OSPF6_INTERFACE_WAITING 3 -#define OSPF6_INTERFACE_POINTTOPOINT 4 -#define OSPF6_INTERFACE_DROTHER 5 -#define OSPF6_INTERFACE_BDR 6 -#define OSPF6_INTERFACE_DR 7 -#define OSPF6_INTERFACE_MAX 8 +#define OSPF6_INTERFACE_NONE 0 +#define OSPF6_INTERFACE_DOWN 1 +#define OSPF6_INTERFACE_LOOPBACK 2 +#define OSPF6_INTERFACE_WAITING 3 +#define OSPF6_INTERFACE_POINTTOPOINT 4 +#define OSPF6_INTERFACE_POINTTOMULTIPOINT 5 +#define OSPF6_INTERFACE_DROTHER 6 +#define OSPF6_INTERFACE_BDR 7 +#define OSPF6_INTERFACE_DR 8 +#define OSPF6_INTERFACE_MAX 9 extern const char *const ospf6_interface_state_str[]; @@ -197,7 +231,7 @@ extern const char *const ospf6_interface_state_str[]; #define OSPF6_INTERFACE_TRANSDELAY 1 #define OSPF6_INTERFACE_INSTANCE_ID 0 #define OSPF6_INTERFACE_BANDWIDTH 10000 /* Mbps */ -#define OSPF6_REFERENCE_BANDWIDTH 100000 /* Mbps */ +#define OSPF6_REFERENCE_BANDWIDTH 100000 /* Kbps */ #define OSPF6_INTERFACE_SSO_RETRY_INT 1 #define OSPF6_INTERFACE_SSO_RETRY_MAX 5 diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index eb5514a..cb03675 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -321,13 +321,14 @@ void ospf6_router_lsa_originate(struct event *thread) } /* Point-to-Point interfaces */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { if (on->state != OSPF6_NEIGHBOR_FULL) continue; lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT; - lsdesc->metric = htons(oi->cost); + lsdesc->metric = htons(ospf6_neighbor_cost(on)); lsdesc->interface_id = htonl(oi->interface->ifindex); lsdesc->neighbor_interface_id = @@ -1068,6 +1069,7 @@ void ospf6_intra_prefix_lsa_originate_stub(struct event *thread) if (oi->state != OSPF6_INTERFACE_LOOPBACK && oi->state != OSPF6_INTERFACE_POINTTOPOINT + && oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT && full_count != 0) { if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) zlog_debug(" Interface %s is not stub, ignore", diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 9323045..17966e7 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -38,8 +38,13 @@ /* Default configuration file name for ospf6d. */ #define OSPF6_DEFAULT_CONFIG "ospf6d.conf" -/* Default port values. */ -#define OSPF6_VTY_PORT 2606 +/* GR and auth trailer persistent state */ +#define OSPF6D_STATE_NAME "%s/ospf6d.json", frr_libstatedir +#define OSPF6D_COMPAT_STATE_NAME "%s/ospf6d-gr.json", frr_runstatedir +/* for extra confusion, "ospf6d-at-seq-no.dat" is handled directly in + * ospf6_auth_trailer.c; the alternative would be somehow merging JSON which + * is excessive for just supporting a legacy compatibility file location + */ /* ospf6d privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -103,7 +108,11 @@ static void __attribute__((noreturn)) ospf6_exit(int status) zclient_free(zclient); } + ospf6_master_delete(); + frr_fini(); + + keychain_terminate(); exit(status); } @@ -162,16 +171,31 @@ static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { &frr_ospf6_route_map_info, }; -FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, +/* actual paths filled in main() */ +static char state_path[512]; +static char state_compat_path[512]; +static char *state_paths[] = { + state_path, + state_compat_path, + NULL, +}; - .proghelp = "Implementation of the OSPFv3 routing protocol.", +/* clang-format off */ +FRR_DAEMON_INFO(ospf6d, OSPF6, + .vty_port = OSPF6_VTY_PORT, + .proghelp = "Implementation of the OSPFv3 routing protocol.", - .signals = ospf6_signals, - .n_signals = array_size(ospf6_signals), + .signals = ospf6_signals, + .n_signals = array_size(ospf6_signals), - .privs = &ospf6d_privs, .yang_modules = ospf6d_yang_modules, - .n_yang_modules = array_size(ospf6d_yang_modules), + .privs = &ospf6d_privs, + + .yang_modules = ospf6d_yang_modules, + .n_yang_modules = array_size(ospf6d_yang_modules), + + .state_paths = state_paths, ); +/* clang-format on */ /* Max wait time for config to load before accepting hellos */ #define OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS 600 @@ -229,6 +253,10 @@ int main(int argc, char *argv[], char *envp[]) exit(1); } + snprintf(state_path, sizeof(state_path), OSPF6D_STATE_NAME); + snprintf(state_compat_path, sizeof(state_compat_path), + OSPF6D_COMPAT_STATE_NAME); + /* OSPF6 master init. */ ospf6_master_init(frr_init()); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 07da9a5..d13799c 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -268,6 +268,18 @@ static struct ospf6_packet *ospf6_packet_new(size_t size) return new; } +static struct ospf6_packet *ospf6_packet_dup(struct ospf6_packet *old) +{ + struct ospf6_packet *new; + + new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet)); + new->s = stream_dup(old->s); + new->dst = old->dst; + new->length = old->length; + + return new; +} + static void ospf6_packet_free(struct ospf6_packet *op) { if (op->s) @@ -407,6 +419,25 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, hello = (struct ospf6_hello *)((caddr_t)oh + sizeof(struct ospf6_header)); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT + || oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT) + && oi->p2xp_only_cfg_neigh) { + /* NEVER, never, ever, do this on broadcast (or NBMA)! + * DR/BDR election requires everyone to talk to everyone else + * only for PtP/PtMP we can be selective in adjacencies! + */ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + p2xp_cfg = ospf6_if_p2xp_find(oi, src); + if (!p2xp_cfg) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "ignoring PtP/PtMP hello from %pI6, neighbor not configured", + src); + return; + } + } + /* HelloInterval check */ if (ntohs(hello->hello_interval) != oi->hello_interval) { zlog_warn( @@ -479,7 +510,7 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, on->hello_in++; /* Always override neighbor's source address */ - memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr)); + ospf6_neighbor_lladdr_set(on, src); /* Neighbor ifindex check */ if (on->ifindex != (ifindex_t)ntohl(hello->interface_id)) { @@ -631,8 +662,8 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, "Neighbor state is not ExStart, ignore"); return; } - /* else fall through to ExStart */ - /* fallthru */ + /* else fall through to ExStart */ + fallthrough; case OSPF6_NEIGHBOR_EXSTART: /* if neighbor obeys us as our slave, schedule negotiation_done and process LSA Headers. Otherwise, ignore this message */ @@ -650,8 +681,8 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, on->ospf6_if->interface->vrf->name, on->name); return; } - /* fall through to exchange */ - + /* fall through to exchange */ + fallthrough; case OSPF6_NEIGHBOR_EXCHANGE: if (!memcmp(dbdesc, &on->dbdesc_last, sizeof(struct ospf6_dbdesc))) { @@ -835,8 +866,8 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, "Neighbor state is not ExStart, ignore"); return; } - /* else fall through to ExStart */ - /* fallthru */ + /* else fall through to ExStart */ + fallthrough; case OSPF6_NEIGHBOR_EXSTART: /* If the neighbor is Master, act as Slave. Schedule negotiation_done @@ -2239,8 +2270,6 @@ static void ospf6_write(struct event *thread) void ospf6_hello_send(struct event *thread) { struct ospf6_interface *oi; - struct ospf6_packet *op; - uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)EVENT_ARG(thread); @@ -2266,6 +2295,20 @@ void ospf6_hello_send(struct event *thread) return; } + event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, + &oi->thread_send_hello); + + ospf6_hello_send_addr(oi, NULL); +} + +/* used to send polls for PtP/PtMP too */ +void ospf6_hello_send_addr(struct ospf6_interface *oi, + const struct in6_addr *addr) +{ + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + bool anything = false; + op = ospf6_packet_new(oi->ifmtu); ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s); @@ -2284,20 +2327,40 @@ void ospf6_hello_send(struct event *thread) /* Set packet length. */ op->length = length; - op->dst = allspfrouters6; - - ospf6_fill_hdr_checksum(oi, op); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT + || oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT) + && !addr && oi->p2xp_no_multicast_hello) { + struct listnode *node; + struct ospf6_neighbor *on; + struct ospf6_packet *opdup; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) { + if (on->state < OSPF6_NEIGHBOR_INIT) + /* poll-interval for these */ + continue; + + opdup = ospf6_packet_dup(op); + opdup->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(oi, opdup); + ospf6_packet_add_top(oi, opdup); + anything = true; + } - /* Add packet to the top of the interface output queue, so that they - * can't get delayed by things like long queues of LS Update packets - */ - ospf6_packet_add_top(oi, op); + ospf6_packet_free(op); + } else { + op->dst = addr ? *addr : allspfrouters6; - /* set next thread */ - event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, - &oi->thread_send_hello); + /* Add packet to the top of the interface output queue, so that + * they can't get delayed by things like long queues of LS + * Update packets + */ + ospf6_fill_hdr_checksum(oi, op); + ospf6_packet_add_top(oi, op); + anything = true; + } - OSPF6_MESSAGE_WRITE_ON(oi); + if (anything) + OSPF6_MESSAGE_WRITE_ON(oi); } static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 2b25b07..2434079 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -50,6 +50,8 @@ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ #define OSPF6_MESSAGE_TYPE_MAX 0x6 /* same as OSPF6_MESSAGE_TYPE_ALL */ +struct ospf6_interface; + struct ospf6_packet { struct ospf6_packet *next; @@ -169,6 +171,9 @@ extern void ospf6_lsupdate_send_neighbor(struct event *thread); extern void ospf6_lsack_send_interface(struct event *thread); extern void ospf6_lsack_send_neighbor(struct event *thread); +extern void ospf6_hello_send_addr(struct ospf6_interface *oi, + const struct in6_addr *addr); + extern int config_write_ospf6_debug_message(struct vty *); extern void install_element_ospf6_debug_message(void); extern const char *ospf6_message_type(int type); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index e1aec06..a6089b2 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -34,6 +34,16 @@ #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR_P2XP_CFG, + "OSPF6 PtP/PtMP neighbor config"); + +static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, + const struct ospf6_if_p2xp_neighcfg *b); + +DECLARE_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs, struct ospf6_if_p2xp_neighcfg, + item, ospf6_if_p2xp_neighcfg_cmp); + +static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost); DEFINE_HOOK(ospf6_neighbor_change, (struct ospf6_neighbor * on, int state, int next_state), @@ -42,13 +52,14 @@ DEFINE_HOOK(ospf6_neighbor_change, unsigned char conf_debug_ospf6_neighbor = 0; const char *const ospf6_neighbor_state_str[] = { - "None", "Down", "Attempt", "Init", "Twoway", - "ExStart", "ExChange", "Loading", "Full", NULL}; + "None", "Down", "Attempt", "Init", "Twoway", + "ExStart", "ExChange", "Loading", "Full", NULL +}; const char *const ospf6_neighbor_event_str[] = { - "NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone", - "ExchangeDone", "LoadingDone", "AdjOK?", "SeqNumberMismatch", - "BadLSReq", "1-WayReceived", "InactivityTimer", + "NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone", + "ExchangeDone", "LoadingDone", "AdjOK?", "SeqNumberMismatch", + "BadLSReq", "1-WayReceived", "InactivityTimer", }; int ospf6_neighbor_cmp(void *va, void *vb) @@ -119,8 +130,7 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, on = XCALLOC(MTYPE_OSPF6_NEIGHBOR, sizeof(struct ospf6_neighbor)); inet_ntop(AF_INET, &router_id, buf, sizeof(buf)); - snprintf(on->name, sizeof(on->name), "%s%%%s", buf, - oi->interface->name); + snprintf(on->name, sizeof(on->name), "%s%%%s", buf, oi->interface->name); on->ospf6_if = oi; on->state = OSPF6_NEIGHBOR_DOWN; on->state_change = 0; @@ -150,6 +160,9 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, void ospf6_neighbor_delete(struct ospf6_neighbor *on) { + if (on->p2xp_cfg) + on->p2xp_cfg->active = NULL; + ospf6_neighbor_clear_ls_lists(on); ospf6_lsdb_remove_all(on->dbdesc_list); @@ -182,6 +195,22 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) XFREE(MTYPE_OSPF6_NEIGHBOR, on); } +void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, + const struct in6_addr *addr) +{ + if (IPV6_ADDR_SAME(addr, &on->linklocal_addr)) + return; + + memcpy(&on->linklocal_addr, addr, sizeof(struct in6_addr)); + + if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT || + on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + uint32_t prev_cost = ospf6_neighbor_cost(on); + + p2xp_neigh_refresh(on, prev_cost); + } +} + static void ospf6_neighbor_state_change(uint8_t next_state, struct ospf6_neighbor *on, int event) { @@ -198,31 +227,28 @@ static void ospf6_neighbor_state_change(uint8_t next_state, /* log */ if (IS_OSPF6_DEBUG_NEIGHBOR(STATE)) { - zlog_debug( - "Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)", - on->name, &on->router_id, - ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state], - ospf6_neighbor_event_string(event)); + zlog_debug("Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)", + on->name, &on->router_id, + ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); } /* Optionally notify about adjacency changes */ if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, - OSPF6_LOG_ADJACENCY_CHANGES) - && (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, - OSPF6_LOG_ADJACENCY_DETAIL) - || (next_state == OSPF6_NEIGHBOR_FULL) - || (next_state < prev_state))) - zlog_notice( - "AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)", - &on->router_id, - vrf_id_to_name(on->ospf6_if->interface->vrf->vrf_id), - on->name, ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state], - ospf6_neighbor_event_string(event)); - - if (prev_state == OSPF6_NEIGHBOR_FULL - || next_state == OSPF6_NEIGHBOR_FULL) { + OSPF6_LOG_ADJACENCY_CHANGES) && + (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL) || + (next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state))) + zlog_notice("AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)", + &on->router_id, + vrf_id_to_name(on->ospf6_if->interface->vrf->vrf_id), + on->name, ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); + + if (prev_state == OSPF6_NEIGHBOR_FULL || + next_state == OSPF6_NEIGHBOR_FULL) { if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); if (on->ospf6_if->state == OSPF6_INTERFACE_DR) { @@ -235,12 +261,11 @@ static void ospf6_neighbor_state_change(uint8_t next_state, on->ospf6_if->area->intra_prefix_originate = 1; if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB( - on->ospf6_if->area); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area); - if ((prev_state == OSPF6_NEIGHBOR_LOADING - || prev_state == OSPF6_NEIGHBOR_EXCHANGE) - && next_state == OSPF6_NEIGHBOR_FULL) { + if ((prev_state == OSPF6_NEIGHBOR_LOADING || + prev_state == OSPF6_NEIGHBOR_EXCHANGE) && + next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if); on->ospf6_if->area->full_nbrs++; } @@ -249,10 +274,10 @@ static void ospf6_neighbor_state_change(uint8_t next_state, on->ospf6_if->area->full_nbrs--; } - if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE - || prev_state == OSPF6_NEIGHBOR_LOADING) - && (next_state != OSPF6_NEIGHBOR_EXCHANGE - && next_state != OSPF6_NEIGHBOR_LOADING)) + if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE || + prev_state == OSPF6_NEIGHBOR_LOADING) && + (next_state != OSPF6_NEIGHBOR_EXCHANGE && + next_state != OSPF6_NEIGHBOR_LOADING)) ospf6_maxage_remove(on->ospf6_if->area->ospf6); hook_call(ospf6_neighbor_change, on, next_state, prev_state); @@ -262,13 +287,14 @@ static void ospf6_neighbor_state_change(uint8_t next_state, /* RFC2328 section 10.4 */ static int need_adjacency(struct ospf6_neighbor *on) { - if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT - || on->ospf6_if->state == OSPF6_INTERFACE_DR - || on->ospf6_if->state == OSPF6_INTERFACE_BDR) + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT || + on->ospf6_if->state == OSPF6_INTERFACE_POINTTOMULTIPOINT || + on->ospf6_if->state == OSPF6_INTERFACE_DR || + on->ospf6_if->state == OSPF6_INTERFACE_BDR) return 1; - if (on->ospf6_if->drouter == on->router_id - || on->ospf6_if->bdrouter == on->router_id) + if (on->ospf6_if->drouter == on->router_id || + on->ospf6_if->bdrouter == on->router_id) return 1; return 0; @@ -422,13 +448,12 @@ void exchange_done(struct event *thread) /* Check loading state. */ void ospf6_check_nbr_loading(struct ospf6_neighbor *on) { - /* RFC2328 Section 10.9: When the neighbor responds to these requests with the proper Link State Update packet(s), the Link state request list is truncated and a new Link State Request packet is sent. */ - if ((on->state == OSPF6_NEIGHBOR_LOADING) - || (on->state == OSPF6_NEIGHBOR_EXCHANGE)) { + if ((on->state == OSPF6_NEIGHBOR_LOADING) || + (on->state == OSPF6_NEIGHBOR_EXCHANGE)) { if (on->request_list->count == 0) event_add_event(master, loading_done, on, 0, &on->event_loading_done); @@ -587,9 +612,8 @@ void inactivity_timer(struct event *thread) on->drouter = on->prev_drouter = 0; on->bdrouter = on->prev_bdrouter = 0; - ospf6_neighbor_state_change( - OSPF6_NEIGHBOR_DOWN, on, - OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_DOWN, on, + OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); listnode_delete(on->ospf6_if->neighbor_list, on); @@ -597,9 +621,8 @@ void inactivity_timer(struct event *thread) } else { if (IS_DEBUG_OSPF6_GR) - zlog_debug( - "%s, Acting as HELPER for this neighbour, So restart the dead timer.", - __PRETTY_FUNCTION__); + zlog_debug("%s, Acting as HELPER for this neighbour, So restart the dead timer.", + __PRETTY_FUNCTION__); event_add_timer(master, inactivity_timer, on, on->ospf6_if->dead_interval, @@ -607,8 +630,224 @@ void inactivity_timer(struct event *thread) } } +/* P2P/P2MP stuff */ + +uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on) +{ + if (on->p2xp_cfg && on->p2xp_cfg->cfg_cost) + return on->p2xp_cfg->cost; + return on->ospf6_if->cost; +} + +static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, + const struct ospf6_if_p2xp_neighcfg *b) +{ + return IPV6_ADDR_CMP(&a->addr, &b->addr); +} + +struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi, + const struct in6_addr *addr) +{ + struct ospf6_if_p2xp_neighcfg ref; + + if (!oi) + return NULL; + + ref.addr = *addr; + return ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref); +} + +static struct ospf6_if_p2xp_neighcfg * +ospf6_if_p2xp_get(struct ospf6_interface *oi, const struct in6_addr *addr) +{ + struct ospf6_if_p2xp_neighcfg ref, *ret; + + if (!oi) + return NULL; + + ref.addr = *addr; + ret = ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref); + if (!ret) { + ret = XCALLOC(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, sizeof(*ret)); + ret->addr = *addr; + ret->ospf6_if = oi; + + ospf6_if_p2xp_neighcfgs_add(&oi->p2xp_neighs, ret); + } + + return ret; +} + +static void ospf6_if_p2xp_destroy(struct ospf6_if_p2xp_neighcfg *p2xp_cfg) +{ + EVENT_OFF(p2xp_cfg->t_unicast_hello); + ospf6_if_p2xp_neighcfgs_del(&p2xp_cfg->ospf6_if->p2xp_neighs, p2xp_cfg); + + XFREE(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, p2xp_cfg); +} + +static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost) +{ + if (on->p2xp_cfg) + on->p2xp_cfg->active = NULL; + on->p2xp_cfg = ospf6_if_p2xp_find(on->ospf6_if, &on->linklocal_addr); + if (on->p2xp_cfg) + on->p2xp_cfg->active = on; + + if (ospf6_neighbor_cost(on) != prev_cost) + OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); +} /* vty functions */ + +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_neighbor_clippy.c" +#endif + +DEFPY(ipv6_ospf6_p2xp_neigh, ipv6_ospf6_p2xp_neigh_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X", + NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n" + "Neighbor link-local address\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + + if (no) { + struct ospf6_neighbor *on; + uint32_t prev_cost = 0; + + p2xp_cfg = ospf6_if_p2xp_find(oi, &neighbor); + if (!p2xp_cfg) + return CMD_SUCCESS; + + on = p2xp_cfg->active; + if (on) + prev_cost = ospf6_neighbor_cost(on); + + p2xp_cfg->active = NULL; + ospf6_if_p2xp_destroy(p2xp_cfg); + + if (on) { + on->p2xp_cfg = NULL; + p2xp_neigh_refresh(on, prev_cost); + } + return CMD_SUCCESS; + } + + (void)ospf6_if_p2xp_get(oi, &neighbor); + return CMD_SUCCESS; +} + +DEFPY(ipv6_ospf6_p2xp_neigh_cost, ipv6_ospf6_p2xp_neigh_cost_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X cost (1-65535)", + NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n" + "Neighbor link-local address\n" + "Outgoing metric for this neighbor\n" + "Outgoing metric for this neighbor\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + uint32_t prev_cost = 0; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + + p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor); + + if (p2xp_cfg->active) + prev_cost = ospf6_neighbor_cost(p2xp_cfg->active); + + if (no) { + p2xp_cfg->cfg_cost = false; + p2xp_cfg->cost = 0; + } else { + p2xp_cfg->cfg_cost = true; + p2xp_cfg->cost = cost; + } + + if (p2xp_cfg->active) + p2xp_neigh_refresh(p2xp_cfg->active, prev_cost); + return CMD_SUCCESS; +} + +static void p2xp_unicast_hello_send(struct event *event); + +static void p2xp_unicast_hello_sched(struct ospf6_if_p2xp_neighcfg *p2xp_cfg) +{ + if (!p2xp_cfg->poll_interval || + (p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOMULTIPOINT && + p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOPOINT)) + /* state check covers DOWN state too */ + EVENT_OFF(p2xp_cfg->t_unicast_hello); + else + event_add_timer(master, p2xp_unicast_hello_send, p2xp_cfg, + p2xp_cfg->poll_interval, + &p2xp_cfg->t_unicast_hello); +} + +void ospf6_if_p2xp_up(struct ospf6_interface *oi) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) + p2xp_unicast_hello_sched(p2xp_cfg); +} + +static void p2xp_unicast_hello_send(struct event *event) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg = EVENT_ARG(event); + struct ospf6_interface *oi = p2xp_cfg->ospf6_if; + + if (oi->state != OSPF6_INTERFACE_POINTTOPOINT && + oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT) + return; + + p2xp_unicast_hello_sched(p2xp_cfg); + + if (p2xp_cfg->active && p2xp_cfg->active->state >= OSPF6_NEIGHBOR_INIT) + return; + + ospf6_hello_send_addr(oi, &p2xp_cfg->addr); +} + +DEFPY(ipv6_ospf6_p2xp_neigh_poll_interval, + ipv6_ospf6_p2xp_neigh_poll_interval_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)", + NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n" + "Neighbor link-local address\n" + "Send unicast hellos to neighbor when down\n" + "Unicast hello interval when down (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + if (no) + poll_interval = 0; + + p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor); + p2xp_cfg->poll_interval = poll_interval; + + p2xp_unicast_hello_sched(p2xp_cfg); + return CMD_SUCCESS; +} + /* show neighbor structure */ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, json_object *json_array, bool use_json) @@ -631,8 +870,8 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, /* Dead time */ h = m = s = 0; if (on->inactivity_timer) { - s = monotime_until(&on->inactivity_timer->u.sands, NULL) - / 1000000LL; + s = monotime_until(&on->inactivity_timer->u.sands, NULL) / + 1000000LL; h = s / 3600; s -= h * 3600; m = s / 60; @@ -643,6 +882,8 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, /* Neighbor State */ if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) snprintf(nstate, sizeof(nstate), "PointToPoint"); + else if (on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT) + snprintf(nstate, sizeof(nstate), "PtMultipoint"); else { if (on->router_id == on->drouter) snprintf(nstate, sizeof(nstate), "DR"); @@ -673,9 +914,9 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, json_object_string_add(json_route, "duration", duration); json_object_string_add(json_route, "interfaceName", on->ospf6_if->interface->name); - json_object_string_add( - json_route, "interfaceState", - ospf6_interface_state_str[on->ospf6_if->state]); + json_object_string_add(json_route, "interfaceState", + ospf6_interface_state_str + [on->ospf6_if->state]); json_object_array_add(json_array, json_route); } else @@ -720,9 +961,9 @@ static void ospf6_neighbor_show_drchoice(struct vty *vty, json_object_string_add(json_route, "bdRouter", bdrouter); json_object_string_add(json_route, "interfaceName", on->ospf6_if->interface->name); - json_object_string_add( - json_route, "interfaceState", - ospf6_interface_state_str[on->ospf6_if->state]); + json_object_string_add(json_route, "interfaceState", + ospf6_interface_state_str + [on->ospf6_if->state]); json_object_array_add(json_array, json_route); } else @@ -777,9 +1018,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) ? "Initial " : ""), - (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) - ? "More" - : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More" + : ""), (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) ? "Master" : "Slave")); @@ -793,8 +1033,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, json_object_int_add(json_neighbor, "summaryListCount", on->summary_list->count); for (ALL_LSDB(on->summary_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "summaryListLsa", json_array); @@ -802,8 +1042,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, json_object_int_add(json_neighbor, "requestListCount", on->request_list->count); for (ALL_LSDB(on->request_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "requestListLsa", json_array); @@ -811,8 +1051,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, json_object_int_add(json_neighbor, "reTransListCount", on->retrans_list->count); for (ALL_LSDB(on->retrans_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "reTransListLsa", json_array); @@ -825,14 +1065,14 @@ static void ospf6_neighbor_show_detail(struct vty *vty, on->dbdesc_list->count); json_object_string_add(json_neighbor, "pendingLsaDbDescTime", duration); - json_object_string_add( - json_neighbor, "dbDescSendThread", - (event_is_scheduled(on->thread_send_dbdesc) ? "on" - : "off")); + json_object_string_add(json_neighbor, "dbDescSendThread", + (event_is_scheduled(on->thread_send_dbdesc) + ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "pendingLsaDbDesc", json_array); @@ -844,35 +1084,35 @@ static void ospf6_neighbor_show_detail(struct vty *vty, on->request_list->count); json_object_string_add(json_neighbor, "pendingLsaLsReqTime", duration); - json_object_string_add( - json_neighbor, "lsReqSendThread", - (event_is_scheduled(on->thread_send_lsreq) ? "on" - : "off")); + json_object_string_add(json_neighbor, "lsReqSendThread", + (event_is_scheduled(on->thread_send_lsreq) + ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->request_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "pendingLsaLsReq", json_array); timerclear(&res); if (event_is_scheduled(on->thread_send_lsupdate)) - timersub(&on->thread_send_lsupdate->u.sands, &now, - &res); + timersub(&on->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); json_object_int_add(json_neighbor, "pendingLsaLsUpdateCount", on->lsupdate_list->count); json_object_string_add(json_neighbor, "pendingLsaLsUpdateTime", duration); - json_object_string_add( - json_neighbor, "lsUpdateSendThread", - (event_is_scheduled(on->thread_send_lsupdate) ? "on" - : "off")); + json_object_string_add(json_neighbor, "lsUpdateSendThread", + (event_is_scheduled( + on->thread_send_lsupdate) + ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "pendingLsaLsUpdate", json_array); @@ -884,14 +1124,14 @@ static void ospf6_neighbor_show_detail(struct vty *vty, on->lsack_list->count); json_object_string_add(json_neighbor, "pendingLsaLsAckTime", duration); - json_object_string_add( - json_neighbor, "lsAckSendThread", - (event_is_scheduled(on->thread_send_lsack) ? "on" - : "off")); + json_object_string_add(json_neighbor, "lsAckSendThread", + (event_is_scheduled(on->thread_send_lsack) + ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->lsack_list, lsa, lsanext)) - json_object_array_add( - json_array, json_object_new_string(lsa->name)); + json_object_array_add(json_array, + json_object_new_string(lsa->name)); json_object_object_add(json_neighbor, "pendingLsaLsAck", json_array); @@ -900,36 +1140,36 @@ static void ospf6_neighbor_show_detail(struct vty *vty, if (on->auth_present == true) { json_object_string_add(json_neighbor, "authStatus", "enabled"); - json_object_int_add( - json_neighbor, "recvdHelloHigherSeqNo", - on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO]); - json_object_int_add( - json_neighbor, "recvdHelloLowerSeqNo", - on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO]); - json_object_int_add( - json_neighbor, "recvdDBDescHigherSeqNo", - on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC]); - json_object_int_add( - json_neighbor, "recvdDBDescLowerSeqNo", - on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC]); - json_object_int_add( - json_neighbor, "recvdLSReqHigherSeqNo", - on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ]); - json_object_int_add( - json_neighbor, "recvdLSReqLowerSeqNo", - on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ]); - json_object_int_add( - json_neighbor, "recvdLSUpdHigherSeqNo", - on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE]); - json_object_int_add( - json_neighbor, "recvdLSUpdLowerSeqNo", - on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE]); - json_object_int_add( - json_neighbor, "recvdLSAckHigherSeqNo", - on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); - json_object_int_add( - json_neighbor, "recvdLSAckLowerSeqNo", - on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); + json_object_int_add(json_neighbor, + "recvdHelloHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add(json_neighbor, + "recvdHelloLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add(json_neighbor, + "recvdDBDescHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add(json_neighbor, + "recvdDBDescLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add(json_neighbor, + "recvdLSReqHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add(json_neighbor, + "recvdLSReqLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add(json_neighbor, + "recvdLSUpdHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add(json_neighbor, + "recvdLSUpdLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add(json_neighbor, + "recvdLSAckHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); + json_object_int_add(json_neighbor, + "recvdLSAckLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); } else json_object_string_add(json_neighbor, "authStatus", "disabled"); @@ -951,9 +1191,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) ? "Initial " : ""), - (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) - ? "More " - : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More " + : ""), (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) ? "Master" : "Slave"), @@ -1000,8 +1239,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty, timerclear(&res); if (event_is_scheduled(on->thread_send_lsupdate)) - timersub(&on->thread_send_lsupdate->u.sands, &now, - &res); + timersub(&on->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", @@ -1141,8 +1379,7 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, static int ospf6_neighbor_show_common(struct vty *vty, int argc, struct cmd_token **argv, - struct ospf6 *ospf6, int idx_ipv4, - bool uj) + struct ospf6 *ospf6, int idx_ipv4, bool uj) { struct ospf6_neighbor *on; struct ospf6_interface *oi; @@ -1214,16 +1451,18 @@ void ospf6_neighbor_init(void) { install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_one_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cost_cmd); + install_element(INTERFACE_NODE, + &ipv6_ospf6_p2xp_neigh_poll_interval_cmd); } -DEFUN (debug_ospf6_neighbor, - debug_ospf6_neighbor_cmd, - "debug ospf6 neighbor []", - DEBUG_STR - OSPF6_STR - "Debug OSPFv3 Neighbor\n" - "Debug OSPFv3 Neighbor State Change\n" - "Debug OSPFv3 Neighbor Event\n") +DEFUN(debug_ospf6_neighbor, debug_ospf6_neighbor_cmd, + "debug ospf6 neighbor []", + DEBUG_STR OSPF6_STR "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n") { int idx_type = 3; unsigned char level = 0; @@ -1241,15 +1480,11 @@ DEFUN (debug_ospf6_neighbor, } -DEFUN (no_debug_ospf6_neighbor, - no_debug_ospf6_neighbor_cmd, - "no debug ospf6 neighbor []", - NO_STR - DEBUG_STR - OSPF6_STR - "Debug OSPFv3 Neighbor\n" - "Debug OSPFv3 Neighbor State Change\n" - "Debug OSPFv3 Neighbor Event\n") +DEFUN(no_debug_ospf6_neighbor, no_debug_ospf6_neighbor_cmd, + "no debug ospf6 neighbor []", + NO_STR DEBUG_STR OSPF6_STR "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n") { int idx_type = 4; unsigned char level = 0; @@ -1267,12 +1502,8 @@ DEFUN (no_debug_ospf6_neighbor, } -DEFUN (no_debug_ospf6, - no_debug_ospf6_cmd, - "no debug ospf6", - NO_STR - DEBUG_STR - OSPF6_STR) +DEFUN(no_debug_ospf6, no_debug_ospf6_cmd, "no debug ospf6", + NO_STR DEBUG_STR OSPF6_STR) { unsigned int i; @@ -1287,12 +1518,11 @@ DEFUN (no_debug_ospf6, ospf6_lsa_debug_set_all(false); for (i = 0; i < 6; i++) - OSPF6_DEBUG_MESSAGE_OFF(i, - OSPF6_DEBUG_NEIGHBOR_STATE - | OSPF6_DEBUG_NEIGHBOR_EVENT); + OSPF6_DEBUG_MESSAGE_OFF(i, OSPF6_DEBUG_NEIGHBOR_STATE | + OSPF6_DEBUG_NEIGHBOR_EVENT); - OSPF6_DEBUG_NEIGHBOR_OFF(OSPF6_DEBUG_NEIGHBOR_STATE - | OSPF6_DEBUG_NEIGHBOR_EVENT); + OSPF6_DEBUG_NEIGHBOR_OFF(OSPF6_DEBUG_NEIGHBOR_STATE | + OSPF6_DEBUG_NEIGHBOR_EVENT); OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_TABLE); OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTRA); OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTER); @@ -1316,6 +1546,25 @@ int config_write_ospf6_debug_neighbor(struct vty *vty) return 0; } +int config_write_ospf6_p2xp_neighbor(struct vty *vty, struct ospf6_interface *oi) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) { + vty_out(vty, " ipv6 ospf6 neighbor %pI6\n", &p2xp_cfg->addr); + + if (p2xp_cfg->poll_interval) + vty_out(vty, + " ipv6 ospf6 neighbor %pI6 poll-interval %u\n", + &p2xp_cfg->addr, p2xp_cfg->poll_interval); + + if (p2xp_cfg->cfg_cost) + vty_out(vty, " ipv6 ospf6 neighbor %pI6 cost %u\n", + &p2xp_cfg->addr, p2xp_cfg->cost); + } + return 0; +} + void install_element_ospf6_debug_neighbor(void) { install_element(ENABLE_NODE, &debug_ospf6_neighbor_cmd); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 226f4c1..60a7621 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -6,8 +6,11 @@ #ifndef OSPF6_NEIGHBOR_H #define OSPF6_NEIGHBOR_H +#include "typesafe.h" #include "hook.h" +#include "ospf6_message.h" + /* Forward declaration(s). */ struct ospf6_area; @@ -52,6 +55,8 @@ struct ospf6_helper_info { uint32_t rejected_reason; }; +struct ospf6_if_p2xp_neighcfg; + /* Neighbor structure */ struct ospf6_neighbor { /* Neighbor Router ID String */ @@ -60,6 +65,11 @@ struct ospf6_neighbor { /* OSPFv3 Interface this neighbor belongs to */ struct ospf6_interface *ospf6_if; + /* P2P/P2MP config for this neighbor. + * can be NULL if not explicitly configured! + */ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + /* Neighbor state */ uint8_t state; @@ -190,6 +200,14 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, struct ospf6_interface *oi); void ospf6_neighbor_delete(struct ospf6_neighbor *on); +void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, + const struct in6_addr *addr); +struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi, + const struct in6_addr *addr); +void ospf6_if_p2xp_up(struct ospf6_interface *oi); + +uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on); + /* Neighbor event */ extern void hello_received(struct event *thread); extern void twoway_received(struct event *thread); @@ -205,6 +223,8 @@ extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); extern void ospf6_neighbor_init(void); extern int config_write_ospf6_debug_neighbor(struct vty *vty); +extern int config_write_ospf6_p2xp_neighbor(struct vty *vty, + struct ospf6_interface *oi); extern void install_element_ospf6_debug_neighbor(void); DECLARE_HOOK(ospf6_neighbor_change, diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 4430329..10a1208 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -363,7 +363,7 @@ void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, case NEXTHOP_TYPE_IPV6_IFINDEX: nexthops[i].ifindex = nh->ifindex; - /* FALLTHROUGH */ + fallthrough; case NEXTHOP_TYPE_IPV6: nexthops[i].gate.ipv6 = nh->address; break; @@ -540,6 +540,10 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb) if (ra->path.area_id != rb->path.area_id) return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id)); + if ((ra->prefix_options & OSPF6_PREFIX_OPTION_LA) + != (rb->prefix_options & OSPF6_PREFIX_OPTION_LA)) + return ra->prefix_options & OSPF6_PREFIX_OPTION_LA ? -1 : 1; + return 0; } diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index c212595..2c1d17e 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -67,7 +67,7 @@ static inline bool ospf6_nexthop_is_same(const struct ospf6_nexthop *nha, case NEXTHOP_TYPE_IPV6_IFINDEX: if (nha->ifindex != nhb->ifindex) return false; - /* FALLTHROUGH */ + fallthrough; case NEXTHOP_TYPE_IPV6: if (!IN6_ARE_ADDR_EQUAL(&nha->address, &nhb->address)) return false; diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index f88667b..17cdcda 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -697,8 +697,8 @@ static uint8_t *ospfv3GeneralGroup(struct variable *v, oid *name, case OSPFv3REFERENCEBANDWIDTH: if (ospf6) return SNMP_INTEGER(ospf6->ref_bandwidth); - /* Otherwise, like for "not implemented". */ - /* fallthru */ + /* Otherwise, like for "not implemented". */ + return NULL; case OSPFv3RESTARTSUPPORT: case OSPFv3RESTARTINTERVAL: case OSPFv3RESTARTSTRICTLSACHECKING: @@ -1126,6 +1126,8 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); else if (oi->type == OSPF_IFTYPE_POINTOPOINT) return SNMP_INTEGER(3); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + return SNMP_INTEGER(5); else break; /* Unknown, don't put anything */ case OSPFv3IFADMINSTATUS: @@ -1367,6 +1369,7 @@ static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state, /* Terminal state or regression */ if ((next_state != OSPF6_INTERFACE_POINTTOPOINT) + && (next_state != OSPF6_INTERFACE_POINTTOMULTIPOINT) && (next_state != OSPF6_INTERFACE_DROTHER) && (next_state != OSPF6_INTERFACE_BDR) && (next_state != OSPF6_INTERFACE_DR) && (next_state >= prev_state)) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 216837d..a3fb205 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -16,6 +16,7 @@ #include "defaults.h" #include "lib/json.h" #include "lib_errors.h" +#include "frrdistance.h" #include "ospf6_proto.h" #include "ospf6_message.h" @@ -429,17 +430,7 @@ static struct ospf6 *ospf6_create(const char *name) /* Make ospf protocol socket. */ ospf6_serv_sock(o); - /* If sequence number is stored in persistent storage, read it. - */ - if (ospf6_auth_nvm_file_exist() == OSPF6_AUTH_FILE_EXIST) { - ospf6_auth_seqno_nvm_read(o); - o->seqnum_h = o->seqnum_h + 1; - ospf6_auth_seqno_nvm_update(o); - } else { - o->seqnum_l = o->seqnum_h = 0; - ospf6_auth_seqno_nvm_update(o); - } - + ospf6_auth_init(o); return o; } @@ -485,6 +476,7 @@ void ospf6_delete(struct ospf6 *o) struct ospf6_area *oa; struct vrf *vrf; struct ospf6_external_aggr_rt *aggr; + uint32_t i; QOBJ_UNREG(o); @@ -531,6 +523,13 @@ void ospf6_delete(struct ospf6 *o) } route_table_finish(o->rt_aggr_tbl); + for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { + if (!o->redist[i]) + continue; + + list_delete(&o->redist[i]); + } + XFREE(MTYPE_OSPF6_TOP, o->name); XFREE(MTYPE_OSPF6_TOP, o); } @@ -575,6 +574,11 @@ void ospf6_master_init(struct event_loop *master) om6->master = master; } +void ospf6_master_delete(void) +{ + list_delete(&om6->ospf6); +} + static void ospf6_maxage_remover(struct event *thread) { struct ospf6 *o = (struct ospf6 *)EVENT_ARG(thread); @@ -1056,148 +1060,6 @@ DEFUN (no_ospf6_distance_ospf6, return CMD_SUCCESS; } -DEFUN_HIDDEN (ospf6_interface_area, - ospf6_interface_area_cmd, - "interface IFNAME area ", - "Enable routing on an IPv6 interface\n" - IFNAME_STR - "Specify the OSPF6 area ID\n" - "OSPF6 area ID in IPv4 address notation\n" - "OSPF6 area ID in decimal notation\n" - ) -{ - VTY_DECLVAR_CONTEXT(ospf6, ospf6); - int idx_ifname = 1; - int idx_ipv4 = 3; - struct ospf6_area *oa; - struct ospf6_interface *oi; - struct interface *ifp; - uint32_t area_id; - int format; - - vty_out(vty, - "This command is deprecated, because it is not VRF-aware.\n"); - vty_out(vty, - "Please, use \"ipv6 ospf6 area\" on an interface instead.\n"); - - /* find/create ospf6 interface */ - ifp = if_get_by_name(argv[idx_ifname]->arg, ospf6->vrf_id, ospf6->name); - oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) - oi = ospf6_interface_create(ifp); - if (oi->area) { - vty_out(vty, "%s already attached to Area %s\n", - oi->interface->name, oi->area->name); - return CMD_SUCCESS; - } - - 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; - } - - oi->area_id = area_id; - oi->area_id_format = format; - - oa = ospf6_area_lookup(area_id, ospf6); - if (oa == NULL) - oa = ospf6_area_create(area_id, ospf6, format); - - /* attach interface to area */ - listnode_add(oa->if_list, oi); /* sort ?? */ - oi->area = oa; - - SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); - - /* ospf6 process is currently disabled, not much more to do */ - if (CHECK_FLAG(ospf6->flag, OSPF6_DISABLED)) - return CMD_SUCCESS; - - /* start up */ - ospf6_interface_enable(oi); - - /* If the router is ABR, originate summary routes */ - if (ospf6_check_and_set_router_abr(ospf6)) { - ospf6_abr_enable_area(oa); - ospf6_schedule_abr_task(oa->ospf6); - } - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_ospf6_interface_area, - no_ospf6_interface_area_cmd, - "no interface IFNAME area ", - NO_STR - "Disable routing on an IPv6 interface\n" - IFNAME_STR - "Specify the OSPF6 area ID\n" - "OSPF6 area ID in IPv4 address notation\n" - "OSPF6 area ID in decimal notation\n" - ) -{ - VTY_DECLVAR_CONTEXT(ospf6, ospf6); - int idx_ifname = 2; - int idx_ipv4 = 4; - struct ospf6_interface *oi; - struct ospf6_area *oa; - struct interface *ifp; - uint32_t area_id; - - vty_out(vty, - "This command is deprecated, because it is not VRF-aware.\n"); - vty_out(vty, - "Please, use \"no ipv6 ospf6 area\" on an interface instead.\n"); - - /* find/create ospf6 interface */ - ifp = if_get_by_name(argv[idx_ifname]->arg, ospf6->vrf_id, ospf6->name); - - if (ifp == NULL) { - vty_out(vty, "No such interface %s\n", argv[idx_ifname]->arg); - return CMD_SUCCESS; - } - - oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) { - vty_out(vty, "Interface %s not enabled\n", ifp->name); - return CMD_SUCCESS; - } - - /* parse Area-ID */ - if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) - area_id = htonl(strtoul(argv[idx_ipv4]->arg, NULL, 10)); - - /* Verify Area */ - if (oi->area == NULL) { - vty_out(vty, "%s not attached to area %s\n", - oi->interface->name, argv[idx_ipv4]->arg); - return CMD_SUCCESS; - } - - if (oi->area->area_id != area_id) { - vty_out(vty, "Wrong Area-ID: %s is attached to area %s\n", - oi->interface->name, oi->area->name); - return CMD_SUCCESS; - } - - ospf6_interface_disable(oi); - - oa = oi->area; - listnode_delete(oi->area->if_list, oi); - oi->area = (struct ospf6_area *)NULL; - - /* Withdraw inter-area routes from this area, if necessary */ - if (oa->if_list->count == 0) { - UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); - ospf6_abr_disable_area(oa); - } - - oi->area_id = 0; - oi->area_id_format = OSPF6_AREA_FMT_UNSET; - - return CMD_SUCCESS; -} - DEFUN (ospf6_stub_router_admin, ospf6_stub_router_admin_cmd, "stub-router administrative", @@ -2346,8 +2208,6 @@ void ospf6_top_init(void) install_element(OSPF6_NODE, &ospf6_timers_lsa_cmd); install_element(OSPF6_NODE, &no_ospf6_timers_lsa_cmd); - install_element(OSPF6_NODE, &ospf6_interface_area_cmd); - install_element(OSPF6_NODE, &no_ospf6_interface_area_cmd); install_element(OSPF6_NODE, &ospf6_stub_router_admin_cmd); install_element(OSPF6_NODE, &no_ospf6_stub_router_admin_cmd); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index a38dad8..8288413 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -236,6 +236,8 @@ extern struct ospf6_master *om6; /* prototypes */ extern void ospf6_master_init(struct event_loop *master); +extern void ospf6_master_delete(void); + extern void install_element_ospf6_clear_process(void); extern void ospf6_top_init(void); extern void ospf6_delete(struct ospf6 *o); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 8bd0d8f..3245578 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -147,30 +147,22 @@ void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg) __func__); } -static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +static void ospf6_zebra_import_check_update(struct vrf *vrf, + struct prefix *matched, + struct zapi_route *nhr) { struct ospf6 *ospf6; - struct zapi_route nhr; - struct prefix matched; - ospf6 = ospf6_lookup_by_vrf_id(vrf_id); + ospf6 = (struct ospf6 *)vrf->info; if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6)) - return 0; - - if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { - zlog_err("%s[%u]: Failure to decode route", __func__, - ospf6->vrf_id); - return -1; - } + return; - if (matched.family != AF_INET6 || matched.prefixlen != 0 || - nhr.type == ZEBRA_ROUTE_OSPF6) - return 0; + if (matched->family != AF_INET6 || matched->prefixlen != 0 || + nhr->type == ZEBRA_ROUTE_OSPF6) + return; - ospf6->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf6->nssa_default_import_check.status = !!nhr->nexthop_num; ospf6_abr_nssa_type_7_defaults(ospf6); - - return 0; } static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) @@ -763,7 +755,6 @@ static zclient_handler *const ospf6_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_DELETE] = ospf6_zebra_if_address_update_delete, [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf6_zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf6_zebra_read_route, - [ZEBRA_NEXTHOP_UPDATE] = ospf6_zebra_import_check_update, }; void ospf6_zebra_init(struct event_loop *master) @@ -773,6 +764,7 @@ void ospf6_zebra_init(struct event_loop *master) array_size(ospf6_handlers)); zclient_init(zclient, ZEBRA_ROUTE_OSPF6, 0, &ospf6d_privs); zclient->zebra_connected = ospf6_zebra_connected; + zclient->nexthop_update = ospf6_zebra_import_check_update; /* Install command element for zebra node. */ install_element(VIEW_NODE, &show_ospf6_zebra_cmd); diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index f6d27c8..5f89af9 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -83,8 +83,10 @@ clippy_scan += \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_gr.c \ + ospf6d/ospf6_interface.c \ ospf6d/ospf6_nssa.c \ ospf6d/ospf6_route.c \ + ospf6d/ospf6_neighbor.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h index 6c8bed3..6160a0f 100644 --- a/ospfd/ospf_api.h +++ b/ospfd/ospf_api.h @@ -21,10 +21,6 @@ #define MTYPE_OSPF_API_MSG MTYPE_TMP #define MTYPE_OSPF_API_FIFO MTYPE_TMP -/* Default API server port to accept connection request from client-side. */ -/* This value could be overridden by "ospfapi" entry in "/etc/services". */ -#define OSPF_API_SYNC_PORT 2607 - /* ----------------------------------------------------------- * Generic messages * ----------------------------------------------------------- diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 34e59c5..419113e 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -2092,7 +2092,7 @@ void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv, lsa, (void *)¶m, 0); break; case OSPF_OPAQUE_AS_LSA: - LSDB_LOOP (OPAQUE_LINK_LSDB(ospf), rn, lsa) + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0); break; diff --git a/ospfd/ospf_auth.c b/ospfd/ospf_auth.c index 11ee1dd..2b090dc 100644 --- a/ospfd/ospf_auth.c +++ b/ospfd/ospf_auth.c @@ -5,6 +5,12 @@ */ #include +#include + +#ifdef CRYPTO_OPENSSL +#include +#include +#endif #include "linklist.h" #include "if.h" diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index 75b5803..df0b3b9 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -31,6 +31,7 @@ #include "network.h" #include "if.h" #include "libospf.h" /* for ospf interface types */ +#include #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -1715,85 +1716,138 @@ static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op) /* Cisco experimental SubTLV */ static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ext_subtlv_rmt_itf_addr *top = (struct ext_subtlv_rmt_itf_addr *)tlvh; check_tlv_size(EXT_SUBTLV_RMT_ITF_ADDR_SIZE, "Remote Itf. Address"); - vty_out(vty, - " Remote Interface Address Sub-TLV: Length %u\n Address: %pI4\n", - ntohs(top->header.length), &top->value); + if (!json) + vty_out(vty, + " Remote Interface Address Sub-TLV: Length %u\n Address: %pI4\n", + ntohs(top->header.length), &top->value); + else + json_object_string_addf(json, "remoteInterfaceAddress", "%pI4", + &top->value); return TLV_SIZE(tlvh); } /* Adjacency SID SubTLV */ static uint16_t show_vty_ext_link_adj_sid(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh; + uint8_t tlv_size; - check_tlv_size(EXT_SUBTLV_ADJ_SID_SIZE, "Adjacency SID"); + tlv_size = CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? SID_LABEL_SIZE(EXT_SUBTLV_ADJ_SID_SIZE) + : SID_INDEX_SIZE(EXT_SUBTLV_ADJ_SID_SIZE); + check_tlv_size(tlv_size, "Adjacency SID"); - vty_out(vty, - " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", - ntohs(top->header.length), top->flags, top->mtid, top->weight, - CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" - : "Index", - CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) - ? GET_LABEL(ntohl(top->value)) - : ntohl(top->value)); + if (!json) + vty_out(vty, + " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", + ntohs(top->header.length), top->flags, top->mtid, + top->weight, + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + else { + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); + json_object_string_addf(json, "weight", "0x%x", top->weight); + if (CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + json_object_int_add(json, "label", + GET_LABEL(ntohl(top->value))); + else + json_object_int_add(json, "index", ntohl(top->value)); + } return TLV_SIZE(tlvh); } /* LAN Adjacency SubTLV */ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ext_subtlv_lan_adj_sid *top = (struct ext_subtlv_lan_adj_sid *)tlvh; + uint8_t tlv_size; - check_tlv_size(EXT_SUBTLV_LAN_ADJ_SID_SIZE, "Lan-Adjacency SID"); + tlv_size = CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? SID_LABEL_SIZE(EXT_SUBTLV_LAN_ADJ_SID_SIZE) + : SID_INDEX_SIZE(EXT_SUBTLV_LAN_ADJ_SID_SIZE); + check_tlv_size(tlv_size, "LAN-Adjacency SID"); - vty_out(vty, - " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %pI4\n\t%s: %u\n", - ntohs(top->header.length), top->flags, top->mtid, top->weight, - &top->neighbor_id, - CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" - : "Index", - CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) - ? GET_LABEL(ntohl(top->value)) - : ntohl(top->value)); + if (!json) + vty_out(vty, + " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %pI4\n\t%s: %u\n", + ntohs(top->header.length), top->flags, top->mtid, + top->weight, &top->neighbor_id, + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + else { + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); + json_object_string_addf(json, "weight", "0x%x", top->weight); + json_object_string_addf(json, "neighborID", "%pI4", + &top->neighbor_id); + if (CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + json_object_int_add(json, "label", + GET_LABEL(ntohl(top->value))); + else + json_object_int_add(json, "index", ntohl(top->value)); + } return TLV_SIZE(tlvh); } static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, json_object *json) { + json_object *obj; + if (TLV_SIZE(tlvh) > buf_size) { vty_out(vty, " TLV size %d exceeds buffer size. Abort!", TLV_SIZE(tlvh)); return buf_size; } - - vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", - ntohs(tlvh->type), ntohs(tlvh->length)); + if (!json) + vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + else { + obj = json_object_new_object(); + json_object_string_addf(obj, "type", "0x%x", + ntohs(tlvh->type)); + json_object_string_addf(obj, "length", "0x%x", + ntohs(tlvh->length)); + json_object_object_add(json, "unknownTLV", obj); + } return TLV_SIZE(tlvh); } /* Extended Link Sub TLVs */ static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext, - size_t buf_size) + size_t buf_size, json_object *json) { struct ext_tlv_link *top = (struct ext_tlv_link *)ext; struct tlv_header *tlvh; uint16_t length = ntohs(top->header.length); uint16_t sum = 0; + json_object *jadj = NULL, *obj = NULL; /* Verify that TLV length is valid against remaining buffer size */ if (length > buf_size) { @@ -1803,12 +1857,22 @@ static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext, return buf_size; } - vty_out(vty, - " Extended Link TLV: Length %u\n Link Type: 0x%x\n" - " Link ID: %pI4\n", - ntohs(top->header.length), top->link_type, - &top->link_id); - vty_out(vty, " Link data: %pI4\n", &top->link_data); + if (!json) { + vty_out(vty, + " Extended Link TLV: Length %u\n Link Type: 0x%x\n" + " Link ID: %pI4\n", + ntohs(top->header.length), top->link_type, + &top->link_id); + vty_out(vty, " Link data: %pI4\n", &top->link_data); + } else { + json_object_string_addf(json, "linkType", "0x%x", + top->link_type); + json_object_string_addf(json, "linkID", "%pI4", &top->link_id); + json_object_string_addf(json, "linkData", "%pI4", + &top->link_data); + jadj = json_object_new_array(); + json_object_object_add(json, "adjacencySID", jadj); + } /* Skip Extended TLV and parse sub-TLVs */ length -= EXT_TLV_LINK_SIZE; @@ -1817,16 +1881,27 @@ static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext, for (; sum < length && tlvh; tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case EXT_SUBTLV_ADJ_SID: - sum += show_vty_ext_link_adj_sid(vty, tlvh); + if (json) { + obj = json_object_new_object(); + json_object_array_add(jadj, obj); + } else + obj = NULL; + sum += show_vty_ext_link_adj_sid(vty, tlvh, obj); break; case EXT_SUBTLV_LAN_ADJ_SID: - sum += show_vty_ext_link_lan_adj_sid(vty, tlvh); + if (json) { + obj = json_object_new_object(); + json_object_array_add(jadj, obj); + } else + obj = NULL; + sum += show_vty_ext_link_lan_adj_sid(vty, tlvh, obj); break; case EXT_SUBTLV_RMT_ITF_ADDR: - sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh); + sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh, json); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + sum += show_vty_unknown_tlv(vty, tlvh, length - sum, + json); break; } } @@ -1841,9 +1916,12 @@ static void ospf_ext_link_show_info(struct vty *vty, struct json_object *json, struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; + json_object *jlink = NULL; - if (json) - return; + if (json) { + jlink = json_object_new_object(); + json_object_object_add(json, "extendedLink", jlink); + } /* Initialize TLV browsing */ length = lsa->size - OSPF_LSA_HEADER_SIZE; @@ -1852,10 +1930,12 @@ static void ospf_ext_link_show_info(struct vty *vty, struct json_object *json, tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case EXT_TLV_LINK: - sum += show_vty_link_info(vty, tlvh, length - sum); + sum += show_vty_link_info(vty, tlvh, length - sum, + jlink); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + sum += show_vty_unknown_tlv(vty, tlvh, length - sum, + jlink); break; } } @@ -1863,34 +1943,51 @@ static void ospf_ext_link_show_info(struct vty *vty, struct json_object *json, /* Prefix SID SubTLV */ static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ext_subtlv_prefix_sid *top = (struct ext_subtlv_prefix_sid *)tlvh; + uint8_t tlv_size; - check_tlv_size(EXT_SUBTLV_PREFIX_SID_SIZE, "Prefix SID"); - - vty_out(vty, - " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", - ntohs(top->header.length), top->algorithm, top->flags, - top->mtid, - CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label" - : "Index", - CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) - ? GET_LABEL(ntohl(top->value)) - : ntohl(top->value)); + tlv_size = CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE) + : SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE); + check_tlv_size(tlv_size, "Prefix SID"); + if (!json) + vty_out(vty, + " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", + ntohs(top->header.length), top->algorithm, top->flags, + top->mtid, + CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + else { + json_object_int_add(json, "algorithm", top->algorithm); + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); + if (CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) + json_object_int_add(json, "label", + GET_LABEL(ntohl(top->value))); + else + json_object_int_add(json, "index", ntohl(top->value)); + } return TLV_SIZE(tlvh); } /* Extended Prefix SubTLVs */ static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext, - size_t buf_size) + size_t buf_size, json_object *json) { struct ext_tlv_prefix *top = (struct ext_tlv_prefix *)ext; struct tlv_header *tlvh; uint16_t length = ntohs(top->header.length); uint16_t sum = 0; + json_object *jsid = NULL; /* Verify that TLV length is valid against remaining buffer size */ if (length > buf_size) { @@ -1900,11 +1997,21 @@ static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext, return buf_size; } - vty_out(vty, - " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n" - "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %pI4/%u\n", - ntohs(top->header.length), top->route_type, top->af, top->flags, - &top->address, top->pref_length); + if (!json) + vty_out(vty, + " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n" + "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %pI4/%u\n", + ntohs(top->header.length), top->route_type, top->af, + top->flags, &top->address, top->pref_length); + else { + json_object_int_add(json, "routeType", top->route_type); + json_object_string_addf(json, "addressFamily", "0x%x", top->af); + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "address", "%pI4", &top->address); + json_object_int_add(json, "prefixLength", top->pref_length); + jsid = json_object_new_object(); + json_object_object_add(json, "prefixSID", jsid); + } /* Skip Extended Prefix TLV and parse sub-TLVs */ length -= EXT_TLV_PREFIX_SIZE; @@ -1913,10 +2020,11 @@ static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext, for (; sum < length && tlvh; tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case EXT_SUBTLV_PREFIX_SID: - sum += show_vty_ext_pref_pref_sid(vty, tlvh); + sum += show_vty_ext_pref_pref_sid(vty, tlvh, jsid); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + sum += show_vty_unknown_tlv(vty, tlvh, length - sum, + json); break; } } @@ -1931,9 +2039,12 @@ static void ospf_ext_pref_show_info(struct vty *vty, struct json_object *json, struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; + json_object *jpref = NULL; - if (json) - return; + if (json) { + jpref = json_object_new_object(); + json_object_object_add(json, "extendedPrefix", jpref); + } /* Initialize TLV browsing */ length = lsa->size - OSPF_LSA_HEADER_SIZE; @@ -1942,10 +2053,12 @@ static void ospf_ext_pref_show_info(struct vty *vty, struct json_object *json, tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case EXT_TLV_PREFIX: - sum += show_vty_pref_info(vty, tlvh, length - sum); + sum += show_vty_pref_info(vty, tlvh, length - sum, + jpref); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + sum += show_vty_unknown_tlv(vty, tlvh, length - sum, + jpref); break; } } diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index dd8c926..95a593a 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -954,7 +954,7 @@ int ospf_flood_through(struct ospf *ospf, struct ospf_neighbor *inbr, if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: LOCAL NSSA FLOOD of Type-7.", __func__); - /* Fallthrough */ + fallthrough; default: lsa_ack_flag = ospf_flood_through_area(lsa->area, inbr, lsa); break; diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index c23c420..0a4d579 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -555,21 +555,6 @@ static void ospf_gr_grace_period_expired(struct event *thread) ospf_gr_restart_exit(ospf, "grace period has expired"); } -/* - * 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; -} - /* Send extra Grace-LSA out the interface (unplanned outages only). */ void ospf_gr_iface_send_grace_lsa(struct event *thread) { @@ -591,18 +576,14 @@ void ospf_gr_iface_send_grace_lsa(struct event *thread) */ static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) { - 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_get_name(ospf); - json = json_object_from_file(filepath); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -630,8 +611,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) 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); + frr_daemon_state_save(&json); } /* @@ -640,17 +620,13 @@ static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) */ 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_get_name(ospf); - json = json_object_from_file(filepath); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -660,8 +636,7 @@ void ospf_gr_nvm_delete(struct ospf *ospf) json_object_object_del(json_instances, inst_name); - json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } /* @@ -670,7 +645,6 @@ void ospf_gr_nvm_delete(struct ospf *ospf) */ void ospf_gr_nvm_read(struct ospf *ospf) { - char *filepath; const char *inst_name; json_object *json; json_object *json_instances; @@ -679,12 +653,9 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object *json_grace_period; time_t timestamp = 0; - filepath = ospf_gr_nvm_filepath(ospf); inst_name = ospf_get_name(ospf); - json = json_object_from_file(filepath); - if (json == NULL) - json = json_object_new_object(); + json = frr_daemon_state_load(); json_object_object_get_ex(json, "instances", &json_instances); if (!json_instances) { @@ -730,8 +701,7 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object_object_del(json_instances, inst_name); - json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); - json_object_free(json); + frr_daemon_state_save(&json); } void ospf_gr_unplanned_start_interface(struct ospf_interface *oi) diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 7601419..0969ae1 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -339,6 +339,8 @@ void ospf_if_free(struct ospf_interface *oi) assert(oi->state == ISM_Down); + ospf_opaque_type9_lsa_if_cleanup(oi); + ospf_opaque_type9_lsa_term(oi); QOBJ_UNREG(oi); @@ -380,26 +382,6 @@ int ospf_if_is_up(struct ospf_interface *oi) return if_is_up(oi->ifp); } -struct ospf_interface *ospf_if_exists(struct ospf_interface *oic) -{ - struct listnode *node; - struct ospf *ospf; - struct ospf_interface *oi; - - if (!oic) - return NULL; - - ospf = oic->ospf; - if (ospf == NULL) - return NULL; - - for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) - if (oi == oic) - return oi; - - return NULL; -} - /* Lookup OSPF interface by router LSA posistion */ struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *area, int lsa_pos) @@ -932,7 +914,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, { struct ospf_interface *voi; struct interface *vi; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; struct ospf_area *area; struct in_addr area_id; struct connected *co; @@ -962,7 +944,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, UNSET_FLAG(vi->status, ZEBRA_INTERFACE_LINKDETECTION); co = connected_new(); co->ifp = vi; - listnode_add(vi->connected, co); + if_connected_add_tail(vi->connected, co); p = prefix_ipv4_new(); p->family = AF_INET; @@ -1167,7 +1149,7 @@ static int ospf_vl_set_params(struct ospf_area *area, if (IS_DEBUG_OSPF_EVENT) zlog_debug( "found back link through VL"); - /* fallthru */ + fallthrough; case LSA_LINK_TYPE_TRANSIT: case LSA_LINK_TYPE_POINTOPOINT: if (!IPV4_ADDR_SAME(&vl_data->peer_addr, @@ -1562,8 +1544,10 @@ void ospf_reset_hello_timer(struct interface *ifp, struct in_addr addr, void ospf_if_init(void) { - if_zapi_callbacks(ospf_ifp_create, ospf_ifp_up, - ospf_ifp_down, ospf_ifp_destroy); + hook_register_prio(if_real, 0, ospf_ifp_create); + hook_register_prio(if_up, 0, ospf_ifp_up); + hook_register_prio(if_down, 0, ospf_ifp_down); + hook_register_prio(if_unreal, 0, ospf_ifp_destroy); /* Initialize Zebra interface data structure. */ hook_register_prio(if_add, 0, ospf_if_new_hook); diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index e2290a8..08a2b11 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -300,7 +300,6 @@ extern int ospf_if_up(struct ospf_interface *oi); extern int ospf_if_down(struct ospf_interface *oi); extern int ospf_if_is_up(struct ospf_interface *oi); -extern struct ospf_interface *ospf_if_exists(struct ospf_interface *oi); extern struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *area, int lsa_pos); extern struct ospf_interface * diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index e47f832..f125fa9 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -3096,13 +3096,14 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, /* Incoming "oi" for this LSA has set at LSUpd * reception. */ } - /* Fallthrough */ + fallthrough; case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: new = ospf_opaque_lsa_install(lsa, rt_recalc); break; case OSPF_AS_NSSA_LSA: new = ospf_external_lsa_install(ospf, lsa, rt_recalc); + break; default: /* type-6,8,9....nothing special */ break; } diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index dd00024..6a4a9a1 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -45,6 +45,16 @@ #include "ospfd/ospf_ldp_sync.h" #include "ospfd/ospf_routemap_nb.h" +#define OSPFD_STATE_NAME "%s/ospfd.json", frr_libstatedir +#define OSPFD_INST_STATE_NAME(i) "%s/ospfd-%d.json", frr_runstatedir, i + +/* this one includes the path... because the instance number was in the path + * before :( ... which totally didn't have a mkdir anywhere. + */ +#define OSPFD_COMPAT_STATE_NAME "%s/ospfd-gr.json", frr_libstatedir +#define OSPFD_COMPAT_INST_STATE_NAME(i) \ + "%s-%d/ospfd-gr.json", frr_runstatedir, i + /* ospfd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN}; @@ -89,6 +99,7 @@ static void sigint(void) zlog_notice("Terminating on signal"); bfd_protocol_integration_set_shutdown(true); ospf_terminate(); + exit(0); } @@ -125,15 +136,31 @@ static const struct frr_yang_module_info *const ospfd_yang_modules[] = { &frr_ospf_route_map_info, }; -FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, +/* actual paths filled in main() */ +static char state_path[512]; +static char state_compat_path[512]; +static char *state_paths[] = { + state_path, + state_compat_path, + NULL, +}; - .proghelp = "Implementation of the OSPFv2 routing protocol.", +/* clang-format off */ +FRR_DAEMON_INFO(ospfd, OSPF, + .vty_port = OSPF_VTY_PORT, + .proghelp = "Implementation of the OSPFv2 routing protocol.", - .signals = ospf_signals, .n_signals = array_size(ospf_signals), + .signals = ospf_signals, + .n_signals = array_size(ospf_signals), - .privs = &ospfd_privs, .yang_modules = ospfd_yang_modules, - .n_yang_modules = array_size(ospfd_yang_modules), + .privs = &ospfd_privs, + + .yang_modules = ospfd_yang_modules, + .n_yang_modules = array_size(ospfd_yang_modules), + + .state_paths = state_paths, ); +/* clang-format on */ /** Max wait time for config to load before accepting hellos */ #define OSPF_PRE_CONFIG_MAX_WAIT_SECONDS 600 @@ -207,6 +234,17 @@ int main(int argc, char **argv) exit(1); } + if (ospf_instance) { + snprintf(state_path, sizeof(state_path), + OSPFD_INST_STATE_NAME(ospf_instance)); + snprintf(state_compat_path, sizeof(state_compat_path), + OSPFD_COMPAT_INST_STATE_NAME(ospf_instance)); + } else { + snprintf(state_path, sizeof(state_path), OSPFD_STATE_NAME); + snprintf(state_compat_path, sizeof(state_compat_path), + OSPFD_COMPAT_STATE_NAME); + } + /* OSPF master init. */ ospf_master_init(frr_init()); diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index bcbe028..08aa103 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -107,7 +107,7 @@ static void nsm_timer_set(struct ospf_neighbor *nbr) case NSM_Down: EVENT_OFF(nbr->t_inactivity); EVENT_OFF(nbr->t_hello_reply); - /* fallthru */ + fallthrough; case NSM_Attempt: case NSM_Init: case NSM_TwoWay: @@ -219,7 +219,7 @@ static int ospf_db_summary_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) case OSPF_OPAQUE_LINK_LSA: /* Exclude type-9 LSAs that does not have the same "oi" with * "nbr". */ - if (ospf_if_exists(lsa->oi) != nbr->oi) + if (lsa->oi != nbr->oi) return 0; break; case OSPF_OPAQUE_AREA_LSA: diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 27f47a6..5d2d656 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -257,7 +257,7 @@ static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, struct ospf_opaque_functab { uint8_t opaque_type; - struct opaque_info_per_type *oipt; + uint32_t ref_count; int (*new_if_hook)(struct interface *ifp); int (*del_if_hook)(struct interface *ifp); @@ -280,9 +280,28 @@ static struct list *ospf_opaque_type9_funclist; static struct list *ospf_opaque_type10_funclist; static struct list *ospf_opaque_type11_funclist; +static void ospf_opaque_functab_ref(struct ospf_opaque_functab *functab) +{ + functab->ref_count++; +} + +static void ospf_opaque_functab_deref(struct ospf_opaque_functab *functab) +{ + assert(functab->ref_count); + functab->ref_count--; + if (functab->ref_count == 0) + XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, functab); +} + static void ospf_opaque_del_functab(void *val) { - XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, val); + struct ospf_opaque_functab *functab = (struct ospf_opaque_functab *)val; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Opaque LSA functab list deletion callback type %u (%p)", + __func__, functab->opaque_type, functab); + + ospf_opaque_functab_deref(functab); return; } @@ -290,6 +309,9 @@ static void ospf_opaque_funclist_init(void) { struct list *funclist; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Function list initialize", __func__); + funclist = ospf_opaque_wildcard_funclist = list_new(); funclist->del = ospf_opaque_del_functab; @@ -308,6 +330,9 @@ static void ospf_opaque_funclist_term(void) { struct list *funclist; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Function list terminate", __func__); + funclist = ospf_opaque_wildcard_funclist; list_delete(&funclist); @@ -383,18 +408,24 @@ int ospf_register_opaque_functab( for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) if (functab->opaque_type == opaque_type) { - flog_warn( - EC_OSPF_LSA, - "%s: Duplicated entry?: lsa_type(%u), opaque_type(%u)", - __func__, lsa_type, opaque_type); - return -1; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Opaque LSA functab found type %u, (%p)", + __func__, functab->opaque_type, + functab); + break; } - new = XCALLOC(MTYPE_OSPF_OPAQUE_FUNCTAB, - sizeof(struct ospf_opaque_functab)); + if (functab == NULL) + new = XCALLOC(MTYPE_OSPF_OPAQUE_FUNCTAB, + sizeof(struct ospf_opaque_functab)); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Re-register Opaque LSA type %u, opaque type %u, (%p)", + __func__, lsa_type, opaque_type, functab); + return 0; + } new->opaque_type = opaque_type; - new->oipt = NULL; new->new_if_hook = new_if_hook; new->del_if_hook = del_if_hook; new->ism_change_hook = ism_change_hook; @@ -408,7 +439,12 @@ int ospf_register_opaque_functab( new->new_lsa_hook = new_lsa_hook; new->del_lsa_hook = del_lsa_hook; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Register Opaque LSA type %u, opaque type %u, (%p)", + __func__, lsa_type, opaque_type, new); + listnode_add(funclist, new); + ospf_opaque_functab_ref(new); return 0; } @@ -422,15 +458,18 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type) if ((funclist = ospf_get_opaque_funclist(lsa_type)) != NULL) for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) { if (functab->opaque_type == opaque_type) { - /* Cleanup internal control information, if it - * still remains. */ - if (functab->oipt != NULL) - free_opaque_info_per_type(functab->oipt, - true); - /* Dequeue listnode entry from the list. */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Delete Opaque functab LSA type %u, opaque type %u, (%p)", + __func__, lsa_type, + opaque_type, functab); + + /* Dequeue listnode entry from the function table + * list coreesponding to the opaque LSA type. + * Note that the list deletion callback frees + * the functab entry memory. + */ listnode_delete(funclist, functab); - - XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, functab); + ospf_opaque_functab_deref(functab); break; } } @@ -565,10 +604,15 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, oipt->opaque_type = GET_OPAQUE_TYPE(ntohl(new->data->id.s_addr)); oipt->status = PROC_NORMAL; oipt->functab = functab; - functab->oipt = oipt; + ospf_opaque_functab_ref(functab); oipt->id_list = list_new(); oipt->id_list->del = free_opaque_info_per_id; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Register Opaque info-per-type LSA type %u, opaque type %u, (%p), Functab (%p)", + __func__, oipt->lsa_type, oipt->opaque_type, oipt, + oipt->functab); + out: return oipt; } @@ -614,6 +658,15 @@ static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, } listnode_delete(l, oipt); } + + if (oipt->functab) + ospf_opaque_functab_deref(oipt->functab); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Free Opaque info-per-type LSA type %u, opaque type %u, (%p), Functab (%p)", + __func__, oipt->lsa_type, oipt->opaque_type, oipt, + oipt->functab); + XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt); return; } @@ -746,6 +799,45 @@ int ospf_opaque_is_owned(struct ospf_lsa *lsa) return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL); } +/* + * Cleanup Link-Local LSAs assocaited with an interface that is being deleted. + * Since these LSAs are stored in the area link state database (LSDB) as opposed + * to a separate per-interface, they must be deleted from the area database. + * Since their flooding scope is solely the deleted OSPF interface, there is no + * need to attempt to flush them from the routing domain. For link local LSAs + * originated via the OSPF server API, LSA deletion before interface deletion + * is required so that the callback can access the OSPF interface address. + */ +void ospf_opaque_type9_lsa_if_cleanup(struct ospf_interface *oi) +{ + struct route_node *rn; + struct ospf_lsdb *lsdb; + struct ospf_lsa *lsa; + + lsdb = oi->area->lsdb; + LSDB_LOOP (OPAQUE_LINK_LSDB(oi->area), rn, lsa) + /* + * While the LSA shouldn't be referenced on any LSA + * lists since the flooding scoped is confined to the + * interface being deleted, clear the pointer to the + * deleted interface to avoid references and set the + * age to MAXAGE to avoid flush processing when the LSA + * is removed from the interface opaque info list. + */ + if (lsa->oi == oi) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Delete Type-9 Opaque-LSA on interface delete: [opaque-type=%u, opaque-id=%x]", + GET_OPAQUE_TYPE( + ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl( + lsa->data->id.s_addr))); + ospf_lsdb_delete(lsdb, lsa); + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + lsa->oi = NULL; + ospf_lsa_discard(lsa); + } +} + /*------------------------------------------------------------------------* * Following are (vty) configuration functions for Opaque-LSAs handling. *------------------------------------------------------------------------*/ @@ -1159,12 +1251,12 @@ void ospf_opaque_config_write_debug(struct vty *vty) void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, json_object *json) { - char buf[128], *bp; struct lsa_header *lsah = lsa->data; uint32_t lsid = ntohl(lsah->id.s_addr); uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); uint32_t opaque_id = GET_OPAQUE_ID(lsid); struct ospf_opaque_functab *functab; + json_object *jopaque = NULL; int len, lenValid; /* Switch output functionality by vty address. */ @@ -1185,17 +1277,14 @@ void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, ospf_opaque_type_name(opaque_type)); json_object_int_add(json, "opaqueId", opaque_id); len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; - json_object_int_add(json, "opaqueDataLength", len); + json_object_int_add(json, "opaqueLength", len); lenValid = VALID_OPAQUE_INFO_LEN(lsah); - json_object_boolean_add(json, "opaqueDataLengthValid", + json_object_boolean_add(json, "opaqueLengthValid", lenValid); if (lenValid) { - bp = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), - "%*pHXn", (int)len, - (lsah + 1)); - json_object_string_add(json, "opaqueData", buf); - if (bp != buf) - XFREE(MTYPE_TMP, bp); + jopaque = json_object_new_object(); + json_object_object_add(json, "opaqueValues", + jopaque); } } } else { @@ -1212,7 +1301,7 @@ void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, /* Call individual output functions. */ if ((functab = ospf_opaque_functab_lookup(lsa)) != NULL) if (functab->show_opaque_info != NULL) - (*functab->show_opaque_info)(vty, json, lsa); + (*functab->show_opaque_info)(vty, jopaque, lsa); return; } diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h index e0c78cd..54651f8 100644 --- a/ospfd/ospf_opaque.h +++ b/ospfd/ospf_opaque.h @@ -109,6 +109,7 @@ extern void ospf_opaque_term(void); extern void ospf_opaque_finish(void); extern int ospf_opaque_type9_lsa_init(struct ospf_interface *oi); extern void ospf_opaque_type9_lsa_term(struct ospf_interface *oi); +extern void ospf_opaque_type9_lsa_if_cleanup(struct ospf_interface *oi); extern int ospf_opaque_type10_lsa_init(struct ospf_area *area); extern void ospf_opaque_type10_lsa_term(struct ospf_area *area); extern int ospf_opaque_type11_lsa_init(struct ospf *ospf); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index b37efa3..4bf4ae9 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1042,7 +1042,7 @@ static void ospf_db_desc_proc(struct stream *s, struct ospf_interface *oi, /* Neighbour has a more recent LSA, we must request it */ ospf_ls_request_add(nbr, new); - /* fallthru */ + fallthrough; case 0: /* If we have a copy of this LSA, it's either less * recent @@ -1231,7 +1231,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, through to case ExStart below. */ if (nbr->state != NSM_ExStart) break; - /* fallthru */ + fallthrough; case NSM_ExStart: /* Initial DBD */ if ((IS_SET_DD_ALL(dd->flags) == OSPF_DD_FLAG_ALL) @@ -1641,7 +1641,7 @@ static struct list *ospf_ls_upd_list_lsa(struct ospf_neighbor *nbr, case OSPF_OPAQUE_LINK_LSA: lsa->oi = oi; /* Remember incoming interface for flooding control. */ - /* Fallthrough */ + fallthrough; default: lsa->area = oi->area; break; diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index 725443f..99326b4 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -24,6 +24,7 @@ #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "mpls.h" +#include #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -153,6 +154,7 @@ static int ospf_router_info_unregister(void) void ospf_router_info_term(void) { + list_delete(&OspfRI.area_info); list_delete(&OspfRI.pce_info.pce_domain); list_delete(&OspfRI.pce_info.pce_neighbor); @@ -1215,15 +1217,20 @@ static int ospf_router_info_lsa_update(struct ospf_lsa *lsa) } \ } while (0) -static uint16_t show_vty_router_cap(struct vty *vty, struct tlv_header *tlvh) +static uint16_t show_vty_router_cap(struct vty *vty, struct tlv_header *tlvh, + json_object *json) { struct ri_tlv_router_cap *top = (struct ri_tlv_router_cap *)tlvh; check_tlv_size(RI_TLV_CAPABILITIES_SIZE, "Router Capabilities"); if (vty != NULL) - vty_out(vty, " Router Capabilities: 0x%x\n", - ntohl(top->value)); + if (!json) + vty_out(vty, " Router Capabilities: 0x%x\n", + ntohl(top->value)); + else + json_object_string_addf(json, "routerCapabilities", + "0x%x", ntohl(top->value)); else zlog_debug(" Router Capabilities: 0x%x", ntohl(top->value)); @@ -1231,7 +1238,8 @@ static uint16_t show_vty_router_cap(struct vty *vty, struct tlv_header *tlvh) } static uint16_t show_vty_pce_subtlv_address(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ri_pce_subtlv_address *top = (struct ri_pce_subtlv_address *)tlvh; @@ -1239,20 +1247,28 @@ static uint16_t show_vty_pce_subtlv_address(struct vty *vty, if (ntohs(top->address.type) == PCE_ADDRESS_IPV4) { check_tlv_size(PCE_ADDRESS_IPV4_SIZE, "PCE Address"); if (vty != NULL) - vty_out(vty, " PCE Address: %pI4\n", - &top->address.value); + if (!json) + vty_out(vty, " PCE Address: %pI4\n", + &top->address.value); + else + json_object_string_addf(json, "pceAddress", + "%pI4", + &top->address.value); else zlog_debug(" PCE Address: %pI4", &top->address.value); } else if (ntohs(top->address.type) == PCE_ADDRESS_IPV6) { - /* TODO: Add support to IPv6 with inet_ntop() */ check_tlv_size(PCE_ADDRESS_IPV6_SIZE, "PCE Address"); if (vty != NULL) - vty_out(vty, " PCE Address: 0x%x\n", - ntohl(top->address.value.s_addr)); + if (!json) + vty_out(vty, + " PCE Address: unsupported IPv6\n"); + else + json_object_string_add(json, "pceAddress", + "unsupported IPv6"); + else - zlog_debug(" PCE Address: 0x%x", - ntohl(top->address.value.s_addr)); + zlog_debug(" PCE Address: unsupported IPv6"); } else { if (vty != NULL) vty_out(vty, " Wrong PCE Address type: 0x%x\n", @@ -1266,7 +1282,8 @@ static uint16_t show_vty_pce_subtlv_address(struct vty *vty, } static uint16_t show_vty_pce_subtlv_path_scope(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ri_pce_subtlv_path_scope *top = (struct ri_pce_subtlv_path_scope *)tlvh; @@ -1274,7 +1291,12 @@ static uint16_t show_vty_pce_subtlv_path_scope(struct vty *vty, check_tlv_size(RI_PCE_SUBTLV_PATH_SCOPE_SIZE, "PCE Path Scope"); if (vty != NULL) - vty_out(vty, " PCE Path Scope: 0x%x\n", ntohl(top->value)); + if (!json) + vty_out(vty, " PCE Path Scope: 0x%x\n", + ntohl(top->value)); + else + json_object_string_addf(json, "pcePathScope", "0x%x", + ntohl(top->value)); else zlog_debug(" PCE Path Scope: 0x%x", ntohl(top->value)); @@ -1282,7 +1304,8 @@ static uint16_t show_vty_pce_subtlv_path_scope(struct vty *vty, } static uint16_t show_vty_pce_subtlv_domain(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ri_pce_subtlv_domain *top = (struct ri_pce_subtlv_domain *)tlvh; struct in_addr tmp; @@ -1292,13 +1315,21 @@ static uint16_t show_vty_pce_subtlv_domain(struct vty *vty, if (ntohs(top->type) == PCE_DOMAIN_TYPE_AREA) { tmp.s_addr = top->value; if (vty != NULL) - vty_out(vty, " PCE Domain Area: %pI4\n", &tmp); + if (!json) + vty_out(vty, " PCE Domain Area: %pI4\n", &tmp); + else + json_object_string_addf(json, "pceDomainArea", + "%pI4", &tmp); else zlog_debug(" PCE Domain Area: %pI4", &tmp); } else if (ntohs(top->type) == PCE_DOMAIN_TYPE_AS) { if (vty != NULL) - vty_out(vty, " PCE Domain AS: %d\n", - ntohl(top->value)); + if (!json) + vty_out(vty, " PCE Domain AS: %d\n", + ntohl(top->value)); + else + json_object_int_add(json, "pceDomainAS", + ntohl(top->value)); else zlog_debug(" PCE Domain AS: %d", ntohl(top->value)); } else { @@ -1314,7 +1345,8 @@ static uint16_t show_vty_pce_subtlv_domain(struct vty *vty, } static uint16_t show_vty_pce_subtlv_neighbor(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ri_pce_subtlv_neighbor *top = @@ -1326,13 +1358,22 @@ static uint16_t show_vty_pce_subtlv_neighbor(struct vty *vty, if (ntohs(top->type) == PCE_DOMAIN_TYPE_AREA) { tmp.s_addr = top->value; if (vty != NULL) - vty_out(vty, " PCE Neighbor Area: %pI4\n", &tmp); + if (!json) + vty_out(vty, " PCE Neighbor Area: %pI4\n", + &tmp); + else + json_object_string_addf(json, "pceNeighborArea", + "%pI4", &tmp); else zlog_debug(" PCE Neighbor Area: %pI4", &tmp); } else if (ntohs(top->type) == PCE_DOMAIN_TYPE_AS) { if (vty != NULL) - vty_out(vty, " PCE Neighbor AS: %d\n", - ntohl(top->value)); + if (!json) + vty_out(vty, " PCE Neighbor AS: %d\n", + ntohl(top->value)); + else + json_object_int_add(json, "pceNeighborAS", + ntohl(top->value)); else zlog_debug(" PCE Neighbor AS: %d", ntohl(top->value)); @@ -1349,7 +1390,8 @@ static uint16_t show_vty_pce_subtlv_neighbor(struct vty *vty, } static uint16_t show_vty_pce_subtlv_cap_flag(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct ri_pce_subtlv_cap_flag *top = (struct ri_pce_subtlv_cap_flag *)tlvh; @@ -1357,8 +1399,12 @@ static uint16_t show_vty_pce_subtlv_cap_flag(struct vty *vty, check_tlv_size(RI_PCE_SUBTLV_CAP_FLAG_SIZE, "PCE Capabilities"); if (vty != NULL) - vty_out(vty, " PCE Capabilities Flag: 0x%x\n", - ntohl(top->value)); + if (!json) + vty_out(vty, " PCE Capabilities Flag: 0x%x\n", + ntohl(top->value)); + else + json_object_string_addf(json, "pceCapabilities", + "0x%x", ntohl(top->value)); else zlog_debug(" PCE Capabilities Flag: 0x%x", ntohl(top->value)); @@ -1367,8 +1413,10 @@ static uint16_t show_vty_pce_subtlv_cap_flag(struct vty *vty, } static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, json_object *json) { + json_object *obj; + if (TLV_SIZE(tlvh) > buf_size) { if (vty != NULL) vty_out(vty, @@ -1382,8 +1430,18 @@ static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, } if (vty != NULL) - vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", - ntohs(tlvh->type), ntohs(tlvh->length)); + if (!json) + vty_out(vty, + " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + else { + obj = json_object_new_object(); + json_object_string_addf(obj, "type", "0x%x", + ntohs(tlvh->type)); + json_object_string_addf(obj, "length", "0x%x", + ntohs(tlvh->length)); + json_object_object_add(json, "unknownTLV", obj); + } else zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]", ntohs(tlvh->type), ntohs(tlvh->length)); @@ -1392,7 +1450,7 @@ static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, } static uint16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri, - size_t buf_size) + size_t buf_size, json_object *json) { struct tlv_header *tlvh; uint16_t length = ntohs(ri->length); @@ -1409,22 +1467,23 @@ static uint16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri, for (tlvh = ri; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case RI_PCE_SUBTLV_ADDRESS: - sum += show_vty_pce_subtlv_address(vty, tlvh); + sum += show_vty_pce_subtlv_address(vty, tlvh, json); break; case RI_PCE_SUBTLV_PATH_SCOPE: - sum += show_vty_pce_subtlv_path_scope(vty, tlvh); + sum += show_vty_pce_subtlv_path_scope(vty, tlvh, json); break; case RI_PCE_SUBTLV_DOMAIN: - sum += show_vty_pce_subtlv_domain(vty, tlvh); + sum += show_vty_pce_subtlv_domain(vty, tlvh, json); break; case RI_PCE_SUBTLV_NEIGHBOR: - sum += show_vty_pce_subtlv_neighbor(vty, tlvh); + sum += show_vty_pce_subtlv_neighbor(vty, tlvh, json); break; case RI_PCE_SUBTLV_CAP_FLAG: - sum += show_vty_pce_subtlv_cap_flag(vty, tlvh); + sum += show_vty_pce_subtlv_cap_flag(vty, tlvh, json); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + sum += show_vty_unknown_tlv(vty, tlvh, length - sum, + json); break; } } @@ -1432,33 +1491,62 @@ static uint16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri, } /* Display Segment Routing Algorithm TLV information */ -static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh) +static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh, + json_object *json) { struct ri_sr_tlv_sr_algorithm *algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; int i; + json_object *json_algo, *obj; + char buf[2]; check_tlv_size(ALGORITHM_COUNT, "Segment Routing Algorithm"); - if (vty != NULL) { - vty_out(vty, " Segment Routing Algorithm TLV:\n"); - for (i = 0; i < ntohs(algo->header.length); i++) { - switch (algo->value[i]) { - case 0: - vty_out(vty, " Algorithm %d: SPF\n", i); - break; - case 1: - vty_out(vty, " Algorithm %d: Strict SPF\n", - i); - break; - default: - vty_out(vty, - " Algorithm %d: Unknown value %d\n", i, - algo->value[i]); - break; + if (vty != NULL) + if (!json) { + vty_out(vty, " Segment Routing Algorithm TLV:\n"); + for (i = 0; i < ntohs(algo->header.length); i++) { + switch (algo->value[i]) { + case 0: + vty_out(vty, + " Algorithm %d: SPF\n", i); + break; + case 1: + vty_out(vty, + " Algorithm %d: Strict SPF\n", + i); + break; + default: + vty_out(vty, + " Algorithm %d: Unknown value %d\n", i, + algo->value[i]); + break; + } + } + } else { + json_algo = json_object_new_array(); + json_object_object_add(json, "algorithms", + json_algo); + for (i = 0; i < ntohs(algo->header.length); i++) { + obj = json_object_new_object(); + snprintfrr(buf, 2, "%d", i); + switch (algo->value[i]) { + case 0: + json_object_string_add(obj, buf, "SPF"); + break; + case 1: + json_object_string_add(obj, buf, + "strictSPF"); + break; + default: + json_object_string_add(obj, buf, + "unknown"); + break; + } + json_object_array_add(json_algo, obj); } } - } else { + else { zlog_debug(" Segment Routing Algorithm TLV:"); for (i = 0; i < ntohs(algo->header.length); i++) switch (algo->value[i]) { @@ -1479,24 +1567,47 @@ static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh) } /* Display Segment Routing SID/Label Range TLV information */ -static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh) +static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh, + json_object *json) { struct ri_sr_tlv_sid_label_range *range = (struct ri_sr_tlv_sid_label_range *)tlvh; + json_object *obj; + uint32_t upper; check_tlv_size(RI_SR_TLV_LABEL_RANGE_SIZE, "SR Label Range"); - if (vty != NULL) { - vty_out(vty, - " Segment Routing %s Range TLV:\n" - " Range Size = %d\n" - " SID Label = %d\n\n", - ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE - ? "Global" - : "Local", - GET_RANGE_SIZE(ntohl(range->size)), - GET_LABEL(ntohl(range->lower.value))); - } else { + if (vty != NULL) + if (!json) { + vty_out(vty, + " Segment Routing %s Range TLV:\n" + " Range Size = %d\n" + " SID Label = %d\n\n", + ntohs(range->header.type) == + RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", + GET_RANGE_SIZE(ntohl(range->size)), + GET_LABEL(ntohl(range->lower.value))); + } else { + /* + * According to draft-ietf-teas-yang-sr-te-topo, SRGB + * and SRLB are describe with lower and upper bounds + */ + upper = GET_LABEL(ntohl(range->lower.value)) + + GET_RANGE_SIZE(ntohl(range->size)) - 1; + obj = json_object_new_object(); + json_object_int_add(obj, "upperBound", upper); + json_object_int_add(obj, "lowerBound", + GET_LABEL(ntohl(range->lower.value))); + json_object_object_add(json, + ntohs(range->header.type) == + RI_SR_TLV_SRGB_LABEL_RANGE + ? "srgb" + : "srlb", + obj); + } + else { zlog_debug( " Segment Routing %s Range TLV: Range Size = %d SID Label = %d", ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE @@ -1510,22 +1621,25 @@ static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh) } /* Display Segment Routing Maximum Stack Depth TLV information */ -static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh) +static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh, + json_object *json) { struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh; check_tlv_size(RI_SR_TLV_NODE_MSD_SIZE, "Node Maximum Stack Depth"); - if (vty != NULL) { - vty_out(vty, - " Segment Routing MSD TLV:\n" - " Node Maximum Stack Depth = %d\n", - msd->value); - } else { + if (vty != NULL) + if (!json) + vty_out(vty, + " Segment Routing MSD TLV:\n" + " Node Maximum Stack Depth = %d\n", + msd->value); + else + json_object_int_add(json, "nodeMsd", msd->value); + else zlog_debug( " Segment Routing MSD TLV: Node Maximum Stack Depth = %d", msd->value); - } return TLV_SIZE(tlvh); } @@ -1537,9 +1651,14 @@ static void ospf_router_info_show_info(struct vty *vty, struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; + json_object *jri = NULL, *jpce = NULL, *jsr = NULL; - if (json) - return; + if (json) { + jri = json_object_new_object(); + json_object_object_add(json, "routerInformation", jri); + jpce = json_object_new_object(); + jsr = json_object_new_object(); + } /* Initialize TLV browsing */ length = lsa->size - OSPF_LSA_HEADER_SIZE; @@ -1548,30 +1667,36 @@ static void ospf_router_info_show_info(struct vty *vty, tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case RI_TLV_CAPABILITIES: - sum += show_vty_router_cap(vty, tlvh); + sum += show_vty_router_cap(vty, tlvh, jri); break; case RI_TLV_PCE: tlvh++; sum += TLV_HDR_SIZE; - sum += show_vty_pce_info(vty, tlvh, length - sum); + sum += show_vty_pce_info(vty, tlvh, length - sum, jpce); break; case RI_SR_TLV_SR_ALGORITHM: - sum += show_vty_sr_algorithm(vty, tlvh); + sum += show_vty_sr_algorithm(vty, tlvh, jsr); break; case RI_SR_TLV_SRGB_LABEL_RANGE: case RI_SR_TLV_SRLB_LABEL_RANGE: - sum += show_vty_sr_range(vty, tlvh); + sum += show_vty_sr_range(vty, tlvh, jsr); break; case RI_SR_TLV_NODE_MSD: - sum += show_vty_sr_msd(vty, tlvh); + sum += show_vty_sr_msd(vty, tlvh, jsr); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, length); + sum += show_vty_unknown_tlv(vty, tlvh, length, jri); break; } } + if (json) { + if (json_object_object_length(jpce) > 1) + json_object_object_add(jri, "pceInformation", jpce); + if (json_object_object_length(jsr) > 1) + json_object_object_add(jri, "segmentRouting", jsr); + } return; } @@ -1722,9 +1847,11 @@ DEFUN (router_info, DEFUN (no_router_info, no_router_info_cmd, - "no router-info", + "no router-info []", NO_STR - "Disable the Router Information functionality\n") + "Disable the Router Information functionality\n" + "Disable the Router Information functionality with AS flooding scope\n" + "Disable the Router Information functionality with Area flooding scope\n") { if (!OspfRI.enabled) @@ -2042,7 +2169,7 @@ DEFUN (show_ip_ospf_router_info, if (OspfRI.enabled) { vty_out(vty, "--- Router Information parameters ---\n"); - show_vty_router_cap(vty, &OspfRI.router_cap.header); + show_vty_router_cap(vty, &OspfRI.router_cap.header, NULL); } else { if (vty != NULL) vty_out(vty, @@ -2071,27 +2198,32 @@ DEFUN (show_ip_opsf_router_info_pce, if (pce->pce_address.header.type != 0) show_vty_pce_subtlv_address(vty, - &pce->pce_address.header); + &pce->pce_address.header, + NULL); if (pce->pce_scope.header.type != 0) show_vty_pce_subtlv_path_scope(vty, - &pce->pce_scope.header); + &pce->pce_scope.header, + NULL); for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) { if (domain->header.type != 0) show_vty_pce_subtlv_domain(vty, - &domain->header); + &domain->header, + NULL); } for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) { if (neighbor->header.type != 0) show_vty_pce_subtlv_neighbor(vty, - &neighbor->header); + &neighbor->header, + NULL); } if (pce->pce_cap_flag.header.type != 0) show_vty_pce_subtlv_cap_flag(vty, - &pce->pce_cap_flag.header); + &pce->pce_cap_flag.header, + NULL); } else { vty_out(vty, " PCE info is disabled on this router\n"); diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index c9aaa9f..fc0c143 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -1348,7 +1348,7 @@ static int ospf_snmp_if_update(struct interface *ifp) ifindex = 0; /* Lookup first IPv4 address entry. */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { p = CONNECTED_ID(ifc); if (p->family == AF_INET) { @@ -1396,11 +1396,10 @@ static int ospf_snmp_if_update(struct interface *ifp) static int ospf_snmp_is_if_have_addr(struct interface *ifp) { - struct listnode *nn; struct connected *ifc; /* Is this interface having any connected IPv4 address ? */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { if (CONNECTED_PREFIX(ifc)->family == AF_INET) return 1; } diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index 467cb05..e26fe6f 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -580,6 +580,7 @@ static void ospf_sr_stop(void) hash_clean(OspfSR.neighbors, (void *)sr_node_del); OspfSR.self = NULL; OspfSR.status = SR_OFF; + OspfSR.msd = 0; } /* diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 3cf39e5..80ebab3 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -31,6 +31,7 @@ #include "link_state.h" #include "zclient.h" #include "printfrr.h" +#include #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -980,8 +981,8 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) struct ospf_interface *oi = nbr->oi; struct mpls_te_link *lp; - /* Process Neighbor only when its state is NSM Full */ - if (nbr->state != NSM_Full) + /* Process Link only when neighbor old or new state is NSM Full */ + if (nbr->state != NSM_Full && old_state != NSM_Full) return; /* Get interface information for Traffic Engineering */ @@ -1839,6 +1840,7 @@ static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = addr; + ote_debug(" |- Delete Subnet info. for Prefix %pFX", &p); subnet = ls_find_subnet(ted, &p); /* Remove subnet if found */ @@ -1851,8 +1853,7 @@ static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) /** * Parse Router LSA. This function will create or update corresponding Vertex, - * Edge and Subnet. It also remove Edge and Subnet if they are marked as Orphan - * once Router LSA is parsed. + * Edge and Subnet. * * @param ted Link State Traffic Engineering Database * @param lsa OSPF Link State Advertisement @@ -1864,9 +1865,6 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) struct router_lsa *rl; enum ls_node_type type; struct ls_vertex *vertex; - struct ls_edge *edge; - struct ls_subnet *subnet; - struct listnode *node; int len, links; /* Sanity Check */ @@ -1909,13 +1907,6 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) vertex->status = SYNC; } - /* Mark outgoing Edge and Subnet as ORPHAN to detect deletion */ - for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) - edge->status = ORPHAN; - - for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) - subnet->status = ORPHAN; - /* Then, process Link Information */ len = lsa->size - OSPF_LSA_HEADER_SIZE - OSPF_ROUTER_LSA_MIN_SIZE; links = ntohs(rl->links); @@ -1948,11 +1939,6 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) break; } } - /* Clean remaining Orphan Edges or Subnets */ - if (OspfMplsTE.export) - ls_vertex_clean(ted, vertex, zclient); - else - ls_vertex_clean(ted, vertex, NULL); return 0; } @@ -2260,6 +2246,10 @@ static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) } /* Get corresponding Edge from Link State Data Base */ + if (IPV4_NET0(attr.standard.local.s_addr) && !attr.standard.local_id) { + ote_debug(" |- Found no TE Link local address/ID. Abort!"); + return -1; + } edge = get_edge(ted, attr.adv, attr.standard.local); old = edge->attributes; @@ -2405,7 +2395,10 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) ote_debug(" |- Delete TE info. for Edge %pI4", &edge->attributes->standard.local); - /* Remove Link State Attributes TE information */ + /* First remove the associated Subnet */ + ospf_te_delete_subnet(ted, attr->standard.local); + + /* Then ,remove Link State Attributes TE information */ memset(&attr->standard, 0, sizeof(struct ls_standard)); attr->flags &= 0x0FFFF; memset(&attr->extended, 0, sizeof(struct ls_extended)); @@ -2420,7 +2413,6 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) edge->status = SYNC; } else { /* Remove completely the Edge if Segment Routing is not set */ - ospf_te_delete_subnet(ted, attr->standard.local); edge->status = DELETE; ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); ls_edge_del_all(ted, edge); @@ -3154,14 +3146,19 @@ static void ospf_te_init_ted(struct ls_ted *ted, struct ospf *ospf) } \ } while (0) -static uint16_t show_vty_router_addr(struct vty *vty, struct tlv_header *tlvh) +static uint16_t show_vty_router_addr(struct vty *vty, struct tlv_header *tlvh, + json_object *json) { struct te_tlv_router_addr *top = (struct te_tlv_router_addr *)tlvh; check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Router Address"); if (vty != NULL) - vty_out(vty, " Router-Address: %pI4\n", &top->value); + if (!json) + vty_out(vty, " Router-Address: %pI4\n", &top->value); + else + json_object_string_addf(json, "routerAddress", "%pI4", + &top->value); else zlog_debug(" Router-Address: %pI4", &top->value); @@ -3169,7 +3166,7 @@ static uint16_t show_vty_router_addr(struct vty *vty, struct tlv_header *tlvh) } static uint16_t show_vty_link_header(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, json_object *json) { struct te_tlv_link *top = (struct te_tlv_link *)tlvh; @@ -3186,8 +3183,12 @@ static uint16_t show_vty_link_header(struct vty *vty, struct tlv_header *tlvh, } if (vty != NULL) - vty_out(vty, " Link: %u octets of data\n", - ntohs(top->header.length)); + if (!json) + vty_out(vty, " Link: %u octets of data\n", + ntohs(top->header.length)); + else + json_object_int_add(json, "teLinkDataLength", + ntohs(top->header.length)); else zlog_debug(" Link: %u octets of data", ntohs(top->header.length)); @@ -3196,7 +3197,8 @@ static uint16_t show_vty_link_header(struct vty *vty, struct tlv_header *tlvh, } static uint16_t show_vty_link_subtlv_link_type(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_link_type *top; const char *cp = "Unknown"; @@ -3216,8 +3218,11 @@ static uint16_t show_vty_link_subtlv_link_type(struct vty *vty, } if (vty != NULL) - vty_out(vty, " Link-Type: %s (%u)\n", cp, - top->link_type.value); + if (!json) + vty_out(vty, " Link-Type: %s (%u)\n", cp, + top->link_type.value); + else + json_object_string_add(json, "accessType", cp); else zlog_debug(" Link-Type: %s (%u)", cp, top->link_type.value); @@ -3225,7 +3230,8 @@ static uint16_t show_vty_link_subtlv_link_type(struct vty *vty, } static uint16_t show_vty_link_subtlv_link_id(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_link_id *top; @@ -3233,7 +3239,11 @@ static uint16_t show_vty_link_subtlv_link_id(struct vty *vty, top = (struct te_link_subtlv_link_id *)tlvh; if (vty != NULL) - vty_out(vty, " Link-ID: %pI4\n", &top->value); + if (!json) + vty_out(vty, " Link-ID: %pI4\n", &top->value); + else + json_object_string_addf(json, "linkID", "%pI4", + &top->value); else zlog_debug(" Link-ID: %pI4", &top->value); @@ -3242,9 +3252,12 @@ static uint16_t show_vty_link_subtlv_link_id(struct vty *vty, static uint16_t show_vty_link_subtlv_lclif_ipaddr(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, + json_object *json) { struct te_link_subtlv_lclif_ipaddr *top; + json_object *json_addr, *json_obj; + char buf[4]; int i, n; if (TLV_SIZE(tlvh) > buf_size) { @@ -3263,13 +3276,29 @@ static uint16_t show_vty_link_subtlv_lclif_ipaddr(struct vty *vty, n = ntohs(tlvh->length) / sizeof(top->value[0]); if (vty != NULL) - vty_out(vty, " Local Interface IP Address(es): %d\n", n); + if (!json) + vty_out(vty, " Local Interface IP Address(es): %d\n", + n); + else { + json_addr = json_object_new_array(); + json_object_object_add(json, "localIPAddresses", + json_addr); + } else zlog_debug(" Local Interface IP Address(es): %d", n); for (i = 0; i < n; i++) { if (vty != NULL) - vty_out(vty, " #%d: %pI4\n", i, &top->value[i]); + if (!json) + vty_out(vty, " #%d: %pI4\n", i, + &top->value[i]); + else { + json_obj = json_object_new_object(); + snprintfrr(buf, 2, "%d", i); + json_object_string_addf(json_obj, buf, "%pI4", + &top->value[i]); + json_object_array_add(json_addr, json_obj); + } else zlog_debug(" #%d: %pI4", i, &top->value[i]); } @@ -3278,9 +3307,12 @@ static uint16_t show_vty_link_subtlv_lclif_ipaddr(struct vty *vty, static uint16_t show_vty_link_subtlv_rmtif_ipaddr(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, + json_object *json) { struct te_link_subtlv_rmtif_ipaddr *top; + json_object *json_addr, *json_obj; + char buf[4]; int i, n; if (TLV_SIZE(tlvh) > buf_size) { @@ -3298,13 +3330,29 @@ static uint16_t show_vty_link_subtlv_rmtif_ipaddr(struct vty *vty, top = (struct te_link_subtlv_rmtif_ipaddr *)tlvh; n = ntohs(tlvh->length) / sizeof(top->value[0]); if (vty != NULL) - vty_out(vty, " Remote Interface IP Address(es): %d\n", n); + if (!json) + vty_out(vty, " Remote Interface IP Address(es): %d\n", + n); + else { + json_addr = json_object_new_array(); + json_object_object_add(json, "remoteIPAddresses", + json_addr); + } else zlog_debug(" Remote Interface IP Address(es): %d", n); for (i = 0; i < n; i++) { if (vty != NULL) - vty_out(vty, " #%d: %pI4\n", i, &top->value[i]); + if (!json) + vty_out(vty, " #%d: %pI4\n", i, + &top->value[i]); + else { + json_obj = json_object_new_object(); + snprintfrr(buf, 2, "%d", i); + json_object_string_addf(json_obj, buf, "%pI4", + &top->value[i]); + json_object_array_add(json_addr, json_obj); + } else zlog_debug(" #%d: %pI4", i, &top->value[i]); } @@ -3312,7 +3360,8 @@ static uint16_t show_vty_link_subtlv_rmtif_ipaddr(struct vty *vty, } static uint16_t show_vty_link_subtlv_te_metric(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_te_metric *top; @@ -3320,8 +3369,12 @@ static uint16_t show_vty_link_subtlv_te_metric(struct vty *vty, top = (struct te_link_subtlv_te_metric *)tlvh; if (vty != NULL) - vty_out(vty, " Traffic Engineering Metric: %u\n", - (uint32_t)ntohl(top->value)); + if (!json) + vty_out(vty, " Traffic Engineering Metric: %u\n", + (uint32_t)ntohl(top->value)); + else + json_object_int_add(json, "teDefaultMetric", + (uint32_t)ntohl(top->value)); else zlog_debug(" Traffic Engineering Metric: %u", (uint32_t)ntohl(top->value)); @@ -3330,7 +3383,8 @@ static uint16_t show_vty_link_subtlv_te_metric(struct vty *vty, } static uint16_t show_vty_link_subtlv_max_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_max_bw *top; float fval; @@ -3341,7 +3395,11 @@ static uint16_t show_vty_link_subtlv_max_bw(struct vty *vty, fval = ntohf(top->value); if (vty != NULL) - vty_out(vty, " Maximum Bandwidth: %g (Bytes/sec)\n", fval); + if (!json) + vty_out(vty, " Maximum Bandwidth: %g (Bytes/sec)\n", + fval); + else + json_object_double_add(json, "maxLinkBandwidth", fval); else zlog_debug(" Maximum Bandwidth: %g (Bytes/sec)", fval); @@ -3349,7 +3407,8 @@ static uint16_t show_vty_link_subtlv_max_bw(struct vty *vty, } static uint16_t show_vty_link_subtlv_max_rsv_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_max_rsv_bw *top; float fval; @@ -3360,8 +3419,12 @@ static uint16_t show_vty_link_subtlv_max_rsv_bw(struct vty *vty, fval = ntohf(top->value); if (vty != NULL) - vty_out(vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)\n", - fval); + if (!json) + vty_out(vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + fval); + else + json_object_double_add(json, "maxResvLinkBandwidth", + fval); else zlog_debug(" Maximum Reservable Bandwidth: %g (Bytes/sec)", fval); @@ -3370,18 +3433,27 @@ static uint16_t show_vty_link_subtlv_max_rsv_bw(struct vty *vty, } static uint16_t show_vty_link_subtlv_unrsv_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_unrsv_bw *top; + json_object *json_bw, *json_obj; float fval1, fval2; + char buf[16]; int i; check_tlv_size(TE_LINK_SUBTLV_UNRSV_SIZE, "Unreserved Bandwidth"); top = (struct te_link_subtlv_unrsv_bw *)tlvh; if (vty != NULL) - vty_out(vty, - " Unreserved Bandwidth per Class Type in Byte/s:\n"); + if (!json) + vty_out(vty, + " Unreserved Bandwidth per Class Type in Byte/s:\n"); + else { + json_bw = json_object_new_array(); + json_object_object_add(json, "unreservedBandwidth", + json_bw); + } else zlog_debug( " Unreserved Bandwidth per Class Type in Byte/s:"); @@ -3390,9 +3462,20 @@ static uint16_t show_vty_link_subtlv_unrsv_bw(struct vty *vty, fval2 = ntohf(top->value[i + 1]); if (vty != NULL) - vty_out(vty, - " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", - i, fval1, i + 1, fval2); + if (!json) + vty_out(vty, + " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + i, fval1, i + 1, fval2); + else { + json_obj = json_object_new_object(); + snprintfrr(buf, 12, "classType-%u", i); + json_object_double_add(json_obj, buf, fval1); + json_object_array_add(json_bw, json_obj); + json_obj = json_object_new_object(); + snprintfrr(buf, 12, "classType-%u", i + 1); + json_object_double_add(json_obj, buf, fval2); + json_object_array_add(json_bw, json_obj); + } else zlog_debug( " [%d]: %g (Bytes/sec), [%d]: %g (Bytes/sec)", @@ -3403,7 +3486,8 @@ static uint16_t show_vty_link_subtlv_unrsv_bw(struct vty *vty, } static uint16_t show_vty_link_subtlv_rsc_clsclr(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_rsc_clsclr *top; @@ -3411,8 +3495,13 @@ static uint16_t show_vty_link_subtlv_rsc_clsclr(struct vty *vty, top = (struct te_link_subtlv_rsc_clsclr *)tlvh; if (vty != NULL) - vty_out(vty, " Resource class/color: 0x%x\n", - (uint32_t)ntohl(top->value)); + if (!json) + vty_out(vty, " Resource class/color: 0x%x\n", + (uint32_t)ntohl(top->value)); + else + json_object_string_addf(json, "administrativeGroup", + "0x%x", + (uint32_t)ntohl(top->value)); else zlog_debug(" Resource Class/Color: 0x%x", (uint32_t)ntohl(top->value)); @@ -3421,7 +3510,8 @@ static uint16_t show_vty_link_subtlv_rsc_clsclr(struct vty *vty, } static uint16_t show_vty_link_subtlv_lrrid(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_lrrid *top; @@ -3430,10 +3520,17 @@ static uint16_t show_vty_link_subtlv_lrrid(struct vty *vty, top = (struct te_link_subtlv_lrrid *)tlvh; if (vty != NULL) { - vty_out(vty, " Local TE Router ID: %pI4\n", - &top->local); - vty_out(vty, " Remote TE Router ID: %pI4\n", - &top->remote); + if (!json) { + vty_out(vty, " Local TE Router ID: %pI4\n", + &top->local); + vty_out(vty, " Remote TE Router ID: %pI4\n", + &top->remote); + } else { + json_object_string_addf(json, "localTeRouterID", "%pI4", + &top->local); + json_object_string_addf(json, "remoteTeRouterID", + "%pI4", &top->remote); + } } else { zlog_debug(" Local TE Router ID: %pI4", &top->local); @@ -3445,7 +3542,8 @@ static uint16_t show_vty_link_subtlv_lrrid(struct vty *vty, } static uint16_t show_vty_link_subtlv_llri(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_llri *top; @@ -3454,10 +3552,17 @@ static uint16_t show_vty_link_subtlv_llri(struct vty *vty, top = (struct te_link_subtlv_llri *)tlvh; if (vty != NULL) { - vty_out(vty, " Link Local ID: %d\n", - (uint32_t)ntohl(top->local)); - vty_out(vty, " Link Remote ID: %d\n", - (uint32_t)ntohl(top->remote)); + if (!json) { + vty_out(vty, " Link Local ID: %d\n", + (uint32_t)ntohl(top->local)); + vty_out(vty, " Link Remote ID: %d\n", + (uint32_t)ntohl(top->remote)); + } else { + json_object_int_add(json, "localLinkID", + (uint32_t)ntohl(top->local)); + json_object_int_add(json, "remoteLinkID", + (uint32_t)ntohl(top->remote)); + } } else { zlog_debug(" Link Local ID: %d", (uint32_t)ntohl(top->local)); @@ -3469,7 +3574,8 @@ static uint16_t show_vty_link_subtlv_llri(struct vty *vty, } static uint16_t show_vty_link_subtlv_rip(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_rip *top; @@ -3478,8 +3584,12 @@ static uint16_t show_vty_link_subtlv_rip(struct vty *vty, top = (struct te_link_subtlv_rip *)tlvh; if (vty != NULL) - vty_out(vty, " Inter-AS TE Remote ASBR IP address: %pI4\n", - &top->value); + if (!json) + vty_out(vty, " Inter-AS TE Remote ASBR IP address: %pI4\n", + &top->value); + else + json_object_string_addf(json, "remoteAsbrAddress", + "%pI4", &top->value); else zlog_debug(" Inter-AS TE Remote ASBR IP address: %pI4", &top->value); @@ -3488,7 +3598,8 @@ static uint16_t show_vty_link_subtlv_rip(struct vty *vty, } static uint16_t show_vty_link_subtlv_ras(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_ras *top; @@ -3497,8 +3608,12 @@ static uint16_t show_vty_link_subtlv_ras(struct vty *vty, top = (struct te_link_subtlv_ras *)tlvh; if (vty != NULL) - vty_out(vty, " Inter-AS TE Remote AS number: %u\n", - ntohl(top->value)); + if (!json) + vty_out(vty, " Inter-AS TE Remote AS number: %u\n", + ntohl(top->value)); + else + json_object_int_add(json, "remoteAsbrNumber", + ntohl(top->value)); else zlog_debug(" Inter-AS TE Remote AS number: %u", ntohl(top->value)); @@ -3507,7 +3622,8 @@ static uint16_t show_vty_link_subtlv_ras(struct vty *vty, } static uint16_t show_vty_link_subtlv_av_delay(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_av_delay *top; uint32_t delay; @@ -3520,8 +3636,15 @@ static uint16_t show_vty_link_subtlv_av_delay(struct vty *vty, anomalous = (uint32_t)ntohl(top->value) & TE_EXT_ANORMAL; if (vty != NULL) - vty_out(vty, " %s Average Link Delay: %d (micro-sec)\n", - anomalous ? "Anomalous" : "Normal", delay); + if (!json) + vty_out(vty, " %s Average Link Delay: %d (micro-sec)\n", + anomalous ? "Anomalous" : "Normal", delay); + else { + json_object_int_add(json, "oneWayDelay", delay); + json_object_string_add(json, "oneWayDelayNormality", + anomalous ? "abnormal" + : "normal"); + } else zlog_debug(" %s Average Link Delay: %d (micro-sec)", anomalous ? "Anomalous" : "Normal", delay); @@ -3530,7 +3653,8 @@ static uint16_t show_vty_link_subtlv_av_delay(struct vty *vty, } static uint16_t show_vty_link_subtlv_mm_delay(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_mm_delay *top; uint32_t low, high; @@ -3544,8 +3668,20 @@ static uint16_t show_vty_link_subtlv_mm_delay(struct vty *vty, high = (uint32_t)ntohl(top->high); if (vty != NULL) - vty_out(vty, " %s Min/Max Link Delay: %d/%d (micro-sec)\n", - anomalous ? "Anomalous" : "Normal", low, high); + if (!json) + vty_out(vty, + " %s Min/Max Link Delay: %d/%d (micro-sec)\n", + anomalous ? "Anomalous" : "Normal", low, high); + else { + json_object_int_add(json, "oneWayMinDelay", low); + json_object_string_add(json, "oneWayMinDelayNormality", + anomalous ? "abnormal" + : "normal"); + json_object_int_add(json, "oneWayMaxDelay", high); + json_object_string_add(json, "oneWayMaxDelayNormality", + anomalous ? "abnormal" + : "normal"); + } else zlog_debug(" %s Min/Max Link Delay: %d/%d (micro-sec)", anomalous ? "Anomalous" : "Normal", low, high); @@ -3554,7 +3690,8 @@ static uint16_t show_vty_link_subtlv_mm_delay(struct vty *vty, } static uint16_t show_vty_link_subtlv_delay_var(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_delay_var *top; uint32_t jitter; @@ -3565,7 +3702,12 @@ static uint16_t show_vty_link_subtlv_delay_var(struct vty *vty, jitter = (uint32_t)ntohl(top->value) & TE_EXT_MASK; if (vty != NULL) - vty_out(vty, " Delay Variation: %d (micro-sec)\n", jitter); + if (!json) + vty_out(vty, " Delay Variation: %d (micro-sec)\n", + jitter); + else + json_object_int_add(json, "oneWayDelayVariation", + jitter); else zlog_debug(" Delay Variation: %d (micro-sec)", jitter); @@ -3573,7 +3715,8 @@ static uint16_t show_vty_link_subtlv_delay_var(struct vty *vty, } static uint16_t show_vty_link_subtlv_pkt_loss(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_pkt_loss *top; uint32_t loss; @@ -3588,8 +3731,16 @@ static uint16_t show_vty_link_subtlv_pkt_loss(struct vty *vty, anomalous = (uint32_t)ntohl(top->value) & TE_EXT_ANORMAL; if (vty != NULL) - vty_out(vty, " %s Link Loss: %g (%%)\n", - anomalous ? "Anomalous" : "Normal", fval); + if (!json) + vty_out(vty, " %s Link Loss: %g (%%)\n", + anomalous ? "Anomalous" : "Normal", fval); + else { + json_object_double_add(json, "oneWayPacketLoss", fval); + json_object_string_add(json, + "oneWayPacketLossNormality", + anomalous ? "abnormal" + : "normal"); + } else zlog_debug(" %s Link Loss: %g (%%)", anomalous ? "Anomalous" : "Normal", fval); @@ -3598,7 +3749,8 @@ static uint16_t show_vty_link_subtlv_pkt_loss(struct vty *vty, } static uint16_t show_vty_link_subtlv_res_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_res_bw *top; float fval; @@ -3609,9 +3761,13 @@ static uint16_t show_vty_link_subtlv_res_bw(struct vty *vty, fval = ntohf(top->value); if (vty != NULL) - vty_out(vty, - " Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", - fval); + if (!json) + vty_out(vty, + " Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", + fval); + else + json_object_double_add(json, "oneWayResidualBandwidth", + fval); else zlog_debug( " Unidirectional Residual Bandwidth: %g (Bytes/sec)", @@ -3621,7 +3777,8 @@ static uint16_t show_vty_link_subtlv_res_bw(struct vty *vty, } static uint16_t show_vty_link_subtlv_ava_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_ava_bw *top; float fval; @@ -3632,9 +3789,13 @@ static uint16_t show_vty_link_subtlv_ava_bw(struct vty *vty, fval = ntohf(top->value); if (vty != NULL) - vty_out(vty, - " Unidirectional Available Bandwidth: %g (Bytes/sec)\n", - fval); + if (!json) + vty_out(vty, + " Unidirectional Available Bandwidth: %g (Bytes/sec)\n", + fval); + else + json_object_double_add(json, "oneWayAvailableBandwidth", + fval); else zlog_debug( " Unidirectional Available Bandwidth: %g (Bytes/sec)", @@ -3644,7 +3805,8 @@ static uint16_t show_vty_link_subtlv_ava_bw(struct vty *vty, } static uint16_t show_vty_link_subtlv_use_bw(struct vty *vty, - struct tlv_header *tlvh) + struct tlv_header *tlvh, + json_object *json) { struct te_link_subtlv_use_bw *top; float fval; @@ -3655,9 +3817,13 @@ static uint16_t show_vty_link_subtlv_use_bw(struct vty *vty, fval = ntohf(top->value); if (vty != NULL) - vty_out(vty, - " Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", - fval); + if (!json) + vty_out(vty, + " Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", + fval); + else + json_object_double_add(json, "oneWayUtilizedBandwidth", + fval); else zlog_debug( " Unidirectional Utilized Bandwidth: %g (Bytes/sec)", @@ -3667,8 +3833,10 @@ static uint16_t show_vty_link_subtlv_use_bw(struct vty *vty, } static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, - size_t buf_size) + size_t buf_size, json_object *json) { + json_object *obj; + if (TLV_SIZE(tlvh) > buf_size) { if (vty != NULL) vty_out(vty, @@ -3682,8 +3850,17 @@ static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, } if (vty != NULL) - vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", - ntohs(tlvh->type), ntohs(tlvh->length)); + if (!json) + vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + else { + obj = json_object_new_object(); + json_object_string_addf(obj, "type", "0x%x", + ntohs(tlvh->type)); + json_object_string_addf(obj, "length", "0x%x", + ntohs(tlvh->length)); + json_object_object_add(json, "unknownTLV", obj); + } else zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]", ntohs(tlvh->type), ntohs(tlvh->length)); @@ -3693,7 +3870,8 @@ static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, static uint16_t ospf_mpls_te_show_link_subtlv(struct vty *vty, struct tlv_header *tlvh0, - uint16_t subtotal, uint16_t total) + uint16_t subtotal, uint16_t total, + json_object *json) { struct tlv_header *tlvh; uint16_t sum = subtotal; @@ -3701,69 +3879,72 @@ static uint16_t ospf_mpls_te_show_link_subtlv(struct vty *vty, for (tlvh = tlvh0; sum < total; tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case TE_LINK_SUBTLV_LINK_TYPE: - sum += show_vty_link_subtlv_link_type(vty, tlvh); + sum += show_vty_link_subtlv_link_type(vty, tlvh, json); break; case TE_LINK_SUBTLV_LINK_ID: - sum += show_vty_link_subtlv_link_id(vty, tlvh); + sum += show_vty_link_subtlv_link_id(vty, tlvh, json); break; case TE_LINK_SUBTLV_LCLIF_IPADDR: sum += show_vty_link_subtlv_lclif_ipaddr(vty, tlvh, - total - sum); + total - sum, + json); break; case TE_LINK_SUBTLV_RMTIF_IPADDR: sum += show_vty_link_subtlv_rmtif_ipaddr(vty, tlvh, - total - sum); + total - sum, + json); break; case TE_LINK_SUBTLV_TE_METRIC: - sum += show_vty_link_subtlv_te_metric(vty, tlvh); + sum += show_vty_link_subtlv_te_metric(vty, tlvh, json); break; case TE_LINK_SUBTLV_MAX_BW: - sum += show_vty_link_subtlv_max_bw(vty, tlvh); + sum += show_vty_link_subtlv_max_bw(vty, tlvh, json); break; case TE_LINK_SUBTLV_MAX_RSV_BW: - sum += show_vty_link_subtlv_max_rsv_bw(vty, tlvh); + sum += show_vty_link_subtlv_max_rsv_bw(vty, tlvh, json); break; case TE_LINK_SUBTLV_UNRSV_BW: - sum += show_vty_link_subtlv_unrsv_bw(vty, tlvh); + sum += show_vty_link_subtlv_unrsv_bw(vty, tlvh, json); break; case TE_LINK_SUBTLV_RSC_CLSCLR: - sum += show_vty_link_subtlv_rsc_clsclr(vty, tlvh); + sum += show_vty_link_subtlv_rsc_clsclr(vty, tlvh, json); break; case TE_LINK_SUBTLV_LRRID: - sum += show_vty_link_subtlv_lrrid(vty, tlvh); + sum += show_vty_link_subtlv_lrrid(vty, tlvh, json); break; case TE_LINK_SUBTLV_LLRI: - sum += show_vty_link_subtlv_llri(vty, tlvh); + sum += show_vty_link_subtlv_llri(vty, tlvh, json); break; case TE_LINK_SUBTLV_RIP: - sum += show_vty_link_subtlv_rip(vty, tlvh); + sum += show_vty_link_subtlv_rip(vty, tlvh, json); break; case TE_LINK_SUBTLV_RAS: - sum += show_vty_link_subtlv_ras(vty, tlvh); + sum += show_vty_link_subtlv_ras(vty, tlvh, json); break; case TE_LINK_SUBTLV_AV_DELAY: - sum += show_vty_link_subtlv_av_delay(vty, tlvh); + sum += show_vty_link_subtlv_av_delay(vty, tlvh, json); break; case TE_LINK_SUBTLV_MM_DELAY: - sum += show_vty_link_subtlv_mm_delay(vty, tlvh); + sum += show_vty_link_subtlv_mm_delay(vty, tlvh, json); break; case TE_LINK_SUBTLV_DELAY_VAR: - sum += show_vty_link_subtlv_delay_var(vty, tlvh); + sum += show_vty_link_subtlv_delay_var(vty, tlvh, json); break; case TE_LINK_SUBTLV_PKT_LOSS: - sum += show_vty_link_subtlv_pkt_loss(vty, tlvh); + sum += show_vty_link_subtlv_pkt_loss(vty, tlvh, json); break; case TE_LINK_SUBTLV_RES_BW: - sum += show_vty_link_subtlv_res_bw(vty, tlvh); + sum += show_vty_link_subtlv_res_bw(vty, tlvh, json); break; case TE_LINK_SUBTLV_AVA_BW: - sum += show_vty_link_subtlv_ava_bw(vty, tlvh); + sum += show_vty_link_subtlv_ava_bw(vty, tlvh, json); break; case TE_LINK_SUBTLV_USE_BW: - sum += show_vty_link_subtlv_use_bw(vty, tlvh); + sum += show_vty_link_subtlv_use_bw(vty, tlvh, json); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, total - sum); + sum += show_vty_unknown_tlv(vty, tlvh, total - sum, + json); break; } } @@ -3775,37 +3956,47 @@ static void ospf_mpls_te_show_info(struct vty *vty, struct json_object *json, { struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh, *next; - uint16_t sum, total; + uint16_t sum, sub, total; uint16_t (*subfunc)(struct vty * vty, struct tlv_header * tlvh, - uint16_t subtotal, uint16_t total) = NULL; - - if (json) - return; + uint16_t subtotal, uint16_t total, + struct json_object *json) = NULL; + json_object *jobj = NULL; sum = 0; + sub = 0; total = lsa->size - OSPF_LSA_HEADER_SIZE; for (tlvh = TLV_HDR_TOP(lsah); sum < total && tlvh; tlvh = (next ? next : TLV_HDR_NEXT(tlvh))) { if (subfunc != NULL) { - sum = (*subfunc)(vty, tlvh, sum, total); + sum = (*subfunc)(vty, tlvh, sum, total, jobj); next = (struct tlv_header *)((char *)tlvh + sum); subfunc = NULL; continue; } next = NULL; + sub = total - sum; switch (ntohs(tlvh->type)) { case TE_TLV_ROUTER_ADDR: - sum += show_vty_router_addr(vty, tlvh); + if (json) { + jobj = json_object_new_object(); + json_object_object_add(json, "teRouterAddress", + jobj); + } + sum += show_vty_router_addr(vty, tlvh, jobj); break; case TE_TLV_LINK: - sum += show_vty_link_header(vty, tlvh, total - sum); + if (json) { + jobj = json_object_new_object(); + json_object_object_add(json, "teLink", jobj); + } + sum += show_vty_link_header(vty, tlvh, sub, jobj); subfunc = ospf_mpls_te_show_link_subtlv; next = TLV_DATA(tlvh); break; default: - sum += show_vty_unknown_tlv(vty, tlvh, total - sum); + sum += show_vty_unknown_tlv(vty, tlvh, sub, json); break; } } @@ -4139,7 +4330,8 @@ DEFUN (show_ip_ospf_mpls_te_router, if (ntohs(OspfMplsTE.router_addr.header.type) != 0) show_vty_router_addr(vty, - &OspfMplsTE.router_addr.header); + &OspfMplsTE.router_addr.header, + NULL); else vty_out(vty, " Router address is not set\n"); vty_out(vty, " Link State distribution is %s\n", @@ -4148,7 +4340,8 @@ DEFUN (show_ip_ospf_mpls_te_router, return CMD_SUCCESS; } -static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp) +static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp, + json_object *json) { struct mpls_te_link *lp; @@ -4178,53 +4371,69 @@ static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp) if (TLV_TYPE(lp->link_type) != 0) show_vty_link_subtlv_link_type(vty, - &lp->link_type.header); + &lp->link_type.header, + json); if (TLV_TYPE(lp->link_id) != 0) - show_vty_link_subtlv_link_id(vty, &lp->link_id.header); + show_vty_link_subtlv_link_id(vty, &lp->link_id.header, + json); if (TLV_TYPE(lp->lclif_ipaddr) != 0) show_vty_link_subtlv_lclif_ipaddr( vty, &lp->lclif_ipaddr.header, - lp->lclif_ipaddr.header.length); + lp->lclif_ipaddr.header.length, + json); if (TLV_TYPE(lp->rmtif_ipaddr) != 0) show_vty_link_subtlv_rmtif_ipaddr( vty, &lp->rmtif_ipaddr.header, - lp->rmtif_ipaddr.header.length); + lp->rmtif_ipaddr.header.length, + json); if (TLV_TYPE(lp->rip) != 0) - show_vty_link_subtlv_rip(vty, &lp->rip.header); + show_vty_link_subtlv_rip(vty, &lp->rip.header, json); if (TLV_TYPE(lp->ras) != 0) - show_vty_link_subtlv_ras(vty, &lp->ras.header); + show_vty_link_subtlv_ras(vty, &lp->ras.header, json); if (TLV_TYPE(lp->te_metric) != 0) show_vty_link_subtlv_te_metric(vty, - &lp->te_metric.header); + &lp->te_metric.header, + json); if (TLV_TYPE(lp->max_bw) != 0) - show_vty_link_subtlv_max_bw(vty, &lp->max_bw.header); + show_vty_link_subtlv_max_bw(vty, &lp->max_bw.header, + json); if (TLV_TYPE(lp->max_rsv_bw) != 0) show_vty_link_subtlv_max_rsv_bw(vty, - &lp->max_rsv_bw.header); + &lp->max_rsv_bw.header, + json); if (TLV_TYPE(lp->unrsv_bw) != 0) show_vty_link_subtlv_unrsv_bw(vty, - &lp->unrsv_bw.header); + &lp->unrsv_bw.header, + json); if (TLV_TYPE(lp->rsc_clsclr) != 0) show_vty_link_subtlv_rsc_clsclr(vty, - &lp->rsc_clsclr.header); + &lp->rsc_clsclr.header, + json); if (TLV_TYPE(lp->av_delay) != 0) show_vty_link_subtlv_av_delay(vty, - &lp->av_delay.header); + &lp->av_delay.header, + json); if (TLV_TYPE(lp->mm_delay) != 0) show_vty_link_subtlv_mm_delay(vty, - &lp->mm_delay.header); + &lp->mm_delay.header, + json); if (TLV_TYPE(lp->delay_var) != 0) show_vty_link_subtlv_delay_var(vty, - &lp->delay_var.header); + &lp->delay_var.header, + json); if (TLV_TYPE(lp->pkt_loss) != 0) show_vty_link_subtlv_pkt_loss(vty, - &lp->pkt_loss.header); + &lp->pkt_loss.header, + json); if (TLV_TYPE(lp->res_bw) != 0) - show_vty_link_subtlv_res_bw(vty, &lp->res_bw.header); + show_vty_link_subtlv_res_bw(vty, &lp->res_bw.header, + json); if (TLV_TYPE(lp->ava_bw) != 0) - show_vty_link_subtlv_ava_bw(vty, &lp->ava_bw.header); + show_vty_link_subtlv_ava_bw(vty, &lp->ava_bw.header, + json); if (TLV_TYPE(lp->use_bw) != 0) - show_vty_link_subtlv_use_bw(vty, &lp->use_bw.header); + show_vty_link_subtlv_use_bw(vty, &lp->use_bw.header, + json); vty_out(vty, "---------------\n\n"); } else { vty_out(vty, " %s: MPLS-TE is disabled on this interface\n", @@ -4253,7 +4462,6 @@ DEFUN (show_ip_ospf_mpls_te_link, ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf == NULL || !ospf->oi_running) return CMD_SUCCESS; - vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!vrf) return CMD_SUCCESS; @@ -4267,11 +4475,11 @@ DEFUN (show_ip_ospf_mpls_te_link, } if (!ifp) { FOR_ALL_INTERFACES (vrf, ifp) - show_mpls_te_link_sub(vty, ifp); + show_mpls_te_link_sub(vty, ifp, NULL); return CMD_SUCCESS; } - show_mpls_te_link_sub(vty, ifp); + show_mpls_te_link_sub(vty, ifp, NULL); return CMD_SUCCESS; } diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 355742a..93dd12c 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -22,6 +22,7 @@ #include "defaults.h" #include "lib/printfrr.h" #include "keychain.h" +#include "frrdistance.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_asbr.h" @@ -1871,7 +1872,7 @@ DEFUN (no_ospf_area_import_list, DEFUN (ospf_area_filter_list, ospf_area_filter_list_cmd, - "area filter-list prefix PREFIXLIST_NAME ", + "area filter-list prefix PREFIXLIST4_NAME ", "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" @@ -1916,7 +1917,7 @@ DEFUN (ospf_area_filter_list, DEFUN (no_ospf_area_filter_list, no_ospf_area_filter_list_cmd, - "no area filter-list prefix PREFIXLIST_NAME ", + "no area filter-list prefix PREFIXLIST4_NAME ", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" @@ -5709,7 +5710,7 @@ DEFPY(show_ip_ospf_neighbor_id, "%% OSPF is not enabled in vrf %s\n", vrf_name); else - vty_json_empty(vty); + vty_json_empty(vty, NULL); return CMD_SUCCESS; } ret = show_ip_ospf_neighbor_id_common( @@ -6210,7 +6211,7 @@ DEFPY(show_ip_ospf_neighbor_int, if (!ospf || !ospf->oi_running) { if (json) - vty_json_empty(vty); + vty_json_empty(vty, NULL); return ret; } @@ -6220,7 +6221,7 @@ DEFPY(show_ip_ospf_neighbor_int, ifp = if_lookup_by_name(ifname, vrf_id); if (!ifp) { if (json) - vty_json_empty(vty); + vty_json_empty(vty, NULL); else vty_out(vty, "No such interface.\n"); return ret; @@ -6257,7 +6258,7 @@ DEFPY(show_ip_ospf_neighbor_int_detail, "%% OSPF is not enabled in vrf %s\n", vrf_name); else - vty_json_empty(vty); + vty_json_empty(vty, NULL); return CMD_SUCCESS; } return show_ip_ospf_neighbor_int_detail_common( @@ -8301,7 +8302,7 @@ DEFUN_HIDDEN (ospf_dead_interval, DEFUN (ip_ospf_dead_interval_minimal, ip_ospf_dead_interval_minimal_addr_cmd, - "ip ospf dead-interval minimal hello-multiplier (1-10) [A.B.C.D]", + "ip ospf dead-interval minimal hello-multiplier (2-20) [A.B.C.D]", "IP Information\n" "OSPF interface commands\n" "Interval time after which a neighbor is declared down\n" @@ -8322,7 +8323,7 @@ DEFUN (ip_ospf_dead_interval_minimal, DEFUN (no_ip_ospf_dead_interval, no_ip_ospf_dead_interval_cmd, - "no ip ospf dead-interval [<(1-65535)|minimal hello-multiplier (1-10)> [A.B.C.D]]", + "no ip ospf dead-interval [<(1-65535)|minimal hello-multiplier (2-20)> [A.B.C.D]]", NO_STR "IP Information\n" "OSPF interface commands\n" @@ -8389,7 +8390,7 @@ DEFUN (no_ip_ospf_dead_interval, DEFUN_HIDDEN (no_ospf_dead_interval, no_ospf_dead_interval_cmd, - "no ospf dead-interval [<(1-65535)|minimal hello-multiplier (1-10)> [A.B.C.D]]", + "no ospf dead-interval [<(1-65535)|minimal hello-multiplier (2-20)> [A.B.C.D]]", NO_STR "OSPF interface commands\n" "Interval time after which a neighbor is declared down\n" diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 1af703d..bb6cc3a 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1487,30 +1487,20 @@ void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) __func__); } -static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +static void ospf_zebra_import_check_update(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr) { - struct ospf *ospf; - struct zapi_route nhr; - struct prefix matched; + struct ospf *ospf = vrf->info; - ospf = ospf_lookup_by_vrf_id(vrf_id); if (ospf == NULL || !IS_OSPF_ASBR(ospf)) - return 0; - - if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { - zlog_err("%s[%u]: Failure to decode route", __func__, - ospf->vrf_id); - return -1; - } + return; - if (matched.family != AF_INET || matched.prefixlen != 0 || - nhr.type == ZEBRA_ROUTE_OSPF) - return 0; + if (match->family != AF_INET || match->prefixlen != 0 || + nhr->type == ZEBRA_ROUTE_OSPF) + return; - ospf->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf->nssa_default_import_check.status = !!nhr->nexthop_num; ospf_abr_nssa_type7_defaults(ospf); - - return 0; } int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name) @@ -2183,7 +2173,6 @@ static zclient_handler *const ospf_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route, - [ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update, [ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler, @@ -2197,11 +2186,10 @@ void ospf_zebra_init(struct event_loop *master, unsigned short instance) array_size(ospf_handlers)); zclient_init(zclient, ZEBRA_ROUTE_OSPF, instance, &ospfd_privs); zclient->zebra_connected = ospf_zebra_connected; + zclient->nexthop_update = ospf_zebra_import_check_update; /* Initialize special zclient for synchronous message exchanges. */ - struct zclient_options options = zclient_options_default; - options.synchronous = true; - zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync = zclient_new(master, &zclient_options_sync, NULL, 0); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_OSPF; zclient_sync->instance = instance; diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 9ed1d30..4c4666d 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -575,39 +575,12 @@ static struct ospf *ospf_lookup_by_name(const char *vrf_name) return NULL; } -/* Handle the second half of deferred shutdown. This is called either - * from the deferred-shutdown timer thread, or directly through - * ospf_deferred_shutdown_check. - * - * Function is to cleanup G-R state, if required then call ospf_finish_final - * to complete shutdown of this ospf instance. Possibly exit if the - * whole process is being shutdown and this was the last OSPF instance. - */ -static void ospf_deferred_shutdown_finish(struct ospf *ospf) -{ - ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; - EVENT_OFF(ospf->t_deferred_shutdown); - - ospf_finish_final(ospf); - - /* *ospf is now invalid */ - - /* ospfd being shut-down? If so, was this the last ospf instance? */ - if (CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN) - && (listcount(om->ospf) == 0)) { - frr_fini(); - exit(0); - } - - return; -} - -/* Timer thread for G-R */ +/* Timer thread for deferred shutdown */ static void ospf_deferred_shutdown_timer(struct event *t) { struct ospf *ospf = EVENT_ARG(t); - ospf_deferred_shutdown_finish(ospf); + ospf_finish_final(ospf); } /* Check whether deferred-shutdown must be scheduled, otherwise call @@ -634,15 +607,12 @@ static void ospf_deferred_shutdown_check(struct ospf *ospf) ospf_router_lsa_update_area(area); } timeout = ospf->stub_router_shutdown_time; + OSPF_TIMER_ON(ospf->t_deferred_shutdown, + ospf_deferred_shutdown_timer, timeout); } else { /* No timer needed */ - ospf_deferred_shutdown_finish(ospf); - return; + ospf_finish_final(ospf); } - - OSPF_TIMER_ON(ospf->t_deferred_shutdown, ospf_deferred_shutdown_timer, - timeout); - return; } /* Shut down the entire process */ @@ -657,10 +627,6 @@ void ospf_terminate(void) SET_FLAG(om->options, OSPF_MASTER_SHUTDOWN); - /* Skip some steps if OSPF not actually running */ - if (listcount(om->ospf) == 0) - goto done; - for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) ospf_finish(ospf); @@ -678,6 +644,11 @@ void ospf_terminate(void) /* Cleanup vrf info */ ospf_vrf_terminate(); + keychain_terminate(); + + ospf_opaque_term(); + list_delete(&om->ospf); + /* Deliberately go back up, hopefully to thread scheduler, as * One or more ospf_finish()'s may have deferred shutdown to a timer * thread @@ -687,20 +658,17 @@ void ospf_terminate(void) zclient_stop(zclient_sync); zclient_free(zclient_sync); -done: frr_fini(); } void ospf_finish(struct ospf *ospf) { - /* let deferred shutdown decide */ - ospf_deferred_shutdown_check(ospf); - - /* if ospf_deferred_shutdown returns, then ospf_finish_final is - * deferred to expiry of G-S timer thread. Return back up, hopefully - * to thread scheduler. - */ - return; + if (CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) + ospf_finish_final(ospf); + else { + /* let deferred shutdown decide */ + ospf_deferred_shutdown_check(ospf); + } } /* Final cleanup of ospf instance */ @@ -900,6 +868,7 @@ static void ospf_finish_final(struct ospf *ospf) EVENT_OFF(ospf->t_ase_calc); EVENT_OFF(ospf->t_maxage); EVENT_OFF(ospf->t_maxage_walker); + EVENT_OFF(ospf->t_deferred_shutdown); EVENT_OFF(ospf->t_abr_task); EVENT_OFF(ospf->t_abr_fr); EVENT_OFF(ospf->t_asbr_check); @@ -1442,7 +1411,6 @@ static void ospf_network_run_interface(struct ospf *ospf, struct interface *ifp, struct prefix *p, struct ospf_area *given_area) { - struct listnode *cnode; struct connected *co; if (memcmp(ifp->name, "VLINK", 5) == 0) @@ -1454,7 +1422,7 @@ static void ospf_network_run_interface(struct ospf *ospf, struct interface *ifp, /* if interface prefix is match specified prefix, then create socket and join multicast group. */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) + frr_each (if_connected, ifp->connected, co) ospf_network_run_subnet(ospf, co, p, given_area); } diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 2ab7db1..6051dff 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -22,9 +22,6 @@ #define OSPF_VERSION 2 -/* VTY port number. */ -#define OSPF_VTY_PORT 2604 - /* IP TTL for OSPF protocol. */ #define OSPF_IP_TTL 1 #define OSPF_VL_IP_TTL 100 diff --git a/pathd/path_cli.c b/pathd/path_cli.c index 45f4ac7..e22931c 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -287,7 +287,7 @@ void cli_show_srte_segment_list(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " segment-list %s\n", - yang_dnode_get_string(dnode, "./name")); + yang_dnode_get_string(dnode, "name")); } void cli_show_srte_segment_list_end(struct vty *vty, @@ -546,37 +546,37 @@ void cli_show_srte_segment_list_segment(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - vty_out(vty, " index %s", yang_dnode_get_string(dnode, "./index")); - if (yang_dnode_exists(dnode, "./sid-value")) { + vty_out(vty, " index %s", yang_dnode_get_string(dnode, "index")); + if (yang_dnode_exists(dnode, "sid-value")) { vty_out(vty, " mpls label %s", - yang_dnode_get_string(dnode, "./sid-value")); + yang_dnode_get_string(dnode, "sid-value")); } - if (yang_dnode_exists(dnode, "./nai")) { + if (yang_dnode_exists(dnode, "nai")) { struct ipaddr addr; struct ipaddr addr_rmt; - switch (yang_dnode_get_enum(dnode, "./nai/type")) { + switch (yang_dnode_get_enum(dnode, "nai/type")) { case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: - yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr, dnode, "nai/local-address"); vty_out(vty, " nai prefix %pI4", &addr.ipaddr_v4); break; case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: - yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr, dnode, "nai/local-address"); vty_out(vty, " nai prefix %pI6", &addr.ipaddr_v6); break; case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: - yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr, dnode, "nai/local-address"); yang_dnode_get_ip(&addr_rmt, dnode, "./nai/remote-address"); vty_out(vty, " nai adjacency %pI4", &addr.ipaddr_v4); vty_out(vty, " %pI4", &addr_rmt.ipaddr_v4); break; case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: - yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr, dnode, "nai/local-address"); yang_dnode_get_ip(&addr_rmt, dnode, "./nai/remote-address"); vty_out(vty, " nai adjacency %pI6", &addr.ipaddr_v6); @@ -585,17 +585,17 @@ void cli_show_srte_segment_list_segment(struct vty *vty, default: break; } - if (yang_dnode_exists(dnode, "./nai/local-prefix-len")) { + if (yang_dnode_exists(dnode, "nai/local-prefix-len")) { vty_out(vty, "/%s", yang_dnode_get_string( dnode, "./nai/local-prefix-len")); } - if (yang_dnode_exists(dnode, "./nai/local-interface")) { + if (yang_dnode_exists(dnode, "nai/local-interface")) { vty_out(vty, " iface %s", yang_dnode_get_string(dnode, "./nai/local-interface")); } - if (yang_dnode_exists(dnode, "./nai/algorithm")) { + if (yang_dnode_exists(dnode, "nai/algorithm")) { vty_out(vty, " algorithm %s", yang_dnode_get_string(dnode, "./nai/algorithm")); @@ -658,8 +658,8 @@ void cli_show_srte_policy(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " policy color %s endpoint %s\n", - yang_dnode_get_string(dnode, "./color"), - yang_dnode_get_string(dnode, "./endpoint")); + yang_dnode_get_string(dnode, "color"), + yang_dnode_get_string(dnode, "endpoint")); } void cli_show_srte_policy_end(struct vty *vty, const struct lyd_node *dnode) @@ -860,7 +860,7 @@ DEFPY(srte_candidate_no_affinity_filter, srte_candidate_no_affinity_filter_cmd, DEFPY(srte_candidate_metric, srte_candidate_metric_cmd, - "metric [bound$bound] $type METRIC$value [required$required]", + "metric [bound$bound] $type METRIC$value [required$required] [computed$computed]", "Define a metric constraint\n" "If the metric is bounded\n" "IGP metric\n" @@ -885,7 +885,8 @@ DEFPY(srte_candidate_metric, "Domain Count metric\n" "Border Node Count metric\n" "Metric value\n" - "Required constraint\n") + "Required constraint\n" + "Force the PCE to provide the computed path metric\n") { char xpath[XPATH_CANDIDATE_MAXLEN]; snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']/value", @@ -899,12 +900,16 @@ DEFPY(srte_candidate_metric, "./constraints/metrics[type='%s']/required", type); nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, required ? "true" : "false"); + snprintf(xpath, sizeof(xpath), + "./constraints/metrics[type='%s']/is-computed", type); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + computed ? "true" : "false"); return nb_cli_apply_changes(vty, NULL); } DEFPY(srte_candidate_no_metric, srte_candidate_no_metric_cmd, - "no metric [bound] $type [METRIC$value] [required$required]", + "no metric [bound] $type [METRIC$value] [required$required] [computed$computed]", NO_STR "Remove a metric constraint\n" "If the metric is bounded\n" @@ -930,7 +935,8 @@ DEFPY(srte_candidate_no_metric, "Domain Count metric\n" "Border Node Count metric\n" "Metric value\n" - "Required constraint\n") + "Required constraint\n" + "Force the PCE to provide the computed path metric\n") { char xpath[XPATH_CANDIDATE_MAXLEN]; snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']", @@ -1164,7 +1170,8 @@ static void config_write_float(struct vty *vty, float value) static void config_write_metric(struct vty *vty, enum srte_candidate_metric_type type, - float value, bool required, bool is_bound) + float value, bool required, bool is_bound, + bool is_computed) { const char *name = metric_type_name(type); if (name == NULL) @@ -1173,6 +1180,7 @@ static void config_write_metric(struct vty *vty, metric_type_name(type)); config_write_float(vty, value); vty_out(vty, required ? " required" : ""); + vty_out(vty, is_computed ? " computed" : ""); vty_out(vty, "\n"); } @@ -1180,16 +1188,18 @@ static int config_write_metric_cb(const struct lyd_node *dnode, void *arg) { struct vty *vty = arg; enum srte_candidate_metric_type type; - bool required, is_bound = false; + bool required, is_bound = false, is_computed = false; float value; - type = yang_dnode_get_enum(dnode, "./type"); - value = (float)yang_dnode_get_dec64(dnode, "./value"); - required = yang_dnode_get_bool(dnode, "./required"); - if (yang_dnode_exists(dnode, "./is-bound")) - is_bound = yang_dnode_get_bool(dnode, "./is-bound"); + type = yang_dnode_get_enum(dnode, "type"); + value = (float)yang_dnode_get_dec64(dnode, "value"); + required = yang_dnode_get_bool(dnode, "required"); + if (yang_dnode_exists(dnode, "is-bound")) + is_bound = yang_dnode_get_bool(dnode, "is-bound"); + if (yang_dnode_exists(dnode, "is-computed")) + is_computed = yang_dnode_get_bool(dnode, "is-computed"); - config_write_metric(vty, type, value, required, is_bound); + config_write_metric(vty, type, value, required, is_bound, is_computed); return YANG_ITER_CONTINUE; } @@ -1201,18 +1211,18 @@ void cli_show_srte_policy_candidate_path(struct vty *vty, uint32_t affinity; bool required; enum objfun_type objfun_type; - const char *type = yang_dnode_get_string(dnode, "./type"); + const char *type = yang_dnode_get_string(dnode, "type"); vty_out(vty, " candidate-path preference %s name %s %s", - yang_dnode_get_string(dnode, "./preference"), - yang_dnode_get_string(dnode, "./name"), type); + yang_dnode_get_string(dnode, "preference"), + yang_dnode_get_string(dnode, "name"), type); if (strmatch(type, "explicit")) vty_out(vty, " segment-list %s", - yang_dnode_get_string(dnode, "./segment-list-name")); + yang_dnode_get_string(dnode, "segment-list-name")); vty_out(vty, "\n"); if (strmatch(type, "dynamic")) { - if (yang_dnode_exists(dnode, "./constraints/bandwidth")) { + if (yang_dnode_exists(dnode, "constraints/bandwidth")) { bandwidth = (float)yang_dnode_get_dec64( dnode, "./constraints/bandwidth/value"); required = yang_dnode_get_bool( @@ -1262,7 +1272,7 @@ void cli_show_srte_policy_candidate_path(struct vty *vty, void cli_show_srte_policy_candidate_path_end(struct vty *vty, const struct lyd_node *dnode) { - const char *type = yang_dnode_get_string(dnode, "./type"); + const char *type = yang_dnode_get_string(dnode, "type"); if (strmatch(type, "dynamic")) vty_out(vty, " exit\n"); diff --git a/pathd/path_main.c b/pathd/path_main.c index c333162..fe636c5 100644 --- a/pathd/path_main.c +++ b/pathd/path_main.c @@ -95,17 +95,20 @@ static const struct frr_yang_module_info *pathd_yang_modules[] = { &frr_pathd_info, }; -#define PATH_VTY_PORT 2621 +/* clang-format off */ +FRR_DAEMON_INFO(pathd, PATH, + .vty_port = PATH_VTY_PORT, + .proghelp = "Implementation of PATH.", -FRR_DAEMON_INFO(pathd, PATH, .vty_port = PATH_VTY_PORT, + .signals = path_signals, + .n_signals = array_size(path_signals), - .proghelp = "Implementation of PATH.", + .privs = &pathd_privs, - .signals = path_signals, .n_signals = array_size(path_signals), - - .privs = &pathd_privs, .yang_modules = pathd_yang_modules, - .n_yang_modules = array_size(pathd_yang_modules), + .yang_modules = pathd_yang_modules, + .n_yang_modules = array_size(pathd_yang_modules), ); +/* clang-format on */ int main(int argc, char **argv, char **envp) { diff --git a/pathd/path_nb.c b/pathd/path_nb.c index 9e91957..e1c0cc3 100644 --- a/pathd/path_nb.c +++ b/pathd/path_nb.c @@ -313,8 +313,8 @@ int iter_objfun_cb(const struct lyd_node *dnode, void *arg) pref = &of_arg->prefs[of_arg->free_slot++]; - pref->index = yang_dnode_get_uint32(dnode, "./index"); - pref->type = yang_dnode_get_enum(dnode, "./type"); + pref->index = yang_dnode_get_uint32(dnode, "index"); + pref->type = yang_dnode_get_enum(dnode, "type"); /* Simplistic insertion sort */ p = &of_arg->first; diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c index ad24f23..48531ba 100644 --- a/pathd/path_nb_config.c +++ b/pathd/path_nb_config.c @@ -31,7 +31,7 @@ int pathd_srte_segment_list_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - name = yang_dnode_get_string(args->dnode, "./name"); + name = yang_dnode_get_string(args->dnode, "name"); segment_list = srte_segment_list_add(name); nb_running_set_entry(args->dnode, segment_list); SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); @@ -104,7 +104,7 @@ int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args) return NB_OK; segment_list = nb_running_get_entry(args->dnode, NULL, true); - index = yang_dnode_get_uint32(args->dnode, "./index"); + index = yang_dnode_get_uint32(args->dnode, "index"); segment = srte_segment_entry_add(segment_list, index); nb_running_set_entry(args->dnode, segment); SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); @@ -191,9 +191,9 @@ void pathd_srte_segment_list_segment_nai_apply_finish( const char *algo_buf, *local_prefix_len_buf; segment = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./type"); + type = yang_dnode_get_enum(args->dnode, "type"); - yang_dnode_get_ip(&local_addr, args->dnode, "./local-address"); + yang_dnode_get_ip(&local_addr, args->dnode, "local-address"); switch (type) { case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: @@ -208,12 +208,12 @@ void pathd_srte_segment_list_segment_nai_apply_finish( yang_dnode_get_ip(&remote_addr, args->dnode, "./remote-address"); local_iface = - yang_dnode_get_uint32(args->dnode, "./local-interface"); + yang_dnode_get_uint32(args->dnode, "local-interface"); remote_iface = yang_dnode_get_uint32(args->dnode, "./remote-interface"); break; case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: - algo_buf = yang_dnode_get_string(args->dnode, "./algorithm"); + algo_buf = yang_dnode_get_string(args->dnode, "algorithm"); algo = atoi(algo_buf); local_prefix_len_buf = yang_dnode_get_string( args->dnode, "./local-prefix-len"); @@ -221,7 +221,7 @@ void pathd_srte_segment_list_segment_nai_apply_finish( break; case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: local_iface = - yang_dnode_get_uint32(args->dnode, "./local-interface"); + yang_dnode_get_uint32(args->dnode, "local-interface"); local_prefix_len_buf = yang_dnode_get_string( args->dnode, "./local-prefix-len"); local_prefix_len = atoi(local_prefix_len_buf); @@ -254,8 +254,8 @@ int pathd_srte_policy_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - color = yang_dnode_get_uint32(args->dnode, "./color"); - yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint"); + color = yang_dnode_get_uint32(args->dnode, "color"); + yang_dnode_get_ip(&endpoint, args->dnode, "endpoint"); policy = srte_policy_add(color, &endpoint, SRTE_ORIGIN_LOCAL, NULL); nb_running_set_entry(args->dnode, policy); @@ -377,7 +377,7 @@ int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args) return NB_OK; policy = nb_running_get_entry(args->dnode, NULL, true); - preference = yang_dnode_get_uint32(args->dnode, "./preference"); + preference = yang_dnode_get_uint32(args->dnode, "preference"); candidate = srte_candidate_add(policy, preference, SRTE_ORIGIN_LOCAL, NULL); nb_running_set_entry(args->dnode, candidate); @@ -539,7 +539,7 @@ int pathd_srte_policy_candidate_path_metrics_destroy( assert(args->context != NULL); candidate = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./type"); + type = yang_dnode_get_enum(args->dnode, "type"); srte_candidate_unset_metric(candidate, type); return NB_OK; @@ -557,13 +557,13 @@ void pathd_srte_policy_candidate_path_metrics_apply_finish( candidate = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./type"); - value = (float)yang_dnode_get_dec64(args->dnode, "./value"); - required = yang_dnode_get_bool(args->dnode, "./required"); - if (yang_dnode_exists(args->dnode, "./is-bound")) - is_bound = yang_dnode_get_bool(args->dnode, "./is-bound"); - if (yang_dnode_exists(args->dnode, "./is-computed")) - is_computed = yang_dnode_get_bool(args->dnode, "./is-computed"); + type = yang_dnode_get_enum(args->dnode, "type"); + value = (float)yang_dnode_get_dec64(args->dnode, "value"); + required = yang_dnode_get_bool(args->dnode, "required"); + if (yang_dnode_exists(args->dnode, "is-bound")) + is_bound = yang_dnode_get_bool(args->dnode, "is-bound"); + if (yang_dnode_exists(args->dnode, "is-computed")) + is_computed = yang_dnode_get_bool(args->dnode, "is-computed"); srte_candidate_set_metric(candidate, type, value, required, is_bound, is_computed); @@ -597,8 +597,8 @@ void pathd_srte_policy_candidate_path_objfun_apply_finish( bool required; candidate = nb_running_get_entry(args->dnode, NULL, true); - required = yang_dnode_get_bool(args->dnode, "./required"); - type = yang_dnode_get_enum(args->dnode, "./type"); + required = yang_dnode_get_bool(args->dnode, "required"); + type = yang_dnode_get_enum(args->dnode, "type"); srte_candidate_set_objfun(candidate, required, type); } @@ -739,8 +739,8 @@ void pathd_srte_policy_candidate_path_bandwidth_apply_finish( assert(args->context != NULL); candidate = nb_running_get_entry(args->dnode, NULL, true); - value = (float)yang_dnode_get_dec64(args->dnode, "./value"); - required = yang_dnode_get_bool(args->dnode, "./required"); + value = (float)yang_dnode_get_dec64(args->dnode, "value"); + required = yang_dnode_get_bool(args->dnode, "required"); srte_candidate_set_bandwidth(candidate, value, required); } diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index 5c6a023..d6dbcb5 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -25,6 +25,7 @@ DECLARE_MTYPE(PCEP); #define PCEP_DEBUG_MODE_PATH 0x02 #define PCEP_DEBUG_MODE_PCEP 0x04 #define PCEP_DEBUG_MODE_PCEPLIB 0x08 +#define PCEP_DEBUG_MODE_ALL 0x0F #define PCEP_DEBUG(fmt, ...) \ do { \ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) \ diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c index c46649b..47a811d 100644 --- a/pathd/path_pcep_cli.c +++ b/pathd/path_pcep_cli.c @@ -43,6 +43,8 @@ #define DEFAULT_TIMER_SESSION_TIMEOUT_INTERVAL 30 #define DEFAULT_DELEGATION_TIMEOUT_INTERVAL 10 +#define BUFFER_PCC_PCE_SIZE 1024 + /* CLI Function declarations */ static int pcep_cli_debug_config_write(struct vty *vty); static int pcep_cli_debug_set_all(uint32_t flags, bool set); @@ -73,6 +75,9 @@ static void print_pcep_capabilities(char *buf, size_t buf_len, pcep_configuration *config); static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, struct pcep_pcc_info *pcc_info); +static void print_pcep_session_json(struct vty *vty, struct pce_opts *pce_opts, + struct pcep_pcc_info *pcc_info, + json_object *json); static bool pcep_cli_pcc_has_pce(const char *pce_name); static void pcep_cli_add_pce_connection(struct pce_opts *pce_opts); static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts); @@ -458,28 +463,32 @@ static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts) * VTY command implementations */ -static int path_pcep_cli_debug(struct vty *vty, const char *no_str, - const char *basic_str, const char *path_str, - const char *message_str, const char *pceplib_str) +static int path_pcep_cli_debug(struct vty *vty, const char *debug_type, bool set) { uint32_t mode = DEBUG_NODE2MODE(vty->node); - bool no = (no_str != NULL); - - DEBUG_MODE_SET(&pcep_g->dbg, mode, !no); - if (basic_str != NULL) { - DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC, !no); - } - if (path_str != NULL) { - DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH, !no); - } - if (message_str != NULL) { - DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP, !no); - } - if (pceplib_str != NULL) { - DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB, !no); + /* Global Set */ + if (debug_type == NULL) { + DEBUG_MODE_SET(&pcep_g->dbg, mode, set); + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_ALL, set); + return CMD_SUCCESS; } + DEBUG_MODE_SET(&pcep_g->dbg, mode, true); + + if (strcmp(debug_type, "basic") == 0) + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC, set); + else if (strcmp(debug_type, "path") == 0) + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH, set); + else if (strcmp(debug_type, "message") == 0) + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP, set); + else if (strcmp(debug_type, "pceplib") == 0) + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB, set); + + /* Unset the pcep debug mode if there is no flag at least set*/ + if (!DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_ALL)) + DEBUG_MODE_SET(&pcep_g->dbg, mode, false); + return CMD_SUCCESS; } @@ -1022,7 +1031,7 @@ static int path_pcep_cli_pcc_pcc_msd(struct vty *vty, const char *msd_str, { if (reset) pcc_msd_configured_g = false; - else { + else if (msd_str) { pcc_msd_configured_g = true; PCEP_VTYSH_INT_ARG_CHECK(msd_str, msd, pcc_msd_g, 0, 33); } @@ -1182,11 +1191,189 @@ static void print_pcep_capabilities(char *buf, size_t buf_len, } } +/* Internal util function to print a pcep session */ +static void print_pcep_session_json(struct vty *vty, struct pce_opts *pce_opts, + struct pcep_pcc_info *pcc_info, + json_object *json) +{ + char buf[BUFFER_PCC_PCE_SIZE] = {}; + int index = 0; + pcep_session *session; + struct pcep_config_group_opts *config_opts; + struct counters_group *group; + + /* PCE IP */ + if (IS_IPADDR_V4(&pce_opts->addr)) + json_object_string_addf(json, "pceAddress", "%pI4", + &pce_opts->addr.ipaddr_v4); + else if (IS_IPADDR_V6(&pce_opts->addr)) + json_object_string_addf(json, "pceAddress", "%pI6", + &pce_opts->addr.ipaddr_v6); + json_object_int_add(json, "pcePort", pce_opts->port); + + /* PCC IP */ + if (IS_IPADDR_V4(&pcc_info->pcc_addr)) + json_object_string_addf(json, "pccAddress", "%pI4", + &pcc_info->pcc_addr.ipaddr_v4); + else if (IS_IPADDR_V6(&pcc_info->pcc_addr)) + json_object_string_addf(json, "pccAddress", "%pI6", + &pcc_info->pcc_addr.ipaddr_v6); + + json_object_int_add(json, "pccPort", pcc_info->pcc_port); + json_object_int_add(json, "pccMsd", pcc_info->msd); + + if (pcc_info->status == PCEP_PCC_OPERATING) + json_object_string_add(json, "sessionStatus", "UP"); + else + json_object_string_add(json, "sessionStatus", + pcc_status_name(pcc_info->status)); + + json_object_boolean_add(json, "bestMultiPce", + pcc_info->is_best_multi_pce); + json_object_int_add(json, "precedence", + pcc_info->precedence > 0 ? pcc_info->precedence + : DEFAULT_PCE_PRECEDENCE); + json_object_string_add(json, "confidence", + pcc_info->previous_best ? "low" : "normal"); + + /* PCEPlib pcep session values, get a thread safe copy of the counters + */ + session = pcep_ctrl_get_pcep_session(pcep_g->fpt, pcc_info->pcc_id); + + /* Config Options values */ + config_opts = &pce_opts->config_opts; + json_object_int_add(json, "keepaliveConfig", + config_opts->keep_alive_seconds); + json_object_int_add(json, "deadTimerConfig", + config_opts->dead_timer_seconds); + json_object_int_add(json, "pccPcepRequestTimerConfig", + config_opts->pcep_request_time_seconds); + json_object_int_add(json, "sessionTimeoutIntervalSec", + config_opts->session_timeout_inteval_seconds); + json_object_int_add(json, "delegationTimeout", + config_opts->delegation_timeout_seconds); + json_object_boolean_add(json, "tcpMd5Authentication", + (strlen(config_opts->tcp_md5_auth) > 0)); + if (strlen(config_opts->tcp_md5_auth) > 0) + json_object_string_add(json, "tcpMd5AuthenticationString", + config_opts->tcp_md5_auth); + json_object_boolean_add(json, "draft07", !!config_opts->draft07); + json_object_boolean_add(json, "draft16AndRfc8408", + !config_opts->draft07); + + json_object_int_add(json, "nextPcRequestId", pcc_info->next_reqid); + /* original identifier used by the PCC for LSP instantiation */ + json_object_int_add(json, "nextPLspId", pcc_info->next_plspid); + + if (session != NULL) { + json_object_int_add(json, "sessionKeepalivePceNegotiatedSec", + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds); + json_object_int_add(json, "sessionDeadTimerPceNegotiatedSec", + session->pcc_config + .dead_timer_pce_negotiated_seconds); + if (pcc_info->status == PCEP_PCC_SYNCHRONIZING || + pcc_info->status == PCEP_PCC_OPERATING) { + time_t current_time = time(NULL); + struct tm lt = { 0 }; + /* Just for the timezone */ + localtime_r(¤t_time, <); + gmtime_r(&session->time_connected, <); + json_object_int_add(json, "sessionConnectionDurationSec", + (uint32_t)(current_time - + session->time_connected)); + json_object_string_addf(json, + "sessionConnectionStartTimeUTC", + "%d-%02d-%02d %02d:%02d:%02d", + lt.tm_year + 1900, lt.tm_mon + 1, + lt.tm_mday, lt.tm_hour, + lt.tm_min, lt.tm_sec); + } + + /* PCC capabilities */ + buf[0] = '\0'; + + if (config_opts->pce_initiated) + index += csnprintfrr(buf, sizeof(buf), "%s", + PCEP_CLI_CAP_PCC_PCE_INITIATED); + else + index += csnprintfrr(buf, sizeof(buf), "%s", + PCEP_CLI_CAP_PCC_INITIATED); + print_pcep_capabilities(buf, sizeof(buf) - index, + &session->pcc_config); + json_object_string_add(json, "pccCapabilities", buf); + + /* PCE capabilities */ + buf[0] = '\0'; + print_pcep_capabilities(buf, sizeof(buf), &session->pce_config); + if (buf[0] != '\0') + json_object_string_add(json, "pceCapabilities", buf); + XFREE(MTYPE_PCEP, session); + } else { + json_object_string_add(json, "warningSession", + "Detailed session information not available."); + } + + /* Message Counters, get a thread safe copy of the counters */ + group = pcep_ctrl_get_counters(pcep_g->fpt, pcc_info->pcc_id); + + if (group != NULL) { + struct counters_subgroup *rx_msgs = + find_subgroup(group, COUNTER_SUBGROUP_ID_RX_MSG); + struct counters_subgroup *tx_msgs = + find_subgroup(group, COUNTER_SUBGROUP_ID_TX_MSG); + json_object *json_counter; + struct counter *tx_counter, *rx_counter; + + if (rx_msgs != NULL) { + json_counter = json_object_new_object(); + for (int i = 0; i < rx_msgs->max_counters; i++) { + rx_counter = rx_msgs->counters[i]; + + if (rx_counter && + rx_counter->counter_name_json[0] != '\0') + json_object_int_add( + json_counter, + rx_counter->counter_name_json, + rx_counter->counter_value); + } + json_object_int_add(json_counter, "total", + subgroup_counters_total(rx_msgs)); + json_object_object_add(json, "messageStatisticsReceived", + json_counter); + } + if (tx_msgs != NULL) { + json_counter = json_object_new_object(); + for (int i = 0; i < tx_msgs->max_counters; i++) { + tx_counter = tx_msgs->counters[i]; + + if (tx_counter && + tx_counter->counter_name_json[0] != '\0') + json_object_int_add( + json_counter, + tx_counter->counter_name_json, + tx_counter->counter_value); + } + json_object_int_add(json_counter, "total", + subgroup_counters_total(tx_msgs)); + json_object_object_add(json, "messageStatisticsSent", + json_counter); + } + pcep_lib_free_counters(group); + } else { + json_object_string_add(json, "messageStatisticsWarning", + "Counters not available."); + } + + XFREE(MTYPE_PCEP, pcc_info); +} + /* Internal util function to print a pcep session */ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, struct pcep_pcc_info *pcc_info) { char buf[1024]; + buf[0] = '\0'; vty_out(vty, "\nPCE %s\n", pce_opts->pce_name); @@ -1237,6 +1424,7 @@ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, /* Config Options values */ struct pcep_config_group_opts *config_opts = &pce_opts->config_opts; + if (session != NULL) { vty_out(vty, " Timer: KeepAlive config %d, pce-negotiated %d\n", config_opts->keep_alive_seconds, @@ -1352,34 +1540,66 @@ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, } static int path_pcep_cli_show_srte_pcep_session(struct vty *vty, - const char *pcc_peer) + const char *pcc_peer, bool uj) { struct pce_opts_cli *pce_opts_cli; struct pcep_pcc_info *pcc_info; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); /* Only show 1 PCEP session */ if (pcc_peer != NULL) { + if (json) + json_object_string_add(json, "pceName", pcc_peer); pce_opts_cli = pcep_cli_find_pce(pcc_peer); if (pce_opts_cli == NULL) { - vty_out(vty, "%% PCE [%s] does not exist.\n", pcc_peer); + if (json) { + json_object_string_addf(json, "warning", + "PCE [%s] does not exist.", + pcc_peer); + vty_json(vty, json); + } else + vty_out(vty, "%% PCE [%s] does not exist.\n", + pcc_peer); return CMD_WARNING; } if (!pcep_cli_pcc_has_pce(pcc_peer)) { - vty_out(vty, "%% PCC is not connected to PCE [%s].\n", - pcc_peer); + if (json) { + json_object_string_addf(json, "warning", + "PCC is not connected to PCE [%s].", + pcc_peer); + vty_json(vty, json); + } else + vty_out(vty, + "%% PCC is not connected to PCE [%s].\n", + pcc_peer); return CMD_WARNING; } pcc_info = pcep_ctrl_get_pcc_info(pcep_g->fpt, pcc_peer); if (pcc_info == NULL) { - vty_out(vty, - "%% Cannot retrieve PCEP session info for PCE [%s]\n", - pcc_peer); + if (json) { + json_object_string_addf(json, "warning", + "Cannot retrieve PCEP session info for PCE [%s].", + pcc_peer); + vty_json(vty, json); + } else + vty_out(vty, + "%% Cannot retrieve PCEP session info for PCE [%s]\n", + pcc_peer); return CMD_WARNING; } - print_pcep_session(vty, &pce_opts_cli->pce_opts, pcc_info); + if (json) { + print_pcep_session_json(vty, &pce_opts_cli->pce_opts, + pcc_info, json); + vty_json(vty, json); + } else + print_pcep_session(vty, &pce_opts_cli->pce_opts, + pcc_info); return CMD_SUCCESS; } @@ -1388,29 +1608,56 @@ static int path_pcep_cli_show_srte_pcep_session(struct vty *vty, struct pce_opts *pce_opts; int num_pcep_sessions_conf = 0; int num_pcep_sessions_conn = 0; + json_object *json_array = NULL, *json_entry = NULL; + + if (json) + json_array = json_object_new_array(); for (int i = 0; i < MAX_PCC; i++) { pce_opts = pce_connections_g.connections[i]; if (pce_opts == NULL) { continue; } + if (json) { + json_entry = json_object_new_object(); + json_object_string_add(json_entry, "pceName", + pce_opts->pce_name); + } pcc_info = pcep_ctrl_get_pcc_info(pcep_g->fpt, pce_opts->pce_name); if (pcc_info == NULL) { - vty_out(vty, - "%% Cannot retrieve PCEP session info for PCE [%s]\n", - pce_opts->pce_name); + if (json_entry) { + json_object_string_addf(json_entry, "warning", + "Cannot retrieve PCEP session info for PCE [%s].", + pce_opts->pce_name); + json_object_array_add(json_array, json_entry); + } else + vty_out(vty, + "%% Cannot retrieve PCEP session info for PCE [%s]\n", + pce_opts->pce_name); continue; } num_pcep_sessions_conn += pcc_info->status == PCEP_PCC_OPERATING ? 1 : 0; num_pcep_sessions_conf++; - print_pcep_session(vty, pce_opts, pcc_info); - } - - vty_out(vty, "PCEP Sessions => Configured %d ; Connected %d\n", - num_pcep_sessions_conf, num_pcep_sessions_conn); + if (json_entry) { + print_pcep_session_json(vty, pce_opts, pcc_info, + json_entry); + json_object_array_add(json_array, json_entry); + } else + print_pcep_session(vty, pce_opts, pcc_info); + } + if (json) { + json_object_object_add(json, "pcepSessions", json_array); + json_object_int_add(json, "pcepSessionsConfigured", + num_pcep_sessions_conf); + json_object_int_add(json, "pcepSessionsConnected", + num_pcep_sessions_conn); + vty_json(vty, json); + } else + vty_out(vty, "PCEP Sessions => Configured %d ; Connected %d\n", + num_pcep_sessions_conf, num_pcep_sessions_conn); return CMD_SUCCESS; } @@ -1702,7 +1949,7 @@ int pcep_cli_pce_config_write(struct vty *vty) &pce_opts->addr.ipaddr_v4); } if (pce_opts->port != PCEP_DEFAULT_PORT) { - vty_out(vty, " %s %d", PCEP_VTYSH_ARG_PORT, + vty_out(vty, " %s %d", PCEP_VTYSH_ARG_PORT, pce_opts->port); } vty_out(vty, "%s\n", buf); @@ -1788,7 +2035,7 @@ DEFPY(show_debugging_pathd_pcep, DEFPY(pcep_cli_debug, pcep_cli_debug_cmd, - "[no] debug pathd pcep [basic]$basic_str [path]$path_str [message]$message_str [pceplib]$pceplib_str", + "[no] debug pathd pcep [$debug_type]", NO_STR DEBUG_STR "pathd debugging\n" "pcep module debugging\n" @@ -1797,8 +2044,7 @@ DEFPY(pcep_cli_debug, "pcep message debugging\n" "pceplib debugging\n") { - return path_pcep_cli_debug(vty, no, basic_str, path_str, message_str, - pceplib_str); + return path_pcep_cli_debug(vty, debug_type, !no); } DEFPY(pcep_cli_show_srte_pcep_counters, @@ -2040,12 +2286,21 @@ DEFPY(pcep_cli_no_pcc, DEFPY(pcep_cli_pcc_pcc_msd, pcep_cli_pcc_pcc_msd_cmd, - "[no] msd (1-32)", + "msd (1-32)", + "PCC maximum SID depth \n" + "PCC maximum SID depth value\n") +{ + return path_pcep_cli_pcc_pcc_msd(vty, msd_str, msd, false); +} + +DEFPY(no_pcep_cli_pcc_pcc_msd, + no_pcep_cli_pcc_pcc_msd_cmd, + "no msd [(1-32)]", NO_STR "PCC maximum SID depth \n" "PCC maximum SID depth value\n") { - return path_pcep_cli_pcc_pcc_msd(vty, msd_str, msd, no); + return path_pcep_cli_pcc_pcc_msd(vty, msd_str, msd, true); } DEFPY(pcep_cli_pcc_pcc_peer, @@ -2079,14 +2334,27 @@ DEFPY(pcep_cli_show_srte_pcc, DEFPY(pcep_cli_show_srte_pcep_session, pcep_cli_show_srte_pcep_session_cmd, - "show sr-te pcep session [WORD]$pce", + "show sr-te pcep session WORD$pce [json$uj]", SHOW_STR "SR-TE info\n" "PCEP info\n" "Show PCEP Session information\n" - "PCE name\n") + "PCE name\n" + JSON_STR) +{ + return path_pcep_cli_show_srte_pcep_session(vty, pce, !!uj); +} + +DEFPY(pcep_cli_show_srte_pcep_sessions, + pcep_cli_show_srte_pcep_sessions_cmd, + "show sr-te pcep session [json$uj]", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "Show PCEP Session information\n" + JSON_STR) { - return path_pcep_cli_show_srte_pcep_session(vty, pce); + return path_pcep_cli_show_srte_pcep_session(vty, NULL, !!uj); } DEFPY(pcep_cli_clear_srte_pcep_session, @@ -2150,6 +2418,7 @@ void pcep_cli_init(void) install_element(PCEP_NODE, &pcep_cli_no_pcc_cmd); install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_peer_cmd); install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_msd_cmd); + install_element(PCEP_PCC_NODE, &no_pcep_cli_pcc_pcc_msd_cmd); /* Top commands */ install_element(CONFIG_NODE, &pcep_cli_debug_cmd); @@ -2159,5 +2428,6 @@ void pcep_cli_init(void) install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_config_cmd); install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_cmd); install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_session_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_sessions_cmd); install_element(ENABLE_NODE, &pcep_cli_clear_srte_pcep_session_cmd); } diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c index 826443f..645fa50 100644 --- a/pathd/path_zebra.c +++ b/pathd/path_zebra.c @@ -320,9 +320,6 @@ static zclient_handler *const path_handlers[] = { */ void path_zebra_init(struct event_loop *master) { - struct zclient_options options = zclient_options_default; - options.synchronous = true; - /* Initialize asynchronous zclient. */ zclient = zclient_new(master, &zclient_options_default, path_handlers, array_size(path_handlers)); @@ -330,7 +327,7 @@ void path_zebra_init(struct event_loop *master) zclient->zebra_connected = path_zebra_connected; /* Initialize special zclient for synchronous message exchanges. */ - zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync = zclient_new(master, &zclient_options_sync, NULL, 0); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_SRTE; zclient_sync->instance = 1; diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index c4708d3..6695b53 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -71,6 +71,8 @@ static void sigint(void) pbr_vrf_terminate(); + pbr_zebra_destroy(); + frr_fini(); exit(0); @@ -101,26 +103,26 @@ struct frr_signal_t pbr_signals[] = { }, }; -#define PBR_VTY_PORT 2615 - static const struct frr_yang_module_info *const pbrd_yang_modules[] = { &frr_filter_info, &frr_interface_info, &frr_vrf_info, }; -FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, - - .proghelp = "Implementation of PBR.", +/* clang-format off */ +FRR_DAEMON_INFO(pbrd, PBR, + .vty_port = PBR_VTY_PORT, + .proghelp = "Implementation of PBR.", - .signals = pbr_signals, - .n_signals = array_size(pbr_signals), + .signals = pbr_signals, + .n_signals = array_size(pbr_signals), - .privs = &pbr_privs, + .privs = &pbr_privs, - .yang_modules = pbrd_yang_modules, - .n_yang_modules = array_size(pbrd_yang_modules), + .yang_modules = pbrd_yang_modules, + .n_yang_modules = array_size(pbrd_yang_modules), ); +/* clang-format on */ int main(int argc, char **argv, char **envp) { @@ -158,8 +160,10 @@ int main(int argc, char **argv, char **envp) access_list_init(); pbr_nht_init(); pbr_map_init(); - if_zapi_callbacks(pbr_ifp_create, pbr_ifp_up, - pbr_ifp_down, pbr_ifp_destroy); + hook_register_prio(if_real, 0, pbr_ifp_create); + hook_register_prio(if_up, 0, pbr_ifp_up); + hook_register_prio(if_down, 0, pbr_ifp_down); + hook_register_prio(if_unreal, 0, pbr_ifp_destroy); pbr_zebra_init(); pbr_vrf_init(); pbr_vty_init(); diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 4f7882f..ff252f8 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -534,6 +534,7 @@ void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence *pbrms, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: pbrms->family = AF_INET; + break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_BLACKHOLE: break; @@ -649,7 +650,15 @@ static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms) void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) { - pbr_map_delete_nexthops(pbrms); + struct pbr_map *pbrm = pbrms->parent; + + /* The idea here is to send a delete command to zebra only once, + * and set 'valid' and 'installed' to false only when the last + * rule is being deleted. In other words, the pbr common should be + * updated only when the last rule is being updated or deleted. + */ + if (pbrm->seqnumbers->count == 1) + pbr_map_delete_nexthops(pbrms); pbr_nht_release_individual_nexthop(pbrms); } @@ -889,7 +898,7 @@ static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc, pbr_nht_individual_nexthop_interface_update(pnhc, pnhi); break; } - /* Intentional fall thru */ + fallthrough; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 9b67492..a702a57 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -36,7 +36,7 @@ struct pbr_nexthop_cache { struct pbr_nexthop_group_cache *parent; char vrf_name[VRF_NAMSIZ + 1]; - char intf_name[INTERFACE_NAMSIZ + 1]; + char intf_name[IFNAMSIZ + 1]; struct nexthop nexthop; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 9589e5b..64d8884 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1667,8 +1667,7 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno); json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno); json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged); - json_object_boolean_add(jpbrm, "installed", - pbr_nht_get_installed(nhg_name)); + json_object_boolean_add(jpbrm, "installed", pbrms->installed); json_object_string_add(jpbrm, "installedReason", pbrms->reason ? rbuf : "Valid"); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 30eaf62..dd15bea 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -178,7 +178,7 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) enum zapi_rule_notify_owner note; struct pbr_map_sequence *pbrms; struct pbr_map_interface *pmi; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; uint64_t installed; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, @@ -222,6 +222,8 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) static void zebra_connected(struct zclient *zclient) { DEBUGD(&pbr_dbg_zebra, "%s: Registering for fun and profit", __func__); + + zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true); zclient_send_reg_requests(zclient, VRF_DEFAULT); } @@ -364,38 +366,30 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) } } -static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) +static void pbr_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched, + struct zapi_route *nhr) { - struct zapi_route nhr; - struct prefix matched; uint32_t i; - if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { - zlog_err("Failure to decode Nexthop update message"); - return 0; - } - if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) { - DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %pFX against %pFX", - __func__, &matched, &nhr.prefix); + __func__, matched, &nhr->prefix); DEBUGD(&pbr_dbg_zebra, "%s: (Nexthops(%u)", __func__, - nhr.nexthop_num); + nhr->nexthop_num); - for (i = 0; i < nhr.nexthop_num; i++) { + for (i = 0; i < nhr->nexthop_num; i++) { DEBUGD(&pbr_dbg_zebra, "%s: Type: %d: vrf: %d, ifindex: %d gate: %pI4", - __func__, nhr.nexthops[i].type, - nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex, - &nhr.nexthops[i].gate.ipv4); + __func__, nhr->nexthops[i].type, + nhr->nexthops[i].vrf_id, nhr->nexthops[i].ifindex, + &nhr->nexthops[i].gate.ipv4); } } - nhr.prefix = matched; - pbr_nht_nexthop_update(&nhr); - return 1; + nhr->prefix = *matched; + pbr_nht_nexthop_update(nhr); } extern struct zebra_privs_t pbr_privs; @@ -405,18 +399,26 @@ static zclient_handler *const pbr_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, [ZEBRA_RULE_NOTIFY_OWNER] = rule_notify_owner, - [ZEBRA_NEXTHOP_UPDATE] = pbr_zebra_nexthop_update, }; void pbr_zebra_init(void) { - struct zclient_options opt = { .receive_notify = true }; - - zclient = zclient_new(master, &opt, pbr_handlers, + zclient = zclient_new(master, &zclient_options_default, pbr_handlers, array_size(pbr_handlers)); zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs); zclient->zebra_connected = zebra_connected; + zclient->nexthop_update = pbr_zebra_nexthop_update; +} + +void pbr_zebra_destroy(void) +{ + if (zclient == NULL) + return; + + zclient_stop(zclient); + zclient_free(zclient); + zclient = NULL; } void pbr_send_rnh(struct nexthop *nhop, bool reg) diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h index ef844ef..5cbb1fd 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -14,6 +14,7 @@ struct pbr_interface { extern struct event_loop *master; extern void pbr_zebra_init(void); +extern void pbr_zebra_destroy(void); extern void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg, afi_t install_afi); diff --git a/pceplib/pcep_session_logic_counters.c b/pceplib/pcep_session_logic_counters.c index 6f6f2a0..b12b8ab 100644 --- a/pceplib/pcep_session_logic_counters.c +++ b/pceplib/pcep_session_logic_counters.c @@ -36,30 +36,30 @@ void create_session_counters(pcep_session *session) struct counters_subgroup *rx_msg_subgroup = create_counters_subgroup( "RX Message counters", COUNTER_SUBGROUP_ID_RX_MSG, PCEP_TYPE_MAX + 1); - create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_OPEN, - "Message Open"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_OPEN, "Message Open", + "messageOpen"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_KEEPALIVE, - "Message KeepAlive"); + "Message KeepAlive", "messageKeepalive"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREQ, - "Message PcReq"); + "Message PcReq", "messagePcReq"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREP, - "Message PcRep"); + "Message PcRep", "messagePcRep"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCNOTF, - "Message Notify"); + "Message Notify", "messageNotify"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_ERROR, - "Message Error"); + "Message Error", "messageError"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_CLOSE, - "Message Close"); + "Message Close", "messageClose"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_REPORT, - "Message Report"); + "Message Report", "messageReport"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_UPDATE, - "Message Update"); + "Message Update", "messageUpdate"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_INITIATE, - "Message Initiate"); + "Message Initiate", "messageInitiate"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_START_TLS, - "Message StartTls"); + "Message StartTls", "messageStartTls"); create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_MAX, - "Message Erroneous"); + "Message Erroneous", "messageErroneous"); struct counters_subgroup *tx_msg_subgroup = clone_counters_subgroup(rx_msg_subgroup, "TX Message counters", @@ -74,59 +74,61 @@ void create_session_counters(pcep_session *session) struct counters_subgroup *rx_obj_subgroup = create_counters_subgroup( "RX Object counters", COUNTER_SUBGROUP_ID_RX_OBJ, 100); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_OPEN, - "Object Open"); - create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RP, - "Object RP"); + "Object Open", "objectOpen"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RP, "Object RP", + "objectRP"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOPATH, - "Object Nopath"); - create_subgroup_counter( - rx_obj_subgroup, - ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV4), - "Object Endpoint IPv4"); - create_subgroup_counter( - rx_obj_subgroup, - ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV6), - "Object Endpoint IPv6"); + "Object Nopath", "objectNopath"); + create_subgroup_counter(rx_obj_subgroup, + ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | + PCEP_OBJ_TYPE_ENDPOINT_IPV4), + "Object Endpoint IPv4", "objectEndpointIPv4"); + create_subgroup_counter(rx_obj_subgroup, + ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | + PCEP_OBJ_TYPE_ENDPOINT_IPV6), + "Object Endpoint IPv6", "objectEndpointIPv6"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_BANDWIDTH, - "Object Bandwidth"); + "Object Bandwidth", "objectBandwidth"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_METRIC, - "Object Metric"); + "Object Metric", "objectMetric"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERO, - "Object ERO"); + "Object ERO", "objectERO"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RRO, - "Object RRO"); + "Object RRO", "objectRRO"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSPA, - "Object LSPA"); + "Object LSPA", "objectLSPA"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_IRO, - "Object IRO"); + "Object IRO", "objectIRO"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SVEC, - "Object SVEC"); + "Object SVEC", "objectSVEC"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOTF, - "Object Notify"); + "Object Notify", "objectNotify"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERROR, - "Object Error"); + "Object Error", "objectError"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_CLOSE, - "Object Close"); + "Object Close", "objectClose"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSP, - "Object LSP"); + "Object LSP", "objectLSP"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SRP, - "Object SRP"); + "Object SRP", "objectSRP"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_VENDOR_INFO, - "Object Vendor Info"); + "Object Vendor Info", "objectVendorInfo"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_INTER_LAYER, - "Object Inter-Layer"); + "Object Inter-Layer", "objectInterLayer"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SWITCH_LAYER, - "Object Switch-Layer"); + "Object Switch-Layer", "objectSwitchLayer"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_REQ_ADAP_CAP, - "Object Requested Adap-Cap"); + "Object Requested Adap-Cap", + "objectRequestedAdapCap"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SERVER_IND, - "Object Server-Indication"); + "Object Server-Indication", + "objectServerIndication"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ASSOCIATION, - "Object Association"); + "Object Association", "objectAssociation"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX, - "Object Unknown"); + "Object Unknown", "objectUnknown"); create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX + 1, - "Object Erroneous"); + "Object Erroneous", "objectErroneous"); struct counters_subgroup *tx_obj_subgroup = clone_counters_subgroup(rx_obj_subgroup, "TX Object counters", @@ -139,21 +141,22 @@ void create_session_counters(pcep_session *session) "RX RO Sub-Object counters", COUNTER_SUBGROUP_ID_RX_SUBOBJ, RO_SUBOBJ_UNKNOWN + 2); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV4, - "RO Sub-Object IPv4"); + "RO Sub-Object IPv4", "ROSubObjectIPv4"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV6, - "RO Sub-Object IPv6"); + "RO Sub-Object IPv6", "ROSubObjectIPv6"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_LABEL, - "RO Sub-Object Label"); + "RO Sub-Object Label", "ROSubObjectLabel"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_UNNUM, - "RO Sub-Object Unnum"); + "RO Sub-Object Unnum", "ROSubObjectUnnum"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_ASN, - "RO Sub-Object ASN"); + "RO Sub-Object ASN", "ROSubObjectASN"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_SR, - "RO Sub-Object SR"); + "RO Sub-Object SR", "ROSubObjectSR"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN, - "RO Sub-Object Unknown"); + "RO Sub-Object Unknown", "ROSubObjectUnknown"); create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN + 1, - "RO Sub-Object Erroneous"); + "RO Sub-Object Erroneous", + "ROSubObjectErroneous"); struct counters_subgroup *tx_subobj_subgroup = clone_counters_subgroup( rx_subobj_subgroup, "TX RO Sub-Object counters", @@ -168,28 +171,36 @@ void create_session_counters(pcep_session *session) PCEP_SR_SUBOBJ_NAI_UNKNOWN + 1); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_ABSENT, - "RO Sub-Object SR NAI absent"); + "RO Sub-Object SR NAI absent", + "ROSubObjectSRNAIAbsent"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_IPV4_NODE, - "RO Sub-Object SR NAI IPv4 Node"); + "RO Sub-Object SR NAI IPv4 Node", + "ROSubObjectSRNAIIPv4Node"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_IPV6_NODE, - "RO Sub-Object SR NAI IPv6 Node"); + "RO Sub-Object SR NAI IPv6 Node", + "ROSubObjectSRNAIIPv6Node"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, - "RO Sub-Object SR NAI IPv4 Adj"); + "RO Sub-Object SR NAI IPv4 Adj", + "ROSubObjectSRNAIIPv4Adj"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, - "RO Sub-Object SR NAI IPv6 Adj"); + "RO Sub-Object SR NAI IPv6 Adj", + "ROSubObjectSRNAIIPv6Adj"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, - "RO Sub-Object SR NAI Unnumbered IPv4 Adj"); + "RO Sub-Object SR NAI Unnumbered IPv4 Adj", + "ROSubObjectSRNAIUnnumberedIPv4Adj"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, - "RO Sub-Object SR NAI Link Local IPv6 Adj"); + "RO Sub-Object SR NAI Link Local IPv6 Adj", + "ROSubObjectSRNAILinkLocalIPv6Adj"); create_subgroup_counter(rx_subobj_sr_nai_subgroup, PCEP_SR_SUBOBJ_NAI_UNKNOWN, - "RO Sub-Object SR NAI Unknown"); + "RO Sub-Object SR NAI Unknown", + "ROSubObjectSRNAIUnknown"); struct counters_subgroup *tx_subobj_sr_nai_subgroup = clone_counters_subgroup(rx_subobj_sr_nai_subgroup, @@ -204,56 +215,60 @@ void create_session_counters(pcep_session *session) PCEP_OBJ_TLV_TYPE_UNKNOWN + 1); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR, - "TLV No Path Vector"); + "TLV No Path Vector", "TLVNoPathVector"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_VENDOR_INFO, - "TLV Vendor Info"); + "TLV Vendor Info", "TLVVendorInfo"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY, - "TLV Stateful PCE Capability"); + "TLV Stateful PCE Capability", + "TLVStatefulPCCapability"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME, - "TLV Symbolic Path Name"); + "TLV Symbolic Path Name", "TLVSymbolicPathName"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS, - "TLV IPv4 LSP Identifier"); + "TLV IPv4 LSP Identifier", + "TLVIPv4LSPIdentifier"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS, - "TLV IPv6 LSP Identifier"); + "TLV IPv6 LSP Identifier", + "TLVIPv6LSPIdentifier"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE, - "TLV LSP Error Code"); + "TLV LSP Error Code", "TLVLSPErrorCode"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC, - "TLV RSVP Error Spec"); + "TLV RSVP Error Spec", "TLVRSVPErrorSpec"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION, - "TLV LSP DB Version"); + "TLV LSP DB Version", "TLVLSPDBVersion"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID, - "TLV Speaker Entity ID"); + "TLV Speaker Entity ID", "TLVSpeakerEntityId"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY, - "TLV SR PCE Capability"); + "TLV SR PCE Capability", "TLVSRPCECapability"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE, - "TLV Path Setup Type"); + "TLV Path Setup Type", "TLVPathSetupType"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY, - "TLV Path Setup Type Capability"); + "TLV Path Setup Type Capability", + "TLVPathSetupTypeCapability"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID, - "TLV SR Policy PolId"); + "TLV SR Policy PolId", "TLVSRPolicyPolId"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME, - "TLV SR Policy PolName"); + "TLV SR Policy PolName", "TLVSRPolicyPolName"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID, - "TLV SR Policy CpathId"); + "TLV SR Policy CpathId", "TLVSRPolicyCpathId"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE, - "TLV SR Policy CpathRef"); + "TLV SR Policy CpathRef", "TLVSRPolicyCpathRef"); create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_UNKNOWN, - "TLV Unknown"); + "TLV Unknown", "TLVUnknown"); struct counters_subgroup *tx_tlv_subgroup = clone_counters_subgroup( rx_tlv_subgroup, "TX TLV counters", COUNTER_SUBGROUP_ID_TX_TLV); @@ -265,28 +280,32 @@ void create_session_counters(pcep_session *session) "Events counters", COUNTER_SUBGROUP_ID_EVENT, MAX_COUNTERS); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_PCC_CONNECT, - "PCC connect"); + "PCC connect", "PCCConnect"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_PCE_CONNECT, - "PCE connect"); + "PCE connect", "PCEConnect"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT, - "PCC disconnect"); + "PCC disconnect", "PCCDisconnect"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT, - "PCE disconnect"); + "PCE disconnect", "PCEDisconnect"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE, - "Timer KeepAlive expired"); + "Timer KeepAlive expired", + "timerKeepAliveExpired"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER, - "Timer DeadTimer expired"); + "Timer DeadTimer expired", + "timerDeadTimerExpired"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT, - "Timer OpenKeepWait expired"); + "Timer OpenKeepWait expired", + "timerOpenKeepWaitExpired"); create_subgroup_counter(events_subgroup, PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE, - "Timer OpenKeepAlive expired"); + "Timer OpenKeepAlive expired", + "timerOpenKeepAliveExpired"); /* * Create the parent counters group diff --git a/pceplib/pcep_socket_comm_mock.c b/pceplib/pcep_socket_comm_mock.c index 24755ef..bda9b1f 100644 --- a/pceplib/pcep_socket_comm_mock.c +++ b/pceplib/pcep_socket_comm_mock.c @@ -115,7 +115,7 @@ bool initialize_socket_comm_external_infra( return true; } -bool destroy_socket_comm_loop() +bool destroy_socket_comm_loop(void) { mock_socket_metadata.destroy_socket_comm_loop_times_called++; diff --git a/pceplib/pcep_utils_counters.c b/pceplib/pcep_utils_counters.c index badef93..1ab341c 100644 --- a/pceplib/pcep_utils_counters.c +++ b/pceplib/pcep_utils_counters.c @@ -139,7 +139,8 @@ clone_counters_subgroup(struct counters_subgroup *subgroup, if (counter != NULL) { create_subgroup_counter(cloned_subgroup, counter->counter_id, - counter->counter_name); + counter->counter_name, + counter->counter_name_json); } } @@ -180,7 +181,8 @@ bool add_counters_subgroup(struct counters_group *group, } bool create_subgroup_counter(struct counters_subgroup *subgroup, - uint32_t counter_id, const char *counter_name) + uint32_t counter_id, const char *counter_name, + const char *counter_name_json) { if (subgroup == NULL) { pcep_log( @@ -212,7 +214,9 @@ bool create_subgroup_counter(struct counters_subgroup *subgroup, counter->counter_id = counter_id; strlcpy(counter->counter_name, counter_name, sizeof(counter->counter_name)); - + if (counter_name_json) + strlcpy(counter->counter_name_json, counter_name_json, + sizeof(counter->counter_name_json)); subgroup->num_counters++; subgroup->counters[counter->counter_id] = counter; diff --git a/pceplib/pcep_utils_counters.h b/pceplib/pcep_utils_counters.h index 755c94e..dfae02f 100644 --- a/pceplib/pcep_utils_counters.h +++ b/pceplib/pcep_utils_counters.h @@ -55,18 +55,18 @@ extern "C" { * *events_subgroup = create_counters_subgroup("events counters", 4); * * Use message_id: PCEP_TYPE_OPEN=1 - * create_subgroup_counter(rx_subgroup, 1, "Message Open"); - * create_subgroup_counter(rx_subgroup, 2, "Message KeepAlive"); - * create_subgroup_counter(rx_subgroup, 3, "Message PcReq"); + * create_subgroup_counter(rx_subgroup, 1, "Message Open", "messageOpen"); + * create_subgroup_counter(rx_subgroup, 2, "Message KeepAlive", "messageKeepAlive"); + * create_subgroup_counter(rx_subgroup, 3, "Message PcReq", "messagePcReq"); * - * create_subgroup_counter(tx_subgroup, 1, "Message Open"); - * create_subgroup_counter(tx_subgroup, 2, "Message KeepAlive"); - * create_subgroup_counter(tx_subgroup, 3, "Message PcReq"); + * create_subgroup_counter(tx_subgroup, 1, "Message Open", "messageOpen"); + * create_subgroup_counter(tx_subgroup, 2, "Message KeepAlive", "messageKeepAlive"); + * create_subgroup_counter(tx_subgroup, 3, "Message PcReq", "messagePcReq"); * - * create_subgroup_counter(events_subgroup, 1, "PCC Connect"); - * create_subgroup_counter(events_subgroup, 2, "PCE Connect"); - * create_subgroup_counter(events_subgroup, 3, "PCC Disconnect"); - * create_subgroup_counter(events_subgroup, 4, "PCE Disconnect"); + * create_subgroup_counter(events_subgroup, 1, "PCC Connect", "PCConnect"); + * create_subgroup_counter(events_subgroup, 2, "PCE Connect", "PCEConnect"); + * create_subgroup_counter(events_subgroup, 3, "PCC Disconnect", "PCCDisconnect"); + * create_subgroup_counter(events_subgroup, 4, "PCE Disconnect", "PCEDisconnect"); * * struct counters_group *cntrs_group = create_counters_group("PCEP Counters", * 3); add_counters_subgroup(cntrs_group, rx_subgroup); @@ -81,6 +81,7 @@ extern "C" { struct counter { uint16_t counter_id; char counter_name[MAX_COUNTER_STR_LENGTH]; + char counter_name_json[MAX_COUNTER_STR_LENGTH]; uint32_t counter_value; }; @@ -142,13 +143,15 @@ clone_counters_subgroup(struct counters_subgroup *subgroup, const char *subgroup_name, uint16_t subgroup_id); /* - * Create a counter in a subgroup with the given counter_id and counter_name. + * Create a counter in a subgroup with the given counter_id and counter_name + * and counter_name_json. * The counter_id is 0-based. * Return true on success or false if subgroup is NULL, counter_id >= * MAX_COUNTERS, or if counter_name is NULL. */ bool create_subgroup_counter(struct counters_subgroup *subgroup, - uint32_t counter_id, const char *counter_name); + uint32_t counter_id, const char *counter_name, + const char *couter_name_json); /* * Delete the counters_group and recursively delete all subgroups and their diff --git a/pceplib/test/pcep_msg_messages_test.c b/pceplib/test/pcep_msg_messages_test.c index c4eaeaa..e1f5249 100644 --- a/pceplib/test/pcep_msg_messages_test.c +++ b/pceplib/test/pcep_msg_messages_test.c @@ -48,17 +48,17 @@ int pcep_messages_test_suite_teardown(void) return 0; } -void pcep_messages_test_setup() +void pcep_messages_test_setup(void) { versioning = create_default_pcep_versioning(); } -void pcep_messages_test_teardown() +void pcep_messages_test_teardown(void) { destroy_pcep_versioning(versioning); } -void test_pcep_msg_create_open() +void test_pcep_msg_create_open(void) { uint8_t keepalive = 30; uint8_t deadtimer = 60; @@ -96,8 +96,7 @@ void test_pcep_msg_create_open() pcep_msg_free_message(message); } - -void test_pcep_msg_create_request() +void test_pcep_msg_create_request(void) { /* First test with NULL objects */ struct pcep_message *message = @@ -185,13 +184,11 @@ void test_pcep_msg_create_request() pcep_msg_free_message(message); } - -void test_pcep_msg_create_request_svec() +void test_pcep_msg_create_request_svec(void) { } - -void test_pcep_msg_create_reply_nopath() +void test_pcep_msg_create_reply_nopath(void) { struct pcep_object_rp *rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL); @@ -219,8 +216,7 @@ void test_pcep_msg_create_reply_nopath() pcep_msg_free_message(message); } - -void test_pcep_msg_create_reply() +void test_pcep_msg_create_reply(void) { /* First test with NULL ero and rp objects */ struct pcep_message *message = pcep_msg_create_reply(NULL, NULL); @@ -268,8 +264,7 @@ void test_pcep_msg_create_reply() pcep_msg_free_message(message); } - -void test_pcep_msg_create_close() +void test_pcep_msg_create_close(void) { uint8_t reason = PCEP_CLOSE_REASON_UNREC_MSG; @@ -301,8 +296,7 @@ void test_pcep_msg_create_close() pcep_msg_free_message(message); } - -void test_pcep_msg_create_error() +void test_pcep_msg_create_error(void) { uint8_t error_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; uint8_t error_value = PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT; @@ -337,8 +331,7 @@ void test_pcep_msg_create_error() pcep_msg_free_message(message); } - -void test_pcep_msg_create_keepalive() +void test_pcep_msg_create_keepalive(void) { struct pcep_message *message = pcep_msg_create_keepalive(); CU_ASSERT_PTR_NOT_NULL(message); @@ -356,7 +349,7 @@ void test_pcep_msg_create_keepalive() pcep_msg_free_message(message); } -void test_pcep_msg_create_report() +void test_pcep_msg_create_report(void) { double_linked_list *obj_list = dll_initialize(); @@ -387,7 +380,7 @@ void test_pcep_msg_create_report() pcep_msg_free_message(message); } -void test_pcep_msg_create_update() +void test_pcep_msg_create_update(void) { double_linked_list *obj_list = dll_initialize(); double_linked_list *ero_subobj_list = dll_initialize(); @@ -398,10 +391,8 @@ void test_pcep_msg_create_update() /* Should return NULL if obj_list is empty */ message = pcep_msg_create_update(obj_list); CU_ASSERT_PTR_NULL(message); - if (message != NULL) { + if (message != NULL) pcep_msg_free_message(message); - message = NULL; - } struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); struct pcep_object_lsp *lsp = @@ -417,10 +408,9 @@ void test_pcep_msg_create_update() CU_ASSERT_PTR_NULL(message); dll_append(obj_list, ero); - if (message != NULL) { + if (message != NULL) pcep_msg_free_message(message); - message = NULL; - } + message = pcep_msg_create_update(obj_list); CU_ASSERT_PTR_NOT_NULL(message); pcep_encode_message(message, versioning); @@ -442,7 +432,7 @@ void test_pcep_msg_create_update() pcep_msg_free_message(message); } -void test_pcep_msg_create_initiate() +void test_pcep_msg_create_initiate(void) { double_linked_list *obj_list = dll_initialize(); double_linked_list *ero_subobj_list = dll_initialize(); @@ -450,10 +440,8 @@ void test_pcep_msg_create_initiate() /* Should return NULL if obj_list is empty */ struct pcep_message *message = pcep_msg_create_initiate(NULL); CU_ASSERT_PTR_NULL(message); - if (message != NULL) { + if (message != NULL) pcep_msg_free_message(message); - message = NULL; - } struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); struct pcep_object_lsp *lsp = @@ -466,10 +454,8 @@ void test_pcep_msg_create_initiate() dll_append(obj_list, srp); message = pcep_msg_create_initiate(obj_list); CU_ASSERT_PTR_NULL(message); - if (message != NULL) { + if (message != NULL) pcep_msg_free_message(message); - message = NULL; - } dll_append(obj_list, lsp); dll_append(obj_list, ero); diff --git a/pceplib/test/pcep_msg_object_error_types_test.c b/pceplib/test/pcep_msg_object_error_types_test.c index 3237fec..b7198fb 100644 --- a/pceplib/test/pcep_msg_object_error_types_test.c +++ b/pceplib/test/pcep_msg_object_error_types_test.c @@ -44,7 +44,7 @@ void pcep_object_error_types_test_teardown(void) { } -void test_get_error_type_str() +void test_get_error_type_str(void) { const char *error_type_str; int i = 0; @@ -57,7 +57,7 @@ void test_get_error_type_str() CU_ASSERT_PTR_NULL(get_error_type_str(MAX_ERROR_TYPE)); } -void test_get_error_value_str() +void test_get_error_value_str(void) { const char *error_value_str; int i = 0, j = 0; diff --git a/pceplib/test/pcep_msg_objects_test.c b/pceplib/test/pcep_msg_objects_test.c index 1315fe3..0a1d34d 100644 --- a/pceplib/test/pcep_msg_objects_test.c +++ b/pceplib/test/pcep_msg_objects_test.c @@ -49,18 +49,18 @@ int pcep_objects_test_suite_teardown(void) return 0; } -void reset_objects_buffer() +void reset_objects_buffer(void) { memset(object_buf, 0, 2000); } -void pcep_objects_test_setup() +void pcep_objects_test_setup(void) { versioning = create_default_pcep_versioning(); reset_objects_buffer(); } -void pcep_objects_test_teardown() +void pcep_objects_test_teardown(void) { destroy_pcep_versioning(versioning); } @@ -123,7 +123,7 @@ static void verify_pcep_obj_header(uint8_t obj_class, uint8_t obj_type, obj_hdr->encoded_object); } -void test_pcep_obj_create_open() +void test_pcep_obj_create_open(void) { uint8_t deadtimer = 60; uint8_t keepalive = 30; @@ -147,7 +147,7 @@ void test_pcep_obj_create_open() pcep_obj_free_object((struct pcep_object_header *)open); } -void test_pcep_obj_create_open_with_tlvs() +void test_pcep_obj_create_open_with_tlvs(void) { uint8_t deadtimer = 60; uint8_t keepalive = 30; @@ -182,7 +182,7 @@ void test_pcep_obj_create_open_with_tlvs() pcep_obj_free_object((struct pcep_object_header *)open); } -void test_pcep_obj_create_rp() +void test_pcep_obj_create_rp(void) { uint32_t reqid = 15; uint8_t invalid_priority = 100; @@ -213,7 +213,7 @@ void test_pcep_obj_create_rp() pcep_obj_free_object((struct pcep_object_header *)rp); } -void test_pcep_obj_create_nopath() +void test_pcep_obj_create_nopath(void) { uint8_t ni = 8; uint32_t errorcode = 42; @@ -251,7 +251,8 @@ void test_pcep_obj_create_nopath() pcep_obj_free_object((struct pcep_object_header *)nopath); } -void test_pcep_obj_create_association_ipv4() + +void test_pcep_obj_create_association_ipv4(void) { uint16_t all_assoc_groups = 0xffff; @@ -275,7 +276,7 @@ void test_pcep_obj_create_association_ipv4() pcep_obj_free_object((struct pcep_object_header *)assoc); } -void test_pcep_obj_create_association_ipv6() +void test_pcep_obj_create_association_ipv6(void) { uint32_t all_assoc_groups = 0xffff; struct in6_addr src; @@ -305,7 +306,7 @@ void test_pcep_obj_create_association_ipv6() pcep_obj_free_object((struct pcep_object_header *)assoc); } -void test_pcep_obj_create_endpoint_ipv4() +void test_pcep_obj_create_endpoint_ipv4(void) { struct in_addr src_ipv4, dst_ipv4; inet_pton(AF_INET, "192.168.1.2", &src_ipv4); @@ -334,7 +335,7 @@ void test_pcep_obj_create_endpoint_ipv4() pcep_obj_free_object((struct pcep_object_header *)ipv4); } -void test_pcep_obj_create_endpoint_ipv6() +void test_pcep_obj_create_endpoint_ipv6(void) { struct in6_addr src_ipv6, dst_ipv6; inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src_ipv6); @@ -368,7 +369,7 @@ void test_pcep_obj_create_endpoint_ipv6() pcep_obj_free_object((struct pcep_object_header *)ipv6); } -void test_pcep_obj_create_bandwidth() +void test_pcep_obj_create_bandwidth(void) { /* 1.8 => binary 1.11001101 * exponent = 127 => 0111 1111 @@ -389,7 +390,7 @@ void test_pcep_obj_create_bandwidth() pcep_obj_free_object((struct pcep_object_header *)bw); } -void test_pcep_obj_create_metric() +void test_pcep_obj_create_metric(void) { uint8_t type = PCEP_METRIC_BORDER_NODE_COUNT; /* https://en.wikipedia.org/wiki/IEEE_754-1985 @@ -421,7 +422,7 @@ void test_pcep_obj_create_metric() pcep_obj_free_object((struct pcep_object_header *)metric); } -void test_pcep_obj_create_lspa() +void test_pcep_obj_create_lspa(void) { uint32_t exclude_any = 10; uint32_t include_any = 20; @@ -448,7 +449,7 @@ void test_pcep_obj_create_lspa() pcep_obj_free_object((struct pcep_object_header *)lspa); } -void test_pcep_obj_create_svec() +void test_pcep_obj_create_svec(void) { struct pcep_object_svec *svec = pcep_obj_create_svec(true, true, true, NULL); @@ -479,7 +480,7 @@ void test_pcep_obj_create_svec() pcep_obj_free_object((struct pcep_object_header *)svec); } -void test_pcep_obj_create_error() +void test_pcep_obj_create_error(void) { uint8_t error_type = PCEP_ERRT_SESSION_FAILURE; uint8_t error_value = PCEP_ERRV_RECVD_INVALID_OPEN_MSG; @@ -499,7 +500,7 @@ void test_pcep_obj_create_error() pcep_obj_free_object((struct pcep_object_header *)error); } -void test_pcep_obj_create_close() +void test_pcep_obj_create_close(void) { uint8_t reason = PCEP_CLOSE_REASON_DEADTIMER; @@ -517,7 +518,7 @@ void test_pcep_obj_create_close() pcep_obj_free_object((struct pcep_object_header *)close); } -void test_pcep_obj_create_srp() +void test_pcep_obj_create_srp(void) { bool lsp_remove = true; uint32_t srp_id_number = 0x89674523; @@ -538,7 +539,7 @@ void test_pcep_obj_create_srp() pcep_obj_free_object((struct pcep_object_header *)srp); } -void test_pcep_obj_create_lsp() +void test_pcep_obj_create_lsp(void) { uint32_t plsp_id = 0x000fffff; enum pcep_lsp_operational_status status = PCEP_LSP_OPERATIONAL_ACTIVE; @@ -579,7 +580,7 @@ void test_pcep_obj_create_lsp() pcep_obj_free_object((struct pcep_object_header *)lsp); } -void test_pcep_obj_create_vendor_info() +void test_pcep_obj_create_vendor_info(void) { uint32_t enterprise_number = 0x01020304; uint32_t enterprise_specific_info = 0x05060708; @@ -645,19 +646,19 @@ static void test_pcep_obj_create_object_common(ro_func func_to_test, pcep_obj_free_object((struct pcep_object_header *)ero); } -void test_pcep_obj_create_ero() +void test_pcep_obj_create_ero(void) { test_pcep_obj_create_object_common( pcep_obj_create_ero, PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO); } -void test_pcep_obj_create_rro() +void test_pcep_obj_create_rro(void) { test_pcep_obj_create_object_common( pcep_obj_create_rro, PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO); } -void test_pcep_obj_create_iro() +void test_pcep_obj_create_iro(void) { test_pcep_obj_create_object_common( pcep_obj_create_iro, PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO); @@ -731,7 +732,7 @@ verify_pcep_obj_ro_sr_header(struct pcep_object_ro *ro, } } -void test_pcep_obj_create_ro_subobj_ipv4() +void test_pcep_obj_create_ro_subobj_ipv4(void) { struct in_addr ro_ipv4; inet_pton(AF_INET, "192.168.1.2", &ro_ipv4); @@ -768,7 +769,7 @@ void test_pcep_obj_create_ro_subobj_ipv4() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_ipv6() +void test_pcep_obj_create_ro_subobj_ipv6(void) { struct in6_addr ro_ipv6; uint8_t prefix_len = 16; @@ -811,7 +812,7 @@ void test_pcep_obj_create_ro_subobj_ipv6() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_unnum() +void test_pcep_obj_create_ro_subobj_unnum(void) { struct in_addr router_id; uint32_t if_id = 123; @@ -836,7 +837,7 @@ void test_pcep_obj_create_ro_subobj_unnum() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_32label() +void test_pcep_obj_create_ro_subobj_32label(void) { uint8_t class_type = 1; uint32_t label = 0xeeffaabb; @@ -856,7 +857,7 @@ void test_pcep_obj_create_ro_subobj_32label() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_asn() +void test_pcep_obj_create_ro_subobj_asn(void) { uint16_t asn = 0x0102; @@ -871,7 +872,7 @@ void test_pcep_obj_create_ro_subobj_asn() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_nonai() +void test_pcep_obj_create_ro_subobj_sr_nonai(void) { uint32_t sid = 0x01020304; @@ -902,7 +903,7 @@ void test_pcep_obj_create_ro_subobj_sr_nonai() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_ipv4_node() +void test_pcep_obj_create_ro_subobj_sr_ipv4_node(void) { uint32_t sid = 0x01020304; struct in_addr ipv4_node_id; @@ -954,7 +955,7 @@ void test_pcep_obj_create_ro_subobj_sr_ipv4_node() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_ipv6_node() +void test_pcep_obj_create_ro_subobj_sr_ipv6_node(void) { uint32_t sid = 0x01020304; struct in6_addr ipv6_node_id; @@ -1007,7 +1008,7 @@ void test_pcep_obj_create_ro_subobj_sr_ipv6_node() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_ipv4_adj() +void test_pcep_obj_create_ro_subobj_sr_ipv4_adj(void) { struct in_addr local_ipv4; struct in_addr remote_ipv4; @@ -1072,7 +1073,7 @@ void test_pcep_obj_create_ro_subobj_sr_ipv4_adj() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_ipv6_adj() +void test_pcep_obj_create_ro_subobj_sr_ipv6_adj(void) { uint32_t sid = 0x01020304; struct in6_addr local_ipv6; @@ -1147,7 +1148,7 @@ void test_pcep_obj_create_ro_subobj_sr_ipv6_adj() pcep_obj_free_object((struct pcep_object_header *)ro); } -void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj() +void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(void) { uint32_t sid = 0x01020304; uint32_t local_node_id = 0x11223344; @@ -1208,7 +1209,7 @@ void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj() /* TODO Test draft07 types */ } -void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj() +void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(void) { uint32_t sid = 0x01020304; uint32_t local_if_id = 0x11002200; diff --git a/pceplib/test/pcep_msg_tlvs_test.c b/pceplib/test/pcep_msg_tlvs_test.c index 3ead269..fc11205 100644 --- a/pceplib/test/pcep_msg_tlvs_test.c +++ b/pceplib/test/pcep_msg_tlvs_test.c @@ -55,23 +55,23 @@ int pcep_tlvs_test_suite_teardown(void) return 0; } -void reset_tlv_buffer() +void reset_tlv_buffer(void) { memset(tlv_buf, 0, 2000); } -void pcep_tlvs_test_setup() +void pcep_tlvs_test_setup(void) { versioning = create_default_pcep_versioning(); reset_tlv_buffer(); } -void pcep_tlvs_test_teardown() +void pcep_tlvs_test_teardown(void) { destroy_pcep_versioning(versioning); } -void test_pcep_tlv_create_stateful_pce_capability() +void test_pcep_tlv_create_stateful_pce_capability(void) { struct pcep_object_tlv_stateful_pce_capability *tlv = pcep_tlv_create_stateful_pce_capability(true, true, true, true, @@ -96,7 +96,7 @@ void test_pcep_tlv_create_stateful_pce_capability() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_speaker_entity_id() +void test_pcep_tlv_create_speaker_entity_id(void) { struct pcep_object_tlv_speaker_entity_identifier *tlv = pcep_tlv_create_speaker_entity_id(NULL); @@ -105,10 +105,8 @@ void test_pcep_tlv_create_speaker_entity_id() double_linked_list *list = dll_initialize(); tlv = pcep_tlv_create_speaker_entity_id(list); CU_ASSERT_PTR_NULL(tlv); - if (tlv != NULL) { + if (tlv != NULL) pceplib_free(PCEPLIB_INFRA, tlv); - tlv = NULL; - } uint32_t *speaker_entity = pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); @@ -130,7 +128,7 @@ void test_pcep_tlv_create_speaker_entity_id() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_lsp_db_version() +void test_pcep_tlv_create_lsp_db_version(void) { uint64_t lsp_db_version = 0xf005ba11ba5eba11; struct pcep_object_tlv_lsp_db_version *tlv = @@ -148,7 +146,7 @@ void test_pcep_tlv_create_lsp_db_version() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_path_setup_type() +void test_pcep_tlv_create_path_setup_type(void) { uint8_t pst = 0x89; @@ -166,7 +164,7 @@ void test_pcep_tlv_create_path_setup_type() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_path_setup_type_capability() +void test_pcep_tlv_create_path_setup_type_capability(void) { /* The sub_tlv list is optional */ @@ -179,28 +177,22 @@ void test_pcep_tlv_create_path_setup_type_capability() double_linked_list *pst_list = dll_initialize(); tlv = pcep_tlv_create_path_setup_type_capability(pst_list, NULL); CU_ASSERT_PTR_NULL(tlv); - if (tlv != NULL) { + if (tlv != NULL) pcep_obj_free_tlv(&tlv->header); - tlv = NULL; - } /* Should still return NULL if pst_list is NULL */ double_linked_list *sub_tlv_list = dll_initialize(); tlv = pcep_tlv_create_path_setup_type_capability(NULL, sub_tlv_list); CU_ASSERT_PTR_NULL(tlv); - if (tlv != NULL) { + if (tlv != NULL) pcep_obj_free_tlv(&tlv->header); - tlv = NULL; - } /* Should still return NULL if pst_list is empty */ tlv = pcep_tlv_create_path_setup_type_capability(pst_list, sub_tlv_list); CU_ASSERT_PTR_NULL(tlv); - if (tlv != NULL) { + if (tlv != NULL) pcep_obj_free_tlv(&tlv->header); - tlv = NULL; - } /* Test only populating the pst list */ uint8_t *pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1); @@ -274,7 +266,7 @@ void test_pcep_tlv_create_path_setup_type_capability() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_sr_pce_capability() +void test_pcep_tlv_create_sr_pce_capability(void) { struct pcep_object_tlv_sr_pce_capability *tlv = pcep_tlv_create_sr_pce_capability(true, true, 8); @@ -294,7 +286,7 @@ void test_pcep_tlv_create_sr_pce_capability() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_symbolic_path_name() +void test_pcep_tlv_create_symbolic_path_name(void) { /* char *symbolic_path_name, uint16_t symbolic_path_name_length); */ char path_name[16] = "Some Path Name"; @@ -336,7 +328,7 @@ void test_pcep_tlv_create_symbolic_path_name() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_ipv4_lsp_identifiers() +void test_pcep_tlv_create_ipv4_lsp_identifiers(void) { struct in_addr sender_ip, endpoint_ip; uint16_t lsp_id = 7; @@ -397,7 +389,7 @@ void test_pcep_tlv_create_ipv4_lsp_identifiers() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_ipv6_lsp_identifiers() +void test_pcep_tlv_create_ipv6_lsp_identifiers(void) { struct in6_addr sender_ip, endpoint_ip; uint16_t lsp_id = 3; @@ -444,7 +436,8 @@ void test_pcep_tlv_create_ipv6_lsp_identifiers() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_srpag_pol_id_ipv4() + +void test_pcep_tlv_create_srpag_pol_id_ipv4(void) { uint32_t color = 1; struct in_addr src; @@ -474,7 +467,8 @@ void test_pcep_tlv_create_srpag_pol_id_ipv4() pceplib_free(PCEPLIB_MESSAGES, dec_hdr); pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_srpag_pol_id_ipv6() + +void test_pcep_tlv_create_srpag_pol_id_ipv6(void) { uint32_t color = 1; @@ -504,7 +498,7 @@ void test_pcep_tlv_create_srpag_pol_id_ipv6() pceplib_free(PCEPLIB_MESSAGES, dec_hdr); pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_srpag_pol_name() +void test_pcep_tlv_create_srpag_pol_name(void) { const char *pol_name = "Some Pol Name"; @@ -524,7 +518,7 @@ void test_pcep_tlv_create_srpag_pol_name() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_srpag_cp_id() +void test_pcep_tlv_create_srpag_cp_id(void) { // draft-ietf-spring-segment-routing-policy-06.pdf#2.3 // 10 PCEP, 20 BGP SR Policy, 30 Via Configuration @@ -558,7 +552,7 @@ void test_pcep_tlv_create_srpag_cp_id() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_srpag_cp_pref() +void test_pcep_tlv_create_srpag_cp_pref(void) { uint32_t preference_default = 100; @@ -587,7 +581,8 @@ void test_pcep_tlv_create_srpag_cp_pref() pceplib_free(PCEPLIB_MESSAGES, dec_hdr); pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_lsp_error_code() + +void test_pcep_tlv_create_lsp_error_code(void) { struct pcep_object_tlv_lsp_error_code *tlv = pcep_tlv_create_lsp_error_code( @@ -605,7 +600,7 @@ void test_pcep_tlv_create_lsp_error_code() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_rsvp_ipv4_error_spec() +void test_pcep_tlv_create_rsvp_ipv4_error_spec(void) { struct in_addr error_node_ip; inet_pton(AF_INET, "192.168.1.1", &error_node_ip); @@ -629,7 +624,7 @@ void test_pcep_tlv_create_rsvp_ipv4_error_spec() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_rsvp_ipv6_error_spec() +void test_pcep_tlv_create_rsvp_ipv6_error_spec(void) { struct in6_addr error_node_ip; inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &error_node_ip); @@ -653,7 +648,7 @@ void test_pcep_tlv_create_rsvp_ipv6_error_spec() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_nopath_vector() +void test_pcep_tlv_create_nopath_vector(void) { uint32_t enterprise_number = 0x01020304; uint32_t enterprise_specific_info = 0x05060708; @@ -673,7 +668,7 @@ void test_pcep_tlv_create_nopath_vector() pcep_obj_free_tlv(&tlv->header); } -void test_pcep_tlv_create_arbitrary() +void test_pcep_tlv_create_arbitrary(void) { char data[16] = "Some Data"; uint16_t data_length = 9; diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c index 858650e..440cccd 100644 --- a/pceplib/test/pcep_msg_tools_test.c +++ b/pceplib/test/pcep_msg_tools_test.c @@ -199,7 +199,7 @@ static bool pcep_obj_has_tlv(struct pcep_object_header *obj_hdr) return (obj_hdr->tlv_list->num_entries > 0); } -void test_pcep_msg_read_pcep_initiate() +void test_pcep_msg_read_pcep_initiate(void) { char filename[BASE_TMPFILE_SIZE]; @@ -303,7 +303,7 @@ void test_pcep_msg_read_pcep_initiate() } -void test_pcep_msg_read_pcep_initiate2() +void test_pcep_msg_read_pcep_initiate2(void) { char filename[BASE_TMPFILE_SIZE]; @@ -396,7 +396,7 @@ void test_pcep_msg_read_pcep_initiate2() unlink(filename); } -void test_pcep_msg_read_pcep_open() +void test_pcep_msg_read_pcep_open(void) { char filename[BASE_TMPFILE_SIZE]; @@ -444,7 +444,7 @@ void test_pcep_msg_read_pcep_open() unlink(filename); } -void test_pcep_msg_read_pcep_update() +void test_pcep_msg_read_pcep_update(void) { char filename[BASE_TMPFILE_SIZE]; @@ -530,7 +530,7 @@ void test_pcep_msg_read_pcep_update() unlink(filename); } -void test_pcep_msg_read_pcep_open_initiate() +void test_pcep_msg_read_pcep_open_initiate(void) { char filename[BASE_TMPFILE_SIZE]; @@ -563,7 +563,7 @@ void test_pcep_msg_read_pcep_open_initiate() unlink(filename); } -void test_pcep_msg_read_pcep_open_cisco_pce() +void test_pcep_msg_read_pcep_open_cisco_pce(void) { char filename[BASE_TMPFILE_SIZE]; @@ -630,7 +630,7 @@ void test_pcep_msg_read_pcep_open_cisco_pce() unlink(filename); } -void test_pcep_msg_read_pcep_update_cisco_pce() +void test_pcep_msg_read_pcep_update_cisco_pce(void) { char filename[BASE_TMPFILE_SIZE]; @@ -778,7 +778,7 @@ void test_pcep_msg_read_pcep_update_cisco_pce() unlink(filename); } -void test_pcep_msg_read_pcep_report_cisco_pcc() +void test_pcep_msg_read_pcep_report_cisco_pcc(void) { char filename[BASE_TMPFILE_SIZE]; @@ -943,7 +943,7 @@ void test_pcep_msg_read_pcep_report_cisco_pcc() unlink(filename); } -void test_pcep_msg_read_pcep_initiate_cisco_pcc() +void test_pcep_msg_read_pcep_initiate_cisco_pcc(void) { char filename[BASE_TMPFILE_SIZE]; @@ -1055,7 +1055,7 @@ void test_pcep_msg_read_pcep_initiate_cisco_pcc() unlink(filename); } -void test_validate_message_header() +void test_validate_message_header(void) { uint8_t pcep_message_invalid_version[] = {0x40, 0x01, 0x04, 0x00}; uint8_t pcep_message_invalid_flags[] = {0x22, 0x01, 0x04, 0x00}; @@ -1141,7 +1141,7 @@ struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class, return msg; } -void test_validate_message_objects() +void test_validate_message_objects(void) { /* Valid Open message */ struct pcep_message *msg = @@ -1203,7 +1203,7 @@ void test_validate_message_objects() pcep_msg_free_message(msg); } -void test_validate_message_objects_invalid() +void test_validate_message_objects_invalid(void) { /* unsupported message ID = 0 * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ diff --git a/pceplib/test/pcep_pcc_api_test.c b/pceplib/test/pcep_pcc_api_test.c index 904308f..768325a 100644 --- a/pceplib/test/pcep_pcc_api_test.c +++ b/pceplib/test/pcep_pcc_api_test.c @@ -34,13 +34,13 @@ extern const char UNKNOWN_EVENT_STR[]; * Test suite setup and teardown called before AND after the test suite. */ -int pcep_pcc_api_test_suite_setup() +int pcep_pcc_api_test_suite_setup(void) { pceplib_memory_reset(); return 0; } -int pcep_pcc_api_test_suite_teardown() +int pcep_pcc_api_test_suite_teardown(void) { printf("\n"); pceplib_memory_dump(); @@ -51,13 +51,13 @@ int pcep_pcc_api_test_suite_teardown() * Test case setup and teardown called before AND after each test. */ -void pcep_pcc_api_test_setup() +void pcep_pcc_api_test_setup(void) { setup_mock_socket_comm_info(); } -void pcep_pcc_api_test_teardown() +void pcep_pcc_api_test_teardown(void) { teardown_mock_socket_comm_info(); } @@ -66,7 +66,7 @@ void pcep_pcc_api_test_teardown() * Unit test cases */ -void test_initialize_pcc() +void test_initialize_pcc(void) { CU_ASSERT_TRUE(initialize_pcc()); /* Give the PCC time to initialize */ @@ -74,7 +74,7 @@ void test_initialize_pcc() CU_ASSERT_TRUE(destroy_pcc()); } -void test_connect_pce() +void test_connect_pce(void) { pcep_configuration *config = create_default_pcep_configuration(); struct hostent *host_info = gethostbyname("localhost"); @@ -109,7 +109,7 @@ void test_connect_pce() destroy_pcc(); } -void test_connect_pce_ipv6() +void test_connect_pce_ipv6(void) { pcep_configuration *config = create_default_pcep_configuration(); struct in6_addr dest_address; @@ -147,7 +147,7 @@ void test_connect_pce_ipv6() destroy_pcc(); } -void test_connect_pce_with_src_ip() +void test_connect_pce_with_src_ip(void) { pcep_configuration *config = create_default_pcep_configuration(); struct hostent *host_info = gethostbyname("localhost"); @@ -180,7 +180,7 @@ void test_connect_pce_with_src_ip() destroy_pcc(); } -void test_disconnect_pce() +void test_disconnect_pce(void) { pcep_configuration *config = create_default_pcep_configuration(); struct hostent *host_info = gethostbyname("localhost"); @@ -225,7 +225,7 @@ void test_disconnect_pce() } -void test_send_message() +void test_send_message(void) { pcep_configuration *config = create_default_pcep_configuration(); struct hostent *host_info = gethostbyname("localhost"); @@ -249,7 +249,7 @@ void test_send_message() destroy_pcc(); } -void test_event_queue() +void test_event_queue(void) { /* This initializes the event_queue */ CU_ASSERT_TRUE(initialize_pcc()); @@ -278,7 +278,7 @@ void test_event_queue() CU_ASSERT_TRUE(destroy_pcc()); } -void test_get_event_type_str() +void test_get_event_type_str(void) { CU_ASSERT_EQUAL(strcmp(get_event_type_str(MESSAGE_RECEIVED), MESSAGE_RECEIVED_STR), diff --git a/pceplib/test/pcep_utils_counters_test.c b/pceplib/test/pcep_utils_counters_test.c index 2cad9a6..7fa2f3c 100644 --- a/pceplib/test/pcep_utils_counters_test.c +++ b/pceplib/test/pcep_utils_counters_test.c @@ -22,7 +22,7 @@ #include "pcep_utils_counters_test.h" -void test_create_counters_group() +void test_create_counters_group(void) { const char group_name[] = "group"; uint16_t num_subgroups = 10; @@ -45,7 +45,7 @@ void test_create_counters_group() delete_counters_group(group); } -void test_create_counters_subgroup() +void test_create_counters_subgroup(void) { const char subgroup_name[] = "subgroup"; uint16_t subgroup_id = 10; @@ -77,7 +77,7 @@ void test_create_counters_subgroup() delete_counters_subgroup(subgroup); } -void test_add_counters_subgroup() +void test_add_counters_subgroup(void) { struct counters_group *group = create_counters_group("group", 1); struct counters_subgroup *subgroup1 = @@ -102,27 +102,29 @@ void test_add_counters_subgroup() delete_counters_subgroup(subgroup2); } -void test_create_subgroup_counter() +void test_create_subgroup_counter(void) { uint16_t counter_id = 1; char counter_name[] = "my counter"; + char counter_name_json[] = "myCounter"; struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", 1, 2); - CU_ASSERT_FALSE( - create_subgroup_counter(NULL, counter_id, counter_name)); + CU_ASSERT_FALSE(create_subgroup_counter(NULL, counter_id, counter_name, + counter_name_json)); CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id + 1, - counter_name)); - CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id, NULL)); + counter_name, counter_name_json)); + CU_ASSERT_FALSE( + create_subgroup_counter(subgroup, counter_id, NULL, NULL)); CU_ASSERT_EQUAL(subgroup->num_counters, 0); - CU_ASSERT_TRUE( - create_subgroup_counter(subgroup, counter_id, counter_name)); + CU_ASSERT_TRUE(create_subgroup_counter(subgroup, counter_id, + counter_name, counter_name_json)); CU_ASSERT_EQUAL(subgroup->num_counters, 1); delete_counters_subgroup(subgroup); } -void test_delete_counters_group() +void test_delete_counters_group(void) { struct counters_group *group = create_counters_group("group", 1); @@ -130,7 +132,7 @@ void test_delete_counters_group() CU_ASSERT_TRUE(delete_counters_group(group)); } -void test_delete_counters_subgroup() +void test_delete_counters_subgroup(void) { struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", 1, 1); @@ -139,14 +141,14 @@ void test_delete_counters_subgroup() CU_ASSERT_TRUE(delete_counters_subgroup(subgroup)); } -void test_reset_group_counters() +void test_reset_group_counters(void) { uint16_t subgroup_id = 1; uint16_t counter_id = 1; struct counters_group *group = create_counters_group("group", 10); struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", subgroup_id, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); add_counters_subgroup(group, subgroup); struct counter *counter = subgroup->counters[counter_id]; @@ -159,12 +161,12 @@ void test_reset_group_counters() delete_counters_group(group); } -void test_reset_subgroup_counters() +void test_reset_subgroup_counters(void) { uint16_t counter_id = 1; struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", 1, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); struct counter *counter = subgroup->counters[counter_id]; counter->counter_value = 100; @@ -176,14 +178,14 @@ void test_reset_subgroup_counters() delete_counters_subgroup(subgroup); } -void test_increment_counter() +void test_increment_counter(void) { uint16_t subgroup_id = 1; uint16_t counter_id = 1; struct counters_group *group = create_counters_group("group", 10); struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", subgroup_id, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); add_counters_subgroup(group, subgroup); struct counter *counter = subgroup->counters[counter_id]; @@ -199,13 +201,13 @@ void test_increment_counter() delete_counters_group(group); } -void test_increment_subgroup_counter() +void test_increment_subgroup_counter(void) { int counter_id = 1; uint32_t counter_value = 100; struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", 1, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); struct counter *counter = subgroup->counters[counter_id]; counter->counter_value = counter_value; @@ -218,14 +220,14 @@ void test_increment_subgroup_counter() delete_counters_subgroup(subgroup); } -void test_dump_counters_group_to_log() +void test_dump_counters_group_to_log(void) { uint16_t subgroup_id = 1; uint16_t counter_id = 1; struct counters_group *group = create_counters_group("group", 10); struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", subgroup_id, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); add_counters_subgroup(group, subgroup); CU_ASSERT_FALSE(dump_counters_group_to_log(NULL)); @@ -234,13 +236,13 @@ void test_dump_counters_group_to_log() delete_counters_group(group); } -void test_dump_counters_subgroup_to_log() +void test_dump_counters_subgroup_to_log(void) { uint16_t subgroup_id = 1; uint16_t counter_id = 1; struct counters_subgroup *subgroup = create_counters_subgroup("subgroup", subgroup_id, 10); - create_subgroup_counter(subgroup, counter_id, "counter"); + create_subgroup_counter(subgroup, counter_id, "counter", "counter"); CU_ASSERT_FALSE(dump_counters_subgroup_to_log(NULL)); CU_ASSERT_TRUE(dump_counters_subgroup_to_log(subgroup)); diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 262ce86..4db1157 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -77,7 +77,7 @@ DEFPY (ipv6_pim_spt_switchover_infinity, DEFPY (ipv6_pim_spt_switchover_infinity_plist, ipv6_pim_spt_switchover_infinity_plist_cmd, - "ipv6 pim spt-switchover infinity-and-beyond prefix-list WORD$plist", + "ipv6 pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST6_NAME$plist", IPV6_STR PIM_STR "SPT-Switchover\n" @@ -102,7 +102,7 @@ DEFPY (no_ipv6_pim_spt_switchover_infinity, DEFPY (no_ipv6_pim_spt_switchover_infinity_plist, no_ipv6_pim_spt_switchover_infinity_plist_cmd, - "no ipv6 pim spt-switchover infinity-and-beyond prefix-list WORD", + "no ipv6 pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST6_NAME", NO_STR IPV6_STR PIM_STR @@ -436,7 +436,7 @@ DEFPY (no_ipv6_pim_rp, DEFPY (ipv6_pim_rp_prefix_list, ipv6_pim_rp_prefix_list_cmd, - "ipv6 pim rp X:X::X:X$rp prefix-list WORD$plist", + "ipv6 pim rp X:X::X:X$rp prefix-list PREFIXLIST6_NAME$plist", IPV6_STR PIM_STR "Rendezvous Point\n" @@ -449,7 +449,7 @@ DEFPY (ipv6_pim_rp_prefix_list, DEFPY (no_ipv6_pim_rp_prefix_list, no_ipv6_pim_rp_prefix_list_cmd, - "no ipv6 pim rp X:X::X:X$rp prefix-list WORD$plist", + "no ipv6 pim rp X:X::X:X$rp prefix-list PREFIXLIST6_NAME$plist", NO_STR IPV6_STR PIM_STR diff --git a/pimd/pim6_main.c b/pimd/pim6_main.c index 1af4a17..5ce6985 100644 --- a/pimd/pim6_main.c +++ b/pimd/pim6_main.c @@ -26,6 +26,7 @@ #include "pim_nb.h" #include "pim6_cmd.h" #include "pim6_mld.h" +#include "pim_zlookup.h" zebra_capabilities_t _caps_p[] = { ZCAP_SYS_ADMIN, @@ -189,11 +190,20 @@ int main(int argc, char **argv, char **envp) static void pim6_terminate(void) { + struct zclient *zclient; + pim_vrf_terminate(); pim_router_terminate(); prefix_list_reset(); access_list_reset(); + zclient = pim_zebra_zclient_get(); + if (zclient) { + zclient_stop(zclient); + zclient_free(zclient); + } + + zclient_lookup_free(); frr_fini(); } diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index 20ef921..a39d182 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -13,6 +13,7 @@ */ #include +#include #include #include "lib/memory.h" diff --git a/pimd/pim_addr.h b/pimd/pim_addr.h index 94c63bb..ecba739 100644 --- a/pimd/pim_addr.h +++ b/pimd/pim_addr.h @@ -33,13 +33,13 @@ typedef struct in_addr pim_addr; #define PIM_ADDR_FUNCNAME(name) ipv4_##name union pimprefixptr { - prefixtype(pimprefixptr, struct prefix, p) - prefixtype(pimprefixptr, struct prefix_ipv4, p4) + uniontype(pimprefixptr, struct prefix, p) + uniontype(pimprefixptr, struct prefix_ipv4, p4) } TRANSPARENT_UNION; union pimprefixconstptr { - prefixtype(pimprefixconstptr, const struct prefix, p) - prefixtype(pimprefixconstptr, const struct prefix_ipv4, p4) + uniontype(pimprefixconstptr, const struct prefix, p) + uniontype(pimprefixconstptr, const struct prefix_ipv4, p4) } TRANSPARENT_UNION; #else @@ -63,13 +63,13 @@ typedef struct in6_addr pim_addr; #define PIM_ADDR_FUNCNAME(name) ipv6_##name union pimprefixptr { - prefixtype(pimprefixptr, struct prefix, p) - prefixtype(pimprefixptr, struct prefix_ipv6, p6) + uniontype(pimprefixptr, struct prefix, p) + uniontype(pimprefixptr, struct prefix_ipv6, p6) } TRANSPARENT_UNION; union pimprefixconstptr { - prefixtype(pimprefixconstptr, const struct prefix, p) - prefixtype(pimprefixconstptr, const struct prefix_ipv6, p6) + uniontype(pimprefixconstptr, const struct prefix, p) + uniontype(pimprefixconstptr, const struct prefix_ipv6, p6) } TRANSPARENT_UNION; #endif diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 2e90cf9..be36a07 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -78,7 +78,7 @@ static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], if (!vrf) { if (uj) - vty_json_empty(vty); + vty_json_empty(vty, NULL); else vty_out(vty, "Specified VRF: %s does not exist\n", argv[*idx]->arg); @@ -2999,7 +2999,7 @@ DEFUN (ip_pim_spt_switchover_infinity, DEFPY (ip_pim_spt_switchover_infinity_plist, ip_pim_spt_switchover_infinity_plist_cmd, - "ip pim spt-switchover infinity-and-beyond prefix-list WORD$plist", + "ip pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME$plist", IP_STR PIM_STR "SPT-Switchover\n" @@ -3024,7 +3024,7 @@ DEFUN (no_ip_pim_spt_switchover_infinity, DEFUN (no_ip_pim_spt_switchover_infinity_plist, no_ip_pim_spt_switchover_infinity_plist_cmd, - "no ip pim spt-switchover infinity-and-beyond prefix-list WORD", + "no ip pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME", NO_STR IP_STR PIM_STR @@ -3038,7 +3038,7 @@ DEFUN (no_ip_pim_spt_switchover_infinity_plist, DEFPY (pim_register_accept_list, pim_register_accept_list_cmd, - "[no] ip pim register-accept-list WORD$word", + "[no] ip pim register-accept-list PREFIXLIST4_NAME$word", NO_STR IP_STR PIM_STR @@ -3283,7 +3283,7 @@ DEFPY (ip_pim_rp, DEFPY (ip_pim_rp_prefix_list, ip_pim_rp_prefix_list_cmd, - "ip pim rp A.B.C.D$rp prefix-list WORD$plist", + "ip pim rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist", IP_STR "pim multicast routing\n" "Rendezvous Point\n" @@ -3311,7 +3311,7 @@ DEFPY (no_ip_pim_rp, DEFPY (no_ip_pim_rp_prefix_list, no_ip_pim_rp_prefix_list_cmd, - "no ip pim rp A.B.C.D$rp prefix-list WORD$plist", + "no ip pim rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist", NO_STR IP_STR "pim multicast routing\n" @@ -3325,7 +3325,7 @@ DEFPY (no_ip_pim_rp_prefix_list, DEFUN (ip_pim_ssm_prefix_list, ip_pim_ssm_prefix_list_cmd, - "ip pim ssm prefix-list WORD", + "ip pim ssm prefix-list PREFIXLIST4_NAME", IP_STR "pim multicast routing\n" "Source Specific Multicast\n" @@ -3376,7 +3376,7 @@ DEFUN (no_ip_pim_ssm_prefix_list, DEFUN (no_ip_pim_ssm_prefix_list_name, no_ip_pim_ssm_prefix_list_name_cmd, - "no ip pim ssm prefix-list WORD", + "no ip pim ssm prefix-list PREFIXLIST4_NAME", NO_STR IP_STR "pim multicast routing\n" diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index c3eb49d..ee318d4 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -6,6 +6,7 @@ */ #include +#include #include "lib/json.h" #include "command.h" @@ -68,7 +69,7 @@ const char *pim_cli_get_vrf_name(struct vty *vty) return NULL; } - return yang_dnode_get_string(vrf_node, "./name"); + return yang_dnode_get_string(vrf_node, "name"); } int pim_process_join_prune_cmd(struct vty *vty, const char *jpi_str) @@ -524,7 +525,9 @@ int pim_process_rp_cmd(struct vty *vty, const char *rp_str, const char *group_str) { const char *vrfname; - char rp_group_xpath[XPATH_MAXLEN]; + char group_xpath[XPATH_MAXLEN]; + char rp_xpath[XPATH_MAXLEN]; + int printed; int result = 0; struct prefix group; pim_addr rp_addr; @@ -569,12 +572,18 @@ int pim_process_rp_cmd(struct vty *vty, const char *rp_str, if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(rp_group_xpath, sizeof(rp_group_xpath), - FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname, - FRR_PIM_AF_XPATH_VAL, rp_str); - strlcat(rp_group_xpath, "/group-list", sizeof(rp_group_xpath)); + snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, + "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str); + printed = snprintf(group_xpath, sizeof(group_xpath), + "%s/group-list[.='%s']", rp_xpath, group_str); + + if (printed >= (int)(sizeof(group_xpath))) { + vty_out(vty, "Xpath too long (%d > %u)", printed + 1, + XPATH_MAXLEN); + return CMD_WARNING_CONFIG_FAILED; + } - nb_cli_enqueue_change(vty, rp_group_xpath, NB_OP_CREATE, group_str); + nb_cli_enqueue_change(vty, group_xpath, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -582,7 +591,6 @@ int pim_process_rp_cmd(struct vty *vty, const char *rp_str, int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str, const char *group_str) { - char group_list_xpath[XPATH_MAXLEN]; char group_xpath[XPATH_MAXLEN]; char rp_xpath[XPATH_MAXLEN]; int printed; @@ -595,18 +603,8 @@ int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str, snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str); - - printed = snprintf(group_list_xpath, sizeof(group_list_xpath), - "%s/group-list", rp_xpath); - - if (printed >= (int)(sizeof(group_list_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - printed = snprintf(group_xpath, sizeof(group_xpath), "%s[.='%s']", - group_list_xpath, group_str); + printed = snprintf(group_xpath, sizeof(group_xpath), + "%s/group-list[.='%s']", rp_xpath, group_str); if (printed >= (int)(sizeof(group_xpath))) { vty_out(vty, "Xpath too long (%d > %u)", printed + 1, @@ -623,8 +621,7 @@ int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str, if (yang_is_last_list_dnode(group_dnode)) nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL); else - nb_cli_enqueue_change(vty, group_list_xpath, NB_OP_DESTROY, - group_str); + nb_cli_enqueue_change(vty, group_xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -1059,8 +1056,8 @@ void pim_show_state(struct pim_instance *pim, struct vty *vty, frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { char src_str[PIM_ADDRSTRLEN]; char grp_str[PIM_ADDRSTRLEN]; - char in_ifname[INTERFACE_NAMSIZ + 1]; - char out_ifname[INTERFACE_NAMSIZ + 1]; + char in_ifname[IFNAMSIZ + 1]; + char out_ifname[IFNAMSIZ + 1]; int oif_vif_index; struct interface *ifp_in; bool isRpt; @@ -3406,6 +3403,8 @@ int pim_process_ssmpingd_cmd(struct vty *vty, enum nb_operation operation, { const char *vrfname; char ssmpingd_ip_xpath[XPATH_MAXLEN]; + char ssmpingd_src_ip_xpath[XPATH_MAXLEN]; + int printed; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) @@ -3414,10 +3413,16 @@ int pim_process_ssmpingd_cmd(struct vty *vty, enum nb_operation operation, snprintf(ssmpingd_ip_xpath, sizeof(ssmpingd_ip_xpath), FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL); - strlcat(ssmpingd_ip_xpath, "/ssm-pingd-source-ip", - sizeof(ssmpingd_ip_xpath)); + printed = snprintf(ssmpingd_src_ip_xpath, sizeof(ssmpingd_src_ip_xpath), + "%s/ssm-pingd-source-ip[.='%s']", ssmpingd_ip_xpath, + src_str); + if (printed >= (int)sizeof(ssmpingd_src_ip_xpath)) { + vty_out(vty, "Xpath too long (%d > %u)", printed + 1, + XPATH_MAXLEN); + return CMD_WARNING_CONFIG_FAILED; + } - nb_cli_enqueue_change(vty, ssmpingd_ip_xpath, operation, src_str); + nb_cli_enqueue_change(vty, ssmpingd_src_ip_xpath, operation, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -3662,8 +3667,8 @@ void show_mroute(struct pim_instance *pim, struct vty *vty, pim_sgaddr *sg, int first; char grp_str[PIM_ADDRSTRLEN]; char src_str[PIM_ADDRSTRLEN]; - char in_ifname[INTERFACE_NAMSIZ + 1]; - char out_ifname[INTERFACE_NAMSIZ + 1]; + char in_ifname[IFNAMSIZ + 1]; + char out_ifname[IFNAMSIZ + 1]; int oif_vif_index; struct interface *ifp_in; char proto[100]; diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 978607d..a0661ef 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -440,7 +440,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, } /* Secondary Address List */ - if (ifp->connected->count) { + if (if_connected_count(ifp->connected)) { curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp, PIM_AF); if (!curr) { diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 5fa4715..5d7132c 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -379,7 +379,7 @@ static int pim_sec_addr_update(struct interface *ifp) sec_addr->flags |= PIM_SEC_ADDRF_STALE; } - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { pim_addr addr = pim_addr_from_prefix(ifc->address); if (pim_addr_is_any(addr)) @@ -723,13 +723,12 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) if (pim_ifp && (!IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_lowest) || !IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_highest))) { - struct listnode *cnode; struct connected *cc; memset(&pim_ifp->ll_lowest, 0xff, sizeof(pim_ifp->ll_lowest)); memset(&pim_ifp->ll_highest, 0, sizeof(pim_ifp->ll_highest)); - for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected, cnode, cc)) { + frr_each (if_connected, ifc->ifp->connected, cc) { if (!IN6_IS_ADDR_LINKLOCAL(&cc->address->u.prefix6) && !IN6_IS_ADDR_LOOPBACK(&cc->address->u.prefix6)) continue; @@ -765,8 +764,6 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) void pim_if_addr_add_all(struct interface *ifp) { struct connected *ifc; - struct listnode *node; - struct listnode *nextnode; int v4_addrs = 0; int v6_addrs = 0; struct pim_interface *pim_ifp = ifp->info; @@ -777,7 +774,7 @@ void pim_if_addr_add_all(struct interface *ifp) if (!pim_ifp) return; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; if (p->family != AF_INET) @@ -813,8 +810,6 @@ void pim_if_addr_add_all(struct interface *ifp) void pim_if_addr_del_all(struct interface *ifp) { struct connected *ifc; - struct listnode *node; - struct listnode *nextnode; struct pim_instance *pim; pim = ifp->vrf->info; @@ -825,7 +820,7 @@ void pim_if_addr_del_all(struct interface *ifp) if (!ifp->info) return; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + frr_each_safe (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; if (p->family != PIM_AF) @@ -841,14 +836,12 @@ void pim_if_addr_del_all(struct interface *ifp) void pim_if_addr_del_all_igmp(struct interface *ifp) { struct connected *ifc; - struct listnode *node; - struct listnode *nextnode; /* PIM/IGMP enabled ? */ if (!ifp->info) return; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + frr_each_safe (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; if (p->family != AF_INET) @@ -861,7 +854,6 @@ void pim_if_addr_del_all_igmp(struct interface *ifp) pim_addr pim_find_primary_addr(struct interface *ifp) { struct connected *ifc; - struct listnode *node; struct pim_interface *pim_ifp = ifp->info; if (pim_ifp && !pim_addr_is_any(pim_ifp->update_source)) @@ -873,7 +865,7 @@ pim_addr pim_find_primary_addr(struct interface *ifp) pim_addr best_addr = PIMADDR_ANY; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { pim_addr addr; if (ifc->address->family != AF_INET6) @@ -892,7 +884,7 @@ pim_addr pim_find_primary_addr(struct interface *ifp) int v6_addrs = 0; struct connected *promote_ifc = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { switch (ifc->address->family) { case AF_INET: v4_addrs++; @@ -1487,7 +1479,7 @@ void pim_if_update_assert_tracking_desired(struct interface *ifp) */ void pim_if_create_pimreg(struct pim_instance *pim) { - char pimreg_name[INTERFACE_NAMSIZ]; + char pimreg_name[IFNAMSIZ]; if (!pim->regiface) { if (pim->vrf->vrf_id == VRF_DEFAULT) @@ -1523,7 +1515,6 @@ void pim_if_create_pimreg(struct pim_instance *pim) struct prefix *pim_if_connected_to_source(struct interface *ifp, pim_addr src) { - struct listnode *cnode; struct connected *c; struct prefix p; @@ -1532,7 +1523,7 @@ struct prefix *pim_if_connected_to_source(struct interface *ifp, pim_addr src) pim_addr_to_prefix(&p, src); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (c->address->family != PIM_AF) continue; if (prefix_match(c->address, &p)) @@ -1771,8 +1762,10 @@ void pim_iface_init(void) hook_register_prio(if_add, 0, pim_if_new_hook); hook_register_prio(if_del, 0, pim_if_delete_hook); - if_zapi_callbacks(pim_ifp_create, pim_ifp_up, pim_ifp_down, - pim_ifp_destroy); + hook_register_prio(if_real, 0, pim_ifp_create); + hook_register_prio(if_up, 0, pim_ifp_up); + hook_register_prio(if_down, 0, pim_ifp_down); + hook_register_prio(if_unreal, 0, pim_ifp_destroy); } static void pim_if_membership_clear(struct interface *ifp) diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 4d3f602..309da13 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -21,7 +21,6 @@ static struct in_addr mtrace_primary_address(struct interface *ifp) { struct connected *ifc; - struct listnode *node; struct in_addr any; struct pim_interface *pim_ifp; @@ -32,7 +31,7 @@ static struct in_addr mtrace_primary_address(struct interface *ifp) any.s_addr = INADDR_ANY; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; if (p->family != AF_INET) diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 11577ae..ec33133 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -97,7 +97,7 @@ struct pim_router { struct in_addr local_vtep_ip; struct pim_mlag_stats mlag_stats; enum pim_mlag_flags mlag_flags; - char peerlink_rif[INTERFACE_NAMSIZ]; + char peerlink_rif[IFNAMSIZ]; struct interface *peerlink_rif_p; }; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 671f7a3..bfdb0f0 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -43,7 +43,7 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh, { struct pim_interface *pim_ifp = NULL; - if (PIM_DEBUG_PIM_TRACE) + if (PIM_DEBUG_PIM_J_P) zlog_debug( "%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s", __func__, sg, !!(source_flags & PIM_RPT_BIT_MASK), @@ -115,7 +115,7 @@ static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh, { struct pim_interface *pim_ifp = NULL; - if (PIM_DEBUG_PIM_TRACE) + if (PIM_DEBUG_PIM_J_P) zlog_debug( "%s: prune (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s", __func__, sg, source_flags & PIM_RPT_BIT_MASK, @@ -147,7 +147,7 @@ static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh, * Received Prune(*,G) messages are processed even if the * RP in the message does not match RP(G). */ - if (PIM_DEBUG_PIM_TRACE) + if (PIM_DEBUG_PIM_J_P) zlog_debug("%s: Prune received with RP(%pPAs) for %pSG", __func__, &sg->src, sg); diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 7db0a76..400db39 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -70,17 +70,20 @@ static const struct frr_yang_module_info *const pimd_yang_modules[] = { &frr_gmp_info, }; -FRR_DAEMON_INFO(pimd, PIM, .vty_port = PIMD_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(pimd, PIM, + .vty_port = PIMD_VTY_PORT, + .proghelp = "Implementation of the PIM routing protocol.", - .proghelp = "Implementation of the PIM routing protocol.", + .signals = pimd_signals, + .n_signals = 4 /* XXX array_size(pimd_signals) XXX*/, - .signals = pimd_signals, - .n_signals = 4 /* XXX array_size(pimd_signals) XXX*/, + .privs = &pimd_privs, - .privs = &pimd_privs, .yang_modules = pimd_yang_modules, - .n_yang_modules = array_size(pimd_yang_modules), + .yang_modules = pimd_yang_modules, + .n_yang_modules = array_size(pimd_yang_modules), ); - +/* clang-format on */ int main(int argc, char **argv, char **envp) { diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c index 5d72eb6..dcef2d0 100644 --- a/pimd/pim_mlag.c +++ b/pimd/pim_mlag.c @@ -434,7 +434,7 @@ static void pim_mlag_up_local_add_send(struct pim_instance *pim, stream_putc(s, !(PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags))); stream_putl(s, vrf->vrf_id); /* XXX - this field is a No-op for VXLAN*/ - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); stream_fifo_push_safe(router->mlag_fifo, s); pim_mlag_signal_zpthread(); @@ -467,7 +467,7 @@ static void pim_mlag_up_local_del_send(struct pim_instance *pim, stream_putl(s, MLAG_OWNER_VXLAN); stream_putl(s, vrf->vrf_id); /* XXX - this field is a No-op for VXLAN */ - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); /* XXX - is this the the most optimal way to do things */ stream_fifo_push_safe(router->mlag_fifo, s); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 7ea6ed9..c63e0f3 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -5,6 +5,9 @@ */ #include +#include +#include + #include "log.h" #include "privs.h" #include "if.h" @@ -1216,7 +1219,7 @@ int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) return pim_upstream_mroute_update(c_oil, name); } -/* Look for IIF changes and update the dateplane entry only if the IIF +/* Look for IIF changes and update the dataplane entry only if the IIF * has changed. */ int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index 8706f42..fd4913c 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -25,8 +25,19 @@ #include #if defined(HAVE_LINUX_MROUTE_H) #include -#else -#include "linux/mroute.h" +#endif + +#if defined(HAVE_NETINET_IP_MROUTE_H) +#include +/* + * MRT_TABLE of 155 is needed because it is not defined + * on FreeBSD. MRT_TABLE is for vrf's. There is no + * equivalent on BSD at this point in time. Let's + * just get it compiling + */ +#ifndef MRT_TABLE +#define MRT_TABLE 155 +#endif #endif typedef struct vifctl pim_vifctl; @@ -70,8 +81,17 @@ typedef struct sioc_sg_req pim_sioc_sg_req; #if defined(HAVE_LINUX_MROUTE6_H) #include -#else -#include "linux/mroute6.h" +#endif +#if defined(HAVE_NETINET_IP6_MROUTE_H) +#include +#include + +/* + * See the v4 discussion above + */ +#ifndef MRT_TABLE +#define MRT_TABLE 155 +#endif #endif #ifndef MRT_INIT diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 6814798..7a86e6b 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -185,84 +185,78 @@ size_t pim_msg_get_jp_group_size(struct list *sources) size += sizeof(pim_encoded_source) * sources->count; js = listgetdata(listhead(sources)); - if (js && pim_addr_is_any(js->up->sg.src) && js->is_join) { - struct pim_upstream *child, *up; - struct listnode *up_node; - - up = js->up; - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: Considering (%s) children for (S,G,rpt) prune", - __func__, up->sg_str); - - for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { - /* - * PIM VXLAN is weird - * It auto creates the S,G and populates a bunch - * of flags that make it look like a SPT prune should - * be sent. But this regularly scheduled join - * for the *,G in the VXLAN setup can happen at - * scheduled times *before* the null register - * is received by the RP to cause it to initiate - * the S,G joins toward the source. Let's just - * assume that if this is a SRC VXLAN ORIG route - * and no actual ifchannels( joins ) have been - * created then do not send the embedded prune - * Why you may ask? Well if the prune is S,G - * RPT Prune is received *before* the join - * from the RP( if it flows to this routers - * upstream interface ) then we'll just wisely - * create a mroute with an empty oil on - * the upstream intermediate router preventing - * packets from flowing to the RP + if (!js || !pim_addr_is_any(js->up->sg.src) || !js->is_join) + return size; + + struct pim_upstream *child, *up; + struct listnode *up_node; + + up = js->up; + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s: Considering (%s) children for (S,G,rpt) prune", + __func__, up->sg_str); + + for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { + /* + * PIM VXLAN is weird + * It auto creates the S,G and populates a bunch + * of flags that make it look like a SPT prune should + * be sent. But this regularly scheduled join + * for the *,G in the VXLAN setup can happen at + * scheduled times *before* the null register + * is received by the RP to cause it to initiate + * the S,G joins toward the source. Let's just + * assume that if this is a SRC VXLAN ORIG route + * and no actual ifchannels( joins ) have been + * created then do not send the embedded prune + * Why you may ask? Well if the prune is S,G + * RPT Prune is received *before* the join + * from the RP( if it flows to this routers + * upstream interface ) then we'll just wisely + * create a mroute with an empty oil on + * the upstream intermediate router preventing + * packets from flowing to the RP + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(child->flags) && + listcount(child->ifchannels) == 0) { + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s: %s Vxlan originated S,G route with no ifchannels, not adding prune to compound message", + __func__, child->sg_str); + } else if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) { + /* If we are using SPT and the SPT and RPT IIFs + * are different we can prune the source off + * of the RPT. + * If RPF_interface(S) is not resolved hold + * decision to prune as SPT may end up on the + * same IIF as RPF_interface(RP). */ - if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(child->flags) && - listcount(child->ifchannels) == 0) { - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug("%s: %s Vxlan originated S,G route with no ifchannels, not adding prune to compound message", - __func__, child->sg_str); - } else if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) { - /* If we are using SPT and the SPT and RPT IIFs - * are different we can prune the source off - * of the RPT. - * If RPF_interface(S) is not resolved hold - * decision to prune as SPT may end up on the - * same IIF as RPF_interface(RP). - */ - if (child->rpf.source_nexthop.interface && - !pim_rpf_is_same(&up->rpf, - &child->rpf)) { - size += sizeof(pim_encoded_source); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( - child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message", - __func__, up->sg_str, - child->sg_str); - } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", - __func__, up->sg_str, - child->sg_str); - } else if (pim_upstream_empty_inherited_olist(child)) { - /* S is supposed to be forwarded along the RPT - * but it's inherited OIL is empty. So just - * prune it off. - */ + if (child->rpf.source_nexthop.interface && + !pim_rpf_is_same(&up->rpf, &child->rpf)) { size += sizeof(pim_encoded_source); PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( - child->flags); + child->flags); if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", - __func__, child->sg_str); + zlog_debug("%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message", + __func__, up->sg_str, + child->sg_str); } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: Do not add Prune %s to compound message %s", - __func__, child->sg_str, up->sg_str); - } + zlog_debug("%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", + __func__, up->sg_str, child->sg_str); + } else if (pim_upstream_empty_inherited_olist(child)) { + /* S is supposed to be forwarded along the RPT + * but it's inherited OIL is empty. So just + * prune it off. + */ + size += sizeof(pim_encoded_source); + PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(child->flags); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", + __func__, child->sg_str); + } else if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s: Do not add Prune %s to compound message %s", + __func__, child->sg_str, up->sg_str); } + return size; } diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index be05b69..4f1a4a1 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -485,7 +485,7 @@ int routing_control_plane_protocols_name_validate( { const char *name; - name = yang_dnode_get_string(args->dnode, "./name"); + name = yang_dnode_get_string(args->dnode, "name"); if (!strmatch(name, "pim")) { snprintf(args->errmsg, args->errmsg_len, "pim supports only one instance with name pimd"); @@ -779,7 +779,7 @@ void routing_control_plane_protocols_control_plane_protocol_pim_address_family_s vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - spt_switch_action = yang_dnode_get_enum(args->dnode, "./spt-action"); + spt_switch_action = yang_dnode_get_enum(args->dnode, "spt-action"); switch (spt_switch_action) { case PIM_SPT_INFINITY: @@ -922,8 +922,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_pimaddr(&source_addr, args->dnode, - "./source-addr"); + yang_dnode_get_pimaddr(&source_addr, args->dnode, NULL); result = pim_ssmpingd_start(pim, source_addr); if (result) { snprintf( @@ -953,8 +952,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_pimaddr(&source_addr, args->dnode, - "./source-addr"); + yang_dnode_get_pimaddr(&source_addr, args->dnode, NULL); result = pim_ssmpingd_stop(pim, source_addr); if (result) { snprintf( @@ -1233,8 +1231,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_ip(&peer_ip, args->dnode, "./peer-ip"); - yang_dnode_get_ip(&source_ip, args->dnode, "./source-ip"); + yang_dnode_get_ip(&peer_ip, args->dnode, "peer-ip"); + yang_dnode_get_ip(&source_ip, args->dnode, "source-ip"); mp = pim_msdp_peer_add(pim, &peer_ip.ipaddr_v4, &source_ip.ipaddr_v4, NULL); nb_running_set_entry(args->dnode, mp); @@ -1338,16 +1336,16 @@ void routing_control_plane_protocols_control_plane_protocol_pim_address_family_m struct interface *ifp; struct ipaddr reg_addr; - ifname = yang_dnode_get_string(args->dnode, "./peerlink-rif"); + ifname = yang_dnode_get_string(args->dnode, "peerlink-rif"); ifp = if_lookup_by_name(ifname, VRF_DEFAULT); if (!ifp) { snprintf(args->errmsg, args->errmsg_len, "No such interface name %s", ifname); return; } - role = yang_dnode_get_enum(args->dnode, "./my-role"); - peer_state = yang_dnode_get_bool(args->dnode, "./peer-state"); - yang_dnode_get_ip(®_addr, args->dnode, "./reg-address"); + role = yang_dnode_get_enum(args->dnode, "my-role"); + peer_state = yang_dnode_get_bool(args->dnode, "peer-state"); + yang_dnode_get_ip(®_addr, args->dnode, "reg-address"); pim_vxlan_mlag_update(true, peer_state, role, ifp, ®_addr.ip._v4_addr); @@ -1759,11 +1757,11 @@ void lib_interface_pim_address_family_bfd_apply_finish( } pim_ifp->bfd_config.detection_multiplier = - yang_dnode_get_uint8(args->dnode, "./detect_mult"); + yang_dnode_get_uint8(args->dnode, "detect_mult"); pim_ifp->bfd_config.min_rx = - yang_dnode_get_uint16(args->dnode, "./min-rx-interval"); + yang_dnode_get_uint16(args->dnode, "min-rx-interval"); pim_ifp->bfd_config.min_tx = - yang_dnode_get_uint16(args->dnode, "./min-tx-interval"); + yang_dnode_get_uint16(args->dnode, "min-tx-interval"); pim_bfd_reg_dereg_all_nbr(ifp); } @@ -2191,7 +2189,7 @@ int lib_interface_pim_address_family_mroute_destroy( pim_iifp = iif->info; pim = pim_iifp->pim; - oifname = yang_dnode_get_string(args->dnode, "./oif"); + oifname = yang_dnode_get_string(args->dnode, "oif"); oif = if_lookup_by_name(oifname, pim->vrf->vrf_id); if (!oif) { @@ -2201,8 +2199,8 @@ int lib_interface_pim_address_family_mroute_destroy( return NB_ERR_INCONSISTENCY; } - yang_dnode_get_pimaddr(&source_addr, args->dnode, "./source-addr"); - yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr"); + yang_dnode_get_pimaddr(&source_addr, args->dnode, "source-addr"); + yang_dnode_get_pimaddr(&group_addr, args->dnode, "group-addr"); if (pim_static_del(pim, iif, oif, group_addr, source_addr)) { snprintf(args->errmsg, args->errmsg_len, @@ -2341,9 +2339,9 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_pimaddr(&rp_addr, args->dnode, "./rp-address"); + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "rp-address"); - if (yang_dnode_get(args->dnode, "./group-list")) { + if (yang_dnode_get(args->dnode, "group-list")) { yang_dnode_get_prefix(&group, args->dnode, "./group-list"); apply_mask(&group); @@ -2352,7 +2350,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp args->errmsg_len); } - else if (yang_dnode_get(args->dnode, "./prefix-list")) { + else if (yang_dnode_get(args->dnode, "prefix-list")) { plist = yang_dnode_get_string(args->dnode, "./prefix-list"); if (!pim_get_all_mcast_group(&group)) { diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 4e8e5f0..32cdf4b 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -338,7 +338,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, if (nh->ifindex == IFINDEX_INTERNAL) continue; - /* fallthru */ + fallthrough; case NEXTHOP_TYPE_IPV4_IFINDEX: nhaddr = nh->gate.ipv4; break; @@ -350,7 +350,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, if (nh->ifindex == IFINDEX_INTERNAL) continue; - /* fallthru */ + fallthrough; case NEXTHOP_TYPE_IPV6_IFINDEX: nhaddr = nh->gate.ipv6; break; @@ -723,7 +723,8 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, /* This API is used to parse Registered address nexthop update coming from Zebra */ -int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) +void pim_nexthop_update(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr) { struct nexthop *nexthop; struct nexthop *nhlist_head = NULL; @@ -732,38 +733,27 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) struct pim_rpf rpf; struct pim_nexthop_cache *pnc = NULL; struct interface *ifp = NULL; - struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct pim_instance *pim; - struct zapi_route nhr; - struct prefix match; - if (!vrf) - return 0; pim = vrf->info; - if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) { - zlog_err("%s: Decode of nexthop update from zebra failed", - __func__); - return 0; - } - - rpf.rpf_addr = pim_addr_from_prefix(&match); + rpf.rpf_addr = pim_addr_from_prefix(match); pnc = pim_nexthop_cache_find(pim, &rpf); if (!pnc) { if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: Skipping NHT update, addr %pPA is not in local cached DB.", __func__, &rpf.rpf_addr); - return 0; + return; } pnc->last_update = pim_time_monotonic_usec(); - if (nhr.nexthop_num) { + if (nhr->nexthop_num) { pnc->nexthop_num = 0; - for (i = 0; i < nhr.nexthop_num; i++) { - nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]); + for (i = 0; i < nhr->nexthop_num; i++) { + nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]); switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* @@ -842,11 +832,11 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) #else pim_addr nhaddr = nexthop->gate.ipv6; #endif - zlog_debug( - "%s: NHT addr %pFX(%s) %d-nhop via %pPA(%s) type %d distance:%u metric:%u ", - __func__, &match, pim->vrf->name, i + 1, - &nhaddr, ifp->name, nexthop->type, - nhr.distance, nhr.metric); + zlog_debug("%s: NHT addr %pFX(%s) %d-nhop via %pPA(%s) type %d distance:%u metric:%u ", + __func__, match, pim->vrf->name, + i + 1, &nhaddr, ifp->name, + nexthop->type, nhr->distance, + nhr->metric); } if (!ifp->info) { @@ -887,23 +877,22 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) pnc->nexthop = nhlist_head; if (pnc->nexthop_num) { pnc->flags |= PIM_NEXTHOP_VALID; - pnc->distance = nhr.distance; - pnc->metric = nhr.metric; + pnc->distance = nhr->distance; + pnc->metric = nhr->metric; } } else { pnc->flags &= ~PIM_NEXTHOP_VALID; - pnc->nexthop_num = nhr.nexthop_num; + pnc->nexthop_num = nhr->nexthop_num; nexthops_free(pnc->nexthop); pnc->nexthop = NULL; } SET_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED); if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d", - __func__, &match, pim->vrf->name, nhr.nexthop_num, - pnc->nexthop_num, vrf_id, pnc->upstream_hash->count, - listcount(pnc->rp_list)); + zlog_debug("%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d", + __func__, match, pim->vrf->name, nhr->nexthop_num, + pnc->nexthop_num, vrf->vrf_id, + pnc->upstream_hash->count, listcount(pnc->rp_list)); pim_rpf_set_refresh_time(pim); @@ -911,8 +900,6 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) pim_update_rp_nh(pim, pnc); if (pnc->upstream_hash->count) pim_update_upstream_nh(pim, pnc); - - return 0; } int pim_ecmp_nexthop_lookup(struct pim_instance *pim, diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h index 5a54e1c..a1feb76 100644 --- a/pimd/pim_nht.h +++ b/pimd/pim_nht.h @@ -45,7 +45,8 @@ struct pnc_hash_walk_data { struct interface *ifp; }; -int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS); +void pim_nexthop_update(struct vrf *vrf, struct prefix *match, + struct zapi_route *nhr); int pim_find_or_track_nexthop(struct pim_instance *pim, pim_addr addr, struct pim_upstream *up, struct rp_info *rp, struct pim_nexthop_cache *out_pnc); diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index a4c9178..1bc265b 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -743,14 +743,13 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) pim_ifp = ifp->info; if (PIM_DEBUG_PIM_HELLO) - zlog_debug( - "%s: to %pPA on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", - __func__, &qpim_all_pim_routers_addr, ifp->name, - holdtime, pim_ifp->pim_propagation_delay_msec, - pim_ifp->pim_override_interval_msec, - pim_ifp->pim_can_disable_join_suppression, - pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id, - listcount(ifp->connected)); + zlog_debug("%s: to %pPA on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%zu", + __func__, &qpim_all_pim_routers_addr, ifp->name, + holdtime, pim_ifp->pim_propagation_delay_msec, + pim_ifp->pim_override_interval_msec, + pim_ifp->pim_can_disable_join_suppression, + pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id, + if_connected_count(ifp->connected)); pim_tlv_size = pim_hello_build_tlv( ifp, pim_msg + PIM_PIM_MIN_LEN, diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index c751624..d8d2571 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -248,7 +248,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, } rp_info = rn->info; - if (PIM_DEBUG_PIM_TRACE) { + if (PIM_DEBUG_PIM_TRACE_DETAIL) { if (best) zlog_debug( "Lookedup(%pFX): prefix_list match %s, rn %p found: %pFX", diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index b17ae31..d18ec49 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -32,7 +32,7 @@ static pim_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); void pim_rpf_set_refresh_time(struct pim_instance *pim) { pim->last_route_change_time = pim_time_monotonic_usec(); - if (PIM_DEBUG_PIM_TRACE) + if (PIM_DEBUG_PIM_TRACE_DETAIL) zlog_debug("%s: vrf(%s) New last route change time: %" PRId64, __func__, pim->vrf->name, pim->last_route_change_time); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 6c65c5d..3476c17 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -5,6 +5,7 @@ */ #include +#include #include #include diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 80d60b8..c463fa2 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -217,18 +217,17 @@ int pim_encode_addr_group(uint8_t *buf, afi_t afi, int bidir, int scope, uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, const uint8_t *buf_pastend, struct interface *ifp, int family) { - struct listnode *node; uint16_t option_len = 0; uint8_t *curr; size_t uel; - struct list *ifconnected = ifp->connected; + struct connected *ifc; struct pim_interface *pim_ifp = ifp->info; pim_addr addr; - node = listhead(ifconnected); + ifc = if_connected_first(ifp->connected); /* Empty address list ? */ - if (!node) { + if (!ifc) { return buf; } @@ -239,8 +238,7 @@ uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, const uint8_t *buf_pastend, /* Scan secondary address list */ curr = buf + 4; /* skip T and L */ - for (; node; node = listnextnode(node)) { - struct connected *ifc = listgetdata(node); + for (; ifc; ifc = if_connected_next(ifp->connected, ifc)) { struct prefix *p = ifc->address; int l_encode; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index e36bd82..556d25b 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -17,6 +17,7 @@ #include "jhash.h" #include "wheel.h" #include "network.h" +#include "frrdistance.h" #include "pimd.h" #include "pim_pim.h" @@ -908,7 +909,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, * Set the right RPF so that future changes will * be right */ - rpf_result = pim_rpf_update(pim, up, NULL, __func__); + (void)pim_rpf_update(pim, up, NULL, __func__); pim_upstream_keep_alive_timer_start( up, pim->keep_alive_time); } @@ -1943,6 +1944,40 @@ void pim_upstream_terminate(struct pim_instance *pim) wheel_delete(pim->upstream_sg_wheel); pim->upstream_sg_wheel = NULL; } +bool pim_sg_is_reevaluate_oil_req(struct pim_instance *pim, + struct pim_upstream *up) +{ + struct pim_interface *pim_ifp = NULL; + + /* + * Attempt to retrieve the PIM interface information if the RPF + * interface is present + */ + if (up->rpf.source_nexthop.interface) { + pim_ifp = up->rpf.source_nexthop.interface->info; + } else { + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); + } + } + + /* + * Determine if a reevaluation of the outgoing interface list (OIL) is + * required. This may be necessary in scenarios such as MSDP where the + * RP role for a group changes from secondary to primary. In such cases, + * SGRpt may receive a prune, resulting in an S,G entry with a NULL OIL. + * The S,G upstream should then inherit the OIL from *,G, which is + * particularly important for VXLAN setups. + */ + if (up->channel_oil->oil_inherited_rescan || + (pim_ifp && I_am_RP(pim_ifp->pim, up->sg.grp)) || + pim_upstream_empty_inherited_olist(up)) { + return true; + } + + return false; +} bool pim_upstream_equal(const void *arg1, const void *arg2) { @@ -2078,7 +2113,7 @@ static void pim_upstream_sg_running(void *arg) * only doing this at this point in time * to get us up and working for the moment */ - if (up->channel_oil->oil_inherited_rescan) { + if (pim_sg_is_reevaluate_oil_req(pim, up)) { if (PIM_DEBUG_TRACE) zlog_debug( "%s: Handling unscanned inherited_olist for %s[%s]", diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 4e0926e..62649cd 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -383,4 +383,6 @@ uint32_t pim_up_mlag_local_cost(struct pim_upstream *up); uint32_t pim_up_mlag_peer_cost(struct pim_upstream *up); void pim_upstream_reeval_use_rpt(struct pim_instance *pim); int pim_upstream_could_register(struct pim_upstream *up); +bool pim_sg_is_reevaluate_oil_req(struct pim_instance *pim, + struct pim_upstream *up); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index 9650da8..f1f315c 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -32,6 +32,30 @@ static void pim_vxlan_work_timer_setup(bool start); static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim, struct interface *ifp); +#define PIM_VXLAN_STARTUP_NULL_REGISTERS 10 + +static void pim_vxlan_rp_send_null_register_startup(struct event *e) +{ + struct pim_vxlan_sg *vxlan_sg = EVENT_ARG(e); + + vxlan_sg->null_register_sent++; + + if (vxlan_sg->null_register_sent > PIM_VXLAN_STARTUP_NULL_REGISTERS) { + if (PIM_DEBUG_VXLAN) + zlog_debug("Null registering stopping for %s", + vxlan_sg->sg_str); + return; + } + + pim_null_register_send(vxlan_sg->up); + + if (PIM_DEBUG_VXLAN) + zlog_debug("Sent null register for %s", vxlan_sg->sg_str); + + event_add_timer(router->master, pim_vxlan_rp_send_null_register_startup, + vxlan_sg, PIM_VXLAN_WORK_TIME, &vxlan_sg->null_register); +} + /* * The rp info has gone from no path to having a * path. Let's immediately send out the null pim register @@ -61,8 +85,13 @@ void pim_vxlan_rp_info_is_alive(struct pim_instance *pim, * If the rp is the same we should send */ if (rpg == rpg_changed) { - zlog_debug("VXLAN RP INFO is alive sending"); - pim_null_register_send(vxlan_sg->up); + if (PIM_DEBUG_VXLAN) + zlog_debug("VXLAN RP info for %s alive sending", + vxlan_sg->sg_str); + vxlan_sg->null_register_sent = 0; + event_add_event(router->master, + pim_vxlan_rp_send_null_register_startup, + vxlan_sg, 0, &vxlan_sg->null_register); } } } @@ -201,8 +230,18 @@ void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, */ if (reg_join) pim_vxlan_add_work(vxlan_sg); - else + else { + /* + * Stop the event that is sending NULL Registers on startup + * there is no need to keep spamming it + */ + if (PIM_DEBUG_VXLAN) + zlog_debug("Received Register stop for %s", + vxlan_sg->sg_str); + + EVENT_OFF(vxlan_sg->null_register); pim_vxlan_del_work(vxlan_sg); + } } static void pim_vxlan_work_timer_cb(struct event *t) @@ -804,6 +843,7 @@ static void pim_vxlan_sg_del_item(struct pim_vxlan_sg *vxlan_sg) { vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG; + EVENT_OFF(vxlan_sg->null_register); pim_vxlan_del_work(vxlan_sg); if (pim_vxlan_is_orig_mroute(vxlan_sg)) @@ -1210,6 +1250,9 @@ void pim_vxlan_exit(struct pim_instance *pim) { hash_clean_and_free(&pim->vxlan.sg_hash, (void (*)(void *))pim_vxlan_sg_del_item); + + if (vxlan_info.work_list) + list_delete(&vxlan_info.work_list); } void pim_vxlan_terminate(void) diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index 5039bf6..fe3d625 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -49,6 +49,9 @@ struct pim_vxlan_sg { struct interface *iif; /* on a MLAG setup the peerlink is added as a static OIF */ struct interface *orig_oif; + + struct event *null_register; + uint32_t null_register_sent; }; enum pim_vxlan_mlag_flags { diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 62f00db..04cd087 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -55,12 +55,11 @@ static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) static void dump_if_address(struct interface *ifp) { struct connected *ifc; - struct listnode *node; zlog_debug("%s %s: interface %s addresses:", __FILE__, __func__, ifp->name); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; if (p->family != AF_INET) @@ -97,8 +96,8 @@ static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) p = c->address; if (PIM_DEBUG_ZEBRA) { - zlog_debug("%s: %s(%u) connected IP address %pFX flags %u %s", - __func__, c->ifp->name, vrf_id, p, c->flags, + zlog_debug("%s: %s(%s) connected IP address %pFX flags %u %s", + __func__, c->ifp->name, VRF_LOGNAME(pim_ifp->pim->vrf), p, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -183,8 +182,8 @@ static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) if (PIM_DEBUG_ZEBRA) { zlog_debug( - "%s: %s(%u) disconnected IP address %pFX flags %u %s", - __func__, c->ifp->name, vrf_id, p, c->flags, + "%s: %s(%s) disconnected IP address %pFX flags %u %s", + __func__, c->ifp->name, VRF_LOGNAME(vrf), p, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -326,7 +325,7 @@ static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS) stream_get(&sg.grp, s, prefixlen); if (PIM_DEBUG_ZEBRA) - zlog_debug("%u:recv SG %s %pSG", vrf_id, + zlog_debug("%s:recv SG %s %pSG", VRF_LOGNAME(pim->vrf), (cmd == ZEBRA_VXLAN_SG_ADD) ? "add" : "del", &sg); if (cmd == ZEBRA_VXLAN_SG_ADD) @@ -428,7 +427,6 @@ static zclient_handler *const pim_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del, - [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, [ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra, #if PIM_IPV == 4 @@ -449,6 +447,7 @@ void pim_zebra_init(void) zclient->zebra_capabilities = pim_zebra_capabilities; zclient->zebra_connected = pim_zebra_connected; + zclient->nexthop_update = pim_nexthop_update; zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); if (PIM_DEBUG_PIM_TRACE) { diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 6a026f9..c19119f 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -122,10 +122,7 @@ void zclient_lookup_free(void) void zclient_lookup_new(void) { - struct zclient_options options = zclient_options_default; - options.synchronous = true; - - zlookup = zclient_new(router->master, &options, NULL, 0); + zlookup = zclient_new(router->master, &zclient_options_sync, NULL, 0); if (!zlookup) { flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_new() failure", __func__); diff --git a/pimd/pimd.h b/pimd/pimd.h index 9ec84fc..3d93189 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -19,9 +19,6 @@ #include "pim_memory.h" #include "pim_assert.h" -#define PIMD_VTY_PORT 2611 -#define PIM6D_VTY_PORT 2622 - #define PIM_IP_PROTO_IGMP (2) #define PIM_IP_PROTO_PIM (103) #define PIM_IGMP_MIN_LEN (8) diff --git a/pkgsrc/bgpd.sh.in b/pkgsrc/bgpd.sh.in index d234b54..a8ba7c6 100644 --- a/pkgsrc/bgpd.sh.in +++ b/pkgsrc/bgpd.sh.in @@ -16,7 +16,7 @@ fi name="bgpd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/eigrpd.sh.in b/pkgsrc/eigrpd.sh.in index b28b81e..dd8caf8 100644 --- a/pkgsrc/eigrpd.sh.in +++ b/pkgsrc/eigrpd.sh.in @@ -16,7 +16,7 @@ fi name="eigrpd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/mgmtd.sh.in b/pkgsrc/mgmtd.sh.in index fb57c0a..313633a 100644 --- a/pkgsrc/mgmtd.sh.in +++ b/pkgsrc/mgmtd.sh.in @@ -16,7 +16,7 @@ fi name="mgmtd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/ospf6d.sh.in b/pkgsrc/ospf6d.sh.in index 3fbdb81..c9854ed 100644 --- a/pkgsrc/ospf6d.sh.in +++ b/pkgsrc/ospf6d.sh.in @@ -16,7 +16,7 @@ fi name="ospf6d" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/ospfd.sh.in b/pkgsrc/ospfd.sh.in index daa2252..07dee55 100644 --- a/pkgsrc/ospfd.sh.in +++ b/pkgsrc/ospfd.sh.in @@ -16,7 +16,7 @@ fi name="ospfd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/ripd.sh.in b/pkgsrc/ripd.sh.in index 3157541..6c9b580 100644 --- a/pkgsrc/ripd.sh.in +++ b/pkgsrc/ripd.sh.in @@ -16,7 +16,7 @@ fi name="ripd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/ripngd.sh.in b/pkgsrc/ripngd.sh.in index d06ac90..d316d46 100644 --- a/pkgsrc/ripngd.sh.in +++ b/pkgsrc/ripngd.sh.in @@ -16,7 +16,7 @@ fi name="ripngd" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/pkgsrc/zebra.sh.in b/pkgsrc/zebra.sh.in index c2f12a7..7a24106 100644 --- a/pkgsrc/zebra.sh.in +++ b/pkgsrc/zebra.sh.in @@ -16,7 +16,7 @@ fi name="zebra" rcvar=$name -required_files="@sysconfdir@/${name}.conf" +required_files="@e_sysconfdir@/${name}.conf" command="@prefix@/sbin/${name}" command_args="-d" diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py index 0a7e28e..91ccba6 100644 --- a/python/xref2vtysh.py +++ b/python/xref2vtysh.py @@ -33,18 +33,18 @@ frr_top_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) daemon_flags = { "lib/agentx.c": "VTYSH_ISISD|VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA", - "lib/filter.c": "VTYSH_ACL", - "lib/filter_cli.c": "VTYSH_ACL", + "lib/filter.c": "VTYSH_ACL_SHOW", + "lib/filter_cli.c": "VTYSH_ACL_CONFIG", "lib/if.c": "VTYSH_INTERFACE", "lib/keychain.c": "VTYSH_KEYS", - "lib/mgmt_be_client.c": "VTYSH_STATICD", - "lib/mgmt_fe_client.c": "VTYSH_MGMTD", + "lib/mgmt_be_client.c": "VTYSH_MGMT_BACKEND", + "lib/mgmt_fe_client.c": "VTYSH_MGMT_FRONTEND", "lib/lib_vty.c": "VTYSH_ALL", "lib/log_vty.c": "VTYSH_ALL", "lib/nexthop_group.c": "VTYSH_NH_GROUP", "lib/resolver.c": "VTYSH_NHRPD|VTYSH_BGPD", - "lib/routemap.c": "VTYSH_RMAP", - "lib/routemap_cli.c": "VTYSH_RMAP", + "lib/routemap.c": "VTYSH_RMAP_SHOW", + "lib/routemap_cli.c": "VTYSH_RMAP_CONFIG", "lib/spf_backoff.c": "VTYSH_ISISD", "lib/event.c": "VTYSH_ALL", "lib/vrf.c": "VTYSH_VRF", @@ -206,10 +206,7 @@ class CommandEntry: } if defun_file == "lib/if_rmap.c": - if v6_cmd: - return {"VTYSH_RIPNGD"} - else: - return {"VTYSH_RIPD"} + return {"VTYSH_MGMTD"} return {} @@ -327,17 +324,7 @@ class CommandEntry: def load(cls, xref): nodes = NodeDict() - mgmtname = "mgmtd/libmgmt_be_nb.la" for cmd_name, origins in xref.get("cli", {}).items(): - # If mgmtd has a yang version of a CLI command, make it the only daemon - # to handle it. For now, daemons can still be compiling their cmds into the - # binaries to allow for running standalone with CLI config files. When they - # do this they will also be present in the xref file, but we want to ignore - # those in vtysh. - if "yang" in origins.get(mgmtname, {}).get("attrs", []): - CommandEntry.process(nodes, cmd_name, mgmtname, origins[mgmtname]) - continue - for origin, spec in origins.items(): CommandEntry.process(nodes, cmd_name, origin, spec) return nodes diff --git a/qpb/qpb.h b/qpb/qpb.h index d52528f..2804614 100644 --- a/qpb/qpb.h +++ b/qpb/qpb.h @@ -14,6 +14,7 @@ #ifndef _QPB_H #define _QPB_H +#include "nexthop.h" #include "prefix.h" #include "qpb/qpb.pb-c.h" diff --git a/redhat/frr.pam b/redhat/frr.pam index 17a62f1..a574c5e 100644 --- a/redhat/frr.pam +++ b/redhat/frr.pam @@ -4,8 +4,8 @@ ##### if running frr as root: # Only allow root (and possibly wheel) to use this because enable access # is unrestricted. -auth sufficient pam_rootok.so -account sufficient pam_rootok.so +auth sufficient pam_permit.so +account sufficient pam_permit.so # Uncomment the following line to implicitly trust users in the "wheel" group. #auth sufficient pam_wheel.so trust use_uid diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index f43d0c1..bd9edd1 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -16,6 +16,7 @@ %{!?with_cumulus: %global with_cumulus 0 } %{!?with_eigrpd: %global with_eigrpd 1 } %{!?with_fpm: %global with_fpm 1 } +%{!?with_mgmtd_test_be_client: %global with_mgmtd_test_be_client 0 } %{!?with_ldpd: %global with_ldpd 1 } %{!?with_multipath: %global with_multipath 256 } %{!?with_nhrpd: %global with_nhrpd 1 } @@ -42,8 +43,9 @@ %define zeb_docs %{zeb_src}/doc %define frr_tools %{zeb_src}/tools -# defines for configure -%define rundir %{_localstatedir}/run/%{name} +%if 0%{!?_runstatedir:1} +%define _runstatedir %{_localstatedir}/run +%endif ############################################################################ @@ -180,6 +182,7 @@ BuildRequires: flex BuildRequires: gcc BuildRequires: json-c-devel BuildRequires: libcap-devel +BuildRequires: protobuf-c-devel BuildRequires: make BuildRequires: ncurses-devel BuildRequires: readline-devel @@ -316,11 +319,13 @@ routing state through standard SNMP MIBs. %configure \ --sbindir=%{_sbindir} \ - --sysconfdir=%{configdir} \ - --localstatedir=%{rundir} \ + --sysconfdir=%{_sysconfdir} \ + --localstatedir=%{_localstatedir} \ --disable-static \ --disable-werror \ - --enable-irdp \ +%if %{with_mgmtd_test_be_client} + --enable-mgmtd-test-be-client \ +%endif %if %{with_multipath} --enable-multipath=%{with_multipath} \ %endif @@ -470,7 +475,7 @@ install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr install %{zeb_src}/tools/etc/frr/frr.conf %{buildroot}%{_sysconfdir}/frr/frr.conf.template install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_src}/tools/etc/logrotate.d/frr %{buildroot}%{_sysconfdir}/logrotate.d/frr -install -d -m750 %{buildroot}%{rundir} +install -d -m750 %{buildroot}%{_runstatedir}/frr %if 0%{?rhel} > 7 || 0%{?fedora} > 29 # avoid `ERROR: ambiguous python shebang in` errors @@ -494,7 +499,7 @@ rm -f %{buildroot}%{_sbindir}/ospfclient.py getent passwd %{frr_user} >/dev/null || \ useradd -r -u %{frr_uid} -g %{frr_user} \ -s /sbin/nologin -c "FRRouting suite" \ - -d %{rundir} %{frr_user} + -d %{_runstatedir}/frr %{frr_user} %if 0%{?vty_group:1} usermod -a -G %{vty_group} %{frr_user} @@ -654,11 +659,11 @@ fi %if 0%{?frr_user:1} %dir %attr(751,%{frr_user},%{frr_user}) %{configdir} %dir %attr(755,%{frr_user},%{frr_user}) %{_localstatedir}/log/frr - %dir %attr(751,%{frr_user},%{frr_user}) %{rundir} + %dir %attr(751,%{frr_user},%{frr_user}) %{_runstatedir}/frr %else %dir %attr(750,root,root) %{configdir} %dir %attr(755,root,root) %{_localstatedir}/log/frr - %dir %attr(750,root,root) %{rundir} + %dir %attr(750,root,root) %{_runstatedir}/frr %endif %{_infodir}/frr.info.gz %{_mandir}/man*/* @@ -668,6 +673,9 @@ fi %{_sbindir}/ripd %{_sbindir}/bgpd %{_sbindir}/mgmtd +%if %{with_mgmtd_test_be_client} + %{_sbindir}/mgmtd_testc +%endif %exclude %{_sbindir}/ssd %if %{with_watchfrr} %{_sbindir}/watchfrr @@ -715,7 +723,6 @@ fi %endif %{_libdir}/frr/modules/zebra_cumulus_mlag.so %{_libdir}/frr/modules/dplane_fpm_nl.so -%{_libdir}/frr/modules/zebra_irdp.so %{_libdir}/frr/modules/bgpd_bmp.so %{_libdir}/libfrr_pb.so* %{_libdir}/libfrrfpm_pb.so* @@ -779,8 +786,6 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons %{_libdir}/lib*.so %dir %{_includedir}/%{name} %{_includedir}/%{name}/*.h -%dir %{_includedir}/%{name}/mgmtd -%{_includedir}/%{name}/mgmtd/*.h %dir %{_includedir}/%{name}/ospfd %{_includedir}/%{name}/ospfd/*.h %if %{with_bfdd} @@ -799,9 +804,23 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons %changelog -* Thu Oct 09 2023 Donatas Abraitis - %{version} +* Mon Apr 08 2024 Jafar Al-Gharaibeh - %{version} -* Thu Oct 09 2023 Donatas Abraitis - 9.1 +* Mon Apr 08 2024 Jafar Al-Gharaibeh - 10.0 +- Major highlights: +- Introduce local host routes +- Require libyang 2.1.128 +- Add suport to configire a log file per daemon +- BGP BMP Loc-RIB (RFC9069) support +- eBGP-OAD (One Administrative Domain) support +- BGP RPKI VRF support +- BGP SNMP traps for BGP4-MIBV2 +- Management (mgmtd) daemon "replace" operation support +- BGP dynamic capabilities for addpath, fqdn, orf capabilities +- SRv6 encapsulation source address feature +- OSPFv3 Point-To-Multipoint mode + +* Mon Oct 09 2023 Donatas Abraitis - 9.1 - Major highlights: - OSPFv2 HMAC-SHA Cryptographic Authentication - BGP MAC-VRF Site-Of-Origin support @@ -821,18 +840,20 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons - Memory footprint for BGP reduced drastically - Add BGP `neighbor path-attribute treat-as-withdraw` command - Add BGP ASN dot notation support (RFC 5396) -- Add BGP Software Version capability (draft-abraitis-bgp-version-capability) +- Add BGP Software Version capability - Allow BGP peering via 127.0.0.0/8 -- Deprecate BGP internet community - this is the Cisco-specific community, which is never been RFC-defined and confusing +- Deprecate BGP `internet` community - this is the Cisco-specific community, which is never been RFC-defined and confusing - Implement `match source-protocol` for BGP route maps - Implement BGP Node Target extended communities (draft-ietf-idr-node-target-ext-comm) - Implement Flex-Algo for SR-MPLS (RFC 9350) -- Add support for IS-IS advertise-passive-only -- Add IS-IS affinity-map support -- Add the graceful-restart hello-delay OSPFv2/OSPFv3 command +- Add support for IS-IS `advertise-passive-only` +- Add IS-IS `affinity-map` support +- Add the `graceful-restart hello-delay` OSPFv2/OSPFv3 command - Add the `ipv6 mld join` PIMv6 command - Add `allow-ecmp x` RIP/RIPng command - Add BFD support for RIP +- For a full list of new features and bug fixes, please refer to: +- https://frrouting.org/release/ * Fri Mar 10 2023 Jafar Al-Gharaibeh - 8.5 - Major Highlights: diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index 097c708..4d4349b 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -8,6 +8,7 @@ #include #include "if.h" +#include "if_rmap.h" #include "vrf.h" #include "log.h" #include "prefix.h" @@ -73,7 +74,7 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode, { const char *vrf_name; - vrf_name = yang_dnode_get_string(dnode, "./vrf"); + vrf_name = yang_dnode_get_string(dnode, "vrf"); vty_out(vty, "!\n"); vty_out(vty, "router rip"); @@ -82,6 +83,11 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "\n"); } +void cli_show_end_router_rip(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); +} + /* * XPath: /frr-ripd:ripd/instance/allow-ecmp */ @@ -255,11 +261,11 @@ void cli_show_rip_distance_source(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " distance %s %s", - yang_dnode_get_string(dnode, "./distance"), - yang_dnode_get_string(dnode, "./prefix")); - if (yang_dnode_exists(dnode, "./access-list")) + yang_dnode_get_string(dnode, "distance"), + yang_dnode_get_string(dnode, "prefix")); + if (yang_dnode_exists(dnode, "access-list")) vty_out(vty, " %s", - yang_dnode_get_string(dnode, "./access-list")); + yang_dnode_get_string(dnode, "access-list")); vty_out(vty, "\n"); } @@ -273,8 +279,13 @@ DEFPY_YANG (rip_neighbor, "Specify a neighbor router\n" "Neighbor address\n") { - nb_cli_enqueue_change(vty, "./explicit-neighbor", - no ? NB_OP_DESTROY : NB_OP_CREATE, neighbor_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./explicit-neighbor[.='%s']", + neighbor_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -295,8 +306,12 @@ DEFPY_YANG (rip_network_prefix, "Enable routing on an IP network\n" "IP prefix /, e.g., 35.0.0.0/8\n") { - nb_cli_enqueue_change(vty, "./network", - no ? NB_OP_DESTROY : NB_OP_CREATE, network_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./network[.='%s']", network_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -317,8 +332,12 @@ DEFPY_YANG (rip_network_if, "Enable routing on an IP network\n" "Interface name\n") { - nb_cli_enqueue_change(vty, "./interface", - no ? NB_OP_DESTROY : NB_OP_CREATE, network); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./interface[.='%s']", network); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -362,12 +381,12 @@ void cli_show_rip_offset_list(struct vty *vty, const struct lyd_node *dnode, { const char *interface; - interface = yang_dnode_get_string(dnode, "./interface"); + interface = yang_dnode_get_string(dnode, "interface"); vty_out(vty, " offset-list %s %s %s", - yang_dnode_get_string(dnode, "./access-list"), - yang_dnode_get_string(dnode, "./direction"), - yang_dnode_get_string(dnode, "./metric")); + yang_dnode_get_string(dnode, "access-list"), + yang_dnode_get_string(dnode, "direction"), + yang_dnode_get_string(dnode, "metric")); if (!strmatch(interface, "*")) vty_out(vty, " %s", interface); vty_out(vty, "\n"); @@ -412,17 +431,21 @@ DEFPY_YANG (rip_passive_interface, bool passive_default = yang_dnode_get_bool(vty->candidate_config->dnode, "%s%s", VTY_CURR_XPATH, "/passive-default"); + char xpath[XPATH_MAXLEN]; + enum nb_operation op; if (passive_default) { - nb_cli_enqueue_change(vty, "./non-passive-interface", - no ? NB_OP_CREATE : NB_OP_DESTROY, - ifname); + snprintf(xpath, sizeof(xpath), + "./non-passive-interface[.='%s']", ifname); + op = no ? NB_OP_CREATE : NB_OP_DESTROY; } else { - nb_cli_enqueue_change(vty, "./passive-interface", - no ? NB_OP_DESTROY : NB_OP_CREATE, - ifname); + snprintf(xpath, sizeof(xpath), "./passive-interface[.='%s']", + ifname); + op = no ? NB_OP_DESTROY : NB_OP_CREATE; } + nb_cli_enqueue_change(vty, xpath, op, NULL); + return nb_cli_apply_changes(vty, NULL); } @@ -475,13 +498,13 @@ void cli_show_rip_redistribute(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " redistribute %s", - yang_dnode_get_string(dnode, "./protocol")); - if (yang_dnode_exists(dnode, "./metric")) + yang_dnode_get_string(dnode, "protocol")); + if (yang_dnode_exists(dnode, "metric")) vty_out(vty, " metric %s", - yang_dnode_get_string(dnode, "./metric")); - if (yang_dnode_exists(dnode, "./route-map")) + yang_dnode_get_string(dnode, "metric")); + if (yang_dnode_exists(dnode, "route-map")) vty_out(vty, " route-map %s", - yang_dnode_get_string(dnode, "./route-map")); + yang_dnode_get_string(dnode, "route-map")); vty_out(vty, "\n"); } @@ -495,8 +518,12 @@ DEFPY_YANG (rip_route, "RIP static route configuration\n" "IP prefix /\n") { - nb_cli_enqueue_change(vty, "./static-route", - no ? NB_OP_DESTROY : NB_OP_CREATE, route_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./static-route[.='%s']", route_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -550,9 +577,9 @@ void cli_show_rip_timers(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " timers basic %s %s %s\n", - yang_dnode_get_string(dnode, "./update-interval"), - yang_dnode_get_string(dnode, "./holddown-interval"), - yang_dnode_get_string(dnode, "./flush-interval")); + yang_dnode_get_string(dnode, "update-interval"), + yang_dnode_get_string(dnode, "holddown-interval"), + yang_dnode_get_string(dnode, "flush-interval")); } /* @@ -591,7 +618,7 @@ void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode, * We have only one "version" command and three possible combinations of * send/receive values. */ - switch (yang_dnode_get_enum(dnode, "./receive")) { + switch (yang_dnode_get_enum(dnode, "receive")) { case RI_RIP_VERSION_1: vty_out(vty, " version 1\n"); break; @@ -912,7 +939,7 @@ void cli_show_ip_rip_authentication_scheme(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - switch (yang_dnode_get_enum(dnode, "./mode")) { + switch (yang_dnode_get_enum(dnode, "mode")) { case RIP_NO_AUTH: vty_out(vty, " no ip rip authentication mode\n"); break; @@ -922,8 +949,8 @@ void cli_show_ip_rip_authentication_scheme(struct vty *vty, case RIP_AUTH_MD5: vty_out(vty, " ip rip authentication mode md5"); if (show_defaults - || !yang_dnode_is_default(dnode, "./md5-auth-length")) { - if (yang_dnode_get_enum(dnode, "./md5-auth-length") + || !yang_dnode_is_default(dnode, "md5-auth-length")) { + if (yang_dnode_get_enum(dnode, "md5-auth-length") == RIP_AUTH_MD5_SIZE) vty_out(vty, " auth-length rfc"); else @@ -1098,85 +1125,134 @@ void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string(dnode, NULL)); } -/* - * XPath: /frr-ripd:clear-rip-route - */ -DEFPY_YANG (clear_ip_rip, - clear_ip_rip_cmd, - "clear ip rip [vrf WORD]", - CLEAR_STR - IP_STR - "Clear IP RIP database\n" - VRF_CMD_HELP_STR) +DEFPY_YANG( + rip_distribute_list, rip_distribute_list_cmd, + "distribute-list ACCESSLIST4_NAME$name $dir [WORD$ifname]", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - struct list *input; - int ret; - - input = list_new(); - if (vrf) { - struct yang_data *yang_vrf; - - yang_vrf = yang_data_new("/frr-ripd:clear-rip-route/input/vrf", - vrf); - listnode_add(input, yang_vrf); - } - - ret = nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", input, NULL); - - list_delete(&input); + char xpath[XPATH_MAXLEN]; - return ret; + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (rip_distribute_list, - rip_distribute_list_cmd, - "distribute-list [prefix] ACCESSLIST4_NAME [WORD]", - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") +DEFPY_YANG( + rip_distribute_list_prefix, rip_distribute_list_prefix_cmd, + "distribute-list prefix PREFIXLIST4_NAME$name $dir [WORD$ifname]", + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - const char *ifname = NULL; - int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0; - - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; + char xpath[XPATH_MAXLEN]; - return distribute_list_parser(prefix, true, argv[2 + prefix]->text, - argv[1 + prefix]->arg, ifname); + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (rip_no_distribute_list, - rip_no_distribute_list_cmd, - "no distribute-list [prefix] ACCESSLIST4_NAME [WORD]", - NO_STR - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") +DEFPY_YANG(no_rip_distribute_list, + no_rip_distribute_list_cmd, + "no distribute-list [ACCESSLIST4_NAME$name] $dir [WORD$ifname]", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - const char *ifname = NULL; - int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0; + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; +DEFPY_YANG(no_rip_distribute_list_prefix, + no_rip_distribute_list_prefix_cmd, + "no distribute-list prefix [PREFIXLIST4_NAME$name] $dir [WORD$ifname]", + NO_STR + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; - return distribute_list_no_parser(vty, prefix, true, - argv[3 + prefix]->text, - argv[2 + prefix]->arg, ifname); + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } +/* RIP node structure. */ +static struct cmd_node rip_node = { + .name = "rip", + .node = RIP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + // .config_write = config_write_rip, +}; + void rip_cli_init(void) { + install_node(&rip_node); + install_element(CONFIG_NODE, &router_rip_cmd); install_element(CONFIG_NODE, &no_router_rip_cmd); install_element(RIP_NODE, &rip_distribute_list_cmd); - install_element(RIP_NODE, &rip_no_distribute_list_cmd); + install_element(RIP_NODE, &rip_distribute_list_prefix_cmd); + install_element(RIP_NODE, &no_rip_distribute_list_cmd); + install_element(RIP_NODE, &no_rip_distribute_list_prefix_cmd); install_element(RIP_NODE, &rip_allow_ecmp_cmd); install_element(RIP_NODE, &no_rip_allow_ecmp_cmd); @@ -1200,6 +1276,7 @@ void rip_cli_init(void) install_element(RIP_NODE, &no_rip_version_cmd); install_element(RIP_NODE, &rip_bfd_default_profile_cmd); install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd); + install_default(RIP_NODE); install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd); install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd); @@ -1218,5 +1295,128 @@ void rip_cli_init(void) install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd); install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd); - install_element(ENABLE_NODE, &clear_ip_rip_cmd); -} + if_rmap_init(RIP_NODE); +} +/* clang-format off */ +const struct frr_yang_module_info frr_ripd_cli_info = { + .name = "frr-ripd", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-ripd:ripd/instance", + .cbs.cli_show = cli_show_router_rip, + .cbs.cli_show_end = cli_show_end_router_rip, + }, + { + .xpath = "/frr-ripd:ripd/instance/allow-ecmp", + .cbs.cli_show = cli_show_rip_allow_ecmp, + }, + { + .xpath = "/frr-ripd:ripd/instance/default-information-originate", + .cbs.cli_show = cli_show_rip_default_information_originate, + }, + { + .xpath = "/frr-ripd:ripd/instance/default-metric", + .cbs.cli_show = cli_show_rip_default_metric, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/default", + .cbs.cli_show = cli_show_rip_distance, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/source", + .cbs.cli_show = cli_show_rip_distance_source, + }, + { + .xpath = "/frr-ripd:ripd/instance/explicit-neighbor", + .cbs.cli_show = cli_show_rip_neighbor, + }, + { + .xpath = "/frr-ripd:ripd/instance/network", + .cbs.cli_show = cli_show_rip_network_prefix, + }, + { + .xpath = "/frr-ripd:ripd/instance/interface", + .cbs.cli_show = cli_show_rip_network_interface, + }, + { + .xpath = "/frr-ripd:ripd/instance/offset-list", + .cbs.cli_show = cli_show_rip_offset_list, + }, + { + .xpath = "/frr-ripd:ripd/instance/passive-default", + .cbs.cli_show = cli_show_rip_passive_default, + }, + { + .xpath = "/frr-ripd:ripd/instance/passive-interface", + .cbs.cli_show = cli_show_rip_passive_interface, + }, + { + .xpath = "/frr-ripd:ripd/instance/non-passive-interface", + .cbs.cli_show = cli_show_rip_non_passive_interface, + }, + { + .xpath = "/frr-ripd:ripd/instance/redistribute", + .cbs.cli_show = cli_show_rip_redistribute, + }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map", + .cbs.cli_show = cli_show_if_route_map, + }, + { + .xpath = "/frr-ripd:ripd/instance/static-route", + .cbs.cli_show = cli_show_rip_route, + }, + { + .xpath = "/frr-ripd:ripd/instance/timers", + .cbs.cli_show = cli_show_rip_timers, + }, + { + .xpath = "/frr-ripd:ripd/instance/version", + .cbs.cli_show = cli_show_rip_version, + }, + { + .xpath = "/frr-ripd:ripd/instance/default-bfd-profile", + .cbs.cli_show = cli_show_ripd_instance_default_bfd_profile, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon", + .cbs.cli_show = cli_show_ip_rip_split_horizon, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast", + .cbs.cli_show = cli_show_ip_rip_v2_broadcast, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive", + .cbs.cli_show = cli_show_ip_rip_receive_version, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send", + .cbs.cli_show = cli_show_ip_rip_send_version, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme", + .cbs.cli_show = cli_show_ip_rip_authentication_scheme, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password", + .cbs.cli_show = cli_show_ip_rip_authentication_string, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain", + .cbs.cli_show = cli_show_ip_rip_authentication_key_chain, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable", + .cbs.cli_show = cli_show_ip_rip_bfd_enable, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile", + .cbs.cli_show = cli_show_ip_rip_bfd_profile, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 505290e..486d7b0 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -128,14 +128,12 @@ static void rip_request_interface_send(struct interface *ifp, uint8_t version) /* RIPv1 and non multicast interface. */ if (if_is_pointopoint(ifp) || if_is_broadcast(ifp)) { - struct listnode *cnode, *cnnode; struct connected *connected; if (IS_RIP_DEBUG_EVENT) zlog_debug("broadcast request to %s", ifp->name); - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, - connected)) { + frr_each (if_connected, ifp->connected, connected) { if (connected->address->family != AF_INET) continue; @@ -197,14 +195,13 @@ static void rip_request_interface(struct interface *ifp) /* Multicast packet receive socket. */ static int rip_multicast_join(struct interface *ifp, int sock) { - struct listnode *cnode; struct connected *ifc; if (if_is_operative(ifp) && if_is_multicast(ifp)) { if (IS_RIP_DEBUG_EVENT) zlog_debug("multicast join at %s", ifp->name); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { struct prefix_ipv4 *p; struct in_addr group; @@ -228,14 +225,13 @@ static int rip_multicast_join(struct interface *ifp, int sock) /* Leave from multicast group. */ static void rip_multicast_leave(struct interface *ifp, int sock) { - struct listnode *cnode; struct connected *connected; if (if_is_up(ifp) && if_is_multicast(ifp)) { if (IS_RIP_DEBUG_EVENT) zlog_debug("multicast leave from %s", ifp->name); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix_ipv4 *p; struct in_addr group; @@ -256,11 +252,10 @@ static void rip_multicast_leave(struct interface *ifp, int sock) /* Is there and address on interface that I could use ? */ static int rip_if_ipv4_address_check(struct interface *ifp) { - struct listnode *nn; struct connected *connected; int count = 0; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; p = connected->address; @@ -279,10 +274,9 @@ int if_check_address(struct rip *rip, struct in_addr addr) struct interface *ifp; FOR_ALL_INTERFACES (rip->vrf, ifp) { - struct listnode *cnode; struct connected *connected; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix_ipv4 *p; p = (struct prefix_ipv4 *)connected->address; @@ -596,14 +590,13 @@ static int rip_enable_network_lookup_if(struct interface *ifp) { struct rip_interface *ri = ifp->info; struct rip *rip = ri->rip; - struct listnode *node, *nnode; struct connected *connected; struct prefix_ipv4 address; if (!rip) return -1; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; struct route_node *n; @@ -780,14 +773,13 @@ static void rip_connect_set(struct interface *ifp, int set) { struct rip_interface *ri = ifp->info; struct rip *rip = ri->rip; - struct listnode *node, *nnode; struct connected *connected; struct prefix_ipv4 address; struct nexthop nh; memset(&nh, 0, sizeof(nh)); - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; p = connected->address; @@ -1117,7 +1109,8 @@ void rip_if_init(void) hook_register_prio(if_del, 0, rip_interface_delete_hook); /* Install interface node. */ - if_cmd_init_default(); - if_zapi_callbacks(rip_ifp_create, rip_ifp_up, - rip_ifp_down, rip_ifp_destroy); + hook_register_prio(if_real, 0, rip_ifp_create); + hook_register_prio(if_up, 0, rip_ifp_up); + hook_register_prio(if_down, 0, rip_ifp_down); + hook_register_prio(if_unreal, 0, rip_ifp_destroy); } diff --git a/ripd/rip_main.c b/ripd/rip_main.c index ac358eb..845c507 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -22,6 +22,7 @@ #include "libfrr.h" #include "routemap.h" #include "bfd.h" +#include "mgmt_be_client.h" #include "ripd/ripd.h" #include "ripd/rip_bfd.h" @@ -53,6 +54,8 @@ struct zebra_privs_t ripd_privs = { /* Master of threads. */ struct event_loop *master; +struct mgmt_be_client *mgmt_be_client; + static struct frr_daemon_info ripd_di; /* SIGHUP handler. */ @@ -67,12 +70,31 @@ static void sighup(void) /* SIGINT handler. */ static void sigint(void) { + struct vrf *vrf; + zlog_notice("Terminating on signal"); bfd_protocol_integration_set_shutdown(true); + + + nb_oper_cancel_all_walks(); + mgmt_be_client_destroy(mgmt_be_client); + mgmt_be_client = NULL; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (!vrf->info) + continue; + + rip_clean(vrf->info); + } + rip_vrf_terminate(); if_rmap_terminate(); rip_zclient_stop(); + + route_map_finish(); + + keychain_terminate(); frr_fini(); exit(0); @@ -111,15 +133,23 @@ static const struct frr_yang_module_info *const ripd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(ripd, RIP, + .vty_port = RIP_VTY_PORT, + .proghelp = "Implementation of the RIP routing protocol.", - .proghelp = "Implementation of the RIP routing protocol.", + .signals = ripd_signals, + .n_signals = array_size(ripd_signals), - .signals = ripd_signals, .n_signals = array_size(ripd_signals), + .privs = &ripd_privs, - .privs = &ripd_privs, .yang_modules = ripd_yang_modules, - .n_yang_modules = array_size(ripd_yang_modules), + .yang_modules = ripd_yang_modules, + .n_yang_modules = array_size(ripd_yang_modules), + + /* mgmtd will load the per-daemon config file now */ + .flags = FRR_NO_SPLIT_CONFIG, ); +/* clang-format on */ #define DEPRECATED_OPTIONS "" @@ -165,7 +195,9 @@ int main(int argc, char **argv) /* RIP related initialization. */ rip_init(); rip_if_init(); - rip_cli_init(); + + mgmt_be_client = mgmt_be_client_create("ripd", NULL, 0, master); + rip_zclient_init(master); rip_bfd_init(master); diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c index d11f1e1..d5df591 100644 --- a/ripd/rip_nb.c +++ b/ripd/rip_nb.c @@ -6,11 +6,12 @@ #include -#include "northbound.h" +#include "distribute.h" +#include "if_rmap.h" #include "libfrr.h" +#include "northbound.h" #include "ripd/rip_nb.h" -#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripd_info = { @@ -19,7 +20,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance", .cbs = { - .cli_show = cli_show_router_rip, .create = ripd_instance_create, .destroy = ripd_instance_destroy, .get_keys = ripd_instance_get_keys, @@ -30,35 +30,30 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/allow-ecmp", .cbs = { - .cli_show = cli_show_rip_allow_ecmp, .modify = ripd_instance_allow_ecmp_modify, }, }, { .xpath = "/frr-ripd:ripd/instance/default-information-originate", .cbs = { - .cli_show = cli_show_rip_default_information_originate, .modify = ripd_instance_default_information_originate_modify, }, }, { .xpath = "/frr-ripd:ripd/instance/default-metric", .cbs = { - .cli_show = cli_show_rip_default_metric, .modify = ripd_instance_default_metric_modify, }, }, { .xpath = "/frr-ripd:ripd/instance/distance/default", .cbs = { - .cli_show = cli_show_rip_distance, .modify = ripd_instance_distance_default_modify, }, }, { .xpath = "/frr-ripd:ripd/instance/distance/source", .cbs = { - .cli_show = cli_show_rip_distance_source, .create = ripd_instance_distance_source_create, .destroy = ripd_instance_distance_source_destroy, }, @@ -79,7 +74,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/explicit-neighbor", .cbs = { - .cli_show = cli_show_rip_neighbor, .create = ripd_instance_explicit_neighbor_create, .destroy = ripd_instance_explicit_neighbor_destroy, }, @@ -87,7 +81,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/network", .cbs = { - .cli_show = cli_show_rip_network_prefix, .create = ripd_instance_network_create, .destroy = ripd_instance_network_destroy, }, @@ -95,7 +88,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/interface", .cbs = { - .cli_show = cli_show_rip_network_interface, .create = ripd_instance_interface_create, .destroy = ripd_instance_interface_destroy, }, @@ -103,7 +95,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/offset-list", .cbs = { - .cli_show = cli_show_rip_offset_list, .create = ripd_instance_offset_list_create, .destroy = ripd_instance_offset_list_destroy, }, @@ -123,14 +114,12 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/passive-default", .cbs = { - .cli_show = cli_show_rip_passive_default, .modify = ripd_instance_passive_default_modify, }, }, { .xpath = "/frr-ripd:ripd/instance/passive-interface", .cbs = { - .cli_show = cli_show_rip_passive_interface, .create = ripd_instance_passive_interface_create, .destroy = ripd_instance_passive_interface_destroy, }, @@ -138,16 +127,53 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/non-passive-interface", .cbs = { - .cli_show = cli_show_rip_non_passive_interface, .create = ripd_instance_non_passive_interface_create, .destroy = ripd_instance_non_passive_interface_destroy, }, }, + { + .xpath = "/frr-ripd:ripd/instance/distribute-list", + .cbs = { + .create = ripd_instance_distribute_list_create, + .destroy = group_distribute_list_destroy, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/distribute-list/in/access-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/distribute-list/out/access-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/distribute-list/in/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/distribute-list/out/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv4_modify, + .destroy = group_distribute_list_ipv4_destroy, + .cli_show = group_distribute_list_ipv4_cli_show, + } + }, { .xpath = "/frr-ripd:ripd/instance/redistribute", .cbs = { .apply_finish = ripd_instance_redistribute_apply_finish, - .cli_show = cli_show_rip_redistribute, .create = ripd_instance_redistribute_create, .destroy = ripd_instance_redistribute_destroy, }, @@ -171,7 +197,6 @@ const struct frr_yang_module_info frr_ripd_info = { .cbs = { .create = ripd_instance_if_route_maps_if_route_map_create, .destroy = ripd_instance_if_route_maps_if_route_map_destroy, - .cli_show = cli_show_if_route_map, } }, { @@ -191,7 +216,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-ripd:ripd/instance/static-route", .cbs = { - .cli_show = cli_show_rip_route, .create = ripd_instance_static_route_create, .destroy = ripd_instance_static_route_destroy, }, @@ -200,7 +224,6 @@ const struct frr_yang_module_info frr_ripd_info = { .xpath = "/frr-ripd:ripd/instance/timers", .cbs = { .apply_finish = ripd_instance_timers_apply_finish, - .cli_show = cli_show_rip_timers, }, }, { @@ -221,12 +244,6 @@ const struct frr_yang_module_info frr_ripd_info = { .modify = ripd_instance_timers_update_interval_modify, }, }, - { - .xpath = "/frr-ripd:ripd/instance/version", - .cbs = { - .cli_show = cli_show_rip_version, - }, - }, { .xpath = "/frr-ripd:ripd/instance/version/receive", .cbs = { @@ -244,43 +261,32 @@ const struct frr_yang_module_info frr_ripd_info = { .cbs = { .modify = ripd_instance_default_bfd_profile_modify, .destroy = ripd_instance_default_bfd_profile_destroy, - .cli_show = cli_show_ripd_instance_default_bfd_profile, }, }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon", .cbs = { - .cli_show = cli_show_ip_rip_split_horizon, .modify = lib_interface_rip_split_horizon_modify, }, }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast", .cbs = { - .cli_show = cli_show_ip_rip_v2_broadcast, .modify = lib_interface_rip_v2_broadcast_modify, }, }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive", .cbs = { - .cli_show = cli_show_ip_rip_receive_version, .modify = lib_interface_rip_version_receive_modify, }, }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send", .cbs = { - .cli_show = cli_show_ip_rip_send_version, .modify = lib_interface_rip_version_send_modify, }, }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme", - .cbs = { - .cli_show = cli_show_ip_rip_authentication_scheme, - }, - }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode", .cbs = { @@ -297,7 +303,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password", .cbs = { - .cli_show = cli_show_ip_rip_authentication_string, .destroy = lib_interface_rip_authentication_password_destroy, .modify = lib_interface_rip_authentication_password_modify, }, @@ -305,7 +310,6 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain", .cbs = { - .cli_show = cli_show_ip_rip_authentication_key_chain, .destroy = lib_interface_rip_authentication_key_chain_destroy, .modify = lib_interface_rip_authentication_key_chain_modify, }, @@ -320,14 +324,12 @@ const struct frr_yang_module_info frr_ripd_info = { { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable", .cbs = { - .cli_show = cli_show_ip_rip_bfd_enable, .modify = lib_interface_rip_bfd_enable_modify, }, }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile", .cbs = { - .cli_show = cli_show_ip_rip_bfd_profile, .modify = lib_interface_rip_bfd_profile_modify, .destroy = lib_interface_rip_bfd_profile_destroy, }, diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h index 9929e09..ee592da 100644 --- a/ripd/rip_nb.h +++ b/ripd/rip_nb.h @@ -7,7 +7,10 @@ #ifndef _FRR_RIP_NB_H_ #define _FRR_RIP_NB_H_ +#include "northbound.h" + extern const struct frr_yang_module_info frr_ripd_info; +extern const struct frr_yang_module_info frr_ripd_cli_info; /* Mandatory callbacks. */ int ripd_instance_create(struct nb_cb_create_args *args); @@ -45,6 +48,8 @@ int ripd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args); int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args); int ripd_instance_non_passive_interface_destroy( struct nb_cb_destroy_args *args); +int ripd_instance_distribute_list_create(struct nb_cb_create_args *args); +int ripd_instance_distribute_list_destroy(struct nb_cb_destroy_args *args); int ripd_instance_redistribute_create(struct nb_cb_create_args *args); int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args); int ripd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args); @@ -168,6 +173,7 @@ void ripd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args); /* Optional 'cli_show' callbacks. */ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_end_router_rip(struct vty *vty, const struct lyd_node *dnode); void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_rip_default_information_originate(struct vty *vty, @@ -235,4 +241,6 @@ void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode, extern void ripd_notif_send_auth_type_failure(const char *ifname); extern void ripd_notif_send_auth_failure(const char *ifname); +extern void rip_cli_init(void); + #endif /* _FRR_RIP_NB_H_ */ diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c index 8d3b670..fb75337 100644 --- a/ripd/rip_nb_config.c +++ b/ripd/rip_nb_config.c @@ -35,7 +35,7 @@ int ripd_instance_create(struct nb_cb_create_args *args) const char *vrf_name; int socket; - vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + vrf_name = yang_dnode_get_string(args->dnode, "vrf"); vrf = vrf_lookup_by_name(vrf_name); /* @@ -189,7 +189,7 @@ int ripd_instance_distance_source_create(struct nb_cb_create_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - yang_dnode_get_ipv4p(&prefix, args->dnode, "./prefix"); + yang_dnode_get_ipv4p(&prefix, args->dnode, "prefix"); apply_mask_ipv4(&prefix); /* Get RIP distance node. */ @@ -395,7 +395,7 @@ int ripd_instance_offset_list_create(struct nb_cb_create_args *args) return NB_OK; rip = nb_running_get_entry(args->dnode, NULL, true); - ifname = yang_dnode_get_string(args->dnode, "./interface"); + ifname = yang_dnode_get_string(args->dnode, "interface"); offset = rip_offset_list_new(rip, ifname); nb_running_set_entry(args->dnode, offset); @@ -411,7 +411,7 @@ int ripd_instance_offset_list_destroy(struct nb_cb_destroy_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - direct = yang_dnode_get_enum(args->dnode, "./direction"); + direct = yang_dnode_get_enum(args->dnode, "direction"); offset = nb_running_unset_entry(args->dnode); if (offset->direct[direct].alist_name) { @@ -548,6 +548,23 @@ int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args) return rip_passive_nondefault_unset(rip, ifname); } + +/* + * XPath: /frr-ripd:ripd/instance/distribute-list + */ +int ripd_instance_distribute_list_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + group_distribute_list_create_helper(args, rip->distribute_ctx); + + return NB_OK; +} + /* * XPath: /frr-ripd:ripd/instance/redistribute */ @@ -560,7 +577,7 @@ int ripd_instance_redistribute_create(struct nb_cb_create_args *args) return NB_OK; rip = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); rip->redist[type].enabled = true; @@ -576,7 +593,7 @@ int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) return NB_OK; rip = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); rip->redist[type].enabled = false; if (rip->redist[type].route_map.name) { @@ -600,7 +617,7 @@ void ripd_instance_redistribute_apply_finish( int type; rip = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); if (rip->enabled) rip_redistribute_conf_update(rip, type); @@ -1123,12 +1140,12 @@ int lib_interface_rip_bfd_create(struct nb_cb_create_args *args) ifp = nb_running_get_entry(args->dnode, NULL, true); ri = ifp->info; - ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable"); + ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "enable"); XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile); - if (yang_dnode_exists(args->dnode, "./profile")) + if (yang_dnode_exists(args->dnode, "profile")) ri->bfd.profile = XSTRDUP( MTYPE_RIP_BFD_PROFILE, - yang_dnode_get_string(args->dnode, "./profile")); + yang_dnode_get_string(args->dnode, "profile")); rip_bfd_interface_update(ri); diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c index 3e8ddec..7e848be 100644 --- a/ripd/rip_peer.c +++ b/ripd/rip_peer.c @@ -12,6 +12,7 @@ #include "frrevent.h" #include "memory.h" #include "table.h" +#include "frrdistance.h" #include "ripd/ripd.h" #include "ripd/rip_bfd.h" diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index 2ae8857..be17277 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -531,7 +531,7 @@ static const struct route_map_rule_cmd route_set_tag_cmd = { /* Route-map init */ void rip_route_map_init(void) { - route_map_init(); + route_map_init_new(true); route_map_match_interface_hook(generic_match_add); route_map_no_match_interface_hook(generic_match_delete); diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 0e5d4d5..f6a7a82 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -195,7 +195,7 @@ static int rip_snmp_ifaddr_del(struct connected *ifc) if (!rn) return 0; i = rn->info; - if (!strncmp(i->name, ifp->name, INTERFACE_NAMSIZ)) { + if (!strncmp(i->name, ifp->name, IFNAMSIZ)) { rn->info = NULL; route_unlock_node(rn); route_unlock_node(rn); diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 36b58cb..ce94e8e 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -14,6 +14,8 @@ #include "log.h" #include "vrf.h" #include "bfd.h" +#include "frrdistance.h" + #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" diff --git a/ripd/ripd.c b/ripd/ripd.c index 7bfcaad..e3220a9 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -6,6 +6,11 @@ #include +#ifdef CRYPTO_OPENSSL +#include +#include +#endif + #include "vrf.h" #include "if.h" #include "command.h" @@ -29,8 +34,10 @@ #include "privs.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "mgmt_be_client.h" #include "network.h" #include "lib/printfrr.h" +#include "frrdistance.h" #include "ripd/ripd.h" #include "ripd/rip_nb.h" @@ -403,7 +410,6 @@ static int rip_filter(int rip_distribute, struct prefix_ipv4 *p, static int rip_nexthop_check(struct rip *rip, struct in_addr *addr) { struct interface *ifp; - struct listnode *cnode; struct connected *ifc; struct prefix *p; @@ -411,7 +417,7 @@ static int rip_nexthop_check(struct rip *rip, struct in_addr *addr) invalid nexthop. */ FOR_ALL_INTERFACES (rip->vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { p = ifc->address; if (p->family == AF_INET @@ -2212,8 +2218,8 @@ void rip_output_process(struct connected *ifc, struct sockaddr_in *to, } if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT) { - for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected, - listnode, tmp_ifc)) + frr_each (if_connected, ifc->ifp->connected, + tmp_ifc) if (prefix_match((struct prefix *)p, tmp_ifc->address)) { suppress = 1; @@ -2322,8 +2328,8 @@ void rip_output_process(struct connected *ifc, struct sockaddr_in *to, if (rinfo->metric_out != RIP_METRIC_INFINITY && rinfo->type == ZEBRA_ROUTE_CONNECT) { - for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected, - listnode, tmp_ifc)) + frr_each (if_connected, ifc->ifp->connected, + tmp_ifc) if (prefix_match((struct prefix *)p, tmp_ifc->address)) { rinfo->metric_out = @@ -2436,7 +2442,6 @@ static void rip_update_interface(struct connected *ifc, uint8_t version, /* Update send to all interface and neighbor. */ static void rip_update_process(struct rip *rip, int route_type) { - struct listnode *ifnode, *ifnnode; struct connected *connected; struct interface *ifp; struct rip_interface *ri; @@ -2475,8 +2480,7 @@ static void rip_update_process(struct rip *rip, int route_type) ifp->ifindex); /* send update on each connected network */ - for (ALL_LIST_ELEMENTS(ifp->connected, ifnode, ifnnode, - connected)) { + frr_each (if_connected, ifp->connected, connected) { if (connected->address->family == AF_INET) { if (vsend & RIPv1) rip_update_interface(connected, RIPv1, @@ -2767,7 +2771,6 @@ int rip_request_send(struct sockaddr_in *to, struct interface *ifp, { struct rte *rte; struct rip_packet rip_packet; - struct listnode *node, *nnode; memset(&rip_packet, 0, sizeof(rip_packet)); @@ -2791,7 +2794,7 @@ int rip_request_send(struct sockaddr_in *to, struct interface *ifp, } /* send request on each connected network */ - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix_ipv4 *p; p = (struct prefix_ipv4 *)connected->address; @@ -3251,45 +3254,38 @@ DEFUN (show_ip_rip_status, return CMD_SUCCESS; } -/* RIP configuration write function. */ -static int config_write_rip(struct vty *vty) -{ - struct rip *rip; - int write = 0; - - RB_FOREACH(rip, rip_instance_head, &rip_instances) { - char xpath[XPATH_MAXLEN]; - struct lyd_node *dnode; - - snprintf(xpath, sizeof(xpath), - "/frr-ripd:ripd/instance[vrf='%s']", rip->vrf_name); +#include "ripd/ripd_clippy.c" - dnode = yang_dnode_get(running_config->dnode, xpath); - assert(dnode); +/* + * XPath: /frr-ripd:clear-rip-route + */ +DEFPY_YANG (clear_ip_rip, + clear_ip_rip_cmd, + "clear ip rip [vrf WORD]", + CLEAR_STR + IP_STR + "Clear IP RIP database\n" + VRF_CMD_HELP_STR) +{ + struct list *input; + int ret; - nb_cli_show_dnode_cmds(vty, dnode, false); + input = list_new(); + if (vrf) { + struct yang_data *yang_vrf; - /* Distribute configuration. */ - config_write_distribute(vty, rip->distribute_ctx); + yang_vrf = yang_data_new("/frr-ripd:clear-rip-route/input/vrf", + vrf); + listnode_add(input, yang_vrf); + } - vty_out(vty, "exit\n"); + ret = nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", input, NULL); - write = 1; - } + list_delete(&input); - return write; + return ret; } -static int config_write_rip(struct vty *vty); -/* RIP node structure. */ -static struct cmd_node rip_node = { - .name = "rip", - .node = RIP_NODE, - .parent_node = CONFIG_NODE, - .prompt = "%s(config-router)# ", - .config_write = config_write_rip, -}; - /* Distribute-list update functions. */ static void rip_distribute_update(struct distribute_ctx *ctx, struct distribute *dist) @@ -3651,8 +3647,6 @@ static int rip_vrf_disable(struct vrf *vrf) void rip_vrf_init(void) { vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete); - - vrf_cmd_init(NULL); } void rip_vrf_terminate(void) @@ -3663,20 +3657,18 @@ void rip_vrf_terminate(void) /* Allocate new rip structure and set default value. */ void rip_init(void) { - /* Install top nodes. */ - install_node(&rip_node); - /* Install rip commands. */ install_element(VIEW_NODE, &show_ip_rip_cmd); install_element(VIEW_NODE, &show_ip_rip_status_cmd); - - install_default(RIP_NODE); + install_element(ENABLE_NODE, &clear_ip_rip_cmd); /* Debug related init. */ rip_debug_init(); + /* Enable mgmt be debug */ + mgmt_be_client_lib_vty_init(); /* Access list install. */ - access_list_init(); + access_list_init_new(true); access_list_add_hook(rip_distribute_update_all_wrapper); access_list_delete_hook(rip_distribute_update_all_wrapper); @@ -3690,6 +3682,4 @@ void rip_init(void) route_map_add_hook(rip_routemap_update); route_map_delete_hook(rip_routemap_update); - - if_rmap_init(RIP_NODE); } diff --git a/ripd/ripd.h b/ripd/ripd.h index ac4a51f..08f7a24 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -51,7 +51,6 @@ /* RIP port number. */ #define RIP_PORT_DEFAULT 520 -#define RIP_VTY_PORT 2602 /* Default configuration file name. */ #define RIPD_DEFAULT_CONFIG "ripd.conf" @@ -526,7 +525,6 @@ extern int offset_list_cmp(struct rip_offset_list *o1, extern void rip_vrf_init(void); extern void rip_vrf_terminate(void); -extern void rip_cli_init(void); extern struct zebra_privs_t ripd_privs; extern struct rip_instance_head rip_instances; diff --git a/ripd/subdir.am b/ripd/subdir.am index c793a6d..7fb3726 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -14,7 +14,6 @@ endif ripd_ripd_SOURCES = \ ripd/rip_bfd.c \ - ripd/rip_cli.c \ ripd/rip_debug.c \ ripd/rip_errors.c \ ripd/rip_interface.c \ @@ -34,6 +33,7 @@ ripd_ripd_SOURCES = \ clippy_scan += \ ripd/rip_bfd.c \ ripd/rip_cli.c \ + ripd/ripd.c \ # end noinst_HEADERS += \ diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c index 9a96e29..4806861 100644 --- a/ripngd/ripng_cli.c +++ b/ripngd/ripng_cli.c @@ -8,6 +8,7 @@ #include #include "if.h" +#include "if_rmap.h" #include "vrf.h" #include "log.h" #include "prefix.h" @@ -73,7 +74,7 @@ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode, { const char *vrf_name; - vrf_name = yang_dnode_get_string(dnode, "./vrf"); + vrf_name = yang_dnode_get_string(dnode, "vrf"); vty_out(vty, "!\n"); vty_out(vty, "router ripng"); @@ -82,6 +83,11 @@ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "\n"); } +void cli_show_end_router_ripng(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); +} + /* * XPath: /frr-ripngd:ripngd/instance/allow-ecmp */ @@ -199,8 +205,12 @@ DEFPY_YANG (ripng_network_prefix, "RIPng enable on specified interface or network.\n" "IPv6 network\n") { - nb_cli_enqueue_change(vty, "./network", - no ? NB_OP_DESTROY : NB_OP_CREATE, network_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./network[.='%s']", network_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -222,8 +232,12 @@ DEFPY_YANG (ripng_network_if, "RIPng enable on specified interface or network.\n" "Interface name\n") { - nb_cli_enqueue_change(vty, "./interface", - no ? NB_OP_DESTROY : NB_OP_CREATE, network); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./interface[.='%s']", network); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -267,12 +281,12 @@ void cli_show_ripng_offset_list(struct vty *vty, const struct lyd_node *dnode, { const char *interface; - interface = yang_dnode_get_string(dnode, "./interface"); + interface = yang_dnode_get_string(dnode, "interface"); vty_out(vty, " offset-list %s %s %s", - yang_dnode_get_string(dnode, "./access-list"), - yang_dnode_get_string(dnode, "./direction"), - yang_dnode_get_string(dnode, "./metric")); + yang_dnode_get_string(dnode, "access-list"), + yang_dnode_get_string(dnode, "direction"), + yang_dnode_get_string(dnode, "metric")); if (!strmatch(interface, "*")) vty_out(vty, " %s", interface); vty_out(vty, "\n"); @@ -288,8 +302,12 @@ DEFPY_YANG (ripng_passive_interface, "Suppress routing updates on an interface\n" "Interface name\n") { - nb_cli_enqueue_change(vty, "./passive-interface", - no ? NB_OP_DESTROY : NB_OP_CREATE, ifname); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./passive-interface[.='%s']", ifname); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + ifname); return nb_cli_apply_changes(vty, NULL); } @@ -335,13 +353,13 @@ void cli_show_ripng_redistribute(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " redistribute %s", - yang_dnode_get_string(dnode, "./protocol")); - if (yang_dnode_exists(dnode, "./metric")) + yang_dnode_get_string(dnode, "protocol")); + if (yang_dnode_exists(dnode, "metric")) vty_out(vty, " metric %s", - yang_dnode_get_string(dnode, "./metric")); - if (yang_dnode_exists(dnode, "./route-map")) + yang_dnode_get_string(dnode, "metric")); + if (yang_dnode_exists(dnode, "route-map")) vty_out(vty, " route-map %s", - yang_dnode_get_string(dnode, "./route-map")); + yang_dnode_get_string(dnode, "route-map")); vty_out(vty, "\n"); } @@ -355,8 +373,12 @@ DEFPY_YANG (ripng_route, "Static route setup\n" "Set static RIPng route announcement\n") { - nb_cli_enqueue_change(vty, "./static-route", - no ? NB_OP_DESTROY : NB_OP_CREATE, route_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./static-route[.='%s']", route_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -377,9 +399,13 @@ DEFPY_YANG (ripng_aggregate_address, "Set aggregate RIPng route announcement\n" "Aggregate network\n") { - nb_cli_enqueue_change(vty, "./aggregate-address", - no ? NB_OP_DESTROY : NB_OP_CREATE, - aggregate_address_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./aggregate-address[.='%s']", + aggregate_address_str); + + nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, + NULL); return nb_cli_apply_changes(vty, NULL); } @@ -435,9 +461,9 @@ void cli_show_ripng_timers(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { vty_out(vty, " timers basic %s %s %s\n", - yang_dnode_get_string(dnode, "./update-interval"), - yang_dnode_get_string(dnode, "./holddown-interval"), - yang_dnode_get_string(dnode, "./flush-interval")); + yang_dnode_get_string(dnode, "update-interval"), + yang_dnode_get_string(dnode, "holddown-interval"), + yang_dnode_get_string(dnode, "flush-interval")); } /* @@ -486,87 +512,139 @@ void cli_show_ipv6_ripng_split_horizon(struct vty *vty, } } -/* - * XPath: /frr-ripngd:clear-ripng-route - */ -DEFPY_YANG (clear_ipv6_rip, - clear_ipv6_rip_cmd, - "clear ipv6 ripng [vrf WORD]", - CLEAR_STR - IPV6_STR - "Clear IPv6 RIP database\n" - VRF_CMD_HELP_STR) +DEFPY_YANG( + ripng_ipv6_distribute_list, ripng_ipv6_distribute_list_cmd, + "ipv6 distribute-list ACCESSLIST6_NAME$name $dir [WORD$ifname]", + "IPv6\n" + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - struct list *input; - int ret; - - input = list_new(); - if (vrf) { - struct yang_data *yang_vrf; - - yang_vrf = yang_data_new( - "/frr-ripngd:clear-ripng-route/input/vrf", vrf); - listnode_add(input, yang_vrf); - } + char xpath[XPATH_MAXLEN]; - ret = nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", input, NULL); + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); +} - list_delete(&input); +DEFPY_YANG( + ripng_ipv6_distribute_list_prefix, ripng_ipv6_distribute_list_prefix_cmd, + "ipv6 distribute-list prefix PREFIXLIST6_NAME$name $dir [WORD$ifname]", + "IPv6\n" + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + char xpath[XPATH_MAXLEN]; - return ret; + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); */ + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, name); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (ripng_ipv6_distribute_list, - ripng_ipv6_distribute_list_cmd, - "ipv6 distribute-list [prefix] ACCESSLIST6_NAME [WORD]", - "IPv6\n" - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") +DEFPY_YANG(no_ripng_ipv6_distribute_list, + no_ripng_ipv6_distribute_list_cmd, + "no ipv6 distribute-list [ACCESSLIST6_NAME$name] $dir [WORD$ifname]", + NO_STR + "IPv6\n" + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - const char *ifname = NULL; - int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0; - - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; - return distribute_list_parser(prefix, false, argv[3 + prefix]->text, - argv[2 + prefix]->arg, ifname); + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/access-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (ripng_no_ipv6_distribute_list, - ripng_no_ipv6_distribute_list_cmd, - "no ipv6 distribute-list [prefix] ACCESSLIST6_NAME [WORD]", - NO_STR - "IPv6\n" - "Filter networks in routing updates\n" - "Specify a prefix\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") +DEFPY_YANG(no_ripng_ipv6_distribute_list_prefix, + no_ripng_ipv6_distribute_list_prefix_cmd, + "no ipv6 distribute-list prefix [PREFIXLIST6_NAME$name] $dir [WORD$ifname]", + NO_STR + "IPv6\n" + "Filter networks in routing updates\n" + "Specify a prefix list\n" + "Prefix-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") { - const char *ifname = NULL; - int prefix = (argv[3]->type == WORD_TKN) ? 1 : 0; - - if (argv[argc - 1]->type == VARIABLE_TKN) - ifname = argv[argc - 1]->arg; + const struct lyd_node *value_node; + char xpath[XPATH_MAXLEN]; - return distribute_list_no_parser(vty, prefix, false, - argv[4 + prefix]->text, - argv[3 + prefix]->arg, ifname); + snprintf(xpath, sizeof(xpath), + "./distribute-list[interface='%s']/%s/prefix-list", + ifname ? ifname : "", dir); + /* + * See if the user has specified specific list so check it exists. + * + * NOTE: Other FRR CLI commands do not do this sort of verification and + * there may be an official decision not to. + */ + if (name) { + value_node = yang_dnode_getf(vty->candidate_config->dnode, "%s/%s", + VTY_CURR_XPATH, xpath); + if (!value_node || strcmp(name, lyd_get_value(value_node))) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } +/* RIPng node structure. */ +static struct cmd_node cmd_ripng_node = { + .name = "ripng", + .node = RIPNG_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + void ripng_cli_init(void) { + /* Install RIPNG_NODE. */ + install_node(&cmd_ripng_node); + install_default(RIPNG_NODE); + install_element(CONFIG_NODE, &router_ripng_cmd); install_element(CONFIG_NODE, &no_router_ripng_cmd); install_element(RIPNG_NODE, &ripng_ipv6_distribute_list_cmd); - install_element(RIPNG_NODE, &ripng_no_ipv6_distribute_list_cmd); + install_element(RIPNG_NODE, &ripng_ipv6_distribute_list_prefix_cmd); + install_element(RIPNG_NODE, &no_ripng_ipv6_distribute_list_cmd); + install_element(RIPNG_NODE, &no_ripng_ipv6_distribute_list_prefix_cmd); install_element(RIPNG_NODE, &ripng_allow_ecmp_cmd); install_element(RIPNG_NODE, &no_ripng_allow_ecmp_cmd); @@ -585,5 +663,91 @@ void ripng_cli_init(void) install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd); - install_element(ENABLE_NODE, &clear_ipv6_rip_cmd); -} + if_rmap_init(RIPNG_NODE); +} + +/* clang-format off */ +const struct frr_yang_module_info frr_ripngd_cli_info = { + .name = "frr-ripngd", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-ripngd:ripngd/instance", + .cbs.cli_show = cli_show_router_ripng, + .cbs.cli_show_end = cli_show_end_router_ripng, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp", + .cbs.cli_show = cli_show_ripng_allow_ecmp, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/default-information-originate", + .cbs.cli_show = cli_show_ripng_default_information_originate, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/default-metric", + .cbs.cli_show = cli_show_ripng_default_metric, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/network", + .cbs.cli_show = cli_show_ripng_network_prefix, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/interface", + .cbs.cli_show = cli_show_ripng_network_interface, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/offset-list", + .cbs.cli_show = cli_show_ripng_offset_list, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/passive-interface", + .cbs.cli_show = cli_show_ripng_passive_interface, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/in/access-list", + .cbs.cli_show = group_distribute_list_ipv6_cli_show, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/out/access-list", + .cbs.cli_show = group_distribute_list_ipv6_cli_show, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/in/prefix-list", + .cbs.cli_show = group_distribute_list_ipv6_cli_show, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/out/prefix-list", + .cbs.cli_show = group_distribute_list_ipv6_cli_show, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/redistribute", + .cbs.cli_show = cli_show_ripng_redistribute, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map", + .cbs.cli_show = cli_show_if_route_map, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/static-route", + .cbs.cli_show = cli_show_ripng_route, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/aggregate-address", + .cbs.cli_show = cli_show_ripng_aggregate_address, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/timers", + .cbs.cli_show = cli_show_ripng_timers, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon", + .cbs = { + .cli_show = cli_show_ipv6_ripng_split_horizon, + }, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 4cb4bb5..9ef9f89 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -128,11 +128,10 @@ static int ripng_multicast_leave(struct interface *ifp, int sock) /* How many link local IPv6 address could be used on the interface ? */ static int ripng_if_ipv6_lladdress_check(struct interface *ifp) { - struct listnode *nn; struct connected *connected; int count = 0; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; p = connected->address; @@ -408,14 +407,13 @@ static int ripng_enable_network_lookup_if(struct interface *ifp) { struct ripng_interface *ri = ifp->info; struct ripng *ripng = ri->ripng; - struct listnode *node; struct connected *connected; struct prefix_ipv6 address; if (!ripng) return -1; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; struct agg_node *n; @@ -590,11 +588,10 @@ static void ripng_connect_set(struct interface *ifp, int set) { struct ripng_interface *ri = ifp->info; struct ripng *ripng = ri->ripng; - struct listnode *node, *nnode; struct connected *connected; struct prefix_ipv6 address; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { + frr_each (if_connected, ifp->connected, connected) { struct prefix *p; p = connected->address; @@ -876,7 +873,8 @@ void ripng_if_init(void) hook_register_prio(if_del, 0, ripng_if_delete_hook); /* Install interface node. */ - if_cmd_init_default(); - if_zapi_callbacks(ripng_ifp_create, ripng_ifp_up, - ripng_ifp_down, ripng_ifp_destroy); + hook_register_prio(if_real, 0, ripng_ifp_create); + hook_register_prio(if_up, 0, ripng_ifp_up); + hook_register_prio(if_down, 0, ripng_ifp_down); + hook_register_prio(if_unreal, 0, ripng_ifp_destroy); } diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 9933dae..ada9ad4 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -22,6 +22,7 @@ #include "if_rmap.h" #include "libfrr.h" #include "routemap.h" +#include "mgmt_be_client.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_nb.h" @@ -52,6 +53,8 @@ struct zebra_privs_t ripngd_privs = { /* Master of threads. */ struct event_loop *master; +struct mgmt_be_client *mgmt_be_client; + static struct frr_daemon_info ripngd_di; /* SIGHUP handler. */ @@ -66,11 +69,27 @@ static void sighup(void) /* SIGINT handler. */ static void sigint(void) { + struct vrf *vrf; + zlog_notice("Terminating on signal"); + nb_oper_cancel_all_walks(); + mgmt_be_client_destroy(mgmt_be_client); + mgmt_be_client = NULL; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (!vrf->info) + continue; + + ripng_clean(vrf->info); + } + ripng_vrf_terminate(); if_rmap_terminate(); ripng_zebra_stop(); + + route_map_finish(); + frr_fini(); exit(0); } @@ -108,18 +127,23 @@ static const struct frr_yang_module_info *const ripngd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(ripngd, RIPNG, + .vty_port = RIPNG_VTY_PORT, + .proghelp = "Implementation of the RIPng routing protocol.", - .proghelp = "Implementation of the RIPng routing protocol.", + .signals = ripng_signals, + .n_signals = array_size(ripng_signals), - .signals = ripng_signals, - .n_signals = array_size(ripng_signals), + .privs = &ripngd_privs, - .privs = &ripngd_privs, + .yang_modules = ripngd_yang_modules, + .n_yang_modules = array_size(ripngd_yang_modules), - .yang_modules = ripngd_yang_modules, - .n_yang_modules = array_size(ripngd_yang_modules), + /* mgmtd will load the per-daemon config file now */ + .flags = FRR_NO_SPLIT_CONFIG, ); +/* clang-format on */ #define DEPRECATED_OPTIONS "" @@ -160,7 +184,9 @@ int main(int argc, char **argv) /* RIPngd inits. */ ripng_init(); - ripng_cli_init(); + + mgmt_be_client = mgmt_be_client_create("ripngd", NULL, 0, master); + zebra_init(master); frr_config_fork(); diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c index 1c6d719..8e20541 100644 --- a/ripngd/ripng_nb.c +++ b/ripngd/ripng_nb.c @@ -6,11 +6,12 @@ #include -#include "northbound.h" +#include "distribute.h" +#include "if_rmap.h" #include "libfrr.h" +#include "northbound.h" #include "ripngd/ripng_nb.h" -#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripngd_info = { @@ -19,7 +20,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance", .cbs = { - .cli_show = cli_show_router_ripng, .create = ripngd_instance_create, .destroy = ripngd_instance_destroy, .get_keys = ripngd_instance_get_keys, @@ -30,28 +30,24 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp", .cbs = { - .cli_show = cli_show_ripng_allow_ecmp, .modify = ripngd_instance_allow_ecmp_modify, }, }, { .xpath = "/frr-ripngd:ripngd/instance/default-information-originate", .cbs = { - .cli_show = cli_show_ripng_default_information_originate, .modify = ripngd_instance_default_information_originate_modify, }, }, { .xpath = "/frr-ripngd:ripngd/instance/default-metric", .cbs = { - .cli_show = cli_show_ripng_default_metric, .modify = ripngd_instance_default_metric_modify, }, }, { .xpath = "/frr-ripngd:ripngd/instance/network", .cbs = { - .cli_show = cli_show_ripng_network_prefix, .create = ripngd_instance_network_create, .destroy = ripngd_instance_network_destroy, }, @@ -59,7 +55,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/interface", .cbs = { - .cli_show = cli_show_ripng_network_interface, .create = ripngd_instance_interface_create, .destroy = ripngd_instance_interface_destroy, }, @@ -67,7 +62,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/offset-list", .cbs = { - .cli_show = cli_show_ripng_offset_list, .create = ripngd_instance_offset_list_create, .destroy = ripngd_instance_offset_list_destroy, }, @@ -87,16 +81,49 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/passive-interface", .cbs = { - .cli_show = cli_show_ripng_passive_interface, .create = ripngd_instance_passive_interface_create, .destroy = ripngd_instance_passive_interface_destroy, }, }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list", + .cbs = { + .create = ripngd_instance_distribute_list_create, + .destroy = group_distribute_list_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/in/access-list", + .cbs = { + .modify = group_distribute_list_ipv6_modify, + .destroy = group_distribute_list_ipv6_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/out/access-list", + .cbs = { + .modify = group_distribute_list_ipv6_modify, + .destroy = group_distribute_list_ipv6_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/in/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv6_modify, + .destroy = group_distribute_list_ipv6_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/distribute-list/out/prefix-list", + .cbs = { + .modify = group_distribute_list_ipv6_modify, + .destroy = group_distribute_list_ipv6_destroy, + } + }, { .xpath = "/frr-ripngd:ripngd/instance/redistribute", .cbs = { .apply_finish = ripngd_instance_redistribute_apply_finish, - .cli_show = cli_show_ripng_redistribute, .create = ripngd_instance_redistribute_create, .destroy = ripngd_instance_redistribute_destroy, }, @@ -120,7 +147,6 @@ const struct frr_yang_module_info frr_ripngd_info = { .cbs = { .create = ripngd_instance_if_route_maps_if_route_map_create, .destroy = ripngd_instance_if_route_maps_if_route_map_destroy, - .cli_show = cli_show_if_route_map, } }, { @@ -140,7 +166,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/static-route", .cbs = { - .cli_show = cli_show_ripng_route, .create = ripngd_instance_static_route_create, .destroy = ripngd_instance_static_route_destroy, }, @@ -148,7 +173,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-ripngd:ripngd/instance/aggregate-address", .cbs = { - .cli_show = cli_show_ripng_aggregate_address, .create = ripngd_instance_aggregate_address_create, .destroy = ripngd_instance_aggregate_address_destroy, }, @@ -157,7 +181,6 @@ const struct frr_yang_module_info frr_ripngd_info = { .xpath = "/frr-ripngd:ripngd/instance/timers", .cbs = { .apply_finish = ripngd_instance_timers_apply_finish, - .cli_show = cli_show_ripng_timers, }, }, { @@ -251,7 +274,6 @@ const struct frr_yang_module_info frr_ripngd_info = { { .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon", .cbs = { - .cli_show = cli_show_ipv6_ripng_split_horizon, .modify = lib_interface_ripng_split_horizon_modify, }, }, diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h index 1c0e63c..790a50f 100644 --- a/ripngd/ripng_nb.h +++ b/ripngd/ripng_nb.h @@ -7,7 +7,10 @@ #ifndef _FRR_RIPNG_NB_H_ #define _FRR_RIPNG_NB_H_ +#include "northbound.h" + extern const struct frr_yang_module_info frr_ripngd_info; +extern const struct frr_yang_module_info frr_ripngd_cli_info; /* Mandatory callbacks. */ int ripngd_instance_create(struct nb_cb_create_args *args); @@ -30,6 +33,8 @@ int ripngd_instance_offset_list_access_list_modify( int ripngd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args); int ripngd_instance_passive_interface_create(struct nb_cb_create_args *args); int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_distribute_list_create(struct nb_cb_create_args *args); +int ripngd_instance_distribute_list_destroy(struct nb_cb_destroy_args *args); int ripngd_instance_redistribute_create(struct nb_cb_create_args *args); int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args); int ripngd_instance_redistribute_route_map_modify( @@ -102,6 +107,7 @@ void ripngd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args); /* Optional 'cli_show' callbacks. */ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_end_router_ripng(struct vty *vty, const struct lyd_node *dnode); void cli_show_ripng_allow_ecmp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_ripng_default_information_originate(struct vty *vty, @@ -134,4 +140,6 @@ void cli_show_ipv6_ripng_split_horizon(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +extern void ripng_cli_init(void); + #endif /* _FRR_RIPNG_NB_H_ */ diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c index 0b1bd68..d05d91c 100644 --- a/ripngd/ripng_nb_config.c +++ b/ripngd/ripng_nb_config.c @@ -35,7 +35,7 @@ int ripngd_instance_create(struct nb_cb_create_args *args) const char *vrf_name; int socket; - vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + vrf_name = yang_dnode_get_string(args->dnode, "vrf"); vrf = vrf_lookup_by_name(vrf_name); /* @@ -262,7 +262,7 @@ int ripngd_instance_offset_list_create(struct nb_cb_create_args *args) return NB_OK; ripng = nb_running_get_entry(args->dnode, NULL, true); - ifname = yang_dnode_get_string(args->dnode, "./interface"); + ifname = yang_dnode_get_string(args->dnode, "interface"); offset = ripng_offset_list_new(ripng, ifname); nb_running_set_entry(args->dnode, offset); @@ -278,7 +278,7 @@ int ripngd_instance_offset_list_destroy(struct nb_cb_destroy_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - direct = yang_dnode_get_enum(args->dnode, "./direction"); + direct = yang_dnode_get_enum(args->dnode, "direction"); offset = nb_running_unset_entry(args->dnode); if (offset->direct[direct].alist_name) { @@ -368,6 +368,22 @@ int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args) return ripng_passive_interface_unset(ripng, ifname); } +/* + * XPath: /frr-ripng:ripng/instance/distribute-list + */ +int ripngd_instance_distribute_list_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + group_distribute_list_create_helper(args, ripng->distribute_ctx); + + return NB_OK; +} + /* * XPath: /frr-ripngd:ripngd/instance/redistribute */ @@ -380,7 +396,7 @@ int ripngd_instance_redistribute_create(struct nb_cb_create_args *args) return NB_OK; ripng = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); ripng->redist[type].enabled = true; @@ -396,7 +412,7 @@ int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) return NB_OK; ripng = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); ripng->redist[type].enabled = false; if (ripng->redist[type].route_map.name) { @@ -420,7 +436,7 @@ void ripngd_instance_redistribute_apply_finish( int type; ripng = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_enum(args->dnode, "./protocol"); + type = yang_dnode_get_enum(args->dnode, "protocol"); if (ripng->enabled) ripng_redistribute_conf_update(ripng, type); diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index 901b548..247bac4 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -15,6 +15,7 @@ #include "linklist.h" #include "frrevent.h" #include "memory.h" +#include "frrdistance.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_nexthop.h" diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index b5f74be..3370546 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -386,7 +386,7 @@ static const struct route_map_rule_cmd route_set_tag_cmd = { void ripng_route_map_init(void) { - route_map_init(); + route_map_init_new(true); route_map_match_interface_hook(generic_match_add); route_map_no_match_interface_hook(generic_match_delete); diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 465b40b..80b9013 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -23,6 +23,7 @@ #include "lib_errors.h" #include "northbound_cli.h" #include "network.h" +#include "mgmt_be_client.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_route.h" @@ -392,11 +393,10 @@ static void ripng_nexthop_rte(struct rte *rte, struct sockaddr_in6 *from, /* If ifp has same link-local address then return 1. */ static int ripng_lladdr_check(struct interface *ifp, struct in6_addr *addr) { - struct listnode *node; struct connected *connected; struct prefix *p; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { p = connected->address; if (p->family == AF_INET6 @@ -2231,6 +2231,38 @@ DEFUN (show_ipv6_ripng_status, return CMD_SUCCESS; } +#include "ripngd/ripngd_clippy.c" + +/* + * XPath: /frr-ripngd:clear-ripng-route + */ +DEFPY_YANG (clear_ipv6_rip, + clear_ipv6_rip_cmd, + "clear ipv6 ripng [vrf WORD]", + CLEAR_STR + IPV6_STR + "Clear IPv6 RIP database\n" + VRF_CMD_HELP_STR) +{ + struct list *input; + int ret; + + input = list_new(); + if (vrf) { + struct yang_data *yang_vrf; + + yang_vrf = yang_data_new( + "/frr-ripngd:clear-ripng-route/input/vrf", vrf); + listnode_add(input, yang_vrf); + } + + ret = nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", input, NULL); + + list_delete(&input); + + return ret; +} + /* Update ECMP routes to zebra when ECMP is disabled. */ void ripng_ecmp_disable(struct ripng *ripng) { @@ -2268,45 +2300,6 @@ void ripng_ecmp_disable(struct ripng *ripng) } } -/* RIPng configuration write function. */ -static int ripng_config_write(struct vty *vty) -{ - struct ripng *ripng; - int write = 0; - - RB_FOREACH(ripng, ripng_instance_head, &ripng_instances) { - char xpath[XPATH_MAXLEN]; - struct lyd_node *dnode; - - snprintf(xpath, sizeof(xpath), - "/frr-ripngd:ripngd/instance[vrf='%s']", - ripng->vrf_name); - - dnode = yang_dnode_get(running_config->dnode, xpath); - assert(dnode); - - nb_cli_show_dnode_cmds(vty, dnode, false); - - config_write_distribute(vty, ripng->distribute_ctx); - - vty_out(vty, "exit\n"); - - write = 1; - } - - return write; -} - -static int ripng_config_write(struct vty *vty); -/* RIPng node structure. */ -static struct cmd_node cmd_ripng_node = { - .name = "ripng", - .node = RIPNG_NODE, - .parent_node = CONFIG_NODE, - .prompt = "%s(config-router)# ", - .config_write = ripng_config_write, -}; - static void ripng_distribute_update(struct distribute_ctx *ctx, struct distribute *dist) { @@ -2674,8 +2667,6 @@ void ripng_vrf_init(void) { vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable, ripng_vrf_delete); - - vrf_cmd_init(NULL); } void ripng_vrf_terminate(void) @@ -2686,20 +2677,19 @@ void ripng_vrf_terminate(void) /* Initialize ripng structure and set commands. */ void ripng_init(void) { - /* Install RIPNG_NODE. */ - install_node(&cmd_ripng_node); - /* Install ripng commands. */ install_element(VIEW_NODE, &show_ipv6_ripng_cmd); install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd); - - install_default(RIPNG_NODE); + install_element(ENABLE_NODE, &clear_ipv6_rip_cmd); ripng_if_init(); ripng_debug_init(); + /* Enable mgmt be debug */ + mgmt_be_client_lib_vty_init(); + /* Access list install. */ - access_list_init(); + access_list_init_new(true); access_list_add_hook(ripng_distribute_update_all_wrapper); access_list_delete_hook(ripng_distribute_update_all_wrapper); @@ -2713,6 +2703,4 @@ void ripng_init(void) route_map_add_hook(ripng_routemap_update); route_map_delete_hook(ripng_routemap_update); - - if_rmap_init(RIPNG_NODE); } diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index 3a2bc0c..a316f2b 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -16,7 +16,6 @@ /* RIPng version and port number. */ #define RIPNG_V1 1 #define RIPNG_PORT_DEFAULT 521 -#define RIPNG_VTY_PORT 2603 #define RIPNG_MAX_PACKET_SIZE 1500 #define RIPNG_PRIORITY_DEFAULT 0 @@ -432,7 +431,6 @@ extern void ripng_ecmp_change(struct ripng *ripng); extern void ripng_vrf_init(void); extern void ripng_vrf_terminate(void); -extern void ripng_cli_init(void); extern uint32_t zebra_ecmp_count; diff --git a/ripngd/subdir.am b/ripngd/subdir.am index 162426c..a881144 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -9,7 +9,6 @@ man8 += $(MANBUILD)/frr-ripngd.8 endif ripngd_ripngd_SOURCES = \ - ripngd/ripng_cli.c \ ripngd/ripng_debug.c \ ripngd/ripng_interface.c \ ripngd/ripng_nexthop.c \ @@ -28,6 +27,7 @@ ripngd_ripngd_SOURCES = \ clippy_scan += \ ripngd/ripng_cli.c \ + ripngd/ripngd.c \ # end noinst_HEADERS += \ diff --git a/sharpd/sharp_logpump.c b/sharpd/sharp_logpump.c index 5474e80..d02921f 100644 --- a/sharpd/sharp_logpump.c +++ b/sharpd/sharp_logpump.c @@ -5,6 +5,7 @@ */ #include +#include #include "vty.h" #include "command.h" diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index fa85c2b..f4ce147 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -54,6 +54,23 @@ struct zebra_privs_t sharp_privs = { struct option longopts[] = {{0}}; +struct sharp_global sg; + +static void sharp_global_init(void) +{ + memset(&sg, 0, sizeof(sg)); + sg.nhs = list_new(); + sg.nhs->del = (void (*)(void *))sharp_nh_tracker_free; + sg.ted = NULL; + sg.srv6_locators = list_new(); +} + +static void sharp_global_destroy(void) +{ + list_delete(&sg.nhs); + list_delete(&sg.srv6_locators); +} + /* Master of threads. */ struct event_loop *master; @@ -68,6 +85,11 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + vrf_terminate(); + sharp_zebra_terminate(); + + sharp_global_destroy(); + frr_fini(); exit(0); @@ -98,8 +120,6 @@ struct frr_signal_t sharp_signals[] = { }, }; -#define SHARP_VTY_PORT 2614 - static const struct frr_yang_module_info *const sharpd_yang_modules[] = { &frr_filter_info, &frr_interface_info, @@ -107,26 +127,20 @@ static const struct frr_yang_module_info *const sharpd_yang_modules[] = { &frr_vrf_info, }; -FRR_DAEMON_INFO(sharpd, SHARP, .vty_port = SHARP_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(sharpd, SHARP, + .vty_port = SHARP_VTY_PORT, + .proghelp = "Implementation of a Sharp of routes daemon.", - .proghelp = "Implementation of a Sharp of routes daemon.", + .signals = sharp_signals, + .n_signals = array_size(sharp_signals), - .signals = sharp_signals, - .n_signals = array_size(sharp_signals), + .privs = &sharp_privs, - .privs = &sharp_privs, .yang_modules = sharpd_yang_modules, - .n_yang_modules = array_size(sharpd_yang_modules), + .yang_modules = sharpd_yang_modules, + .n_yang_modules = array_size(sharpd_yang_modules), ); - -struct sharp_global sg; - -static void sharp_global_init(void) -{ - memset(&sg, 0, sizeof(sg)); - sg.nhs = list_new(); - sg.ted = NULL; - sg.srv6_locators = list_new(); -} +/* clang-format on */ static void sharp_start_configuration(void) { diff --git a/sharpd/sharp_nht.c b/sharpd/sharp_nht.c index fa78805..6d64fcf 100644 --- a/sharpd/sharp_nht.c +++ b/sharpd/sharp_nht.c @@ -40,6 +40,11 @@ struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p) return nht; } +void sharp_nh_tracker_free(struct sharp_nh_tracker *nht) +{ + XFREE(MTYPE_NH_TRACKER, nht); +} + void sharp_nh_tracker_dump(struct vty *vty) { struct listnode *node; @@ -169,7 +174,8 @@ static void sharp_nhgroup_delete_cb(const char *name) if (!snhg) return; - nhg_del(snhg->id); + if (sharp_nhgroup_id_is_installed(snhg->id)) + nhg_del(snhg->id); sharp_nhg_rb_del(&nhg_head, snhg); XFREE(MTYPE_NHG, snhg); } diff --git a/sharpd/sharp_nht.h b/sharpd/sharp_nht.h index 5523f28..b27952a 100644 --- a/sharpd/sharp_nht.h +++ b/sharpd/sharp_nht.h @@ -18,6 +18,7 @@ struct sharp_nh_tracker { }; extern struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p); +extern void sharp_nh_tracker_free(struct sharp_nh_tracker *nht); extern void sharp_nh_tracker_dump(struct vty *vty); diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index cf79bac..07050ab 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -27,8 +27,35 @@ DEFINE_MTYPE_STATIC(SHARPD, SRV6_LOCATOR, "SRv6 Locator"); +DEFPY(watch_neighbor, watch_neighbor_cmd, + "sharp watch [vrf NAME$vrf_name] neighbor", + "Sharp routing Protocol\n" + "Watch for changes\n" + "The vrf we would like to watch if non-default\n" + "The NAME of the vrf\n" + "Neighbor events\n") +{ + struct vrf *vrf; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); + + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + sharp_zebra_register_neigh(vrf->vrf_id, AFI_IP, true); + + return CMD_SUCCESS; +} + + DEFPY(watch_redistribute, watch_redistribute_cmd, - "sharp watch [vrf NAME$vrf_name] redistribute " FRR_REDIST_STR_SHARPD, + "[no] sharp watch [vrf NAME$vrf_name] redistribute " FRR_REDIST_STR_SHARPD, + NO_STR "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" @@ -49,7 +76,7 @@ DEFPY(watch_redistribute, watch_redistribute_cmd, } source = proto_redistnum(AFI_IP, argv[argc-1]->text); - sharp_redistribute_vrf(vrf, source); + sharp_redistribute_vrf(vrf, source, !no); return CMD_SUCCESS; } @@ -1419,6 +1446,7 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &remove_routes_cmd); install_element(ENABLE_NODE, &vrf_label_cmd); install_element(ENABLE_NODE, &sharp_nht_data_dump_cmd); + install_element(ENABLE_NODE, &watch_neighbor_cmd); install_element(ENABLE_NODE, &watch_redistribute_cmd); install_element(ENABLE_NODE, &watch_nexthop_v6_cmd); install_element(ENABLE_NODE, &watch_nexthop_v4_cmd); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index c095fec..133da91 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -251,8 +251,7 @@ static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance, /* Only send via ID if nhgroup has been successfully installed */ if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) { - SET_FLAG(api.message, ZAPI_MESSAGE_NHG); - api.nhgid = nhgid; + zapi_route_set_nhg_id(&api, &nhgid); } else { for (ALL_NEXTHOPS_PTR(nhg, nh)) { /* Check if we set a VNI label */ @@ -512,6 +511,7 @@ static int route_notify_owner(ZAPI_CALLBACK_ARGS) static void zebra_connected(struct zclient *zclient) { + zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true); zclient_send_reg_requests(zclient, VRF_DEFAULT); /* @@ -562,6 +562,12 @@ void nhg_add(uint32_t id, const struct nexthop_group *nhg, } if (api_nhg.nexthop_num == 0) { + if (sharp_nhgroup_id_is_installed(id)) { + zlog_debug("%s: nhg %u: no nexthops, deleting nexthop group", __func__, + id); + zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg); + return; + } zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__, id); is_valid = false; @@ -666,27 +672,20 @@ static int sharp_debug_nexthops(struct zapi_route *api) return i; } -static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) + +static void sharp_nexthop_update(struct vrf *vrf, struct prefix *matched, + struct zapi_route *nhr) { struct sharp_nh_tracker *nht; - struct zapi_route nhr; - struct prefix matched; - - if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { - zlog_err("%s: Decode of update failed", __func__); - return 0; - } zlog_debug("Received update for %pFX actual match: %pFX metric: %u", - &matched, &nhr.prefix, nhr.metric); + matched, &nhr->prefix, nhr->metric); - nht = sharp_nh_tracker_get(&matched); - nht->nhop_num = nhr.nexthop_num; + nht = sharp_nh_tracker_get(matched); + nht->nhop_num = nhr->nexthop_num; nht->updates++; - sharp_debug_nexthops(&nhr); - - return 0; + sharp_debug_nexthops(nhr); } static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS) @@ -705,10 +704,11 @@ static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS) return 0; } -void sharp_redistribute_vrf(struct vrf *vrf, int type) +void sharp_redistribute_vrf(struct vrf *vrf, int type, bool turn_on) { - zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, - 0, vrf->vrf_id); + zebra_redistribute_send(turn_on ? ZEBRA_REDISTRIBUTE_ADD + : ZEBRA_REDISTRIBUTE_DELETE, + zclient, AFI_IP, type, 0, vrf->vrf_id); } static zclient_handler *const sharp_opaque_handlers[] = { @@ -934,6 +934,7 @@ static int nhg_notify_owner(ZAPI_CALLBACK_ARGS) zlog_debug("Failed install of nhg %u", id); break; case ZAPI_NHG_REMOVED: + sharp_nhgroup_id_set_installed(id, false); zlog_debug("Removed nhg %u", id); break; case ZAPI_NHG_REMOVE_FAIL: @@ -989,6 +990,41 @@ static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) return 0; } +static int sharp_zebra_process_neigh(ZAPI_CALLBACK_ARGS) +{ + union sockunion addr = {}, lladdr = {}; + struct zapi_neigh_ip api = {}; + struct interface *ifp; + + zlog_debug("Received a neighbor event"); + zclient_neigh_ip_decode(zclient->ibuf, &api); + + if (api.ip_in.ipa_type == AF_UNSPEC) + return 0; + + sockunion_family(&addr) = api.ip_in.ipa_type; + memcpy((uint8_t *)sockunion_get_addr(&addr), &api.ip_in.ip.addr, + family2addrsize(api.ip_in.ipa_type)); + + sockunion_family(&lladdr) = api.ip_out.ipa_type; + if (api.ip_out.ipa_type != AF_UNSPEC) + memcpy((uint8_t *)sockunion_get_addr(&lladdr), + &api.ip_out.ip.addr, + family2addrsize(api.ip_out.ipa_type)); + ifp = if_lookup_by_index(api.index, vrf_id); + if (!ifp) { + zlog_debug("Failed to lookup interface for neighbor entry: %u for %u", + api.index, vrf_id); + return 0; + } + + zlog_debug("Received: %s %pSU dev %s lladr %pSU", + (cmd == ZEBRA_NEIGH_ADDED) ? "NEW" : "DEL", &addr, ifp->name, + &lladdr); + + return 0; +} + int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down) { zlog_debug("Sending zebra to set %s protodown %s", ifp->name, @@ -1059,11 +1095,16 @@ int sharp_zebra_send_tc_filter_rate(struct interface *ifp, return 0; } +void sharp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg) +{ + zclient_register_neigh(zclient, vrf_id, afi, reg); +} + + static zclient_handler *const sharp_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, - [ZEBRA_NEXTHOP_UPDATE] = sharp_nexthop_update, [ZEBRA_NHG_NOTIFY_OWNER] = nhg_notify_owner, [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route, @@ -1071,19 +1112,36 @@ static zclient_handler *const sharp_handlers[] = { [ZEBRA_OPAQUE_NOTIFY] = sharp_opq_notify_handler, [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = sharp_zebra_process_srv6_locator_chunk, + [ZEBRA_NEIGH_ADDED] = sharp_zebra_process_neigh, + [ZEBRA_NEIGH_REMOVED] = sharp_zebra_process_neigh, }; void sharp_zebra_init(void) { - struct zclient_options opt = {.receive_notify = true}; + hook_register_prio(if_real, 0, sharp_ifp_create); + hook_register_prio(if_up, 0, sharp_ifp_up); + hook_register_prio(if_down, 0, sharp_ifp_down); + hook_register_prio(if_unreal, 0, sharp_ifp_destroy); - if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, sharp_ifp_down, - sharp_ifp_destroy); - - zclient = zclient_new(master, &opt, sharp_handlers, + zclient = zclient_new(master, &zclient_options_default, sharp_handlers, array_size(sharp_handlers)); zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs); zclient->zebra_connected = zebra_connected; zclient->zebra_buffer_write_ready = sharp_zclient_buffer_ready; + zclient->nexthop_update = sharp_nexthop_update; +} + +void sharp_zebra_terminate(void) +{ + struct sharp_zclient *node = sharp_clients_head; + + while (node) { + sharp_zclient_delete(node->client->session_id); + + node = sharp_clients_head; + } + + zclient_stop(zclient); + zclient_free(zclient); } diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 025b4d8..5cbcc14 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -8,6 +8,7 @@ #define __SHARP_ZEBRA_H__ extern void sharp_zebra_init(void); +extern void sharp_zebra_terminate(void); /* Add and delete extra zapi client sessions, for testing */ int sharp_zclient_create(uint32_t session_id); @@ -54,7 +55,7 @@ extern void sharp_zebra_send_arp(const struct interface *ifp, /* Register Link State Opaque messages */ extern void sharp_zebra_register_te(void); -extern void sharp_redistribute_vrf(struct vrf *vrf, int source); +extern void sharp_redistribute_vrf(struct vrf *vrf, int source, bool turn_on); extern int sharp_zebra_srv6_manager_get_locator_chunk(const char *lname); extern int sharp_zebra_srv6_manager_release_locator_chunk(const char *lname); @@ -70,4 +71,6 @@ extern int sharp_zebra_send_tc_filter_rate(struct interface *ifp, const struct prefix *destination, uint8_t ip_proto, uint16_t src_port, uint16_t dst_port, uint64_t rate); + +extern void sharp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg); #endif diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index 607cbc7..cef02c3 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -369,7 +369,6 @@ parts: - --enable-ospfapi=yes - --enable-multipath=64 - --enable-rtadv - - --enable-irdp - --enable-user=root - --enable-group=root - --enable-pimd @@ -381,10 +380,10 @@ parts: - --enable-vrrpd - --enable-configfile-mask=0640 - --enable-logfile-mask=0640 - - --localstatedir=/var/run + - --sysconfdir=/etc + - --localstatedir=/var - --sbindir=/sbin - --bindir=/bin - - --sysconfdir=/etc/frr - --with-pkg-extra-version=@PACKAGE_EXTRAVERSION@ frr-defaults: plugin: dump diff --git a/staticd/static_bfd.c b/staticd/static_bfd.c index 507c64e..c35751f 100644 --- a/staticd/static_bfd.c +++ b/staticd/static_bfd.c @@ -91,11 +91,11 @@ void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn, struct vrf *vrf = NULL; use_interface = false; - use_source = yang_dnode_exists(dnode, "./source"); - use_profile = yang_dnode_exists(dnode, "./profile"); + use_source = yang_dnode_exists(dnode, "source"); + use_profile = yang_dnode_exists(dnode, "profile"); onlink = yang_dnode_exists(dnode, "../onlink") && yang_dnode_get_bool(dnode, "../onlink"); - mhop = yang_dnode_get_bool(dnode, "./multi-hop"); + mhop = yang_dnode_get_bool(dnode, "multi-hop"); vrf = vrf_lookup_by_name(yang_dnode_get_string(dnode, "../vrf")); family = static_next_hop_type_to_family(sn); @@ -112,7 +112,7 @@ void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn, /* Configure the session. */ if (use_source) - yang_dnode_get_ip(&source, dnode, "./source"); + yang_dnode_get_ip(&source, dnode, "source"); if (onlink || mhop == false) bfd_sess_set_auto_source(sn->bsp, false); @@ -263,14 +263,13 @@ static void static_bfd_show_path_json(struct vty *vty, struct json_object *jo, static void static_bfd_show_json(struct vty *vty) { struct json_object *jo, *jo_path, *jo_afi_safi; - struct vrf *vrf; + struct static_vrf *svrf; jo = json_object_new_object(); jo_path = json_object_new_object(); json_object_object_add(jo, "path-list", jo_path); - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - const struct static_vrf *svrf = vrf->info; + RB_FOREACH (svrf, svrf_name_head, &svrfs) { struct route_table *rt; jo_afi_safi = json_object_new_array(); @@ -346,7 +345,7 @@ static void static_bfd_show_path(struct vty *vty, struct route_table *rt) void static_bfd_show(struct vty *vty, bool json) { - struct vrf *vrf; + struct static_vrf *svrf; if (json) { static_bfd_show_json(vty); @@ -355,21 +354,20 @@ void static_bfd_show(struct vty *vty, bool json) vty_out(vty, "Showing BFD monitored static routes:\n"); vty_out(vty, "\n Next hops:\n"); - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - const struct static_vrf *svrf = vrf->info; + RB_FOREACH (svrf, svrf_name_head, &svrfs) { struct route_table *rt; - vty_out(vty, " VRF %s IPv4 Unicast:\n", vrf->name); + vty_out(vty, " VRF %s IPv4 Unicast:\n", svrf->name); rt = svrf->stable[AFI_IP][SAFI_UNICAST]; if (rt) static_bfd_show_path(vty, rt); - vty_out(vty, "\n VRF %s IPv4 Multicast:\n", vrf->name); + vty_out(vty, "\n VRF %s IPv4 Multicast:\n", svrf->name); rt = svrf->stable[AFI_IP][SAFI_MULTICAST]; if (rt) static_bfd_show_path(vty, rt); - vty_out(vty, "\n VRF %s IPv6 Unicast:\n", vrf->name); + vty_out(vty, "\n VRF %s IPv6 Unicast:\n", svrf->name); rt = svrf->stable[AFI_IP6][SAFI_UNICAST]; if (rt) static_bfd_show_path(vty, rt); diff --git a/staticd/static_main.c b/staticd/static_main.c index 165fb4d..9468a98 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -53,7 +53,7 @@ struct option longopts[] = { { 0 } }; /* Master of threads. */ struct event_loop *master; -struct mgmt_be_client *mgmt_be_client; +static struct mgmt_be_client *mgmt_be_client; static struct frr_daemon_info staticd_di; @@ -113,23 +113,26 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = { &frr_staticd_info, }; -#define STATIC_VTY_PORT 2616 - /* * NOTE: .flags == FRR_NO_SPLIT_CONFIG to avoid reading split config, mgmtd will * do this for us now */ -FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(staticd, STATIC, + .vty_port = STATIC_VTY_PORT, + .proghelp = "Implementation of STATIC.", - .proghelp = "Implementation of STATIC.", + .signals = static_signals, + .n_signals = array_size(static_signals), - .signals = static_signals, - .n_signals = array_size(static_signals), + .privs = &static_privs, - .privs = &static_privs, .yang_modules = staticd_yang_modules, - .n_yang_modules = array_size(staticd_yang_modules), + .yang_modules = staticd_yang_modules, + .n_yang_modules = array_size(staticd_yang_modules), - .flags = FRR_NO_SPLIT_CONFIG); + .flags = FRR_NO_SPLIT_CONFIG, +); +/* clang-format on */ int main(int argc, char **argv, char **envp) { @@ -165,8 +168,10 @@ int main(int argc, char **argv, char **envp) hook_register(routing_conf_event, routing_control_plane_protocols_name_validate); - - routing_control_plane_protocols_register_vrf_dependency(); + hook_register(routing_create, + routing_control_plane_protocols_staticd_create); + hook_register(routing_destroy, + routing_control_plane_protocols_staticd_destroy); /* * We set FRR_NO_SPLIT_CONFIG flag to avoid reading our config, but we diff --git a/staticd/static_nb.c b/staticd/static_nb.c index 1c69a58..e6aa71a 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -15,19 +15,11 @@ const struct frr_yang_module_info frr_staticd_info = { .name = "frr-staticd", .nodes = { - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", - .cbs = { - .cli_show = static_cli_show, - .cli_show_end = static_cli_show_end, - } - }, { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy, - .cli_cmp = static_route_list_cli_cmp, } }, { @@ -35,7 +27,6 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy, - .cli_cmp = static_path_list_cli_cmp, } }, { @@ -51,8 +42,6 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, - .cli_show = static_nexthop_cli_show, - .cli_cmp = static_nexthop_cli_cmp, } }, { @@ -150,7 +139,6 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, - .cli_cmp = static_src_list_cli_cmp, } }, { @@ -158,7 +146,6 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, - .cli_cmp = static_path_list_cli_cmp, } }, { @@ -174,8 +161,6 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, - .cli_show = static_src_nexthop_cli_show, - .cli_cmp = static_nexthop_cli_cmp, } }, { diff --git a/staticd/static_nb.h b/staticd/static_nb.h index 9f80653..be75d9d 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -11,6 +11,12 @@ extern "C" { #endif extern const struct frr_yang_module_info frr_staticd_info; +extern const struct frr_yang_module_info frr_staticd_cli_info; + +int routing_control_plane_protocols_staticd_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_staticd_destroy( + struct nb_cb_destroy_args *args); /* Mandatory callbacks. */ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create( diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index ede2e38..7de5f04 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -18,6 +18,7 @@ #include "static_vrf.h" #include "static_routes.h" #include "static_nb.h" +#include "static_zebra.h" static int static_path_list_create(struct nb_cb_create_args *args) @@ -33,8 +34,8 @@ static int static_path_list_create(struct nb_cb_create_args *args) case NB_EV_VALIDATE: vrf_dnode = yang_dnode_get_parent(args->dnode, "control-plane-protocol"); - vrf = yang_dnode_get_string(vrf_dnode, "./vrf"); - table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); + vrf = yang_dnode_get_string(vrf_dnode, "vrf"); + table_id = yang_dnode_get_uint32(args->dnode, "table-id"); /* * TableId is not applicable for VRF. Consider the case of @@ -55,8 +56,8 @@ static int static_path_list_create(struct nb_cb_create_args *args) break; case NB_EV_APPLY: rn = nb_running_get_entry(args->dnode, NULL, true); - distance = yang_dnode_get_uint8(args->dnode, "./distance"); - table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); + distance = yang_dnode_get_uint8(args->dnode, "distance"); + table_id = yang_dnode_get_uint32(args->dnode, "table-id"); pn = static_add_path(rn, table_id, distance); nb_running_set_entry(args->dnode, pn); } @@ -111,7 +112,7 @@ static int nexthop_iter_cb(const struct lyd_node *dnode, void *arg) struct nexthop_iter *iter = arg; enum static_nh_type nh_type; - nh_type = yang_dnode_get_enum(dnode, "./nh-type"); + nh_type = yang_dnode_get_enum(dnode, "nh-type"); if (nh_type == STATIC_BLACKHOLE) iter->blackhole = true; @@ -134,9 +135,8 @@ static bool static_nexthop_create(struct nb_cb_create_args *args) switch (args->event) { case NB_EV_VALIDATE: - ifname = yang_dnode_get_string(args->dnode, "./interface"); - nh_type = yang_dnode_get_enum(args->dnode, "./nh-type"); - if (ifname != NULL && nh_type != STATIC_BLACKHOLE) { + ifname = yang_dnode_get_string(args->dnode, "interface"); + if (ifname != NULL) { if (strcasecmp(ifname, "Null0") == 0 || strcasecmp(ifname, "reject") == 0 || strcasecmp(ifname, "blackhole") == 0) { @@ -170,12 +170,15 @@ static bool static_nexthop_create(struct nb_cb_create_args *args) case NB_EV_ABORT: break; case NB_EV_APPLY: - yang_dnode_get_ip(&ipaddr, args->dnode, "./gateway"); - nh_type = yang_dnode_get_enum(args->dnode, "./nh-type"); - ifname = yang_dnode_get_string(args->dnode, "./interface"); - nh_vrf = yang_dnode_get_string(args->dnode, "./vrf"); + yang_dnode_get_ip(&ipaddr, args->dnode, "gateway"); + nh_type = yang_dnode_get_enum(args->dnode, "nh-type"); + ifname = yang_dnode_get_string(args->dnode, "interface"); + nh_vrf = yang_dnode_get_string(args->dnode, "vrf"); pn = nb_running_get_entry(args->dnode, NULL, true); + if (strmatch(ifname, "(null)")) + ifname = ""; + if (!static_add_nexthop_validate(nh_vrf, nh_type, &ipaddr)) flog_warn( EC_LIB_NB_CB_CONFIG_VALIDATE, @@ -464,33 +467,10 @@ static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; enum static_nh_type nh_type; - const char *nh_ifname; - const char *nh_vrf; switch (args->event) { case NB_EV_VALIDATE: nh_type = yang_dnode_get_enum(args->dnode, "../nh-type"); - nh_ifname = yang_dnode_get_string(args->dnode, "../interface"); - nh_vrf = yang_dnode_get_string(args->dnode, "../vrf"); - if (nh_ifname && nh_vrf) { - struct vrf *vrf = vrf_lookup_by_name(nh_vrf); - - if (!vrf) { - snprintf(args->errmsg, args->errmsg_len, - "nexthop vrf %s not found", nh_vrf); - return NB_ERR_VALIDATION; - } - - struct interface *ifp = if_lookup_by_name(nh_ifname, - vrf->vrf_id); - - if (ifp && (!strmatch(nh_ifname, "blackhole") || - !strmatch(nh_ifname, "reject"))) { - snprintf(args->errmsg, args->errmsg_len, - "nexthop interface name must be (reject, blackhole)"); - return NB_ERR_VALIDATION; - } - } if (nh_type != STATIC_BLACKHOLE) { snprintf(args->errmsg, args->errmsg_len, "nexthop type is not the blackhole type"); @@ -535,7 +515,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa const struct lyd_node *mls_dnode; uint32_t count; - mls_dnode = yang_dnode_get(args->dnode, "./mpls-label-stack"); + mls_dnode = yang_dnode_get(args->dnode, "mpls-label-stack"); count = yang_get_list_elements_count(lyd_child(mls_dnode)); if (count > MPLS_MAX_LABELS) { @@ -552,7 +532,7 @@ int routing_control_plane_protocols_name_validate( { const char *name; - name = yang_dnode_get_string(args->dnode, "./name"); + name = yang_dnode_get_string(args->dnode, "name"); if (!strmatch(name, "staticd")) { snprintf(args->errmsg, args->errmsg_len, "static routing supports only one instance with name staticd"); @@ -560,6 +540,48 @@ int routing_control_plane_protocols_name_validate( } return NB_OK; } + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol + */ +int routing_control_plane_protocols_staticd_create(struct nb_cb_create_args *args) +{ + struct static_vrf *svrf; + const char *vrf; + + vrf = yang_dnode_get_string(args->dnode, "vrf"); + svrf = static_vrf_alloc(vrf); + nb_running_set_entry(args->dnode, svrf); + + return NB_OK; +} + +int routing_control_plane_protocols_staticd_destroy( + struct nb_cb_destroy_args *args) +{ + struct static_vrf *svrf; + struct route_table *stable; + struct route_node *rn; + afi_t afi; + safi_t safi; + + svrf = nb_running_unset_entry(args->dnode); + + FOREACH_AFI_SAFI (afi, safi) { + stable = svrf->stable[afi][safi]; + if (!stable) + continue; + + for (rn = route_top(stable); rn; rn = route_next(rn)) + static_del_route(rn); + } + + static_vrf_free(svrf); + + return NB_OK; +} + /* * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list @@ -567,8 +589,7 @@ int routing_control_plane_protocols_name_validate( int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create( struct nb_cb_create_args *args) { - struct vrf *vrf; - struct static_vrf *s_vrf; + struct static_vrf *svrf; struct route_node *rn; const struct lyd_node *vrf_dnode; struct prefix prefix; @@ -579,15 +600,15 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr switch (args->event) { case NB_EV_VALIDATE: - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); - afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); prefix_afi = family2afi(prefix.family); if (afi != prefix_afi) { flog_warn( EC_LIB_NB_CB_CONFIG_VALIDATE, "route node %s creation failed", - yang_dnode_get_string(args->dnode, "./prefix")); + yang_dnode_get_string(args->dnode, "prefix")); return NB_ERR_VALIDATION; } break; @@ -597,19 +618,18 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr case NB_EV_APPLY: vrf_dnode = yang_dnode_get_parent(args->dnode, "control-plane-protocol"); - vrf = nb_running_get_entry(vrf_dnode, NULL, true); - s_vrf = vrf->info; + svrf = nb_running_get_entry(vrf_dnode, NULL, true); - yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); - afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi"); + yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); - rn = static_add_route(afi, safi, &prefix, NULL, s_vrf); - if (vrf->vrf_id == VRF_UNKNOWN) + rn = static_add_route(afi, safi, &prefix, NULL, svrf); + if (!svrf->vrf || svrf->vrf->vrf_id == VRF_UNKNOWN) snprintf( args->errmsg, args->errmsg_len, "Static Route to %s not installed currently because dependent config not fully available", - yang_dnode_get_string(args->dnode, "./prefix")); + yang_dnode_get_string(args->dnode, "prefix")); nb_running_set_entry(args->dnode, rn); break; } @@ -960,6 +980,17 @@ int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args) sn = nb_running_get_entry(args->dnode, NULL, true); static_next_hop_bfd_auto_source(sn); + + /* NHT information are needed by BFD to automatically find the source + * + * Force zebra to resend the information to BFD by unregistering and + * registering again NHT. The (...)/frr-nexthops/nexthop northbound + * apply_finish function will trigger a call to static_install_nexthop() + * that does a call to static_zebra_nht_register(nh, true); + * static_zebra_nht_register(sn, false); + */ + static_zebra_nht_register(sn, false); + return NB_OK; } @@ -1036,7 +1067,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr rn = nb_running_get_entry(args->dnode, NULL, true); info = route_table_get_info(rn->table); s_vrf = info->svrf; - yang_dnode_get_ipv6p(&src_prefix, args->dnode, "./src-prefix"); + yang_dnode_get_ipv6p(&src_prefix, args->dnode, "src-prefix"); afi = family2afi(src_prefix.family); src_rn = static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); diff --git a/staticd/static_nht.c b/staticd/static_nht.c index ebc5ea1..6be5984 100644 --- a/staticd/static_nht.c +++ b/staticd/static_nht.c @@ -18,8 +18,7 @@ #include "static_nht.h" static void static_nht_update_path(struct static_path *pn, struct prefix *nhp, - uint32_t nh_num, vrf_id_t nh_vrf_id, - struct vrf *vrf) + uint32_t nh_num, vrf_id_t nh_vrf_id) { struct static_nexthop *nh; @@ -49,18 +48,13 @@ static void static_nht_update_path(struct static_path *pn, struct prefix *nhp, static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, - struct vrf *vrf, vrf_id_t nh_vrf_id) + struct static_vrf *svrf, vrf_id_t nh_vrf_id) { struct route_table *stable; - struct static_vrf *svrf; struct route_node *rn; struct static_path *pn; struct static_route_info *si; - svrf = vrf->info; - if (!svrf) - return; - stable = static_vrf_static_table(afi, safi, svrf); if (!stable) return; @@ -71,7 +65,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, si = static_route_info_from_rnode(rn); frr_each(static_path_list, &si->path_list, pn) { static_nht_update_path(pn, nhp, nh_num, - nh_vrf_id, vrf); + nh_vrf_id); } route_unlock_node(rn); } @@ -83,7 +77,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, if (!si) continue; frr_each(static_path_list, &si->path_list, pn) { - static_nht_update_path(pn, nhp, nh_num, nh_vrf_id, vrf); + static_nht_update_path(pn, nhp, nh_num, nh_vrf_id); } } } @@ -91,29 +85,23 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, void static_nht_update(struct prefix *sp, struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) { + struct static_vrf *svrf; - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - static_nht_update_safi(sp, nhp, nh_num, afi, safi, vrf, + RB_FOREACH (svrf, svrf_name_head, &svrfs) + static_nht_update_safi(sp, nhp, nh_num, afi, safi, svrf, nh_vrf_id); } static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, - safi_t safi, struct vrf *vrf, + safi_t safi, struct static_vrf *svrf, vrf_id_t nh_vrf_id) { - struct static_vrf *svrf; struct route_table *stable; struct static_nexthop *nh; struct static_path *pn; struct route_node *rn; struct static_route_info *si; - svrf = vrf->info; - if (!svrf) - return; - stable = static_vrf_static_table(afi, safi, svrf); if (!stable) return; @@ -153,10 +141,10 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) { - struct vrf *vrf; + struct static_vrf *svrf; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - static_nht_reset_start_safi(nhp, afi, safi, vrf, nh_vrf_id); + RB_FOREACH (svrf, svrf_name_head, &svrfs) + static_nht_reset_start_safi(nhp, afi, safi, svrf, nh_vrf_id); } static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 1fbbf7e..cba3818 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -87,11 +87,6 @@ void zebra_stable_node_cleanup(struct route_table *table, /* Install static path into rib. */ void static_install_path(struct static_path *pn) { - struct static_nexthop *nh; - - frr_each(static_nexthop_list, &pn->nexthop_list, nh) - static_zebra_nht_register(nh, true); - if (static_nexthop_list_count(&pn->nexthop_list)) static_zebra_route_add(pn, true); } @@ -245,21 +240,20 @@ void static_del_path(struct static_path *pn) XFREE(MTYPE_STATIC_PATH, pn); } -struct static_nexthop *static_add_nexthop(struct static_path *pn, - enum static_nh_type type, - struct ipaddr *ipaddr, - const char *ifname, - const char *nh_vrf, uint32_t color) +struct static_nexthop * +static_add_nexthop(struct static_path *pn, enum static_nh_type type, + struct ipaddr *ipaddr, const char *ifname, + const char *nh_vrfname, uint32_t color) { struct route_node *rn = pn->rn; struct static_nexthop *nh; - struct static_vrf *nh_svrf; + struct vrf *nh_vrf; struct interface *ifp; struct static_nexthop *cp; route_lock_node(rn); - nh_svrf = static_vrf_lookup_by_name(nh_vrf); + nh_vrf = vrf_lookup_by_name(nh_vrfname); /* Make new static route structure. */ nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop)); @@ -274,8 +268,8 @@ struct static_nexthop *static_add_nexthop(struct static_path *pn, 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)); + nh->nh_vrf_id = nh_vrf ? nh_vrf->vrf_id : VRF_UNKNOWN; + strlcpy(nh->nh_vrfname, nh_vrfname, sizeof(nh->nh_vrfname)); if (ifname) strlcpy(nh->ifname, ifname, sizeof(nh->ifname)); @@ -378,6 +372,17 @@ void static_install_nexthop(struct static_nexthop *nh) } } +void static_uninstall_nexthop(struct static_nexthop *nh) +{ + struct static_path *pn = nh->pn; + + if (nh->nh_vrf_id == VRF_UNKNOWN) + return; + + static_zebra_nht_register(nh, false); + static_uninstall_path(pn); +} + void static_delete_nexthop(struct static_nexthop *nh) { struct static_path *pn = nh->pn; @@ -387,17 +392,8 @@ void static_delete_nexthop(struct static_nexthop *nh) /* Remove BFD session/configuration if any. */ bfd_sess_free(&nh->bsp); - if (nh->nh_vrf_id == VRF_UNKNOWN) - goto EXIT; - - static_zebra_nht_register(nh, false); - /* - * If we have other si nodes then route replace - * else delete the route - */ - static_uninstall_path(pn); + static_uninstall_nexthop(nh); -EXIT: route_unlock_node(rn); /* Free static route configuration. */ XFREE(MTYPE_STATIC_NEXTHOP, nh); @@ -437,14 +433,10 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi, struct route_node *rn; struct static_nexthop *nh; struct static_path *pn; - struct vrf *vrf; + struct static_vrf *svrf; struct static_route_info *si; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - struct static_vrf *svrf; - - svrf = vrf->info; - + RB_FOREACH (svrf, svrf_name_head, &svrfs) { stable = static_vrf_static_table(afi, safi, svrf); if (!stable) continue; @@ -476,8 +468,8 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi, * afi -> The afi to look at * safi -> the safi to look at */ -static void static_fixup_vrf(struct static_vrf *svrf, - struct route_table *stable, afi_t afi, safi_t safi) +static void static_fixup_vrf(struct vrf *vrf, struct route_table *stable, + afi_t afi, safi_t safi) { struct route_node *rn; struct static_nexthop *nh; @@ -491,13 +483,11 @@ static void static_fixup_vrf(struct static_vrf *svrf, continue; frr_each(static_path_list, &si->path_list, pn) { frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - if (strcmp(svrf->vrf->name, nh->nh_vrfname) - != 0) + if (strcmp(vrf->name, nh->nh_vrfname) != 0) continue; - nh->nh_vrf_id = svrf->vrf->vrf_id; - nh->nh_registered = false; - if (nh->ifindex) { + nh->nh_vrf_id = vrf->vrf_id; + if (nh->ifname[0]) { ifp = if_lookup_by_name(nh->ifname, nh->nh_vrf_id); if (ifp) @@ -506,7 +496,7 @@ static void static_fixup_vrf(struct static_vrf *svrf, continue; } - static_install_path(pn); + static_install_nexthop(nh); } } } @@ -521,13 +511,9 @@ static void static_fixup_vrf(struct static_vrf *svrf, * afi -> the afi in question * safi -> the safi in question */ -static void static_enable_vrf(struct static_vrf *svrf, - struct route_table *stable, afi_t afi, - safi_t safi) +static void static_enable_vrf(struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_nexthop *nh; - struct interface *ifp; struct static_path *pn; struct static_route_info *si; @@ -535,21 +521,8 @@ static void static_enable_vrf(struct static_vrf *svrf, si = static_route_info_from_rnode(rn); if (!si) continue; - frr_each(static_path_list, &si->path_list, pn) { - frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - if (nh->ifindex) { - ifp = if_lookup_by_name(nh->ifname, - nh->nh_vrf_id); - if (ifp) - nh->ifindex = ifp->ifindex; - else - continue; - } - if (nh->nh_vrf_id == VRF_UNKNOWN) - continue; - static_install_path(pn); - } - } + frr_each(static_path_list, &si->path_list, pn) + static_install_path(pn); } } @@ -560,27 +533,26 @@ static void static_enable_vrf(struct static_vrf *svrf, * * enable_svrf -> the vrf being enabled */ -void static_fixup_vrf_ids(struct static_vrf *enable_svrf) +void static_fixup_vrf_ids(struct vrf *vrf) { struct route_table *stable; - struct vrf *vrf; + struct static_vrf *svrf, *enable_svrf; afi_t afi; safi_t safi; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - struct static_vrf *svrf; + enable_svrf = vrf->info; - svrf = vrf->info; + RB_FOREACH (svrf, svrf_name_head, &svrfs) { /* Install any static routes configured for this VRF. */ FOREACH_AFI_SAFI (afi, safi) { stable = svrf->stable[afi][safi]; if (!stable) continue; - static_fixup_vrf(enable_svrf, stable, afi, safi); + static_fixup_vrf(vrf, stable, afi, safi); if (enable_svrf == svrf) - static_enable_vrf(svrf, stable, afi, safi); + static_enable_vrf(stable, afi, safi); } } } @@ -595,8 +567,7 @@ void static_fixup_vrf_ids(struct static_vrf *enable_svrf) * afi -> the afi in question * safi -> the safi in question */ -static void static_cleanup_vrf(struct static_vrf *svrf, - struct route_table *stable, +static void static_cleanup_vrf(struct vrf *vrf, struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; @@ -610,11 +581,13 @@ static void static_cleanup_vrf(struct static_vrf *svrf, continue; frr_each(static_path_list, &si->path_list, pn) { frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - if (strcmp(svrf->vrf->name, nh->nh_vrfname) - != 0) + if (strcmp(vrf->name, nh->nh_vrfname) != 0) continue; - static_uninstall_path(pn); + static_uninstall_nexthop(nh); + + nh->nh_vrf_id = VRF_UNKNOWN; + nh->ifindex = IFINDEX_INTERNAL; } } } @@ -632,7 +605,6 @@ static void static_disable_vrf(struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_nexthop *nh; struct static_path *pn; struct static_route_info *si; @@ -640,11 +612,8 @@ static void static_disable_vrf(struct route_table *stable, si = static_route_info_from_rnode(rn); if (!si) continue; - frr_each(static_path_list, &si->path_list, pn) { - frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - static_uninstall_path(pn); - } - } + frr_each(static_path_list, &si->path_list, pn) + static_uninstall_path(pn); } } @@ -656,26 +625,23 @@ static void static_disable_vrf(struct route_table *stable, * * disable_svrf - The vrf being disabled */ -void static_cleanup_vrf_ids(struct static_vrf *disable_svrf) +void static_cleanup_vrf_ids(struct vrf *vrf) { - struct vrf *vrf; + struct route_table *stable; + struct static_vrf *svrf, *disable_svrf; afi_t afi; safi_t safi; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - struct static_vrf *svrf; - - svrf = vrf->info; + disable_svrf = vrf->info; + RB_FOREACH (svrf, svrf_name_head, &svrfs) { /* Uninstall any static routes configured for this VRF. */ FOREACH_AFI_SAFI (afi, safi) { - struct route_table *stable; - stable = svrf->stable[afi][safi]; if (!stable) continue; - static_cleanup_vrf(disable_svrf, stable, afi, safi); + static_cleanup_vrf(vrf, stable, afi, safi); if (disable_svrf == svrf) static_disable_vrf(stable, afi, safi); @@ -683,71 +649,6 @@ void static_cleanup_vrf_ids(struct static_vrf *disable_svrf) } } -/* - * This function enables static routes when an interface it relies - * on in a different vrf is coming up. - * - * stable -> The stable we are looking at. - * ifp -> interface coming up - * afi -> the afi in question - * safi -> the safi in question - */ -static void static_fixup_intf_nh(struct route_table *stable, - struct interface *ifp, - afi_t afi, safi_t safi) -{ - struct route_node *rn; - struct static_nexthop *nh; - struct static_path *pn; - struct static_route_info *si; - - for (rn = route_top(stable); rn; rn = route_next(rn)) { - si = static_route_info_from_rnode(rn); - if (!si) - continue; - frr_each(static_path_list, &si->path_list, pn) { - frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - if (nh->nh_vrf_id != ifp->vrf->vrf_id) - continue; - - if (nh->ifindex != ifp->ifindex) - continue; - - static_install_path(pn); - } - } - } -} - -/* - * This function enables static routes that rely on an interface in - * a different vrf when that interface comes up. - */ -void static_install_intf_nh(struct interface *ifp) -{ - struct route_table *stable; - struct vrf *vrf; - afi_t afi; - safi_t safi; - - RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { - struct static_vrf *svrf = vrf->info; - - /* Not needed if same vrf since happens naturally */ - if (vrf->vrf_id == ifp->vrf->vrf_id) - continue; - - /* Install any static routes configured for this interface. */ - FOREACH_AFI_SAFI (afi, safi) { - stable = svrf->stable[afi][safi]; - if (!stable) - continue; - - static_fixup_intf_nh(stable, ifp, afi, safi); - } - } -} - /* called from if_{add,delete}_update, i.e. when ifindex becomes [in]valid */ void static_ifindex_update(struct interface *ifp, bool up) { diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 548148b..2e2e498 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -131,7 +131,7 @@ struct static_nexthop { bool nh_registered; bool nh_valid; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; /* Label information */ struct static_nh_label snh_label; @@ -199,20 +199,18 @@ extern uint32_t zebra_ecmp_count; extern struct zebra_privs_t static_privs; -void static_fixup_vrf_ids(struct static_vrf *svrf); +extern void static_fixup_vrf_ids(struct vrf *vrf); +extern void static_cleanup_vrf_ids(struct vrf *vrf); extern struct static_nexthop * static_add_nexthop(struct static_path *pn, enum static_nh_type type, struct ipaddr *ipaddr, const char *ifname, const char *nh_vrf, uint32_t color); extern void static_install_nexthop(struct static_nexthop *nh); +extern void static_uninstall_nexthop(struct static_nexthop *nh); extern void static_delete_nexthop(struct static_nexthop *nh); -extern void static_cleanup_vrf_ids(struct static_vrf *disable_svrf); - -extern void static_install_intf_nh(struct interface *ifp); - extern void static_ifindex_update(struct interface *ifp, bool up); extern void static_install_path(struct static_path *pn); @@ -239,7 +237,7 @@ extern void zebra_stable_node_cleanup(struct route_table *table, * Max string return via API static_get_nh_str in size_t */ -#define NEXTHOP_STR (INET6_ADDRSTRLEN + INTERFACE_NAMSIZ + 25) +#define NEXTHOP_STR (INET6_ADDRSTRLEN + IFNAMSIZ + 25) /* * For the given nexthop, returns the string * nexthop : returns the formatted string in nexthop diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index a67dce2..710827a 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -18,16 +18,37 @@ DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info"); -static struct static_vrf *static_vrf_alloc(void) +static int svrf_name_compare(const struct static_vrf *a, + const struct static_vrf *b) +{ + return strcmp(a->name, b->name); +} + +RB_GENERATE(svrf_name_head, static_vrf, entry, svrf_name_compare); + +struct svrf_name_head svrfs = RB_INITIALIZER(&svrfs); + +static struct static_vrf *static_vrf_lookup_by_name(const char *name) +{ + struct static_vrf svrf; + + strlcpy(svrf.name, name, sizeof(svrf.name)); + return RB_FIND(svrf_name_head, &svrfs, &svrf); +} + +struct static_vrf *static_vrf_alloc(const char *name) { struct route_table *table; struct static_vrf *svrf; struct stable_info *info; + struct vrf *vrf; safi_t safi; afi_t afi; svrf = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct static_vrf)); + strlcpy(svrf->name, name, sizeof(svrf->name)); + for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { if (afi == AFI_IP6) @@ -46,16 +67,56 @@ static struct static_vrf *static_vrf_alloc(void) svrf->stable[afi][safi] = table; } } + + RB_INSERT(svrf_name_head, &svrfs, svrf); + + vrf = vrf_lookup_by_name(name); + if (vrf) { + svrf->vrf = vrf; + vrf->info = svrf; + } + return svrf; } +void static_vrf_free(struct static_vrf *svrf) +{ + struct route_table *table; + struct vrf *vrf; + safi_t safi; + afi_t afi; + void *info; + + vrf = svrf->vrf; + if (vrf) { + vrf->info = NULL; + svrf->vrf = NULL; + } + + RB_REMOVE(svrf_name_head, &svrfs, svrf); + + for (afi = AFI_IP; afi <= AFI_IP6; afi++) { + for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { + table = svrf->stable[afi][safi]; + info = route_table_get_info(table); + route_table_finish(table); + XFREE(MTYPE_STATIC_RTABLE_INFO, info); + svrf->stable[afi][safi] = NULL; + } + } + + XFREE(MTYPE_STATIC_RTABLE_INFO, svrf); +} + static int static_vrf_new(struct vrf *vrf) { struct static_vrf *svrf; - svrf = static_vrf_alloc(); - vrf->info = svrf; - svrf->vrf = vrf; + svrf = static_vrf_lookup_by_name(vrf->name); + if (svrf) { + vrf->info = svrf; + svrf->vrf = vrf; + } return 0; } @@ -63,37 +124,27 @@ static int static_vrf_new(struct vrf *vrf) static int static_vrf_enable(struct vrf *vrf) { static_zebra_vrf_register(vrf); - - static_fixup_vrf_ids(vrf->info); - + static_fixup_vrf_ids(vrf); return 0; } static int static_vrf_disable(struct vrf *vrf) { + static_cleanup_vrf_ids(vrf); static_zebra_vrf_unregister(vrf); return 0; } static int static_vrf_delete(struct vrf *vrf) { - struct route_table *table; struct static_vrf *svrf; - safi_t safi; - afi_t afi; - void *info; svrf = vrf->info; - for (afi = AFI_IP; afi <= AFI_IP6; afi++) { - for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { - table = svrf->stable[afi][safi]; - info = route_table_get_info(table); - route_table_finish(table); - XFREE(MTYPE_STATIC_RTABLE_INFO, info); - svrf->stable[afi][safi] = NULL; - } + if (svrf) { + svrf->vrf = NULL; + vrf->info = NULL; } - XFREE(MTYPE_STATIC_RTABLE_INFO, svrf); + return 0; } @@ -110,43 +161,20 @@ struct route_table *static_vrf_static_table(afi_t afi, safi_t safi, return svrf->stable[afi][safi]; } -struct static_vrf *static_vrf_lookup_by_name(const char *name) -{ - struct vrf *vrf; - - if (!name) - name = VRF_DEFAULT_NAME; - - vrf = vrf_lookup_by_name(name); - if (vrf) - return ((struct static_vrf *)vrf->info); - - return NULL; -} - -static int static_vrf_config_write(struct vty *vty) -{ - struct lyd_node *dnode; - int written = 0; - - dnode = yang_dnode_get(running_config->dnode, "/frr-routing:routing"); - if (dnode) { - nb_cli_show_dnode_cmds(vty, dnode, false); - written = 1; - } - - return written; -} - void static_vrf_init(void) { vrf_init(static_vrf_new, static_vrf_enable, static_vrf_disable, static_vrf_delete); - vrf_cmd_init(static_vrf_config_write); + vrf_cmd_init(NULL); } void static_vrf_terminate(void) { + struct static_vrf *svrf, *svrf_next; + + RB_FOREACH_SAFE (svrf, svrf_name_head, &svrfs, svrf_next) + static_vrf_free(svrf); + vrf_terminate(); } diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h index 8f55775..26ee28f 100644 --- a/staticd/static_vrf.h +++ b/staticd/static_vrf.h @@ -7,15 +7,27 @@ #ifndef __STATIC_VRF_H__ #define __STATIC_VRF_H__ +#include "openbsd-tree.h" + #ifdef __cplusplus extern "C" { #endif struct static_vrf { + RB_ENTRY(static_vrf) entry; + + char name[VRF_NAMSIZ + 1]; struct vrf *vrf; struct route_table *stable[AFI_MAX][SAFI_MAX]; }; +RB_HEAD(svrf_name_head, static_vrf); +RB_PROTOTYPE(svrf_name_head, static_vrf, entry, svrf_name_compare) + +extern struct svrf_name_head svrfs; + +struct static_vrf *static_vrf_alloc(const char *name); +void static_vrf_free(struct static_vrf *svrf); struct stable_info { struct static_vrf *svrf; @@ -25,8 +37,6 @@ struct stable_info { #define GET_STABLE_VRF_ID(info) info->svrf->vrf->vrf_id -struct static_vrf *static_vrf_lookup_by_name(const char *vrf_name); - void static_vrf_init(void); struct route_table *static_vrf_static_table(afi_t afi, safi_t safi, diff --git a/staticd/static_vty.c b/staticd/static_vty.c index e94acba..a18028e 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -19,6 +19,7 @@ #include "libfrr.h" #include "routing_nb.h" #include "northbound_cli.h" +#include "frrdistance.h" #include "static_vrf.h" #include "static_vty.h" @@ -59,8 +60,6 @@ struct static_route_args { bool bfd_multi_hop; const char *bfd_source; const char *bfd_profile; - - const char *input; }; static int static_route_nb_run(struct vty *vty, struct static_route_args *args) @@ -100,7 +99,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) return CMD_WARNING_CONFIG_FAILED; } - args->vrf = yang_dnode_get_string(vrf_dnode, "./name"); + args->vrf = yang_dnode_get_string(vrf_dnode, "name"); } else { if (args->vrf == NULL) args->vrf = VRF_DEFAULT_NAME; @@ -152,20 +151,9 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) else buf_gate_str = ""; - if (args->gateway == NULL && args->interface_name == NULL) { + if (args->gateway == NULL && args->interface_name == NULL) type = STATIC_BLACKHOLE; - /* If this is blackhole/reject flagged route, then - * specify interface_name with the value of what was really - * entered. - * interface_name will be validated later in NB functions - * to check if we don't create blackhole/reject routes that - * match the real interface names. - * E.g.: `ip route 10.0.0.1/32 bla` will create a blackhole - * route despite the real interface named `bla` exists. - */ - if (args->input) - args->interface_name = args->input; - } else if (args->gateway && args->interface_name) { + else if (args->gateway && args->interface_name) { if (args->afi == AFI_IP) type = STATIC_IPV4_GATEWAY_IFNAME; else @@ -552,8 +540,6 @@ DEFPY_YANG(ip_route_blackhole, "Table to configure\n" "The table number to configure\n") { - int idx_flag = 0; - struct static_route_args args = { .delete = !!no, .afi = AFI_IP, @@ -568,9 +554,6 @@ DEFPY_YANG(ip_route_blackhole, .vrf = vrf, }; - if (flag && argv_find(argv, argc, flag, &idx_flag)) - args.input = argv[idx_flag]->arg; - return static_route_nb_run(vty, &args); } @@ -599,8 +582,6 @@ DEFPY_YANG(ip_route_blackhole_vrf, "Table to configure\n" "The table number to configure\n") { - int idx_flag = 0; - struct static_route_args args = { .delete = !!no, .afi = AFI_IP, @@ -622,9 +603,6 @@ DEFPY_YANG(ip_route_blackhole_vrf, */ assert(args.prefix); - if (flag && argv_find(argv, argc, flag, &idx_flag)) - args.input = argv[idx_flag]->arg; - return static_route_nb_run(vty, &args); } @@ -915,8 +893,6 @@ DEFPY_YANG(ipv6_route_blackhole, "Table to configure\n" "The table number to configure\n") { - int idx_flag = 0; - struct static_route_args args = { .delete = !!no, .afi = AFI_IP6, @@ -931,9 +907,6 @@ DEFPY_YANG(ipv6_route_blackhole, .vrf = vrf, }; - if (flag && argv_find(argv, argc, flag, &idx_flag)) - args.input = argv[idx_flag]->arg; - return static_route_nb_run(vty, &args); } @@ -962,8 +935,6 @@ DEFPY_YANG(ipv6_route_blackhole_vrf, "Table to configure\n" "The table number to configure\n") { - int idx_flag = 0; - struct static_route_args args = { .delete = !!no, .afi = AFI_IP6, @@ -985,9 +956,6 @@ DEFPY_YANG(ipv6_route_blackhole_vrf, */ assert(args.prefix); - if (flag && argv_find(argv, argc, flag, &idx_flag)) - args.input = argv[idx_flag]->arg; - return static_route_nb_run(vty, &args); } @@ -1233,8 +1201,10 @@ DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, return static_route_nb_run(vty, &args); } -void static_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) +#ifdef INCLUDE_MGMTD_CMDDEFS_ONLY + +static void static_cli_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) { const char *vrf; @@ -1243,7 +1213,7 @@ void static_cli_show(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "vrf %s\n", vrf); } -void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +static void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode) { const char *vrf; @@ -1261,13 +1231,13 @@ static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg) { struct mpls_label_iter *iter = arg; - if (yang_dnode_exists(dnode, "./label")) { + if (yang_dnode_exists(dnode, "label")) { if (iter->first) vty_out(iter->vty, " label %s", - yang_dnode_get_string(dnode, "./label")); + yang_dnode_get_string(dnode, "label")); else vty_out(iter->vty, "/%s", - yang_dnode_get_string(dnode, "./label")); + yang_dnode_get_string(dnode, "label")); iter->first = false; } @@ -1285,16 +1255,16 @@ static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg) char buffer[INET6_ADDRSTRLEN]; struct in6_addr cli_seg; - if (yang_dnode_exists(dnode, "./seg")) { + if (yang_dnode_exists(dnode, "seg")) { if (iter->first) { - yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); + yang_dnode_get_ipv6(&cli_seg, dnode, "seg"); if (inet_ntop(AF_INET6, &cli_seg, buffer, INET6_ADDRSTRLEN) == NULL) { return 1; } vty_out(iter->vty, " segments %s", buffer); } else { - yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); + yang_dnode_get_ipv6(&cli_seg, dnode, "seg"); if (inet_ntop(AF_INET6, &cli_seg, buffer, INET6_ADDRSTRLEN) == NULL) { return 1; @@ -1328,7 +1298,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, vrf = yang_dnode_get_string(route, "../../vrf"); - afi_safi = yang_dnode_get_string(route, "./afi-safi"); + afi_safi = yang_dnode_get_string(route, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); if (afi == AFI_IP) @@ -1343,32 +1313,32 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, else vty_out(vty, " mroute"); - vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix")); + vty_out(vty, " %s", yang_dnode_get_string(route, "prefix")); if (src) vty_out(vty, " from %s", - yang_dnode_get_string(src, "./src-prefix")); + yang_dnode_get_string(src, "src-prefix")); - nh_type = yang_dnode_get_enum(nexthop, "./nh-type"); + nh_type = yang_dnode_get_enum(nexthop, "nh-type"); switch (nh_type) { case STATIC_IFNAME: vty_out(vty, " %s", - yang_dnode_get_string(nexthop, "./interface")); + yang_dnode_get_string(nexthop, "interface")); break; case STATIC_IPV4_GATEWAY: case STATIC_IPV6_GATEWAY: vty_out(vty, " %s", - yang_dnode_get_string(nexthop, "./gateway")); + yang_dnode_get_string(nexthop, "gateway")); break; case STATIC_IPV4_GATEWAY_IFNAME: case STATIC_IPV6_GATEWAY_IFNAME: vty_out(vty, " %s", - yang_dnode_get_string(nexthop, "./gateway")); + yang_dnode_get_string(nexthop, "gateway")); vty_out(vty, " %s", - yang_dnode_get_string(nexthop, "./interface")); + yang_dnode_get_string(nexthop, "interface")); break; case STATIC_BLACKHOLE: - bh_type = yang_dnode_get_enum(nexthop, "./bh-type"); + bh_type = yang_dnode_get_enum(nexthop, "bh-type"); switch (bh_type) { case STATIC_BLACKHOLE_DROP: vty_out(vty, " blackhole"); @@ -1383,13 +1353,13 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, break; } - if (yang_dnode_exists(path, "./tag")) { - tag = yang_dnode_get_uint32(path, "./tag"); + if (yang_dnode_exists(path, "tag")) { + tag = yang_dnode_get_uint32(path, "tag"); if (tag != 0 || show_defaults) vty_out(vty, " tag %" PRIu32, tag); } - distance = yang_dnode_get_uint8(path, "./distance"); + distance = yang_dnode_get_uint8(path, "distance"); if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults) vty_out(vty, " %" PRIu8, distance); @@ -1403,48 +1373,49 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, yang_dnode_iterate(srv6_seg_iter_cb, &seg_iter, nexthop, "./srv6-segs-stack/entry"); - nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf"); + nexthop_vrf = yang_dnode_get_string(nexthop, "vrf"); if (strcmp(vrf, nexthop_vrf)) vty_out(vty, " nexthop-vrf %s", nexthop_vrf); - table_id = yang_dnode_get_uint32(path, "./table-id"); + table_id = yang_dnode_get_uint32(path, "table-id"); if (table_id || show_defaults) vty_out(vty, " table %" PRIu32, table_id); - if (yang_dnode_exists(nexthop, "./onlink")) { - onlink = yang_dnode_get_bool(nexthop, "./onlink"); + if (yang_dnode_exists(nexthop, "onlink")) { + onlink = yang_dnode_get_bool(nexthop, "onlink"); if (onlink) vty_out(vty, " onlink"); } - if (yang_dnode_exists(nexthop, "./srte-color")) + if (yang_dnode_exists(nexthop, "srte-color")) vty_out(vty, " color %s", - yang_dnode_get_string(nexthop, "./srte-color")); + yang_dnode_get_string(nexthop, "srte-color")); - if (yang_dnode_exists(nexthop, "./bfd-monitoring")) { + if (yang_dnode_exists(nexthop, "bfd-monitoring")) { const struct lyd_node *bfd_dnode = - yang_dnode_get(nexthop, "./bfd-monitoring"); + yang_dnode_get(nexthop, "bfd-monitoring"); - if (yang_dnode_get_bool(bfd_dnode, "./multi-hop")) { + if (yang_dnode_get_bool(bfd_dnode, "multi-hop")) { vty_out(vty, " bfd multi-hop"); - if (yang_dnode_exists(bfd_dnode, "./source")) + if (yang_dnode_exists(bfd_dnode, "source")) vty_out(vty, " source %s", yang_dnode_get_string(bfd_dnode, "./source")); } else vty_out(vty, " bfd"); - if (yang_dnode_exists(bfd_dnode, "./profile")) + if (yang_dnode_exists(bfd_dnode, "profile")) vty_out(vty, " profile %s", - yang_dnode_get_string(bfd_dnode, "./profile")); + yang_dnode_get_string(bfd_dnode, "profile")); } vty_out(vty, "\n"); } -void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) +static void static_nexthop_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) { const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); const struct lyd_node *route = @@ -1453,8 +1424,9 @@ void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); } -void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) +static void static_src_nexthop_cli_show(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) { const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); @@ -1463,16 +1435,16 @@ void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, nexthop_cli_show(vty, route, src, path, dnode, show_defaults); } -int static_nexthop_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) +static int static_nexthop_cli_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { enum static_nh_type nh_type1, nh_type2; struct prefix prefix1, prefix2; const char *vrf1, *vrf2; int ret = 0; - nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type"); - nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type"); + nh_type1 = yang_dnode_get_enum(dnode1, "nh-type"); + nh_type2 = yang_dnode_get_enum(dnode2, "nh-type"); if (nh_type1 != nh_type2) return (int)nh_type1 - (int)nh_type2; @@ -1480,24 +1452,24 @@ int static_nexthop_cli_cmp(const struct lyd_node *dnode1, switch (nh_type1) { case STATIC_IFNAME: ret = if_cmp_name_func( - yang_dnode_get_string(dnode1, "./interface"), - yang_dnode_get_string(dnode2, "./interface")); + yang_dnode_get_string(dnode1, "interface"), + yang_dnode_get_string(dnode2, "interface")); break; case STATIC_IPV4_GATEWAY: case STATIC_IPV6_GATEWAY: - yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); - yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + yang_dnode_get_prefix(&prefix1, dnode1, "gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "gateway"); ret = prefix_cmp(&prefix1, &prefix2); break; case STATIC_IPV4_GATEWAY_IFNAME: case STATIC_IPV6_GATEWAY_IFNAME: - yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); - yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + yang_dnode_get_prefix(&prefix1, dnode1, "gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "gateway"); ret = prefix_cmp(&prefix1, &prefix2); if (!ret) ret = if_cmp_name_func( - yang_dnode_get_string(dnode1, "./interface"), - yang_dnode_get_string(dnode2, "./interface")); + yang_dnode_get_string(dnode1, "interface"), + yang_dnode_get_string(dnode2, "interface")); break; case STATIC_BLACKHOLE: /* There's only one blackhole nexthop per route */ @@ -1508,28 +1480,28 @@ int static_nexthop_cli_cmp(const struct lyd_node *dnode1, if (ret) return ret; - vrf1 = yang_dnode_get_string(dnode1, "./vrf"); + vrf1 = yang_dnode_get_string(dnode1, "vrf"); if (strmatch(vrf1, "default")) vrf1 = ""; - vrf2 = yang_dnode_get_string(dnode2, "./vrf"); + vrf2 = yang_dnode_get_string(dnode2, "vrf"); if (strmatch(vrf2, "default")) vrf2 = ""; return if_cmp_name_func(vrf1, vrf2); } -int static_route_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) +static int static_route_list_cli_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { const char *afi_safi1, *afi_safi2; afi_t afi1, afi2; safi_t safi1, safi2; struct prefix prefix1, prefix2; - afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi"); + afi_safi1 = yang_dnode_get_string(dnode1, "afi-safi"); yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); - afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi"); + afi_safi2 = yang_dnode_get_string(dnode2, "afi-safi"); yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2); if (afi1 != afi2) @@ -1538,42 +1510,97 @@ int static_route_list_cli_cmp(const struct lyd_node *dnode1, if (safi1 != safi2) return (int)safi1 - (int)safi2; - yang_dnode_get_prefix(&prefix1, dnode1, "./prefix"); - yang_dnode_get_prefix(&prefix2, dnode2, "./prefix"); + yang_dnode_get_prefix(&prefix1, dnode1, "prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "prefix"); return prefix_cmp(&prefix1, &prefix2); } -int static_src_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) +static int static_src_list_cli_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { struct prefix prefix1, prefix2; - yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix"); - yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix"); + yang_dnode_get_prefix(&prefix1, dnode1, "src-prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "src-prefix"); return prefix_cmp(&prefix1, &prefix2); } -int static_path_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) +static int static_path_list_cli_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) { uint32_t table_id1, table_id2; uint8_t distance1, distance2; - table_id1 = yang_dnode_get_uint32(dnode1, "./table-id"); - table_id2 = yang_dnode_get_uint32(dnode2, "./table-id"); + table_id1 = yang_dnode_get_uint32(dnode1, "table-id"); + table_id2 = yang_dnode_get_uint32(dnode2, "table-id"); if (table_id1 != table_id2) return (int)table_id1 - (int)table_id2; - distance1 = yang_dnode_get_uint8(dnode1, "./distance"); - distance2 = yang_dnode_get_uint8(dnode2, "./distance"); + distance1 = yang_dnode_get_uint8(dnode1, "distance"); + distance2 = yang_dnode_get_uint8(dnode2, "distance"); return (int)distance1 - (int)distance2; } -#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY +const struct frr_yang_module_info frr_staticd_cli_info = { + .name = "frr-staticd", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + .cbs = { + .cli_show = static_cli_show, + .cli_show_end = static_cli_show_end, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list", + .cbs = { + .cli_cmp = static_route_list_cli_cmp, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list", + .cbs = { + .cli_cmp = static_path_list_cli_cmp, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop", + .cbs = { + .cli_show = static_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", + .cbs = { + .cli_cmp = static_src_list_cli_cmp, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", + .cbs = { + .cli_cmp = static_path_list_cli_cmp, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", + .cbs = { + .cli_show = static_src_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, + } + }, + { + .xpath = NULL, + }, + } +}; + +#else /* ifdef INCLUDE_MGMTD_CMDDEFS_ONLY */ DEFPY_YANG(debug_staticd, debug_staticd_cmd, "[no] debug static [{events$events|route$route|bfd$bfd}]", @@ -1636,8 +1663,7 @@ void static_vty_init(void) install_element(CONFIG_NODE, &debug_staticd_cmd); install_element(ENABLE_NODE, &show_debugging_static_cmd); install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd); -#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ - +#else /* else INCLUDE_MGMTD_CMDDEFS_ONLY */ install_element(CONFIG_NODE, &ip_mroute_dist_cmd); install_element(CONFIG_NODE, &ip_route_blackhole_cmd); @@ -1653,6 +1679,9 @@ void static_vty_init(void) install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd); install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); +#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ +#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY mgmt_be_client_lib_vty_init(); +#endif } diff --git a/staticd/static_vty.h b/staticd/static_vty.h index 77e52b5..4b4cc1c 100644 --- a/staticd/static_vty.h +++ b/staticd/static_vty.h @@ -11,22 +11,6 @@ extern "C" { #endif -void static_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults); -void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode); -void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults); -void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults); -int static_nexthop_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2); -int static_route_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2); -int static_src_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2); -int static_path_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2); - void static_vty_init(void); #ifdef __cplusplus diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 6abbdad..c4efc14 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -111,8 +111,6 @@ static int interface_address_delete(ZAPI_CALLBACK_ARGS) static int static_ifp_up(struct interface *ifp) { - /* Install any static reliant on this interface coming up */ - static_install_intf_nh(ifp); static_ifindex_update(ifp, true); return 0; @@ -166,9 +164,14 @@ static int route_notify_owner(ZAPI_CALLBACK_ARGS) static void zebra_connected(struct zclient *zclient) { + struct vrf *vrf; + + zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true); zclient_send_reg_requests(zclient, VRF_DEFAULT); - static_fixup_vrf_ids(vrf_info_lookup(VRF_DEFAULT)); + vrf = vrf_lookup_by_id(VRF_DEFAULT); + assert(vrf); + static_fixup_vrf_ids(vrf); } /* API to check whether the configured nexthop address is @@ -186,48 +189,40 @@ static_nexthop_is_local(vrf_id_t vrfid, struct prefix *addr, int family) } return false; } -static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) + +static void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched, + struct zapi_route *nhr) { struct static_nht_data *nhtd, lookup; - struct zapi_route nhr; - struct prefix matched; afi_t afi = AFI_IP; - if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { - zlog_err("Failure to decode nexthop update message"); - return 1; - } - if (zclient->bfd_integration) - bfd_nht_update(&matched, &nhr); + bfd_nht_update(matched, nhr); - if (matched.family == AF_INET6) + if (matched->family == AF_INET6) afi = AFI_IP6; - if (nhr.type == ZEBRA_ROUTE_CONNECT) { - if (static_nexthop_is_local(vrf_id, &matched, - nhr.prefix.family)) - nhr.nexthop_num = 0; + if (nhr->type == ZEBRA_ROUTE_CONNECT) { + if (static_nexthop_is_local(vrf->vrf_id, matched, + nhr->prefix.family)) + nhr->nexthop_num = 0; } memset(&lookup, 0, sizeof(lookup)); - lookup.nh = matched; - lookup.nh_vrf_id = vrf_id; - lookup.safi = nhr.safi; + lookup.nh = *matched; + lookup.nh_vrf_id = vrf->vrf_id; + lookup.safi = nhr->safi; nhtd = static_nht_hash_find(static_nht_hash, &lookup); if (nhtd) { - nhtd->nh_num = nhr.nexthop_num; + nhtd->nh_num = nhr->nexthop_num; - static_nht_reset_start(&matched, afi, nhr.safi, - nhtd->nh_vrf_id); - static_nht_update(NULL, &matched, nhr.nexthop_num, afi, - nhr.safi, nhtd->nh_vrf_id); + static_nht_reset_start(matched, afi, nhr->safi, nhtd->nh_vrf_id); + static_nht_update(NULL, matched, nhr->nexthop_num, afi, + nhr->safi, nhtd->nh_vrf_id); } else zlog_err("No nhtd?"); - - return 1; } static void static_zebra_capabilities(struct zclient_capabilities *cap) @@ -395,6 +390,9 @@ extern void static_zebra_route_add(struct static_path *pn, bool install) struct zapi_route api; uint32_t nh_num = 0; + if (!si->svrf->vrf || si->svrf->vrf->vrf_id == VRF_UNKNOWN) + return; + p = src_pp = NULL; srcdest_rnode_prefixes(rn, &p, &src_pp); @@ -535,22 +533,22 @@ static zclient_handler *const static_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, - [ZEBRA_NEXTHOP_UPDATE] = static_zebra_nexthop_update, }; void static_zebra_init(void) { - struct zclient_options opt = { .receive_notify = true }; - - if_zapi_callbacks(static_ifp_create, static_ifp_up, - static_ifp_down, static_ifp_destroy); + hook_register_prio(if_real, 0, static_ifp_create); + hook_register_prio(if_up, 0, static_ifp_up); + hook_register_prio(if_down, 0, static_ifp_down); + hook_register_prio(if_unreal, 0, static_ifp_destroy); - zclient = zclient_new(master, &opt, static_handlers, + zclient = zclient_new(master, &zclient_options_default, static_handlers, array_size(static_handlers)); zclient_init(zclient, ZEBRA_ROUTE_STATIC, 0, &static_privs); zclient->zebra_capabilities = static_zebra_capabilities; zclient->zebra_connected = zebra_connected; + zclient->nexthop_update = static_zebra_nexthop_update; static_nht_hash_init(static_nht_hash); static_bfd_initialize(zclient, master); diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 799733b..29d2e4c 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -653,7 +653,7 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, - PEER_CAP_AS4_RCV, + 0, { COMMON_ATTRS, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL, @@ -685,7 +685,7 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, - PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV, + 0, { COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, @@ -701,7 +701,7 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, - PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV, + 0, { COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, @@ -717,7 +717,7 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, - PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV, + 0, { COMMON_ATTRS, BGP_ATTR_FLAG_TRANS, @@ -733,7 +733,7 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, - PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV, + 0, { COMMON_ATTRS, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL, diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 9d3d0ec..1ee47a3 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -617,6 +617,7 @@ static struct test_segment misc_segments[] = }, 2, SHOULD_ERR, + -1, }, { "dyn-empty", @@ -835,7 +836,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) switch (type) { case CAPABILITY: len += 2; /* to cover the OPT-Param header */ - _FALLTHROUGH + fallthrough; case OPT_PARAM: printf("len: %u\n", len); /* peek_for_as4 wants getp at capibility*/ diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index a83276b..e050fd4 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -7,6 +7,7 @@ */ #include +#include #include "qobj.h" #include "vty.h" diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index bc6eba9..12c2f11 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -261,24 +261,10 @@ static struct test_peer_attr test_peer_attrs[] = { .u.flag = PEER_FLAG_CAPABILITY_ENHE, .type = PEER_AT_GLOBAL_FLAG, }, - { - .cmd = "capability extended-nexthop", - .u.flag = PEER_FLAG_CAPABILITY_ENHE, - .type = PEER_AT_GLOBAL_FLAG, - .o.invert_peer = true, - .o.use_iface_peer = true, - }, - { - .cmd = "capability software-version", - .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION, - .type = PEER_AT_GLOBAL_FLAG, - }, { .cmd = "capability software-version", .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION, .type = PEER_AT_GLOBAL_FLAG, - .o.invert_peer = true, - .o.use_iface_peer = true, }, { .cmd = "description", @@ -297,9 +283,11 @@ static struct test_peer_attr test_peer_attrs[] = { .type = PEER_AT_GLOBAL_FLAG, }, { - .cmd = "enforce-first-as", - .u.flag = PEER_FLAG_ENFORCE_FIRST_AS, + .cmd = "capability fqdn", + .u.flag = PEER_FLAG_CAPABILITY_FQDN, .type = PEER_AT_GLOBAL_FLAG, + .o.invert_peer = true, + .o.invert_group = true, }, { .cmd = "local-as", diff --git a/tests/bgpd/test_peer_attr.py b/tests/bgpd/test_peer_attr.py index eb57618..b1f88d2 100644 --- a/tests/bgpd/test_peer_attr.py +++ b/tests/bgpd/test_peer_attr.py @@ -15,7 +15,7 @@ TestFlag.okfail("peer\\capability extended-nexthop") TestFlag.okfail("peer\\description") TestFlag.okfail("peer\\disable-connected-check") TestFlag.okfail("peer\\dont-capability-negotiate") -TestFlag.okfail("peer\\enforce-first-as") +TestFlag.okfail("peer\\capability fqdn") TestFlag.okfail("peer\\local-as") TestFlag.okfail("peer\\local-as 1 no-prepend") TestFlag.okfail("peer\\local-as 1 no-prepend replace-as") diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 8af53a2..fdda7f1 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -3,6 +3,7 @@ */ #include +#include #include #include "getopt.h" diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index 6eb180b..4ea28cd 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -5,6 +5,7 @@ */ #include +#include #include #include "getopt.h" @@ -539,7 +540,7 @@ int main(int argc, char **argv) zlog_aux_init("NONE: ", ZLOG_DISABLED); /* IS-IS inits. */ - yang_module_load("frr-isisd"); + yang_module_load("frr-isisd", NULL); SET_FLAG(im->options, F_ISIS_UNIT_TEST); debug_spf_events |= DEBUG_SPF_EVENTS; debug_lfa |= DEBUG_LFA; diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index e0981b9..f9f584f 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -7,6 +7,7 @@ */ #include +#include #include "frrevent.h" #include "vty.h" diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index f82eddd..8f7e7c5 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -5,6 +5,7 @@ */ #include +#include #include "frrevent.h" #include "vty.h" @@ -199,6 +200,25 @@ static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem( return NULL; } + +/* + * XPath: /frr-test-module:frr-test-module/c1value + */ +static struct yang_data * +frr_test_module_c1value_get_elem(struct nb_cb_get_elem_args *args) +{ + return yang_data_new_uint8(args->xpath, 21); +} + +/* + * XPath: /frr-test-module:frr-test-module/c2cont/c2value + */ +static struct yang_data * +frr_test_module_c2cont_c2value_get_elem(struct nb_cb_get_elem_args *args) +{ + return yang_data_new_uint32(args->xpath, 0xAB010203); +} + /* clang-format off */ const struct frr_yang_module_info frr_test_module_info = { .name = "frr-test-module", @@ -242,6 +262,14 @@ const struct frr_yang_module_info frr_test_module_info = { .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active", .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem, }, + { + .xpath = "/frr-test-module:frr-test-module/c1value", + .cbs.get_elem = frr_test_module_c1value_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/c2cont/c2value", + .cbs.get_elem = frr_test_module_c2cont_c2value_get_elem, + }, { .xpath = NULL, }, diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout index 57ecd2f..aa930fe 100644 --- a/tests/lib/northbound/test_oper_data.refout +++ b/tests/lib/northbound/test_oper_data.refout @@ -112,6 +112,10 @@ test# show yang operational-data /frr-test-module:frr-test-module } } ] + }, + "c1value": 21, + "c2cont": { + "c2value": 2868969987 } } } diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index 6c1be50..94df362 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -25,7 +25,7 @@ copy_script: tests/lib/script1.lua $(INSTALL_SCRIPT) $< tests/lib/script1.lua ############################################################################## -GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm +GRPC_TESTS_LDADD = mgmtd/libmgmt_be_nb.la staticd/libstatic.a grpc/libfrrgrpc_pb.la $(GRPC_LIBS) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm if GRPC check_PROGRAMS += tests/lib/test_grpc @@ -162,6 +162,7 @@ tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_darr_SOURCES = tests/lib/test_darr.c +EXTRA_DIST += tests/lib/test_darr.py check_PROGRAMS += tests/lib/test_graph diff --git a/tests/lib/test_darr.c b/tests/lib/test_darr.c index 9150aed..74aedac 100644 --- a/tests/lib/test_darr.c +++ b/tests/lib/test_darr.c @@ -14,7 +14,8 @@ * [x] - darr_append_n * [x] - darr_append_nz * [x] - darr_cap - * [-] - darr_ensure_cap + * [x] - darr_ensure_avail + * [x] - darr_ensure_cap * [x] - darr_ensure_i * [x] - darr_foreach_i * [x] - darr_foreach_p @@ -23,6 +24,15 @@ * [ ] - darr_insertz * [x] - darr_insert_n * [x] - darr_insert_nz + * [x] - darr_in_sprintf + * [x] - darr_in_strcat + * [x] - darr_in_strcat_tail + * [ ] - darr_in_strcatf + * [ ] - darr_in_vstrcatf + * [x] - darr_in_strdup + * [x] - darr_in_strdup_cap + * [-] - darr_in_vsprintf + * [x] - darr_lasti * [x] - darr_maxi * [x] - darr_pop * [x] - darr_push @@ -31,6 +41,13 @@ * [x] - darr_remove_n * [x] - darr_reset * [x] - darr_setlen + * [x] - darr_set_strlen + * [x] - darr_sprintf + * [x] - darr_strdup + * [x] - darr_strdup_cap + * [x] - darr_strlen + * [x] - darr_strnul + * [ ] - darr_vsprintf */ static void test_int(void) @@ -43,6 +60,11 @@ static void test_int(void) int *dap; uint i; + assert(darr_len(da1) == 0); + assert(darr_lasti(da1) == -1); + assert(darr_last(da1) == NULL); + assert(darr_end(da1) == NULL); + darr_ensure_i(da1, 0); da1[0] = 0; assert(darr_len(da1) == 1); @@ -57,9 +79,11 @@ static void test_int(void) da1[i] = i; assert(darr_len(da1) == 5); + assert(darr_lasti(da1) == 4); /* minimum non-pow2 array size for long long and smaller */ assert(darr_cap(da1) == 8); assert(!memcmp(da1, a1, sizeof(a1))); + assert(&da1[darr_lasti(da1)] == darr_last(da1)); /* reverse the numbers */ darr_foreach_p (da1, dap) @@ -185,6 +209,20 @@ static void test_struct(void) assert(darr_cap(da1) == 8); assert(!memcmp(da1, a1, sizeof(a1))); + assert(darr_cap(da1) - darr_len(da1) == 3); + darr_ensure_avail(da1, 2); + assert(darr_cap(da1) == 8); + darr_ensure_avail(da1, 3); + assert(darr_cap(da1) == 8); + darr_ensure_avail(da1, 4); + assert(darr_cap(da1) == 16); + + darr_ensure_cap(da1, 16); + assert(darr_cap(da1) == 16); + + darr_ensure_cap(da1, 20); + assert(darr_cap(da1) == 32); + darr_append_n(da1, 100); assert(darr_len(da1) == 105); @@ -272,8 +310,113 @@ static void test_struct(void) darr_free(da2); } +static void test_string(void) +{ + const char *src = "ABCDE"; + const char *add = "FGHIJ"; + uint srclen = strlen(src); + uint addlen = strlen(add); + char *da1 = NULL; + char *da2 = NULL; + + assert(darr_strlen(da1) == 0); + + da1 = darr_strdup(src); + assert(darr_strlen(da1) == strlen(da1)); + assert(darr_strlen(da1) == srclen); + assert(darr_len(da1) == srclen + 1); + assert(darr_ilen(da1) == (int)srclen + 1); + assert(darr_cap(da1) >= 8); + assert(darr_last(da1) == darr_strnul(da1)); + assert(darr_strnul(da1) == da1 + darr_strlen(da1)); + + da2 = da1; + darr_in_strdup(da1, src); + assert(da1 == da2); + assert(darr_strlen(da1) == strlen(da1)); + assert(darr_strlen(da1) == srclen); + assert(darr_len(da1) == srclen + 1); + darr_free(da1); + assert(da1 == NULL); + + da1 = darr_strdup_cap(src, 128); + assert(darr_strlen(da1) == srclen); + assert(darr_cap(da1) >= 128); + + darr_in_strdup_cap(da1, src, 256); + assert(darr_strlen(da1) == srclen); + assert(darr_cap(da1) >= 256); + darr_free(da1); + + da1 = darr_strdup_cap(add, 2); + assert(darr_strlen(da1) == addlen); + assert(darr_cap(da1) >= 8); + + darr_in_strdup(da1, "ab"); + darr_in_strcat(da1, "/"); + darr_in_strcat(da1, "foo"); + assert(!strcmp("ab/foo", da1)); + darr_free(da1); + + da1 = darr_in_strcat(da1, "ab"); + darr_in_strcat(da1, "/"); + darr_in_strcat(da1, "foo"); + assert(!strcmp("ab/foo", da1)); + + darr_set_strlen(da1, 5); + assert(!strcmp("ab/fo", da1)); + darr_set_strlen(da1, 1); + assert(!strcmp("a", da1)); + + darr_in_strdup(da1, "ab"); + da2 = darr_strdup(add); + darr_in_strcat_tail(da1, da2); + assert(!strcmp("abHIJ", da1)); + assert(darr_strlen(da1) == 5); + assert(darr_len(da1) == 6); + darr_free(da1); + darr_free(da2); + + da1 = darr_strdup("abcde"); + da2 = darr_strdup(add); + darr_in_strcat_tail(da1, da2); + assert(!strcmp("abcde", da1)); + assert(darr_strlen(da1) == 5); + assert(darr_len(da1) == 6); + darr_free(da1); + darr_free(da2); + + da1 = darr_sprintf("0123456789: %08X", 0xDEADBEEF); + assert(!strcmp(da1, "0123456789: DEADBEEF")); + assert(darr_strlen(da1) == 20); + assert(darr_cap(da1) == 128); + da2 = da1; + darr_in_sprintf(da1, "9876543210: %08x", 0x0BADF00D); + assert(da1 == da2); + assert(!strcmp("9876543210: 0badf00d", da2)); + darr_free(da1); + da2 = NULL; + + da1 = NULL; + darr_in_sprintf(da1, "0123456789: %08X", 0xDEADBEEF); + assert(!strcmp(da1, "0123456789: DEADBEEF")); + assert(darr_strlen(da1) == 20); + assert(darr_cap(da1) == 128); + darr_free(da1); + + da1 = darr_sprintf("0123456789: %08x", 0xDEADBEEF); + darr_in_strcatf(da1, " 9876543210: %08x", 0x0BADF00D); + assert(!strcmp("0123456789: deadbeef 9876543210: 0badf00d", da1)); + darr_free(da1); + + da1 = darr_in_strcatf(da1, "0123456789: %08x", 0xDEADBEEF); + assert(!strcmp("0123456789: deadbeef", da1)); + darr_free(da1); +} + int main(int argc, char **argv) { test_int(); test_struct(); + test_string(); } diff --git a/tests/lib/test_darr.py b/tests/lib/test_darr.py new file mode 100644 index 0000000..dea3bdf --- /dev/null +++ b/tests/lib/test_darr.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestDarr(frrtest.TestMultiOut): + program = "./test_darr" + + +TestDarr.exit_cleanly() diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c index 701e171..2760a27 100644 --- a/tests/lib/test_frrlua.c +++ b/tests/lib/test_frrlua.c @@ -13,14 +13,22 @@ static void test_encode_decode(void) { lua_State *L = luaL_newstate(); - long long a = 123; - long long b = a; + int a = 123; + int b = a; lua_pushintegerp(L, &a); lua_decode_integerp(L, -1, &a); assert(a == b); assert(lua_gettop(L) == 0); + long long ll_a = 123L; + long long ll_b = a; + + lua_pushlonglongp(L, &ll_a); + lua_decode_longlongp(L, -1, &ll_a); + assert(ll_a == ll_b); + assert(lua_gettop(L) == 0); + time_t time_a = 100; time_t time_b; diff --git a/tests/lib/test_frrscript.c b/tests/lib/test_frrscript.c index 7d4746c..9698aea 100644 --- a/tests/lib/test_frrscript.c +++ b/tests/lib/test_frrscript.c @@ -32,7 +32,7 @@ int main(int argc, char **argv) assert(result == 0); result = frrscript_call(fs, "bar", ("a", &a), ("b", &b)); assert(result == 0); - long long *cptr = frrscript_get_result(fs, "bar", "c", lua_tointegerp); + long long *cptr = frrscript_get_result(fs, "bar", "c", lua_tolonglongp); /* a should not occur in the returned table in script */ assert(a == 100); @@ -47,7 +47,7 @@ int main(int argc, char **argv) result = frrscript_call(fs, "fact", ("n", &n)); assert(result == 0); long long *ansptr = - frrscript_get_result(fs, "fact", "ans", lua_tointegerp); + frrscript_get_result(fs, "fact", "ans", lua_tolonglongp); assert(*ansptr == 120); /* check consecutive call + get_result without re-loading */ diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp index 957ffde..2023136 100644 --- a/tests/lib/test_grpc.cpp +++ b/tests/lib/test_grpc.cpp @@ -119,8 +119,10 @@ static void static_startup(void) hook_register(routing_conf_event, routing_control_plane_protocols_name_validate); - - routing_control_plane_protocols_register_vrf_dependency(); + hook_register(routing_create, + routing_control_plane_protocols_staticd_create); + hook_register(routing_destroy, + routing_control_plane_protocols_staticd_destroy); // Add a route vty = vty_new(); diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c index 225573a..8c2765c 100644 --- a/tests/lib/test_heavy_wq.c +++ b/tests/lib/test_heavy_wq.c @@ -76,12 +76,6 @@ static wq_item_status slow_func(struct work_queue *wq, void *data) for (j = 0; j < 300; j++) x += sin(x) * j; - if ((hn->i % ITERS_LATER) == 0) - return WQ_RETRY_LATER; - - if ((hn->i % ITERS_ERR) == 0) - return WQ_RETRY_NOW; - if ((hn->i % ITERS_PRINT) == 0) printf("%s did %d, x = %g\n", hn->str, hn->i, x); diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index e267548..caf55c7 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -3,6 +3,7 @@ */ #include +#include #include #include "getopt.h" diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c index fc6b8e8..9327631 100644 --- a/tests/ospfd/test_ospf_spf.c +++ b/tests/ospfd/test_ospf_spf.c @@ -1,4 +1,5 @@ #include +#include #include "getopt.h" #include "frrevent.h" diff --git a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref index 044cffa..a4a4aba 100644 --- a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref +++ b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref @@ -8,6 +8,16 @@ C>* 192.168.6.0/26 is directly connected, r1-eth6, XX:XX:XX C>* 192.168.7.0/26 is directly connected, r1-eth7, XX:XX:XX C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX +L>* 192.168.0.1/32 is directly connected, r1-eth0, XX:XX:XX +L>* 192.168.1.1/32 is directly connected, r1-eth1, XX:XX:XX +L>* 192.168.2.1/32 is directly connected, r1-eth2, XX:XX:XX +L>* 192.168.3.1/32 is directly connected, r1-eth3, XX:XX:XX +L>* 192.168.4.1/32 is directly connected, r1-eth4, XX:XX:XX +L>* 192.168.5.1/32 is directly connected, r1-eth5, XX:XX:XX +L>* 192.168.6.1/32 is directly connected, r1-eth6, XX:XX:XX +L>* 192.168.7.1/32 is directly connected, r1-eth7, XX:XX:XX +L>* 192.168.8.1/32 is directly connected, r1-eth8, XX:XX:XX +L>* 192.168.9.1/32 is directly connected, r1-eth9, XX:XX:XX O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, weight 1, XX:XX:XX S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref index ef12d61..a25b53a 100644 --- a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref +++ b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref @@ -19,6 +19,16 @@ C * fe80::/64 is directly connected, r1-eth6, XX:XX:XX C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX +L>* fc00:0:0:1::1/128 is directly connected, r1-eth1, XX:XX:XX +L>* fc00:0:0:2::1/128 is directly connected, r1-eth2, XX:XX:XX +L>* fc00:0:0:3::1/128 is directly connected, r1-eth3, XX:XX:XX +L>* fc00:0:0:4::1/128 is directly connected, r1-eth4, XX:XX:XX +L>* fc00:0:0:5::1/128 is directly connected, r1-eth5, XX:XX:XX +L>* fc00:0:0:6::1/128 is directly connected, r1-eth6, XX:XX:XX +L>* fc00:0:0:7::1/128 is directly connected, r1-eth7, XX:XX:XX +L>* fc00:0:0:8::1/128 is directly connected, r1-eth8, XX:XX:XX +L>* fc00:0:0:9::1/128 is directly connected, r1-eth9, XX:XX:XX +L>* fc00::1/128 is directly connected, r1-eth0, XX:XX:XX O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, weight 1, XX:XX:XX S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf b/tests/topotests/all_protocol_startup/r1/ospf6d.conf index 33c2650..31c904b 100644 --- a/tests/topotests/all_protocol_startup/r1/ospf6d.conf +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf @@ -6,12 +6,12 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r1-eth4 - ipv6 ospf6 hello-interval 1 + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 1 ! router ospf6 ospf6 router-id 192.168.0.1 log-adjacency-changes - interface r1-eth4 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref index 0246687..4464e23 100644 --- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref +++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref @@ -1,4 +1,4 @@ -BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP router identifier 192.168.0.1, local AS number 100 VRF default vrf-id 0 BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 2, using XXXX KiB of memory diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref index deeae87..9baec12 100644 --- a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref +++ b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref @@ -1,4 +1,4 @@ -BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0 +BGP router identifier 192.168.0.1, local AS number 100 VRF default vrf-id 0 BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 4, using XXXX KiB of memory diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index c319477..e067cdb 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -599,16 +599,16 @@ def test_nexthop_groups(): count = 0 dups = [] nhg_id = route_get_nhg_id("6.6.6.1/32") - while (len(dups) != 3) and count < 10: + while (len(dups) != 4) and count < 10: output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) dups = re.findall(r"(via 1\.1\.1\.1)", output) - if len(dups) != 3: + if len(dups) != 4: count += 1 sleep(1) # Should find 3, itself is inactive - assert len(dups) == 3, ( + assert len(dups) == 4, ( "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops" % nhg_id ) @@ -951,26 +951,24 @@ def test_bgp_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Unicast Summary:", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Multicast Summary:", "", actual) actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 VPN Summary:", "", actual) actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv4 Encap Summary:", "", actual) actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"Unknown Summary:", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) # Make Connect/Active/Idle the same (change them all to Active) actual = re.sub(r" Connect ", " Active ", actual) actual = re.sub(r" Idle ", " Active ", actual) - actual = re.sub( - r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual - ) + actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) actual = re.sub( r"No IPv4 labeled-unicast neighbor is configured", "", actual ) @@ -1108,27 +1106,25 @@ def test_bgp_ipv6_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Unicast Summary:", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Multicast Summary:", "", actual) actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 VPN Summary:", "", actual) actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual) + actual = re.sub(r"IPv6 Encap Summary:", "", actual) actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) + actual = re.sub(r"Unknown Summary:", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) # Make Connect/Active/Idle the same (change them all to Active) actual = re.sub(r" Connect ", " Active ", actual) actual = re.sub(r" Idle ", " Active ", actual) # Remove Labeled Unicast Summary (all of it) - actual = re.sub( - r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual - ) + actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual) actual = re.sub( r"No IPv6 labeled-unicast neighbor is configured", "", actual ) diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf index 98da8c2..a8ce562 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf @@ -6,12 +6,14 @@ hostname rt1 password 1 ! interface eth-rt2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt3 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast @@ -19,7 +21,5 @@ interface eth-rt3 ! router ospf6 ospf6 router-id 1.1.1.1 - interface eth-rt2 area 0.0.0.0 - interface eth-rt3 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf index 34b0902..f04d017 100644 --- a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf @@ -5,19 +5,19 @@ hostname rt2 password 1 ! interface eth-rt1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 2.2.2.2 - interface eth-rt1 area 0.0.0.0 - interface eth-rt5 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf index 8ab4eee..faf9754 100644 --- a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf @@ -5,19 +5,19 @@ hostname rt3 password 1 ! interface eth-rt1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ipv6 ospf6 bfd ! interface eth-rt4 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 3.3.3.3 - interface eth-rt1 area 0.0.0.0 - interface eth-rt4 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf index 138b688..c96093b 100644 --- a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf @@ -5,18 +5,18 @@ hostname rt4 password 1 ! interface eth-rt3 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! interface eth-rt5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ipv6 ospf6 network broadcast ! router ospf6 ospf6 router-id 4.4.4.4 - interface eth-rt3 area 0.0.0.0 - interface eth-rt5 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf index 6eb4fe5..6d40d17 100644 --- a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf +++ b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf @@ -5,18 +5,18 @@ hostname rt5 password 1 ! interface eth-rt2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ! interface eth-rt4 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 8 ! router ospf6 ospf6 router-id 5.5.5.5 - interface eth-rt2 area 0.0.0.0 - interface eth-rt4 area 0.0.0.0 redistribute connected ! diff --git a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf index 4ef28c3..948874c 100644 --- a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf +++ b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf @@ -1,4 +1,5 @@ interface r4-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 bfd profile fast-tx ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -6,5 +7,4 @@ interface r4-eth1 router ospf6 ospf6 router-id 10.254.254.4 redistribute connected - interface r4-eth1 area 0.0.0.0 ! diff --git a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf index 20b53cf..f6e8dc3 100644 --- a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf +++ b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf @@ -1,4 +1,5 @@ interface r5-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 bfd ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -6,5 +7,4 @@ interface r5-eth0 router ospf6 ospf6 router-id 10.254.254.5 redistribute connected - interface r5-eth0 area 0.0.0.0 ! diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.py b/tests/topotests/bfd_topo3/test_bfd_topo3.py index c0dc052..f767b0e 100644 --- a/tests/topotests/bfd_topo3/test_bfd_topo3.py +++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py @@ -65,6 +65,41 @@ def setup_module(mod): tgen.start_router() +def expect_static_bfd_output(router, filename): + "Load JSON file and compare with 'show bfd peer json'" + + tgen = get_topogen() + + logger.info("waiting BFD configuration on router {}".format(router)) + bfd_config = json.loads(open("{}/{}/{}.json".format(CWD, router, filename)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd static route json", + bfd_config, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" BFD static route status failure'.format(router) + assert result is None, assertmsg + + +def expect_route_missing(router, iptype, route): + "Wait until route is present on RIB for protocol." + + tgen = get_topogen() + + logger.info("waiting route {} to disapear in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: None}, + ) + rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + def test_wait_bgp_convergence(): "Wait for BGP to converge" tgen = get_topogen() @@ -166,7 +201,7 @@ def test_wait_bfd_convergence(): expect_bfd_configuration("r6") -def test_static_route_monitoring(): +def test_static_route_monitoring_convergence(): "Test static route monitoring output." tgen = get_topogen() if tgen.routers_have_failure(): @@ -174,31 +209,47 @@ def test_static_route_monitoring(): logger.info("test BFD static route status") - def expect_static_bfd_output(router, filename): - "Load JSON file and compare with 'show bfd peer json'" - logger.info("waiting BFD configuration on router {}".format(router)) - bfd_config = json.loads( - open("{}/{}/{}.json".format(CWD, router, filename)).read() - ) - test_func = partial( - topotest.router_json_cmp, - tgen.gears[router], - "show bfd static route json", - bfd_config, - ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) - assertmsg = '"{}" BFD static route status failure'.format(router) - assert result is None, assertmsg - expect_static_bfd_output("r3", "bfd-static") expect_static_bfd_output("r6", "bfd-static") - logger.info("Setting r4 link down ...") - tgen.gears["r4"].link_enable("r4-eth0", False) +def test_static_route_monitoring_wrong_source(): + "Test that static monitoring fails if setting a wrong source." - expect_static_bfd_output("r3", "bfd-static-down") - expect_static_bfd_output("r6", "bfd-static-down") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test route wrong ") + + tgen.gears["r3"].vtysh_cmd( + """ +configure +ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop source 2001:db8:4::2 profile slow-tx +""" + ) + + expect_route_missing("r3", "ipv6", "2001:db8:5::/64") + + +def test_static_route_monitoring_unset_source(): + "Test that static monitoring fails if setting a wrong source." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test route wrong ") + + tgen.gears["r3"].vtysh_cmd( + """ +configure +ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx +""" + ) + + expect_static_bfd_output("r3", "bfd-static") + expect_static_bfd_output("r6", "bfd-static") def test_expect_static_rib_removal(): @@ -208,18 +259,12 @@ def test_expect_static_rib_removal(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def expect_route_missing(router, iptype, route): - "Wait until route is present on RIB for protocol." - logger.info("waiting route {} to disapear in {}".format(route, router)) - test_func = partial( - topotest.router_json_cmp, - tgen.gears[router], - "show {} route json".format(iptype), - {route: None}, - ) - rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1) - assertmsg = '"{}" convergence failure'.format(router) - assert result is None, assertmsg + logger.info("Setting r4 link down ...") + + tgen.gears["r4"].link_enable("r4-eth0", False) + + expect_static_bfd_output("r3", "bfd-static-down") + expect_static_bfd_output("r6", "bfd-static-down") expect_route_missing("r1", "ip", "10.254.254.5/32") expect_route_missing("r2", "ip", "10.254.254.5/32") diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py index a532f3a..9e5a68f 100644 --- a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py +++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py @@ -84,6 +84,7 @@ def setup_module(mod): router.net.set_intf_netns(rname + "-eth2", ns, up=True) for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf index 15466b4..1f7abac 100644 --- a/tests/topotests/bgp_accept_own/pe1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -25,7 +25,7 @@ router bgp 65001 vrf Customer neighbor 192.168.1.1 timers connect 1 address-family ipv4 unicast redistribute connected - label vpn export 10 + label vpn export 250 rd vpn export 192.168.1.2:2 rt vpn import 192.168.1.2:2 rt vpn export 192.168.1.2:2 @@ -40,7 +40,7 @@ router bgp 65001 vrf Service neighbor 192.168.2.1 timers 1 3 neighbor 192.168.2.1 timers connect 1 address-family ipv4 unicast - label vpn export 20 + label vpn export 350 rd vpn export 192.168.2.2:2 rt vpn import 192.168.2.2:2 rt vpn export 192.168.2.2:2 diff --git a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py index cec0692..eeac714 100644 --- a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py +++ b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py @@ -74,7 +74,8 @@ def test_bgp_maximum_prefix_invalid(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] def _bgp_converge(router): output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) @@ -86,22 +87,30 @@ def test_bgp_maximum_prefix_invalid(): } return topotest.json_cmp(output, expected) - def _bgp_aggregate_address_has_metric(router): + def _bgp_aggregate_address_has_metric(router, metric): output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) - expected = {"paths": [{"metric": 123}]} + expected = {"paths": [{"metric": metric}]} return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_converge, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) - - assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) - - test_func = functools.partial(_bgp_aggregate_address_has_metric, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) - - assert ( - result is None - ), 'Failed to see applied metric for aggregated prefix in "{}"'.format(router) + test_func = functools.partial(_bgp_converge, r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see bgp convergence in r2" + + test_func = functools.partial(_bgp_aggregate_address_has_metric, r2, 123) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see applied metric for aggregated prefix in r2" + + r1.vtysh_cmd( + """ + configure terminal + route-map aggr-rmap permit 10 + set metric 666 + """ + ) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, r2, 666) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see applied metric for aggregated prefix in r2" if __name__ == "__main__": diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 65f191b..250f1cb 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -49,6 +49,7 @@ SEQ = 0 PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" +LOC_RIB = "loc-rib" def build_topo(tgen): @@ -120,7 +121,7 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy): +def check_for_prefixes(expected_prefixes, bmp_log_type, policy): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. @@ -138,7 +139,7 @@ def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy): if "ip_prefix" in m.keys() and "bmp_log_type" in m.keys() and m["bmp_log_type"] == bmp_log_type - and m["post_policy"] == post_policy + and m["policy"] == policy ] # check for prefixes @@ -202,7 +203,7 @@ def unicast_prefixes(policy): logger.info("checking for updated prefixes") # check - test_func = partial(check_for_prefixes, prefixes, "update", policy == POST_POLICY) + test_func = partial(check_for_prefixes, prefixes, "update", policy) success, _ = topotest.run_and_expect(test_func, True, wait=0.5) assert success, "Checking the updated prefixes has been failed !." @@ -210,7 +211,7 @@ def unicast_prefixes(policy): configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) logger.info("checking for withdrawed prefxies") # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy == POST_POLICY) + test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) success, _ = topotest.run_and_expect(test_func, True, wait=0.5) assert success, "Checking the withdrawed prefixes has been failed !." @@ -239,6 +240,8 @@ def test_bmp_bgp_unicast(): unicast_prefixes(PRE_POLICY) logger.info("*** Unicast prefixes post-policy logging ***") unicast_prefixes(POST_POLICY) + logger.info("*** Unicast prefixes loc-rib logging ***") + unicast_prefixes(LOC_RIB) if __name__ == "__main__": diff --git a/tests/topotests/bgp_default_route/r1/bgpd.conf b/tests/topotests/bgp_default_route/r1/bgpd.conf index 8699d62..10ced36 100644 --- a/tests/topotests/bgp_default_route/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route/r1/bgpd.conf @@ -3,6 +3,7 @@ router bgp 65000 neighbor 192.168.255.2 remote-as 65001 neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast + network 0.0.0.0/1 neighbor 192.168.255.2 default-originate exit-address-family ! diff --git a/tests/topotests/bgp_default_route/r1/zebra.conf b/tests/topotests/bgp_default_route/r1/zebra.conf index 0a283c0..fbf97b0 100644 --- a/tests/topotests/bgp_default_route/r1/zebra.conf +++ b/tests/topotests/bgp_default_route/r1/zebra.conf @@ -1,4 +1,6 @@ ! +ip route 0.0.0.0/1 blackhole +! interface lo ip address 172.16.255.254/32 ! 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 2463b05..333beb0 100644 --- a/tests/topotests/bgp_default_route/test_bgp_default-originate.py +++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py @@ -69,18 +69,18 @@ def test_bgp_default_originate_route_map(): expected = { "192.168.255.1": { "bgpState": "Established", - "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, } } 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}}}} + expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 2}}}} 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")) + def _bgp_route_is_valid(router, prefix): + output = json.loads(router.vtysh_cmd("show ip bgp {} json".format(prefix))) expected = {"paths": [{"valid": True}]} return topotest.json_cmp(output, expected) @@ -92,10 +92,14 @@ def test_bgp_default_originate_route_map(): 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" - test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"]) + test_func = functools.partial(_bgp_route_is_valid, tgen.gears["r2"], "0.0.0.0/0") 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" + test_func = functools.partial(_bgp_route_is_valid, tgen.gears["r2"], "0.0.0.0/1") + 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/1 in r2" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf index 6f6d394..c442e06 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf @@ -1,12 +1,17 @@ -router bgp 65000 - no bgp ebgp-requires-policy - neighbor 192.168.255.2 remote-as 65001 - neighbor 192.168.255.2 timers 3 10 - address-family ipv4 unicast - neighbor 192.168.255.2 default-originate route-map default - exit-address-family +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as external + neighbor 192.168.255.2 timers 3 10 + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + bgp listen range 192.168.255.0/24 peer-group PG + address-family ipv4 unicast + neighbor PG default-originate route-map default + neighbor 192.168.255.2 default-originate route-map default + exit-address-family ! route-map default permit 10 - set metric 123 - set as-path prepend 65000 65000 65000 + set metric 123 + set as-path prepend 65001 65001 65001 ! diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf index 0a283c0..4af88e5 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf @@ -5,5 +5,3 @@ interface lo interface r1-eth0 ip address 192.168.255.1/24 ! -ip forwarding -! diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf index 00c96cc..3a18a11 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf @@ -1,8 +1,8 @@ -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 +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as external + 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_route_map_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf index 606c17b..c03dd7e 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf @@ -2,5 +2,3 @@ interface r2-eth0 ip address 192.168.255.2/24 ! -ip forwarding -! diff --git a/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf new file mode 100644 index 0000000..c477037 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as external + 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_route_map_set/r3/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf new file mode 100644 index 0000000..5ae9daf --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_set/r3/zebra.conf @@ -0,0 +1,4 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! 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 3bd900b..e633b61 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 @@ -26,12 +26,13 @@ pytestmark = [pytest.mark.bgpd] def build_topo(tgen): - for routern in range(1, 3): + for routern in range(1, 4): tgen.add_router("r{}".format(routern)) switch = tgen.add_switch("s1") switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) def setup_module(mod): @@ -62,14 +63,17 @@ def test_bgp_default_originate_route_map(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] - def _bgp_converge(router): + def _bgp_converge(router, pfxCount): output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) expected = { "192.168.255.1": { "bgpState": "Established", - "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + "addressFamilyInfo": { + "ipv4Unicast": {"acceptedPrefixCounter": pfxCount} + }, } } return topotest.json_cmp(output, expected) @@ -77,21 +81,25 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) expected = { - "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + "paths": [{"aspath": {"string": "65001 65001 65001 65001"}, "metric": 123}] } return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_converge, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + test_func = functools.partial(_bgp_converge, r2, 1) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see bgp convergence in r2" - assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + test_func = functools.partial(_bgp_default_route_has_metric, r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see applied metric for default route in r2" - test_func = functools.partial(_bgp_default_route_has_metric, router) - success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + test_func = functools.partial(_bgp_converge, r3, 2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see bgp convergence in r3" - assert ( - result is None - ), 'Failed to see applied metric for default route in "{}"'.format(router) + test_func = functools.partial(_bgp_default_route_has_metric, r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see applied metric for default route in r3" if __name__ == "__main__": diff --git a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py index ab71f87..9a0f562 100644 --- a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py +++ b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py @@ -122,8 +122,9 @@ def test_bgp_check_fqdn(): step("Wait to converge") test_func = functools.partial(bgp_converge, r1) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Can't converge with dont-capability-negotiate" + assert result is None, "Can't converge with all capabilities" + step("Make sure FQDN capability is set") test_func = functools.partial(_bgp_check_fqdn, "r2") _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "FQDN capability enabled, but r1 can't see it" @@ -150,6 +151,48 @@ def test_bgp_check_fqdn(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "FQDN capability disabled, but we still have a hostname" + step("Re-enable sending any capability from r2") + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + address-family ipv4 unicast + no neighbor 192.168.1.1 dont-capability-negotiate + end + clear bgp 192.168.1.1 + """ + ) + step("Wait to converge") + tgen = get_topogen() + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with all capabilities re enabled" + + step("Make sure FQDN capability is r2") + test_func = functools.partial(_bgp_check_fqdn, "r2") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability enabled, but r1 can't see it" + + step("Disable sending fqdn capability") + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + no neighbor 192.168.1.1 capability fqdn + end + clear bgp 192.168.1.1 + """ + ) + step("Wait to converge") + tgen = get_topogen() + test_func = functools.partial(bgp_converge, r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge with no capability fqdn" + + step("Make sure FQDN capability is reset") + test_func = functools.partial(_bgp_check_fqdn) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "FQDN capability disabled, but we still have a hostname" if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/r1/frr.conf b/tests/topotests/bgp_dynamic_capability/r1/frr.conf index 50280a9..aa5c3db 100644 --- a/tests/topotests/bgp_dynamic_capability/r1/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r1/frr.conf @@ -12,4 +12,10 @@ router bgp 65001 neighbor 192.168.1.2 timers 1 3 neighbor 192.168.1.2 timers connect 1 neighbor 192.168.1.2 capability dynamic + ! + address-family ipv4 unicast + neighbor 192.168.1.2 addpath-tx-all-paths + exit-address-family +! +ip prefix-list r2 seq 5 permit 10.10.10.10/32 ! diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf index 16b83a5..7f25665 100644 --- a/tests/topotests/bgp_dynamic_capability/r2/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf @@ -3,6 +3,7 @@ ! int lo ip address 10.10.10.10/32 + ip address 10.10.10.20/32 ! int r2-eth0 ip address 192.168.1.2/24 diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py new file mode 100644 index 0000000..5202f51 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if Addpath capability is adjusted dynamically. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_addpath(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertised": True, + "rxAdvertisedAndReceived": True, + } + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step("Enable Addpath capability and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # change the settings fo LLGR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 addpath-tx-all-paths + """ + ) + + def _bgp_check_if_addpath_rx_tx_and_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": True, + "rxAdvertisedAndReceived": True, + } + }, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_rx_tx_and_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after enabling Addpath capability" + + step("Disable Addpath capability RX and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # disable addpath-rx. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 disable-addpath-rx + """ + ) + + def _bgp_check_if_addpath_tx_and_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": True, + "rxAdvertised": True, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + "capabilityRecv": 1, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_tx_and_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after disabling Addpath RX flags" + + # Clear message stats to check if we receive a notification or not after we + # disable Addpath capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 addpath-tx-all-paths + """ + ) + + def _bgp_check_if_addpath_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "addPath": { + "ipv4Unicast": { + "txAdvertisedAndReceived": None, + "txAdvertised": None, + "rxAdvertised": True, + } + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_addpath_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable Addpath capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py new file mode 100644 index 0000000..338886d --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_fqdn.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis +# + +""" +Test if fqdn capability is exchanged dynamically. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_fqdn(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "hostName": { + "advHostName": "r1", + "rcvHostName": "r2", + }, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step("Disable fqdn capability and check if it's exchanged dynamically") + + # Clear message stats to check if we receive a notification or not after we + # disable fqdn capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 capability fqdn + """ + ) + + def _bgp_check_if_fqdn_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "hostName": { + "advHostName": None, + "rcvHostName": "r2", + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_fqdn_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable fqdn capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py index 75c712e..4644ef3 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py @@ -70,7 +70,7 @@ def test_bgp_dynamic_capability_graceful_restart(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "gracefulRestartInfo": { @@ -123,7 +123,7 @@ def test_bgp_dynamic_capability_graceful_restart(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "gracefulRestartInfo": { @@ -159,7 +159,7 @@ def test_bgp_dynamic_capability_graceful_restart(): ) # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # disable graceful-restart notification support. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r2.vtysh_cmd( """ @@ -207,6 +207,40 @@ def test_bgp_dynamic_capability_graceful_restart(): result is None ), "Session was reset after changing Graceful-Restart notification support" + # Clear message stats to check if we receive a notification or not after we + # disable GR. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp graceful-restart-disable + """ + ) + + def _bgp_check_if_gr_llgr_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "gracefulRestartCapability": "received", + "longLivedGracefulRestart": "received", + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_gr_llgr_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable GR/LLGR capabilities" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py new file mode 100644 index 0000000..ba95bd1 --- /dev/null +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_orf.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if ORF capability is adjusted dynamically. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_orf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 3, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + step( + "Apply incoming prefix-list to r1 and check if we advertise only 10.10.10.20/32 from r2" + ) + + # Clear message stats to check if we receive a notification or not after we + # enable ORF capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.2 prefix-list r2 in + neighbor 192.168.1.2 capability orf prefix-list both + """ + ) + + r2.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + neighbor 192.168.1.1 capability orf prefix-list both + """ + ) + + def _bgp_check_if_session_not_reset(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "messageStats": { + "notificationsRecv": 0, + "notificationsSent": 0, + "capabilityRecv": 1, + "capabilitySent": 1, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 1, + "afDependentCap": { + "orfPrefixList": { + "sendMode": "advertisedAndReceived", + "recvMode": "advertisedAndReceived", + } + }, + "incomingUpdatePrefixFilterList": "r2", + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_session_not_reset, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Session was reset after setting up ORF capability" + + r1.vtysh_cmd( + """ + configure terminal + ip prefix-list r2 seq 5 permit 10.10.10.20/32 + """ + ) + + def _bgp_check_if_we_send_correct_prefix(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.1 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.20/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_we_send_correct_prefix, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Only 10.10.10.20/32 SHOULD be advertised due to ORF filtering" + + # Clear message stats to check if we receive a notification or not after we + # disable ORF capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 capability orf prefix-list both + """ + ) + + def _bgp_check_if_orf_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + }, + "messageStats": { + "notificationsRecv": 0, + "notificationsSent": 0, + }, + "addressFamilyInfo": { + "ipv4Unicast": { + "acceptedPrefixCounter": 1, + "afDependentCap": { + "orfPrefixList": { + "sendMode": "received", + "recvMode": "received", + } + }, + } + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_orf_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable ORF capability" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py index 576ef74..aa9ad5f 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py @@ -68,7 +68,7 @@ def test_bgp_dynamic_capability_role(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, } @@ -84,7 +84,7 @@ def test_bgp_dynamic_capability_role(): step("Set local-role and check if it's exchanged dynamically") # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # change the role. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r1.vtysh_cmd( """ @@ -115,7 +115,7 @@ def test_bgp_dynamic_capability_role(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "messageStats": { @@ -132,6 +132,41 @@ def test_bgp_dynamic_capability_role(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Session was reset after setting role capability" + # Clear message stats to check if we receive a notification or not after we + # change the role. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 local-role customer + """ + ) + + def _bgp_check_if_role_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "localRole": "undefined", + "remoteRole": "provider", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "role": "received", + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_role_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable role capability" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py index 002d782..737e694 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py @@ -70,7 +70,7 @@ def test_bgp_dynamic_capability_software_version(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, } @@ -86,7 +86,7 @@ def test_bgp_dynamic_capability_software_version(): step("Enable software version capability and check if it's exchanged dynamically") # Clear message stats to check if we receive a notification or not after we - # change the settings fo LLGR. + # enable software-version capability. r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") r1.vtysh_cmd( """ @@ -136,7 +136,7 @@ def test_bgp_dynamic_capability_software_version(): }, "addressFamilyInfo": { "ipv4Unicast": { - "acceptedPrefixCounter": 2, + "acceptedPrefixCounter": 3, } }, "messageStats": { @@ -155,6 +155,41 @@ def test_bgp_dynamic_capability_software_version(): result is None ), "Session was reset after enabling software version capability" + # Clear message stats to check if we receive a notification or not after we + # disable software-version capability. + r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats") + r1.vtysh_cmd( + """ + configure terminal + router bgp + no neighbor 192.168.1.2 capability software-version + """ + ) + + def _bgp_check_if_software_version_capability_is_absent(): + output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) + expected = { + "192.168.1.2": { + "bgpState": "Established", + "neighborCapabilities": { + "dynamic": "advertisedAndReceived", + "softwareVersion": { + "advertisedSoftwareVersion": None, + }, + }, + "messageStats": { + "notificationsRecv": 0, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_software_version_capability_is_absent, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to disable software version capability" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_ecmp_topo1/exabgp.env b/tests/topotests/bgp_ecmp_topo1/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_ecmp_topo1/exabgp.env +++ b/tests/topotests/bgp_ecmp_topo1/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg index 2d0ca89..97a024c 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.101; - local-address 10.0.1.101; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg index 0c842a0..e318162 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 10 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 10"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 10 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.110; - local-address 10.0.2.110; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 10; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.110; + local-address 10.0.2.110; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg index 936dc57..ea5bdcc 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 11 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 11"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 11 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.111; - local-address 10.0.3.111; - local-as 111; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 11; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.111; + local-address 10.0.3.111; + local-as 111; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg index 56b33ea..81fb503 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 12 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 12"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 12 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.112; - local-address 10.0.3.112; - local-as 112; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 12; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.112; + local-address 10.0.3.112; + local-as 112; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg index b933ffb..4007841 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 13 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 13"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 13 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.113; - local-address 10.0.3.113; - local-as 113; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 13; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.113; + local-address 10.0.3.113; + local-as 113; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg index bcfa41e..afb8741 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 14 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 14"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 14 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.114; - local-address 10.0.3.114; - local-as 114; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 14; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.114; + local-address 10.0.3.114; + local-as 114; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg index 022e835..9a4ca7f 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 15 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 15"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 15 10; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.115; - local-address 10.0.3.115; - local-as 115; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 15; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.115; + local-address 10.0.3.115; + local-as 115; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg index 0649202..a0bb88a 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 16 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 16"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 16 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.116; - local-address 10.0.4.116; - local-as 116; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 16; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.116; + local-address 10.0.4.116; + local-as 116; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg index 0aeeed9..870d33d 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 17 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 17"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 17 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.117; - local-address 10.0.4.117; - local-as 117; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 17; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.117; + local-address 10.0.4.117; + local-as 117; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg index 352c030..c5a1ca6 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 18 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 18"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 18 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.118; - local-address 10.0.4.118; - local-as 118; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 18; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.118; + local-address 10.0.4.118; + local-as 118; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg index 9913c22..f662ccf 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 19 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 19"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 19 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.119; - local-address 10.0.4.119; - local-as 119; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 19; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.119; + local-address 10.0.4.119; + local-as 119; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg index 46b436d..673d6ec 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 2 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 2 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.102; - local-address 10.0.1.102; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.102; + local-address 10.0.1.102; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg index 17fb816..5ea2c91 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 20 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 20"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 20 10; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.120; - local-address 10.0.4.120; - local-as 120; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 20; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.120; + local-address 10.0.4.120; + local-as 120; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg index acd5775..47a25ca 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 3 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 3 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.103; - local-address 10.0.1.103; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.103; + local-address 10.0.1.103; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg index 4c9a989..376ac5f 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 4 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 4 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.104; - local-address 10.0.1.104; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.104; + local-address 10.0.1.104; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg index eba2aae..878d728 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 5 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 5"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 5 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.105; - local-address 10.0.1.105; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 5; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.105; + local-address 10.0.1.105; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg index 38b6af0..fc70267 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 6 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 6"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 6 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.106; - local-address 10.0.2.106; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 6; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.106; + local-address 10.0.2.106; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg index 7631e43..09827a8 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 7 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 7"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 7 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.107; - local-address 10.0.2.107; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 7; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.107; + local-address 10.0.2.107; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg index 1cd1cd9..37c01da 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 8 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 8"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 8 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.108; - local-address 10.0.2.108; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 8; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.108; + local-address 10.0.2.108; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py index 6bef355..1a8e6bf 100755 --- a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg index 5771553..8fb5c58 100644 --- a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg +++ b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 9 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 9"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 9 10; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.109; - local-address 10.0.2.109; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 9; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.109; + local-address 10.0.2.109; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py index 97751ec..7af3f48 100644 --- a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py +++ b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # SPDX-License-Identifier: ISC # diff --git a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py index ec52278..945c038 100644 --- a/tests/topotests/bgp_evpn_mh/test_evpn_mh.py +++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py @@ -264,7 +264,7 @@ def config_bridge(node): node.run("ip link set dev bridge type bridge mcast_snooping 0") node.run("ip link set dev bridge type bridge vlan_stats_enabled 1") node.run("ip link set dev bridge up") - node.run("/sbin/bridge vlan add vid 1000 dev bridge") + node.run("/sbin/bridge vlan add vid 1000 dev bridge self") def config_vxlan(node, node_ip): 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 1066269..2041a40 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 @@ -58,7 +58,6 @@ from lib.common_config import ( step, write_test_header, write_test_footer, - generate_support_bundle, ) # Required to instantiate the topology builder class. @@ -277,8 +276,6 @@ def test_evpn_gateway_ip_basic_topo(request): result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg write_test_footer(tc_name) @@ -319,8 +316,6 @@ def test_evpn_gateway_ip_flap_rt5(request): ) result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") - if result is not None: - generate_support_bundle() assert result is None, assertmsg step("Advertise type-5 routes again") @@ -339,8 +334,6 @@ def test_evpn_gateway_ip_flap_rt5(request): ) result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg @@ -371,8 +364,6 @@ def test_evpn_gateway_ip_flap_rt2(request): pe1.cmd_raises("ip link set dev vxlan100 down") result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") - if result is not None: - generate_support_bundle() assert result is None, assertmsg step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") @@ -380,8 +371,6 @@ def test_evpn_gateway_ip_flap_rt2(request): pe1.cmd_raises("ip link set dev vxlan100 up") result, assertmsg = evpn_gateway_ip_show_op_check("base") - if result is not None: - generate_support_bundle() assert result is None, assertmsg write_test_footer(tc_name) diff --git a/tests/topotests/bgp_evpn_route_map_match/__init__.py b/tests/topotests/bgp_evpn_route_map_match/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf b/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf new file mode 100644 index 0000000..7476a37 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/c1/frr.conf @@ -0,0 +1,4 @@ +! +int c1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf b/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf new file mode 100644 index 0000000..a203daa --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/c2/frr.conf @@ -0,0 +1,4 @@ +! +int c2-eth0 + ip address 192.168.0.2/24 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf new file mode 100644 index 0000000..4347052 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/r1/frr.conf @@ -0,0 +1,44 @@ +! +!debug bgp neighbor +!debug route-map detail +! +vni 10 +! +int lo + ip address 10.10.10.1/32 +! +int r1-eth1 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + network 10.10.10.10/32 + exit-address-family + ! + address-family l2vpn evpn + neighbor 192.168.1.2 activate + neighbor 192.168.1.2 route-map r2 out + advertise-all-vni + advertise ipv4 unicast + exit-address-family +! +route-map r2 deny 10 + match evpn route-type macip +! +route-map r2 deny 20 + match ip address prefix-list pl + match evpn route-type prefix +! +route-map r2 permit 30 +! +ip prefix-list pl seq 5 permit 192.168.1.0/24 +ip prefix-list pl seq 10 permit 10.10.10.1/32 +ip prefix-list pl seq 15 permit 10.10.10.2/32 +! diff --git a/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf new file mode 100644 index 0000000..9ed298d --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/r2/frr.conf @@ -0,0 +1,24 @@ +! +!debug bgp neighbor +! +int lo + ip address 10.10.10.2/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family l2vpn evpn + neighbor 192.168.1.1 activate + advertise-all-vni + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py new file mode 100644 index 0000000..5781684 --- /dev/null +++ b/tests/topotests/bgp_evpn_route_map_match/test_bgp_evpn_route_map_match.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if route-map match by EVPN route-type works. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("c1", "r1"), "s2": ("r1", "r2"), "s3": ("r2", "c2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + tgen.net["r1"].cmd( + """ +ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.1 nolearning +ip link add name br10 type bridge +ip link set dev vxlan10 master br10 +ip link set dev r1-eth0 master br10 +ip link set up dev br10 +ip link set up dev vxlan10""" + ) + + tgen.net["r2"].cmd( + """ +ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.2 nolearning +ip link add name br10 type bridge +ip link set dev vxlan10 master br10 +ip link set dev r2-eth1 master br10 +ip link set up dev br10 +ip link set up dev vxlan10""" + ) + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_evpn_route_map_match_route_type(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + r1.vtysh_cmd( + "show bgp l2vpn evpn neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.1:1": { + "[5]:[0]:[32]:[10.10.10.10]": { + "valid": True, + } + }, + "10.10.10.2:2": { + "[3]:[0]:[32]:[10.10.10.2]": { + "valid": True, + } + }, + }, + "totalPrefixCounter": 2, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Filtered EVPN routes should not be advertised" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 7e4bcc8..d9177b4 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -132,6 +132,7 @@ def setup_module(mod): for rname, router in router_list.items(): if rname == "r1": + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json new file mode 100644 index 0000000..0b5b1a1 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.after.json @@ -0,0 +1 @@ +{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[16]:[10.27.0.0]":{"prefix":"[5]:[0]:[16]:[10.27.0.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":16,"ip":"10.27.0.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":2,"numPaths":2} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json new file mode 100644 index 0000000..67417b5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgp.evpn.route.prefix.before.json @@ -0,0 +1 @@ +{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[24]:[10.27.7.0]":{"prefix":"[5]:[0]:[24]:[10.27.7.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":24,"ip":"10.27.7.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[30]:[10.27.7.8]":{"prefix":"[5]:[0]:[30]:[10.27.7.8]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":30,"ip":"10.27.7.8","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[32]:[10.27.7.22]":{"prefix":"[5]:[0]:[32]:[10.27.7.22]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":32,"ip":"10.27.7.22","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":4,"numPaths":4} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf index 9fb2bd6..b58219a 100644 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf @@ -11,9 +11,22 @@ router bgp 65000 advertise-svi-ip ! router bgp 65000 vrf vrf-red + address-family ipv4 unicast + redistribute static + exit-address-family ! address-family l2vpn evpn route-target import *:300 route-target import auto exit-address-family ! +router bgp 65000 vrf vrf-purple + address-family ipv4 unicast + redistribute static + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf index 8c6cf3e..cea60a8 100644 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf @@ -5,6 +5,12 @@ vrf vrf-red vni 100 exit-vrf ! +vrf vrf-purple + vni 4000 + ip route 10.27.7.0/24 blackhole + ip route 10.27.7.22/32 blackhole + ip route 10.27.7.10/30 blackhole +exit-vrf ! interface lo ip address 10.10.10.10/32 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py index 65c0c35..fd75c34 100755 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -91,6 +91,10 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("ip link add name bridge type bridge stp_state 0") pe.run("ip link set dev bridge type bridge vlan_filtering 1") pe.run("bridge vlan add vid 1 dev bridge self") + if pe_name == "PE1": + pe.run("ip link set dev bridge address 44:00:00:ff:ff:01") + if pe_name == "PE2": + pe.run("ip link set dev bridge address 44:20:00:ff:ff:01") pe.run("ip link set dev bridge up") # setup svi @@ -113,6 +117,18 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101") pe.run("ip link set up dev vxlan0") + # VRF creation + pe.run("ip link add vrf-purple type vrf table 1003") + pe.run("ip link set dev vrf-purple up") + if pe_name == "PE1": + pe.run("ip link add vrf-blue type vrf table 1002") + pe.run("ip link set dev vrf-blue up") + pe.run("ip addr add 27.2.0.85/32 dev vrf-blue") + pe.run("ip addr add 27.3.0.85/32 dev vrf-purple") + if pe_name == "PE2": + pe.run("ip link add vrf-blue type vrf table 2400") + pe.run("ip link set dev vrf-blue up") + # setup PE interface pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf)) pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf)) @@ -120,7 +136,7 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf)) pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf)) - # l3vni 100 + # L3VNI 100 pe.run("ip link add vrf-red type vrf table 1400") pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q") pe.run("ip link set dev vlan100 master vrf-blue") @@ -129,9 +145,16 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 100") pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100") + # L3VNI 4000 + pe.run("ip link add link bridge name vlan400 type vlan id 400 protocol 802.1q") + pe.run("ip link set dev vlan400 master vrf-purple") + pe.run("ip link set dev vlan400 up") + pe.run("bridge vlan add vid 400 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 400") + pe.run("bridge vlan add dev vxlan0 vid 400 tunnel_info id 4000") + # add a vrf for testing DVNI if pe_name == "PE2": - pe.run("ip link add vrf-blue type vrf table 2400") pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q") pe.run("ip link set dev vlan300 master vrf-blue") pe.run("ip link set dev vlan300 up") @@ -199,6 +222,14 @@ def show_vni_json_elide_ifindex(pe, vni, expected): return topotest.json_cmp(output_json, expected) +def show_bgp_l2vpn_evpn_route_type_prefix_json(pe, expected): + output_json = pe.vtysh_cmd( + "show bgp l2vpn evpn route type prefix json", isjson=True + ) + + return topotest.json_cmp(output_json, expected) + + def check_vni_macs_present(tgen, router, vni, maclist): result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) for rname, ifname in maclist: @@ -331,6 +362,7 @@ def mac_test_local_remote(local, remote): remote_output_json = json.loads(remote_output) local_output_vni_json = json.loads(local_output_vni) + # breakpoint() for vni in local_output_json: if vni not in remote_output_json: continue @@ -542,6 +574,42 @@ def test_dvni(): # tgen.mininet_cli() +def test_pe_advertise_aggr_evpn_route(): + "BGP advertise aggregated Type-5 prefix route" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP EVPN route contains non-aggregate prefixes") + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/bgp.evpn.route.prefix.before.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + assert result is None, assertmsg + + logger.info("Configure BGP aggregate-address summary-only under ipv4-unicast") + pe1.cmd( + 'vtysh -c "config t" -c "router bgp 65000 vrf vrf-purple" ' + + ' -c "address-family ipv4 unicast" ' + + ' -c "aggregate-address 10.27.0.0/16 summary-only" ' + ) + + logger.info("Checking BGP EVPN route contains aggregated prefix") + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/bgp.evpn.route.prefix.after.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + assert result is None, assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf index 9afc6f4..3e6196e 100644 --- a/tests/topotests/bgp_features/r1/ospf6d.conf +++ b/tests/topotests/bgp_features/r1/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r1-lo + ipv6 ospf6 area 0.0.0.0 ! interface r1-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! interface r1-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! router ospf6 ospf6 router-id 192.168.0.1 log-adjacency-changes - interface r1-lo area 0.0.0.0 - interface r1-eth1 area 0.0.0.0 - interface r1-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf index 7fe5356..56aecd0 100644 --- a/tests/topotests/bgp_features/r2/ospf6d.conf +++ b/tests/topotests/bgp_features/r2/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r2-lo + ipv6 ospf6 area 0.0.0.0 ! interface r2-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! interface r2-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 10 ! router ospf6 ospf6 router-id 192.168.0.2 log-adjacency-changes - interface r2-lo area 0.0.0.0 - interface r2-eth1 area 0.0.0.0 - interface r2-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf index 07325b6..f15b9d9 100644 --- a/tests/topotests/bgp_features/r3/ospf6d.conf +++ b/tests/topotests/bgp_features/r3/ospf6d.conf @@ -3,19 +3,19 @@ log file ospf6d.log ! debug ospf6 neighbor ! interface r3-lo + ipv6 ospf6 area 0.0.0.0 ! interface r3-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! interface r3-eth2 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 priority 5 ! router ospf6 ospf6 router-id 192.168.0.3 log-adjacency-changes - interface r3-lo area 0.0.0.0 - interface r3-eth1 area 0.0.0.0 - interface r3-eth2 area 0.0.0.0 ! line vty ! diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py index a2be859..57aeea8 100644 --- a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -51,7 +51,6 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger -from lib.common_config import generate_support_bundle # Required to instantiate the topology builder class. @@ -140,10 +139,7 @@ def test_bgp_convergence(): ) _, res = topotest.run_and_expect(test_func, None, count=210, wait=1) assertmsg = "BGP router network did not converge" - if res is not None: - generate_support_bundle() - assert res is None, assertmsg - generate_support_bundle() + assert res is None, assertmsg def test_bgp_flowspec(): diff --git a/tests/topotests/bgp_l3vpn_label_export/__init__.py b/tests/topotests/bgp_l3vpn_label_export/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf new file mode 100644 index 0000000..bb1ed4c --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65001 + bgp router-id 192.0.2.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 2 + address-family ipv4 vpn + neighbor 192.0.2.2 activate + exit-address-family +! +router bgp 65001 vrf vrf1 + address-family ipv4 unicast + redistribute connected + label vpn export 1111 + rd vpn export 101:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf new file mode 100644 index 0000000..04ae068 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/ldpd.conf @@ -0,0 +1,26 @@ +hostname r1 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.1 + ! + address-family ipv4 + discovery transport-address 192.0.2.1 + ! + interface r1-eth0 + ! + interface r1-eth1 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf b/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf new file mode 100644 index 0000000..7f2f057 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.1.2 diff --git a/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf b/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf new file mode 100644 index 0000000..7bdacb1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf new file mode 100644 index 0000000..18a11cf --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/bgpd.conf @@ -0,0 +1,23 @@ +router bgp 65002 + bgp router-id 192.0.2.2 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as 65001 + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 update-source 192.0.2.2 + address-family ipv4 vpn + neighbor 192.168.1.1 activate + exit-address-family +! +router bgp 65002 vrf vrf1 + address-family ipv4 unicast + redistribute connected + label vpn export 2222 + rd vpn export 102:1 + rt vpn both 52:100 + import vpn + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf new file mode 100644 index 0000000..f4307f1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/ldpd.conf @@ -0,0 +1,24 @@ +hostname r2 +log file ldpd.log +password zebra +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 192.0.2.2 + ! + address-family ipv4 + discovery transport-address 192.0.2.2 + ! + interface r2-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf b/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf new file mode 100644 index 0000000..e3f5d7d --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.1/32 192.168.1.1 diff --git a/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf b/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf new file mode 100644 index 0000000..40dfa98 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/r2/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py b/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py new file mode 100644 index 0000000..7c23a3e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_label_export/test_bgp_l3vpn_label_export.py @@ -0,0 +1,587 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by Louis Scalbert +# Copyright 2023 6WIND S.A. +# + +""" + +""" + +import os +import re +import sys +import json +import pytest +import functools + +from copy import deepcopy + +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.common_config import kill_router_daemons, start_router_daemons, step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for rtr in [1, 2]: + tgen.add_router("r{}".format(rtr)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rtr in [1, 2]: + tgen.gears["r{}".format(rtr)].cmd("ip link add vrf1 type vrf table 10") + tgen.gears["r{}".format(rtr)].cmd("ip link set vrf1 up") + tgen.gears["r{}".format(rtr)].cmd( + "ip address add dev vrf1 192.0.3.{}/32".format(rtr) + ) + tgen.gears["r{}".format(rtr)].run( + "sysctl -w net.mpls.conf.r{}-eth0.input=1".format(rtr) + ) + tgen.gears["r{}".format(rtr)].run("sysctl -w net.mpls.conf.vrf1.input=1") + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def check_bgp_vpn_prefix(label, rname="r1", rd=None): + tgen = get_topogen() + + if rd: + output = json.loads( + tgen.gears[rname].vtysh_cmd( + "show bgp ipv4 vpn rd {} 192.0.3.2/32 json".format(rd) + ) + ) + else: + output = json.loads( + tgen.gears[rname].vtysh_cmd( + "show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json" + ) + ) + + if label == "auto": + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2"}], + }, + ] + } + elif label and not rd: + expected = { + "paths": [ + { + "valid": True, + "remoteLabel": label, + "aspath": {"string": "65002"}, + "nexthops": [{"ip": "192.0.2.2"}], + }, + ] + } + elif label and rd: + expected = { + "102:1": { + "prefix": "192.0.3.2/32", + "paths": [ + { + "valid": True, + "remoteLabel": label, + "nexthops": [{"ip": "0.0.0.0"}], + } + ], + } + } + else: + expected = {} + + return topotest.json_cmp(output, expected, exact=(label is None)) + + +def check_mpls_table(label, protocol): + tgen = get_topogen() + + if label == "auto": + cmd = "show mpls table json" + else: + cmd = "show mpls table {} json".format(label) + + output = json.loads(tgen.gears["r2"].vtysh_cmd(cmd)) + + if label == "auto" and protocol: + output_copy = deepcopy(output) + for key, data in output_copy.items(): + for nexthop in data.get("nexthops", []): + if nexthop.get("type", None) != protocol: + continue + output = data + break + + if protocol: + expected = { + "nexthops": [ + { + "type": protocol, + }, + ] + } + else: + expected = {} + + return topotest.json_cmp(output, expected, exact=(protocol is None)) + + +def check_mpls_ldp_binding(): + tgen = get_topogen() + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show mpls ldp binding 192.0.2.2/32 json") + ) + expected = { + "bindings": [ + { + "prefix": "192.0.2.2/32", + "localLabel": "16", # first available label + "inUse": 1, + }, + ] + } + + return topotest.json_cmp(output, expected) + + +def test_convergence(): + "Test protocol convergence" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check BGP and LDP convergence") + test_func = functools.partial(check_bgp_vpn_prefix, 2222) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, 2222, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: [2222/2222]" in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_16(): + "Test that assigning the label value of 16 is not possible because it used by LDP" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export 16" + ) + + step("Check that label vpn export 16 fails") + test_func = functools.partial(check_bgp_vpn_prefix, None) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, 2222, None) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp" not in output, "Unexpected BGP label chunk" + + +def test_vpn_label_export_2222(): + "Test that setting back the label value of 2222 works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export 2222" + ) + + step("Check that label vpn export 2222 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 2222) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: [2222/2222]" in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_auto(): + "Test that setting label vpn export auto works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + + step("Check that label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, "auto") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_no_auto(): + "Test that UNsetting label vpn export auto removes the prefix from R1 table and R2 LDP table" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + auto_label = output.get("paths")[0].get("remoteLabel", None) + assert auto_label is not None, "Failed to fetch prefix label on R1" + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "no label vpn export auto" + ) + + step("Check that no label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 3, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, auto_label, None) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " not in output, "Unexpected BGP label chunk" + + +def test_vpn_label_export_auto_back(): + "Test that setting back label vpn export auto works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + + step("Check that label vpn export auto is OK") + test_func = functools.partial(check_bgp_vpn_prefix, "auto") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R1" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, "auto", "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_export_manual_from_auto(): + "Test that setting a manual label value from the BGP chunk range works" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.3.2/32 json") + ) + + auto_label = output.get("paths")[0].get("remoteLabel", None) + assert auto_label is not None, "Failed to fetch prefix label on R1" + + auto_label = auto_label + 1 + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export {}".format(auto_label) + ) + + step("Check that label vpn export {} is OK".format(auto_label)) + test_func = functools.partial( + check_bgp_vpn_prefix, auto_label, rname="r2", rd="102:1" + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefix on R2" + + test_func = functools.partial(check_mpls_ldp_binding) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP binding on R2" + + test_func = functools.partial(check_mpls_table, 16, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + test_func = functools.partial(check_mpls_table, auto_label, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Failed to see BGP label on R2" + + output = tgen.net["r2"].cmd("vtysh -c 'show debugging label-table' | grep Proto") + assert re.match( + r"Proto ldp: \[16/(1[7-9]|[2-9]\d+|\d{3,})\]", output + ), "Failed to see LDP label chunk" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +def test_vpn_label_configure_dynamic_range(): + "Test that if a dynamic range is configured, then the next dynamic allocations will be done in that block" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + tgen.gears["r2"].vtysh_cmd("conf\n" "mpls label dynamic-block 500 1000\n") + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + step("Check that label vpn export auto starting at 500 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 500, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_table, 500, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + + +def test_vpn_label_restart_ldp(): + "Test that if a dynamic range is configured, then when LDP restarts, it follows the new dynamic range" + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("Kill LDP on R2") + kill_router_daemons(tgen, "r2", ["ldpd"]) + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto ldp: " not in output, "Unexpected LDP label chunk" + + step("Bring up LDP on R2") + + start_router_daemons(tgen, "r2", ["ldpd"]) + + test_func = functools.partial(check_mpls_table, 628, "LDP") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see LDP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto ldp: [628/691]" in output, "Failed to see LDP label chunk [628/691]" + assert "Proto ldp: [692/755]" in output, "Failed to see LDP label chunk [692/755]" + + +def test_vpn_label_unconfigure_dynamic_range(): + "Test that if the dynamic range is unconfigured, then the next dynamic allocations will be done at the first free place." + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + tgen.gears["r2"].vtysh_cmd("conf\n" "no mpls label dynamic-block 500 1000\n") + step("Check that unconfiguring label vpn export auto will remove BGP label chunk") + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "no label vpn export auto" + ) + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " not in output, "Unexpected BGP label chunk" + + tgen.gears["r2"].vtysh_cmd( + "conf\n" + "router bgp 65002 vrf vrf1\n" + "address-family ipv4 unicast\n" + "label vpn export auto" + ) + step("Check that label vpn export auto starting at 16 is OK") + test_func = functools.partial(check_bgp_vpn_prefix, 16, rname="r2", rd="102:1") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected BGP prefix on R2" + + test_func = functools.partial(check_mpls_table, 16, "BGP") + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, "Unexpected BGP label on R2" + + output = tgen.gears["r2"].vtysh_cmd("show debugging label-table") + assert "Proto bgp: " in output, "Failed to see BGP label chunk" diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 0ac5350..45c44ba 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -161,6 +161,17 @@ def ltemplatePreRouterStartHook(): logger.info( "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr) ) + # configure cust4 VRFs & MPLS + cmds = [ + "ip link add {0}-cust4 type vrf table 30", + "ip link set dev {0}-cust4 up", + "ip link add {0}-cust5 type vrf table 40", + "ip link set dev {0}-cust5 up", + ] + rtr = "r1" + for cmd in cmds: + cc.doCmd(tgen, rtr, cmd.format(rtr)) + logger.info("setup {0} vrf {0}-cust3 and{0}-cust4.".format(rtr)) # configure cust2 VRFs & MPLS rtrs = ["r4"] cmds = [ diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf index 72211fe..0d652da 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -50,6 +50,31 @@ router bgp 5227 vrf r1-cust1 export vpn exit-address-family +router bgp 5227 vrf r1-cust4 + no bgp network import-check -! -end + bgp router-id 192.168.1.1 + + address-family ipv4 unicast + network 172.16.0.0/24 + + rd vpn export 10:14 + rt vpn export 52:100 + + import vpn + export vpn + exit-address-family + +router bgp 5227 vrf r1-cust5 + bgp router-id 192.168.1.1 + + address-family ipv4 unicast + network 172.16.1.1/32 + + label vpn export 105 + rd vpn export 10:15 + rt vpn both 52:100 + + import vpn + export vpn + exit-address-family diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf index 221bc7a..9a5b0a6 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf @@ -18,6 +18,10 @@ interface r1-eth4 ip address 192.168.1.1/24 no link-detect +interface r1-cust5 + ip address 172.16.1.1/32 + no link-detect + ip forwarding diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py index 1e2758c..217657d 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -59,12 +59,28 @@ want_r1_cust1_routes = [ {"p": "5.1.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.2.0/24", "n": "99.0.0.1"}, + {"p": "172.16.0.0/24", "n": "0.0.0.0", "bp": True}, + {"p": "172.16.1.1/32", "n": "0.0.0.0", "bp": True}, {"p": "99.0.0.1/32", "n": "192.168.1.2"}, ] bgpribRequireUnicastRoutes( "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes ) +want_r1_cust4_routes = [ + {"p": "172.16.0.0/24", "n": "0.0.0.0", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust4", "Customer 4 routes in r1 vrf", want_r1_cust4_routes +) + +want_r1_cust5_routes = [ + {"p": "172.16.1.1/32", "n": "0.0.0.0", "bp": True}, +] +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust5", "Customer 5 routes in r1 vrf", want_r1_cust5_routes +) + want_r3_cust1_routes = [ {"p": "5.1.0.0/24", "n": "99.0.0.2"}, {"p": "5.1.1.0/24", "n": "99.0.0.2"}, @@ -667,7 +683,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce1", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 12", + "14 routes and 14", "wait", "Local and remote routes", 10, @@ -689,7 +705,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce2", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 15", + "14 routes and 17", "wait", "Local and remote routes", 10, @@ -721,7 +737,7 @@ luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"') luCommand( "ce3", 'vtysh -c "show bgp ipv4 uni"', - "12 routes and 13", + "14 routes and 15", "wait", "Local and remote routes", 10, @@ -743,7 +759,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce4", 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"', - "12 routes and 14", + "14 routes and 16", "wait", "Local and remote routes", 10, @@ -875,4 +891,11 @@ luCommand( "pass", "Redundant route 2 details", ) +luCommand( + "r1", + 'vtysh -c "show ip route vrf r1-cust5 5.1.0.0/24"', + "Known via .bgp., distance 200, .* vrf r1-cust5, best", + "pass", + "Recursive route leak details", +) # done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py index 36be926..190879c 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -49,7 +49,7 @@ if ret != False and found != None: luCommand( rtr, 'vtysh -c "show bgp ipv4 uni" | grep Display', - " 12 route", + " 14 route", "wait", "BGP routes removed", wait, diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py index 46993c7..e05bf21 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -226,7 +226,7 @@ else: ave_b = float(delta_b) / float(num) luCommand( rtr, - 'vtysh -c "show thread cpu"', + 'vtysh -c "show event cpu"', ".", "pass", "BGPd heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( @@ -239,7 +239,7 @@ else: ) luCommand( rtr, - 'vtysh -c "show thread cpu"', + 'vtysh -c "show event cpu"', ".", "pass", "Zebra heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json index 3c02e26..bb50bc9 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":25 + "weight":85 }, { "fib":true, "ip":"11.1.1.2", - "weight":75 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json index 3c2d42c..57726ce 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.1.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json index 3d80018..f2368df 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.1.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.1.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json index 6ed3f8e..7b4da2a 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json @@ -7,7 +7,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json index 95531d9..3062d1c 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json @@ -7,7 +7,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json index beac501..662b7f7 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json @@ -12,7 +12,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json index eb27ce2..d9b5a89 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json @@ -12,7 +12,7 @@ { "fib":true, "ip":"11.1.1.2", - "weight":100 + "weight":255 } ] } diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json index 7e2fa6b..53be117 100644 --- a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json @@ -7,12 +7,12 @@ { "fib":true, "ip":"11.1.2.6", - "weight":33 + "weight":127 }, { "fib":true, "ip":"11.1.2.2", - "weight":66 + "weight":255 } ] } diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json index d35e4ef..17b9acc 100644 --- a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json @@ -2,5 +2,5 @@ "ledger":0, "inUse":0, "requests":0, - "labelChunks":1 + "labelChunks":0 } diff --git a/tests/topotests/bgp_multiview_topo1/exabgp.env b/tests/topotests/bgp_multiview_topo1/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_multiview_topo1/exabgp.env +++ b/tests/topotests/bgp_multiview_topo1/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg index 20e71c8..0303230 100644 --- a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.1; - local-address 172.16.1.1; - local-as 65001; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.1; + local-address 172.16.1.1; + local-as 65001; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg index 1e8eef1..13670c3 100644 --- a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 2 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 2 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.2; - local-address 172.16.1.2; - local-as 65002; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.2; + local-address 172.16.1.2; + local-as 65002; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg index ef1b249..0afc484 100644 --- a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 3 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 3 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.3; - local-address 172.16.1.3; - local-as 65003; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.3; + local-address 172.16.1.3; + local-as 65003; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg index 7c50f73..0f5b536 100644 --- a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 4 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 4 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.4; - local-address 172.16.1.4; - local-as 65004; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.4; + local-address 172.16.1.4; + local-as 65004; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg index 22163c7..365aa6b 100644 --- a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 5 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 5"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 5 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.5; - local-address 172.16.1.5; - local-as 65005; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 5; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.5; + local-address 172.16.1.5; + local-as 65005; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg index 40b54c3..1038033 100644 --- a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 6 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 6"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 6 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.6; - local-address 172.16.1.6; - local-as 65006; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 6; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.6; + local-address 172.16.1.6; + local-as 65006; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg index 33312d0..7411338 100644 --- a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 7 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 7"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 7 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.7; - local-address 172.16.1.7; - local-as 65007; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 7; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.7; + local-address 172.16.1.7; + local-as 65007; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py index 09f6ea5..0e50d46 100755 --- a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg index 173ccb9..17a4e49 100644 --- a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg +++ b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 8 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 8"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 8 10; + encoder text; +} - neighbor 172.16.1.254 { - router-id 172.16.1.8; - local-address 172.16.1.8; - local-as 65008; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 8; + encoder text; +} +neighbor 172.16.1.254 { + router-id 172.16.1.8; + local-address 172.16.1.8; + local-as 65008; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_oad/__init__.py b/tests/topotests/bgp_oad/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_oad/r1/frr.conf b/tests/topotests/bgp_oad/r1/frr.conf new file mode 100644 index 0000000..39045ba --- /dev/null +++ b/tests/topotests/bgp_oad/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.1.2 oad + neighbor 192.168.1.4 remote-as external + neighbor 192.168.1.4 timers 1 3 + neighbor 192.168.1.4 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.1.4 route-map r4 in + exit-address-family +! +route-map r4 permit 10 + set local-preference 123 + set metric 123 +exit diff --git a/tests/topotests/bgp_oad/r2/frr.conf b/tests/topotests/bgp_oad/r2/frr.conf new file mode 100644 index 0000000..fdd23f3 --- /dev/null +++ b/tests/topotests/bgp_oad/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.1.1 oad + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers 1 3 + neighbor 192.168.2.3 timers connect 1 + neighbor 192.168.2.3 oad +! diff --git a/tests/topotests/bgp_oad/r3/frr.conf b/tests/topotests/bgp_oad/r3/frr.conf new file mode 100644 index 0000000..02dd5ad --- /dev/null +++ b/tests/topotests/bgp_oad/r3/frr.conf @@ -0,0 +1,22 @@ +! +int lo + ip address 10.10.10.10/32 +! +int r3-eth0 + ip address 192.168.2.3/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor 192.168.2.2 oad + ! + address-family ipv4 unicast + redistribute connected route-map connected + exit-address-family +! +route-map connected permit 10 + set local-preference 123 + set metric 123 +! diff --git a/tests/topotests/bgp_oad/r4/frr.conf b/tests/topotests/bgp_oad/r4/frr.conf new file mode 100644 index 0000000..83dcf39 --- /dev/null +++ b/tests/topotests/bgp_oad/r4/frr.conf @@ -0,0 +1,16 @@ +! +int r4-eth0 + ip address 192.168.1.4/24 +! +int r4-eth1 + ip address 192.168.4.4/24 +! +router bgp 65004 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.4.5 remote-as external + neighbor 192.168.4.5 timers 1 3 + neighbor 192.168.4.5 timers connect 1 +! diff --git a/tests/topotests/bgp_oad/r5/frr.conf b/tests/topotests/bgp_oad/r5/frr.conf new file mode 100644 index 0000000..f8e1609 --- /dev/null +++ b/tests/topotests/bgp_oad/r5/frr.conf @@ -0,0 +1,17 @@ +! +int lo + ip address 10.10.10.10/32 +! +int r5-eth0 + ip address 192.168.4.5/24 +! +router bgp 65005 + no bgp ebgp-requires-policy + neighbor 192.168.4.4 remote-as external + neighbor 192.168.4.4 timers 1 3 + neighbor 192.168.4.4 timers connect 1 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_oad/test_bgp_oad.py b/tests/topotests/bgp_oad/test_bgp_oad.py new file mode 100644 index 0000000..b26c548 --- /dev/null +++ b/tests/topotests/bgp_oad/test_bgp_oad.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if local-preference is passed between different EBGP peers when +EBGP-OAD is configured. +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r4"), "s2": ("r2", "r3"), "s3": ("r4", "r5")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_dynamic_capability_role(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10/32 json")) + expected = { + "paths": [ + { + "aspath": {"string": "65002 65003"}, + "metric": 123, + "locPrf": 123, + "peer": { + "hostname": "r2", + "type": "external (oad)", + }, + }, + { + "aspath": {"string": "65004 65005"}, + "metric": 123, + "locPrf": 123, + "bestpath": {"selectionReason": "Peer Type"}, + "peer": { + "hostname": "r4", + "type": "external", + }, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env index 6c554f5..989228a 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env +++ b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false @@ -43,7 +44,7 @@ enable = false file = '' [exabgp.reactor] -speed = 1.0 +speed = 5.0 [exabgp.tcp] acl = false diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg index 4a7dc48..e6606d2 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.2; - local-address 10.0.1.2; - local-as 64510; - peer-as 64510; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.2; + local-address 10.0.1.2; + local-as 64510; + peer-as 64510; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg index b53b054..6a6ba0b 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 2"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in; + encoder text; +} - neighbor 10.0.2.1 { - router-id 10.0.2.2; - local-address 10.0.2.2; - local-as 64511; - peer-as 64511; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} +neighbor 10.0.2.1 { + router-id 10.0.2.2; + local-address 10.0.2.2; + local-as 64511; + peer-as 64511; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg index 6a1cc2f..9714b1f 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 3"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in; + encoder text; +} - neighbor 10.0.3.1 { - router-id 10.0.3.2; - local-address 10.0.3.2; - local-as 64502; - peer-as 64501; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} +neighbor 10.0.3.1 { + router-id 10.0.3.2; + local-address 10.0.3.2; + local-as 64502; + peer-as 64501; + api {processes [ announce-routes, receive-routes ];} } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py index 0f998c1..6c1f8b0 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 "Helper script to read api commands from a pipe and feed them to ExaBGP" import sys diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg index 2cc26cb..8c38a88 100644 --- a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg @@ -1,21 +1,17 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; - encoder text; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 4"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in; + encoder text; +} - neighbor 10.0.4.1 { - router-id 10.0.4.2; - local-address 10.0.4.2; - local-as 64503; - peer-as 64501; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} +neighbor 10.0.4.1 { + router-id 10.0.4.2; + local-address 10.0.4.2; + local-as 64503; + peer-as 64501; + api {processes [ announce-routes, receive-routes ];} } 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 ad6674c..9239be9 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 @@ -62,6 +62,19 @@ from lib.topolog import logger pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] +# Prefixes used in the test +prefix1 = "203.0.113.0/30" +prefix2 = "203.0.113.4/30" +prefix3 = "203.0.113.8/30" +# Next hops used for iBGP/confed routes +resolved_nh1 = "198.51.100.1" +resolved_nh2 = "198.51.100.2" +# BGP route used for recursive resolution +bgp_resolving_prefix = "198.51.100.0/24" +# Next hop that will require non-connected recursive resolution +ebgp_resolved_nh = "198.51.100.10" + + def build_topo(tgen): "Build function" @@ -125,36 +138,26 @@ def teardown_module(mod): tgen.stop_topology() -def test_bgp_peer_type_multipath_relax(): +def exabgp_cmd(peer, cmd): + pipe = open("/run/exabgp_{}.in".format(peer), "w") + with pipe: + pipe.write(cmd) + pipe.close() + + +def test_bgp_peer_type_multipath_relax_test1(): tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def exabgp_cmd(peer, cmd): - pipe = open("/run/exabgp_{}.in".format(peer), "w") - with pipe: - pipe.write(cmd) - pipe.close() - - # Prefixes used in the test - prefix1 = "203.0.113.0/30" - prefix2 = "203.0.113.4/30" - prefix3 = "203.0.113.8/30" - # Next hops used for iBGP/confed routes - resolved_nh1 = "198.51.100.1" - resolved_nh2 = "198.51.100.2" - # BGP route used for recursive resolution - bgp_resolving_prefix = "198.51.100.0/24" - # Next hop that will require non-connected recursive resolution - ebgp_resolved_nh = "198.51.100.10" + r1 = tgen.gears["r1"] # Send a non-connected route to resolve others exabgp_cmd( "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix) ) - router = tgen.gears["r1"] # It seems that if you write to the exabgp socket too quickly in # succession, requests get lost. So verify prefix1 now instead of @@ -177,7 +180,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -185,6 +188,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Mixed-type multipath not found" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test2(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Create and verify eBGP and iBGP+confed multipaths") exabgp_cmd( "peer1", @@ -203,38 +216,66 @@ def test_bgp_peer_type_multipath_relax(): reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Not all expected multipaths found" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test3(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Toggle peer-type multipath-relax and verify the changes") - router.vtysh_cmd( + r1.vtysh_cmd( "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" ) # This file verifies "multipath" is not set reffile = os.path.join(CWD, "r1/not-multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Disabling peer-type multipath-relax did not take effect" assert res is None, assertMsg - router.vtysh_cmd( - "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n" - ) + +def test_bgp_peer_type_multipath_relax_test4(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n") reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show ip bgp json", expected + topotest.router_json_cmp, r1, "show ip bgp json", expected ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Reenabling peer-type multipath-relax did not take effect" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test5(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Check recursive resolution of eBGP next hops is not affected") # eBGP next hop resolution rejects recursively resolved next hops by # default, even with peer-type multipath-relax @@ -245,7 +286,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix3), expected, ) @@ -253,6 +294,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test6(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + exabgp_cmd( "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh) ) @@ -260,7 +311,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -268,14 +319,24 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test7(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # When other config allows recursively resolved eBGP next hops, # such next hops in all-eBGP multipaths should be valid - router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") + r1.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") reffile = os.path.join(CWD, "r1/prefix3-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix3), expected, ) @@ -287,7 +348,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip bgp {} json".format(prefix1), expected, ) @@ -295,6 +356,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test8(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + logger.info("Check mixed-type multipath next hop recursive resolution in FIB") # There are now two eBGP-learned routes with a recursively resolved next; # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ @@ -305,7 +376,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix3), expected, ) @@ -313,6 +384,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "FIB next hops mismatch for all-eBGP multipath" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test9(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # check confed-external enables recursively resolved next hops by itself exabgp_cmd( "peer1", @@ -324,7 +405,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix1), expected, ) @@ -332,6 +413,16 @@ def test_bgp_peer_type_multipath_relax(): assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" assert res is None, assertMsg + +def test_bgp_peer_type_multipath_relax_test10(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # check iBGP by itself exabgp_cmd( "peer1", @@ -349,7 +440,7 @@ def test_bgp_peer_type_multipath_relax(): expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, - router, + r1, "show ip route {} json".format(prefix1), expected, ) diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env index 6c554f5..bb36af5 100644 --- a/tests/topotests/bgp_prefix_sid/exabgp.env +++ b/tests/topotests/bgp_prefix_sid/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg index 5b55366..a5108ff 100644 --- a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg @@ -1,103 +1,101 @@ -group controller { - neighbor 10.0.0.1 { - router-id 10.0.0.101; - local-address 10.0.0.101; - local-as 2; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; - family { - ipv4 nlri-mpls; - } + family { + ipv4 nlri-mpls; + } - static { - # ref: draft-ietf-idr-bgp-prefix-sid-27 - # - # IANA temporarily assigned the following: - # attribute code type (suggested value: 40) to - # the BGP Prefix-SID attribute - # - # 0 1 2 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Type | Length | RESERVED | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Flags | Label Index | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Label Index | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # Figure. Label-Index TLV (Prefix-SID type-1) - # - # 0 1 2 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Type | Length | Flags | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Flags | - # +-+-+-+-+-+-+-+-+ - # - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | SRGB 1 (6 octets) | - # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | SRGB n (6 octets) | - # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # Figure. Originator SRGB TLV (Prefix-SID type-3) + static { + # ref: draft-ietf-idr-bgp-prefix-sid-27 + # + # IANA temporarily assigned the following: + # attribute code type (suggested value: 40) to + # the BGP Prefix-SID attribute + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | RESERVED | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Label-Index TLV (Prefix-SID type-1) + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | Flags | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | + # +-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB 1 (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB n (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Originator SRGB TLV (Prefix-SID type-3) - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000001 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000001 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000002 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000002 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; - # ExaBGP generic-attribute binary pattern: - # Attribute-type: 0x28 (40:BGP_PREFIX_SID) - # Attribute-flag: 0xc0 (Option, Transitive) - # Attribute-body: Label-Index TLV and Originator SRGB TLV - # Label-Index TLV: 0x01000700000000000001 - # Type (08bit): 0x01 - # Length (16bit): 0x0007 - # RESERVED (08bit): 0x00 - # Flags (16bit): 0x0000 - # Label Index (32bit): 0x00000003 - # Originator SRGB TLV: 0x03000800000c350000000a - # Type (08bit): 0x03 - # Length (16bit): 0x0008 (nb-SRGB is 1) - # Flags (16bit): 0x0000 - # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) - route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; - } + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000003 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; } } diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg index 379d0a3..50ebdc2 100644 --- a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg @@ -1,19 +1,22 @@ -group controller { - - process receive-routes { - run "/etc/exabgp/exa-receive.py --no-timestamp 2"; - receive-routes; - encoder json; - } +process receive-routes { + run /etc/exabgp/exa-receive.py --no-timestamp 2; + encoder json; +} - neighbor 10.0.0.1 { - router-id 10.0.0.102; - local-address 10.0.0.102; - local-as 3; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.102; + local-address 10.0.0.102; + local-as 3; + peer-as 1; - family { - ipv4 nlri-mpls; + family { + ipv4 nlri-mpls; + } + api { + processes [ receive-routes ]; + receive { + parsed; + update; } } } 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 bfc083b..1e6e731 100644 --- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -120,13 +120,9 @@ def exabgp_get_update_prefix(filename, afi, nexthop, prefix): ret = ret.get(afi) if ret is None: continue - ret = ret.get(nexthop) - if ret is None: - continue - ret = ret.get(prefix) - if ret is None: - continue - return output + for nh in ret.get(nexthop, []): + if nh.get("nlri") == prefix: + return output return "Not found" @@ -135,33 +131,39 @@ def test_peer2_receive_prefix_sid_type1(): peer2 = tgen.gears["peer2"] logfile = "{}/{}-received.log".format(peer2.gearlogdir, peer2.name) - def _check_type1_peer2(prefix, labelindex): + def _check_type1_peer2(prefix, label): output = exabgp_get_update_prefix( logfile, "ipv4 nlri-mpls", "10.0.0.101", prefix ) expected = { "type": "update", "neighbor": { - "ip": "10.0.0.1", + "address": { + "peer": "10.0.0.1", + }, "message": { "update": { - "attribute": { - "attribute-0x28-0xE0": "0x010007000000{:08x}".format( - labelindex - ) + "announce": { + "ipv4 nlri-mpls": { + "10.0.0.101": [ + { + "nlri": prefix, + "label": [[label]], + } + ] + } }, - "announce": {"ipv4 nlri-mpls": {"10.0.0.101": {}}}, } }, }, } return topotest.json_cmp(output, expected) - test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", labelindex=1) + test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", label=8001) success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") - test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", labelindex=2) + test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", label=8002) success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg index 3819179..7a291a3 100644 --- a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg @@ -1,29 +1,27 @@ -group controller { - neighbor 10.0.0.1 { - router-id 10.0.0.101; - local-address 10.0.0.101; - local-as 2; - peer-as 1; +neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; - family { - ipv6 mpls-vpn; - } + family { + ipv6 mpls-vpn; + } - static { - route 2001:1::/64 { - rd 2:10; - next-hop 2001::2; - extended-community [ target:2:10 ]; - label 3; - attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; - } - route 2001:2::/64 { - rd 2:10; - next-hop 2001::2; - extended-community [ target:2:10 ]; - label 3; - attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; - } + static { + route 2001:1::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; + } + route 2001:2::/64 { + rd 2:10; + next-hop 2001::2; + extended-community [ target:2:10 ]; + label 3; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; } } } diff --git a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf index ddc1f07..b3ca0e1 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf +++ b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf @@ -1,5 +1,4 @@ log stdout notifications -log monitor notifications !log commands ! !debug bgp zebra @@ -22,5 +21,7 @@ router bgp 1 ! address-family ipv6 vpn neighbor 10.0.0.101 activate + neighbor 10.0.0.101 route-map DENY_ALL out exit-address-family ! +route-map DENY_ALL deny 10 diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json index 42293b1..65c51f1 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json @@ -1,10 +1,6 @@ { "2:10":{ "prefix":"2001:1::\/64", - "advertisedTo":{ - "10.0.0.101":{ - } - }, "paths":[ { "aspath":{ diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json index c9ad871..4a12c2a 100644 --- a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json +++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json @@ -1,10 +1,6 @@ { "2:10":{ "prefix":"2001:2::\/64", - "advertisedTo":{ - "10.0.0.101":{ - } - }, "paths":[ { "aspath":{ diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index 35459f6..88251fa 100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -257,10 +257,10 @@ def test_recursive_routes_iBGP_peer_p1(request): ) dut = "r2" create_interface_in_kernel( - tgen, dut, "lo", "40.40.40.50", netmask="255.255.255.0", create=True + tgen, dut, "lo10", "40.40.40.50", netmask="255.255.255.0", create=True ) create_interface_in_kernel( - tgen, dut, "lo", "40:40::40:50", netmask="120", create=True + tgen, dut, "lo10", "40:40::40:50", netmask="120", create=True ) for addr_type in ADDR_TYPES: input_dict_3 = { diff --git a/tests/topotests/bgp_redistribute_table/__init__.py b/tests/topotests/bgp_redistribute_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_redistribute_table/r1/bgpd.conf b/tests/topotests/bgp_redistribute_table/r1/bgpd.conf new file mode 100644 index 0000000..c5e0fcd --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + address-family ipv4 unicast + neighbor 192.168.0.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json new file mode 100644 index 0000000..e5a27f3 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_all_redistribute.json @@ -0,0 +1,71 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.15/32": [ + { + "prefix": "172.31.0.15/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json new file mode 100644 index 0000000..1304edd --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_with_redistribute.json @@ -0,0 +1,48 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json new file mode 100644 index 0000000..74f594f --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/ipv4_routes_without_redistribute.json @@ -0,0 +1,25 @@ +{ + "172.31.0.2/32": [ + { + "prefix": "172.31.0.2/32", + "protocol": "bgp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_redistribute_table/r1/zebra.conf b/tests/topotests/bgp_redistribute_table/r1/zebra.conf new file mode 100644 index 0000000..abe6d39 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r1-eth1 + ip address 172.31.0.1/32 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_redistribute_table/r2/bgpd.conf b/tests/topotests/bgp_redistribute_table/r2/bgpd.conf new file mode 100644 index 0000000..2ce7043 --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.2/32 + neighbor 192.168.0.1 activate + redistribute table-direct 2200 + exit-address-family +! diff --git a/tests/topotests/bgp_redistribute_table/r2/zebra.conf b/tests/topotests/bgp_redistribute_table/r2/zebra.conf new file mode 100644 index 0000000..89ad2ec --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/r2/zebra.conf @@ -0,0 +1,8 @@ +log stdout +interface r2-eth0 + ip address 192.168.0.2/24 +! +interface r2-eth1 + ip address 172.31.0.2/32 + ip address 172.31.1.2/24 +! diff --git a/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py b/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py new file mode 100644 index 0000000..08b70ae --- /dev/null +++ b/tests/topotests/bgp_redistribute_table/test_bgp_redistribute_table.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_redistribute_table.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" + test_bgp_redistribute_table.py: Test the FRR BGP daemon with 'redistribute table-direct' +""" + +import os +import sys +import json +from functools import partial +import pytest + +# 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.common_config import step +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + 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_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def _router_json_cmp_exact_filter(router, cmd, expected): + output = router.vtysh_cmd(cmd) + logger.info("{}: {}\n{}".format(router.name, cmd, output)) + + json_output = json.loads(output) + + # filter out tableVersion, version, nhVrfId and vrfId + for route, attrs in json_output.items(): + for attr in attrs: + if "table" in attr: + attr.pop("table") + if "internalStatus" in attr: + attr.pop("internalStatus") + if "internalFlags" in attr: + attr.pop("internalFlags") + if "internalNextHopNum" in attr: + attr.pop("internalNextHopNum") + if "internalNextHopActiveNum" in attr: + attr.pop("internalNextHopActiveNum") + if "nexthopGroupId" in attr: + attr.pop("nexthopGroupId") + if "installedNexthopGroupId" in attr: + attr.pop("installedNexthopGroupId") + if "uptime" in attr: + attr.pop("uptime") + if "prefixLen" in attr: + attr.pop("prefixLen") + if "asPath" in attr: + attr.pop("asPath") + for nexthop in attr.get("nexthops", []): + if "flags" in nexthop: + nexthop.pop("flags") + if "interfaceIndex" in nexthop: + nexthop.pop("interfaceIndex") + + return topotest.json_cmp(json_output, expected, exact=True) + + +def _check_zebra_rib_r1(with_redistributed_route, with_second_route=False): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + with_str = "" if with_redistributed_route else "out" + + router = tgen.gears["r1"] + if with_redistributed_route: + with_str = "" + if with_second_route: + json_file = "{}/{}/ipv4_routes_with_all_redistribute.json".format( + CWD, router.name + ) + else: + json_file = "{}/{}/ipv4_routes_with_redistribute.json".format( + CWD, router.name + ) + else: + with_str = "out" + json_file = "{}/{}/ipv4_routes_without_redistribute.json".format( + CWD, router.name + ) + + step(f"Checking IPv4 routes for convergence on r1 with{with_str} kernel route") + expected = json.loads(open(json_file).read()) + test_func = partial( + _router_json_cmp_exact_filter, + router, + "show ip route bgp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def _test_add_and_check_kernel_route_on_table_2200(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("r2, adding new kernel route 172.31.0.10/32 on table 2200") + cmd = "ip route add 172.31.0.10/32 via 172.31.1.10 table 2200" + tgen.net["r2"].cmd(cmd) + + _check_zebra_rib_r1(True) + + +def test_step1_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking IPv4 routes for convergence on r1") + _check_zebra_rib_r1(False) + + +def test_step2_add_kernel_route_on_table_2200(): + """ + On r2, create a kernel route on table 2200 + * Check that the kernel route is redistributed to r1 + """ + _test_add_and_check_kernel_route_on_table_2200() + + +def test_step3_remove_kernel_route_on_table_2200(): + """ + On r2, remove a kernel route on table 2200 + * Check that the kernel route is no more redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + step("r2, remove a kernel route on table 2200") + cmd = "ip route delete 172.31.0.10/32 via 172.31.1.10 table 2200" + tgen.net["r2"].cmd(cmd) + + _check_zebra_rib_r1(False) + + +def test_step4_add_kernel_route_on_table_2200(): + """ + On r2, add a kernel route on table 2200 + * Check that the kernel route is redistributed to r1 + """ + _test_add_and_check_kernel_route_on_table_2200() + + +def test_step5_no_redistribute_table_2200(): + """ + On r2, unconfigure the 'no redistribute' service + * Check that the 'redistribute' command is not configured + * Check that the kernel route is not redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 unicast\nno redistribute table-direct\n" + ) + + step("r2, check that the 'redistribute' command is not configured") + out = tgen.net["r2"].cmd( + "vtysh -c 'show running-config' | grep 'redistribute table-direct'" + ) + + if "redistribute" in out: + assert True, "r2, redistribute command still present" + + _check_zebra_rib_r1(False) + + +def test_step6_redistribute_table_2200(): + """ + On r2, configure the 'redistribute' service + * Check that the 'redistribute' command is configured + * Check that the kernel route is redistributed to r1 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r2"].vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 unicast\nredistribute table-direct 2200\n" + ) + + step("r2, check that the 'redistribute' command is configured") + out = tgen.net["r2"].cmd( + "vtysh -c 'show running-config' | grep 'redistribute table-direct'" + ) + if "redistribute" not in out: + assert True, "r2, redistribute command still present" + + _check_zebra_rib_r1(True) + + +def test_step7_reset_bgp_instance_add_kernel_route_and_add_bgp(): + """ + On r2, remove BGP configuration, create a kernel route on table 2200, + then restore BGP configuration + * Check that the kernel route is redistributed to r1 + """ + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_list = tgen.routers() + + router = tgen.gears["r2"] + step("r2, removing r2 BGP configuration") + router.vtysh_cmd("configure terminal\nno router bgp 65501\n") + + step("r2, adding new kernel route 172.31.0.15/32 on table 2200") + cmd = "ip route add 172.31.0.15/32 via 172.31.1.100 table 2200" + tgen.net["r2"].cmd(cmd) + + router = tgen.gears["r2"] + step("r2, restoring r2 BGP configuration") + tgen.net["r2"].cmd("vtysh -f {}".format(os.path.join(CWD, "r2/bgpd.conf"))) + + _check_zebra_rib_r1(True, with_second_route=True) + + +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_route_server_client/r1/bgpd.conf b/tests/topotests/bgp_route_server_client/r1/bgpd.conf index 9826b67..e464e6c 100644 --- a/tests/topotests/bgp_route_server_client/r1/bgpd.conf +++ b/tests/topotests/bgp_route_server_client/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65001 bgp router-id 10.10.10.1 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 2001:db8:1::1 remote-as external neighbor 2001:db8:1::1 timers 3 10 neighbor 2001:db8:1::1 timers connect 5 diff --git a/tests/topotests/bgp_rpki_topo1/__init__.py b/tests/topotests/bgp_rpki_topo1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf new file mode 100644 index 0000000..437d393 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65530 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 3 + neighbor 192.0.2.2 update-source 192.0.2.1 + address-family ipv4 unicast + network 198.51.100.0/24 + network 203.0.113.0/24 + network 10.0.0.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_rpki_topo1/r1/rtrd.py b/tests/topotests/bgp_rpki_topo1/r1/rtrd.py new file mode 100755 index 0000000..bca58a6 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/rtrd.py @@ -0,0 +1,330 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright (C) 2023 Tomas Hlavacek (tmshlvck@gmail.com) + +from typing import List, Tuple, Callable, Type +import socket +import threading +import socketserver +import struct +import ipaddress +import csv +import os +import sys + +LISTEN_HOST, LISTEN_PORT = "0.0.0.0", 15432 +VRPS_FILE = os.path.join(sys.path[0], "vrps.csv") + + +def dbg(m: str): + print(m) + sys.stdout.flush() + + +class RTRDatabase(object): + def __init__(self, vrps_file: str) -> None: + self.last_serial = 0 + self.ann4 = [] + self.ann6 = [] + self.withdraw4 = [] + self.withdraw6 = [] + + with open(vrps_file, "r") as fh: + for rasn, rnet, rmaxlen, _ in csv.reader(fh): + try: + net = ipaddress.ip_network(rnet) + asn = int(rasn[2:]) + maxlen = int(rmaxlen) + if net.version == 6: + self.ann6.append((asn, str(net), maxlen)) + elif net.version == 4: + self.ann4.append((asn, str(net), maxlen)) + else: + raise ValueError(f"Unknown AFI: {net.version}") + except Exception as e: + dbg( + f"VRPS load: ignoring {str((rasn, rnet,rmaxlen))} because {str(e)}" + ) + + def get_serial(self) -> int: + return self.last_serial + + def set_serial(self, serial: int) -> None: + self.last_serial = serial + + def get_announcements4(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.ann4 + else: + return [] + + def get_withdrawals4(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.withdraw4 + else: + return [] + + def get_announcements6(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.ann6 + else: + return [] + + def get_withdrawals6(self, serial: int = 0) -> List[Tuple[int, str, int]]: + if serial > self.last_serial: + return self.withdraw6 + else: + return [] + + +class RTRConnHandler(socketserver.BaseRequestHandler): + PROTO_VERSION = 0 + + def setup(self) -> None: + self.session_id = 2345 + self.serial = 1024 + + dbg(f"New connection from: {str(self.client_address)} ") + # TODO: register for notifies + + def finish(self) -> None: + pass + # TODO: de-register + + HEADER_LEN = 8 + + def decode_header(self, buf: bytes) -> Tuple[int, int, int, int]: + # common header in all received packets + return struct.unpack("!BBHI", buf) + # reutnrs (proto_ver, pdu_type, sess_id, length) + + SERNOTIFY_TYPE = 0 + SERNOTIFY_LEN = 12 + + def send_sernotify(self, serial: int) -> None: + # serial notify PDU + dbg(f" None: + # cache response PDU + dbg(f"Serial query: {serial}") + if sess_id: + self.server.db.set_serial(serial) + else: + self.server.db.set_serial(0) + self.send_cacheresponse() + + for asn, ipnet, maxlen in self.server.db.get_announcements4(serial): + self.announce_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_withdrawals4(serial): + self.withdraw_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_announcements6(serial): + self.announce_ipv6(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_withdrawals6(serial): + self.withdraw_ipv6(ipnet, asn, maxlen) + + self.send_endofdata(self.serial) + + RESET_TYPE = 2 + + def handle_reset(self): + dbg(">Reset") + self.session_id += 1 + self.server.db.set_serial(0) + self.send_cacheresponse() + + for asn, ipnet, maxlen in self.server.db.get_announcements4(self.serial): + self.announce_ipv4(ipnet, asn, maxlen) + + for asn, ipnet, maxlen in self.server.db.get_announcements6(self.serial): + self.announce_ipv6(ipnet, asn, maxlen) + + self.send_endofdata(self.serial) + + ERROR_TYPE = 10 + + def handle_error(self, buf: bytes): + dbg(f">Error: {str(buf)}") + self.server.shutdown() + self.server.stopped = True + raise ConnectionError("Received an RPKI error packet from FRR. Exiting") + + def handle(self): + while True: + b = self.request.recv(self.HEADER_LEN, socket.MSG_WAITALL) + if len(b) == 0: + break + proto_ver, pdu_type, sess_id, length = self.decode_header(b) + dbg( + f">Header proto_ver={proto_ver} pdu_type={pdu_type} sess_id={sess_id} length={length}" + ) + + if sess_id: + self.session_id = sess_id + + if pdu_type == self.SERIAL_QUERY_TYPE: + b = self.request.recv( + self.SERIAL_QUERY_LEN - self.HEADER_LEN, socket.MSG_WAITALL + ) + self.handle_serial_query(b, sess_id) + + elif pdu_type == self.RESET_TYPE: + self.handle_reset() + + elif pdu_type == self.ERROR_TYPE: + b = self.request.recv(length - self.HEADER_LEN, socket.MSG_WAITALL) + self.handle_error(b) + + +class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): + def __init__( + self, bind: Tuple[str, int], handler: Type[RTRConnHandler], db: RTRDatabase + ) -> None: + super().__init__(bind, handler) + self.db = db + + +def main(): + db = RTRDatabase(VRPS_FILE) + server = ThreadedTCPServer((LISTEN_HOST, LISTEN_PORT), RTRConnHandler, db) + dbg(f"Server listening on {LISTEN_HOST} port {LISTEN_PORT}") + server.serve_forever() + + +if __name__ == "__main__": + if len(sys.argv) > 1: + f = open(sys.argv[1], "w") + sys.__stdout__ = f + sys.stdout = f + sys.__stderr__ = f + sys.stderr = f + + main() diff --git a/tests/topotests/bgp_rpki_topo1/r1/staticd.conf b/tests/topotests/bgp_rpki_topo1/r1/staticd.conf new file mode 100644 index 0000000..7f2f057 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.1.2 diff --git a/tests/topotests/bgp_rpki_topo1/r1/vrps.csv b/tests/topotests/bgp_rpki_topo1/r1/vrps.csv new file mode 100644 index 0000000..5a6e023 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/vrps.csv @@ -0,0 +1,3 @@ +ASN,IP Prefix,Max Length,Trust Anchor +AS65530,198.51.100.0/24,24,private +AS65530,203.0.113.0/24,24,private diff --git a/tests/topotests/bgp_rpki_topo1/r1/zebra.conf b/tests/topotests/bgp_rpki_topo1/r1/zebra.conf new file mode 100644 index 0000000..b742b70 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json new file mode 100644 index 0000000..a04e9ef --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_any.json @@ -0,0 +1,37 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json new file mode 100644 index 0000000..01e288c --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_notfound.json @@ -0,0 +1,7 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json new file mode 120000 index 0000000..2645bfa --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rmap_rpki_valid.json @@ -0,0 +1 @@ +bgp_table_rpki_valid.json \ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json new file mode 100644 index 0000000..5546d45 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_any.json @@ -0,0 +1,52 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "10.0.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.0.0.0", + "prefixLen": 24, + "network": "10.0.0.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json new file mode 100644 index 0000000..7b9a5c8 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_notfound.json @@ -0,0 +1,22 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "10.0.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.0.0.0", + "prefixLen": 24, + "network": "10.0.0.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json new file mode 100644 index 0000000..eb3852a --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgp_table_rpki_valid.json @@ -0,0 +1,35 @@ +{ + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65002, + "routes": { + "198.51.100.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "198.51.100.0", + "prefixLen": 24, + "network": "198.51.100.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ], + "203.0.113.0/24": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "203.0.113.0", + "prefixLen": 24, + "network": "203.0.113.0/24", + "metric": 0, + "weight": 0, + "path": "65530", + "origin": "IGP" + } + ] + } +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf new file mode 100644 index 0000000..87d7214 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.0.2.1 remote-as 65530 + neighbor 192.0.2.1 timers connect 1 + neighbor 192.0.2.1 ebgp-multihop 3 + neighbor 192.0.2.1 update-source 192.0.2.2 + neighbor 192.168.4.4 remote-as internal + neighbor 192.168.4.4 timers 1 3 + neighbor 192.168.4.4 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.4.4 next-hop-self + exit-address-family +! +router bgp 65002 vrf vrf10 + no bgp ebgp-requires-policy + neighbor 192.0.2.3 remote-as 65530 + neighbor 192.0.2.3 timers 1 3 + neighbor 192.0.2.3 timers connect 1 + neighbor 192.0.2.3 ebgp-multihop 3 + neighbor 192.0.2.3 update-source 192.0.2.2 +! +rpki + rpki retry_interval 5 + rpki cache 192.0.2.1 15432 preference 1 +exit diff --git a/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json b/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json new file mode 100644 index 0000000..fbc5cc9 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/rpki_prefix_table.json @@ -0,0 +1,18 @@ +{ + "prefixes":[ + { + "prefix":"198.51.100.0", + "prefixLenMin":24, + "prefixLenMax":24, + "asn":65530 + }, + { + "prefix":"203.0.113.0", + "prefixLenMin":24, + "prefixLenMax":24, + "asn":65530 + } + ], + "ipv4PrefixCount":2, + "ipv6PrefixCount":0 +} diff --git a/tests/topotests/bgp_rpki_topo1/r2/staticd.conf b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf new file mode 100644 index 0000000..de58dde --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf @@ -0,0 +1,5 @@ +ip route 192.0.2.1/32 192.168.1.1 +! +vrf vrf10 + ip route 192.0.2.3/32 192.168.2.3 +! diff --git a/tests/topotests/bgp_rpki_topo1/r2/zebra.conf b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf new file mode 100644 index 0000000..785dbc6 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf @@ -0,0 +1,15 @@ +interface lo + ip address 192.0.2.2/32 +! +interface vrf10 vrf vrf10 + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 vrf vrf10 + ip address 192.168.2.2/24 +! +interface r2-eth2 + ip address 192.168.4.2/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf new file mode 100644 index 0000000..596dc20 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65530 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 3 + neighbor 192.0.2.2 update-source 192.0.2.3 + address-family ipv4 unicast + network 198.51.100.0/24 + network 203.0.113.0/24 + network 10.0.0.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/rtrd.py b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py new file mode 120000 index 0000000..1c5871a --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py @@ -0,0 +1 @@ +../r1/rtrd.py \ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/staticd.conf b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf new file mode 100644 index 0000000..6822f7e --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.2.2 diff --git a/tests/topotests/bgp_rpki_topo1/r3/vrps.csv b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv new file mode 120000 index 0000000..8daa27f --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv @@ -0,0 +1 @@ +../r1/vrps.csv \ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/zebra.conf b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf new file mode 100644 index 0000000..0975114 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf @@ -0,0 +1,5 @@ +interface lo + ip address 192.0.2.3/32 +! +interface r3-eth0 + ip address 192.168.2.3/24 \ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf new file mode 100644 index 0000000..80dc9ca --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r4/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.4.2 remote-as internal + neighbor 192.168.4.2 timers 1 3 + neighbor 192.168.4.2 timers connect 1 +! diff --git a/tests/topotests/bgp_rpki_topo1/r4/zebra.conf b/tests/topotests/bgp_rpki_topo1/r4/zebra.conf new file mode 100644 index 0000000..ed793ae --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r4/zebra.conf @@ -0,0 +1,4 @@ +! +interface r4-eth0 + ip address 192.168.4.4/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py new file mode 100644 index 0000000..a12204f --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py @@ -0,0 +1,453 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. + +import os +import sys +import json +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.common_config import step +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + " -M bgpd_rpki" if rname == "r2" else "", + ) + + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + + tgen.gears["r2"].run("ip link set r2-eth1 master vrf10") + + tgen.start_router() + + global rtrd_process + rtrd_process = {} + + for rname in ["r1", "r3"]: + rtr_path = os.path.join(CWD, rname) + log_dir = os.path.join(tgen.logdir, rname) + log_file = os.path.join(log_dir, "rtrd.log") + + tgen.gears[rname].cmd("chmod u+x {}/rtrd.py".format(rtr_path)) + rtrd_process[rname] = tgen.gears[rname].popen( + "{}/rtrd.py {}".format(rtr_path, log_file) + ) + + +def teardown_module(mod): + tgen = get_topogen() + + for rname in ["r1", "r3"]: + logger.info("{}: sending SIGTERM to rtrd RPKI server".format(rname)) + rtrd_process[rname].kill() + + tgen.stop_topology() + + +def show_rpki_prefixes(rname, expected, vrf=None): + tgen = get_topogen() + + if vrf: + cmd = "show rpki prefix-table vrf {} json".format(vrf) + else: + cmd = "show rpki prefix-table json" + + output = json.loads(tgen.gears[rname].vtysh_cmd(cmd)) + + return topotest.json_cmp(output, expected) + + +def show_bgp_ipv4_table_rpki(rname, rpki_state, expected, vrf=None): + tgen = get_topogen() + + cmd = "show bgp" + if vrf: + cmd += " vrf {}".format(vrf) + cmd += " ipv4 unicast" + if rpki_state: + cmd += " rpki {}".format(rpki_state) + cmd += " json" + + output = json.loads(tgen.gears[rname].vtysh_cmd(cmd)) + + expected_nb = len(expected.get("routes")) + output_nb = len(output.get("routes", {})) + + if expected_nb != output_nb: + return {"error": "expected {} prefixes. Got {}".format(expected_nb, output_nb)} + + return topotest.json_cmp(output, expected) + + +def test_show_bgp_rpki_prefixes(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + rname = "r2" + + step("Check RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, rname, rpki_state, expected_json + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_no_rpki_cache(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + def _show_rpki_no_connection(rname): + output = json.loads( + tgen.gears[rname].vtysh_cmd("show rpki cache-connection json") + ) + + return output == {"error": "No connection to RPKI cache server."} + + step("Remove RPKI server from configuration") + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +rpki + no rpki cache 192.0.2.1 15432 preference 1 +exit +""" + ) + + step("Check RPKI connection state") + + test_func = functools.partial(_show_rpki_no_connection, rname) + _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5) + assert result, "RPKI is still connected on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_reconnect(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Restore RPKI server configuration") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +rpki + rpki cache 192.0.2.1 15432 preference 1 +exit +""" + ) + + step("Check RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Apply RPKI valid route-map on neighbor") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +route-map RPKI permit 10 + match rpki valid +! +router bgp 65002 + address-family ipv4 unicast + neighbor 192.0.2.1 route-map RPKI in +""" + ) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step("Check RPKI state of prefixes in BGP table: {}".format(rpki_state)) + else: + step("Check prefixes in BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rmap_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, + rname, + rpki_state, + expected_json, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_prefixes_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Configure RPKI cache server on vrf10") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +vrf vrf10 + rpki + rpki cache 192.0.2.3 15432 preference 1 + exit +exit +""" + ) + + step("Check vrf10 RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json, vrf="vrf10") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, rname, rpki_state, expected_json, vrf="vrf10" + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_route_map_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Apply RPKI valid route-map on vrf10 neighbor") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +router bgp 65002 vrf vrf10 + address-family ipv4 unicast + neighbor 192.0.2.3 route-map RPKI in +""" + ) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rmap_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, + rname, + rpki_state, + expected_json, + vrf="vrf10", + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_bgp_ecommunity_rpki(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + # Flush all the states what was before and try sending out the prefixes + # with RPKI extended community. + r2.vtysh_cmd("clear ip bgp 192.168.4.4 soft out") + + def _bgp_check_ecommunity_rpki(community=None): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 unicast 198.51.100.0/24 json")) + expected = { + "paths": [ + { + "extendedCommunity": community, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_ecommunity_rpki, {"string": "OVS:valid"}) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Didn't receive RPKI extended community" + + r2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + address-family ipv4 unicast + no neighbor 192.168.4.4 send-community extended rpki + """ + ) + + test_func = functools.partial(_bgp_check_ecommunity_rpki) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Received RPKI extended community" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py index a0cd89f..d373a74 100644 --- a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py +++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py @@ -62,23 +62,48 @@ def teardown_module(mod): tgen.stop_topology() +expected_1 = { + "routes": { + "172.16.255.31/32": [{"path": "65002"}], + "172.16.255.32/32": [{"path": ""}], + } +} + +expected_2 = { + "routes": { + "172.16.255.31/32": [{"path": ""}], + "172.16.255.32/32": [{"path": ""}], + } +} + +expected_3 = { + "routes": { + "172.16.255.31/32": [{"path": "65003"}], + "172.16.255.32/32": [{"path": "65003"}], + } +} + +expected_4 = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65003"}], + "172.16.255.32/32": [{"path": "65002 65003"}], + } +} + + +def bgp_converge(router, expected): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + + return topotest.json_cmp(output, expected) + + def test_bgp_set_aspath_exclude(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def _bgp_converge(router): - output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) - expected = { - "routes": { - "172.16.255.31/32": [{"path": "65002"}], - "172.16.255.32/32": [{"path": ""}], - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_1) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with route-map" @@ -102,19 +127,7 @@ conf """ ) - expected = { - "routes": { - "172.16.255.31/32": [{"path": ""}], - "172.16.255.32/32": [{"path": ""}], - } - } - - def _bgp_regexp_1(router): - output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) - - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_2) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map" @@ -127,19 +140,46 @@ conf """ ) - expected = { - "routes": { - "172.16.255.31/32": [{"path": "65003"}], - "172.16.255.32/32": [{"path": "65003"}], - } - } - - test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"]) + # tgen.mininet_cli() + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3) _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map" +def test_no_bgp_set_aspath_exclude_access_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rname = "r1" + r1 = tgen.gears[rname] + + r1.vtysh_cmd( + """ +conf + no bgp as-path access-list SECOND permit 2 + """ + ) + + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed removing bgp as-path access-list" + + r1.vtysh_cmd( + """ +clear bgp * + """ + ) + + test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_4) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed to renegotiate with peers" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf index d82a21e..144466e 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf @@ -1,24 +1,28 @@ ! +!debug bgp updates +! router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check no bgp default ipv4-unicast - neighbor 192.168.12.2 remote-as external - neighbor 192.168.12.2 timers 1 3 - neighbor 192.168.12.2 timers connect 1 - neighbor 2001:db8::12:2 remote-as external - neighbor 2001:db8::12:2 timers 1 3 - neighbor 2001:db8::12:2 timers connect 1 + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 ! address-family ipv4 unicast network 10.0.0.0/31 route-map p1 network 10.0.0.2/32 route-map p2 - neighbor 192.168.12.2 activate + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + network 10.10.10.10/32 exit-address-family address-family ipv6 unicast network 2001:db8::1/128 route-map p1 network 2001:db8:1::/56 route-map p2 - neighbor 2001:db8::12:2 activate + neighbor 2001:db8::12:4 activate exit-address-family ! route-map p1 permit 10 @@ -28,4 +32,3 @@ route-map p2 permit 10 set metric 2 set origin incomplete exit -! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf index cf0013e..55686f4 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf @@ -5,18 +5,20 @@ router bgp 65002 no bgp ebgp-requires-policy no bgp network import-check no bgp default ipv4-unicast - neighbor 192.168.12.1 remote-as external - neighbor 192.168.12.1 timers 1 3 - neighbor 192.168.12.1 timers connect 1 - neighbor 2001:db8::12:1 remote-as external - neighbor 2001:db8::12:1 timers 1 3 - neighbor 2001:db8::12:1 timers connect 1 + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 ! address-family ipv4 unicast - neighbor 192.168.12.1 activate + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + exit-address-family address-family ipv6 unicast - neighbor 2001:db8::12:1 activate + neighbor 2001:db8::12:4 activate exit-address-family ! agentx diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf index 032b93b..f0957cc 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf @@ -6,6 +6,15 @@ access public_group "" any noauth prefix all all none rocommunity public default +trapsess -v2c -c public 127.0.0.1 + +notificationEvent linkUpTrap linkUp ifIndex ifAdminStatus ifOperStatus +notificationEvent linkDownTrap linkDown ifIndex ifAdminStatus ifOperStatus + +monitor -r 2 -e linkUpTrap "Generate linkUp" ifOperStatus != 2 +monitor -r 2 -e linkDownTrap "Generate linkDown" ifOperStatus == 2 + + view all included .1 iquerySecName frr diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf new file mode 100644 index 0000000..f6e4abf --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmptrapd.conf @@ -0,0 +1,2 @@ +authCommunity net,log public +disableAuthorization yes diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf new file mode 100644 index 0000000..71dbda0 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r3/bgpd.conf @@ -0,0 +1,25 @@ +! +!debug bgp updates +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.4 remote-as external + neighbor 192.168.12.4 timers 1 3 + neighbor 192.168.12.4 timers connect 1 + neighbor 2001:db8::12:4 remote-as external + neighbor 2001:db8::12:4 timers 1 3 + neighbor 2001:db8::12:4 timers connect 1 + ! + address-family ipv4 unicast + neighbor 192.168.12.4 activate + neighbor 192.168.12.4 addpath-tx-all-paths + network 10.10.10.10/32 + exit-address-family + address-family ipv6 unicast + neighbor 2001:db8::12:4 activate + exit-address-family +! +agentx +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf new file mode 100644 index 0000000..398af65 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r3/zebra.conf @@ -0,0 +1,5 @@ +! +interface r3-eth0 + ip address 192.168.12.3/24 + ipv6 address 2001:db8::12:3/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf new file mode 100644 index 0000000..5ebbde6 --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/rr/bgpd.conf @@ -0,0 +1,67 @@ +! +! debug bgp updates +! +router bgp 65004 + no bgp ebgp-requires-policy + no bgp network import-check + no bgp default ipv4-unicast + neighbor 192.168.12.1 remote-as external + neighbor 192.168.12.1 timers 1 3 + neighbor 192.168.12.1 timers connect 1 + neighbor 192.168.12.2 remote-as external + neighbor 192.168.12.2 timers 1 3 + neighbor 192.168.12.2 timers connect 1 + neighbor 192.168.12.3 remote-as external + neighbor 192.168.12.3 timers 1 3 + neighbor 192.168.12.3 timers connect 1 + neighbor 2001:db8::12:1 remote-as external + neighbor 2001:db8::12:1 timers 1 3 + neighbor 2001:db8::12:1 timers connect 1 + neighbor 2001:db8::12:2 remote-as external + neighbor 2001:db8::12:2 timers 1 3 + neighbor 2001:db8::12:2 timers connect 1 + neighbor 2001:db8::12:3 remote-as external + neighbor 2001:db8::12:3 timers 1 3 + neighbor 2001:db8::12:3 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.0/31 route-map p1 + network 10.0.0.2/32 route-map p2 + neighbor 192.168.12.1 activate + neighbor 192.168.12.2 activate + neighbor 192.168.12.2 addpath-tx-all-paths + neighbor 192.168.12.2 route-map r2-import in + neighbor 192.168.12.2 route-map r2-export out +! neighbor 192.168.12.2 soft-reconfiguration inbound + neighbor 192.168.12.3 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8::1/128 route-map p1 + network 2001:db8:1::/56 route-map p2 + neighbor 2001:db8::12:1 activate + neighbor 2001:db8::12:2 activate + neighbor 2001:db8::12:2 addpath-tx-all-paths + neighbor 2001:db8::12:3 activate + exit-address-family + + +ip prefix-list r2-toto permit any + +route-map r2-import permit 10 + match ip address prefix-list r2-toto + +route-map r2-export permit 10 + match ip address prefix-list r2-toto +! +route-map p1 permit 10 + set metric 1 +exit +route-map p2 permit 10 + set metric 2 + set origin incomplete +exit + + + +agentx +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf new file mode 100644 index 0000000..092673b --- /dev/null +++ b/tests/topotests/bgp_snmp_bgp4v2mib/rr/zebra.conf @@ -0,0 +1,5 @@ +! +interface rr-eth0 + ip address 192.168.12.4/24 + ipv6 address 2001:db8::12:4/64 +! diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py index a253d84..c296aaa 100755 --- a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py +++ b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py @@ -24,6 +24,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.snmptest import SnmpTester from lib import topotest +from lib.topolog import logger pytestmark = [pytest.mark.bgpd, pytest.mark.snmp] @@ -31,10 +32,14 @@ pytestmark = [pytest.mark.bgpd, pytest.mark.snmp] def build_topo(tgen): tgen.add_router("r1") tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("rr") switch = tgen.add_switch("s1") switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rr"]) def setup_module(mod): @@ -55,11 +60,18 @@ def setup_module(mod): os.path.join(CWD, "{}/bgpd.conf".format(rname)), "-M snmp", ) - router.load_config( - TopoRouter.RD_SNMP, - os.path.join(CWD, "{}/snmpd.conf".format(rname)), - "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX", - ) + + r2 = tgen.gears["r2"] + r2.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(r2.name)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX", + ) + r2.load_config( + TopoRouter.RD_TRAP, + os.path.join(CWD, "{}/snmptrapd.conf".format(r2.name)), + " -On -OQ ", + ) tgen.start_router() @@ -72,28 +84,31 @@ def teardown_module(mod): def test_bgp_snmp_bgp4v2(): tgen = get_topogen() + r1 = tgen.gears["r1"] r2 = tgen.gears["r2"] + rr = tgen.gears["rr"] def _bgp_converge_summary(): output = json.loads(r2.vtysh_cmd("show bgp summary json")) expected = { "ipv4Unicast": { "peers": { - "192.168.12.1": { + "192.168.12.4": { "state": "Established", - "pfxRcd": 2, + "pfxRcd": 6, } } }, "ipv6Unicast": { "peers": { - "2001:db8::12:1": { + "2001:db8::12:4": { "state": "Established", - "pfxRcd": 2, + "pfxRcd": 4, } } }, } + # tgen.mininet_cli() return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge_summary) @@ -136,6 +151,7 @@ def test_bgp_snmp_bgp4v2(): } }, } + # tgen.mininet_cli() return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge_prefixes) @@ -146,11 +162,12 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_remote_addr(): expected = { - "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.1": "C0 A8 0C 01", - "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 01", + "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.4": "C0 A8 0C 04", + "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 04", } # bgp4V2PeerRemoteAddr + # tgen.mininet_cli() output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.5") return output == expected @@ -160,8 +177,8 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_peer_state(): expected = { - "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.1": "6", - "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "6", + "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.4": "6", + "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "6", } # bgp4V2PeerState @@ -174,8 +191,8 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_peer_last_error_code_received(): expected = { - "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.1": "0", - "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "0", + "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.4": "0", + "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4": "0", } # bgp4V2PeerLastErrorCodeReceived @@ -190,10 +207,16 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_origin(): expected = { - "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "3", - "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.10.10.10.32.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.10.10.10.32.1.192.168.12.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.4.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.4.2": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "1", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "3", + "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "3", } # bgp4V2NlriOrigin @@ -206,10 +229,16 @@ def test_bgp_snmp_bgp4v2(): def _snmpwalk_med(): expected = { - "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "2", - "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1", - "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.10.10.10.32.1.192.168.12.4.1": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.10.10.10.32.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.4.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "1", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "0", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.1": "2", + "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4.2": "0", } # bgp4V2NlriMed @@ -221,6 +250,68 @@ def test_bgp_snmp_bgp4v2(): assertmsg = "Can't fetch SNMP for bgp4V2NlriMed" assert result, assertmsg + # + # traps + # + + # + # bgp4 traps + # + def _snmptrap_ipv4(): + def __get_notif_bgp4_in_trap_file(router): + snmptrapfile = "{}/{}/snmptrapd.log".format(router.logdir, router.name) + outputfile = open(snmptrapfile).read() + output = snmp.get_notif_bgp4(outputfile) + + return output + + output = __get_notif_bgp4_in_trap_file(r2) + logger.info("output bgp4") + logger.info(output) + return snmp.is_notif_bgp4_valid(output, "192.168.12.4") + + # skip tests is SNMP not installed + if not os.path.isfile("/usr/sbin/snmptrapd"): + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + rr.vtysh_cmd("clear bgp *") + _, result = topotest.run_and_expect(_snmptrap_ipv4, True, count=30, wait=1) + assertmsg = "Can't fetch SNMP trap for ipv4" + assert result, assertmsg + + # + # bgp4v2 traps + # + def _snmptrap_ipv6(): + def __get_notif_bgp4v2_in_trap_file(router): + snmptrapfile = "{}/{}/snmptrapd.log".format(router.logdir, router.name) + outputfile = open(snmptrapfile).read() + output = snmp.get_notif_bgp4v2(outputfile) + + return output + + # tgen.mininet_cli() + output = __get_notif_bgp4v2_in_trap_file(r2) + logger.info("output bgp4v2") + logger.info(output) + p_ipv4_addr = "1.192.168.12.4" + p_ipv6_addr = "2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.4" + return ( + snmp.is_notif_bgp4v2_valid(output, p_ipv4_addr, "Estab") + and snmp.is_notif_bgp4v2_valid(output, p_ipv6_addr, "Estab") + and snmp.is_notif_bgp4v2_valid(output, p_ipv4_addr, "Backward") + and snmp.is_notif_bgp4v2_valid(output, p_ipv6_addr, "Backward") + ) + + sleep(10) + r2.vtysh_cmd("conf\nbgp snmp traps bgp4-mibv2") + r2.vtysh_cmd("conf\nno bgp snmp traps rfc4273") + rr.vtysh_cmd("clear bgp *") + _, result = topotest.run_and_expect(_snmptrap_ipv6, True, count=60, wait=1) + assertmsg = "Can't fetch SNMP trap for ipv6" + assert result, assertmsg + def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf index 22b9014..e8c14ab 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf index 42b9d51..88de281 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65002 diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf index 339b4eb..443a64a 100644 --- a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf index 15779aa..3d9c2cf 100644 --- a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf @@ -4,7 +4,6 @@ hostname pe1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json index 9f78447..2ce936b 100644 --- a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json @@ -12,7 +12,7 @@ { "fib": true, "directlyConnected": true, - "interfaceName": "eth0", + "interfaceName": "vrf10", "vrf": "vrf10", "active": true } diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf index bfc9db9..8805009 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf index cf31a5c..be43805 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf index 892a9f7..0770a96 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf index 9771ee1..70627ad 100644 --- a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf index d113db1..8079bb0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index 8ccf7a2..c84106f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf index 9a7831d..c2e8530 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index 839454d..5c12429 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf index 30a0f8f..12b9e8f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json index 3cc2fdd..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf index cbc5ce1..f202493 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf index 7ca2300..db36c18 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json index 9557054..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf index 449ca74..9dfed4f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf index 01b0cc3..57c19e2 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf @@ -6,7 +6,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json index 3cc2fdd..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json index eb34333..2050795 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json index 5517fc7..7a4e0d7 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json index 25b7a86..0fdd3d6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json index a1f2158..e289df1 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json index 7eeccd1..0fdd3d6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "1.1.1.1", "defaultLocPrf": 100, "localAS": 1, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf index f913b9f..dd8a756 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf index 8700f12..abf4971 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf @@ -6,7 +6,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug bgp neighbor-events diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json index 9557054..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json index d801671..a440ab4 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json index 25da05b..0dcdec6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json index 2cd47b9..03bbcc0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 2, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json index f390ef6..5c70cf6 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 4, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json index 3353d75..03bbcc0 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json @@ -1,7 +1,6 @@ { "vrfId": 0, "vrfName": "default", - "tableVersion": 6, "routerId": "2.2.2.2", "defaultLocPrf": 100, "localAS": 2, diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf index 201d0cc..3c9e4e3 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf @@ -4,7 +4,6 @@ hostname r2 password zebra ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py index f89f337..9fbcdc2 100644 --- a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -83,6 +83,7 @@ from lib.bgp import ( create_router_bgp, clear_bgp_and_verify, ) +from lib.ospf import verify_ospf_neighbor, clear_ospf # Global variables topo = None @@ -886,6 +887,154 @@ def test_bgp_unique_rid_chaos4_p2(): write_test_footer(tc_name) +def test_bgp_unique_rid_chaos2_p2(): + """ + TC: 8 + 8. Chaos - Verify bgp unique rid functionality when ospf and bgp share the same router ids. + + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure base config as per the topology") + step("Redistribute routes between bgp and ospf.") + reset_config_on_routers(tgen) + + step( + "Base config should be up, verify using BGP convergence on all \ + the routers for IPv4 and IPv6 nbrs" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut="r3") + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Configure ospf between R3 and R4 with same router ids in both ospf and bgp 10.10.10.10 on R3 BGP and OSPF, and 10.10.10.10 in R4 BGP and 11.11.11.11 in R4 OSPF." + ) + input_dict = { + "r3": {"bgp": {"router_id": "10.10.10.10"}}, + "r4": {"bgp": {"router_id": "10.10.10.10"}}, + "r5": {"bgp": {"router_id": "10.10.10.10"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "The session should be established between R3 & R4 between BGP process and neighborship should be full between OSPF too." + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut="r3") + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("All the routes should be calculated and installed.") + # Verifying RIB routes + protocol = "bgp" + input_dict = topo["routers"] + verify_rib_rtes = { + "ipv4": { + "r3": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "Null0"}, + ] + } + }, + "ipv6": { + "r3": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "Null0", + } + ] + } + }, + } + dut = "r3" + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Clear ospf process.") + clear_ospf(tgen, "r3") + + step("All the routes should be calculated and installed.") + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step("Clear bgp process.") + clear_bgp_and_verify(tgen, topo, "r3") + + step("All the routes should be calculated and installed.") + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + step( + "Configure ospf between R3 and R5. Configure static routes in R5 and redistribute static routes in ospf on R5." + ) + # Covered as base config. + + step("Verify routes are installed in R3 and R4 route tables.") + dut = "r4" + for addr_type in ADDR_TYPES: + result4 = verify_rib( + tgen, + addr_type, + dut, + verify_rib_rtes, + protocol=protocol, + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf index 3bbcc20..473e56b 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65500 bgp router-id 192.0.2.1 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.0.2.100 remote-as 65500 neighbor 192.0.2.100 update-source lo neighbor 192.168.0.100 remote-as 65500 diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf index 4c84d52..c7244c0 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf @@ -1,9 +1,10 @@ -debug bgp nht -debug bgp zebra -debug bgp labelpool +!debug bgp nht +!debug bgp zebra +!debug bgp labelpool router bgp 65500 bgp router-id 192.0.2.2 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.0.2.100 remote-as 65500 neighbor 192.0.2.100 update-source lo neighbor 192.168.0.100 remote-as 65500 diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf index c5d5727..b7592e4 100644 --- a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65501 bgp router-id 192.0.2.3 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 192.168.1.200 remote-as 65502 address-family ipv4 unicast no neighbor 192.168.1.200 activate diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py index a908e74..39865eb 100644 --- a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py +++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py @@ -48,6 +48,10 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.checkping import check_ping @@ -259,62 +263,6 @@ def mpls_table_check_entry(router, out_label, out_nexthop): ) -def check_show_bgp_vpn_prefix_found( - router, ipversion, prefix, rd, label=None, nexthop=None -): - """ - Check if a given vpn prefix is present in the BGP RIB - * 'router': the router to check BGP VPN RIB - * 'ipversion': The ip version to check: ipv4 or ipv6 - * 'prefix': the IP prefix to check - * 'rd': the route distinguisher to check - * 'label: the label to check - """ - output = json.loads( - router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) - ) - if label: - if nexthop: - expected = { - rd: { - "prefix": prefix, - "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], - } - } - else: - expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} - else: - if nexthop: - expected = { - rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} - } - else: - expected = {rd: {"prefix": prefix}} - return topotest.json_cmp(output, expected) - - -def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): - """ - Check if a given vpn prefix is not present in the BGP RIB - * 'router': the router to check BGP VPN RIB - * 'ipversion': The ip version to check: ipv4 or ipv6 - * 'prefix': the IP prefix to check - * 'rd': the route distinguisher to check - * 'label: the label to check - """ - output = json.loads( - router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) - ) - if label: - expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} - else: - expected = {rd: {"prefix": prefix}} - ret = topotest.json_cmp(output, expected) - if ret is None: - return "not good" - return None - - def check_show_mpls_table_entry_label_not_found(router, inlabel): output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) expected = {"inLabel": inlabel, "installed": True} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf index 0249279..d8a45ce 100644 --- a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf @@ -1,3 +1,4 @@ +bgp route-map delay-timer 1 router bgp 65500 bgp router-id 192.0.2.1 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py index 61e1163..8b2e674 100644 --- a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py +++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py @@ -25,6 +25,10 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger @@ -214,6 +218,164 @@ def test_protocols_convergence(): assert result is None, assertmsg +def test_export_route_target_empty(): + """ + Check that when removing 'rt vpn export' command, exported prefix is removed + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, Remove 'rt vpn export 52:100' command") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno rt vpn export 52:100\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_route_target_with_routemap_with_export_route_target(): + """ + Check that when removing 'rt vpn export' command, exported prefix is added back + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, configuring route target with route-map with export route target") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nroute-map vpn export rmap\n" + ) + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:100\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + +def test_export_route_target_with_routemap_without_export_route_target(): + """ + Check that when removing 'set extcommunity rt' command, prefix is removed + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, removing 'set extcommunity rt 52:100.") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nno set extcommunity rt\n" + ) + + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_route_target_with_default_command(): + """ + Add back route target with 'rt vpn export' command + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, detach route-map and re-add route target vpn export") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nrt vpn export 52:100\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + +def test_export_suppress_route_target_with_route_map_command(): + """ + Add back route target with 'rt vpn export' command + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, add an extended comm-list to delete 52:100") + + router.vtysh_cmd("configure terminal\nbgp extcommunity-list 1 permit rt 52:100\n") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extended-comm-list 1 delete\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is removed".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + +def test_export_add_route_target_to_route_map_command(): + """ + Add route target with route-map so that route is added back + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + logger.info("r1, add an additional set extcommunity 52:101") + router.vtysh_cmd( + "configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:101\n" + ) + prefix = "172.31.0.1/32" + logger.info("r1, check that exported prefix {} is added back".format(prefix)) + test_func = partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py index ce278ed..d4c355a 100644 --- a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -151,6 +151,16 @@ def teardown_module(_mod): tgen.stop_topology() +def check_bgp_vpnv4_prefix_presence(router, prefix): + "Check the presence of a prefix" + tgen = get_topogen() + + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{}, prefix ipv4 vpn {} is not installed yet".format(router.name, prefix) + return None + + def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None): """ Dump and check that vpnv4 entries have the same MPLS label value @@ -163,6 +173,12 @@ def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=N stored_label_inited = False for prefix in group: + test_func = functools.partial(check_bgp_vpnv4_prefix_presence, router, prefix) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, prefix ipv4 vpn {} is not installed yet".format( + router.name, prefix + ) + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) assert dump, "{0}, {1}, route distinguisher not present".format( router.name, prefix diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py index e936ccc..3d5f8f6 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -54,7 +54,7 @@ pytestmark = [pytest.mark.bgpd] PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"] PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"] PREFIXES_REDIST_R14 = ["172:31::14/128"] -PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"] +PREFIXES_CONNECTED = ["192:168::255:0/112", "192:2::/64"] def build_topo(tgen): @@ -150,6 +150,16 @@ def teardown_module(_mod): tgen.stop_topology() +def check_bgp_vpnv6_prefix_presence(router, prefix): + "Check the presence of a prefix" + tgen = get_topogen() + + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{}, prefix ipv6 vpn {} is not installed yet".format(router.name, prefix) + return None + + def bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=None): """ Dump and check that vpnv6 entries have the same MPLS label value @@ -162,6 +172,12 @@ def bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=N stored_label_inited = False for prefix in group: + test_func = functools.partial(check_bgp_vpnv6_prefix_presence, router, prefix) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, prefix ipv6 vpn {} is not installed yet".format( + router.name, prefix + ) + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) for rd, pathes in dump.items(): for path in pathes["paths"]: @@ -237,7 +253,9 @@ def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=Non label_list.add(in_label) for nh in label_info["nexthops"]: if "installed" not in nh.keys(): - return "{} {} is not installed yet on {}".format(in_label, label_info, router.name) + return "{} {} is not installed yet on {}".format( + in_label, label_info, router.name + ) if nh["installed"] != True or nh["type"] != "BGP": return "{}, show mpls table, nexthop is not installed".format( router.name diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf index 66493f0..eb2563d 100644 --- a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf @@ -4,7 +4,6 @@ hostname ce1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65002 diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf index c5c9927..bbc5ae5 100644 --- a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf +++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf @@ -4,7 +4,6 @@ hostname pe1 password zebra ! log stdout notifications -log monitor notifications log commands ! router bgp 65001 diff --git a/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg index 3260513..11a827a 100644 --- a/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg +++ b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg @@ -3,7 +3,7 @@ neighbor 10.0.0.1 { local-address 10.0.0.2; local-as 65001; peer-as 65534; - md5 test123; + md5-password test123; static { route 192.168.100.1/32 { diff --git a/tests/topotests/bgp_vrf_netns/exabgp.env b/tests/topotests/bgp_vrf_netns/exabgp.env index a328e04..ec978c6 100644 --- a/tests/topotests/bgp_vrf_netns/exabgp.env +++ b/tests/topotests/bgp_vrf_netns/exabgp.env @@ -1,5 +1,6 @@ [exabgp.api] +ack = false encoder = text highres = false respawn = false diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py index ab0eb8c..c6a4499 100755 --- a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py +++ b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ exa-send.py: Send a few testroutes with ExaBGP diff --git a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg index 2d0ca89..97a024c 100644 --- a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg +++ b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg @@ -1,21 +1,18 @@ -group controller { - - process announce-routes { - run "/etc/exabgp/exa-send.py 1 10"; - } - - process receive-routes { - run "/etc/exabgp/exa-receive.py 1"; - receive-routes; - encoder text; - } +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} - neighbor 10.0.1.1 { - router-id 10.0.1.101; - local-address 10.0.1.101; - local-as 99; - peer-as 100; - graceful-restart; - } +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} +neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} } 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 8457b75..028bc35 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 @@ -94,6 +94,7 @@ def setup_module(module): router.net.set_intf_netns("r1-eth0", ns, up=True) # run daemons + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")), @@ -205,7 +206,6 @@ def test_bgp_vrf_netns(): if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] ret = pytest.main(args) diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf index 03dfbf9..f52f56b 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf @@ -1,10 +1,19 @@ hostname r1 +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + import vrf DONNA + ! +! router bgp 99 vrf DONNA no bgp ebgp-requires-policy address-family ipv4 unicast redistribute connected import vrf EVA + import vrf NOTEXISTING + import vrf default ! ! router bgp 99 vrf EVA @@ -12,5 +21,13 @@ router bgp 99 vrf EVA address-family ipv4 unicast redistribute connected import vrf DONNA + import vrf NOTEXISTING + ! +! +router bgp 99 vrf NOTEXISTING + no bgp ebgp-requires-policy + no bgp network import-check + address-family ipv4 unicast + network 172.16.101.0/24 ! ! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf index 3503855..4de9e89 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf @@ -1,5 +1,9 @@ hostname r1 +int dummy0 + ip address 10.0.4.1/24 + no shut +! int dummy1 ip address 10.0.0.1/24 no shut @@ -16,3 +20,9 @@ int dummy4 ip address 10.0.3.1/24 no shut ! +int EVA + no shut +! +int DONNA + no shut +! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs index fb67953..f62c5cd 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs +++ b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs @@ -3,6 +3,7 @@ ip link add DONNA type vrf table 1001 ip link add EVA type vrf table 1002 +ip link add dummy0 type dummy # vrf default ip link add dummy1 type dummy ip link add dummy2 type dummy ip link add dummy3 type dummy 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 fd7ffff..ef813e9 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 @@ -64,7 +64,7 @@ def teardown_module(mod): tgen.stop_topology() -def test_vrf_route_leak(): +def test_vrf_route_leak_donna(): logger.info("Ensure that routes are leaked back and forth") tgen = get_topogen() # Don't run this test if we have any failure. @@ -81,11 +81,59 @@ def test_vrf_route_leak(): } ], "10.0.1.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, ], "10.0.2.0/24": [{"protocol": "connected"}], "10.0.3.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "lo", + "vrf": "default", + "active": True, + }, + ], + }, + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, ], } @@ -95,10 +143,31 @@ def test_vrf_route_leak(): result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + +def test_vrf_route_leak_eva(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + # Test EVA VRF. expect = { "10.0.0.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, ], "10.0.1.0/24": [ { @@ -106,13 +175,36 @@ def test_vrf_route_leak(): } ], "10.0.2.0/24": [ - {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, ], "10.0.3.0/24": [ { "protocol": "connected", } ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], } test_func = partial( @@ -122,6 +214,217 @@ def test_vrf_route_leak(): assert result, "BGP VRF EVA check failed:\n{}".format(diff) +def test_vrf_route_leak_donna(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test DONNA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.1.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [{"protocol": "connected"}], + "10.0.3.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "lo", + "vrf": "default", + "active": True, + }, + ], + }, + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_eva(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test EVA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "nexthops": [ + { + "interfaceIndex": 0, + "interfaceName": "unknown", + "vrf": "Unknown", + }, + ], + }, + ], + } + + +def test_vrf_route_leak_default(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test default VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "connected", + } + ], + } + + test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF default check failed:\n{}".format(diff) + + +def test_ping(): + "Simple ping tests" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + logger.info("Ping from default to DONNA") + output = r1.run("ping -c 4 -w 4 -I 10.0.4.1 10.0.0.1") + assert " 0% packet loss" in output, "Ping default->DONNA FAILED" + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 0afebde..23eab68 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -17,6 +17,7 @@ from pathlib import Path import lib.fixtures import pytest +from lib.common_config import generate_support_bundle from lib.micronet_compat import Mininet from lib.topogen import diagnose_env, get_topogen from lib.topolog import get_test_logdir, logger @@ -102,6 +103,12 @@ def pytest_addoption(parser): help="Comma-separated list of routers to spawn gdb on, or 'all'", ) + parser.addoption( + "--gdb-use-emacs", + action="store_true", + help="Use emacsclient to run gdb instead of a shell", + ) + parser.addoption( "--logd", action="append", @@ -167,6 +174,24 @@ def pytest_addoption(parser): help="Options to pass to `perf record`.", ) + parser.addoption( + "--rr-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to run `rr` on, or 'all'", + ) + + parser.addoption( + "--rr-routers", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to run `rr` on, or 'all'", + ) + + parser.addoption( + "--rr-options", + metavar="OPTS", + help="Options to pass to `rr record`.", + ) + rundir_help = "directory for running in and log files" parser.addini("rundir", rundir_help, default="/tmp/topotests") parser.addoption("--rundir", metavar="DIR", help=rundir_help) @@ -202,6 +227,12 @@ def pytest_addoption(parser): help="Generate suppression file, and enable more precise (slower) valgrind checks", ) + parser.addoption( + "--valgrind-leak-kinds", + metavar="KIND[,KIND...]", + help="Comma-separated list of valgrind leak kinds or 'all'", + ) + parser.addoption( "--valgrind-memleaks", action="store_true", @@ -568,6 +599,11 @@ def pytest_runtest_setup(item): module = item.parent.module script_dir = os.path.abspath(os.path.dirname(module.__file__)) os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] = script_dir + os.environ["CONFIGDIR"] = script_dir + + +def pytest_exception_interact(node, call, report): + generate_support_bundle() def pytest_runtest_makereport(item, call): diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh index 3b296a1..4a88dc6 100755 --- a/tests/topotests/docker/inner/compile_frr.sh +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -64,9 +64,9 @@ if [ ! -e Makefile ]; then --enable-dev-build \ --with-moduledir=/usr/lib/frr/modules \ --prefix=/usr \ - --localstatedir=/var/run/frr \ + --sysconfdir=/etc \ + --localstatedir=/var \ --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ --enable-multipath=0 \ --enable-fpm \ --enable-sharpd \ diff --git a/tests/topotests/grpc_basic/test_basic_grpc.py b/tests/topotests/grpc_basic/test_basic_grpc.py index 88bfa98..1ded663 100644 --- a/tests/topotests/grpc_basic/test_basic_grpc.py +++ b/tests/topotests/grpc_basic/test_basic_grpc.py @@ -30,7 +30,7 @@ GRPCP_OSPFD = 50055 GRPCP_PIMD = 50056 pytestmark = [ - # pytest.mark.mgmtd -- Need a new non-protocol marker + pytest.mark.mgmtd, # pytest.mark.bfdd, # pytest.mark.isisd, # pytest.mark.ospfd, @@ -142,7 +142,7 @@ def test_shutdown_checks(tgen): time.sleep(1) try: for r in tgen.routers().values(): - r.net.stopRouter() + r.net.stopRouter(False) r.net.checkRouterCores() finally: if p: diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref index f5d9aa7..8300ca0 100644 --- a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref @@ -9,7 +9,7 @@ "adjacencies": { "adjacency": [ { - "neighbor-sys-type": "level-1-2", + "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", "hold-timer": 10, "neighbor-priority": 0, @@ -28,7 +28,7 @@ "adjacencies": { "adjacency": [ { - "neighbor-sys-type": "level-1-2", + "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", "hold-timer": 10, "neighbor-priority": 0, diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf index 5140eda..92b619e 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt1 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf index 388348f..3ea5e0a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt2 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf index fb45ee1..ea35de8 100644 --- a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt3 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf index 89837d4..a4b1f2a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt0 ! !log stdout notifications -!log monitor notifications !log commands ! debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf index 25a9629..f11daa5 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt1 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf index d739a73..ca3235a 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt2 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf index 5c3bed0..9a99910 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt3 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf index 9c00013..5037041 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt4 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf index 61c599d..5f96fd4 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt5 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf index b63401e..23a99a0 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt6 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf index b5a28c7..9be1b48 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt7 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf index dd63f8c..eaaac74 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt8 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf index 378a196..513cf56 100644 --- a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf @@ -3,7 +3,6 @@ log file zebra.log hostname rt9 ! log stdout notifications -log monitor notifications log commands ! !debug zebra packet diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 48e9d53..532e708 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -708,9 +708,9 @@ def _check_overload_timer(router, timer_expected): tgen = get_topogen() router = tgen.gears[router] - thread_output = router.vtysh_cmd("show thread timers") + output = router.vtysh_cmd("show event timers") - timer_running = "set_overload_on_start_timer" in thread_output + timer_running = "set_overload_on_start_timer" in output if timer_running == timer_expected: return True return "Expected timer running status: {}".format(timer_expected) diff --git a/tests/topotests/kinds.yaml b/tests/topotests/kinds.yaml index 127790e..5f4b61d 100644 --- a/tests/topotests/kinds.yaml +++ b/tests/topotests/kinds.yaml @@ -12,6 +12,7 @@ kinds: - "./%NAME%:/etc/frr" - "%RUNDIR%/var.log.frr:/var/log/frr" - "%RUNDIR%/var.run.frr:/var/run/frr" + - "%RUNDIR%/var.lib.frr:/var/lib/frr" cap-add: - SYS_ADMIN - AUDIT_WRITE diff --git a/tests/topotests/lib/bgpcheck.py b/tests/topotests/lib/bgpcheck.py new file mode 100644 index 0000000..5ca35a5 --- /dev/null +++ b/tests/topotests/lib/bgpcheck.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright 2023, 6wind +import json + +from lib import topotest + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + """ + Check if a given vpn prefix is not present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found( + router, ipversion, prefix, rd, label=None, nexthop=None +): + """ + Check if a given vpn prefix is present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + if nexthop: + expected = { + rd: { + "prefix": prefix, + "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], + } + } + else: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + if nexthop: + expected = { + rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} + } + else: + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index b07329c..57f642a 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -252,6 +252,7 @@ class BMPPerPeerMessage: if peer_type == 0x03: msg['is_filtered'] = bool(peer_flags & IS_FILTERED) + msg['policy'] = 'loc-rib' else: # peer_flags = 0x0000 0000 # ipv6, post-policy, as-path, adj-rib-out, reserverdx4 @@ -259,7 +260,7 @@ class BMPPerPeerMessage: is_as_path = bool(peer_flags & IS_AS_PATH) is_post_policy = bool(peer_flags & IS_POST_POLICY) is_ipv6 = bool(peer_flags & IS_IPV6) - msg['post_policy'] = is_post_policy + msg['policy'] = 'post-policy' if is_post_policy else 'pre-policy' msg['ipv6'] = is_ipv6 msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index e19d96f..598db84 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -14,6 +14,7 @@ import socket import subprocess import sys import traceback +import configparser from collections import OrderedDict from copy import deepcopy from datetime import datetime, timedelta @@ -21,12 +22,6 @@ from functools import wraps from re import search as re_search from time import sleep -try: - # Imports from python2 - import ConfigParser as configparser -except ImportError: - # Imports from python3 - import configparser from lib.micronet import comm_error from lib.topogen import TopoRouter, get_topogen diff --git a/tests/topotests/lib/fe_client.py b/tests/topotests/lib/fe_client.py new file mode 100755 index 0000000..07059cc --- /dev/null +++ b/tests/topotests/lib/fe_client.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# November 27 2023, Christian Hopps +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +# noqa: E501 +# +import argparse +import json +import logging +import os +import socket +import struct +import sys +import time +from pathlib import Path + +CWD = os.path.dirname(os.path.realpath(__file__)) + +# This is painful but works if you have installed protobuf would be better if we +# actually built and installed these but ... python packaging. +try: + sys.path.append(os.path.dirname(CWD)) + from munet.base import commander + + commander.cmd_raises(f"protoc --python_out={CWD} -I {CWD}/../../../lib mgmt.proto") +except Exception as error: + logging.error("can't create protobuf definition modules %s", error) + raise + +try: + sys.path[0:0] = "." + import mgmt_pb2 +except Exception as error: + logging.error("can't import proto definition modules %s", error) + raise + +CANDIDATE_DS = mgmt_pb2.DatastoreId.CANDIDATE_DS +OPERATIONAL_DS = mgmt_pb2.DatastoreId.OPERATIONAL_DS +RUNNING_DS = mgmt_pb2.DatastoreId.RUNNING_DS +STARTUP_DS = mgmt_pb2.DatastoreId.STARTUP_DS + +# ===================== +# Native message values +# ===================== + +MGMT_MSG_MARKER_PROTOBUF = b"\000###" +MGMT_MSG_MARKER_NATIVE = b"\001###" + +# +# Native message formats +# +MSG_HDR_FMT = "=H2xIQQ" +HDR_FIELD_CODE = 0 +HDR_FIELD_VSPLIT = 1 +HDR_FIELD_SESS_ID = 2 +HDR_FIELD_REQ_ID = 3 + +MSG_ERROR_FMT = "=h6x" +ERROR_FIELD_ERROR = 0 + +# MSG_GET_TREE_FMT = "=B7x" +# GET_TREE_FIELD_RESULT_TYPE = 0 + +MSG_TREE_DATA_FMT = "=bBB5x" +TREE_DATA_FIELD_PARTIAL_ERROR = 0 +TREE_DATA_FIELD_RESULT_TYPE = 1 +TREE_DATA_FIELD_MORE = 2 + +MSG_GET_DATA_FMT = "=BB6x" +GET_DATA_FIELD_RESULT_TYPE = 0 +GET_DATA_FIELD_FLAGS = 1 +GET_DATA_FLAG_STATE = 0x1 +GET_DATA_FLAG_CONFIG = 0x2 +GET_DATA_FLAG_EXACT = 0x4 + +MSG_NOTIFY_FMT = "=B7x" +NOTIFY_FIELD_RESULT_TYPE = 0 + +# +# Native message codes +# +MSG_CODE_ERROR = 0 +# MSG_CODE_GET_TREE = 1 +MSG_CODE_TREE_DATA = 2 +MSG_CODE_GET_DATA = 3 +MSG_CODE_NOTIFY = 4 + +msg_native_formats = { + MSG_CODE_ERROR: MSG_ERROR_FMT, + # MSG_CODE_GET_TREE: MSG_GET_TREE_FMT, + MSG_CODE_TREE_DATA: MSG_TREE_DATA_FMT, + MSG_CODE_GET_DATA: MSG_GET_DATA_FMT, + MSG_CODE_NOTIFY: MSG_NOTIFY_FMT, +} + + +# Result formats +MSG_FORMAT_XML = 1 +MSG_FORMAT_JSON = 2 +MSG_FORMAT_LYB = 3 + + +def cstr(mdata): + assert mdata[-1] == 0 + return mdata[:-1] + + +class FEClientError(Exception): + pass + + +class PBMessageError(FEClientError): + def __init__(self, msg, errstr): + self.msg = msg + # self.sess_id = mhdr[HDR_FIELD_SESS_ID] + # self.req_id = mhdr[HDR_FIELD_REQ_ID] + self.error = -1 + self.errstr = errstr + super().__init__(f"PBMessageError: {self.errstr}: {msg}") + + +class NativeMessageError(FEClientError): + def __init__(self, mhdr, mfixed, mdata): + self.mhdr = mhdr + self.sess_id = mhdr[HDR_FIELD_SESS_ID] + self.req_id = mhdr[HDR_FIELD_REQ_ID] + self.error = mfixed[0] + self.errstr = cstr(mdata) + super().__init__( + "NativeMessageError: " + f"session {self.sess_id} reqid {self.req_id} " + f"error {self.error}: {self.errstr}" + ) + + +# +# Low-level socket functions +# + + +def recv_wait(sock, size): + """Receive a fixed number of bytes from a stream socket.""" + data = b"" + while len(data) < size: + newdata = sock.recv(size - len(data)) + if not newdata: + raise Exception("Socket closed") + data += newdata + return data + + +def recv_msg(sock): + marker = recv_wait(sock, 4) + assert marker in (MGMT_MSG_MARKER_PROTOBUF, MGMT_MSG_MARKER_NATIVE) + + msize = int.from_bytes(recv_wait(sock, 4), byteorder=sys.byteorder) + assert msize >= 8 + mdata = recv_wait(sock, msize - 8) if msize > 8 else b"" + + return mdata, marker == MGMT_MSG_MARKER_NATIVE + + +def send_msg(sock, marker, mdata): + """Send a mgmtd native message to a stream socket.""" + msize = int.to_bytes(len(mdata) + 8, byteorder=sys.byteorder, length=4) + sock.send(marker) + sock.send(msize) + sock.send(mdata) + + +class Session: + """A session to the mgmtd server.""" + + client_id = 1 + + def __init__(self, sock): + self.sock = sock + self.next_req_id = 1 + + req = mgmt_pb2.FeMessage() + req.register_req.client_name = "test-client" + self.send_pb_msg(req) + logging.debug("Sent FeRegisterReq: %s", req) + + req = mgmt_pb2.FeMessage() + req.session_req.create = 1 + req.session_req.client_conn_id = Session.client_id + Session.client_id += 1 + self.send_pb_msg(req) + logging.debug("Sent FeSessionReq: %s", req) + + reply = self.recv_pb_msg(mgmt_pb2.FeMessage()) + logging.debug("Received FeSessionReply: %s", repr(reply)) + + assert reply.session_reply.success + self.sess_id = reply.session_reply.session_id + + def close(self, clean=True): + if clean: + req = mgmt_pb2.FeMessage() + req.session_req.create = 0 + req.session_req.sess_id = self.sess_id + self.send_pb_msg(req) + self.sock.close() + self.sock = None + + def get_next_req_id(self): + req_id = self.next_req_id + self.next_req_id += 1 + return req_id + + # -------------------------- + # Protobuf message functions + # -------------------------- + + def recv_pb_msg(self, msg): + """Receive a protobuf message.""" + mdata, native = recv_msg(self.sock) + assert not native + + msg.ParseFromString(mdata) + + req = getattr(msg, msg.WhichOneof("message")) + if req.HasField("success"): + if not req.success: + raise PBMessageError(msg, req.error_if_any) + + return msg + + def send_pb_msg(self, msg): + """Send a protobuf message.""" + mdata = msg.SerializeToString() + return send_msg(self.sock, MGMT_MSG_MARKER_PROTOBUF, mdata) + + # ------------------------ + # Native message functions + # ------------------------ + + def recv_native_msg(self): + """Send a native message.""" + mdata, native = recv_msg(self.sock) + assert native + + hlen = struct.calcsize(MSG_HDR_FMT) + hdata = mdata[:hlen] + mhdr = struct.unpack(MSG_HDR_FMT, hdata) + code = mhdr[0] + + if code not in msg_native_formats: + raise Exception(f"Unknown native msg code {code} rcvd") + + mfmt = msg_native_formats[code] + flen = struct.calcsize(mfmt) + fdata = mdata[hlen : hlen + flen] + mfixed = struct.unpack(mfmt, fdata) + mdata = mdata[hlen + flen :] + + if code == MSG_ERROR_FMT: + raise NativeMessageError(mhdr, mfixed, mdata) + + return mhdr, mfixed, mdata + + def send_native_msg(self, mdata): + """Send a native message.""" + return send_msg(self.sock, MGMT_MSG_MARKER_NATIVE, mdata) + + def get_native_msg_header(self, msg_code): + req_id = self.get_next_req_id() + hdata = struct.pack(MSG_HDR_FMT, msg_code, 0, self.sess_id, req_id) + return hdata, req_id + + # ----------------------- + # Front-end API Fountains + # ----------------------- + + def lock(self, lock=True, ds_id=mgmt_pb2.CANDIDATE_DS): + req = mgmt_pb2.FeMessage() + req.lockds_req.session_id = self.sess_id + req.lockds_req.req_id = self.get_next_req_id() + req.lockds_req.ds_id = ds_id + req.lockds_req.lock = lock + self.send_pb_msg(req) + logging.debug("Sent LockDsReq: %s", req) + + reply = self.recv_pb_msg(mgmt_pb2.FeMessage()) + logging.debug("Received Reply: %s", repr(reply)) + assert reply.lockds_reply.success + + def get_data(self, query, data=True, config=False): + # Create the message + mdata, req_id = self.get_native_msg_header(MSG_CODE_GET_DATA) + flags = GET_DATA_FLAG_STATE if data else 0 + flags |= GET_DATA_FLAG_CONFIG if config else 0 + mdata += struct.pack(MSG_GET_DATA_FMT, MSG_FORMAT_JSON, flags) + mdata += query.encode("utf-8") + b"\x00" + + self.send_native_msg(mdata) + logging.debug("Sent GET-TREE") + + mhdr, mfixed, mdata = self.recv_native_msg() + assert mdata[-1] == 0 + result = mdata[:-1].decode("utf-8") + + logging.debug("Received GET: %s: %s", mfixed, mdata) + return result + + # def subscribe(self, notif_xpath): + # # Create the message + # mdata, req_id = self.get_native_msg_header(MSG_CODE_SUBSCRIBE) + # mdata += struct.pack(MSG_SUBSCRIBE_FMT, MSG_FORMAT_JSON) + # mdata += notif_xpath.encode("utf-8") + b"\x00" + + # self.send_native_msg(mdata) + # logging.debug("Sent SUBSCRIBE") + + def recv_notify(self, xpaths=None): + while True: + logging.debug("Waiting for Notify Message") + mhdr, mfixed, mdata = self.recv_native_msg() + if mhdr[HDR_FIELD_CODE] == MSG_CODE_NOTIFY: + logging.debug("Received Notify Message: %s: %s", mfixed, mdata) + else: + raise Exception(f"Received NON-NOTIFY Message: {mfixed}: {mdata}") + + vsplit = mhdr[HDR_FIELD_VSPLIT] + assert mdata[vsplit - 1] == 0 + xpath = mdata[: vsplit - 1].decode("utf-8") + + assert mdata[-1] == 0 + result = mdata[vsplit:-1].decode("utf-8") + + if not xpaths: + return result + js = json.loads(result) + key = [x for x in js.keys()][0] + for xpath in xpaths: + if key.startswith(xpath): + return result + logging.debug("'%s' didn't match xpath filters", key) + + +def __parse_args(): + MPATH = "/var/run/frr/mgmtd_fe.sock" + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", "--listen", nargs="*", metavar="XPATH", help="xpath[s] to listen for" + ) + parser.add_argument( + "--notify-count", + type=int, + default=1, + help="Number of notifications to listen for 0 for infinite", + ) + parser.add_argument( + "-b", "--both", action="store_true", help="return both config and data" + ) + parser.add_argument( + "-c", "--config-only", action="store_true", help="return config only" + ) + parser.add_argument( + "-q", "--query", nargs="+", metavar="XPATH", help="xpath[s] to query" + ) + parser.add_argument("-s", "--server", default=MPATH, help="path to server socket") + parser.add_argument("-v", "--verbose", action="store_true", help="Be verbose") + args = parser.parse_args() + + level = logging.DEBUG if args.verbose else logging.INFO + logging.basicConfig(level=level, format="%(asctime)s %(levelname)s: %(message)s") + + return args + + +def __server_connect(spath): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + logging.debug("Connecting to server on %s", spath) + while ec := sock.connect_ex(str(spath)): + logging.warn("retry server connection in .5s (%s)", os.strerror(ec)) + time.sleep(0.5) + logging.info("Connected to server on %s", spath) + return sock + + +def __main(): + args = __parse_args() + sock = __server_connect(Path(args.server)) + sess = Session(sock) + + if args.query: + # Performa an xpath query + # query = "/frr-interface:lib/interface/state/mtu" + for query in args.query: + logging.info("Sending query: %s", query) + result = sess.get_data( + query, data=not args.config_only, config=(args.both or args.config_only) + ) + print(result) + + if args.listen is not None: + i = args.notify_count + while i > 0 or args.notify_count == 0: + notif = sess.recv_notify(args.listen) + print(notif) + i -= 1 + + +def main(): + try: + __main() + except KeyboardInterrupt: + logging.info("Exiting") + except Exception as error: + logging.error("Unexpected error exiting: %s", error, exc_info=True) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py index e7cd657..814813f 100644 --- a/tests/topotests/lib/snmptest.py +++ b/tests/topotests/lib/snmptest.py @@ -18,6 +18,7 @@ Basic usage instructions: """ from lib.topolog import logger +import re class SnmpTester(object): @@ -72,16 +73,6 @@ class SnmpTester(object): # third token onwards is the value of the object return tokens[0].split(".", 1)[1] - @staticmethod - def _get_snmp_oid(snmp_output): - tokens = snmp_output.strip().split() - - # if len(tokens) > 5: - # return None - - # third token is the value of the object - return tokens[0].split(".", 1)[1] - def _parse_multiline(self, snmp_output): results = snmp_output.strip().split("\n") @@ -116,6 +107,151 @@ class SnmpTester(object): result = self.router.cmd(cmd) return self._parse_multiline(result) + def parse_notif_ipv4(self, notif): + # normalise values + notif = re.sub(":", "", notif) + notif = re.sub('"([0-9]{2}) ([0-9]{2}) "', r"\1\2", notif) + notif = re.sub('"([0-9]{2}) "', r"\1", notif) + elems = re.findall(r"([0-9,\.]+) = ([0-9,\.]+)", notif) + + # remove common part + elems = elems[1:] + return elems + + def is_notif_bgp4_valid(self, output_list, address): + oid_notif_type = ".1.3.6.1.6.3.1.1.4.1.0" + peer_notif_established = ".1.3.6.1.2.1.15.0.1" + peer_notif_backward = ".1.3.6.1.2.1.15.0.2" + oid_peer_last_error = ".1.3.6.1.2.1.15.3.1.14" + oid_peer_remote_addr = ".1.3.6.1.2.1.15.3.1.7" + oid_peer_state = ".1.3.6.1.2.1.15.3.1.2" + + nb_notif = len(output_list) + for nb in range(0, nb_notif - 1): + # identify type of notification + # established or BackwardTransition + + if output_list[nb][0][0] != "{}".format(oid_notif_type): + return False + + if output_list[nb][0][1] == "{}".format(peer_notif_established): + logger.info("Established notification") + elif output_list[nb][0][1] == "{}".format(peer_notif_backward): + logger.info("Backward transition notification") + else: + return False + + # same behavior for 2 notification type in bgp4 + if output_list[nb][1][0] != "{}.{}".format(oid_peer_remote_addr, address): + return False + + if output_list[nb][2][0] != "{}.{}".format(oid_peer_last_error, address): + return False + if output_list[nb][3][0] != "{}.{}".format(oid_peer_state, address): + return False + + return True + + def is_notif_bgp4v2_valid(self, output_list, address, type_requested): + oid_notif_type = ".1.3.6.1.6.3.1.1.4.1.0" + peer_notif_established = ".1.3.6.1.3.5.1.0.1" + peer_notif_backward = ".1.3.6.1.3.5.1.0.2" + oid_peer_state = ".1.3.6.1.3.5.1.1.2.1.13" + oid_peer_local_port = ".1.3.6.1.3.5.1.1.2.1.6" + oid_peer_remote_port = ".1.3.6.1.3.5.1.1.2.1.9" + oid_peer_err_code_recv = ".1.3.6.1.3.5.1.1.3.1.1" + oid_peer_err_sub_code_recv = ".1.3.6.1.3.5.1.1.3.1.2" + oid_peer_err_recv_text = ".1.3.6.1.3.5.1.1.3.1.4" + + nb_notif = len(output_list) + for nb in range(nb_notif): + if output_list[nb][0][0] != "{}".format(oid_notif_type): + return False + + if output_list[nb][0][1] == "{}".format(peer_notif_established): + logger.info("Established notification") + notif_type = "Estab" + + elif output_list[nb][0][1] == "{}".format(peer_notif_backward): + logger.info("Backward transition notification") + notif_type = "Backward" + else: + return False + + if notif_type != type_requested: + continue + + if output_list[nb][1][0] != "{}.1.{}".format(oid_peer_state, address): + continue + + if output_list[nb][2][0] != "{}.1.{}".format(oid_peer_local_port, address): + return False + + if output_list[nb][3][0] != "{}.1.{}".format(oid_peer_remote_port, address): + return False + + if notif_type == "Estab": + return True + + if output_list[nb][4][0] != "{}.1.{}".format( + oid_peer_err_code_recv, address + ): + return False + + if output_list[nb][5][0] != "{}.1.{}".format( + oid_peer_err_sub_code_recv, address + ): + return False + + if output_list[nb][6][0] != "{}.1.{}".format( + oid_peer_err_recv_text, address + ): + return False + + return True + + return False + + def get_notif_bgp4(self, output_file): + notifs = [] + notif_list = [] + whitecleanfile = re.sub("\t", " ", output_file) + results = whitecleanfile.strip().split("\n") + + # don't consider additional SNMP or application messages + for result in results: + if re.search(r"(\.([0-9]+))+\s", result): + notifs.append(result) + + oid_v4 = r"1\.3\.6\.1\.2\.1\.15" + for one_notif in notifs: + is_ipv4_notif = re.search(oid_v4, one_notif) + if is_ipv4_notif != None: + formated_notif = self.parse_notif_ipv4(one_notif) + notif_list.append(formated_notif) + + return notif_list + + def get_notif_bgp4v2(self, output_file): + notifs = [] + notif_list = [] + whitecleanfile = re.sub("\t", " ", output_file) + results = whitecleanfile.strip().split("\n") + + # don't consider additional SNMP or application messages + for result in results: + if re.search(r"(\.([0-9]+))+\s", result): + notifs.append(result) + + oid_v6 = r"1\.3\.6\.1\.3\.5\.1" + for one_notif in notifs: + is_ipv6_notif = re.search(oid_v6, one_notif) + if is_ipv6_notif != None: + formated_notif = self.parse_notif_ipv4(one_notif) + notif_list.append(formated_notif) + + return notif_list + def test_oid(self, oid, value): print("oid: {}".format(self.get_next(oid))) return self.get_next(oid) == value diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 4d935b9..155d2d0 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -81,20 +81,20 @@ def is_string(value): def get_exabgp_cmd(commander=None): - """Return the command to use for ExaBGP version < 4.""" + """Return the command to use for ExaBGP version >= 4.2.11""" if commander is None: commander = Commander("exabgp", logger=logging.getLogger("exabgp")) def exacmd_version_ok(exacmd): - logger.debug("checking %s for exabgp < version 4", exacmd) + logger.debug("checking %s for exabgp version >= 4.2.11", exacmd) _, stdout, _ = commander.cmd_status(exacmd + " -v", warn=False) m = re.search(r"ExaBGP\s*:\s*((\d+)\.(\d+)(?:\.(\d+))?)", stdout) if not m: return False version = m.group(1) - if topotest.version_cmp(version, "4") >= 0: - logging.debug("found exabgp version >= 4 in %s will keep looking", exacmd) + if topotest.version_cmp(version, "4.2.11") < 0: + logging.debug("found exabgp version < 4.2.11 in %s will keep looking", exacmd) return False logger.info("Using ExaBGP version %s in %s", version, exacmd) return True @@ -102,14 +102,14 @@ def get_exabgp_cmd(commander=None): exacmd = commander.get_exec_path("exabgp") if exacmd and exacmd_version_ok(exacmd): return exacmd - py2_path = commander.get_exec_path("python2") - if py2_path: - exacmd = py2_path + " -m exabgp" + py3_path = commander.get_exec_path("python3") + if py3_path: + exacmd = py3_path + " -m exabgp" if exacmd_version_ok(exacmd): return exacmd - py2_path = commander.get_exec_path("python") - if py2_path: - exacmd = py2_path + " -m exabgp" + py3_path = commander.get_exec_path("python") + if py3_path: + exacmd = py3_path + " -m exabgp" if exacmd_version_ok(exacmd): return exacmd return None @@ -719,6 +719,7 @@ class TopoRouter(TopoGear): "/etc/frr", "/etc/snmp", "/var/run/frr", + "/var/lib/frr", "/var/log", ] @@ -744,6 +745,7 @@ class TopoRouter(TopoGear): RD_SNMP = 18 RD_PIM6 = 19 RD_MGMTD = 20 + RD_TRAP = 21 RD = { RD_FRR: "frr", RD_ZEBRA: "zebra", @@ -766,6 +768,7 @@ class TopoRouter(TopoGear): RD_PATH: "pathd", RD_SNMP: "snmpd", RD_MGMTD: "mgmtd", + RD_TRAP: "snmptrapd", } def __init__(self, tgen, cls, name, **params): @@ -842,7 +845,7 @@ class TopoRouter(TopoGear): TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR, - TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD. + TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD, TopoRouter.RD_TRAP. Possible `source` values are `None` for an empty config file, a path name which is used directly, or a file name with no path components which is first looked for @@ -880,7 +883,7 @@ class TopoRouter(TopoGear): # Enable all daemon command logging, logging files # and set them to the start dir. for daemon, enabled in nrouter.daemons.items(): - if enabled and daemon != "snmpd": + if enabled and daemon != "snmpd" and daemon != "snmptrapd": self.vtysh_cmd( "\n".join( [ @@ -1196,7 +1199,7 @@ class TopoExaBGP(TopoHost): * Run ExaBGP with env file `env_file` and configuration peer*/exabgp.cfg """ exacmd = self.tgen.get_exabgp_cmd() - assert exacmd, "Can't find a usabel ExaBGP (must be < version 4)" + assert exacmd, "Can't find a usable ExaBGP (must be version >= 4.2.11)" self.run("mkdir -p /etc/exabgp") self.run("chmod 755 /etc/exabgp") @@ -1207,8 +1210,22 @@ class TopoExaBGP(TopoHost): self.run("chmod 644 /etc/exabgp/*") self.run("chmod a+x /etc/exabgp/*.py") self.run("chown -R exabgp:exabgp /etc/exabgp") + self.run("[ -p /var/run/exabgp.in ] || mkfifo /var/run/exabgp.in") + self.run("[ -p /var/run/exabgp.out ] || mkfifo /var/run/exabgp.out") + self.run("chown exabgp:exabgp /var/run/exabgp.{in,out}") + self.run("chmod 600 /var/run/exabgp.{in,out}") + + log_dir = os.path.join(self.logdir, self.name) + self.run("chmod 777 {}".format(log_dir)) + + log_file = os.path.join(log_dir, "exabgp.log") - output = self.run(exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg") + env_cmd = "env exabgp.log.level=INFO " + env_cmd += "exabgp.log.destination={} ".format(log_file) + + output = self.run( + env_cmd + exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg " + ) if output is None or len(output) == 0: output = "" @@ -1369,7 +1386,7 @@ def diagnose_env_linux(rundir): logger.info("LDPd tests will not run (missing mpls-iptunnel kernel module)") if not get_exabgp_cmd(): - logger.warning("Failed to find exabgp < 4") + logger.warning("Failed to find exabgp >= 4.2.11") logger.removeHandler(fhandler) fhandler.close() diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index c220bcf..2bb8923 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -31,7 +31,8 @@ from copy import deepcopy import lib.topolog as topolog from lib.micronet_compat import Node from lib.topolog import logger -from munet.base import Timeout +from munet.base import commander, get_exec_path_host, Timeout +from munet.testing.util import retry from lib import micronet @@ -1261,8 +1262,8 @@ def rlimit_atleast(rname, min_value, raises=False): def fix_netns_limits(ns): # Maximum read and write socket buffer sizes - sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20]) - sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20]) + sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20]) + sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20]) sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0) sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0) @@ -1321,8 +1322,8 @@ def fix_host_limits(): sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024) # Maximum read and write socket buffer sizes - sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20) - sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20) + sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20) + sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20) # Garbage Collection Settings for ARP and Neighbors sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024) @@ -1363,6 +1364,8 @@ def setup_node_tmpdir(logdir, name): class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" + gdb_emacs_router = None + def __init__(self, name, *posargs, **params): # Backward compatibility: # Load configuration defaults like topogen. @@ -1380,6 +1383,8 @@ class Router(Node): ) self.perf_daemons = {} + self.rr_daemons = {} + self.valgrind_gdb_daemons = {} # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. @@ -1420,6 +1425,7 @@ class Router(Node): "pathd": 0, "snmpd": 0, "mgmtd": 0, + "snmptrapd": 0, } self.daemons_options = {"zebra": ""} self.reportCores = True @@ -1799,7 +1805,12 @@ class Router(Node): gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints") gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons") gdb_routers = g_pytest_config.get_option_list("--gdb-routers") + gdb_use_emacs = bool(g_pytest_config.option.gdb_use_emacs) + rr_daemons = g_pytest_config.get_option_list("--rr-daemons") + rr_routers = g_pytest_config.get_option_list("--rr-routers") + rr_options = g_pytest_config.get_option("--rr-options", "") valgrind_extra = bool(g_pytest_config.option.valgrind_extra) + valgrind_leak_kinds = g_pytest_config.option.valgrind_leak_kinds valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks) strace_daemons = g_pytest_config.get_option_list("--strace-daemons") @@ -1875,6 +1886,15 @@ class Router(Node): # do not since apparently presence of the pidfile impacts BGP GR self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase)) + def do_gdb_or_rr(gdb): + routers = gdb_routers if gdb else rr_routers + daemons = gdb_daemons if gdb else rr_daemons + return ( + (routers or daemons) + and (not routers or self.name in routers or "all" in routers) + and (not daemons or daemon in daemons or "all" in daemons) + ) + rediropt = " > {0}.out 2> {0}.err".format(daemon) if daemon == "snmpd": binary = "/usr/sbin/snmpd" @@ -1883,6 +1903,15 @@ class Router(Node): daemon_opts ) + "{}.pid -x /etc/frr/agentx".format(runbase) # check_daemon_files.append(runbase + ".pid") + elif daemon == "snmptrapd": + binary = "/usr/sbin/snmptrapd" + cmdenv = "" + cmdopt = ( + "{} ".format(daemon_opts) + + "-C -c /etc/{}/snmptrapd.conf".format(self.routertype) + + " -p {}.pid".format(runbase) + + " -LF 6-7 {}/{}/snmptrapd.log".format(self.logdir, self.name) + ) else: binary = os.path.join(self.daemondir, daemon) check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) @@ -1901,13 +1930,23 @@ class Router(Node): 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 + + valgrind_logbase = f"{self.logdir}/{self.name}.valgrind.{daemon}" + if do_gdb_or_rr(True): + cmdenv += " exec" + cmdenv += ( + " /usr/bin/valgrind --num-callers=50" + f" --log-file={valgrind_logbase}.%p" + f" --leak-check=full --suppressions={supp_file}" ) + if valgrind_leak_kinds: + cmdenv += f" --show-leak-kinds={valgrind_leak_kinds}" if valgrind_extra: cmdenv += ( " --gen-suppressions=all --expensive-definedness-checks=yes" ) + if do_gdb_or_rr(True): + cmdenv += " --vgdb-error=0" 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 @@ -1922,16 +1961,22 @@ class Router(Node): tail_log_files.append( "{}/{}/{}.log".format(self.logdir, self.name, daemon) ) + if extra_opts: cmdopt += " " + extra_opts + if do_gdb_or_rr(True) and do_gdb_or_rr(False): + logger.warning("cant' use gdb and rr at same time") + if ( - (gdb_routers or gdb_daemons) - and ( - not gdb_routers or self.name in gdb_routers or "all" in gdb_routers - ) - and (not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons) - ): + not gdb_use_emacs or Router.gdb_emacs_router or valgrind_memleaks + ) and do_gdb_or_rr(True): + if Router.gdb_emacs_router is not None: + logger.warning( + "--gdb-use-emacs can only run a single router and daemon, using" + " new window" + ) + if daemon == "snmpd": cmdopt += " -f " @@ -1941,9 +1986,133 @@ class Router(Node): gdbcmd += " -ex 'set breakpoint pending on'" for bp in gdb_breakpoints: gdbcmd += " -ex 'b {}'".format(bp) - gdbcmd += " -ex 'run {}'".format(cmdopt) - self.run_in_window(gdbcmd, daemon) + if not valgrind_memleaks: + gdbcmd += " -ex 'run {}'".format(cmdopt) + self.run_in_window(gdbcmd, daemon) + + logger.info( + "%s: %s %s launched in gdb window", + self, + self.routertype, + daemon, + ) + + else: + cmd = " ".join([cmdenv, binary, cmdopt]) + p = self.popen(cmd) + self.valgrind_gdb_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with perf using: %s', + self, + daemon, + p.returncode, + cmd, + ) + assert False, "Faled to launch valgrind with gdb" + logger.debug( + "%s: %s %s started with perf", self, self.routertype, daemon + ) + # Now read the erorr log file until we ae given launch priority + timeout = Timeout(30) + vpid = None + for remaining in timeout: + try: + fname = f"{valgrind_logbase}.{p.pid}" + logging.info("Checking %s for valgrind launch info", fname) + o = open(fname, encoding="ascii").read() + except FileNotFoundError: + logging.info("%s not present yet", fname) + else: + m = re.search(r"target remote \| (.*vgdb) --pid=(\d+)", o) + if m: + vgdb_cmd = m.group(0) + break + time.sleep(1) + else: + assert False, "Faled to get launch info for valgrind with gdb" + + gdbcmd += f" -ex '{vgdb_cmd}'" + gdbcmd += " -ex 'c'" + self.run_in_window(gdbcmd, daemon) + + logger.info( + "%s: %s %s launched in gdb window", + self, + self.routertype, + daemon, + ) + elif gdb_use_emacs and do_gdb_or_rr(True): + assert Router.gdb_emacs_router is None + Router.gdb_emacs_router = self + + assert not valgrind_memleaks, "vagrind gdb in emacs not supported yet" + + if daemon == "snmpd": + cmdopt += " -f " + cmdopt += rediropt + + sudo_path = get_exec_path_host("sudo") + ecbin = [ + sudo_path, + "-Eu", + os.environ["SUDO_USER"], + get_exec_path_host("emacsclient"), + ] + pre_cmd = self._get_pre_cmd(True, False, ns_only=True, root_level=True) + # why fail:? gdb -i=mi -iex='set debuginfod enabled off' {binary} " + gdbcmd = f"{sudo_path} {pre_cmd} gdb -i=mi {binary} " + + commander.cmd_raises( + ecbin + + [ + "--eval", + f'(gdb "{gdbcmd}"))', + ] + ) + + elcheck = ( + '(ignore-errors (with-current-buffer "*gud-nsenter*"' + " (and (string-match-p" + ' "(gdb) "' + " (buffer-substring-no-properties " + ' (- (point-max) 10) (point-max))) "ready")))' + ) + + @retry(10) + def emacs_gdb_ready(): + check = commander.cmd_nostatus(ecbin + ["--eval", elcheck]) + return None if "ready" in check else False + + emacs_gdb_ready() + + # target gdb commands + cmd = "set breakpoint pending on" + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + # gdb breakpoints + for bp in gdb_breakpoints: + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "br {bp}" "*gud-gdb*")', + ] + ) + + self.cmd_raises( + ecbin + + [ + "--eval", + f'(gud-gdb-run-command-fetch-lines "run {cmdopt}" "*gud-gdb*")', + ] + ) logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon @@ -1969,8 +2138,31 @@ class Router(Node): logger.debug( "%s: %s %s started with perf", self, self.routertype, daemon ) + elif do_gdb_or_rr(False): + cmdopt += rediropt + cmd = " ".join( + [ + "rr record -o {} {} --".format(self.rundir / "rr", rr_options), + binary, + cmdopt, + ] + ) + p = self.popen(cmd) + self.rr_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with rr using: %s', + self, + daemon, + p.returncode, + cmd, + ) + else: + logger.debug( + "%s: %s %s started with rr", self, self.routertype, daemon + ) else: - if daemon != "snmpd": + if daemon != "snmpd" and daemon != "snmptrapd": cmdopt += " -d " cmdopt += rediropt @@ -2213,6 +2405,8 @@ class Router(Node): for daemon in self.daemons: if daemon == "snmpd": continue + if daemon == "snmptrapd": + continue if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning): sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon)) if daemon == "staticd": diff --git a/tests/topotests/mgmt_config/test_config.py b/tests/topotests/mgmt_config/test_config.py index b07ed8f..1d73222 100644 --- a/tests/topotests/mgmt_config/test_config.py +++ b/tests/topotests/mgmt_config/test_config.py @@ -61,8 +61,7 @@ import pytest from lib.common_config import retry, step from lib.topogen import Topogen, TopoRouter -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @retry(retry_timeout=1, initial_wait=0.1) diff --git a/tests/topotests/mgmt_config/test_regression.py b/tests/topotests/mgmt_config/test_regression.py index 00c3e01..928151a 100644 --- a/tests/topotests/mgmt_config/test_regression.py +++ b/tests/topotests/mgmt_config/test_regression.py @@ -12,7 +12,7 @@ Test mgmtd regressions import pytest from lib.topogen import Topogen -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") @@ -51,3 +51,23 @@ def test_regression_issue_13920(tgen): ) output = r1.net.checkRouterCores() assert not output.strip() + + +def test_regression_pullreq_15423(tgen): + r1 = tgen.gears["r1"] + r1.vtysh_multicmd( + """ + conf t + access-list test seq 1 permit ip any 10.10.10.0 0.0.0.255 + """ + ) + + output = r1.vtysh_multicmd( + """ + conf terminal file-lock + mgmt delete-config /frr-filter:lib/access-list[name='test'][type='ipv4']/entry[sequence='1']/destination-network + mgmt commit apply + end + """ + ) + assert "No changes found" not in output diff --git a/tests/topotests/mgmt_debug_flags/r1/frr.conf b/tests/topotests/mgmt_debug_flags/r1/frr.conf new file mode 100644 index 0000000..ba95b8d --- /dev/null +++ b/tests/topotests/mgmt_debug_flags/r1/frr.conf @@ -0,0 +1,11 @@ +log timestamp precision 6 +log file frr.log + +! debug mgmt backend datastore frontend transaction +! debug mgmt client frontend +! debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit +ip route 11.11.11.11/32 1.1.1.2 \ No newline at end of file diff --git a/tests/topotests/mgmt_debug_flags/test_debug.py b/tests/topotests/mgmt_debug_flags/test_debug.py new file mode 100644 index 0000000..e49d9b7 --- /dev/null +++ b/tests/topotests/mgmt_debug_flags/test_debug.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.common_config import step +from lib.topogen import Topogen +from munet.watchlog import WatchLog + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",)} + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + for rname, router in tgen.routers().items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_client_debug_enable(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Start watching log files + watch_mgmtd_log = WatchLog(r1.net.rundir / "mgmtd.log") + watch_staticd_log = WatchLog(r1.net.rundir / "staticd.log") + + def __test_debug(r1, on): + watch_mgmtd_log.snapshot() + watch_staticd_log.snapshot() + + # Add ip route and remove look for debug. + r1.vtysh_cmd("conf t\nip route 11.11.11.11/32 1.1.1.2") + r1.vtysh_cmd("conf t\nno ip route 11.11.11.11/32 1.1.1.2") + + new_mgmt_logs = watch_mgmtd_log.snapshot() + new_be_logs = watch_staticd_log.snapshot() + + fe_cl_msg = "Sending SET_CONFIG_REQ" + fe_ad_msg = "Got SETCFG_REQ" + be_ad_msg = "Sending CFGDATA_CREATE_REQ" + be_cl_msg = "Got CFG_APPLY_REQ" + + if on: + assert fe_cl_msg in new_mgmt_logs + assert fe_ad_msg in new_mgmt_logs + assert be_ad_msg in new_mgmt_logs + assert be_cl_msg in new_be_logs + else: + assert fe_cl_msg not in new_mgmt_logs + assert fe_ad_msg not in new_mgmt_logs + assert be_ad_msg not in new_mgmt_logs + assert be_cl_msg not in new_be_logs + + step("test debug off") + __test_debug(r1, False) + + # Turn it on + step("tests debug on") + r1.vtysh_cmd("debug mgmt client frontend") + r1.vtysh_cmd("debug mgmt client backend") + r1.vtysh_cmd("debug mgmt backend frontend") + __test_debug(r1, True) + + # Turn it off + step("tests debug off") + r1.vtysh_cmd("no debug mgmt client frontend") + r1.vtysh_cmd("no debug mgmt client backend") + r1.vtysh_cmd("no debug mgmt backend frontend") + __test_debug(r1, False) + + # Configure it on + step("tests debug on") + r1.vtysh_cmd("conf t\ndebug mgmt client frontend") + r1.vtysh_cmd("conf t\ndebug mgmt client backend") + r1.vtysh_cmd("conf t\ndebug mgmt backend frontend") + __test_debug(r1, True) + + # Configure it off + step("tests debug off") + r1.vtysh_cmd("conf t\nno debug mgmt client frontend") + r1.vtysh_cmd("conf t\nno debug mgmt client backend") + r1.vtysh_cmd("conf t\nno debug mgmt backend frontend") + __test_debug(r1, False) + + # Turn it on + step("tests debug on") + r1.vtysh_cmd("debug mgmt client frontend") + r1.vtysh_cmd("debug mgmt client backend") + r1.vtysh_cmd("debug mgmt backend frontend") + __test_debug(r1, True) + # Configure it on + step("tests debug on") + r1.vtysh_cmd("conf t\ndebug mgmt client frontend") + r1.vtysh_cmd("conf t\ndebug mgmt client backend") + r1.vtysh_cmd("conf t\ndebug mgmt backend frontend") + __test_debug(r1, True) + # Turn it off + step("tests debug on") + r1.vtysh_cmd("no debug mgmt client frontend") + r1.vtysh_cmd("no debug mgmt client backend") + r1.vtysh_cmd("no debug mgmt backend frontend") + __test_debug(r1, True) + # Configure it off + step("tests debug off") + r1.vtysh_cmd("conf t\nno debug mgmt client frontend") + r1.vtysh_cmd("conf t\nno debug mgmt client backend") + r1.vtysh_cmd("conf t\nno debug mgmt backend frontend") + __test_debug(r1, False) diff --git a/tests/topotests/mgmt_fe_client/mgmt_pb2.py b/tests/topotests/mgmt_fe_client/mgmt_pb2.py new file mode 100644 index 0000000..0aa8803 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/mgmt_pb2.py @@ -0,0 +1,1990 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: mgmt.proto + +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='mgmt.proto', + package='mgmtd', + syntax='proto2', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\nmgmt.proto\x12\x05mgmtd\"\x1e\n\rYangDataXPath\x12\r\n\x05xpath\x18\x01 \x02(\t\"3\n\rYangDataValue\x12\x19\n\x0f\x65ncoded_str_val\x18\x64 \x01(\tH\x00\x42\x07\n\x05value\">\n\x08YangData\x12\r\n\x05xpath\x18\x01 \x02(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.mgmtd.YangDataValue\"X\n\x0eYangCfgDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x02(\x0b\x32\x0f.mgmtd.YangData\x12\'\n\x08req_type\x18\x02 \x02(\x0e\x32\x15.mgmtd.CfgDataReqType\"B\n\x0eYangGetDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x02(\x0b\x32\x0f.mgmtd.YangData\x12\x11\n\tnext_indx\x18\x02 \x02(\x03\"R\n\x0e\x42\x65SubscribeReq\x12\x13\n\x0b\x63lient_name\x18\x01 \x02(\t\x12\x18\n\x10subscribe_xpaths\x18\x02 \x02(\x08\x12\x11\n\txpath_reg\x18\x03 \x03(\t\"#\n\x10\x42\x65SubscribeReply\x12\x0f\n\x07success\x18\x01 \x02(\x08\"*\n\x08\x42\x65TxnReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63reate\x18\x02 \x02(\x08\"=\n\nBeTxnReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63reate\x18\x02 \x02(\x08\x12\x0f\n\x07success\x18\x03 \x02(\x08\"b\n\x12\x42\x65\x43\x66gDataCreateReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\'\n\x08\x64\x61ta_req\x18\x02 \x03(\x0b\x32\x15.mgmtd.YangCfgDataReq\x12\x13\n\x0b\x65nd_of_data\x18\x03 \x02(\x08\"M\n\x14\x42\x65\x43\x66gDataCreateReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x03 \x01(\t\"#\n\x11\x42\x65\x43\x66gDataApplyReq\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\"L\n\x13\x42\x65\x43\x66gDataApplyReply\x12\x0e\n\x06txn_id\x18\x01 \x02(\x04\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x03 \x01(\t\"A\n\rYangDataReply\x12\x1d\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x0f.mgmtd.YangData\x12\x11\n\tnext_indx\x18\x02 \x02(\x03\"\x94\x03\n\tBeMessage\x12+\n\nsubscr_req\x18\x02 \x01(\x0b\x32\x15.mgmtd.BeSubscribeReqH\x00\x12/\n\x0csubscr_reply\x18\x03 \x01(\x0b\x32\x17.mgmtd.BeSubscribeReplyH\x00\x12\"\n\x07txn_req\x18\x04 \x01(\x0b\x32\x0f.mgmtd.BeTxnReqH\x00\x12&\n\ttxn_reply\x18\x05 \x01(\x0b\x32\x11.mgmtd.BeTxnReplyH\x00\x12\x31\n\x0c\x63\x66g_data_req\x18\x06 \x01(\x0b\x32\x19.mgmtd.BeCfgDataCreateReqH\x00\x12\x35\n\x0e\x63\x66g_data_reply\x18\x07 \x01(\x0b\x32\x1b.mgmtd.BeCfgDataCreateReplyH\x00\x12\x31\n\rcfg_apply_req\x18\x08 \x01(\x0b\x32\x18.mgmtd.BeCfgDataApplyReqH\x00\x12\x35\n\x0f\x63\x66g_apply_reply\x18\t \x01(\x0b\x32\x1a.mgmtd.BeCfgDataApplyReplyH\x00\x42\t\n\x07message\"$\n\rFeRegisterReq\x12\x13\n\x0b\x63lient_name\x18\x01 \x02(\t\"T\n\x0c\x46\x65SessionReq\x12\x0e\n\x06\x63reate\x18\x01 \x02(\x08\x12\x18\n\x0e\x63lient_conn_id\x18\x02 \x01(\x04H\x00\x12\x14\n\nsession_id\x18\x03 \x01(\x04H\x00\x42\x04\n\x02id\"]\n\x0e\x46\x65SessionReply\x12\x0e\n\x06\x63reate\x18\x01 \x02(\x08\x12\x0f\n\x07success\x18\x02 \x02(\x08\x12\x16\n\x0e\x63lient_conn_id\x18\x03 \x01(\x04\x12\x12\n\nsession_id\x18\x04 \x02(\x04\"b\n\x0b\x46\x65LockDsReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06req_id\x18\x02 \x02(\x04\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0c\n\x04lock\x18\x04 \x02(\x08\"\x8b\x01\n\rFeLockDsReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06req_id\x18\x02 \x02(\x04\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0c\n\x04lock\x18\x04 \x02(\x08\x12\x0f\n\x07success\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\"\xbf\x01\n\x0e\x46\x65SetConfigReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x03 \x02(\x04\x12#\n\x04\x64\x61ta\x18\x04 \x03(\x0b\x32\x15.mgmtd.YangCfgDataReq\x12\x17\n\x0fimplicit_commit\x18\x05 \x02(\x08\x12(\n\x0c\x63ommit_ds_id\x18\x06 \x02(\x0e\x32\x12.mgmtd.DatastoreId\"\x99\x01\n\x10\x46\x65SetConfigReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x03 \x02(\x04\x12\x0f\n\x07success\x18\x04 \x02(\x08\x12\x17\n\x0fimplicit_commit\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\"\xab\x01\n\x11\x46\x65\x43ommitConfigReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12%\n\tsrc_ds_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12%\n\tdst_ds_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x15\n\rvalidate_only\x18\x05 \x02(\x08\x12\r\n\x05\x61\x62ort\x18\x06 \x02(\x08\"\xd4\x01\n\x13\x46\x65\x43ommitConfigReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12%\n\tsrc_ds_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12%\n\tdst_ds_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x15\n\rvalidate_only\x18\x05 \x02(\x08\x12\x0f\n\x07success\x18\x06 \x02(\x08\x12\r\n\x05\x61\x62ort\x18\x07 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x08 \x01(\t\"\x86\x01\n\x08\x46\x65GetReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63onfig\x18\x02 \x02(\x08\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12#\n\x04\x64\x61ta\x18\x05 \x03(\x0b\x32\x15.mgmtd.YangGetDataReq\"\xae\x01\n\nFeGetReply\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12\x0e\n\x06\x63onfig\x18\x02 \x02(\x08\x12!\n\x05\x64s_id\x18\x03 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12\x0f\n\x07success\x18\x05 \x02(\x08\x12\x14\n\x0c\x65rror_if_any\x18\x06 \x01(\t\x12\"\n\x04\x64\x61ta\x18\x07 \x01(\x0b\x32\x14.mgmtd.YangDataReply\"0\n\x0f\x46\x65NotifyDataReq\x12\x1d\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x0f.mgmtd.YangData\"\x9c\x01\n\x13\x46\x65RegisterNotifyReq\x12\x12\n\nsession_id\x18\x01 \x02(\x04\x12!\n\x05\x64s_id\x18\x02 \x02(\x0e\x32\x12.mgmtd.DatastoreId\x12\x14\n\x0cregister_req\x18\x03 \x02(\x08\x12\x0e\n\x06req_id\x18\x04 \x02(\x04\x12(\n\ndata_xpath\x18\x05 \x03(\x0b\x32\x14.mgmtd.YangDataXPath\"\xf0\x04\n\tFeMessage\x12,\n\x0cregister_req\x18\x02 \x01(\x0b\x32\x14.mgmtd.FeRegisterReqH\x00\x12*\n\x0bsession_req\x18\x03 \x01(\x0b\x32\x13.mgmtd.FeSessionReqH\x00\x12.\n\rsession_reply\x18\x04 \x01(\x0b\x32\x15.mgmtd.FeSessionReplyH\x00\x12(\n\nlockds_req\x18\x05 \x01(\x0b\x32\x12.mgmtd.FeLockDsReqH\x00\x12,\n\x0clockds_reply\x18\x06 \x01(\x0b\x32\x14.mgmtd.FeLockDsReplyH\x00\x12+\n\nsetcfg_req\x18\x07 \x01(\x0b\x32\x15.mgmtd.FeSetConfigReqH\x00\x12/\n\x0csetcfg_reply\x18\x08 \x01(\x0b\x32\x17.mgmtd.FeSetConfigReplyH\x00\x12/\n\x0b\x63ommcfg_req\x18\t \x01(\x0b\x32\x18.mgmtd.FeCommitConfigReqH\x00\x12\x33\n\rcommcfg_reply\x18\n \x01(\x0b\x32\x1a.mgmtd.FeCommitConfigReplyH\x00\x12\"\n\x07get_req\x18\x0b \x01(\x0b\x32\x0f.mgmtd.FeGetReqH\x00\x12&\n\tget_reply\x18\x0c \x01(\x0b\x32\x11.mgmtd.FeGetReplyH\x00\x12\x31\n\x0fnotify_data_req\x18\x0f \x01(\x0b\x32\x16.mgmtd.FeNotifyDataReqH\x00\x12\x33\n\rregnotify_req\x18\x10 \x01(\x0b\x32\x1a.mgmtd.FeRegisterNotifyReqH\x00\x42\t\n\x07message*B\n\x0e\x43\x66gDataReqType\x12\x11\n\rREQ_TYPE_NONE\x10\x00\x12\x0c\n\x08SET_DATA\x10\x01\x12\x0f\n\x0b\x44\x45LETE_DATA\x10\x02*`\n\x0b\x44\x61tastoreId\x12\x0b\n\x07\x44S_NONE\x10\x00\x12\x0e\n\nRUNNING_DS\x10\x01\x12\x10\n\x0c\x43\x41NDIDATE_DS\x10\x02\x12\x12\n\x0eOPERATIONAL_DS\x10\x03\x12\x0e\n\nSTARTUP_DS\x10\x04' +) + +_CFGDATAREQTYPE = _descriptor.EnumDescriptor( + name='CfgDataReqType', + full_name='mgmtd.CfgDataReqType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='REQ_TYPE_NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SET_DATA', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DELETE_DATA', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3674, + serialized_end=3740, +) +_sym_db.RegisterEnumDescriptor(_CFGDATAREQTYPE) + +CfgDataReqType = enum_type_wrapper.EnumTypeWrapper(_CFGDATAREQTYPE) +_DATASTOREID = _descriptor.EnumDescriptor( + name='DatastoreId', + full_name='mgmtd.DatastoreId', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DS_NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RUNNING_DS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CANDIDATE_DS', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OPERATIONAL_DS', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STARTUP_DS', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3742, + serialized_end=3838, +) +_sym_db.RegisterEnumDescriptor(_DATASTOREID) + +DatastoreId = enum_type_wrapper.EnumTypeWrapper(_DATASTOREID) +REQ_TYPE_NONE = 0 +SET_DATA = 1 +DELETE_DATA = 2 +DS_NONE = 0 +RUNNING_DS = 1 +CANDIDATE_DS = 2 +OPERATIONAL_DS = 3 +STARTUP_DS = 4 + + + +_YANGDATAXPATH = _descriptor.Descriptor( + name='YangDataXPath', + full_name='mgmtd.YangDataXPath', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='xpath', full_name='mgmtd.YangDataXPath.xpath', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=21, + serialized_end=51, +) + + +_YANGDATAVALUE = _descriptor.Descriptor( + name='YangDataValue', + full_name='mgmtd.YangDataValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='encoded_str_val', full_name='mgmtd.YangDataValue.encoded_str_val', index=0, + number=100, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='value', full_name='mgmtd.YangDataValue.value', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=53, + serialized_end=104, +) + + +_YANGDATA = _descriptor.Descriptor( + name='YangData', + full_name='mgmtd.YangData', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='xpath', full_name='mgmtd.YangData.xpath', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='mgmtd.YangData.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=106, + serialized_end=168, +) + + +_YANGCFGDATAREQ = _descriptor.Descriptor( + name='YangCfgDataReq', + full_name='mgmtd.YangCfgDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangCfgDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_type', full_name='mgmtd.YangCfgDataReq.req_type', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=170, + serialized_end=258, +) + + +_YANGGETDATAREQ = _descriptor.Descriptor( + name='YangGetDataReq', + full_name='mgmtd.YangGetDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangGetDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='next_indx', full_name='mgmtd.YangGetDataReq.next_indx', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=260, + serialized_end=326, +) + + +_BESUBSCRIBEREQ = _descriptor.Descriptor( + name='BeSubscribeReq', + full_name='mgmtd.BeSubscribeReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='client_name', full_name='mgmtd.BeSubscribeReq.client_name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='subscribe_xpaths', full_name='mgmtd.BeSubscribeReq.subscribe_xpaths', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='xpath_reg', full_name='mgmtd.BeSubscribeReq.xpath_reg', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=328, + serialized_end=410, +) + + +_BESUBSCRIBEREPLY = _descriptor.Descriptor( + name='BeSubscribeReply', + full_name='mgmtd.BeSubscribeReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeSubscribeReply.success', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=412, + serialized_end=447, +) + + +_BETXNREQ = _descriptor.Descriptor( + name='BeTxnReq', + full_name='mgmtd.BeTxnReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeTxnReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.BeTxnReq.create', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=449, + serialized_end=491, +) + + +_BETXNREPLY = _descriptor.Descriptor( + name='BeTxnReply', + full_name='mgmtd.BeTxnReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeTxnReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.BeTxnReply.create', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeTxnReply.success', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=493, + serialized_end=554, +) + + +_BECFGDATACREATEREQ = _descriptor.Descriptor( + name='BeCfgDataCreateReq', + full_name='mgmtd.BeCfgDataCreateReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataCreateReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data_req', full_name='mgmtd.BeCfgDataCreateReq.data_req', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end_of_data', full_name='mgmtd.BeCfgDataCreateReq.end_of_data', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=556, + serialized_end=654, +) + + +_BECFGDATACREATEREPLY = _descriptor.Descriptor( + name='BeCfgDataCreateReply', + full_name='mgmtd.BeCfgDataCreateReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataCreateReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeCfgDataCreateReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.BeCfgDataCreateReply.error_if_any', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=656, + serialized_end=733, +) + + +_BECFGDATAAPPLYREQ = _descriptor.Descriptor( + name='BeCfgDataApplyReq', + full_name='mgmtd.BeCfgDataApplyReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataApplyReq.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=735, + serialized_end=770, +) + + +_BECFGDATAAPPLYREPLY = _descriptor.Descriptor( + name='BeCfgDataApplyReply', + full_name='mgmtd.BeCfgDataApplyReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='txn_id', full_name='mgmtd.BeCfgDataApplyReply.txn_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.BeCfgDataApplyReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.BeCfgDataApplyReply.error_if_any', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=772, + serialized_end=848, +) + + +_YANGDATAREPLY = _descriptor.Descriptor( + name='YangDataReply', + full_name='mgmtd.YangDataReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.YangDataReply.data', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='next_indx', full_name='mgmtd.YangDataReply.next_indx', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=850, + serialized_end=915, +) + + +_BEMESSAGE = _descriptor.Descriptor( + name='BeMessage', + full_name='mgmtd.BeMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='subscr_req', full_name='mgmtd.BeMessage.subscr_req', index=0, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='subscr_reply', full_name='mgmtd.BeMessage.subscr_reply', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='txn_req', full_name='mgmtd.BeMessage.txn_req', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='txn_reply', full_name='mgmtd.BeMessage.txn_reply', index=3, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_data_req', full_name='mgmtd.BeMessage.cfg_data_req', index=4, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_data_reply', full_name='mgmtd.BeMessage.cfg_data_reply', index=5, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_apply_req', full_name='mgmtd.BeMessage.cfg_apply_req', index=6, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cfg_apply_reply', full_name='mgmtd.BeMessage.cfg_apply_reply', index=7, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='message', full_name='mgmtd.BeMessage.message', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=918, + serialized_end=1322, +) + + +_FEREGISTERREQ = _descriptor.Descriptor( + name='FeRegisterReq', + full_name='mgmtd.FeRegisterReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='client_name', full_name='mgmtd.FeRegisterReq.client_name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1324, + serialized_end=1360, +) + + +_FESESSIONREQ = _descriptor.Descriptor( + name='FeSessionReq', + full_name='mgmtd.FeSessionReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.FeSessionReq.create', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_conn_id', full_name='mgmtd.FeSessionReq.client_conn_id', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSessionReq.session_id', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='id', full_name='mgmtd.FeSessionReq.id', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1362, + serialized_end=1446, +) + + +_FESESSIONREPLY = _descriptor.Descriptor( + name='FeSessionReply', + full_name='mgmtd.FeSessionReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='create', full_name='mgmtd.FeSessionReply.create', index=0, + number=1, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeSessionReply.success', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_conn_id', full_name='mgmtd.FeSessionReply.client_conn_id', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSessionReply.session_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1448, + serialized_end=1541, +) + + +_FELOCKDSREQ = _descriptor.Descriptor( + name='FeLockDsReq', + full_name='mgmtd.FeLockDsReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeLockDsReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeLockDsReq.req_id', index=1, + number=2, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeLockDsReq.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lock', full_name='mgmtd.FeLockDsReq.lock', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1543, + serialized_end=1641, +) + + +_FELOCKDSREPLY = _descriptor.Descriptor( + name='FeLockDsReply', + full_name='mgmtd.FeLockDsReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeLockDsReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeLockDsReply.req_id', index=1, + number=2, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeLockDsReply.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lock', full_name='mgmtd.FeLockDsReply.lock', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeLockDsReply.success', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeLockDsReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1644, + serialized_end=1783, +) + + +_FESETCONFIGREQ = _descriptor.Descriptor( + name='FeSetConfigReq', + full_name='mgmtd.FeSetConfigReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSetConfigReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeSetConfigReq.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeSetConfigReq.req_id', index=2, + number=3, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeSetConfigReq.data', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='implicit_commit', full_name='mgmtd.FeSetConfigReq.implicit_commit', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commit_ds_id', full_name='mgmtd.FeSetConfigReq.commit_ds_id', index=5, + number=6, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1786, + serialized_end=1977, +) + + +_FESETCONFIGREPLY = _descriptor.Descriptor( + name='FeSetConfigReply', + full_name='mgmtd.FeSetConfigReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeSetConfigReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeSetConfigReply.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeSetConfigReply.req_id', index=2, + number=3, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeSetConfigReply.success', index=3, + number=4, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='implicit_commit', full_name='mgmtd.FeSetConfigReply.implicit_commit', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeSetConfigReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1980, + serialized_end=2133, +) + + +_FECOMMITCONFIGREQ = _descriptor.Descriptor( + name='FeCommitConfigReq', + full_name='mgmtd.FeCommitConfigReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeCommitConfigReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='src_ds_id', full_name='mgmtd.FeCommitConfigReq.src_ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dst_ds_id', full_name='mgmtd.FeCommitConfigReq.dst_ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeCommitConfigReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='validate_only', full_name='mgmtd.FeCommitConfigReq.validate_only', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='abort', full_name='mgmtd.FeCommitConfigReq.abort', index=5, + number=6, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2136, + serialized_end=2307, +) + + +_FECOMMITCONFIGREPLY = _descriptor.Descriptor( + name='FeCommitConfigReply', + full_name='mgmtd.FeCommitConfigReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeCommitConfigReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='src_ds_id', full_name='mgmtd.FeCommitConfigReply.src_ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dst_ds_id', full_name='mgmtd.FeCommitConfigReply.dst_ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeCommitConfigReply.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='validate_only', full_name='mgmtd.FeCommitConfigReply.validate_only', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeCommitConfigReply.success', index=5, + number=6, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='abort', full_name='mgmtd.FeCommitConfigReply.abort', index=6, + number=7, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeCommitConfigReply.error_if_any', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2310, + serialized_end=2522, +) + + +_FEGETREQ = _descriptor.Descriptor( + name='FeGetReq', + full_name='mgmtd.FeGetReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeGetReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='config', full_name='mgmtd.FeGetReq.config', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeGetReq.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeGetReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeGetReq.data', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2525, + serialized_end=2659, +) + + +_FEGETREPLY = _descriptor.Descriptor( + name='FeGetReply', + full_name='mgmtd.FeGetReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeGetReply.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='config', full_name='mgmtd.FeGetReply.config', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeGetReply.ds_id', index=2, + number=3, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeGetReply.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='success', full_name='mgmtd.FeGetReply.success', index=4, + number=5, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_if_any', full_name='mgmtd.FeGetReply.error_if_any', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeGetReply.data', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2662, + serialized_end=2836, +) + + +_FENOTIFYDATAREQ = _descriptor.Descriptor( + name='FeNotifyDataReq', + full_name='mgmtd.FeNotifyDataReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='mgmtd.FeNotifyDataReq.data', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2838, + serialized_end=2886, +) + + +_FEREGISTERNOTIFYREQ = _descriptor.Descriptor( + name='FeRegisterNotifyReq', + full_name='mgmtd.FeRegisterNotifyReq', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='session_id', full_name='mgmtd.FeRegisterNotifyReq.session_id', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ds_id', full_name='mgmtd.FeRegisterNotifyReq.ds_id', index=1, + number=2, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='register_req', full_name='mgmtd.FeRegisterNotifyReq.register_req', index=2, + number=3, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='req_id', full_name='mgmtd.FeRegisterNotifyReq.req_id', index=3, + number=4, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data_xpath', full_name='mgmtd.FeRegisterNotifyReq.data_xpath', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2889, + serialized_end=3045, +) + + +_FEMESSAGE = _descriptor.Descriptor( + name='FeMessage', + full_name='mgmtd.FeMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='register_req', full_name='mgmtd.FeMessage.register_req', index=0, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_req', full_name='mgmtd.FeMessage.session_req', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_reply', full_name='mgmtd.FeMessage.session_reply', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lockds_req', full_name='mgmtd.FeMessage.lockds_req', index=3, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lockds_reply', full_name='mgmtd.FeMessage.lockds_reply', index=4, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='setcfg_req', full_name='mgmtd.FeMessage.setcfg_req', index=5, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='setcfg_reply', full_name='mgmtd.FeMessage.setcfg_reply', index=6, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commcfg_req', full_name='mgmtd.FeMessage.commcfg_req', index=7, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='commcfg_reply', full_name='mgmtd.FeMessage.commcfg_reply', index=8, + number=10, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='get_req', full_name='mgmtd.FeMessage.get_req', index=9, + number=11, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='get_reply', full_name='mgmtd.FeMessage.get_reply', index=10, + number=12, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='notify_data_req', full_name='mgmtd.FeMessage.notify_data_req', index=11, + number=15, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='regnotify_req', full_name='mgmtd.FeMessage.regnotify_req', index=12, + number=16, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='message', full_name='mgmtd.FeMessage.message', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=3048, + serialized_end=3672, +) + +_YANGDATAVALUE.oneofs_by_name['value'].fields.append( + _YANGDATAVALUE.fields_by_name['encoded_str_val']) +_YANGDATAVALUE.fields_by_name['encoded_str_val'].containing_oneof = _YANGDATAVALUE.oneofs_by_name['value'] +_YANGDATA.fields_by_name['value'].message_type = _YANGDATAVALUE +_YANGCFGDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_YANGCFGDATAREQ.fields_by_name['req_type'].enum_type = _CFGDATAREQTYPE +_YANGGETDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_BECFGDATACREATEREQ.fields_by_name['data_req'].message_type = _YANGCFGDATAREQ +_YANGDATAREPLY.fields_by_name['data'].message_type = _YANGDATA +_BEMESSAGE.fields_by_name['subscr_req'].message_type = _BESUBSCRIBEREQ +_BEMESSAGE.fields_by_name['subscr_reply'].message_type = _BESUBSCRIBEREPLY +_BEMESSAGE.fields_by_name['txn_req'].message_type = _BETXNREQ +_BEMESSAGE.fields_by_name['txn_reply'].message_type = _BETXNREPLY +_BEMESSAGE.fields_by_name['cfg_data_req'].message_type = _BECFGDATACREATEREQ +_BEMESSAGE.fields_by_name['cfg_data_reply'].message_type = _BECFGDATACREATEREPLY +_BEMESSAGE.fields_by_name['cfg_apply_req'].message_type = _BECFGDATAAPPLYREQ +_BEMESSAGE.fields_by_name['cfg_apply_reply'].message_type = _BECFGDATAAPPLYREPLY +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['subscr_req']) +_BEMESSAGE.fields_by_name['subscr_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['subscr_reply']) +_BEMESSAGE.fields_by_name['subscr_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['txn_req']) +_BEMESSAGE.fields_by_name['txn_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['txn_reply']) +_BEMESSAGE.fields_by_name['txn_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_data_req']) +_BEMESSAGE.fields_by_name['cfg_data_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_data_reply']) +_BEMESSAGE.fields_by_name['cfg_data_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_apply_req']) +_BEMESSAGE.fields_by_name['cfg_apply_req'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_BEMESSAGE.oneofs_by_name['message'].fields.append( + _BEMESSAGE.fields_by_name['cfg_apply_reply']) +_BEMESSAGE.fields_by_name['cfg_apply_reply'].containing_oneof = _BEMESSAGE.oneofs_by_name['message'] +_FESESSIONREQ.oneofs_by_name['id'].fields.append( + _FESESSIONREQ.fields_by_name['client_conn_id']) +_FESESSIONREQ.fields_by_name['client_conn_id'].containing_oneof = _FESESSIONREQ.oneofs_by_name['id'] +_FESESSIONREQ.oneofs_by_name['id'].fields.append( + _FESESSIONREQ.fields_by_name['session_id']) +_FESESSIONREQ.fields_by_name['session_id'].containing_oneof = _FESESSIONREQ.oneofs_by_name['id'] +_FELOCKDSREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FELOCKDSREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREQ.fields_by_name['data'].message_type = _YANGCFGDATAREQ +_FESETCONFIGREQ.fields_by_name['commit_ds_id'].enum_type = _DATASTOREID +_FESETCONFIGREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREQ.fields_by_name['src_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREQ.fields_by_name['dst_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREPLY.fields_by_name['src_ds_id'].enum_type = _DATASTOREID +_FECOMMITCONFIGREPLY.fields_by_name['dst_ds_id'].enum_type = _DATASTOREID +_FEGETREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEGETREQ.fields_by_name['data'].message_type = _YANGGETDATAREQ +_FEGETREPLY.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEGETREPLY.fields_by_name['data'].message_type = _YANGDATAREPLY +_FENOTIFYDATAREQ.fields_by_name['data'].message_type = _YANGDATA +_FEREGISTERNOTIFYREQ.fields_by_name['ds_id'].enum_type = _DATASTOREID +_FEREGISTERNOTIFYREQ.fields_by_name['data_xpath'].message_type = _YANGDATAXPATH +_FEMESSAGE.fields_by_name['register_req'].message_type = _FEREGISTERREQ +_FEMESSAGE.fields_by_name['session_req'].message_type = _FESESSIONREQ +_FEMESSAGE.fields_by_name['session_reply'].message_type = _FESESSIONREPLY +_FEMESSAGE.fields_by_name['lockds_req'].message_type = _FELOCKDSREQ +_FEMESSAGE.fields_by_name['lockds_reply'].message_type = _FELOCKDSREPLY +_FEMESSAGE.fields_by_name['setcfg_req'].message_type = _FESETCONFIGREQ +_FEMESSAGE.fields_by_name['setcfg_reply'].message_type = _FESETCONFIGREPLY +_FEMESSAGE.fields_by_name['commcfg_req'].message_type = _FECOMMITCONFIGREQ +_FEMESSAGE.fields_by_name['commcfg_reply'].message_type = _FECOMMITCONFIGREPLY +_FEMESSAGE.fields_by_name['get_req'].message_type = _FEGETREQ +_FEMESSAGE.fields_by_name['get_reply'].message_type = _FEGETREPLY +_FEMESSAGE.fields_by_name['notify_data_req'].message_type = _FENOTIFYDATAREQ +_FEMESSAGE.fields_by_name['regnotify_req'].message_type = _FEREGISTERNOTIFYREQ +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['register_req']) +_FEMESSAGE.fields_by_name['register_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['session_req']) +_FEMESSAGE.fields_by_name['session_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['session_reply']) +_FEMESSAGE.fields_by_name['session_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['lockds_req']) +_FEMESSAGE.fields_by_name['lockds_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['lockds_reply']) +_FEMESSAGE.fields_by_name['lockds_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['setcfg_req']) +_FEMESSAGE.fields_by_name['setcfg_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['setcfg_reply']) +_FEMESSAGE.fields_by_name['setcfg_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['commcfg_req']) +_FEMESSAGE.fields_by_name['commcfg_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['commcfg_reply']) +_FEMESSAGE.fields_by_name['commcfg_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['get_req']) +_FEMESSAGE.fields_by_name['get_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['get_reply']) +_FEMESSAGE.fields_by_name['get_reply'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['notify_data_req']) +_FEMESSAGE.fields_by_name['notify_data_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +_FEMESSAGE.oneofs_by_name['message'].fields.append( + _FEMESSAGE.fields_by_name['regnotify_req']) +_FEMESSAGE.fields_by_name['regnotify_req'].containing_oneof = _FEMESSAGE.oneofs_by_name['message'] +DESCRIPTOR.message_types_by_name['YangDataXPath'] = _YANGDATAXPATH +DESCRIPTOR.message_types_by_name['YangDataValue'] = _YANGDATAVALUE +DESCRIPTOR.message_types_by_name['YangData'] = _YANGDATA +DESCRIPTOR.message_types_by_name['YangCfgDataReq'] = _YANGCFGDATAREQ +DESCRIPTOR.message_types_by_name['YangGetDataReq'] = _YANGGETDATAREQ +DESCRIPTOR.message_types_by_name['BeSubscribeReq'] = _BESUBSCRIBEREQ +DESCRIPTOR.message_types_by_name['BeSubscribeReply'] = _BESUBSCRIBEREPLY +DESCRIPTOR.message_types_by_name['BeTxnReq'] = _BETXNREQ +DESCRIPTOR.message_types_by_name['BeTxnReply'] = _BETXNREPLY +DESCRIPTOR.message_types_by_name['BeCfgDataCreateReq'] = _BECFGDATACREATEREQ +DESCRIPTOR.message_types_by_name['BeCfgDataCreateReply'] = _BECFGDATACREATEREPLY +DESCRIPTOR.message_types_by_name['BeCfgDataApplyReq'] = _BECFGDATAAPPLYREQ +DESCRIPTOR.message_types_by_name['BeCfgDataApplyReply'] = _BECFGDATAAPPLYREPLY +DESCRIPTOR.message_types_by_name['YangDataReply'] = _YANGDATAREPLY +DESCRIPTOR.message_types_by_name['BeMessage'] = _BEMESSAGE +DESCRIPTOR.message_types_by_name['FeRegisterReq'] = _FEREGISTERREQ +DESCRIPTOR.message_types_by_name['FeSessionReq'] = _FESESSIONREQ +DESCRIPTOR.message_types_by_name['FeSessionReply'] = _FESESSIONREPLY +DESCRIPTOR.message_types_by_name['FeLockDsReq'] = _FELOCKDSREQ +DESCRIPTOR.message_types_by_name['FeLockDsReply'] = _FELOCKDSREPLY +DESCRIPTOR.message_types_by_name['FeSetConfigReq'] = _FESETCONFIGREQ +DESCRIPTOR.message_types_by_name['FeSetConfigReply'] = _FESETCONFIGREPLY +DESCRIPTOR.message_types_by_name['FeCommitConfigReq'] = _FECOMMITCONFIGREQ +DESCRIPTOR.message_types_by_name['FeCommitConfigReply'] = _FECOMMITCONFIGREPLY +DESCRIPTOR.message_types_by_name['FeGetReq'] = _FEGETREQ +DESCRIPTOR.message_types_by_name['FeGetReply'] = _FEGETREPLY +DESCRIPTOR.message_types_by_name['FeNotifyDataReq'] = _FENOTIFYDATAREQ +DESCRIPTOR.message_types_by_name['FeRegisterNotifyReq'] = _FEREGISTERNOTIFYREQ +DESCRIPTOR.message_types_by_name['FeMessage'] = _FEMESSAGE +DESCRIPTOR.enum_types_by_name['CfgDataReqType'] = _CFGDATAREQTYPE +DESCRIPTOR.enum_types_by_name['DatastoreId'] = _DATASTOREID +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +YangDataXPath = _reflection.GeneratedProtocolMessageType('YangDataXPath', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAXPATH, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataXPath) + }) +_sym_db.RegisterMessage(YangDataXPath) + +YangDataValue = _reflection.GeneratedProtocolMessageType('YangDataValue', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAVALUE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataValue) + }) +_sym_db.RegisterMessage(YangDataValue) + +YangData = _reflection.GeneratedProtocolMessageType('YangData', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATA, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangData) + }) +_sym_db.RegisterMessage(YangData) + +YangCfgDataReq = _reflection.GeneratedProtocolMessageType('YangCfgDataReq', (_message.Message,), { + 'DESCRIPTOR' : _YANGCFGDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangCfgDataReq) + }) +_sym_db.RegisterMessage(YangCfgDataReq) + +YangGetDataReq = _reflection.GeneratedProtocolMessageType('YangGetDataReq', (_message.Message,), { + 'DESCRIPTOR' : _YANGGETDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangGetDataReq) + }) +_sym_db.RegisterMessage(YangGetDataReq) + +BeSubscribeReq = _reflection.GeneratedProtocolMessageType('BeSubscribeReq', (_message.Message,), { + 'DESCRIPTOR' : _BESUBSCRIBEREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeSubscribeReq) + }) +_sym_db.RegisterMessage(BeSubscribeReq) + +BeSubscribeReply = _reflection.GeneratedProtocolMessageType('BeSubscribeReply', (_message.Message,), { + 'DESCRIPTOR' : _BESUBSCRIBEREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeSubscribeReply) + }) +_sym_db.RegisterMessage(BeSubscribeReply) + +BeTxnReq = _reflection.GeneratedProtocolMessageType('BeTxnReq', (_message.Message,), { + 'DESCRIPTOR' : _BETXNREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeTxnReq) + }) +_sym_db.RegisterMessage(BeTxnReq) + +BeTxnReply = _reflection.GeneratedProtocolMessageType('BeTxnReply', (_message.Message,), { + 'DESCRIPTOR' : _BETXNREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeTxnReply) + }) +_sym_db.RegisterMessage(BeTxnReply) + +BeCfgDataCreateReq = _reflection.GeneratedProtocolMessageType('BeCfgDataCreateReq', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATACREATEREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataCreateReq) + }) +_sym_db.RegisterMessage(BeCfgDataCreateReq) + +BeCfgDataCreateReply = _reflection.GeneratedProtocolMessageType('BeCfgDataCreateReply', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATACREATEREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataCreateReply) + }) +_sym_db.RegisterMessage(BeCfgDataCreateReply) + +BeCfgDataApplyReq = _reflection.GeneratedProtocolMessageType('BeCfgDataApplyReq', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATAAPPLYREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataApplyReq) + }) +_sym_db.RegisterMessage(BeCfgDataApplyReq) + +BeCfgDataApplyReply = _reflection.GeneratedProtocolMessageType('BeCfgDataApplyReply', (_message.Message,), { + 'DESCRIPTOR' : _BECFGDATAAPPLYREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeCfgDataApplyReply) + }) +_sym_db.RegisterMessage(BeCfgDataApplyReply) + +YangDataReply = _reflection.GeneratedProtocolMessageType('YangDataReply', (_message.Message,), { + 'DESCRIPTOR' : _YANGDATAREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.YangDataReply) + }) +_sym_db.RegisterMessage(YangDataReply) + +BeMessage = _reflection.GeneratedProtocolMessageType('BeMessage', (_message.Message,), { + 'DESCRIPTOR' : _BEMESSAGE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.BeMessage) + }) +_sym_db.RegisterMessage(BeMessage) + +FeRegisterReq = _reflection.GeneratedProtocolMessageType('FeRegisterReq', (_message.Message,), { + 'DESCRIPTOR' : _FEREGISTERREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeRegisterReq) + }) +_sym_db.RegisterMessage(FeRegisterReq) + +FeSessionReq = _reflection.GeneratedProtocolMessageType('FeSessionReq', (_message.Message,), { + 'DESCRIPTOR' : _FESESSIONREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSessionReq) + }) +_sym_db.RegisterMessage(FeSessionReq) + +FeSessionReply = _reflection.GeneratedProtocolMessageType('FeSessionReply', (_message.Message,), { + 'DESCRIPTOR' : _FESESSIONREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSessionReply) + }) +_sym_db.RegisterMessage(FeSessionReply) + +FeLockDsReq = _reflection.GeneratedProtocolMessageType('FeLockDsReq', (_message.Message,), { + 'DESCRIPTOR' : _FELOCKDSREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeLockDsReq) + }) +_sym_db.RegisterMessage(FeLockDsReq) + +FeLockDsReply = _reflection.GeneratedProtocolMessageType('FeLockDsReply', (_message.Message,), { + 'DESCRIPTOR' : _FELOCKDSREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeLockDsReply) + }) +_sym_db.RegisterMessage(FeLockDsReply) + +FeSetConfigReq = _reflection.GeneratedProtocolMessageType('FeSetConfigReq', (_message.Message,), { + 'DESCRIPTOR' : _FESETCONFIGREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSetConfigReq) + }) +_sym_db.RegisterMessage(FeSetConfigReq) + +FeSetConfigReply = _reflection.GeneratedProtocolMessageType('FeSetConfigReply', (_message.Message,), { + 'DESCRIPTOR' : _FESETCONFIGREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeSetConfigReply) + }) +_sym_db.RegisterMessage(FeSetConfigReply) + +FeCommitConfigReq = _reflection.GeneratedProtocolMessageType('FeCommitConfigReq', (_message.Message,), { + 'DESCRIPTOR' : _FECOMMITCONFIGREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeCommitConfigReq) + }) +_sym_db.RegisterMessage(FeCommitConfigReq) + +FeCommitConfigReply = _reflection.GeneratedProtocolMessageType('FeCommitConfigReply', (_message.Message,), { + 'DESCRIPTOR' : _FECOMMITCONFIGREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeCommitConfigReply) + }) +_sym_db.RegisterMessage(FeCommitConfigReply) + +FeGetReq = _reflection.GeneratedProtocolMessageType('FeGetReq', (_message.Message,), { + 'DESCRIPTOR' : _FEGETREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeGetReq) + }) +_sym_db.RegisterMessage(FeGetReq) + +FeGetReply = _reflection.GeneratedProtocolMessageType('FeGetReply', (_message.Message,), { + 'DESCRIPTOR' : _FEGETREPLY, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeGetReply) + }) +_sym_db.RegisterMessage(FeGetReply) + +FeNotifyDataReq = _reflection.GeneratedProtocolMessageType('FeNotifyDataReq', (_message.Message,), { + 'DESCRIPTOR' : _FENOTIFYDATAREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeNotifyDataReq) + }) +_sym_db.RegisterMessage(FeNotifyDataReq) + +FeRegisterNotifyReq = _reflection.GeneratedProtocolMessageType('FeRegisterNotifyReq', (_message.Message,), { + 'DESCRIPTOR' : _FEREGISTERNOTIFYREQ, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeRegisterNotifyReq) + }) +_sym_db.RegisterMessage(FeRegisterNotifyReq) + +FeMessage = _reflection.GeneratedProtocolMessageType('FeMessage', (_message.Message,), { + 'DESCRIPTOR' : _FEMESSAGE, + '__module__' : 'mgmt_pb2' + # @@protoc_insertion_point(class_scope:mgmtd.FeMessage) + }) +_sym_db.RegisterMessage(FeMessage) + + +# @@protoc_insertion_point(module_scope) diff --git a/tests/topotests/mgmt_fe_client/oper.py b/tests/topotests/mgmt_fe_client/oper.py new file mode 120000 index 0000000..9244392 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/oper.py @@ -0,0 +1 @@ +../mgmt_oper/oper.py \ No newline at end of file diff --git a/tests/topotests/mgmt_fe_client/r1/frr.conf b/tests/topotests/mgmt_fe_client/r1/frr.conf new file mode 100644 index 0000000..cf8ba16 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/r1/frr.conf @@ -0,0 +1,23 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 +exit +ip route 11.11.11.11/32 1.1.1.2 +!ip route 13.13.13.13/32 3.3.3.2 vrf red \ No newline at end of file diff --git a/tests/topotests/mgmt_fe_client/test_client.py b/tests/topotests/mgmt_fe_client/test_client.py new file mode 100644 index 0000000..b5a74c6 --- /dev/null +++ b/tests/topotests/mgmt_fe_client/test_client.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") diff --git a/tests/topotests/mgmt_notif/oper.py b/tests/topotests/mgmt_notif/oper.py new file mode 120000 index 0000000..9244392 --- /dev/null +++ b/tests/topotests/mgmt_notif/oper.py @@ -0,0 +1 @@ +../mgmt_oper/oper.py \ No newline at end of file diff --git a/tests/topotests/mgmt_notif/r1/frr.conf b/tests/topotests/mgmt_notif/r1/frr.conf new file mode 100644 index 0000000..47e7395 --- /dev/null +++ b/tests/topotests/mgmt_notif/r1/frr.conf @@ -0,0 +1,27 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +ip route 11.11.11.11/32 lo + +interface r1-eth0 + ip address 1.1.1.1/24 + ip rip authentication string foo + ip rip authentication mode text +exit + +router rip + network 1.1.1.0/24 + timers basic 5 15 10 + redistribute static +exit diff --git a/tests/topotests/mgmt_notif/r2/frr.conf b/tests/topotests/mgmt_notif/r2/frr.conf new file mode 100644 index 0000000..cd05201 --- /dev/null +++ b/tests/topotests/mgmt_notif/r2/frr.conf @@ -0,0 +1,27 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +ip route 22.22.22.22/32 lo + +interface r2-eth0 + ip address 1.1.1.2/24 + ip rip authentication string bar + ip rip authentication mode text +exit + +router rip + network 1.1.1.0/24 + timers basic 5 15 10 + redistribute static +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_notif/test_notif.py b/tests/topotests/mgmt_notif/test_notif.py new file mode 100644 index 0000000..c85e7ba --- /dev/null +++ b/tests/topotests/mgmt_notif/test_notif.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# January 23 2024, Christian Hopps +# +# Copyright (c) 2024, LabN Consulting, L.L.C. +# + +""" +Test YANG Notifications +""" +import json +import logging +import os + +import pytest +from lib.topogen import Topogen +from lib.topotest import json_cmp +from oper import check_kernel_32 + +pytestmark = [pytest.mark.ripd, pytest.mark.staticd, pytest.mark.mgmtd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2"), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_frontend_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + fe_client_path = CWD + "/../lib/fe_client.py" + rc, _, _ = r1.cmd_status(fe_client_path + " --help") + + if rc: + pytest.skip("No protoc or present cannot run test") + + # The first notifications is a frr-ripd:authentication-type-failure + # So we filter to avoid that, all the rest are frr-ripd:authentication-failure + # making our test deterministic + output = r1.cmd_raises( + fe_client_path + " --listen frr-ripd:authentication-failure" + ) + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + + output = r1.cmd_raises(fe_client_path + " --listen") + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + + +def test_backend_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + be_client_path = "/usr/lib/frr/mgmtd_testc" + rc, _, _ = r1.cmd_status(be_client_path + " --help") + + if rc: + pytest.skip("No mgmtd_testc") + + output = r1.cmd_raises( + be_client_path + " --timeout 20 --log file:mgmt_testc.log --listen /frr-ripd" + ) + + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json new file mode 100644 index 0000000..435d733 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-default.json @@ -0,0 +1,576 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json new file mode 100644 index 0000000..1a1f648 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json @@ -0,0 +1,1145 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json new file mode 100644 index 0000000..cfabd49 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json @@ -0,0 +1,576 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra-ribs.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-lib.json b/tests/topotests/mgmt_oper/oper-results/result-lib.json new file mode 100644 index 0000000..1a1f648 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-lib.json @@ -0,0 +1,1145 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "13.13.13.13/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "3.3.3.2", + "interface": "r1-eth2", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "14.14.14.14/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "4.4.4.2", + "interface": "r1-eth3", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "4.4.4.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2003:333::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2003:333::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth2", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2004:4444::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth3", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json new file mode 100644 index 0000000..956d3a8 --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-ipv4-unicast.json @@ -0,0 +1,225 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json new file mode 100644 index 0000000..2e2b8ec --- /dev/null +++ b/tests/topotests/mgmt_oper/oper-results/result-ribs-rib-nokeys.json @@ -0,0 +1,572 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.0.0.0/8", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "rubout", + "gateway": "", + "interface": " ", + "bh-type": "null", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + }, + { + "prefix": "12.12.12.12/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "2.2.2.2", + "interface": "r1-eth1", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2.2.2.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + }, + { + "prefix": "2001:1111::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2001:1111::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::/64", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "2002:2222::1/128", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/oper.py b/tests/topotests/mgmt_oper/oper.py new file mode 100644 index 0000000..0f6c3cd --- /dev/null +++ b/tests/topotests/mgmt_oper/oper.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# October 29 2023, Christian Hopps +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# + +import datetime +import ipaddress +import json +import logging +import math +import os +import pprint +import re + +from lib.common_config import retry, step +from lib.topolog import logger +from lib.topotest import json_cmp as tt_json_cmp + +try: + from deepdiff import DeepDiff as dd_json_cmp +except ImportError: + dd_json_cmp = None + + +def json_cmp(got, expect, exact_match): + if dd_json_cmp: + if exact_match: + deep_diff = dd_json_cmp(expect, got) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + else: + json_diff = dd_json_cmp(expect, got, ignore_order=True) + # Convert DeepDiff completely into dicts or lists at all levels + # json_diff = json.loads(deep_diff.to_json()) + # Remove new fields in json object from diff + if json_diff.get("dictionary_item_added") is not None: + del json_diff["dictionary_item_added"] + # Remove new json objects in json array from diff + if (new_items := json_diff.get("iterable_item_added")) is not None: + new_item_paths = list(new_items.keys()) + for path in new_item_paths: + if type(new_items[path]) is dict: + del new_items[path] + if len(new_items) == 0: + del json_diff["iterable_item_added"] + if not json_diff: + json_diff = None + else: + json_diff = tt_json_cmp(got, expect, exact_match) + json_diff = str(json_diff) + return json_diff + + +def enable_debug(router): + router.vtysh_cmd("debug northbound callbacks configuration") + + +def disable_debug(router): + router.vtysh_cmd("no debug northbound callbacks configuration") + + +def do_oper_test(tgen, query_results): + r1 = tgen.gears["r1"].net + + qcmd = ( + r"vtysh -c 'show mgmt get-data {} {}' " + r"""| sed -e 's/"phy-address": ".*"/"phy-address": "rubout"/'""" + r"""| sed -e 's/"uptime": ".*"/"uptime": "rubout"/'""" + r"""| sed -e 's/"vrf": "[0-9]*"/"vrf": "rubout"/'""" + r"""| sed -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/'""" + r"""| sed -e 's/"id": [0-9][0-9]*/"id": "rubout"/'""" + ) + + doreset = True + dd_json_cmp = None + for qr in query_results: + step(f"Perform query '{qr[0]}'", reset=doreset) + if doreset: + doreset = False + expected = open(qr[1], encoding="ascii").read() + output = r1.cmd_nostatus(qcmd.format(qr[0], qr[2] if len(qr) > 2 else "")) + + try: + ojson = json.loads(output) + except json.decoder.JSONDecodeError as error: + logging.error("Error decoding json: %s\noutput:\n%s", error, output) + raise + + try: + ejson = json.loads(expected) + except json.decoder.JSONDecodeError as error: + logging.error( + "Error decoding json exp result: %s\noutput:\n%s", error, expected + ) + raise + + if dd_json_cmp: + cmpout = json_cmp(ojson, ejson, exact_match=True) + if cmpout: + logging.warning( + "-------DIFF---------\n%s\n---------DIFF----------", + pprint.pformat(cmpout), + ) + else: + cmpout = tt_json_cmp(ojson, ejson, exact=True) + if cmpout: + logging.warning( + "-------EXPECT--------\n%s\n------END-EXPECT------", + json.dumps(ejson, indent=4), + ) + logging.warning( + "--------GOT----------\n%s\n-------END-GOT--------", + json.dumps(ojson, indent=4), + ) + + assert cmpout is None + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +@retry(retry_timeout=30, initial_wait=0.1) +def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): + network = ipaddress.ip_network(super_prefix) + vrfstr = f" vrf {vrf}" if vrf else "" + if network.version == 6: + kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}") + else: + kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}") + + # logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) + + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if not add: + assert str(net) not in kernel + continue + + if is_blackhole: + route = f"blackhole {str(net)} proto (static|196) metric 20" + else: + route = ( + f"{str(net)}(?: nhid [0-9]+)? {matchvia} " + "proto (static|196) metric 20" + ) + assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" + + +def addrgen(a, count, step=1): + for _ in range(0, count, step): + yield a + a += step + + +@retry(retry_timeout=30, initial_wait=0.1) +def check_kernel_32(r1, start_addr, count, vrf, step=1): + start = ipaddress.ip_address(start_addr) + vrfstr = f" vrf {vrf}" if vrf else "" + if start.version == 6: + kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}") + else: + kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}") + + nentries = len(re.findall("\n", kernel)) + logging.info("checking kernel routing table%s: (%s entries)", vrfstr, nentries) + + for addr in addrgen(start, count, step): + assert str(addr) in kernel, f"Failed to find '{addr}' in {nentries} entries" + + +def do_config( + r1, + count, + add=True, + do_ipv6=False, + via=None, + vrf=None, + use_cli=False, +): + optype = "adding" if add else "removing" + iptype = "IPv6" if do_ipv6 else "IPv4" + + # + # Set the route details + # + + if vrf: + super_prefix = "2111::/48" if do_ipv6 else "111.0.0.0/8" + else: + super_prefix = "2055::/48" if do_ipv6 else "55.0.0.0/8" + + matchvia = "" + if via == "blackhole": + pass + elif via: + matchvia = f"dev {via}" + else: + if vrf: + via = "2102::2" if do_ipv6 else "3.3.3.2" + matchvia = f"via {via} dev r1-eth1" + else: + via = "2101::2" if do_ipv6 else "1.1.1.2" + matchvia = f"via {via} dev r1-eth0" + + vrfdbg = " in vrf {}".format(vrf) if vrf else "" + logger.debug("{} {} static {} routes{}".format(optype, count, iptype, vrfdbg)) + + # + # Generate config file in a retrievable place + # + + config_file = os.path.join( + r1.logdir, r1.name, "{}-routes-{}.conf".format(iptype.lower(), optype) + ) + with open(config_file, "w") as f: + if use_cli: + f.write("configure terminal\n") + if vrf: + f.write("vrf {}\n".format(vrf)) + + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if add: + f.write("ip route {} {}\n".format(net, via)) + else: + f.write("no ip route {} {}\n".format(net, via)) + + # + # Load config file. + # + + if use_cli: + load_command = 'vtysh < "{}"'.format(config_file) + else: + load_command = 'vtysh -f "{}"'.format(config_file) + tstamp = datetime.datetime.now() + output = r1.cmd_raises(load_command) + delta = (datetime.datetime.now() - tstamp).total_seconds() + + # + # Verify the results are in the kernel + # + check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) + + optyped = "added" if add else "removed" + logger.debug( + "{} {} {} static routes under {}{} in {}s".format( + optyped, count, iptype.lower(), super_prefix, vrfdbg, delta + ) + ) diff --git a/tests/topotests/mgmt_oper/r1/frr-scale.conf b/tests/topotests/mgmt_oper/r1/frr-scale.conf new file mode 100644 index 0000000..237d013 --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr-scale.conf @@ -0,0 +1,25 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +! debug northbound libyang +! debug northbound callbacks + +debug northbound notifications +debug northbound events + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 +exit + +ip route 11.11.11.11/32 1.1.1.2 +ip route 13.13.13.13/32 3.3.3.2 vrf red \ No newline at end of file diff --git a/tests/topotests/mgmt_oper/r1/frr-simple.conf b/tests/topotests/mgmt_oper/r1/frr-simple.conf new file mode 100644 index 0000000..73df6b9 --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr-simple.conf @@ -0,0 +1,26 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 + description r1-eth0-desc + evpn mh es-df-pref 32767 +exit + +interface r1-eth1 vrf red + ip address 3.3.3.1/24 + description r1-eth1-desc +exit +ip route 11.11.11.11/32 1.1.1.2 +!ip route 13.13.13.13/32 3.3.3.2 vrf red diff --git a/tests/topotests/mgmt_oper/r1/frr.conf b/tests/topotests/mgmt_oper/r1/frr.conf new file mode 100644 index 0000000..72a67bf --- /dev/null +++ b/tests/topotests/mgmt_oper/r1/frr.conf @@ -0,0 +1,41 @@ +log timestamp precision 6 +log file frr.log + +no debug memstats-at-exit + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks + +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + +interface r1-eth0 + ip address 1.1.1.1/24 + ipv6 address 2001:1111::1/64 +exit + +interface r1-eth1 + ip address 2.2.2.1/24 + ipv6 address 2002:2222::1/64 +exit + +interface r1-eth2 vrf red + ip address 3.3.3.1/24 + ipv6 address 2003:333::1/64 +exit + +interface r1-eth3 vrf red + ip address 4.4.4.1/24 + ipv6 address 2004:4444::1/64 +exit + +ip route 11.0.0.0/8 Null0 +ip route 11.11.11.11/32 1.1.1.2 +ip route 12.12.12.12/32 2.2.2.2 + +ip route 13.0.0.0/8 Null0 vrf red +ip route 13.13.13.13/32 3.3.3.2 vrf red +ip route 14.14.14.14/32 4.4.4.2 vrf red \ No newline at end of file diff --git a/tests/topotests/mgmt_oper/simple-results/result-empty.json b/tests/topotests/mgmt_oper/simple-results/result-empty.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-empty.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-description.json b/tests/topotests/mgmt_oper/simple-results/result-intf-description.json new file mode 100644 index 0000000..8f8092e --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-description.json @@ -0,0 +1,14 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "description": "r1-eth0-desc" + }, + { + "name": "r1-eth1", + "description": "r1-eth1-desc" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json new file mode 100644 index 0000000..e00f23c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-description-exact.json @@ -0,0 +1,3 @@ +{ + "frr-interface:description": "r1-eth0-desc" +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json new file mode 100644 index 0000000..f04e3a5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-exact.json @@ -0,0 +1,22 @@ +{ + "frr-interface:interface": [ + { + "name": "r1-eth0", + "vrf": "default", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json new file mode 100644 index 0000000..3988204 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json @@ -0,0 +1,9 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json new file mode 100644 index 0000000..e48002e --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-only-config.json @@ -0,0 +1,21 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "description": "r1-eth0-desc", + "frr-zebra:zebra": { + "ipv4-addrs": [ + { + "ip": "1.1.1.1", + "prefix-length": 24 + } + ], + "evpn-mh": { + "df-preference": 32767 + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json new file mode 100644 index 0000000..3a6eeb8 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json @@ -0,0 +1,10 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "vrf": "default" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json new file mode 100644 index 0000000..caee164 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all-tag.json @@ -0,0 +1,13 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767, + "bypass": false, + "@bypass": { + "ietf-netconf-with-defaults:default": true + }, + "uplink": false, + "@uplink": { + "ietf-netconf-with-defaults:default": true + } + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json new file mode 100644 index 0000000..07ba53b --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-all.json @@ -0,0 +1,7 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767, + "bypass": false, + "uplink": false + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json new file mode 100644 index 0000000..1779d1c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-explicit.json @@ -0,0 +1,5 @@ +{ + "frr-zebra:evpn-mh": { + "df-preference": 32767 + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json new file mode 100644 index 0000000..efd7e8c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-wd-trim.json @@ -0,0 +1,3 @@ +{ + "frr-zebra:evpn-mh": {} +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json new file mode 100644 index 0000000..84ad82c --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-with-config.json @@ -0,0 +1,34 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "vrf": "default", + "description": "r1-eth0-desc", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + }, + "frr-zebra:zebra": { + "ipv4-addrs": [ + { + "ip": "1.1.1.1", + "prefix-length": 24 + } + ], + "evpn-mh": { + "df-preference": 32767 + }, + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json new file mode 100644 index 0000000..9d8ea75 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json @@ -0,0 +1,21 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "lo" + }, + { + "name": "r1-eth0" + }, + { + "name": "lo-red" + }, + { + "name": "r1-eth1" + }, + { + "name": "red" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json b/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json new file mode 100644 index 0000000..6035971 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-state-mtu.json @@ -0,0 +1,12 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "state": { + "mtu": 1500 + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-state.json b/tests/topotests/mgmt_oper/simple-results/result-intf-state.json new file mode 100644 index 0000000..981df02 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-state.json @@ -0,0 +1,17 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "state": { + "if-index": "rubout", + "mtu": 1500, + "mtu6": 1500, + "speed": 10000, + "metric": 0, + "phy-address": "rubout" + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json new file mode 100644 index 0000000..cea4bf5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-default.json @@ -0,0 +1,193 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json new file mode 100644 index 0000000..75414ca --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-nokey.json @@ -0,0 +1,350 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json new file mode 100644 index 0000000..0538231 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json @@ -0,0 +1,164 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra-ribs.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib-vrf-zebra.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-lib.json b/tests/topotests/mgmt_oper/simple-results/result-lib.json new file mode 100644 index 0000000..75414ca --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-lib.json @@ -0,0 +1,350 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + }, + { + "name": "red", + "state": { + "id": "rubout", + "active": true + }, + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 10, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "3.3.3.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "3.3.3.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth1", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 10, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json new file mode 100644 index 0000000..7ce60c3 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-ipv4-unicast.json @@ -0,0 +1,110 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json new file mode 100644 index 0000000..4f40820 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-nokeys.json @@ -0,0 +1,189 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv4-multicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "internal-flags": 8, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-unicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + }, + { + "afi-safi-name": "frr-routing:ipv6-multicast", + "table-id": 254, + "route": [ + { + "prefix": "::/0" + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json new file mode 100644 index 0000000..7ce60c3 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-nokey.json @@ -0,0 +1,110 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "0.0.0.0/0" + }, + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "1.1.1.1/32", + "route-entry": [ + { + "protocol": "local", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + }, + { + "prefix": "11.11.11.11/32", + "route-entry": [ + { + "protocol": "static", + "distance": 1, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 73, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ip4-ifindex", + "vrf": "rubout", + "gateway": "1.1.1.2", + "interface": "r1-eth0", + "active": [null], + "fib": [null], + "weight": 1 + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json new file mode 100644 index 0000000..833d418 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-ribs-rib-route-prefix.json @@ -0,0 +1,50 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "distance": 0, + "metric": 0, + "selected": [null], + "installed": [null], + "internal-flags": 8, + "internal-status": 16, + "uptime": "rubout", + "nexthop-group": { + "id": "rubout", + "nexthop": [ + { + "nh-type": "ifindex", + "vrf": "rubout", + "gateway": "", + "interface": "r1-eth0", + "active": [null], + "fib": [null] + } + ] + } + } + ] + } + ] + } + ] + } + } + } + ] + } +} + diff --git a/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json b/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json new file mode 100644 index 0000000..b3a7df5 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-singleton-metric.json @@ -0,0 +1,30 @@ +{ + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "frr-zebra:zebra": { + "ribs": { + "rib": [ + { + "afi-safi-name": "frr-routing:ipv4-unicast", + "table-id": 254, + "route": [ + { + "prefix": "1.1.1.0/24", + "route-entry": [ + { + "protocol": "connected", + "metric": 0 + } + ] + } + ] + } + ] + } + } + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/test_oper.py b/tests/topotests/mgmt_oper/test_oper.py new file mode 100644 index 0000000..6971eaf --- /dev/null +++ b/tests/topotests/mgmt_oper/test_oper.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +""" +Test static route functionality +""" + +import ipaddress +import math +import time + +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32, do_oper_test + +try: + from deepdiff import DeepDiff as dd_json_cmp +except ImportError: + dd_json_cmp = None + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",), "s3": ("r1",), "s4": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth2", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth3", "red") + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def test_oper(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + ("/frr-vrf:lib", "oper-results/result-lib.json"), + ("/frr-vrf:lib/vrf", "oper-results/result-lib-vrf-nokey.json"), + ( + '/frr-vrf:lib/vrf[name="default"]', + "oper-results/result-lib-vrf-default.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra', + "oper-results/result-lib-vrf-zebra.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs', + "oper-results/result-lib-vrf-zebra-ribs.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib', + "oper-results/result-ribs-rib-nokeys.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]', + "oper-results/result-ribs-rib-ipv4-unicast.json", + ), + ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + check_kernel_32(r1, "12.12.12.12", 1, "") + check_kernel_32(r1, "13.13.13.13", 1, "red") + check_kernel_32(r1, "14.14.14.14", 1, "red") + time.sleep(2) + do_oper_test(tgen, query_results) + + +to_gen_new_results = """ +scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper +resdir=${scriptdir}/oper-results +vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json + +for f in ${resdir}/result-*; do + sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f + sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f + sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f +done +""" # noqa: 501 +# should not differ +# diff result-lib.json result-lib-vrf-nokey.json +# diff result-lib-vrf-zebra.json result-lib-vrf-zebra-ribs.json diff --git a/tests/topotests/mgmt_oper/test_querying.py b/tests/topotests/mgmt_oper/test_querying.py new file mode 100644 index 0000000..086a87b --- /dev/null +++ b/tests/topotests/mgmt_oper/test_querying.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# noqa: E501 +# +""" +Test various query types +""" +import json +import logging + +import pytest +from lib.common_config import step +from lib.topogen import Topogen +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-simple.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + """This test is useful for doing manual testing""" + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + # Non-key specific query with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]/vrf', + # Non-key specific query with child value filtering selector + '/frr-interface:lib/interface[vrf="red"]/vrf', + '/frr-interface:lib/interface[./vrf="red"]/vrf', + # Container query with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]/state', + # Multi list elemenet with function filtering selector + '/frr-interface:lib/interface[contains(name,"eth")]', + # + # Specific list entry after non-specific lists + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route/route-entry[protocol="connected"]', + # crashes: All specific until the end, then walk + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]', + # Does nothing: Root level query + "//metric", + # specific leaf after non-specific lists + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + "route/route-entry/metric", + # All specific until the end generic. + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry', + # All specific until the penultimate generic with a specific leaf child. + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry/metric', + # All generic until the end (middle) specific with unspecified + # children below to walk. + '/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route[prefix="1.1.1.0/24"]', + # All generic until the end which is a specific leaf. + "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/metric", + ] + # query_results = [ + # '/frr-interface:lib/frr-interface:interface/frr-zebra:zebra/ip-addrs[frr-rt:address-family="frr-rt:ipv4"][prefix="1.1.1.1/24"]' + # ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + + step("Oper test start", reset=True) + + for qr in query_results: + step(f"Perform query '{qr}'") + try: + output = r1.cmd_nostatus(f"vtysh -c 'show mgmt get-data {qr}'") + except Exception as error: + logging.error("Error sending query: %s: %s", qr, error) + continue + + try: + ojson = json.loads(output) + logging.info("'%s': generates:\n%s", qr, ojson) + except json.decoder.JSONDecodeError as error: + logging.error("Error decoding json: %s\noutput:\n%s", error, output) diff --git a/tests/topotests/mgmt_oper/test_scale.py b/tests/topotests/mgmt_oper/test_scale.py new file mode 100644 index 0000000..41c2b8c --- /dev/null +++ b/tests/topotests/mgmt_oper/test_scale.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# noqa: E501 +# +""" +Test static route functionality +""" +import re +import time + +import pytest +from lib.common_config import step +from lib.topogen import Topogen, TopoRouter +from oper import check_kernel_32 + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-scale.conf") + router.load_config(TopoRouter.RD_SHARP, "") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + time.sleep(2) + count = 20 * 1000 + + vrf = None # "red" + check_kernel_32(r1, "11.11.11.11", 1, vrf) + + step("Found 11.11.11.11 in kernel adding sharpd routes") + r1.cmd_raises(f"vtysh -c 'sharp install routes 20.0.0.0 nexthop 1.1.1.2 {count}'") + check_kernel_32(r1, "20.0.0.0", count, vrf, 1000) + + step(f"All {count} routes installed in kernel, continuing") + # output = r1.cmd_raises("vtysh -c 'show mgmt get-data /frr-vrf:lib'") + # step(f"Got output: {output[0:1024]}") + + query = '/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route[contains(prefix,"20.0.0.12")]/prefix' + output = r1.cmd_raises(f"vtysh -c 'show mgmt get-data {query}'") + matches = re.findall(r'"prefix":', output) + # 20.0.0.12 + 20.0.0.12{0,1,2,3,4,5,6,7,8,9} + assert len(matches) == 11 diff --git a/tests/topotests/mgmt_oper/test_simple.py b/tests/topotests/mgmt_oper/test_simple.py new file mode 100644 index 0000000..3b115f6 --- /dev/null +++ b/tests/topotests/mgmt_oper/test_simple.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# noqa: E501 +# +""" +Test static route functionality +""" +import pytest +from lib.topogen import Topogen +from oper import check_kernel_32, do_oper_test + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + router.load_frr_config("frr-simple.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_oper_simple(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + query_results = [ + ( + # Non-key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/vrf', + "simple-results/result-intf-eth0-vrf.json", + ), + # Test machines will have different sets of interfaces so the test results will + # vary and need to be generated dynamically before this test is re-enabled + # ( + # # Key query on generic list + # "/frr-interface:lib/interface/name", + # "simple-results/result-intf-name.json", + # ), + ( + # Key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/name', + "simple-results/result-intf-eth0-name.json", + ), + ("/frr-vrf:lib", "simple-results/result-lib.json"), + ("/frr-vrf:lib/vrf", "simple-results/result-lib-vrf-nokey.json"), + ( + '/frr-vrf:lib/vrf[name="default"]', + "simple-results/result-lib-vrf-default.json", + ), + ('/frr-vrf:lib/vrf[name="red"]', "simple-results/result-lib-vrf-red.json"), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra', + "simple-results/result-lib-vrf-zebra.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs', + "simple-results/result-lib-vrf-zebra-ribs.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib', + "simple-results/result-ribs-rib-nokeys.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]', + "simple-results/result-ribs-rib-ipv4-unicast.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route', + "simple-results/result-ribs-rib-route-nokey.json", + ), + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]', + "simple-results/result-ribs-rib-route-prefix.json", + ), + # Missing entry + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.0.0/24"]', + "simple-results/result-empty.json", + ), + # Leaf reference + ( + '/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/' + 'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/' + 'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]/metric', + "simple-results/result-singleton-metric.json", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-with-config.json", + "with-config", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-only-config.json", + "only-config", + ), + ( + "/frr-interface:lib/interface/description", + "simple-results/result-intf-description.json", + "with-config", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]', + "simple-results/result-intf-eth0-exact.json", + "exact", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/description', + "simple-results/result-intf-eth0-description-exact.json", + "with-config exact", + ), + # Interface state + ( + '/frr-interface:lib/interface[name="r1-eth0"]/state', + "simple-results/result-intf-state.json", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/state/mtu', + "simple-results/result-intf-state-mtu.json", + ), + # with-defaults + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-explicit.json", + "with-config exact", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-trim.json", + "with-config exact with-defaults trim", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-all.json", + "with-config exact with-defaults all", + ), + ( + '/frr-interface:lib/interface[name="r1-eth0"]/frr-zebra:zebra/evpn-mh', + "simple-results/result-intf-eth0-wd-all-tag.json", + "with-config exact with-defaults all-tag", + ), + ] + + r1 = tgen.gears["r1"].net + check_kernel_32(r1, "11.11.11.11", 1, "") + do_oper_test(tgen, query_results) + + +to_gen_new_results = """ +scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper +resdir=${scriptdir}/simple-results +vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json +vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json + +vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state' > ${resdir}/result-intf-state.json +vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state/mtu' > ${resdir}/result-intf-state-mtu.json + +for f in ${resdir}/result-*; do + sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f + sed -i -e 's/"phy-address": ".*"/"phy-address": "rubout"/' $f + sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f + sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f +done +""" # noqa: 501 + +# Example commands: +# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.0.0/24"] # noqa: E501 +# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.1.0/24"] # noqa: E501 diff --git a/tests/topotests/mgmt_startup/test_bigconf.py b/tests/topotests/mgmt_startup/test_bigconf.py index 4f46c8f..1c08fe6 100644 --- a/tests/topotests/mgmt_startup/test_bigconf.py +++ b/tests/topotests/mgmt_startup/test_bigconf.py @@ -21,8 +21,7 @@ from util import check_kernel, check_vtysh_up, write_big_route_conf CWD = os.path.dirname(os.path.realpath(__file__)) -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] track = Timeout(0) diff --git a/tests/topotests/mgmt_startup/test_cfgfile_var.py b/tests/topotests/mgmt_startup/test_cfgfile_var.py index 6a54f71..8203ae5 100644 --- a/tests/topotests/mgmt_startup/test_cfgfile_var.py +++ b/tests/topotests/mgmt_startup/test_cfgfile_var.py @@ -31,8 +31,7 @@ from lib.common_config import step from lib.topogen import Topogen, TopoRouter from util import check_kernel -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_startup/test_late_bigconf.py b/tests/topotests/mgmt_startup/test_late_bigconf.py index 0b5bf38..5bf3457 100644 --- a/tests/topotests/mgmt_startup/test_late_bigconf.py +++ b/tests/topotests/mgmt_startup/test_late_bigconf.py @@ -22,8 +22,7 @@ from util import check_kernel, check_vtysh_up, write_big_route_conf CWD = os.path.dirname(os.path.realpath(__file__)) -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] track = Timeout(0) ROUTE_COUNT = 2500 diff --git a/tests/topotests/mgmt_startup/test_late_uniconf.py b/tests/topotests/mgmt_startup/test_late_uniconf.py index d4e7e07..380c2eb 100644 --- a/tests/topotests/mgmt_startup/test_late_uniconf.py +++ b/tests/topotests/mgmt_startup/test_late_uniconf.py @@ -14,8 +14,7 @@ import pytest from lib.topogen import Topogen from util import _test_staticd_late_start -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_startup/test_latestart.py b/tests/topotests/mgmt_startup/test_latestart.py index 1c97b9d..c0c0121 100644 --- a/tests/topotests/mgmt_startup/test_latestart.py +++ b/tests/topotests/mgmt_startup/test_latestart.py @@ -14,8 +14,7 @@ import pytest from lib.topogen import Topogen, TopoRouter from util import _test_staticd_late_start -# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] -pytestmark = [pytest.mark.staticd] +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] @pytest.fixture(scope="module") diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 921b4e6..bf4e95b 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -68,7 +68,7 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib from lib.topojson import build_config_from_json -pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd, pytest.mark.mgmtd] # Global variables ADDR_TYPES = check_address_types() diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py index 06ca4de..72b5df5 100644 --- a/tests/topotests/munet/base.py +++ b/tests/topotests/munet/base.py @@ -21,7 +21,6 @@ import subprocess import sys import tempfile import time as time_mod - from collections import defaultdict from pathlib import Path from typing import Union @@ -29,10 +28,8 @@ from typing import Union from . import config as munet_config from . import linux - try: import pexpect - from pexpect.fdpexpect import fdspawn from pexpect.popen_spawn import PopenSpawn @@ -513,9 +510,8 @@ class Commander: # pylint: disable=R0904 self.logger.debug('%s("%s") [no precmd]', method, shlex.join(cmd_list)) else: self.logger.debug( - '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + '%s: %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', self, - "XXX" if method == "_spawn" else "", method, cmd_list, pre_cmd_list if not skip_pre_cmd else "", @@ -566,7 +562,7 @@ class Commander: # pylint: disable=R0904 def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs): logging.debug( - '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', + '%s: _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', self, cmd, skip_pre_cmd, @@ -579,7 +575,7 @@ class Commander: # pylint: disable=R0904 ) self.logger.debug( - '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + '%s: %s("%s", use_pty %s echo %s defaults: %s)', self, "PopenSpawn" if not use_pty else "pexpect.spawn", actual_cmd, @@ -865,14 +861,18 @@ class Commander: # pylint: disable=R0904 else: o, e = await p.communicate() self.logger.debug( - "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + "%s: [cleanup_proc] proc already exited status: %s", + self, + proc_error(p, o, e), ) return None if pid is None: pid = p.pid - self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid) + self.logger.debug( + "%s: [cleanup_proc] terminate process: %s (pid %s)", self, proc_str(p), pid + ) try: # This will SIGHUP and wait a while then SIGKILL and return immediately await self.cleanup_pid(p.pid, pid) @@ -885,14 +885,19 @@ class Commander: # pylint: disable=R0904 else: o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs) self.logger.debug( - "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e) + "%s: [cleanup_proc] exited after kill, status: %s", + self, + proc_error(p, o, e), ) except (asyncio.TimeoutError, subprocess.TimeoutExpired): - self.logger.warning("%s: SIGKILL timeout", self) + self.logger.warning("%s: [cleanup_proc] SIGKILL timeout", self) return p except Exception as error: self.logger.warning( - "%s: kill unexpected exception: %s", self, error, exc_info=True + "%s: [cleanup_proc] kill unexpected exception: %s", + self, + error, + exc_info=True, ) return p return None @@ -1206,7 +1211,7 @@ class Commander: # pylint: disable=R0904 # XXX need to test ssh in Xterm sudo_path = get_exec_path_host(["sudo"]) # This first test case seems same as last but using list instead of string? - if self.is_vm and self.use_ssh: # pylint: disable=E1101 + if self.is_vm and self.use_ssh and not ns_only: # pylint: disable=E1101 if isinstance(cmd, str): cmd = shlex.split(cmd) cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd @@ -1226,8 +1231,14 @@ class Commander: # pylint: disable=R0904 else: # This is the command to execute to be inside the namespace. # We are getting into trouble with quoting. - # Why aren't we passing in MUNET_RUNDIR? - cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}" + envvars = f"MUNET_NODENAME={self.name} NODENAME={self.name}" + if hasattr(self, "rundir"): + envvars += f" RUNDIR={self.rundir}" + if hasattr(self.unet, "config_dirname") and self.unet.config_dirname: + envvars += f" CONFIGDIR={self.unet.config_dirname}" + elif "CONFIGDIR" in os.environ: + envvars += f" CONFIGDIR={os.environ['CONFIGDIR']}" + cmd = f"/usr/bin/env {envvars} {cmd}" # We need sudo b/c we are executing as the user inside the window system. sudo_path = get_exec_path_host(["sudo"]) nscmd = ( @@ -1332,6 +1343,14 @@ class Commander: # pylint: disable=R0904 # Re-adjust the layout if "TMUX" in os.environ: + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, + "even-horizontal", + ] + commander.cmd_status(cmd) cmd = [ get_exec_path_host("tmux"), "select-layout", @@ -1915,18 +1934,19 @@ class LinuxNamespace(Commander, InterfaceMixin): assert unet is None self.uflags = uflags # - # Open file descriptors for current namespaces for later resotration. + # Open file descriptors for current namespaces for later restoration. # try: + # pidfd_open is actually present in 5.4, is this 5.8 check for another + # aspect of what the pidfd_open code is relying on, something in the + # namespace code? If not we can simply check for os.pidfd_open() being + # present as our compat module linux.py runtime patches it in if + # supported by the kernel. kversion = [int(x) for x in platform.release().split("-")[0].split(".")] kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8) except ValueError: kvok = False - if ( - not kvok - or sys.version_info[0] < 3 - or (sys.version_info[0] == 3 and sys.version_info[1] < 9) - ): + if not kvok: # get list of namespace file descriptors before we unshare self.p_ns_fds = [] self.p_ns_fnames = [] @@ -2005,8 +2025,10 @@ class LinuxNamespace(Commander, InterfaceMixin): stdout=stdout, stderr=stderr, text=True, - start_new_session=not unet, shell=False, + # start_new_session=not unet + # preexec_fn=os.setsid if not unet else None, + preexec_fn=os.setsid, ) # The pid number returned is in the global pid namespace. For unshare_inline @@ -2345,14 +2367,14 @@ class LinuxNamespace(Commander, InterfaceMixin): and self.pid != our_pid ): self.logger.debug( - "cleanup pid on separate pid %s from proc pid %s", + "cleanup separate pid %s from namespace proc pid %s", self.pid, self.p.pid if self.p else None, ) await self.cleanup_pid(self.pid) if self.p is not None: - self.logger.debug("cleanup proc pid %s", self.p.pid) + self.logger.debug("cleanup namespace proc pid %s", self.p.pid) await self.async_cleanup_proc(self.p) # return to the previous namespace, need to do this in case anothe munet @@ -2937,7 +2959,7 @@ if True: # pylint: disable=using-constant-test ) logging.debug( - 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + 'ShellWraper: prompt "%s" will_echo %s child.echo %s', prompt, will_echo, spawn.echo, diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py index f631073..133644e 100644 --- a/tests/topotests/munet/cli.py +++ b/tests/topotests/munet/cli.py @@ -325,13 +325,14 @@ def get_shcmd(unet, host, kinds, execfmt, ucmd): if not execfmt: return "" - # Do substitutions for {} in string + # Do substitutions for {} and {N} in string numfmt = len(re.findall(r"{\d*}", execfmt)) if numfmt > 1: ucmd = execfmt.format(*shlex.split(ucmd)) elif numfmt: ucmd = execfmt.format(ucmd) - elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)): + # look for any pair of {}s but do not count escaped {{ or }} + elif len(re.findall(r"{[^}]+}", execfmt.replace("{{", "").replace("}}", ""))): if execfmt.endswith('"'): fstring = "f'''" + execfmt + "'''" else: diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py index 417f745..519c55f 100644 --- a/tests/topotests/munet/linux.py +++ b/tests/topotests/munet/linux.py @@ -132,8 +132,17 @@ def pidfd_open(pid, flags=0): return fd +# Runtime patch if kernel supports the call. if not hasattr(os, "pidfd_open"): - os.pidfd_open = pidfd_open + try: + import platform + + kversion = [int(x) for x in platform.release().split("-")[0].split(".")] + kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 4) + except ValueError: + kvok = False + if kvok: + os.pidfd_open = pidfd_open def setns(fd, nstype): # noqa: D402 diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py index 1df8c0d..7967dd0 100644 --- a/tests/topotests/munet/mutest/userapi.py +++ b/tests/topotests/munet/mutest/userapi.py @@ -144,7 +144,6 @@ class TestCase: result_logger: logging.Logger = None, full_summary: bool = False, ): - self.info = TestCaseInfo(tag, name, path) self.__saved_info = [] self.__short_doc_header = not full_summary @@ -248,7 +247,6 @@ class TestCase: self.rlog.info("%s. %s", tag, header) def __exec_script(self, path, print_header, add_newline): - # Below was the original method to avoid the global TestCase # variable; however, we need global functions so we can import them # into test scripts. Without imports pylint will complain about undefined @@ -393,12 +391,12 @@ class TestCase: self, target: str, cmd: str, - ) -> dict: + ) -> Union[list, dict]: """Execute a json ``cmd`` and return json result. Args: target: the target to execute the command on. - cmd: string to execut on the target. + cmd: string to execute on the target. """ out = self.targets[target].cmd_nostatus(cmd, warn=False) self.last = out = out.rstrip() @@ -420,6 +418,7 @@ class TestCase: match: str, expect_fail: bool, flags: int, + exact_match: bool, ) -> (bool, Union[str, list]): """Execute a ``cmd`` and check result. @@ -429,6 +428,8 @@ class TestCase: match: regex to ``re.search()`` for in output. expect_fail: if True then succeed when the regexp doesn't match. flags: python regex flags to modify matching behavior + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. Returns: (success, matches): if the match fails then "matches" will be None, @@ -436,6 +437,17 @@ class TestCase: ``matches`` otherwise group(0) (i.e., the matching text). """ out = self._command(target, cmd) + if exact_match: + if match not in out: + success = expect_fail + ret = None + else: + success = not expect_fail + ret = match + level = logging.DEBUG if success else logging.WARNING + self.olog.log(level, "exactly matched:%s:", ret) + return success, ret + search = re.search(match, out, flags) self.last_m = search if search is None: @@ -455,17 +467,19 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], expect_fail: bool, - ) -> Union[str, dict]: + exact_match: bool, + ) -> (bool, Union[list, dict]): """Execute a json ``cmd`` and check result. Args: target: the target to execute the command on. cmd: string to execut on the target. - match: A json ``str`` or object (``dict``) to compare against the json - output from ``cmd``. + match: A json ``str``, object (``dict``), or array (``list``) to + compare against the json output from ``cmd``. expect_fail: if True then succeed when the json doesn't match. + exact_match: if True then the json must exactly match. """ js = self._command_json(target, cmd) try: @@ -476,7 +490,27 @@ class TestCase: "JSON load failed. Check match value is in JSON format: %s", error ) - if json_diff := json_cmp(expect, js): + if exact_match: + deep_diff = json_cmp(expect, js) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + else: + deep_diff = json_cmp(expect, js, ignore_order=True) + # Convert DeepDiff completely into dicts or lists at all levels + json_diff = json.loads(deep_diff.to_json()) + # Remove new fields in json object from diff + if json_diff.get("dictionary_item_added") is not None: + del json_diff["dictionary_item_added"] + # Remove new json objects in json array from diff + if (new_items := json_diff.get("iterable_item_added")) is not None: + new_item_paths = list(new_items.keys()) + for path in new_item_paths: + if type(new_items[path]) is dict: + del new_items[path] + if len(new_items) == 0: + del json_diff["iterable_item_added"] + + if json_diff: success = expect_fail if not success: self.logf("JSON DIFF:%s:" % json_diff) @@ -489,14 +523,24 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], is_json: bool, timeout: float, interval: float, expect_fail: bool, flags: int, - ) -> Union[str, dict]: - """Execute a command repeatedly waiting for result until timeout.""" + exact_match: bool, + ) -> Union[str, list, dict]: + """Execute a command repeatedly waiting for result until timeout. + + ``match`` is a regular expression to search for in the output of ``cmd`` + when ``is_json`` is False. + + When ``is_json`` is True ``match`` must be a json object, a json array, + or a ``str`` which parses into a json object. Likewise, the ``cmd`` output + is parsed into a json object or array and then a comparison is done between + the two json objects or arrays. + """ startt = time.time() endt = startt + timeout @@ -504,10 +548,12 @@ class TestCase: ret = None while not success and time.time() < endt: if is_json: - success, ret = self._match_command_json(target, cmd, match, expect_fail) + success, ret = self._match_command_json( + target, cmd, match, expect_fail, exact_match + ) else: success, ret = self._match_command( - target, cmd, match, expect_fail, flags + target, cmd, match, expect_fail, flags, exact_match ) if not success: time.sleep(interval) @@ -626,7 +672,7 @@ class TestCase: ) return self._command(target, cmd) - def step_json(self, target: str, cmd: str) -> dict: + def step_json(self, target: str, cmd: str) -> Union[list, dict]: """See :py:func:`~munet.mutest.userapi.step_json`. :meta private: @@ -649,13 +695,14 @@ class TestCase: desc: str = "", expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """See :py:func:`~munet.mutest.userapi.match_step`. :meta private: """ self.logf( - "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s", + "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -665,8 +712,11 @@ class TestCase: desc, expect_fail, flags, + exact_match, + ) + success, ret = self._match_command( + target, cmd, match, expect_fail, flags, exact_match ) - success, ret = self._match_command(target, cmd, match, expect_fail, flags) if desc: self.__post_result(target, success, desc) return success, ret @@ -684,16 +734,17 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", expect_fail: bool = False, - ) -> (bool, Union[str, dict]): + exact_match: bool = False, + ) -> (bool, Union[list, dict]): """See :py:func:`~munet.mutest.userapi.match_step_json`. :meta private: """ self.logf( - "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s", + "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -702,8 +753,11 @@ class TestCase: match, desc, expect_fail, + exact_match, + ) + success, ret = self._match_command_json( + target, cmd, match, expect_fail, exact_match ) - success, ret = self._match_command_json(target, cmd, match, expect_fail) if desc: self.__post_result(target, success, desc) return success, ret @@ -718,6 +772,7 @@ class TestCase: interval=0.5, expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """See :py:func:`~munet.mutest.userapi.wait_step`. @@ -726,7 +781,7 @@ class TestCase: if interval is None: interval = min(timeout / 20, 0.25) self.logf( - "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -738,9 +793,18 @@ class TestCase: desc, expect_fail, flags, + exact_match, ) success, ret = self._wait( - target, cmd, match, False, timeout, interval, expect_fail, flags + target, + cmd, + match, + False, + timeout, + interval, + expect_fail, + flags, + exact_match, ) if desc: self.__post_result(target, success, desc) @@ -750,12 +814,13 @@ class TestCase: self, target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", timeout=10, interval=None, expect_fail: bool = False, - ) -> (bool, Union[str, dict]): + exact_match: bool = False, + ) -> (bool, Union[list, dict]): """See :py:func:`~munet.mutest.userapi.wait_step_json`. :meta private: @@ -763,7 +828,7 @@ class TestCase: if interval is None: interval = min(timeout / 20, 0.25) self.logf( - "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s", + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", self.tag, self.steps + 1, self.info.path, @@ -774,9 +839,10 @@ class TestCase: interval, desc, expect_fail, + exact_match, ) success, ret = self._wait( - target, cmd, match, True, timeout, interval, expect_fail, 0 + target, cmd, match, True, timeout, interval, expect_fail, 0, exact_match ) if desc: self.__post_result(target, success, desc) @@ -864,15 +930,15 @@ def step(target: str, cmd: str) -> str: return TestCase.g_tc.step(target, cmd) -def step_json(target: str, cmd: str) -> dict: - """Execute a json ``cmd`` on a ``target`` and return the json object. +def step_json(target: str, cmd: str) -> Union[list, dict]: + """Execute a json ``cmd`` on a ``target`` and return the json object or array. Args: target: the target to execute the ``cmd`` on. cmd: string to execute on the target. Returns: - Returns the json object after parsing the ``cmd`` output. + Returns the json object or array after parsing the ``cmd`` output. If json parse fails, a warning is logged and an empty ``dict`` is used. """ @@ -904,6 +970,7 @@ def match_step( desc: str = "", expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """Execute a ``cmd`` on a ``target`` check result. @@ -922,44 +989,53 @@ def match_step( desc: description of test, if no description then no result is logged. expect_fail: if True then succeed when the regexp doesn't match. flags: python regex flags to modify matching behavior + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. Returns: Returns a 2-tuple. The first value is a bool indicating ``success``. The second value will be a list from ``re.Match.groups()`` if non-empty, otherwise ``re.Match.group(0)`` if there was a match otherwise None. """ - return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags) + return TestCase.g_tc.match_step( + target, cmd, match, desc, expect_fail, flags, exact_match + ) def match_step_json( target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", expect_fail: bool = False, -) -> (bool, Union[str, dict]): + exact_match: bool = False, +) -> (bool, Union[list, dict]): """Execute a ``cmd`` on a ``target`` check result. - Execute ``cmd`` on ``target`` and check if the json object in ``match`` + Execute ``cmd`` on ``target`` and check if the json object or array in ``match`` matches or doesn't match (according to the ``expect_fail`` value) the json output from ``cmd``. Args: target: the target to execute the ``cmd`` on. cmd: string to execut on the ``target``. - match: A json ``str`` or object (``dict``) to compare against the json - output from ``cmd``. + match: A json ``str``, object (``dict``), or array (``list``) to compare + against the json output from ``cmd``. desc: description of test, if no description then no result is logged. expect_fail: if True then succeed if the a json doesn't match. + exact_match: if True then the json must exactly match. Returns: Returns a 2-tuple. The first value is a bool indicating ``success``. The - second value is a ``str`` diff if there is a difference found in the json - compare, otherwise the value is the json object (``dict``) from the ``cmd``. + second value is a ``dict`` of the diff if there is a difference found in + the json compare, otherwise the value is the json object (``dict``) or + array (``list``) from the ``cmd``. If json parse fails, a warning is logged and an empty ``dict`` is used. """ - return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail) + return TestCase.g_tc.match_step_json( + target, cmd, match, desc, expect_fail, exact_match + ) def wait_step( @@ -971,6 +1047,7 @@ def wait_step( interval: float = 0.5, expect_fail: bool = False, flags: int = re.DOTALL, + exact_match: bool = False, ) -> (bool, Union[str, list]): """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result. @@ -991,6 +1068,8 @@ def wait_step( desc: description of test, if no description then no result is logged. expect_fail: if True then succeed when the regexp *doesn't* match. flags: python regex flags to modify matching behavior + exact_match: if True then ``match`` must be exactly matched somewhere + in the output of ``cmd`` using ``str.find()``. Returns: Returns a 2-tuple. The first value is a bool indicating ``success``. @@ -998,37 +1077,31 @@ def wait_step( otherwise ``re.Match.group(0)`` if there was a match otherwise None. """ return TestCase.g_tc.wait_step( - target, cmd, match, desc, timeout, interval, expect_fail, flags + target, cmd, match, desc, timeout, interval, expect_fail, flags, exact_match ) def wait_step_json( target: str, cmd: str, - match: Union[str, dict], + match: Union[str, list, dict], desc: str = "", timeout=10, interval=None, expect_fail: bool = False, -) -> (bool, Union[str, dict]): + exact_match: bool = False, +) -> (bool, Union[list, dict]): """Execute a cmd repeatedly and wait for matching result. Execute ``cmd`` on ``target``, every ``interval`` seconds until the output of ``cmd`` matches or doesn't match (according to the ``expect_fail`` value) ``match``, for up to ``timeout`` seconds. - ``match`` is a regular expression to search for in the output of ``cmd`` when - ``is_json`` is False. - - When ``is_json`` is True ``match`` must be a json object or a ``str`` which - parses into a json object. Likewise, the ``cmd`` output is parsed into a json - object and then a comparison is done between the two json objects. - Args: target: the target to execute the ``cmd`` on. cmd: string to execut on the ``target``. - match: A json object or str representation of one to compare against json - output from ``cmd``. + match: A json object, json array, or str representation of json to compare + against json output from ``cmd``. desc: description of test, if no description then no result is logged. timeout: The number of seconds to repeat the ``cmd`` looking for a match (or non-match if ``expect_fail`` is True). @@ -1037,17 +1110,18 @@ def wait_step_json( average the cmd will execute 10 times. The minimum calculated interval is .25s, shorter values can be passed explicitly. expect_fail: if True then succeed if the a json doesn't match. + exact_match: if True then the json must exactly match. Returns: Returns a 2-tuple. The first value is a bool indicating ``success``. - The second value is a ``str`` diff if there is a difference found in the - json compare, otherwise the value is a json object (dict) from the ``cmd`` - output. + The second value is a ``dict`` of the diff if there is a difference + found in the json compare, otherwise the value is a json object (``dict``) + or array (``list``) from the ``cmd`` output. If json parse fails, a warning is logged and an empty ``dict`` is used. """ return TestCase.g_tc.wait_step_json( - target, cmd, match, desc, timeout, interval, expect_fail + target, cmd, match, desc, timeout, interval, expect_fail, exact_match ) diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py index fecf709..4fbbb85 100644 --- a/tests/topotests/munet/native.py +++ b/tests/topotests/munet/native.py @@ -20,6 +20,8 @@ import socket import subprocess import time +from pathlib import Path + from . import cli from .base import BaseMunet from .base import Bridge @@ -38,6 +40,7 @@ from .config import config_to_dict_with_key from .config import find_matching_net_config from .config import find_with_kv from .config import merge_kind_config +from .watchlog import WatchLog class L3ContainerNotRunningError(MunetError): @@ -455,13 +458,14 @@ class NodeMixin: bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") for bp in bps: - gdbcmd += f" '-ex=b {bp}'" + if bp: + gdbcmd += f" '-ex=b {bp}'" - cmds = self.config.get("gdb-run-cmd", []) + cmds = self.config.get("gdb-run-cmds", []) for cmd in cmds: gdbcmd += f" '-ex={cmd}'" - self.run_in_window(gdbcmd) + self.run_in_window(gdbcmd, ns_only=True) elif should_gdb and use_emacs: gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ") ecbin = self.get_exec_path("emacsclient") @@ -664,6 +668,7 @@ class L3NodeMixin(NodeMixin): self.phycount = 0 self.phy_odrivers = {} self.tapmacs = {} + self.watched_logs = {} self.intf_tc_count = 0 @@ -723,6 +728,26 @@ ff02::2\tip6-allrouters if hasattr(self, "bind_mount"): self.bind_mount(hosts_file, "/etc/hosts") + def add_watch_log(self, path, watchfor_re=None): + """Add a WatchLog to this nodes watched logs. + + Args: + path: If relative is relative to the nodes ``rundir`` + watchfor_re: Regular expression to watch the log for and raise an exception + if found. + + Return: + The watching task if request or None otherwise. + """ + path = Path(path) + if not path.is_absolute(): + path = self.rundir.joinpath(path) + + wl = WatchLog(path) + self.watched_logs[wl.path] = wl + task = wl.raise_if_match_task(watchfor_re) if watchfor_re else None + return task + async def console( self, concmd, @@ -938,8 +963,32 @@ ff02::2\tip6-allrouters if hname in self.host_intfs: return self.host_intfs[hname] = lname - self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") - self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + + # See if this interace is missing and needs to be fixed + rc, o, _ = self.unet.rootcmd.cmd_status("ip -o link show") + m = re.search(rf"\d+:\s+(\S+):.*altname {re.escape(hname)}\W", o) + if m: + # need to rename + dname = m.group(1) + self.logger.info("Fixing misnamed %s to %s", dname, hname) + self.unet.rootcmd.cmd_status( + f"ip link property del dev {dname} altname {hname}" + ) + self.unet.rootcmd.cmd_status(f"ip link set {dname} name {hname}") + + rc, o, _ = self.unet.rootcmd.cmd_status("ip -o link show") + m = re.search(rf"\d+:\s+{re.escape(hname)}:.*", o) + if m: + self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") + self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + # Wait for interface to show up in namespace + for retry in range(0, 10): + rc, o, _ = self.cmd_status(f"ip -o link show {hname}") + if not rc: + if re.search(rf"\d+: {re.escape(hname)}:.*", o): + break + if retry > 0: + await asyncio.sleep(1) self.cmd_raises(f"ip link set {hname} name {lname}") if mtu: self.cmd_raises(f"ip link set {lname} mtu {mtu}") @@ -949,7 +998,12 @@ ff02::2\tip6-allrouters lname = self.host_intfs[hname] self.cmd_raises(f"ip link set {lname} down") self.cmd_raises(f"ip link set {lname} name {hname}") - self.cmd_raises(f"ip link set {hname} netns 1") + self.cmd_status(f"ip link set netns 1 dev {hname}") + # The above is failing sometimes and not sure why + # logging.error( + # "XXX after setns %s", + # self.unet.rootcmd.cmd_nostatus(f"ip link show {hname}"), + # ) del self.host_intfs[hname] async def add_phy_intf(self, devaddr, lname): @@ -1019,12 +1073,13 @@ ff02::2\tip6-allrouters "Physical PCI device %s already bound to vfio-pci", devaddr ) return + self.logger.info( "Unbinding physical PCI device %s from driver %s", devaddr, driver ) self.phy_odrivers[devaddr] = driver self.unet.rootcmd.cmd_raises( - f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind" + f"echo {devaddr} | timeout 10 tee /sys/bus/pci/drivers/{driver}/unbind" ) # Add the device vendor and device id to vfio-pci in case it's the first time @@ -1035,7 +1090,14 @@ ff02::2\tip6-allrouters f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False ) - if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"): + for retry in range(0, 10): + if self.unet.rootcmd.path_exists( + f"/sys/bus/pci/drivers/vfio-pci/{devaddr}" + ): + break + if retry > 0: + await asyncio.sleep(1) + # Bind to vfio-pci if wasn't added with new_id self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr) ec, _, _ = self.unet.rootcmd.cmd_status( @@ -1066,7 +1128,7 @@ ff02::2\tip6-allrouters "Unbinding physical PCI device %s from driver vfio-pci", devaddr ) self.unet.rootcmd.cmd_status( - f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind" + f"echo {devaddr} | timeout 10 tee /sys/bus/pci/drivers/vfio-pci/unbind" ) self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver) @@ -1085,13 +1147,13 @@ ff02::2\tip6-allrouters for hname in list(self.host_intfs): await self.rem_host_intf(hname) - # remove any hostintf interfaces - for devaddr in list(self.phy_intfs): - await self.rem_phy_intf(devaddr) - # delete the LinuxNamespace/InterfaceMixin await super()._async_delete() + # remove any hostintf interfaces, needs to come after normal exits + for devaddr in list(self.phy_intfs): + await self.rem_phy_intf(devaddr) + class L3NamespaceNode(L3NodeMixin, LinuxNamespace): """A namespace L3 node.""" @@ -1123,6 +1185,7 @@ class L3ContainerNode(L3NodeMixin, LinuxNamespace): assert self.container_image self.cmd_p = None + self.cmd_pid = None self.__base_cmd = [] self.__base_cmd_pty = [] @@ -1393,7 +1456,13 @@ class L3ContainerNode(L3NodeMixin, LinuxNamespace): start_new_session=True, # keeps main tty signals away from podman ) - self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid) + # If our process is actually the child of an nsenter fetch its pid. + if self.nsenter_fork: + self.cmd_pid = await self.get_proc_child_pid(self.cmd_p) + + self.logger.debug( + "%s: async_popen => %s (%s)", self, self.cmd_p.pid, self.cmd_pid + ) self.pytest_hook_run_cmd(stdout, stderr) @@ -1542,6 +1611,7 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): """Create a Container Node.""" self.cont_exec_paths = {} self.launch_p = None + self.launch_pid = None self.qemu_config = config["qemu"] self.extra_mounts = [] assert self.qemu_config @@ -1968,8 +2038,9 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}") con.cmd_raises("ip link set lo up") - if self.unet.cfgopt.getoption("--coverage"): - con.cmd_raises("mount -t debugfs none /sys/kernel/debug") + # This is already mounted now + # if self.unet.cfgopt.getoption("--coverage"): + # con.cmd_raises("mount -t debugfs none /sys/kernel/debug") async def gather_coverage_data(self): con = self.conrepl @@ -2261,25 +2332,29 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): stdout = open(os.path.join(self.rundir, "qemu.out"), "wb") stderr = open(os.path.join(self.rundir, "qemu.err"), "wb") - self.launch_p = await self.async_popen( + self.launch_p = await self.async_popen_nsonly( args, stdin=subprocess.DEVNULL, stdout=stdout, stderr=stderr, pass_fds=pass_fds, - # We don't need this here b/c we are only ever running qemu and that's all - # we need to kill for cleanup - # XXX reconcile this - start_new_session=True, # allows us to signal all children to exit + # Don't want Keybaord interrupt etc to pass to child. + # start_new_session=True, + preexec_fn=os.setsid, ) + if self.nsenter_fork: + self.launch_pid = await self.get_proc_child_pid(self.launch_p) + self.pytest_hook_run_cmd(stdout, stderr) # We've passed these on, so don't need these open here anymore. for fd in pass_fds: os.close(fd) - self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid) + self.logger.debug( + "%s: popen => %s (%s)", self, self.launch_p.pid, self.launch_pid + ) confiles = ["_console"] if use_cmdcon: @@ -2307,10 +2382,10 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): # the monitor output has super annoying ANSI escapes in it output = self.monrepl.cmd_nostatus("info status") - self.logger.info("VM status: %s", output) + self.logger.debug("VM status: %s", output) output = self.monrepl.cmd_nostatus("info kvm") - self.logger.info("KVM status: %s", output) + self.logger.debug("KVM status: %s", output) # # Set thread affinity @@ -2348,11 +2423,6 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): "%s: node launch (qemu) cmd wait() canceled: %s", future, error ) - async def cleanup_qemu(self): - """Launch qemu.""" - if self.launch_p: - await self.async_cleanup_proc(self.launch_p) - async def async_cleanup_cmd(self): """Run the configured cleanup commands for this node.""" self.cleanup_called = True @@ -2372,7 +2442,7 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): # Need to cleanup early b/c it is running on the VM if self.cmd_p: - await self.async_cleanup_proc(self.cmd_p) + await self.async_cleanup_proc(self.cmd_p, self.cmd_pid) self.cmd_p = None try: @@ -2388,9 +2458,9 @@ class L3QemuVM(L3NodeMixin, LinuxNamespace): if not self.launch_p: self.logger.warning("async_delete: qemu is not running") else: - await self.cleanup_qemu() + await self.async_cleanup_proc(self.launch_p, self.launch_pid) except Exception as error: - self.logger.warning("%s: failued to cleanup qemu process: %s", self, error) + self.logger.warning("%s: failed to cleanup qemu process: %s", self, error) await super()._async_delete() @@ -2814,6 +2884,8 @@ ff02::2\tip6-allrouters logging.debug("Launching nodes") await asyncio.gather(*[x.launch() for x in launch_nodes]) + logging.debug("Launched nodes -- Queueing Waits") + # Watch for launched processes to exit for node in launch_nodes: task = asyncio.create_task( @@ -2822,17 +2894,23 @@ ff02::2\tip6-allrouters task.add_done_callback(node.launch_completed) tasks.append(task) + logging.debug("Wait complete queued, running cmd") + if run_nodes: # would like a info when verbose here. logging.debug("Running `cmd` on nodes") await asyncio.gather(*[x.run_cmd() for x in run_nodes]) + logging.debug("Ran cmds -- Queueing Waits") + # Watch for run_cmd processes to exit for node in run_nodes: task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd") task.add_done_callback(node.cmd_completed) tasks.append(task) + logging.debug("Wait complete queued, waiting for ready") + # Wait for nodes to be ready if ready_nodes: @@ -2853,6 +2931,8 @@ ff02::2\tip6-allrouters raise asyncio.TimeoutError() logging.debug("All nodes ready") + logging.debug("All done returning tasks: %s", tasks) + return tasks async def _async_delete(self): diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py index 25039df..3c6d946 100644 --- a/tests/topotests/munet/testing/fixtures.py +++ b/tests/topotests/munet/testing/fixtures.py @@ -95,7 +95,7 @@ def _push_log_handler(desc, logpath): logging.debug("conftest: adding %s logging at %s", desc, logpath) root_logger = logging.getLogger() handler = logging.FileHandler(logpath, mode="w") - fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s") + fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s") handler.setFormatter(fmt) root_logger.addHandler(handler) return handler diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py index 9b6a49a..985eef9 100644 --- a/tests/topotests/munet/testing/hooks.py +++ b/tests/topotests/munet/testing/hooks.py @@ -196,10 +196,10 @@ def pytest_runtest_makereport(item, call): if error: item.skip_more_pause = True - # we can't asyncio.run() (which pause does) if we are unhsare_inline + # we can't asyncio.run() (which pause does) if we are not unhsare_inline # at this point, count on an autouse fixture to pause instead in this # case - if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline: + if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline: pause_test(f"before test '{item.nodeid}'") # check for a result to try and catch setup (or module setup) failure diff --git a/tests/topotests/munet/watchlog.py b/tests/topotests/munet/watchlog.py new file mode 100644 index 0000000..27bc325 --- /dev/null +++ b/tests/topotests/munet/watchlog.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# August 21 2023, Christian Hopps +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A module supporting an object for watching a logfile.""" +import asyncio +import logging +import re + +from pathlib import Path + + +class MatchFoundError(Exception): + """An error raised when a match is not found.""" + def __init__(self, watchlog, match): + self.watchlog = watchlog + self.match = match + super().__init__(watchlog, match) + + +class WatchLog: + """An object for watching a logfile.""" + + def __init__(self, path, encoding="utf-8"): + """Watch a logfile. + + Args: + path: that path of the logfile to watch + encoding: the encoding of the logfile + """ + # Immutable + self.path = Path(path) + self.encoding = encoding + + # Mutable + self.content = "" + self.last_snap_mark = 0 + self.last_user_mark = 0 + self.stat = None + + if self.path.exists(): + self.snapshot() + + def _stat_snapshot(self): + ostat = self.stat + + if not self.path.exists(): + self.stat = None + return ostat is not None + + stat = self.path.stat() + self.stat = stat + + if ostat is None: + return True + + return ( + stat.st_mtime_ns != ostat.st_mtime_ns + or stat.st_ctime_ns != ostat.st_ctime_ns + or stat.st_ino != ostat.st_ino + or stat.st_size != ostat.st_size + ) + + def reset(self): + self.content = "" + self.last_user_mark = 0 + self.last_snap_mark = 0 + + def update_content(self): + ostat = self.stat + osize = ostat.st_size if ostat else 0 + oino = ostat.st_ino if ostat else -1 + if not self._stat_snapshot(): + logging.debug("XXX logfile %s no stat change", self.path) + return "" + + nino = self.stat.st_ino + # If the inode changed and we had content previously warn + if oino != -1 and oino != nino and self.content: + logging.warning( + "logfile %s replaced (new inode) resetting content", self.path + ) + self.reset() + osize = 0 + + nsize = self.stat.st_size + if osize > nsize: + logging.warning("logfile %s shrunk resetting content", self.path) + self.reset() + osize = 0 + + if osize == nsize: + logging.debug( + "XXX watchlog: %s no update, osize == nsize == %s", self.path, osize + ) + return "" + + # Read non-blocking + with open(self.path, "r", encoding=self.encoding) as f: + if osize: + f.seek(osize) + logging.debug( + "XXX watchlog: %s reading new content from %s to %s", + self.path, + osize, + nsize, + ) + newcontent = f.read(nsize - osize) + + self.content += newcontent + return newcontent + + def raise_if_match_task(self, match): + """Start an async task that searches for a match. + + This doesn't work well with pytest as the task must be awaited for the exception + to propagate. + """ + + async def scan_for_match(wl, regex): + while True: + logging.debug("watchlog: %s scan for updating content", wl.path) + wl.update_content() + if m := regex.search(wl.content): + logging.error( + "XXX watchlog: %s regexp FOUND raising exception!", wl.path + ) + raise MatchFoundError(wl, m) + await asyncio.sleep(2) + + aw = scan_for_match(self, re.compile(match)) + return asyncio.create_task(aw) + + def from_mark(self, mark=None): + """Return the file content starting from ``mark``. + + If ``mark`` is None then return content since last ``set_mark`` was called. + + Args: + mark: the mark in the content to return file content from. + + Return: + returns the content between ``mark`` and the end of content. + """ + return self.content[mark:] + + def set_mark(self): + """Set a mark for later use.""" + last_mark = self.last_user_mark + self.last_user_mark = len(self.content) + return last_mark + + def snapshot(self): + """Update the file content and return new text. + + Returns any new text added since the last snapshot, + also updates the snapshot mark. + + Return: + Newly added text. + """ + # Update the content which may reset marks + self.update_content() + + last_mark = self.last_snap_mark + self.last_snap_mark = len(self.content) + return self.content[last_mark:] diff --git a/tests/topotests/nb_config/r1/frr.conf b/tests/topotests/nb_config/r1/frr.conf new file mode 100644 index 0000000..677ec0b --- /dev/null +++ b/tests/topotests/nb_config/r1/frr.conf @@ -0,0 +1,6 @@ +log timestamp precision 6 +log file frr.log + +interface r1-eth0 + ip address 1.1.1.1/24 +exit diff --git a/tests/topotests/nb_config/test_nb_config.py b/tests/topotests/nb_config/test_nb_config.py new file mode 100644 index 0000000..f699a4e --- /dev/null +++ b/tests/topotests/nb_config/test_nb_config.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# February 24 2024, Christian Hopps +# +# Copyright (c) 2024, LabN Consulting, L.L.C. +# +""" +Test Northbound Config Operations +""" +import json +import os + +import pytest +from lib.topogen import Topogen +from lib.topotest import json_cmp + +pytestmark = [pytest.mark.mgmtd] + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1",) + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_access_list_config_ordering(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + output = r1.vtysh_multicmd([ + "conf t", + "access-list test seq 1 permit host 10.0.0.1"]) + output = r1.vtysh_cmd("show ip access-list test json") + got = json.loads(output) + expected = json.loads('{"ZEBRA":{"test":{"type":"Standard", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "address":"10.0.0.1", "mask":"0.0.0.0"}]}}}') + result = json_cmp(got, expected) + assert result is None + + # + # If the northbound mis-orders the create/delete then this test fails. + # https://github.com/FRRouting/frr/pull/15423/commits/38b85e0c2bc555b8827dbd2cb6515b6febf548b4 + # + output = r1.vtysh_multicmd([ + "conf t", + "access-list test seq 1 permit 10.0.0.0/8"]) + output = r1.vtysh_cmd("show ip access-list test json") + got = json.loads(output) + expected = json.loads('{"ZEBRA":{"test":{"type":"Zebra", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "prefix":"10.0.0.0/8", "exact-match":false}]}}}') + result = json_cmp(got, expected) + assert result is None diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py index 78b82ed..26115de 100644 --- a/tests/topotests/nhrp_topo/test_nhrp_topo.py +++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py @@ -182,6 +182,27 @@ def test_protocols_convergence(): assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg + # check that the NOARP flag is removed from rX-gre0 interfaces + for rname, router in router_list.items(): + if rname == "r3": + continue + + expected = { + "{}-gre0".format(rname): { + "flags": "", + } + } + test_func = partial( + topotest.router_json_cmp, + router, + "show interface {}-gre0 json".format(rname), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + + assertmsg = '"{}-gre0 interface flags incorrect'.format(router.name) + assert result is None, assertmsg + for rname, router in router_list.items(): if rname == "r3": continue diff --git a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py index ec15ff9..2eaccb8 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py +++ b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py @@ -110,15 +110,18 @@ def test_wait_protocol_convergence(): def expect_neighbor_full(router, neighbor): "Wait until OSPFv3 neighborship is full" - logger.info("waiting for OSPFv3 router '{}' neighborship with '{}'".format(router, neighbor)) + logger.info( + "waiting for OSPFv3 router '{}' neighborship with '{}'".format( + router, neighbor + ) + ) test_func = partial( topotest.router_json_cmp, tgen.gears[router], "show ipv6 ospf6 neighbor json", {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, ) - _, result = topotest.run_and_expect(test_func, None, - count=130, wait=1) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) assertmsg = '"{}" convergence failure'.format(router) assert result is None, assertmsg @@ -143,6 +146,7 @@ def test_wait_protocol_convergence(): expect_neighbor_full("r8", "10.254.254.5") expect_neighbor_full("r9", "10.254.254.5") + def test_ecmp_inter_area(): "Test whether OSPFv3 ECMP nexthops are properly updated for inter-area routes after link down" tgen = get_topogen() @@ -156,22 +160,28 @@ def test_ecmp_inter_area(): def expect_num_nexthops(router, expected_num_nexthops, count): "Wait until number of nexthops for routes matches expectation" - logger.info("waiting for OSPFv3 router '{}' nexthops {}".format(router, expected_num_nexthops)) + logger.info( + "waiting for OSPFv3 router '{}' nexthops {}".format( + router, expected_num_nexthops + ) + ) test_func = partial(num_nexthops, router) - _, result = topotest.run_and_expect(test_func, expected_num_nexthops, - count=count, wait=3) - assert result == expected_num_nexthops, \ - "'{}' wrong number of route nexthops".format(router) + _, result = topotest.run_and_expect( + test_func, expected_num_nexthops, count=count, wait=3 + ) + assert ( + result == expected_num_nexthops + ), "'{}' wrong number of route nexthops".format(router) # Check nexthops pre link-down - expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3], 4) + expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3, 3, 3], 4) logger.info("triggering R2-R4 link down") tgen.gears["r2"].run("ip link set r2-eth1 down") - #tgen.mininet_cli() + # tgen.mininet_cli() # Check nexthops post link-down - expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2], 8) + expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2, 2, 2], 8) def teardown_module(_mod): diff --git a/tests/topotests/ospf6_point_to_multipoint/README.md b/tests/topotests/ospf6_point_to_multipoint/README.md new file mode 100644 index 0000000..59c9837 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/README.md @@ -0,0 +1,137 @@ +# OSPFv3 (IPv6) Topology Test (point-to-multipoint) + +## Topology + -----\ + SW1 - Stub Net 1 SW2 - Stub Net 2 \ + fc00:1:1:1::/64 fc00:2:2:2::/64 \ + \___________________/ \___________________/ | + | | | + | | | + | ::1 | ::2 | + +---------+---------+ +---------+---------+ | + | R1 | | R2 | | + | FRRouting | | FRRouting | | + | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | | + +---------+---------+ +---------+---------+ | + | ::1 | ::2 \ + \______ ___________/ OSPFv3 + \ / Area 0.0.0.0 + \ / / + ~~~~~~~~~~~~~~~~~~ | + ~~ SW5 ~~ | + ~~ Switch ~~ | + ~~ fc00:A:A:A::/64 ~~ | + ~~~~~~~~~~~~~~~~~~ | + | /---- | + | ::3 | SW3 - Stub Net 3 | + +---------+---------+ /-+ fc00:3:3:3::/64 | + | R3 | / | / + | FRRouting +--/ \---- / + | Rtr-ID: 10.0.0.3 | ::3 ___________/ + +---------+---------+ \ + | ::3 \ + | \ + ~~~~~~~~~~~~~~~~~~ | + ~~ SW6 ~~ | + ~~ Switch ~~ | + ~~ fc00:B:B:B::/64 ~~ \ + ~~~~~~~~~~~~~~~~~~ OSPFv3 + | Area 0.0.0.1 + | ::4 / + +---------+---------+ /---- | + | R4 | | SW4 - Stub Net 4 | + | FRRouting +------+ fc00:4:4:4::/64 | + | Rtr-ID: 10.0.0.4 | ::4 | / + +-------------------+ \---- / + -----/ + +## FRR Configuration + +Full config as used is in r1 / r2 / r3 / r4 / r5 subdirectories + +Simplified `R1` config (R1 is similar) + + hostname r1 + ! + interface r1-stubnet + ipv6 address fc00:1:1:1::1/64 + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 + ! + interface r1-sw5 + ipv6 address fc00:a:a:a::1/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ! + router ospf6 + router-id 10.0.0.1 + log-adjacency-changes detail + redistribute static + ! + ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 + +Simplified `R3` config + + hostname r3 + ! + interface r3-stubnet + ipv6 address fc00:3:3:3::3/64 + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 + ! + interface r3-sw5 + ipv6 address fc00:a:a:a::3/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 p2p-p2mp connected-prefixes include + ! + interface r3-sw6 + ipv6 address fc00:b:b:b::3/64 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 p2p-p2mp connected-prefixes include + ! + router ospf6 + router-id 10.0.0.3 + log-adjacency-changes detail + redistribute static + ! + ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 + +## Tests executed + +### Check if FRR is running + +Test is executed by running + + vtysh -c "show logging" | grep "Logging configuration for" + +on each FRR router. This should return the logging information for all daemons registered +to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`) + +### Check if OSPFv3 to converge + +OSPFv3 is expected to converge on each view within 60s total time. Convergence is verified by executing (on each node) + + vtysh -c "show ipv6 ospf neigh" + +and checking for "Full" neighbor status in the output. An additional 15 seconds after the full converge is waited for +routes to populate before the following routing table checks are executed + +### Check OSPFv3 Routing Tables + +Routing table is verified by running + + vtysh -c "show ipv6 route" + +on each node and comparing the result to the stored example config (see `show_ipv6_route.ref` in r1 / r2 / r3 / r4 directories). +Link-Local addresses are masked out before the compare. + +### Check Linux Kernel Routing Table + +Linux Kernel IPv6 Routing table is verified on each FRR node with + + ip -6 route + +Tables are compared with reference routing table (see `ip_6_address.ref` in r1 / r2 / r3 / r4 directories). +Link-Local addresses are translated after getting collected on each node with interface name to make them consistent diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref new file mode 100644 index 0000000..203f2de --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref new file mode 100644 index 0000000..b77c996 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf new file mode 100644 index 0000000..79a9dca --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r1 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r1-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r1-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.1 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref new file mode 100644 index 0000000..911b3e4 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf new file mode 100644 index 0000000..3a7db9f --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r1/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r1 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r1-stubnet + ipv6 address fc00:1:1:1::1/64 +! +interface r1-sw5 + ipv6 address fc00:a:a:a::1/64 +! +interface lo +! +ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref new file mode 100644 index 0000000..d4534b1 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium +fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref new file mode 100644 index 0000000..330b696 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium +fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf new file mode 100644 index 0000000..751d8d8 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r2 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r2-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r2-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.2 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref new file mode 100644 index 0000000..2eff029 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf new file mode 100644 index 0000000..5571dc9 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r2/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r2 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r2-stubnet + ipv6 address fc00:2:2:2::2/64 +! +interface r2-sw5 + ipv6 address fc00:a:a:a::2/64 +! +interface lo +! +ipv6 route fc00:2222:2222:2222::/64 fc00:2:2:2::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref new file mode 100644 index 0000000..903c1d9 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.nhg.ref @@ -0,0 +1,13 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium +fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::4 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref new file mode 100644 index 0000000..3dbcf36 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ip_6_address.ref @@ -0,0 +1,13 @@ +fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium +fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium +fc00:4444:4444:4444::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium +fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::4 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf new file mode 100644 index 0000000..74e8ddf --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/ospf6d.conf @@ -0,0 +1,37 @@ +hostname r3 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r3-sw5 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r3-sw6 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r3-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.0 +! +router ospf6 + ospf6 router-id 10.0.0.3 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref new file mode 100644 index 0000000..df949e6 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/show_ipv6_route.ref @@ -0,0 +1,12 @@ +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX +O>* fc00:b:b:b::4/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf new file mode 100644 index 0000000..3cc5626 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r3/zebra.conf @@ -0,0 +1,23 @@ +! +hostname r3 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r3-stubnet + ipv6 address fc00:3:3:3::3/64 +! +interface r3-sw5 + ipv6 address fc00:a:a:a::3/64 +! +interface r3-sw6 + ipv6 address fc00:b:b:b::3/64 +! +interface lo +! +ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref new file mode 100644 index 0000000..ca1e2f0 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.nhg.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium +fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium +fc00:a:a:a::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::1 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::2 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref new file mode 100644 index 0000000..4f4e72f --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ip_6_address.ref @@ -0,0 +1,14 @@ +fc00:1111:1111:1111::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:1:1:1::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2222:2222:2222::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium +fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium +fc00:a:a:a::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::1 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::2 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:a:a:a::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium +fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium +fc00:b:b:b::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf b/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf new file mode 100644 index 0000000..25b8551 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/ospf6d.conf @@ -0,0 +1,30 @@ +hostname r4 +log file ospf6d.log +! +! debug ospf6 message all +! debug ospf6 lsa unknown +! debug ospf6 zebra +! debug ospf6 interface +! debug ospf6 neighbor +! debug ospf6 route table +! debug ospf6 flooding +! +interface r4-sw6 + ipv6 ospf6 network point-to-multipoint + ipv6 ospf6 area 0.0.0.1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 p2p-p2mp connected-prefixes include +! +interface r4-stubnet + ipv6 ospf6 passive + ipv6 ospf6 area 0.0.0.1 +! +router ospf6 + ospf6 router-id 10.0.0.4 + log-adjacency-changes detail + redistribute static +! +line vty + exec-timeout 0 0 +! diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref b/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref new file mode 100644 index 0000000..f377ca3 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/show_ipv6_route.ref @@ -0,0 +1,13 @@ +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::1/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::2/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX +O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf b/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf new file mode 100644 index 0000000..20e27ce --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/r4/zebra.conf @@ -0,0 +1,20 @@ +! +hostname r4 +log file zebra.log +! +! debug zebra events +! debug zebra rib +! +interface r4-stubnet + ipv6 address fc00:4:4:4::4/64 +! +interface r4-sw6 + ipv6 address fc00:b:b:b::4/64 +! +interface lo +! +ipv6 route fc00:4444:4444:4444::/64 fc00:4:4:4::1234 +! +! +line vty +! diff --git a/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py b/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py new file mode 100644 index 0000000..142acf1 --- /dev/null +++ b/tests/topotests/ospf6_point_to_multipoint/test_ospf6_point_to_multipoint.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf6_point_to_multipoint.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +r""" +test_ospf6_point_to_multipoint.py: + + -----\ + SW1 - Stub Net 1 SW2 - Stub Net 2 \ + fc00:1:1:1::/64 fc00:2:2:2::/64 \ +\___________________/ \___________________/ | + | | | + | | | + | ::1 | ::2 | ++---------+---------+ +---------+---------+ | +| R1 | | R2 | | +| FRRouting | | FRRouting | | +| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | | ++---------+---------+ +---------+---------+ | + | ::1 | ::2 \ + \______ ___________/ OSPFv3 + \ / Area 0.0.0.0 + \ / / + ~~~~~~~~~~~~~~~~~~ | + ~~ SW5 ~~ | + ~~ Switch ~~ | + ~~ fc00:A:A:A::/64 ~~ | + ~~~~~~~~~~~~~~~~~~ | + | /---- | + | ::3 | SW3 - Stub Net 3 | + +---------+---------+ /-+ fc00:3:3:3::/64 | + | R3 | / | / + | FRRouting +--/ \---- / + | Rtr-ID: 10.0.0.3 | ::3 ___________/ + +---------+---------+ \ + | ::3 \ + | \ + ~~~~~~~~~~~~~~~~~~ | + ~~ SW6 ~~ | + ~~ Switch ~~ | + ~~ fc00:B:B:B::/64 ~~ \ + ~~~~~~~~~~~~~~~~~~ OSPFv3 + | Area 0.0.0.1 + | ::4 / + +---------+---------+ /---- | + | R4 | | SW4 - Stub Net 4 | + | FRRouting +------+ fc00:4:4:4::/64 | + | Rtr-ID: 10.0.0.4 | ::4 | / + +-------------------+ \---- / + -----/ +""" + +import os +import re +import sys +import pytest + +from functools import partial + + +# Save the Current Working Directory to find configuration files later. +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 + + +pytestmark = [pytest.mark.ospfd] + + +def build_topo(tgen): + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + # + # Wire up the switches and routers + # Note that we specify the link names so we match the config files + # + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet") + + # Create a empty network for router 2 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet") + + # Create a empty network for router 3 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet") + + # Create a empty network for router 4 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet") + + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"], nodeif="r1-sw5") + switch.add_link(tgen.gears["r2"], nodeif="r2-sw5") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw5") + + # Interconnect routers 3 and 4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw6") + switch.add_link(tgen.gears["r4"], nodeif="r4-sw6") + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") + + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() + + router_list = tgen.routers() + 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_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_wait_protocol_convergence(): + "Wait for OSPFv3 to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_neighbor_full(router, neighbor): + "Wait until OSPFv3 convergence." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 neighbor json", + {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + expect_neighbor_full("r1", "10.0.0.2") + expect_neighbor_full("r1", "10.0.0.3") + + expect_neighbor_full("r2", "10.0.0.1") + expect_neighbor_full("r2", "10.0.0.3") + + expect_neighbor_full("r3", "10.0.0.1") + expect_neighbor_full("r3", "10.0.0.2") + expect_neighbor_full("r3", "10.0.0.4") + + expect_neighbor_full("r4", "10.0.0.3") + + +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() + + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) + + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match("^O", line): + linearr.append(line) + + current = "\n".join(linearr) + + return topotest.difflines( + topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output", + ) + + +def test_ospfv3_routingTable(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + +def test_linux_ipv6_kernel_routingTable(): + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Verify Linux Kernel Routing Table + logger.info("Verifying Linux IPv6 Kernel Routing Table") + + failures = 0 + + # Get a list of all current link-local addresses first as they change for + # each run and we need to translate them + linklocals = [] + for i in range(1, 5): + linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) + + for i in range(1, 5): + # Actual output from router + actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip() + if "nhid" in actual: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i)) + else: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i)) + + if os.path.isfile(refTableFile): + expected = open(refTableFile).read().rstrip() + # Fix newlines (make them all the same) + expected = ("\n".join(expected.splitlines())).splitlines(1) + + # Mask out Link-Local mac addresses + for ll in linklocals: + actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) + # Mask out protocol name or number + actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual) + actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual) + # Remove ff00::/8 routes (seen on some kernels - not from FRR) + actual = re.sub(r"ff00::/8.*", "", actual) + + # Strip empty lines + actual = actual.lstrip() + actual = actual.rstrip() + actual = re.sub(r" +", " ", actual) + + filtered_lines = [] + for line in sorted(actual.splitlines()): + if line.startswith("fe80::/64 ") or line.startswith( + "unreachable fe80::/64 " + ): + continue + filtered_lines.append(line) + actual = "\n".join(filtered_lines).splitlines(1) + + # Print Actual table + # logger.info("Router r%s table" % i) + # for line in actual: + # logger.info(line.rstrip()) + + # Generate Diff + diff = topotest.get_textdiff( + actual, + expected, + title1="actual OSPFv3 IPv6 routing table", + title2="expected OSPFv3 IPv6 routing table", + ) + + # Empty string if it matches, otherwise diff contains unified diff + if diff: + sys.stderr.write( + "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n" + % (i, diff) + ) + failures += 1 + else: + logger.info("r%s ok" % i) + + assert failures == 0, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) + else: + logger.error("r{} failed - no nhid ref file: {}".format(i, refTableFile)) + + assert False, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s\n" + % (i) + ) + + +def test_shutdown_check_stderr(): + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + logger.info( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") + + net = tgen.net + + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") + + for i in range(1, 5): + net["r%s" % i].stopRouter() + log = net["r%s" % i].getStdErr("ospf6d") + if log: + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + log = net["r%s" % i].getStdErr("zebra") + if log: + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + + +def test_shutdown_check_memleak(): + "Run the memory leak test and report results." + + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + logger.info( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)" + ) + pytest.skip("Skipping test for memory leaks") + + tgen = get_topogen() + + net = tgen.net + + for i in range(1, 5): + net["r%s" % i].stopRouter() + net["r%s" % i].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) + + +if __name__ == "__main__": + + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli + # retval = pytest.main(["-s", "--tb=no"]) + + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/ospf6_topo1/r1/ospf6d.conf b/tests/topotests/ospf6_topo1/r1/ospf6d.conf index 5f1ceee..d2693ec 100644 --- a/tests/topotests/ospf6_topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r1/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r1-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r1-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.1 log-adjacency-changes detail redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r2/ospf6d.conf b/tests/topotests/ospf6_topo1/r2/ospf6d.conf index d51b41e..c9e88f1 100644 --- a/tests/topotests/ospf6_topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r2/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r2-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r2-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.2 log-adjacency-changes detail redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r3/ospf6d.conf b/tests/topotests/ospf6_topo1/r3/ospf6d.conf index cad71ac..e1c3e44 100644 --- a/tests/topotests/ospf6_topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r3/ospf6d.conf @@ -10,16 +10,19 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r3-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r3-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r3-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -28,9 +31,6 @@ router ospf6 ospf6 router-id 10.0.0.3 log-adjacency-changes detail redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1/r4/ospf6d.conf b/tests/topotests/ospf6_topo1/r4/ospf6d.conf index f0b166b..230ec7a 100644 --- a/tests/topotests/ospf6_topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf6_topo1/r4/ospf6d.conf @@ -10,11 +10,13 @@ log file ospf6d.log ! debug ospf6 flooding ! interface r4-stubnet + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! interface r4-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 ospf6 router-id 10.0.0.4 log-adjacency-changes detail redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json index c0a1f6a..ebb3848 100644 --- a/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json +++ b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json @@ -8,6 +8,6 @@ "type":"sharp" } ], - "routesTotal":4, - "routesTotalFib":4 + "routesTotal":5, + "routesTotalFib":5 } diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf index e365e25..9959581 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf @@ -5,12 +5,18 @@ log file /tmp/r1-frr.log ! interface r1-eth0 ip address 10.0.1.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r1-eth1 ip address 10.0.20.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r1-eth2 vrf neno ip address 10.0.30.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt index 86c089a..248375d 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt @@ -1,9 +1,11 @@ O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX B>* 10.0.3.0/24 [20/20] via 10.0.30.3, r1-eth2 (vrf neno), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r1-eth1, XX:XX:XX -B>* 10.0.30.0/24 [20/0] is directly connected, r1-eth2 (vrf neno), weight 1, XX:XX:XX +L>* 10.0.20.1/32 is directly connected, r1-eth1, XX:XX:XX +B>* 10.0.30.0/24 [20/0] is directly connected, neno (vrf neno), weight 1, XX:XX:XX O>* 10.0.40.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt index 4e818eb..6e13352 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt @@ -3,4 +3,5 @@ O>* 10.0.3.0/24 [110/20] via 10.0.30.3, r1-eth2, weight 1, XX:XX:XX B>* 10.0.4.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX O 10.0.30.0/24 [110/10] is directly connected, r1-eth2, weight 1, XX:XX:XX C>* 10.0.30.0/24 is directly connected, r1-eth2, XX:XX:XX +L>* 10.0.30.1/32 is directly connected, r1-eth2, XX:XX:XX B>* 10.0.40.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf index e87899c..29909de 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf @@ -5,14 +5,20 @@ log file /tmp/r2-frr.log ! interface r2-eth0 ip address 10.0.2.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r2-eth1 ip address 10.0.20.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip route 0.0.0.0/0 10.0.20.1 ! interface r2-eth2 vrf ray ip address 10.0.40.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt index 9681d8a..d7d3143 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt @@ -2,9 +2,11 @@ S>* 0.0.0.0/0 [1/0] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX O>* 10.0.1.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.2/32 is directly connected, r2-eth0, XX:XX:XX O>* 10.0.3.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX B>* 10.0.4.0/24 [20/20] via 10.0.40.4, r2-eth2 (vrf ray), weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.20.2/32 is directly connected, r2-eth1, XX:XX:XX O>* 10.0.30.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX -B>* 10.0.40.0/24 [20/0] is directly connected, r2-eth2 (vrf ray), weight 1, XX:XX:XX +B>* 10.0.40.0/24 [20/0] is directly connected, ray (vrf ray), weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt index ce9903a..6ab1bb8 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt @@ -1,9 +1,10 @@ VRF ray: B 10.0.1.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX -B 10.0.2.0/24 [20/0] is directly connected, r2-eth0 (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.2.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.3.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.40.4, r2-eth2, weight 1, XX:XX:XX -B 10.0.20.0/24 [20/0] is directly connected, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.20.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.30.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O 10.0.40.0/24 [110/10] is directly connected, r2-eth2, weight 1, XX:XX:XX C>* 10.0.40.0/24 is directly connected, r2-eth2, XX:XX:XX +L>* 10.0.40.2/32 is directly connected, r2-eth2, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf index 2657f58..35fe22e 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf @@ -5,9 +5,13 @@ log file /tmp/r3-frr.log ! interface r3-eth0 ip address 10.0.3.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r3-eth1 ip address 10.0.30.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt index f6f861b..b1701fe 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt @@ -1,8 +1,10 @@ O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r3-eth0, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX O 10.0.30.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.30.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.30.3/32 is directly connected, r3-eth1, XX:XX:XX O>* 10.0.40.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf index 79d8077..721c3d9 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf @@ -5,9 +5,13 @@ log file /tmp/r4-frr.log ! interface r4-eth0 ip address 10.0.4.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! interface r4-eth1 ip address 10.0.40.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! ip forwarding ! diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt index b6be5e7..3723a8a 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt @@ -1,7 +1,9 @@ O>* 10.0.3.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX O 10.0.4.0/24 [110/10] is directly connected, r4-eth0, weight 1, XX:XX:XX C>* 10.0.4.0/24 is directly connected, r4-eth0, XX:XX:XX +L>* 10.0.4.4/32 is directly connected, r4-eth0, XX:XX:XX O>* 10.0.30.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX O 10.0.40.0/24 [110/10] is directly connected, r4-eth1, weight 1, XX:XX:XX C>* 10.0.40.0/24 is directly connected, r4-eth1, XX:XX:XX +L>* 10.0.40.4/32 is directly connected, r4-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r1/ospfd.conf b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf index e1e2bfb..ba13146 100644 --- a/tests/topotests/ospf_netns_vrf/r1/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf @@ -3,6 +3,14 @@ hostname r1 password zebra log file /tmp/r1-ospfd.log ! +interface r1-eth0 vrf r1-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r1-eth1 vrf r1-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! router ospf vrf r1-ospf-cust1 ospf router-id 10.0.255.1 redistribute kernel diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt index 979af20..bf874ac 100644 --- a/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt @@ -1,8 +1,10 @@ VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX +L>* 10.0.3.2/32 is directly connected, r1-eth1, XX:XX:XX O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt index ec99fad..e515205 100644 --- a/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt @@ -1,7 +1,9 @@ VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX +L>* 10.0.1.1/32 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX +L>* 10.0.3.2/32 is directly connected, r1-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r2/ospfd.conf b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf index c198427..01b6b15 100644 --- a/tests/topotests/ospf_netns_vrf/r2/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf @@ -3,6 +3,13 @@ hostname r2 password zebra log file /tmp/r2-ospfd.log ! +interface r2-eth0 vrf r2-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r2-eth1 vrf r2-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 ! router ospf vrf r2-ospf-cust1 ospf router-id 10.0.255.2 diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt index df66e92..3763ef8 100644 --- a/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt @@ -2,7 +2,9 @@ VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.1/32 is directly connected, r2-eth0, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r2-eth1, XX:XX:XX O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r2-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt index 4afc354..f6eaba6 100644 --- a/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt @@ -2,6 +2,8 @@ VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX +L>* 10.0.2.1/32 is directly connected, r2-eth0, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX +L>* 10.0.3.3/32 is directly connected, r2-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r3/ospfd.conf b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf index b73d547..abfaa5b 100644 --- a/tests/topotests/ospf_netns_vrf/r3/ospfd.conf +++ b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf @@ -4,6 +4,14 @@ password zebra log file /tmp/r3-ospfd.log ! ! +interface r3-eth0 vrf r3-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! +interface r3-eth1 vrf r3-ospf-cust1 + ip ospf hello-interval 1 + ip ospf dead-interval 4 +! router ospf vrf r3-ospf-cust1 ospf router-id 10.0.255.3 redistribute kernel diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt index b435c2e..5eb92ef 100644 --- a/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt +++ b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt @@ -3,6 +3,8 @@ O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, weight 1, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX +L>* 10.0.3.1/32 is directly connected, r3-eth0, XX:XX:XX O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.10.1/32 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt index f30a4be..26cc196 100644 --- a/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt +++ b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt @@ -1,4 +1,5 @@ VRF r3-ospf-cust1: O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX +L>* 10.0.10.1/32 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py index 2716f63..23eef8f 100644 --- a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py +++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py @@ -87,6 +87,7 @@ def setup_module(mod): router.net.set_intf_netns(rname + "-eth0", ns, up=True) router.net.set_intf_netns(rname + "-eth1", ns, up=True) + router.load_config(TopoRouter.RD_MGMTD, None, "--vrfwnetns") router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), diff --git a/tests/topotests/ospf_suppress_fa/r1/initial.json b/tests/topotests/ospf_suppress_fa/r1/initial.json new file mode 100644 index 0000000..f0e6110 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/initial.json @@ -0,0 +1,44 @@ +{ + "routerId":"10.0.12.1", + "asExternalLinkStates":[ + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.1.1", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.2.2", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.3.3", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"10.0.23.3", + "externalRouteTag":0 + } + ] +} diff --git a/tests/topotests/ospf_suppress_fa/r1/neighbor.json b/tests/topotests/ospf_suppress_fa/r1/neighbor.json new file mode 100644 index 0000000..417fcbe --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/neighbor.json @@ -0,0 +1,13 @@ +{ + "neighbors":{ + "10.0.23.2":[ + { + "nbrPriority":1, + "converged":"Full", + "role":"DROther", + "ifaceAddress":"10.0.12.2", + "ifaceName":"r1-eth0:10.0.12.1" + } + ] + } +} diff --git a/tests/topotests/ospf_suppress_fa/r1/post.json b/tests/topotests/ospf_suppress_fa/r1/post.json new file mode 100644 index 0000000..98cbf8f --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/post.json @@ -0,0 +1,44 @@ +{ + "routerId":"10.0.12.1", + "asExternalLinkStates":[ + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.1.1", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.2.2", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + }, + { + "options":"*|-|-|-|-|-|E|-", + "lsaFlags":6, + "lsaType":"AS-external-LSA", + "linkStateId":"3.3.3.3", + "advertisingRouter":"10.0.23.2", + "networkMask":32, + "metricType":"E2 (Larger than any link state path)", + "tos":0, + "metric":20, + "forwardAddress":"0.0.0.0", + "externalRouteTag":0 + } + ] +} 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 d5583ac..8b3fc58 100644 --- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -22,6 +22,8 @@ test_ospf_unset_suppress_fa() import os import sys +import json +from functools import partial import re import pytest @@ -33,6 +35,7 @@ sys.path.append(os.path.join(CWD, "../")) # 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. @@ -75,6 +78,7 @@ def setup_module(mod): TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) + logger.info("Module Setup") tgen.start_router() @@ -93,7 +97,17 @@ def test_converge_protocols(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - topotest.sleep(10, "Waiting for OSPF convergence") + router = tgen.gears["r1"] + json_file = "{}/r1/neighbor.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show ip ospf neighbor json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r1 has not converged" + + logger.info("Converged Protocol") def ospf_configure_suppress_fa(router_name, area): @@ -114,58 +128,55 @@ def ospf_unconfigure_suppress_fa(router_name, area): router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area)) -def ospf_get_lsa_type5(router_name): - "Return a dict with link state id as key and forwarding addresses as value" - - result = dict() - tgen = get_topogen() - router = tgen.gears[router_name] - cmd = "show ip ospf database external\n" - output = topotest.normalize_text(router.vtysh_cmd(cmd)) - for line in output.splitlines(): - re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line) - if re0: - lsa = re0.group(1) - re1 = re.match(r"\s+Forward Address: (\S+)", line) - if re1: - result[lsa] = re1.group(1) - return result - - -@pytest.fixture(scope="module", name="original") def test_ospf_set_suppress_fa(): "Test OSPF area [x] nssa suppress-fa" + logger.info("Testing Turning on/off suppress-fa") + tgen = get_topogen() + # Get current forwarding address for each LSA type-5 in r1 - initial = ospf_get_lsa_type5("r1") + logger.info("Get Initial State") + router = tgen.gears["r1"] + json_file = "{}/r1/initial.json".format(CWD) + expected_initial = json.loads(open(json_file).read()) + + test_func_initial = partial( + topotest.router_json_cmp, + router, + "show ip ospf data external json", + expected_initial, + ) + _, result = topotest.run_and_expect(test_func_initial, None, count=30, wait=1) + assert result is None, "Unable to get expected initial states" + logger.info("Configure suppress-fa") # Configure suppres-fa in r2 area 1 ospf_configure_suppress_fa("r2", "1") - topotest.sleep(10, "Waiting for OSPF convergence") - # Check forwarding address on r1 for all statics is 0.0.0.0 - assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa" - suppress = ospf_get_lsa_type5("r1") - for prefix in suppress: - assert suppress[prefix] == "0.0.0.0", assertmsg + logger.info("Ensure that OSPF has converged on new values") + json_file = "{}/r1/post.json".format(CWD) + expected_post = json.loads(open(json_file).read()) - # Return the original forwarding addresses so we can compare them - # in the test_ospf_unset_supress_fa - return initial + test_func_post = partial( + topotest.router_json_cmp, + router, + "show ip ospf data external json", + expected_post, + ) + _, result = topotest.run_and_expect(test_func_post, None, count=30, wait=1) + assert result is None, "Unable to get expected state after turning on suppress-fa" -def test_ospf_unset_supress_fa(original): - "Test OSPF no area [x] nssa suppress-fa" + logger.info("Test OSPF no area [x] nssa suppress-fa") # Remove suppress-fa in r2 area 1 ospf_unconfigure_suppress_fa("r2", "1") - topotest.sleep(10, "Waiting for OSPF convergence") - # Check forwarding address is the original value on r1 for all statics - assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa" - restore = ospf_get_lsa_type5("r1") - for prefix in restore: - assert restore[prefix] == original[prefix], assertmsg + logger.info("Has OSPF returned to original values") + _, result = topotest.run_and_expect(test_func_post, None, count=30, wait=1) + assert ( + result is None + ), "Unable to return to original state after turning off suppress-fa" if __name__ == "__main__": diff --git a/tests/topotests/ospf_te_topo1/r1/ospfd.conf b/tests/topotests/ospf_te_topo1/r1/ospfd.conf index 312dd26..1541f3f 100644 --- a/tests/topotests/ospf_te_topo1/r1/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r1/ospfd.conf @@ -2,13 +2,13 @@ interface lo ip ospf area 0.0.0.0 ! -interface r1-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r1-eth1 +interface eth1 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r1/zebra.conf b/tests/topotests/ospf_te_topo1/r1/zebra.conf index c47789a..dc50e3d 100644 --- a/tests/topotests/ospf_te_topo1/r1/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r1/zebra.conf @@ -2,7 +2,7 @@ interface lo ip address 10.0.255.1/32 ! -interface r1-eth0 +interface eth0 ip address 10.0.0.1/24 link-params metric 20 @@ -12,7 +12,7 @@ interface r1-eth0 enable exit-link-params ! -interface r1-eth1 +interface eth1 ip address 10.0.1.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/r2/ospfd.conf b/tests/topotests/ospf_te_topo1/r2/ospfd.conf index e9c3f65..acc2e6b 100644 --- a/tests/topotests/ospf_te_topo1/r2/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r2/ospfd.conf @@ -2,25 +2,25 @@ interface lo ip ospf area 0.0.0.0 ! -interface r2-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r2-eth1 +interface eth1 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! -interface r2-eth2 +interface eth2 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 ip ospf dead-interval 10 ! -interface r2-eth3 +interface eth3 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r2/zebra.conf b/tests/topotests/ospf_te_topo1/r2/zebra.conf index a9771f7..6fe4dd0 100644 --- a/tests/topotests/ospf_te_topo1/r2/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r2/zebra.conf @@ -2,25 +2,25 @@ interface lo ip address 10.0.255.2/32 ! -interface r2-eth0 +interface eth0 ip address 10.0.0.2/24 link-params enable exit-link-params ! -interface r2-eth1 +interface eth1 ip address 10.0.1.2/24 link-params enable exit-link-params ! -interface r2-eth2 +interface eth2 ip address 10.0.3.2/24 link-params enable exit-link-params ! -interface r2-eth3 +interface eth3 ip address 10.0.4.2/24 link-params metric 30 diff --git a/tests/topotests/ospf_te_topo1/r3/ospfd.conf b/tests/topotests/ospf_te_topo1/r3/ospfd.conf index caa5f1e..fc94437 100644 --- a/tests/topotests/ospf_te_topo1/r3/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r3/ospfd.conf @@ -2,13 +2,13 @@ interface lo ip ospf area 0.0.0.0 ! -interface r3-eth0 +interface eth0 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 ip ospf dead-interval 10 ! -interface r3-eth1 +interface eth1 ip ospf network point-to-point ip ospf area 0.0.0.0 ip ospf hello-interval 2 diff --git a/tests/topotests/ospf_te_topo1/r3/zebra.conf b/tests/topotests/ospf_te_topo1/r3/zebra.conf index 4cf9077..262b5ad 100644 --- a/tests/topotests/ospf_te_topo1/r3/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r3/zebra.conf @@ -2,14 +2,14 @@ interface lo ip address 10.0.255.3/32 ! -interface r3-eth0 +interface eth0 ip address 10.0.3.1/24 link-params enable admin-grp 0x20 exit-link-params ! -interface r3-eth1 +interface eth1 ip address 10.0.5.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/r4/ospfd.conf b/tests/topotests/ospf_te_topo1/r4/ospfd.conf index cd50801..5ec7918 100644 --- a/tests/topotests/ospf_te_topo1/r4/ospfd.conf +++ b/tests/topotests/ospf_te_topo1/r4/ospfd.conf @@ -2,7 +2,7 @@ interface lo ip ospf area 0.0.0.0 ! -interface r4-eth0 +interface eth0 ip ospf network point-to-point ip ospf hello-interval 2 ip ospf dead-interval 10 diff --git a/tests/topotests/ospf_te_topo1/r4/zebra.conf b/tests/topotests/ospf_te_topo1/r4/zebra.conf index 18c003b..c482685 100644 --- a/tests/topotests/ospf_te_topo1/r4/zebra.conf +++ b/tests/topotests/ospf_te_topo1/r4/zebra.conf @@ -2,7 +2,7 @@ interface lo ip address 10.0.255.4/32 ! -interface r4-eth0 +interface eth0 ip address 10.0.4.1/24 link-params enable diff --git a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py index c8533cf..11fecc3 100644 --- a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py +++ b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py @@ -18,22 +18,22 @@ test_ospf_te_topo1.py: Test the FRR OSPF with Traffic Engineering. | 10.0.225.1 | | | +------------+ - r1-eth0| |r1-eth1 + eth0| |eth1 | | 10.0.0.0/24| |10.0.1.0/24 | | - r2-eth0| |r2-eth1 + eth0| |eth1 +------------+ +------------+ | | | | - | R2 |r2-eth2 r3-eth0| R3 | + | R2 |eth2 eth0| R3 | | 10.0.255.2 +------------------+ 10.0.255.3 | | | 10.0.3.0/24 | | +------------+ +------+-----+ - r2-eth3| r3-eth1| + eth3| eth1| | | 10.0.4.0/24| 10.0.5.0/24| | | - r4-eth0| V + eth0| V +------------+ ASBR 10.0.255.5 | | | R4 | @@ -70,30 +70,24 @@ def build_topo(tgen): "Build function" # Create 4 routers - for routern in range(1, 5): - tgen.add_router("r{}".format(routern)) + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + r3 = tgen.add_router("r3") + r4 = tgen.add_router("r4") # Interconect router 1 and 2 with 2 links - switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r1, r2, ifname1="eth0", ifname2="eth0") + tgen.add_link(r1, r2, ifname1="eth1", ifname2="eth1") # Interconect router 3 and 2 - switch = tgen.add_switch("s3") - switch.add_link(tgen.gears["r3"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r2, r3, ifname1="eth2", ifname2="eth0") # Interconect router 4 and 2 - switch = tgen.add_switch("s4") - switch.add_link(tgen.gears["r4"]) - switch.add_link(tgen.gears["r2"]) + tgen.add_link(r2, r4, ifname1="eth3", ifname2="eth0") # Interconnect router 3 with next AS - switch = tgen.add_switch("s5") - switch.add_link(tgen.gears["r3"]) + s1 = tgen.add_switch("s1") + tgen.add_link(r3, s1, ifname1="eth1", ifname2="eth0") def setup_module(mod): @@ -174,8 +168,7 @@ def test_step2(): tgen = setup_testcase("Step2: Shutdown interface between r1 & r2") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"') - tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"') + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface eth1" -c "shutdown"') for rname in ["r1", "r2", "r3", "r4"]: compare_ted_json_output(tgen, rname, "ted_step2.json") @@ -227,28 +220,27 @@ def test_step5(): tgen = setup_testcase("Step5: Re-enable interface between r1 & r2") - tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"') - tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"') + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface eth1" -c "no shutdown"') for rname in ["r1", "r2", "r3", "r4"]: compare_ted_json_output(tgen, rname, "ted_step5.json") def test_step6(): - "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \ - for interface r2-eth3 on r2 and verify that corresponding Edges are \ + "Step6: Set delay and jitter for interface eth0 on r4, remove use-bw \ + for interface eth3 on r2 and verify that corresponding Edges are \ updated in the TED on all routers" tgen = setup_testcase("Step6: Modify link parameters on r2 & r4") tgen.net["r2"].cmd( - 'vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"' + 'vtysh -c "conf t" -c "interface eth3" -c "link-params" -c "no use-bw"' ) tgen.net["r4"].cmd( - 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"' + 'vtysh -c "conf t" -c "interface eth0" -c "link-params" -c "delay 20000"' ) tgen.net["r4"].cmd( - 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"' + 'vtysh -c "conf t" -c "interface eth0" -c "link-params" -c "delay-variation 10000"' ) for rname in ["r1", "r2", "r3", "r4"]: diff --git a/tests/topotests/ospf_topo1/r1/ospf6d.conf b/tests/topotests/ospf_topo1/r1/ospf6d.conf index ca3497b..0e6c7da 100644 --- a/tests/topotests/ospf_topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r1/ospf6d.conf @@ -4,10 +4,12 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r1-eth0 area 0.0.0.0 - interface r1-eth1 area 0.0.0.0 +! +interface r1-eth0 + ipv6 ospf6 area 0.0.0.0 ! int r1-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 ! diff --git a/tests/topotests/ospf_topo1/r2/ospf6d.conf b/tests/topotests/ospf_topo1/r2/ospf6d.conf index 44047e1..f6a1f50 100644 --- a/tests/topotests/ospf_topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r2/ospf6d.conf @@ -4,14 +4,14 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r2-eth0 area 0.0.0.0 - interface r2-eth1 area 0.0.0.0 ! int r2-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r2-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_topo1/r3/ospf6d.conf b/tests/topotests/ospf_topo1/r3/ospf6d.conf index 13ad9a7..278a016 100644 --- a/tests/topotests/ospf_topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r3/ospf6d.conf @@ -4,19 +4,19 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r3-eth0 area 0.0.0.0 - interface r3-eth1 area 0.0.0.0 - interface r3-eth2 area 0.0.0.1 ! int r3-eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r3-eth1 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r3-eth2 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_topo1/r4/ospf6d.conf b/tests/topotests/ospf_topo1/r4/ospf6d.conf index f9bde0e..777dd0b 100644 --- a/tests/topotests/ospf_topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf_topo1/r4/ospf6d.conf @@ -4,14 +4,14 @@ router ospf6 redistribute kernel redistribute connected redistribute static - interface r4-eth0 area 0.0.0.1 - interface r4-eth1 area 0.0.0.1 ! int r4-eth0 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! int r4-eth1 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md b/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md new file mode 100644 index 0000000..d9b6817 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/README.md @@ -0,0 +1,41 @@ +# OSPFv2 (IPv4) Topology Test Unumbered (point-to-multipoint over Ethernet) + +## Topology + + SW1 SW2 + \___________________/ \___________________/ + | | + | | + | eth0:10.0.10.1/32 | eth0:10.0.20.1/32 + +---------+---------+ +---------+---------+ + | R1 | | R2 | + | FRRouting | | FRRouting | + | RID: 10.0.255.1 | | RID: 10.0.255.2 | + +---------+---------+ +---------+---------+ + | eth1:10.1.1.2/32 | eth1:10.1.2.2/32 + \______ ___________/ + \ / + \ / + ~~~~~~~~~~~~~~~~~~ + ~~ SW4 ~~ + ~~ Switch ~~ + ~~ ~~ + ~~~~~~~~~~~~~~~~~~ + | + | eth0:10.1.3.2/32 (unumbered) + +---------+---------+ + | R3 | + | FRRouting | + | RID: 10.0.255.3 | + +---------+---------+ + | eth0:10.0.30.1/24 + | + ~~~~~~~~~~~~~~~~~~ + ~~ SW3 ~~ + ~~ Switch ~~ + ~~ ~~ + ~~~~~~~~~~~~~~~~~~ + +## FRR Configuration + +See full config from r1 / r2 / r3 subdirectories diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json new file mode 100644 index 0000000..563522d --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf new file mode 100644 index 0000000..c7327b6 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r1-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.1.2 +! +router ospf + ospf router-id 10.0.255.1 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json new file mode 100644 index 0000000..4395b74 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/v4_route.json @@ -0,0 +1,122 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.1.2\/32": [ + { + "prefix": "10.1.1.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.1.2\/32", + "prefixLen": 32, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf new file mode 100644 index 0000000..63c75d0 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 10.0.10.1/32 +! +interface r1-eth1 + ip address 10.1.1.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json new file mode 100644 index 0000000..8d87e33 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf new file mode 100644 index 0000000..5d0439f --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r2-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.2.2 +! +router ospf + ospf router-id 10.0.255.2 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json new file mode 100644 index 0000000..870b106 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/v4_route.json @@ -0,0 +1,122 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.2.2\/32": [ + { + "prefix": "10.1.2.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.2.2\/32", + "prefixLen": 32, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf new file mode 100644 index 0000000..60ff53b --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface r2-eth0 + ip address 10.0.20.1/32 +! +interface r2-eth1 + ip address 10.1.2.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json new file mode 100644 index 0000000..44ffe64 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospf-route.json @@ -0,0 +1 @@ +{"10.0.10.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.20.1\/32":{"routeType":"N","transit":false,"cost":20,"area":"0.0.0.0"},"10.0.30.0\/24":{"routeType":"N","transit":false,"cost":10,"area":"0.0.0.0"}} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf new file mode 100644 index 0000000..579e273 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r3-eth1 + ip ospf network point-to-multipoint + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf prefix-suppress 10.1.3.2 +! +router ospf + ospf router-id 10.0.255.3 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json new file mode 100644 index 0000000..61d9555 --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/v4_route.json @@ -0,0 +1,126 @@ +{ + "10.0.10.1\/32": [ + { + "prefix": "10.0.10.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.20.1\/32": [ + { + "prefix": "10.0.20.1\/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 110, + "metric": 20, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.0\/24": [ + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfId": 0, + "vrfName": "default", + "distance": 110, + "metric": 10, + "table": 254, + "internalStatus": 0, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.0.30.0\/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.0.30.1\/32": [ + { + "prefix": "10.0.30.1\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ], + "10.1.3.2\/32": [ + { + "prefix": "10.1.3.2\/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 0, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + }, + { + "prefix": "10.1.3.2\/32", + "prefixLen": 32, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1 + } + ] +} diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf new file mode 100644 index 0000000..327bb8f --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface r3-eth0 + ip address 10.0.30.1/24 +! +interface r3-eth1 + ip address 10.1.3.2/32 +! diff --git a/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py b/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py new file mode 100644 index 0000000..c0dd85c --- /dev/null +++ b/tests/topotests/ospf_unnumbered_point_to_multipoint/test_ospf_unnumbered_point_to_multipoint.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_unnumbered_point_to_multipoint.py +# +# Copyright (c) 2024 by +# Vincent Jardin +# + +""" +test_ospf_unnumbered_point_to_multipoint.py: Test the OSPF unnumbered for routers with point to multipoint over Ethernet +""" + +import os +import sys +from functools import partial +import pytest +import json + +# 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. + +pytestmark = [pytest.mark.ospfd] + + +CWD = os.path.dirname(os.path.realpath(__file__)) + + +def build_topo(tgen): + "Build function" + + # Create routers + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + # Create a empty network for router 2 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + + # Create a empty network for router 3 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + + # Interconect router 1, 2 and r3 to a common switch 4 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + 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)) + ) + + # The multicast packet delivery is somewhat controlled by + # the rp_filter. Setting it to '0' allows the kernel to pass + # up the mcast packet not destined for the local routers + # network. + topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.r1-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.all.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.r2-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.all.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r3"], "net.ipv4.conf.r3-eth1.rp_filter", 0) + topotest.sysctl_assure(tgen.net["r3"], "net.ipv4.conf.all.rp_filter", 0) + + # Initialize all routers. + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test OSPF daemon convergence and that we have received the ospf routes" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + json_file = "{}/{}/ospf-route.json".format(CWD, router) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, rnode, "show ip ospf route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_ospf_kernel_route(): + "Test OSPF kernel route installation and we have the onlink success" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + rlist = tgen.routers().values() + for router in rlist: + logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name) + + json_file = "{}/{}/v4_route.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mistmatches'.format(router) + assert result is None, assertmsg + # tgen.mininet_cli() + + +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/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index 7a7ea85..49dd34d 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -277,7 +277,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "230.0.0.2", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "00000202", + "opaqueValues": { + "opaqueData": "00000202" + } }, ], } @@ -327,7 +329,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "231.0.0.1", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "00010101", + "opaqueValues": { + "opaqueData": "00010101", + } }, ], } @@ -376,7 +380,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "232.0.0.3", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "opaqueData": "deadbeaf01234567", + "opaqueValues": { + "opaqueData": "deadbeaf01234567", + } }, ] } @@ -427,7 +433,9 @@ def _test_add_data(tgen, apibin): "linkStateId": "232.0.0.3", "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "opaqueData": "ebadf00d", + "opaqueValues": { + "opaqueData": "ebadf00d", + } }, ] } @@ -574,7 +582,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -583,7 +591,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -599,7 +607,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "231.0.0.2", @@ -607,7 +615,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "7690", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -621,7 +629,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -629,7 +637,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -734,7 +742,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -744,7 +752,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -760,7 +768,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "lsaAge": 3600, @@ -770,7 +778,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "4fe2", # data removed "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, ], }, @@ -785,7 +793,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -793,7 +801,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -827,7 +835,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "76bf", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "230.0.0.2", @@ -837,7 +845,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "8aa2", "length": 24, "opaqueId": 2, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ] } @@ -854,7 +862,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5bd8", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "lsaAge": 3600, @@ -864,7 +872,7 @@ def _test_opaque_add_del(tgen, apibin): "checksum": "4fe2", # data removed "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, ], }, @@ -879,7 +887,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "5ed5", "length": 20, - "opaqueDataLength": 0, + "opaqueLength": 0, }, { "linkStateId": "232.0.0.2", @@ -888,7 +896,7 @@ def _test_opaque_add_del(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "d9bd", "length": 24, - "opaqueDataLength": 4, + "opaqueLength": 4, }, ], }, @@ -1044,7 +1052,7 @@ def _test_opaque_add_restart_add(tgen, apibin): "lsaSeqNumber": "80000001", "checksum": "b07a", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -1100,7 +1108,7 @@ def _test_opaque_add_restart_add(tgen, apibin): "lsaSeqNumber": "80000003", "checksum": "cb27", "length": 28, - "opaqueDataLength": 8, + "opaqueLength": 8, }, ], }, @@ -1205,7 +1213,10 @@ def _test_opaque_interface_disable(tgen, apibin): } } test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf interface json", + r1_interface_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1232,7 +1243,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor has opaque option in optionsList" @@ -1241,7 +1255,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor has opaque option in optionsList" @@ -1282,7 +1299,10 @@ def _test_opaque_interface_disable(tgen, apibin): } } test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF interface has opaque capability disabled" @@ -1338,19 +1358,17 @@ def _test_opaque_interface_disable(tgen, apibin): "asExternalOpaqueLsaCount": 1, } opaque_area_empty_database = { - "routerId":"2.0.0.0", - "areaLocalOpaqueLsa":{ - "areas":{ - "1.2.3.4":[ - ] - } - } + "routerId": "2.0.0.0", + "areaLocalOpaqueLsa": {"areas": {"1.2.3.4": []}}, } # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests step("Check that LSAs are added on r1") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r1, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF database doesn't contain opaque LSAs" @@ -1359,8 +1377,11 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests step("Check that LSAs are not added on r2") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", - opaque_area_empty_database, True + topotest.router_json_cmp, + r2, + "show ip ospf database opaque-area json", + opaque_area_empty_database, + True, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF area database contains opaque LSAs" @@ -1382,7 +1403,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests step("Verify the ospf opaque option is applied to the r1 interface") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf interface json", + r1_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1409,7 +1433,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests step("Verify that the r1 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" @@ -1418,7 +1445,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests step("Verify that the r2 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" @@ -1427,7 +1457,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests step("Check that LSAs are now added to r2") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r2, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF database doesn't contains opaque LSAs" @@ -1463,7 +1496,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests step("Verify the ospf opaque option is not applied to the r2 interface") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" @@ -1472,7 +1508,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests step("Verify that the r1 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor has opaque option in optionsList" @@ -1481,7 +1520,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests step("Verify that the r2 neighbor options don't include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_without_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor has opaque option in optionsList" @@ -1490,7 +1532,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests step("Verify that r1 still has the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r1, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF database doesn't contain opaque LSAs" @@ -1499,8 +1544,11 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests step("Verify that r2 doesn't have the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", - opaque_area_empty_database, True + topotest.router_json_cmp, + r2, + "show ip ospf database opaque-area json", + opaque_area_empty_database, + True, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF area database contains opaque LSAs" @@ -1524,7 +1572,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests step("Verify the ospf opaque option is applied to the r2 interface") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf interface json", + r2_interface_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF interface doesn't have opaque capability disabled" @@ -1533,7 +1584,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests step("Verify that the r2 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + topotest.router_json_cmp, + r2, + "show ip ospf neighbor detail json", + r2_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" @@ -1542,7 +1596,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests step("Verify that the r1 neighbor options include opaque") test_func = partial( - topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + topotest.router_json_cmp, + r1, + "show ip ospf neighbor detail json", + r1_neighbor_with_opaque, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" @@ -1551,7 +1608,10 @@ def _test_opaque_interface_disable(tgen, apibin): # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests step("Verify that r2 now has the opaque LSAs") test_func = partial( - topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + topotest.router_json_cmp, + r2, + "show ip ospf database json", + opaque_LSAs_in_database, ) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2 OSPF database doesn't contain opaque LSAs" @@ -1581,6 +1641,93 @@ def test_ospf_opaque_interface_disable(tgen): _test_opaque_interface_disable(tgen, apibin) +def _test_opaque_link_local_lsa_crash(tgen, apibin): + "Test disabling opaque capability on an interface" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + tc_name = "opaque_interface_disable" + + p = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+") + try: + step("Add a link-local opaque LSA for r1-eth0") + pread = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,1,feedaceedeadbeef"]) + + input_dict = { + "linkLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "230.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000001", + "opaqueValues": { + "opaqueData": "feedaceedeadbeef", + } + }, + ], + } + }, + } + + # verify content + json_cmd = "show ip ospf da opaque-link json" + assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + + step("Shut down r1-eth0 and verify there is no crash") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nshut") + time.sleep(2) + + step("Bring r1-eth0 back up and verify there is no crash") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno shut") + + step("Add another link-local opaque LSA for r1-eth0") + pread = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,1,feedaceecafebeef"]) + + input_dict = { + "linkLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "230.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000001", + "opaqueValues": { + "opaqueData": "feedaceecafebeef", + } + }, + ], + } + }, + } + # verify content + json_cmd = "show ip ospf da opaque-link json" + assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + + except Exception: + if p: + p.terminate() + if p.wait(): + comm_error(p) + p = None + raise + finally: + if p: + p.terminate() + p.wait() + p = None + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_link_local_lsa_crash(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_link_local_lsa_crash(tgen, apibin) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/pbr_topo1/r1/pbr-map.json b/tests/topotests/pbr_topo1/r1/pbr-map.json index bfa0ecb..aaf2b5e 100644 --- a/tests/topotests/pbr_topo1/r1/pbr-map.json +++ b/tests/topotests/pbr_topo1/r1/pbr-map.json @@ -18,7 +18,7 @@ { "sequenceNumber":10, "vrfUnchanged":false, - "installed":true, + "installed":false, "installedReason":"Invalid Src or Dst", "nexthopGroup":{ "name":"C", @@ -98,7 +98,7 @@ { "sequenceNumber":5, "vrfUnchanged":false, - "installed":false, + "installed":true, "installedReason":"Invalid NH-group", "nexthopGroup":{ "name":"B", @@ -111,7 +111,7 @@ { "sequenceNumber":10, "vrfUnchanged":true, - "installed":false, + "installed":true, "installedReason":"Valid", "matchSrc":"1.2.0.0\/16", "matchDst":"3.4.5.0\/24" diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6c2d42e..98fcfbc 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,8 @@ # Skip pytests example directory [pytest] +# NEEDS_EXABGP_4_2_11_FRR + # asyncio_mode = auto # We always turn this on inside conftest.py, default shown @@ -43,6 +45,7 @@ markers = eigrpd: Tests that run against EIGRPD isisd: Tests that run against ISISD ldpd: Tests that run against LDPD + mgmtd: Tests that run against MGMTD nhrpd: Tests that run against NHRPD ospf6d: Tests that run against OSPF6D ospfd: Tests that run against OSPFD diff --git a/tests/topotests/route_scale/r1/installed.routes.json b/tests/topotests/route_scale/r1/installed.routes.json index 25d209f..6e09f52 100644 --- a/tests/topotests/route_scale/r1/installed.routes.json +++ b/tests/topotests/route_scale/r1/installed.routes.json @@ -11,6 +11,6 @@ "type":"sharp" } ], - "routesTotal":1000032, - "routesTotalFib":1000032 + "routesTotal":1000064, + "routesTotalFib":1000064 } diff --git a/tests/topotests/route_scale/r1/no.routes.json b/tests/topotests/route_scale/r1/no.routes.json index abebd1b..13dc767 100644 --- a/tests/topotests/route_scale/r1/no.routes.json +++ b/tests/topotests/route_scale/r1/no.routes.json @@ -6,6 +6,6 @@ "type":"connected" } ], - "routesTotal":32, - "routesTotalFib":32 + "routesTotal":64, + "routesTotalFib":64 } diff --git a/tests/topotests/srv6_encap_src_addr/__init__.py b/tests/topotests/srv6_encap_src_addr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json new file mode 100644 index 0000000..431027f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "fc00:0:1::1" + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json new file mode 100644 index 0000000..431027f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_set.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "fc00:0:1::1" + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json new file mode 100644 index 0000000..18b317f --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/expected_srv6_encap_src_addr_unset.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "encapsulation":{ + "sourceAddress": { + "configured": "::" + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/srv6_encap_src_addr/r1/setup.sh b/tests/topotests/srv6_encap_src_addr/r1/setup.sh new file mode 100644 index 0000000..36ed713 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add dummy0 type dummy +ip link set dummy0 up diff --git a/tests/topotests/srv6_encap_src_addr/r1/zebra.conf b/tests/topotests/srv6_encap_src_addr/r1/zebra.conf new file mode 100644 index 0000000..c570756 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/r1/zebra.conf @@ -0,0 +1,17 @@ +hostname r1 +! +! debug zebra events +! debug zebra rib detailed +! +log stdout notifications +log monitor notifications +log commands +log file zebra.log debugging +! +segment-routing + srv6 + encapsulation + source-address fc00:0:1::1 + ! + ! +! diff --git a/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py b/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py new file mode 100755 index 0000000..4239193 --- /dev/null +++ b/tests/topotests/srv6_encap_src_addr/test_srv6_encap_src_addr.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_srv6_encap_src_addr.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by +# University of Rome Tor Vergata, Carmine Scarpitta +# + +""" +test_srv6_encap_src_addr.py: +Test for SRv6 encap source address on zebra +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_zebra_srv6_encap_src_addr(tgen): + "Test SRv6 encapsulation source address." + logger.info( + "Test SRv6 encapsulation source address." + ) + r1 = tgen.gears["r1"] + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr fc00:0:1::1\n" + + +def test_zebra_srv6_encap_src_addr_unset(tgen): + "Test SRv6 encapsulation source address unset." + logger.info( + "Test SRv6 encapsulation source address unset." + ) + r1 = tgen.gears["r1"] + + # Unset SRv6 encapsulation source address + r1.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + encapsulation + no source-address + """ + ) + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr_unset.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr ::\n" + + +def test_zebra_srv6_encap_src_addr_set(tgen): + "Test SRv6 encapsulation source address set." + logger.info( + "Test SRv6 encapsulation source address set." + ) + r1 = tgen.gears["r1"] + + # Set SRv6 encapsulation source address + r1.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + encapsulation + source-address fc00:0:1::1 + """ + ) + + # Generate expected results + json_file = "{}/r1/expected_srv6_encap_src_addr_set.json".format(CWD) + expected = json.loads(open(json_file).read()) + + ok = topotest.router_json_cmp_retry(r1, "show segment-routing srv6 manager json", expected) + assert ok, '"r1" JSON output mismatches' + + output = r1.cmd("ip sr tunsrc show") + assert output == "tunsrc addr fc00:0:1::1\n" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator/r1/sharpd.conf b/tests/topotests/srv6_locator/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator/r1/sharpd.conf +++ b/tests/topotests/srv6_locator/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator/r1/zebra.conf b/tests/topotests/srv6_locator/r1/zebra.conf index 85001d7..695cf0a 100644 --- a/tests/topotests/srv6_locator/r1/zebra.conf +++ b/tests/topotests/srv6_locator/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf +++ b/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf index 30a520b..96033b6 100644 --- a/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf +++ b/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py deleted file mode 100755 index 92980d3..0000000 --- a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python -# SPDX-License-Identifier: ISC - -# Copyright (c) 2022, University of Rome Tor Vergata -# Authored by Carmine Scarpitta -# - -""" -test_srv6_manager.py: -Test for SRv6 manager on zebra -""" - -import os -import sys -import json -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 - -pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] - - -def open_json_file(filename): - try: - with open(filename, "r") as f: - return json.load(f) - except IOError: - assert False, "Could not read file {}".format(filename) - - -def setup_module(mod): - tgen = Topogen({None: "r1"}, mod.__name__) - tgen.start_topology() - for rname, router in tgen.routers().items(): - router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) - ) - tgen.start_router() - - -def teardown_module(mod): - tgen = get_topogen() - tgen.stop_topology() - - -def test_srv6(): - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - router = tgen.gears["r1"] - - def _check_srv6_locator(router, expected_locator_file): - logger.info("checking zebra locator status") - output = json.loads(router.vtysh_cmd("show segment-routing srv6 locator json")) - expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) - return topotest.json_cmp(output, expected) - - def _check_sharpd_chunk(router, expected_chunk_file): - logger.info("checking sharpd locator chunk status") - output = json.loads(router.vtysh_cmd("show sharp segment-routing srv6 json")) - expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) - return topotest.json_cmp(output, expected) - - def check_srv6_locator(router, expected_file): - func = functools.partial(_check_srv6_locator, router, expected_file) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" - - def check_sharpd_chunk(router, expected_file): - func = functools.partial(_check_sharpd_chunk, router, expected_file) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" - - # FOR DEVELOPER: - # If you want to stop some specific line and start interactive shell, - # please use tgen.mininet_cli() to start it. - - logger.info("Test1 for Locator Configuration") - check_srv6_locator(router, "expected_locators1.json") - check_sharpd_chunk(router, "expected_chunks1.json") - - logger.info("Test2 get chunk for locator loc1") - router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1") - check_srv6_locator(router, "expected_locators2.json") - check_sharpd_chunk(router, "expected_chunks2.json") - - logger.info("Test3 release chunk for locator loc1") - router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1") - check_srv6_locator(router, "expected_locators3.json") - check_sharpd_chunk(router, "expected_chunks3.json") - - logger.info("Test4 additional locator loc3") - router.vtysh_cmd( - """ - configure terminal - segment-routing - srv6 - locators - locator loc3 - prefix 2001:db8:3::/48 block-len 32 node-len 16 func-bits 16 - """ - ) - check_srv6_locator(router, "expected_locators4.json") - check_sharpd_chunk(router, "expected_chunks4.json") - - logger.info("Test5 delete locator and chunk is released automatically") - router.vtysh_cmd( - """ - configure terminal - segment-routing - srv6 - locators - no locator loc1 - """ - ) - check_srv6_locator(router, "expected_locators5.json") - check_sharpd_chunk(router, "expected_chunks5.json") - - logger.info("Test6 delete srv6 all configuration") - router.vtysh_cmd( - """ - configure terminal - segment-routing - no srv6 - """ - ) - check_srv6_locator(router, "expected_locators6.json") - check_sharpd_chunk(router, "expected_chunks6.json") - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py new file mode 100755 index 0000000..92980d3 --- /dev/null +++ b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator_custom_bits_length.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022, University of Rome Tor Vergata +# Authored by Carmine Scarpitta +# + +""" +test_srv6_manager.py: +Test for SRv6 manager on zebra +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_srv6(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_locator(router, expected_locator_file): + logger.info("checking zebra locator status") + output = json.loads(router.vtysh_cmd("show segment-routing srv6 locator json")) + expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) + return topotest.json_cmp(output, expected) + + def _check_sharpd_chunk(router, expected_chunk_file): + logger.info("checking sharpd locator chunk status") + output = json.loads(router.vtysh_cmd("show sharp segment-routing srv6 json")) + expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_locator(router, expected_file): + func = functools.partial(_check_srv6_locator, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + def check_sharpd_chunk(router, expected_file): + func = functools.partial(_check_sharpd_chunk, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test1 for Locator Configuration") + check_srv6_locator(router, "expected_locators1.json") + check_sharpd_chunk(router, "expected_chunks1.json") + + logger.info("Test2 get chunk for locator loc1") + router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1") + check_srv6_locator(router, "expected_locators2.json") + check_sharpd_chunk(router, "expected_chunks2.json") + + logger.info("Test3 release chunk for locator loc1") + router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1") + check_srv6_locator(router, "expected_locators3.json") + check_sharpd_chunk(router, "expected_chunks3.json") + + logger.info("Test4 additional locator loc3") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc3 + prefix 2001:db8:3::/48 block-len 32 node-len 16 func-bits 16 + """ + ) + check_srv6_locator(router, "expected_locators4.json") + check_sharpd_chunk(router, "expected_chunks4.json") + + logger.info("Test5 delete locator and chunk is released automatically") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_srv6_locator(router, "expected_locators5.json") + check_sharpd_chunk(router, "expected_chunks5.json") + + logger.info("Test6 delete srv6 all configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators6.json") + check_sharpd_chunk(router, "expected_chunks6.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator_usid/r1/sharpd.conf b/tests/topotests/srv6_locator_usid/r1/sharpd.conf index d460859..371e6f6 100644 --- a/tests/topotests/srv6_locator_usid/r1/sharpd.conf +++ b/tests/topotests/srv6_locator_usid/r1/sharpd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file sharpd.log debugging ! diff --git a/tests/topotests/srv6_locator_usid/r1/zebra.conf b/tests/topotests/srv6_locator_usid/r1/zebra.conf index 190e831..1a02e28 100644 --- a/tests/topotests/srv6_locator_usid/r1/zebra.conf +++ b/tests/topotests/srv6_locator_usid/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/srv6_static_route/r1/staticd.conf b/tests/topotests/srv6_static_route/r1/staticd.conf index a75c69f..3c65a68 100644 --- a/tests/topotests/srv6_static_route/r1/staticd.conf +++ b/tests/topotests/srv6_static_route/r1/staticd.conf @@ -1,7 +1,6 @@ hostname r1 ! log stdout notifications -log monitor notifications log commands log file staticd.log debugging ! diff --git a/tests/topotests/srv6_static_route/r1/zebra.conf b/tests/topotests/srv6_static_route/r1/zebra.conf index cc70418..a596eeb 100644 --- a/tests/topotests/srv6_static_route/r1/zebra.conf +++ b/tests/topotests/srv6_static_route/r1/zebra.conf @@ -4,7 +4,6 @@ hostname r1 ! debug zebra rib detailed ! log stdout notifications -log monitor notifications log commands log file zebra.log debugging ! diff --git a/tests/topotests/static_simple/r1/mgmtd.conf b/tests/topotests/static_simple/r1/mgmtd.conf index 0f9f97c..dd5761a 100644 --- a/tests/topotests/static_simple/r1/mgmtd.conf +++ b/tests/topotests/static_simple/r1/mgmtd.conf @@ -1 +1,11 @@ log timestamp precision 3 + +! way too noisy +! debug northbound libyang + +debug northbound notifications +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend diff --git a/tests/topotests/static_simple/r1/zebra.conf b/tests/topotests/static_simple/r1/zebra.conf index ec82761..e3a4436 100644 --- a/tests/topotests/static_simple/r1/zebra.conf +++ b/tests/topotests/static_simple/r1/zebra.conf @@ -1,5 +1,15 @@ log timestamp precision 3 +! way too noisy +! debug northbound libyang + +debug northbound notifications +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client frontend +debug mgmt client backend + interface r1-eth0 ip address 101.0.0.1/24 ipv6 address 2101::1/64 diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py index fd87224..f862d81 100644 --- a/tests/topotests/static_simple/test_static_simple.py +++ b/tests/topotests/static_simple/test_static_simple.py @@ -40,6 +40,8 @@ def tgen(request): router.net.add_loop("lo-red") router.net.attach_iface_to_l3vrf("lo-red", "red") router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + # + # router.load_frr_config("frr.conf") # and select daemons to run router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") router.load_config(TopoRouter.RD_MGMTD) @@ -181,10 +183,11 @@ def guts(tgen, vrf, use_cli): r1 = tgen.routers()["r1"] - step("add via gateway", reset=True) - do_config(r1, 1, True, False, vrf=vrf, use_cli=use_cli) - step("remove via gateway") - do_config(r1, 1, False, False, vrf=vrf, use_cli=use_cli) + count = 10 + step(f"add {count} via gateway", reset=True) + do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli) + step(f"remove {count} via gateway") + do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli) via = f"lo-{vrf}" if vrf else "lo" step("add via loopback") diff --git a/tests/topotests/static_vrf/r1/frr.conf b/tests/topotests/static_vrf/r1/frr.conf new file mode 100644 index 0000000..bb373b9 --- /dev/null +++ b/tests/topotests/static_vrf/r1/frr.conf @@ -0,0 +1,18 @@ +interface r1-eth0 vrf red + ip address 192.0.2.1/23 +exit + +interface r1-eth1 vrf blue + ip address 192.0.2.129/24 +exit + +ip route 198.51.100.1/32 192.0.2.2 nexthop-vrf red +ip route 198.51.100.1/32 192.0.2.130 nexthop-vrf blue +ip route 198.51.100.2/32 r1-eth0 nexthop-vrf red +ip route 198.51.100.2/32 r1-eth1 nexthop-vrf blue + +ip route 203.0.113.1/32 192.0.2.130 vrf red nexthop-vrf blue +ip route 203.0.113.2/32 r1-eth1 vrf red nexthop-vrf blue + +ip route 203.0.113.129/32 192.0.2.2 vrf blue nexthop-vrf red +ip route 203.0.113.130/32 r1-eth0 vrf blue nexthop-vrf red diff --git a/tests/topotests/static_vrf/test_static_vrf.py b/tests/topotests/static_vrf/test_static_vrf.py new file mode 100644 index 0000000..97c0800 --- /dev/null +++ b/tests/topotests/static_vrf/test_static_vrf.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2024 NFWare Inc. +# +# noqa: E501 +# +""" +Test static route functionality +""" + +import ipaddress + +import pytest +from lib.topogen import Topogen +from lib.common_config import retry + +pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.attach_iface_to_l3vrf(rname + "-eth0", "red") + # Setup VRF blue + router.net.add_l3vrf("blue", 20) + router.net.attach_iface_to_l3vrf(rname + "-eth1", "blue") + # Load configuration + router.load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@retry(retry_timeout=1, initial_wait=0.1) +def check_kernel(r1, prefix, nexthops, vrf, expected_p=True, expected_nh=True): + vrfstr = f" vrf {vrf}" if vrf else "" + + net = ipaddress.ip_network(prefix) + if net.version == 6: + kernel = r1.run(f"ip -6 route show{vrfstr} {prefix}") + else: + kernel = r1.run(f"ip -4 route show{vrfstr} {prefix}") + + if expected_p: + assert prefix in kernel, f"Failed to find \n'{prefix}'\n in \n'{kernel:.1920}'" + else: + assert ( + prefix not in kernel + ), f"Failed found \n'{prefix}'\n in \n'{kernel:.1920}'" + + if not expected_p: + return + + for nh in nexthops: + if expected_nh: + assert f"{nh}" in kernel, f"Failed to find \n'{nh}'\n in \n'{kernel:.1920}'" + else: + assert ( + f"{nh}" not in kernel + ), f"Failed found \n'{nh}'\n in \n'{kernel:.1920}'" + + +def test_static_vrf(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Check initial configuration + check_kernel(r1, "198.51.100.1", ["192.0.2.2", "192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "203.0.113.1", ["192.0.2.130"], "red") + check_kernel(r1, "203.0.113.2", ["r1-eth1"], "red") + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue") + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue") + + # Delete VRF red + r1.net.del_iface("red") + + # Check that "red" nexthops are removed, "blue" nexthops are still there + check_kernel(r1, "198.51.100.1", ["192.0.2.2"], None, expected_nh=False) + check_kernel(r1, "198.51.100.1", ["192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0"], None, expected_nh=False) + check_kernel(r1, "198.51.100.2", ["r1-eth1"], None) + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue", expected_p=False) + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue", expected_p=False) + + # Delete VRF blue + r1.net.del_iface("blue") + + # Check that "blue" nexthops are removed + check_kernel(r1, "198.51.100.1", ["192.0.2.130"], None, expected_p=False) + check_kernel(r1, "198.51.100.2", ["r1-eth1"], None, expected_p=False) + + # Add VRF red back, attach "eth0" to it + r1.net.add_l3vrf("red", 10) + r1.net.attach_iface_to_l3vrf("r1-eth0", "red") + + # Check that "red" nexthops are restored + check_kernel(r1, "198.51.100.1", ["192.0.2.2"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0"], None) + + # Add VRF blue back, attach "eth1" to it + r1.net.add_l3vrf("blue", 20) + r1.net.attach_iface_to_l3vrf("r1-eth1", "blue") + + # Check that everything is restored + check_kernel(r1, "198.51.100.1", ["192.0.2.2", "192.0.2.130"], None) + check_kernel(r1, "198.51.100.2", ["r1-eth0", "r1-eth1"], None) + check_kernel(r1, "203.0.113.1", ["192.0.2.130"], "red") + check_kernel(r1, "203.0.113.2", ["r1-eth1"], "red") + check_kernel(r1, "203.0.113.129", ["192.0.2.2"], "blue") + check_kernel(r1, "203.0.113.130", ["r1-eth0"], "blue") diff --git a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py index 529520c..0b2937c 100644 --- a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py +++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py @@ -144,6 +144,23 @@ def test_zebra_system_recursion(): assert result is None, "Kernel route is missing from zebra" +def test_zebra_noprefix_connected(): + "Test that a noprefixroute created does not create a connected route" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + router.run("ip addr add 192.168.44.1/24 dev r1-eth1 noprefixroute") + expected = "% Network not in table" + test_func = partial( + topotest.router_output_cmp, router, "show ip route 192.168.44.0/24", expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=20, wait=1) + assert result, "Connected Route should not have been added" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_seg6_route/r1/zebra.conf b/tests/topotests/zebra_seg6_route/r1/zebra.conf index e5e360f..2d0c9e1 100644 --- a/tests/topotests/zebra_seg6_route/r1/zebra.conf +++ b/tests/topotests/zebra_seg6_route/r1/zebra.conf @@ -1,7 +1,6 @@ log file zebra.log ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet 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 4b462a5..f872f7a 100755 --- a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py +++ b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py @@ -59,11 +59,11 @@ def teardown_module(_mod): tgen.stop_topology() -def test_zebra_seg6local_routes(): +def test_zebra_seg6_routes(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info("Test for seg6local route install via ZAPI was start.") + logger.info("Test for seg6 route install via ZAPI was start.") r1 = tgen.gears["r1"] def check(router, dest, expected): diff --git a/tests/topotests/zebra_seg6local_route/r1/zebra.conf b/tests/topotests/zebra_seg6local_route/r1/zebra.conf index dee7a91..a3b9335 100644 --- a/tests/topotests/zebra_seg6local_route/r1/zebra.conf +++ b/tests/topotests/zebra_seg6local_route/r1/zebra.conf @@ -1,7 +1,6 @@ log file zebra.log ! log stdout notifications -log monitor notifications log commands ! ! debug zebra packet diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c index 9ad0bc4..9895c02 100644 --- a/tests/zebra/test_lm_plugin.c +++ b/tests/zebra/test_lm_plugin.c @@ -48,7 +48,7 @@ static int lm_get_chunk_pi(struct label_manager_chunk **lmc, uint32_t base, vrf_id_t vrf_id) { if (base == 0) - *lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size); + *lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size, true); else *lmc = assign_label_chunk(10, 55, 0, 1, size, base); diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index d007c1d..ecae0e9 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -547,7 +547,7 @@ our $Operators = qr{ our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; our $Iterators = qr{ - frr_each|frr_each_safe|frr_each_from| + darr_foreach_p|darr_foreach_i|frr_each|frr_each_safe|frr_each_from| frr_with_mutex|frr_with_privs| LIST_FOREACH|LIST_FOREACH_SAFE| SLIST_FOREACH|SLIST_FOREACH_SAFE|SLIST_FOREACH_PREVPTR| @@ -555,7 +555,7 @@ our $Iterators = qr{ TAILQ_FOREACH|TAILQ_FOREACH_SAFE|TAILQ_FOREACH_REVERSE|TAILQ_FOREACH_REVERSE_SAFE| RB_FOREACH|RB_FOREACH_SAFE|RB_FOREACH_REVERSE|RB_FOREACH_REVERSE_SAFE| SPLAY_FOREACH| - FOR_ALL_INTERFACES|FOR_ALL_INTERFACES_ADDRESSES|JSON_FOREACH| + FOR_ALL_INTERFACES|JSON_FOREACH| LY_FOR_KEYS|LY_LIST_FOR|LY_TREE_FOR|LY_TREE_DFS_BEGIN|LYD_TREE_DFS_BEGIN| RE_DEST_FOREACH_ROUTE|RE_DEST_FOREACH_ROUTE_SAFE| RNODE_FOREACH_RE|RNODE_FOREACH_RE_SAFE| @@ -563,6 +563,7 @@ our $Iterators = qr{ SUBGRP_FOREACH_PEER|SUBGRP_FOREACH_PEER_SAFE| SUBGRP_FOREACH_ADJ|SUBGRP_FOREACH_ADJ_SAFE| AF_FOREACH|FOREACH_AFI_SAFI|FOREACH_SAFI| + FOREACH_BE_CLIENT_BITS|FOREACH_MGMTD_BE_CLIENT_ID| LSDB_LOOP }x; diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh index 6071f48..bf63057 100755 --- a/tools/checkpatch.sh +++ b/tools/checkpatch.sh @@ -3,7 +3,8 @@ usage="./checkpatch.sh " patch=$1 tree=$2 -checkpatch="$tree/tools/checkpatch.pl --no-tree -f" +scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" +checkpatch="$scriptdir/checkpatch.pl --no-tree -f" ignore="ldpd\|babeld" cwd=${PWD##*/} dirty=0 diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index b7a1708..b3889e8 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -34,6 +34,8 @@ show bgp nexthop show bgp vrf all summary show bgp vrf all ipv4 show bgp vrf all ipv6 +show bgp vrf all ipv4 vpn +show bgp vrf all ipv6 vpn show bgp vrf all neighbors show bgp evpn route @@ -78,9 +80,9 @@ show vrf show work-queues show debugging hashtable show running-config -show thread cpu -show thread poll -show thread timers +show event cpu +show event poll +show event timers show daemons show version CMD_LIST_END @@ -175,7 +177,7 @@ CMD_LIST_END PROC_NAME:ospf6 CMD_LIST_START show ipv6 ospf6 vrf all -show ipv6 ospf6 vrfs +show ipv6 ospf6 vrfs show ipv6 ospf6 vrf all border-routers show ipv6 ospf6 vrf all border-routers detail show ipv6 ospf6 vrf all database diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c index 3a7222e..f366ba6 100644 --- a/tools/frr-llvm-cg.c +++ b/tools/frr-llvm-cg.c @@ -231,7 +231,7 @@ static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value, "%s: calls function pointer from unhandled const GEP\n", prefix); *hdr_written = true; - /* fallthru */ + fallthrough; default: /* to help the user / development */ if (!*hdr_written) { diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 3a478f6..73479c6 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -25,6 +25,7 @@ from collections import OrderedDict from ipaddress import IPv6Address, ip_network from pprint import pformat + # Python 3 def iteritems(d): return iter(d.items()) @@ -362,7 +363,7 @@ class Config(object): """ Return the parsed context as strings for display, log etc. """ - for (_, ctx) in sorted(iteritems(self.contexts)): + for _, ctx in sorted(iteritems(self.contexts)): print(str(ctx)) def save_contexts(self, key, lines): @@ -546,7 +547,6 @@ class Config(object): cur_ctx_lines = [] for line in self.lines: - if not line: continue @@ -647,7 +647,7 @@ def lines_to_config(ctx_keys, line, delete): ctx_keys = [] if line: - for (i, ctx_key) in enumerate(ctx_keys): + for i, ctx_key in enumerate(ctx_keys): cmd.append(" " * i + ctx_key) line = line.lstrip() @@ -725,7 +725,7 @@ def get_normalized_ipv6_line(line): def line_exist(lines, target_ctx_keys, target_line, exact_match=True): - for (ctx_keys, line) in lines: + for ctx_keys, line in lines: if ctx_keys == target_ctx_keys: if exact_match: if line == target_line: @@ -744,7 +744,7 @@ def bgp_delete_inst_move_line(lines_to_del): bgp_defult_inst = False bgp_vrf_inst = False - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: # Find bgp default inst if ( ctx_keys[0].startswith("router bgp") @@ -757,7 +757,7 @@ def bgp_delete_inst_move_line(lines_to_del): bgp_vrf_inst = True if bgp_defult_inst and bgp_vrf_inst: - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: # move bgp default inst to end if ( ctx_keys[0].startswith("router bgp") @@ -853,7 +853,6 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): def bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict): - # This method handles deletion of bgp neighbor configs, # if there is neighbor to peer-group cmd is in delete list. # As 'no neighbor .* peer-group' deletes the neighbor, @@ -861,7 +860,7 @@ def bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict): # in error. lines_to_del_to_del = [] - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ( ctx_keys[0].startswith("router bgp") and line @@ -876,7 +875,7 @@ def bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict): if re_nb: lines_to_del_to_del.append((ctx_keys, line)) - for (ctx_keys, line) in lines_to_del_to_del: + for ctx_keys, line in lines_to_del_to_del: lines_to_del.remove((ctx_keys, line)) @@ -942,7 +941,7 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del): # "router bgp 200 no neighbor uplink1 interface remote-as internal" # "router bgp 200 no neighbor underlay peer-group" - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ( ctx_keys[0].startswith("router bgp") and line @@ -999,13 +998,13 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del): bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict) return (lines_to_add, lines_to_del) - for (ctx_keys, line) in lines_to_del_to_app: + for ctx_keys, line in lines_to_del_to_app: lines_to_del.remove((ctx_keys, line)) lines_to_del.append((ctx_keys, line)) # {'router bgp 65001': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']}, # 'router bgp 65001 vrf vrf1': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']}} - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ( ctx_keys[0].startswith("router bgp") and line @@ -1023,7 +1022,7 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del): del_dict[ctx_keys[0]][pg_key].append(re_nbr_pg.group(1)) lines_to_del_to_app = [] - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ( ctx_keys[0].startswith("router bgp") and line @@ -1043,10 +1042,10 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del): if re_pg: lines_to_del_to_app.append((ctx_keys, line)) - for (ctx_keys, line) in lines_to_del_to_del: + for ctx_keys, line in lines_to_del_to_del: lines_to_del.remove((ctx_keys, line)) - for (ctx_keys, line) in lines_to_del_to_app: + for ctx_keys, line in lines_to_del_to_app: lines_to_del.remove((ctx_keys, line)) lines_to_del.append((ctx_keys, line)) @@ -1056,7 +1055,6 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del): def pim_delete_move_lines(lines_to_add, lines_to_del): - # Under interface context, if 'no ip pim' is present # remove subsequent 'no ip pim ' options as it # they are implicitly deleted by 'no ip pim'. @@ -1064,12 +1062,12 @@ def pim_delete_move_lines(lines_to_add, lines_to_del): # pending list. pim_disable = False - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ctx_keys[0].startswith("interface") and line and line == "ip pim": pim_disable = True if pim_disable: - for (ctx_keys, line) in lines_to_del: + for ctx_keys, line in lines_to_del: if ( ctx_keys[0].startswith("interface") and line @@ -1081,7 +1079,6 @@ def pim_delete_move_lines(lines_to_add, lines_to_del): def delete_move_lines(lines_to_add, lines_to_del): - lines_to_add, lines_to_del = bgp_delete_move_lines(lines_to_add, lines_to_del) lines_to_add, lines_to_del = pim_delete_move_lines(lines_to_add, lines_to_del) @@ -1089,13 +1086,12 @@ def delete_move_lines(lines_to_add, lines_to_del): def ignore_delete_re_add_lines(lines_to_add, lines_to_del): - # Quite possibly the most confusing (while accurate) variable names in history lines_to_add_to_del = [] lines_to_del_to_del = [] - index = 0 - for (ctx_keys, line) in lines_to_del: + index = -1 + for ctx_keys, line in lines_to_del: deleted = False # no form of route-map description command only @@ -1110,10 +1106,21 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del.remove((ctx_keys, line)) lines_to_del.insert(index, (ctx_keys, "description")) + # interface x ; description blah + # no form of description does not accept any argument, + # strip arg before rendering + if ( + ctx_keys[0].startswith("interface ") + and line + and line.startswith("description ") + ): + lines_to_del.remove((ctx_keys, line)) + lines_to_del.insert(index, (ctx_keys, "description")) + # If there is a change in the segment routing block ranges, do it # in-place, to avoid requesting spurious label chunks which might fail if line and "segment-routing global-block" in line: - for (add_key, add_line) in lines_to_add: + for add_key, add_line in lines_to_add: if ( ctx_keys[0] == add_key[0] and add_line @@ -1124,7 +1131,6 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): continue if ctx_keys[0].startswith("router bgp") and line: - if line.startswith("neighbor "): # BGP changed how it displays swpX peers that are part of peer-group. Older # versions of frr would display these on separate lines: @@ -1206,7 +1212,7 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): bfd_nbr = "neighbor %s" % nbr bfd_search_string = bfd_nbr + r" bfd (\S+) (\S+) (\S+)" - for (ctx_keys, add_line) in lines_to_add: + for ctx_keys, add_line in lines_to_add: if ctx_keys[0].startswith("router bgp"): re_add_nbr_bfd_timers = re.search( bfd_search_string, add_line @@ -1236,7 +1242,7 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): dir = re_nbr_rm.group(3) search = "neighbor%sroute-map(.*)%s" % (neighbor_name, dir) save_line = "EMPTY" - for (ctx_keys_al, add_line) in lines_to_add: + for ctx_keys_al, add_line in lines_to_add: if ctx_keys_al[0].startswith("router bgp"): if add_line: rm_match = re.search(search, add_line) @@ -1256,7 +1262,7 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys_al, line)) if adjust_for_bgp_node == 1: - for (ctx_keys_dl, dl_line) in lines_to_del: + for ctx_keys_dl, dl_line in lines_to_del: if ( ctx_keys_dl[0].startswith("router bgp") and len(ctx_keys_dl) > 1 @@ -1438,7 +1444,6 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): and ctx_keys[1] == "address-family l2vpn evpn" and ctx_keys[2].startswith("vni") ): - re_route_target = ( re.search("^route-target import (.*)$", line) if line is not None @@ -1514,19 +1519,18 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys, line)) lines_to_add_to_del.append((tmp_ctx_keys, line)) - for (ctx_keys, line) in lines_to_del_to_del: + for ctx_keys, line in lines_to_del_to_del: try: lines_to_del.remove((ctx_keys, line)) except ValueError: pass - for (ctx_keys, line) in lines_to_add_to_del: + for ctx_keys, line in lines_to_add_to_del: try: lines_to_add.remove((ctx_keys, line)) except ValueError: pass - return (lines_to_add, lines_to_del) @@ -1537,8 +1541,7 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del): """ lines_to_del_to_del = [] - for (ctx_keys, line) in lines_to_del: - + for ctx_keys, line in lines_to_del: # The integrated-vtysh-config one is technically "no"able but if we did # so frr-reload would stop working so do not let the user shoot # themselves in the foot by removing this. @@ -1559,7 +1562,7 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del): log.info('"%s" cannot be removed' % (ctx_keys[-1],)) lines_to_del_to_del.append((ctx_keys, line)) - for (ctx_keys, line) in lines_to_del_to_del: + for ctx_keys, line in lines_to_del_to_del: lines_to_del.remove((ctx_keys, line)) return (lines_to_add, lines_to_del) @@ -1584,8 +1587,7 @@ def compare_context_objects(newconf, running): # Find contexts that are in newconf but not in running # Find contexts that are in running but not in newconf - for (running_ctx_keys, running_ctx) in iteritems(running.contexts): - + for running_ctx_keys, running_ctx in iteritems(running.contexts): if running_ctx_keys in newconf.contexts: newconf_ctx = newconf.contexts[running_ctx_keys] @@ -1606,7 +1608,6 @@ def compare_context_objects(newconf, running): lines_to_del.append((running_ctx_keys, new_del_line)) if running_ctx_keys not in newconf.contexts: - # We check that the len is 1 here so that we only look at ('router bgp 10') # and not ('router bgp 10', 'address-family ipv4 unicast'). The # latter could cause a false delete_bgpd positive if ipv4 unicast is in @@ -1777,14 +1778,12 @@ def compare_context_objects(newconf, running): # Find the lines within each context to add # Find the lines within each context to del - for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts): - + for newconf_ctx_keys, newconf_ctx in iteritems(newconf.contexts): if newconf_ctx_keys in running.contexts: running_ctx = running.contexts[newconf_ctx_keys] for line in newconf_ctx.lines: if line not in running_ctx.dlines: - # candidate paths can only be added after the policy and segment list, # so add them to a separate array that is going to be appended at the end if ( @@ -1802,10 +1801,8 @@ def compare_context_objects(newconf, running): if line not in newconf_ctx.dlines: lines_to_del.append((newconf_ctx_keys, line)) - for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts): - + for newconf_ctx_keys, newconf_ctx in iteritems(newconf.contexts): if newconf_ctx_keys not in running.contexts: - # candidate paths can only be added after the policy and segment list, # so add them to a separate array that is going to be appended at the end if ( @@ -2040,7 +2037,6 @@ if __name__ == "__main__": reload_ok = False if args.test: - # Create a Config object from the running config running = Config(vtysh) @@ -2056,8 +2052,7 @@ if __name__ == "__main__": print("\nLines To Delete") print("===============") - for (ctx_keys, line) in lines_to_del: - + for ctx_keys, line in lines_to_del: if line == "!": continue @@ -2083,8 +2078,7 @@ if __name__ == "__main__": print("\nLines To Add") print("============") - for (ctx_keys, line) in lines_to_add: - + for ctx_keys, line in lines_to_add: if line == "!": continue @@ -2172,8 +2166,7 @@ if __name__ == "__main__": # apply to other scenarios as well where configuring FOO adds BAR # to the config. if lines_to_del and x == 0: - for (ctx_keys, line) in lines_to_del: - + for ctx_keys, line in lines_to_del: if line == "!": continue @@ -2203,12 +2196,11 @@ if __name__ == "__main__": vtysh(["configure"] + cmd, stdouts) except VtyshException: - # - Pull the last entry from cmd (this would be # 'no ip ospf authentication message-digest 1.1.1.1' in # our example above # - Split that last entry by whitespace and drop the last word - log.info("Failed to execute %s", " ".join(cmd)) + log.error("Failed to execute %s", " ".join(cmd)) last_arg = cmd[-1].split(" ") if len(last_arg) <= 2: @@ -2231,8 +2223,7 @@ if __name__ == "__main__": if lines_to_add: lines_to_configure = [] - for (ctx_keys, line) in lines_to_add: - + for ctx_keys, line in lines_to_add: if line == "!": continue diff --git a/tools/frr.in b/tools/frr.in index cd24a96..94c15d5 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -14,11 +14,11 @@ # PATH=/bin:/usr/bin:/sbin:/usr/sbin -D_PATH="@CFG_SBIN@" # /usr/lib/frr -C_PATH="@CFG_SYSCONF@" # /etc/frr -V_PATH="@CFG_STATE@" # /var/run/frr -B_PATH="@CFG_BIN@" -VTYSH="@vtysh_bin@" # /usr/bin/vtysh +D_PATH="@e_sbindir@" # /usr/lib/frr +C_PATH="@e_frr_sysconfdir@" # /etc/frr +V_PATH="@e_frr_runstatedir@" # /var/run/frr +B_PATH="@e_bindir@" +VTYSH="@e_vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty diff --git a/tools/frr.service.in b/tools/frr.service.in index 1e958dd..b52ee3f 100644 --- a/tools/frr.service.in +++ b/tools/frr.service.in @@ -17,10 +17,10 @@ WatchdogSec=60s RestartSec=5 Restart=always LimitNOFILE=1024 -PIDFile=@CFG_STATE@/watchfrr.pid -ExecStart=@CFG_SBIN@/frrinit.sh start -ExecStop=@CFG_SBIN@/frrinit.sh stop -ExecReload=@CFG_SBIN@/frrinit.sh reload +PIDFile=@e_frr_runstatedir@/watchfrr.pid +ExecStart=@e_sbindir@/frrinit.sh start +ExecStop=@e_sbindir@/frrinit.sh stop +ExecReload=@e_sbindir@/frrinit.sh reload [Install] WantedBy=multi-user.target diff --git a/tools/frr@.service.in b/tools/frr@.service.in index 85408a0..c8a2d3b 100644 --- a/tools/frr@.service.in +++ b/tools/frr@.service.in @@ -17,10 +17,10 @@ WatchdogSec=60s RestartSec=5 Restart=always LimitNOFILE=1024 -PIDFile=@CFG_STATE@/%I/watchfrr.pid -ExecStart=@CFG_SBIN@/frrinit.sh start %I -ExecStop=@CFG_SBIN@/frrinit.sh stop %I -ExecReload=@CFG_SBIN@/frrinit.sh reload %I +PIDFile=@e_frr_runstatedir@/%I/watchfrr.pid +ExecStart=@e_sbindir@/frrinit.sh start %I +ExecStop=@e_sbindir@/frrinit.sh stop %I +ExecReload=@e_sbindir@/frrinit.sh reload %I [Install] WantedBy=multi-user.target diff --git a/tools/frr_babeltrace.py b/tools/frr_babeltrace.py index 4d974ad..9832568 100755 --- a/tools/frr_babeltrace.py +++ b/tools/frr_babeltrace.py @@ -157,6 +157,46 @@ def parse_frr_bgp_evpn_mh_local_es_evi_del_zrecv(event): parse_event(event, field_parsers) +def parse_frr_bgp_evpn_mh_es_evi_vtep_add(event): + """ + bgp evpn remote ead evi remote vtep add; raw format - + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + """ + field_parsers = {"esi": print_esi, + "vtep": print_net_ipv4_addr} + + parse_event(event, field_parsers) + +def parse_frr_bgp_evpn_mh_es_evi_vtep_del(event): + """ + bgp evpn remote ead evi remote vtep del; raw format - + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + """ + field_parsers = {"esi": print_esi, + "vtep": print_net_ipv4_addr} + + parse_event(event, field_parsers) + +def parse_frr_bgp_evpn_mh_local_ead_es_evi_route_upd(event): + """ + bgp evpn local ead evi vtep; raw format - + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + """ + field_parsers = {"esi": print_esi, + "vtep": print_net_ipv4_addr} + + parse_event(event, field_parsers) + +def parse_frr_bgp_evpn_mh_local_ead_es_evi_route_del(event): + """ + bgp evpn local ead evi vtep del; raw format - + ctf_array(unsigned char, esi, esi, sizeof(esi_t)) + """ + field_parsers = {"esi": print_esi, + "vtep": print_net_ipv4_addr} + + parse_event(event, field_parsers) + def parse_frr_bgp_evpn_local_vni_add_zrecv(event): """ bgp evpn local-vni parser; raw format - @@ -205,6 +245,24 @@ def parse_frr_bgp_evpn_local_macip_del_zrecv(event): parse_event(event, field_parsers) +def parse_frr_bgp_evpn_advertise_type5(event): + """ + local originated type-5 route + """ + field_parsers = {"ip": print_ip_addr, + "rmac": print_mac, + "vtep": print_net_ipv4_addr} + + parse_event(event, field_parsers) + +def parse_frr_bgp_evpn_withdraw_type5(event): + """ + local originated type-5 route withdraw + """ + field_parsers = {"ip": print_ip_addr} + + parse_event(event, field_parsers) + ############################ evpn parsers - end *############################# def main(): @@ -225,6 +283,14 @@ def main(): parse_frr_bgp_evpn_mh_local_es_evi_add_zrecv, "frr_bgp:evpn_mh_local_es_evi_del_zrecv": parse_frr_bgp_evpn_mh_local_es_evi_del_zrecv, + "frr_bgp:evpn_mh_es_evi_vtep_add": + parse_frr_bgp_evpn_mh_es_evi_vtep_add, + "frr_bgp:evpn_mh_es_evi_vtep_del": + parse_frr_bgp_evpn_mh_es_evi_vtep_del, + "frr_bgp:evpn_mh_local_ead_es_evi_route_upd": + parse_frr_bgp_evpn_mh_local_ead_es_evi_route_upd, + "frr_bgp:evpn_mh_local_ead_es_evi_route_del": + parse_frr_bgp_evpn_mh_local_ead_es_evi_route_del, "frr_bgp:evpn_local_vni_add_zrecv": parse_frr_bgp_evpn_local_vni_add_zrecv, "frr_bgp:evpn_local_l3vni_add_zrecv": @@ -233,6 +299,10 @@ def main(): parse_frr_bgp_evpn_local_macip_add_zrecv, "frr_bgp:evpn_local_macip_del_zrecv": parse_frr_bgp_evpn_local_macip_del_zrecv, + "frr_bgp:evpn_advertise_type5": + parse_frr_bgp_evpn_advertise_type5, + "frr_bgp:evpn_withdraw_type5": + parse_frr_bgp_evpn_withdraw_type5, } # get the trace path from the first command line argument diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 00b63a7..44d4195 100755 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -14,18 +14,18 @@ # not perform any action. Note there is an "exit 1" if the main config # file does not exist. # -# This script should be installed in @CFG_SBIN@/frrcommon.sh +# This script should be installed in @e_sbindir@/frrcommon.sh # FRR_PATHSPACE is passed in from watchfrr suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}" nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}" PATH=/bin:/usr/bin:/sbin:/usr/sbin -D_PATH="@CFG_SBIN@" # /usr/lib/frr -C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr -V_PATH="@CFG_STATE@${suffix}" # /var/run/frr -B_PATH="@CFG_BIN@" -VTYSH="@vtysh_bin@" # /usr/bin/vtysh +D_PATH="@e_sbindir@" # /usr/lib/frr +C_PATH="@e_frr_sysconfdir@${suffix}" # /etc/frr +V_PATH="@e_frr_runstatedir@${suffix}" # /var/run/frr +B_PATH="@e_bindir@" +VTYSH="@e_vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in index 428d57c..178d18d 100644 --- a/tools/frrinit.sh.in +++ b/tools/frrinit.sh.in @@ -37,7 +37,7 @@ self="`dirname $0`" if [ -r "$self/frrcommon.sh" ]; then . "$self/frrcommon.sh" else - . "@CFG_SBIN@/frrcommon.sh" + . "@e_sbindir@/frrcommon.sh" fi case "$1" in diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index 5b778a1..a879811 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -7,6 +7,7 @@ #define REALLY_NEED_PLAIN_GETOPT 1 #include +#include #include @@ -25,67 +26,70 @@ static void __attribute__((noreturn)) usage(int status) static struct nb_callback_info { int operation; bool optional; + bool need_config_write; char return_type[32]; char return_value[32]; char arguments[128]; } nb_callbacks[] = { { - .operation = NB_OP_CREATE, + .operation = NB_CB_CREATE, + .need_config_write = true, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_create_args *args", }, { - .operation = NB_OP_MODIFY, + .operation = NB_CB_MODIFY, + .need_config_write = true, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_modify_args *args", }, { - .operation = NB_OP_DESTROY, + .operation = NB_CB_DESTROY, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_destroy_args *args", }, { - .operation = NB_OP_MOVE, + .operation = NB_CB_MOVE, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_move_args *args", }, { - .operation = NB_OP_APPLY_FINISH, + .operation = NB_CB_APPLY_FINISH, .optional = true, .return_type = "void ", .return_value = "", .arguments = "struct nb_cb_apply_finish_args *args", }, { - .operation = NB_OP_GET_ELEM, + .operation = NB_CB_GET_ELEM, .return_type = "struct yang_data *", .return_value = "NULL", .arguments = "struct nb_cb_get_elem_args *args", }, { - .operation = NB_OP_GET_NEXT, + .operation = NB_CB_GET_NEXT, .return_type = "const void *", .return_value = "NULL", .arguments = "struct nb_cb_get_next_args *args", }, { - .operation = NB_OP_GET_KEYS, + .operation = NB_CB_GET_KEYS, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_get_keys_args *args", }, { - .operation = NB_OP_LOOKUP_ENTRY, + .operation = NB_CB_LOOKUP_ENTRY, .return_type = "const void *", .return_value = "NULL", .arguments = "struct nb_cb_lookup_entry_args *args", }, { - .operation = NB_OP_RPC, + .operation = NB_CB_RPC, .return_type = "int ", .return_value = "NB_OK", .arguments = "struct nb_cb_rpc_args *args", @@ -96,6 +100,16 @@ static struct nb_callback_info { }, }; +/* + * Special-purpose info block for the cli-config-write callback. This + * is different enough from the config-oriented callbacks that it doesn't + * really fit in the array above. + */ +static struct nb_callback_info nb_config_write = { + .return_type = "void ", + .arguments = "struct vty *vty, const struct lyd_node *dnode, bool show_defaults", +}; + static void replace_hyphens_by_underscores(char *str) { char *p; @@ -106,7 +120,7 @@ static void replace_hyphens_by_underscores(char *str) } static void generate_callback_name(const struct lysc_node *snode, - enum nb_operation operation, char *buffer, + enum nb_cb_operation operation, char *buffer, size_t size) { struct list *snodes; @@ -128,7 +142,38 @@ static void generate_callback_name(const struct lysc_node *snode, strlcat(buffer, snode->name, size); strlcat(buffer, "_", size); } - strlcat(buffer, nb_operation_name(operation), size); + strlcat(buffer, nb_cb_operation_name(operation), size); + list_delete(&snodes); + + replace_hyphens_by_underscores(buffer); +} + +static void generate_config_write_cb_name(const struct lysc_node *snode, + char *buffer, size_t size) +{ + struct list *snodes; + struct listnode *ln; + + buffer[0] = '\0'; + + snodes = list_new(); + for (; snode; snode = snode->parent) { + /* Skip schema-only snodes. */ + if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE + | LYS_INPUT + | LYS_OUTPUT)) + continue; + + listnode_add_head(snodes, (void *)snode); + } + + for (ALL_LIST_ELEMENTS_RO(snodes, ln, snode)) { + strlcat(buffer, snode->name, size); + strlcat(buffer, "_", size); + } + + strlcat(buffer, "cli_write", size); + list_delete(&snodes); replace_hyphens_by_underscores(buffer); @@ -140,8 +185,16 @@ static void generate_prototype(const struct nb_callback_info *ncinfo, printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); } +static void generate_config_write_prototype(const struct nb_callback_info *ncinfo, + const char *cb_name) +{ + printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); +} + static int generate_prototypes(const struct lysc_node *snode, void *arg) { + bool need_config_write = true; + switch (snode->nodetype) { case LYS_CONTAINER: case LYS_LEAF: @@ -159,12 +212,21 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg) char cb_name[BUFSIZ]; if (cb->optional - || !nb_operation_is_valid(cb->operation, snode)) + || !nb_cb_operation_is_valid(cb->operation, snode)) continue; generate_callback_name(snode, cb->operation, cb_name, sizeof(cb_name)); generate_prototype(cb, cb_name); + + if (cb->need_config_write && need_config_write) { + generate_config_write_cb_name(snode, cb_name, + sizeof(cb_name)); + generate_config_write_prototype(&nb_config_write, + cb_name); + + need_config_write = false; + } } return YANG_ITER_CONTINUE; @@ -177,10 +239,10 @@ static void generate_callback(const struct nb_callback_info *ncinfo, ncinfo->return_type, cb_name, ncinfo->arguments); switch (ncinfo->operation) { - case NB_OP_CREATE: - case NB_OP_MODIFY: - case NB_OP_DESTROY: - case NB_OP_MOVE: + case NB_CB_CREATE: + case NB_CB_MODIFY: + case NB_CB_DESTROY: + case NB_CB_MOVE: printf("\tswitch (args->event) {\n" "\tcase NB_EV_VALIDATE:\n" "\tcase NB_EV_PREPARE:\n" @@ -200,9 +262,22 @@ static void generate_callback(const struct nb_callback_info *ncinfo, printf("\treturn %s;\n}\n\n", ncinfo->return_value); } +static void generate_config_write_callback(const struct nb_callback_info *ncinfo, + const char *cb_name) +{ + printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "", + ncinfo->return_type, cb_name, ncinfo->arguments); + + /* Add a comment, since these callbacks may not all be needed. */ + printf("\t/* TODO: this cli callback is optional; the cli output may not need to be done at each node. */\n"); + + printf("}\n\n"); +} + static int generate_callbacks(const struct lysc_node *snode, void *arg) { bool first = true; + bool need_config_write = true; switch (snode->nodetype) { case LYS_CONTAINER: @@ -221,7 +296,7 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) char cb_name[BUFSIZ]; if (cb->optional - || !nb_operation_is_valid(cb->operation, snode)) + || !nb_cb_operation_is_valid(cb->operation, snode)) continue; if (first) { @@ -240,6 +315,15 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) generate_callback_name(snode, cb->operation, cb_name, sizeof(cb_name)); generate_callback(cb, cb_name); + + if (cb->need_config_write && need_config_write) { + generate_config_write_cb_name(snode, cb_name, + sizeof(cb_name)); + generate_config_write_callback(&nb_config_write, + cb_name); + + need_config_write = false; + } } return YANG_ITER_CONTINUE; @@ -248,6 +332,10 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) static int generate_nb_nodes(const struct lysc_node *snode, void *arg) { bool first = true; + char cb_name[BUFSIZ]; + char xpath[XPATH_MAXLEN]; + bool config_pass = *(bool *)arg; + bool need_config_write = true; switch (snode->nodetype) { case LYS_CONTAINER: @@ -261,31 +349,53 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg) return YANG_ITER_CONTINUE; } + /* We generate two types of structs currently; behavior is a little + * different between the types. + */ for (struct nb_callback_info *cb = &nb_callbacks[0]; cb->operation != -1; cb++) { - char cb_name[BUFSIZ]; if (cb->optional - || !nb_operation_is_valid(cb->operation, snode)) + || !nb_cb_operation_is_valid(cb->operation, snode)) continue; - if (first) { - char xpath[XPATH_MAXLEN]; + if (config_pass) { + if (first) { + yang_snode_get_path(snode, YANG_PATH_DATA, xpath, + sizeof(xpath)); - yang_snode_get_path(snode, YANG_PATH_DATA, xpath, - sizeof(xpath)); + printf("\t\t{\n" + "\t\t\t.xpath = \"%s\",\n", + xpath); + printf("\t\t\t.cbs = {\n"); + first = false; + } - printf("\t\t{\n" - "\t\t\t.xpath = \"%s\",\n", - xpath); - printf("\t\t\t.cbs = {\n"); - first = false; - } + generate_callback_name(snode, cb->operation, cb_name, + sizeof(cb_name)); + printf("\t\t\t\t.%s = %s,\n", + nb_cb_operation_name(cb->operation), + cb_name); + } else if (cb->need_config_write && need_config_write) { + if (first) { + yang_snode_get_path(snode, + YANG_PATH_DATA, + xpath, + sizeof(xpath)); + + printf("\t\t{\n" + "\t\t\t.xpath = \"%s\",\n", + xpath); + printf("\t\t\t.cbs = {\n"); + first = false; + } - generate_callback_name(snode, cb->operation, cb_name, - sizeof(cb_name)); - printf("\t\t\t\t.%s = %s,\n", nb_operation_name(cb->operation), - cb_name); + generate_config_write_cb_name(snode, cb_name, + sizeof(cb_name)); + printf("\t\t\t\t.cli_show = %s,\n", cb_name); + + need_config_write = false; + } } if (!first) { @@ -303,6 +413,7 @@ int main(int argc, char *argv[]) char module_name_underscores[64]; struct stat st; int opt; + bool config_pass; while ((opt = getopt(argc, argv, "hp:s")) != -1) { switch (opt) { @@ -348,13 +459,18 @@ int main(int argc, char *argv[]) module = yang_module_find(argv[0]); if (!module) /* Non-native FRR module (e.g. modules from unit tests). */ - module = yang_module_load(argv[0]); + module = yang_module_load(argv[0], NULL); yang_init_loading_complete(); /* Create a nb_node for all YANG schema nodes. */ nb_nodes_create(); + /* Emit bare-bones license line (and fool the checkpatch regex + * that triggers a warning). + */ + printf("// SPDX-" "License-Identifier: GPL-2.0-or-later\n\n"); + /* Generate callback prototypes. */ if (!static_cbs) { printf("/* prototypes */\n"); @@ -369,13 +485,38 @@ int main(int argc, char *argv[]) sizeof(module_name_underscores)); replace_hyphens_by_underscores(module_name_underscores); - /* Generate frr_yang_module_info array. */ + /* + * We're going to generate two structs here, two arrays of callbacks: + * first one with config-handling callbacks, then a second struct with + * config-output-oriented callbacks. + */ + + /* Generate frr_yang_module_info array, with config-handling callbacks */ + config_pass = true; printf("/* clang-format off */\n" - "const struct frr_yang_module_info %s_info = {\n" + "const struct frr_yang_module_info %s_nb_info = {\n" "\t.name = \"%s\",\n" "\t.nodes = {\n", module_name_underscores, module->name); - yang_snodes_iterate(module->info, generate_nb_nodes, 0, NULL); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + + /* Emit terminator element */ + printf("\t\t{\n" + "\t\t\t.xpath = NULL,\n" + "\t\t},\n"); + printf("\t}\n" + "};\n"); + + /* Generate second array, with output-oriented callbacks. */ + config_pass = false; + printf("\n/* clang-format off */\n" + "const struct frr_yang_module_info %s_cli_info = {\n" + "\t.name = \"%s\",\n" + "\t.nodes = {\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + + /* Emit terminator element */ printf("\t\t{\n" "\t\t\t.xpath = NULL,\n" "\t\t},\n"); diff --git a/tools/gen_yang_deviations.c b/tools/gen_yang_deviations.c index fc9f55b..251643c 100644 --- a/tools/gen_yang_deviations.c +++ b/tools/gen_yang_deviations.c @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) yang_init(false, false); /* Load YANG module. */ - module = yang_module_load(argv[0]); + module = yang_module_load(argv[0], NULL); /* Generate deviations. */ yang_snodes_iterate(module->info, generate_yang_deviation, 0, NULL); diff --git a/tools/valgrind.supp b/tools/valgrind.supp index da3d4a8..d2cb411 100644 --- a/tools/valgrind.supp +++ b/tools/valgrind.supp @@ -23,6 +23,15 @@ fun:cap_init fun:zprivs_caps_init } +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:getgrouplist + fun:zprivs_init + fun:frr_init +} { Memcheck:Leak diff --git a/tools/watchfrr.sh.in b/tools/watchfrr.sh.in index 712f962..31279f6 100644 --- a/tools/watchfrr.sh.in +++ b/tools/watchfrr.sh.in @@ -5,7 +5,7 @@ # internally by watchfrr to start the protocol daemons with the appropriate # options. # -# This script should be installed in @CFG_SBIN@/watchfrr.sh +# This script should be installed in @e_sbindir@/watchfrr.sh log_success_msg() { : @@ -27,7 +27,7 @@ self="`dirname $0`" if [ -r "$self/frrcommon.sh" ]; then . "$self/frrcommon.sh" else - . "@CFG_SBIN@/frrcommon.sh" + . "@e_sbindir@/frrcommon.sh" fi frrcommon_main "$@" diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index b14a6ec..0173879 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -700,10 +700,9 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) */ ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; - struct listnode *ln; struct connected *c = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) + frr_each (if_connected, ifp->connected, c) if (c->address->family == r->family) { if (r->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) @@ -1171,9 +1170,15 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid, family2str(r->family)); /* Join Rx socket to VRRP IPv4 multicast group */ - assert(listhead(r->vr->ifp->connected)); - struct connected *c = listhead(r->vr->ifp->connected)->data; - struct in_addr v4 = c->address->u.prefix4; + struct connected *c; + struct in_addr v4; + + frr_each (if_connected, r->vr->ifp->connected, c) + if (c->address->family == AF_INET) + break; + + assert(c); + v4 = c->address->u.prefix4; ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, v4, htonl(VRRP_MCASTV4_GROUP), @@ -1703,7 +1708,6 @@ int vrrp_event(struct vrrp_router *r, int event) */ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) { - struct listnode *ln; struct connected *c = NULL; bool is_v6_ll; @@ -1714,7 +1718,7 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Setting Virtual IP list to match IPv4 addresses on %s", r->vr->vrid, family2str(r->family), r->mvl_ifp->name); - for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) { + frr_each (if_connected, r->mvl_ifp->connected, c) { is_v6_ll = (c->address->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)); if (c->address->family == r->family && !is_v6_ll) { diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 5245c74..e9af3d9 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -107,16 +107,20 @@ static const struct frr_yang_module_info *const vrrp_yang_modules[] = { &frr_vrrpd_info, }; -#define VRRP_VTY_PORT 2619 - -FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, - .proghelp = "Virtual Router Redundancy Protocol", - .signals = vrrp_signals, - .n_signals = array_size(vrrp_signals), - .privs = &vrrp_privs, - .yang_modules = vrrp_yang_modules, - .n_yang_modules = array_size(vrrp_yang_modules), +/* clang-format off */ +FRR_DAEMON_INFO(vrrpd, VRRP, + .vty_port = VRRP_VTY_PORT, + .proghelp = "Virtual Router Redundancy Protocol", + + .signals = vrrp_signals, + .n_signals = array_size(vrrp_signals), + + .privs = &vrrp_privs, + + .yang_modules = vrrp_yang_modules, + .n_yang_modules = array_size(vrrp_yang_modules), ); +/* clang-format on */ int main(int argc, char **argv, char **envp) { diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c index 1f8da4c..2947a41 100644 --- a/vrrpd/vrrp_northbound.c +++ b/vrrpd/vrrp_northbound.c @@ -27,8 +27,8 @@ static int lib_interface_vrrp_vrrp_group_create(struct nb_cb_create_args *args) uint8_t version = 3; struct vrrp_vrouter *vr; - vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id"); - version = yang_dnode_get_enum(args->dnode, "./version"); + vrid = yang_dnode_get_uint8(args->dnode, "virtual-router-id"); + version = yang_dnode_get_enum(args->dnode, "version"); switch (args->event) { case NB_EV_VALIDATE: diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 9971df5..fd6cbc8 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -62,11 +62,11 @@ DEFPY_YANG(vrrp_vrid, void cli_show_vrrp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - const char *vrid = yang_dnode_get_string(dnode, "./virtual-router-id"); - const char *ver = yang_dnode_get_string(dnode, "./version"); + const char *vrid = yang_dnode_get_string(dnode, "virtual-router-id"); + const char *ver = yang_dnode_get_string(dnode, "version"); vty_out(vty, " vrrp %s", vrid); - if (show_defaults || !yang_dnode_is_default(dnode, "./version")) + if (show_defaults || !yang_dnode_is_default(dnode, "version")) vty_out(vty, " version %s", ver); vty_out(vty, "\n"); } @@ -200,7 +200,11 @@ DEFPY_YANG(vrrp_ip, VRRP_IP_STR) { int op = no ? NB_OP_DESTROY : NB_OP_CREATE; - nb_cli_enqueue_change(vty, "./v4/virtual-address", op, ip_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./v4/virtual-address[.='%s']", ip_str); + + nb_cli_enqueue_change(vty, xpath, op, NULL); return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); } @@ -228,7 +232,11 @@ DEFPY_YANG(vrrp_ip6, VRRP_IP_STR) { int op = no ? NB_OP_DESTROY : NB_OP_CREATE; - nb_cli_enqueue_change(vty, "./v6/virtual-address", op, ipv6_str); + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./v6/virtual-address[.='%s']", ipv6_str); + + nb_cli_enqueue_change(vty, xpath, op, NULL); return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); } diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 6d753d2..009432b 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -36,11 +36,10 @@ static void vrrp_zebra_debug_if_dump_address(struct interface *ifp, const char *func) { struct connected *ifc; - struct listnode *node; DEBUGD(&vrrp_dbg_zebra, "%s: interface %s addresses:", func, ifp->name); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { struct prefix *p = ifc->address; DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %pFX %s", @@ -183,8 +182,10 @@ static zclient_handler *const vrrp_handlers[] = { void vrrp_zebra_init(void) { - if_zapi_callbacks(vrrp_ifp_create, vrrp_ifp_up, - vrrp_ifp_down, vrrp_ifp_destroy); + hook_register_prio(if_real, 0, vrrp_ifp_create); + hook_register_prio(if_up, 0, vrrp_ifp_up); + hook_register_prio(if_down, 0, vrrp_ifp_down); + hook_register_prio(if_unreal, 0, vrrp_ifp_destroy); /* Socket for receiving updates from Zebra daemon */ zclient = zclient_new(master, &zclient_options_default, vrrp_handlers, diff --git a/vtysh/.gitignore b/vtysh/.gitignore index 09e90e5..a6c3d4a 100644 --- a/vtysh/.gitignore +++ b/vtysh/.gitignore @@ -1,6 +1,5 @@ vtysh vtysh_cmd.c -vtysh_daemons.h # does not exist anymore - remove 2023-10-04 or so extract.pl diff --git a/vtysh/subdir.am b/vtysh/subdir.am index a1b81f5..2eae16d 100644 --- a/vtysh/subdir.am +++ b/vtysh/subdir.am @@ -29,13 +29,3 @@ noinst_HEADERS += \ vtysh_vtysh_LDADD = lib/libfrr.la $(LIBCAP) $(LIBREADLINE) $(LIBS) $(LIBPAM) EXTRA_DIST += vtysh/daemons.pl - -BUILT_SOURCES += vtysh/vtysh_daemons.h - -# force vtysh_daemons.h -$(vtysh_vtysh_OBJECTS): vtysh/vtysh_daemons.h - -CLEANFILES += vtysh/vtysh_daemons.h -vtysh/vtysh_daemons.h: - @$(MKDIR_P) vtysh - $(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > vtysh/vtysh_daemons.h diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index e87a69d..f90f898 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -5,6 +5,9 @@ #include +#include +#include + #include #include #include @@ -28,7 +31,7 @@ #include "network.h" #include "filter.h" #include "vtysh/vtysh.h" -#include "vtysh/vtysh_daemons.h" +#include "lib/vtysh_daemons.h" #include "log.h" #include "vrf.h" #include "libfrr.h" @@ -1335,6 +1338,13 @@ static struct cmd_node srv6_loc_node = { .prompt = "%s(config-srv6-locator)# ", }; +static struct cmd_node srv6_encap_node = { + .name = "srv6-encap", + .node = SRV6_ENCAP_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-encap)# " +}; + #ifdef HAVE_PBRD static struct cmd_node pbr_map_node = { .name = "pbr-map", @@ -1605,7 +1615,6 @@ struct cmd_node link_params_node = { .node = LINK_PARAMS_NODE, .parent_node = INTERFACE_NODE, .prompt = "%s(config-link-params)# ", - .no_xpath = true, }; #ifdef HAVE_BGPD @@ -1615,6 +1624,14 @@ static struct cmd_node rpki_node = { .parent_node = CONFIG_NODE, .prompt = "%s(config-rpki)# ", }; + +static struct cmd_node rpki_vrf_node = { + .name = "rpki", + .node = RPKI_VRF_NODE, + .parent_node = VRF_NODE, + .prompt = "%s(config-vrf-rpki)# ", +}; + #endif /* HAVE_BGPD */ #if HAVE_BFDD > 0 @@ -1652,7 +1669,6 @@ static int vtysh_end(void) /* Nothing to do. */ break; default: - vty->vtysh_file_locked = false; vty->node = ENABLE_NODE; break; } @@ -1692,6 +1708,14 @@ DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_ZEBRA, srv6_encap, srv6_encap_cmd, + "encapsulation", + "Segment Routing SRv6 encapsulation\n") +{ + vty->node = SRV6_ENCAP_NODE; + return CMD_SUCCESS; +} + #ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, "router bgp [ASNUM [ VIEWVRFNAME] [as-notation ]]", @@ -1837,7 +1861,10 @@ DEFUNSH(VTYSH_BGPD, "rpki", "Enable rpki and enter rpki configuration mode\n") { - vty->node = RPKI_NODE; + if (vty->node == CONFIG_NODE) + vty->node = RPKI_NODE; + else + vty->node = RPKI_VRF_NODE; return CMD_SUCCESS; } @@ -1977,7 +2004,7 @@ DEFUNSH(VTYSH_KEYS, key, key_cmd, "key (0-2147483647)", } #ifdef HAVE_RIPD -DEFUNSH(VTYSH_RIPD, router_rip, router_rip_cmd, "router rip [vrf NAME]", +DEFUNSH(VTYSH_MGMTD, router_rip, router_rip_cmd, "router rip [vrf NAME]", ROUTER_STR "RIP\n" VRF_CMD_HELP_STR) { vty->node = RIP_NODE; @@ -1986,7 +2013,7 @@ DEFUNSH(VTYSH_RIPD, router_rip, router_rip_cmd, "router rip [vrf NAME]", #endif /* HAVE_RIPD */ #ifdef HAVE_RIPNGD -DEFUNSH(VTYSH_RIPNGD, router_ripng, router_ripng_cmd, "router ripng [vrf NAME]", +DEFUNSH(VTYSH_MGMTD, router_ripng, router_ripng_cmd, "router ripng [vrf NAME]", ROUTER_STR "RIPng\n" VRF_CMD_HELP_STR) { vty->node = RIPNG_NODE; @@ -2274,7 +2301,7 @@ DEFUNSH(VTYSH_AFFMAP, no_affinity_map, vtysh_no_affinity_map_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, +DEFUNSH(VTYSH_RMAP_CONFIG, vtysh_route_map, vtysh_route_map_cmd, "route-map RMAP_NAME (1-65535)", "Create route-map or enter route-map command mode\n" "Route map tag\n" @@ -2365,23 +2392,12 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_disable, vtysh_disable_cmd, "disable", } DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd, - "configure [terminal]", - "Configuration from vty interface\n" - "Configuration terminal\n") -{ - vty->node = CONFIG_NODE; - return CMD_SUCCESS; -} - -DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal_file_lock, - vtysh_config_terminal_file_lock_cmd, - "configure terminal file-lock", + "configure [terminal [file-lock]]", "Configuration from vty interface\n" "Configuration terminal\n" "Configuration with locked datastores\n") { vty->node = CONFIG_NODE; - vty->vtysh_file_locked = true; return CMD_SUCCESS; } @@ -2396,21 +2412,6 @@ static int vtysh_exit(struct vty *vty) if (cnode->parent_node) vty->node = cnode->parent_node; - if (vty->node == CONFIG_NODE) { - bool locked = vty->vtysh_file_locked; - - /* resync in case one of the daemons is somewhere else */ - vtysh_execute("end"); - /* NOTE: a rather expensive thing to do, can we avoid it? */ - - if (locked) - vtysh_execute("configure terminal file-lock"); - else - vtysh_execute("configure terminal"); - } else if (vty->node == ENABLE_NODE) { - vty->vtysh_file_locked = false; - } - return CMD_SUCCESS; } @@ -2507,14 +2508,22 @@ DEFUNSH(VTYSH_ZEBRA, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", return CMD_SUCCESS; } +DEFUNSH(VTYSH_ZEBRA, exit_srv6_encap, exit_srv6_encap_cmd, "exit", + "Exit from SRv6-encapsulation configuration mode\n") +{ + if (vty->node == SRV6_ENCAP_NODE) + vty->node = SRV6_NODE; + return CMD_SUCCESS; +} + #ifdef HAVE_RIPD -DEFUNSH(VTYSH_RIPD, vtysh_exit_ripd, vtysh_exit_ripd_cmd, "exit", +DEFUNSH(VTYSH_MGMTD, vtysh_exit_ripd, vtysh_exit_ripd_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_RIPD, vtysh_quit_ripd, vtysh_quit_ripd_cmd, "quit", +DEFUNSH(VTYSH_MGMTD, vtysh_quit_ripd, vtysh_quit_ripd_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_ripd(self, vty, argc, argv); @@ -2522,26 +2531,26 @@ DEFUNSH(VTYSH_RIPD, vtysh_quit_ripd, vtysh_quit_ripd_cmd, "quit", #endif /* HAVE_RIPD */ #ifdef HAVE_RIPNGD -DEFUNSH(VTYSH_RIPNGD, vtysh_exit_ripngd, vtysh_exit_ripngd_cmd, "exit", +DEFUNSH(VTYSH_MGMTD, vtysh_exit_ripngd, vtysh_exit_ripngd_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_RIPNGD, vtysh_quit_ripngd, vtysh_quit_ripngd_cmd, "quit", +DEFUNSH(VTYSH_MGMTD, vtysh_quit_ripngd, vtysh_quit_ripngd_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_ripngd(self, vty, argc, argv); } #endif /* HAVE_RIPNGD */ -DEFUNSH(VTYSH_RMAP, vtysh_exit_rmap, vtysh_exit_rmap_cmd, "exit", +DEFUNSH(VTYSH_RMAP_CONFIG, vtysh_exit_rmap, vtysh_exit_rmap_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_RMAP, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit", +DEFUNSH(VTYSH_RMAP_CONFIG, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_rmap(self, vty, argc, argv); @@ -2917,35 +2926,38 @@ static int show_one_daemon(struct vty *vty, struct cmd_token **argv, int argc, return ret; } -DEFUN (vtysh_show_thread_timer, - vtysh_show_thread_timer_cmd, - "show thread timers", +#if CONFDATE > 20240707 + CPP_NOTICE("Remove `show thread ...` commands") +#endif +DEFUN (vtysh_show_event_timer, + vtysh_show_event_timer_cmd, + "show event timers", SHOW_STR - "Thread information\n" + "Event information\n" "Show all timers and how long they have in the system\n") { - return show_per_daemon(vty, argv, argc, "Thread timers for %s:\n"); + return show_per_daemon(vty, argv, argc, "Event timers for %s:\n"); } -DEFUN (vtysh_show_poll, - vtysh_show_poll_cmd, - "show thread poll", +DEFUN (vtysh_show_event_poll, + vtysh_show_event_poll_cmd, + "show event poll", SHOW_STR - "Thread information\n" - "Thread Poll Information\n") + "Event information\n" + "Event Poll Information\n") { - return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n"); + return show_per_daemon(vty, argv, argc, "Event statistics for %s:\n"); } -DEFUN (vtysh_show_thread, - vtysh_show_thread_cmd, - "show thread cpu [FILTER]", +DEFUN (vtysh_show_event, + vtysh_show_event_cpu_cmd, + "show event cpu [FILTER]", SHOW_STR - "Thread information\n" - "Thread CPU usage\n" + "Event information\n" + "Event CPU usage\n" "Display filter (rwtexb)\n") { - return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n"); + return show_per_daemon(vty, argv, argc, "Event statistics for %s:\n"); } DEFUN (vtysh_show_work_queues, @@ -2968,14 +2980,22 @@ DEFUN (vtysh_show_work_queues_daemon, return show_one_daemon(vty, argv, argc - 1, argv[argc - 1]->text); } -DEFUNSH(VTYSH_ZEBRA, vtysh_link_params, vtysh_link_params_cmd, "link-params", +DEFUNSH(VTYSH_MGMTD, vtysh_link_params, vtysh_link_params_cmd, "link-params", LINK_PARAMS_STR) { vty->node = LINK_PARAMS_NODE; return CMD_SUCCESS; } -DEFUNSH(VTYSH_ZEBRA, exit_link_params, exit_link_params_cmd, "exit-link-params", +DEFUNSH_HIDDEN(VTYSH_MGMTD, no_link_params_enable, no_link_params_enable_cmd, + "no enable", NO_STR "Disable link parameters on this interface\n") +{ + if (vty->node == LINK_PARAMS_NODE) + vty->node = INTERFACE_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_MGMTD, exit_link_params, exit_link_params_cmd, "exit-link-params", "Exit from Link Params configuration node\n") { if (vty->node == LINK_PARAMS_NODE) @@ -2983,7 +3003,7 @@ 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", +DEFUNSH(VTYSH_MGMTD, 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) @@ -2991,7 +3011,7 @@ DEFUNSH(VTYSH_ZEBRA, vtysh_exit_link_params, vtysh_exit_link_params_cmd, "exit", return CMD_SUCCESS; } -DEFUNSH(VTYSH_ZEBRA, vtysh_quit_link_params, vtysh_quit_link_params_cmd, "quit", +DEFUNSH(VTYSH_MGMTD, 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); @@ -3073,7 +3093,7 @@ DEFUN (vtysh_show_error_code, } /* Northbound. */ -DEFUN_HIDDEN (show_config_running, +DEFUN (show_config_running, show_config_running_cmd, "show configuration running\ [ [translate WORD]]\ @@ -3149,7 +3169,7 @@ DEFUNSH(VTYSH_ALL, debug_nb, debug_nb_cmd, "[no] debug northbound\ [<\ - callbacks [{configuration|state|rpc}]\ + callbacks [{configuration|state|rpc|notify}]\ |notifications\ |events\ |libyang\ @@ -3162,6 +3182,7 @@ DEFUNSH(VTYSH_ALL, debug_nb, "State\n" "RPC\n" "Notifications\n" + "Notifications\n" "Events\n" "libyang debugging\n") { @@ -3396,6 +3417,63 @@ DEFUN (vtysh_show_running_config, return vtysh_write_terminal(self, vty, argc, argv); } +static void show_route_map_send(const char *route_map, bool json) +{ + unsigned int i; + bool first = true; + char command_line[128]; + + snprintf(command_line, sizeof(command_line), "show route-map "); + if (route_map) + strlcat(command_line, route_map, sizeof(command_line)); + if (json) + strlcat(command_line, " json", sizeof(command_line)); + + if (json) + vty_out(vty, "{"); + + for (i = 0; i < array_size(vtysh_client); i++) { + const struct vtysh_client *client = &vtysh_client[i]; + bool is_connected = true; + + if (!CHECK_FLAG(client->flag, VTYSH_RMAP_SHOW)) + continue; + + for (; client; client = client->next) + if (client->fd < 0) + is_connected = false; + + if (!is_connected) + continue; + + if (json && !first) + vty_out(vty, ","); + else + first = false; + + if (json) + vty_out(vty, "\"%s\":", vtysh_client[i].name); + + vtysh_client_execute_name(vtysh_client[i].name, command_line); + } + + if (json) + vty_out(vty, "}\n"); +} + +DEFPY (show_route_map, + show_route_map_cmd, + "show route-map [WORD]$route_map [json]$json", + SHOW_STR + "route-map information\n" + "route-map name\n" + JSON_STR) +{ + show_route_map_send(route_map, !!json); + + return CMD_SUCCESS; +} + DEFUN (vtysh_integrated_config, vtysh_integrated_config_cmd, "service integrated-vtysh-config", @@ -4967,6 +5045,7 @@ void vtysh_init_vty(void) install_node(&link_params_node); install_element(INTERFACE_NODE, &vtysh_link_params_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_enable_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_link_params_cmd); @@ -4985,6 +5064,12 @@ void vtysh_init_vty(void) install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_quit_vrf_cmd); + install_node(&rpki_vrf_node); + install_element(VRF_NODE, &rpki_cmd); + install_element(RPKI_VRF_NODE, &rpki_exit_cmd); + install_element(RPKI_VRF_NODE, &rpki_quit_cmd); + install_element(RPKI_VRF_NODE, &vtysh_end_all_cmd); + install_element(CONFIG_NODE, &vtysh_affinity_map_cmd); install_element(CONFIG_NODE, &vtysh_no_affinity_map_cmd); @@ -5014,7 +5099,6 @@ void vtysh_init_vty(void) if (!user_mode) install_element(VIEW_NODE, &vtysh_enable_cmd); install_element(ENABLE_NODE, &vtysh_config_terminal_cmd); - install_element(ENABLE_NODE, &vtysh_config_terminal_file_lock_cmd); install_element(ENABLE_NODE, &vtysh_disable_cmd); /* "exit" command. */ @@ -5033,6 +5117,7 @@ void vtysh_init_vty(void) install_element(SRV6_NODE, &srv6_locators_cmd); install_element(SRV6_NODE, &exit_srv6_config_cmd); install_element(SRV6_NODE, &vtysh_end_all_cmd); + install_element(SRV6_NODE, &srv6_encap_cmd); install_node(&srv6_locs_node); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); @@ -5043,10 +5128,16 @@ void vtysh_init_vty(void) install_element(SRV6_LOC_NODE, &exit_srv6_loc_config_cmd); install_element(SRV6_LOC_NODE, &vtysh_end_all_cmd); + install_node(&srv6_encap_node); + install_element(SRV6_ENCAP_NODE, &exit_srv6_encap_cmd); + install_element(SRV6_ENCAP_NODE, &vtysh_end_all_cmd); + install_element(ENABLE_NODE, &vtysh_show_running_config_cmd); install_element(ENABLE_NODE, &vtysh_copy_running_config_cmd); install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd); + install_element(ENABLE_NODE, &show_route_map_cmd); + /* "write terminal" command. */ install_element(ENABLE_NODE, &vtysh_write_terminal_cmd); @@ -5112,9 +5203,9 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_show_modules_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); - install_element(VIEW_NODE, &vtysh_show_thread_cmd); - install_element(VIEW_NODE, &vtysh_show_poll_cmd); - install_element(VIEW_NODE, &vtysh_show_thread_timer_cmd); + install_element(VIEW_NODE, &vtysh_show_event_cpu_cmd); + install_element(VIEW_NODE, &vtysh_show_event_poll_cmd); + install_element(VIEW_NODE, &vtysh_show_event_timer_cmd); /* Logging */ install_element(VIEW_NODE, &vtysh_show_logging_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 65733ca..7758cea 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -49,21 +49,40 @@ extern struct event_loop *master; VTYSH_PIM6D | VTYSH_NHRPD | VTYSH_EIGRPD | VTYSH_BABELD | \ VTYSH_SHARPD | VTYSH_PBRD | VTYSH_STATICD | VTYSH_BFDD | \ VTYSH_FABRICD | VTYSH_VRRPD | VTYSH_PATHD | VTYSH_MGMTD -#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA -#define VTYSH_AFFMAP VTYSH_ZEBRA | VTYSH_ISISD -#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD -#define VTYSH_INTERFACE_SUBSET \ +#define VTYSH_ACL_CONFIG \ + VTYSH_BFDD | VTYSH_BABELD | VTYSH_BGPD | VTYSH_EIGRPD | VTYSH_ISISD | \ + VTYSH_FABRICD | VTYSH_LDPD | VTYSH_NHRPD | VTYSH_OSPF6D | \ + VTYSH_OSPFD | VTYSH_PBRD | VTYSH_PIMD | VTYSH_PIM6D | \ + VTYSH_VRRPD | VTYSH_MGMTD +#define VTYSH_ACL_SHOW \ + VTYSH_BFDD | VTYSH_BABELD | VTYSH_BGPD | VTYSH_EIGRPD | VTYSH_ISISD | \ + VTYSH_FABRICD | VTYSH_LDPD | VTYSH_NHRPD | VTYSH_OSPF6D | \ + VTYSH_OSPFD | VTYSH_PBRD | VTYSH_PIMD | VTYSH_PIM6D | \ + VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_VRRPD | VTYSH_ZEBRA + +#define VTYSH_AFFMAP VTYSH_ISISD | VTYSH_MGMTD +#define VTYSH_RMAP_CONFIG \ + VTYSH_OSPFD | VTYSH_OSPF6D | VTYSH_BGPD | VTYSH_ISISD | \ + VTYSH_PIMD | VTYSH_EIGRPD | VTYSH_FABRICD | VTYSH_MGMTD +#define VTYSH_RMAP_SHOW \ VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ + VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \ + VTYSH_FABRICD +#define VTYSH_INTERFACE_SUBSET \ + VTYSH_OSPFD | VTYSH_OSPF6D | \ VTYSH_ISISD | VTYSH_PIMD | VTYSH_PIM6D | VTYSH_NHRPD | \ VTYSH_EIGRPD | VTYSH_BABELD | VTYSH_PBRD | VTYSH_FABRICD | \ - VTYSH_VRRPD + VTYSH_VRRPD | VTYSH_MGMTD #define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD -#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_MGMTD +#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_BGPD #define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D | VTYSH_OSPFD /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD #define VTYSH_SR VTYSH_ZEBRA|VTYSH_PATHD #define VTYSH_DPDK VTYSH_ZEBRA +#define VTYSH_MGMT_BACKEND \ + VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_STATICD | VTYSH_ZEBRA +#define VTYSH_MGMT_FRONTEND VTYSH_MGMTD enum vtysh_write_integrated { WRITE_INTEGRATED_UNSPECIFIED, diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index ae64d51..c207e4d 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -315,11 +315,20 @@ void vtysh_config_parse_line(void *arg, const char *line) } else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) { config_add_line_uniq_end(config->line, line); + } else if ((strncmp(line, " rpki", strlen(" rpki")) == + 0) && + config->index == VRF_NODE) { + config_add_line(config->line, line); + config->index = RPKI_VRF_NODE; } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == VTY_NODE) config_add_line_uniq(config->line, line); - else if (config->index == NH_GROUP_NODE) { + else if (config->index == RPKI_VRF_NODE && + strncmp(line, " exit", strlen(" exit")) == 0) { + config_add_line(config->line, line); + config->index = VRF_NODE; + } else if (config->index == NH_GROUP_NODE) { if (strncmp(line, " resilient", strlen(" resilient")) == 0) config_add_line_head(config->line, @@ -455,6 +464,12 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "debug resolver", strlen("debug resolver")) == 0) config = config_get(RESOLVER_DEBUG_NODE, line); + else if (strncmp(line, "debug mgmt client frontend", + strlen("debug mgmt client frontend")) == 0) + config = config_get(MGMT_FE_DEBUG_NODE, line); + else if (strncmp(line, "debug mgmt client backend", + strlen("debug mgmt client backend")) == 0) + config = config_get(MGMT_BE_DEBUG_NODE, line); else if (strncmp(line, "debug", strlen("debug")) == 0) config = config_get(DEBUG_NODE, line); else if (strncmp(line, "password", strlen("password")) == 0 @@ -601,8 +616,13 @@ static int vtysh_read_file(FILE *confp, bool dry_run) vty->node = CONFIG_NODE; vtysh_execute_no_pager("enable"); - vtysh_execute_no_pager("conf term file-lock"); - vty->vtysh_file_locked = true; + /* + * When reading the config, we need to wait until the lock is acquired. + * If we ignore the failure and continue without the lock, the config + * will be fully ignored. + */ + while (vtysh_execute_no_pager("conf term file-lock") == CMD_WARNING_CONFIG_FAILED) + usleep(100000); if (!dry_run) vtysh_execute_no_pager("XFRR_start_configuration"); @@ -614,7 +634,6 @@ static int vtysh_read_file(FILE *confp, bool dry_run) vtysh_execute_no_pager("XFRR_end_configuration"); vtysh_execute_no_pager("end"); - vty->vtysh_file_locked = false; vtysh_execute_no_pager("disable"); vty_close(vty); diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index c57e8c7..464d82c 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -5,6 +5,9 @@ #include +#include +#include +#include #include #include #include @@ -362,8 +365,7 @@ int main(int argc, char **argv, char **env) strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); - frr_init_vtydir(); - strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); + strlcpy(vtydir, frr_runstatedir, sizeof(vtydir)); /* Option handling. */ while (1) { diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 228b7ff..acc612c 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -6,6 +6,11 @@ */ #include + +#include +#include +#include + #include "frrevent.h" #include #include @@ -27,6 +32,8 @@ #include "watchfrr.h" #include "watchfrr_errors.h" +#include "lib/config_paths.h" + #ifndef MIN #define MIN(X,Y) (((X) <= (Y)) ? (X) : (Y)) #endif @@ -115,7 +122,7 @@ static struct global_state { int numdown; /* # of daemons that are not UP or UNRESPONSIVE */ } gs = { .phase = PHASE_INIT, - .vtydir = frr_vtydir, + .vtydir = frr_runstatedir, .period = 1000 * DEFAULT_PERIOD, .timeout = DEFAULT_TIMEOUT, .restart_timeout = DEFAULT_RESTART_TIMEOUT, @@ -292,11 +299,11 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n", passing command-line arguments with embedded spaces.\n\ -v, --version Print program version\n\ -h, --help Display this help and exit\n", - frr_vtydir, DEFAULT_LOGLEVEL, LOG_EMERG, LOG_DEBUG, LOG_DEBUG, - DEFAULT_MIN_RESTART, DEFAULT_MAX_RESTART, + frr_runstatedir, DEFAULT_LOGLEVEL, LOG_EMERG, LOG_DEBUG, + LOG_DEBUG, DEFAULT_MIN_RESTART, DEFAULT_MAX_RESTART, DEFAULT_OPERATIONAL_TIMEOUT, DEFAULT_PERIOD, DEFAULT_TIMEOUT, DEFAULT_RESTART_TIMEOUT, DEFAULT_RESTART_CMD, DEFAULT_START_CMD, - DEFAULT_STOP_CMD, frr_vtydir); + DEFAULT_STOP_CMD, frr_runstatedir); } static pid_t run_background(char *shell_cmd) @@ -723,7 +730,7 @@ static void daemon_send_ready(int exitcode) frr_detach(); - snprintf(started, sizeof(started), "%s/%s", frr_vtydir, + snprintf(started, sizeof(started), "%s/%s", frr_runstatedir, "watchfrr.started"); fp = fopen(started, "w"); if (fp) @@ -911,7 +918,7 @@ static void phase_check(void) "Phased restart: all routing daemon stop jobs have completed."); set_phase(PHASE_WAITING_DOWN); - /*FALLTHRU*/ + fallthrough; case PHASE_WAITING_DOWN: if (gs.numdown + IS_UP(gs.special) < gs.numdaemons) break; @@ -921,7 +928,7 @@ static void phase_check(void) 1); set_phase(PHASE_ZEBRA_RESTART_PENDING); - /*FALLTHRU*/ + fallthrough; case PHASE_ZEBRA_RESTART_PENDING: if (gs.special->restart.pid) break; @@ -930,7 +937,7 @@ static void phase_check(void) gs.special->name); set_phase(PHASE_WAITING_ZEBRA_UP); - /*FALLTHRU*/ + fallthrough; case PHASE_WAITING_ZEBRA_UP: if (!IS_UP(gs.special)) break; @@ -1347,19 +1354,20 @@ static struct frr_signal_t watchfrr_signals[] = { }, }; +/* clang-format off */ FRR_DAEMON_INFO(watchfrr, WATCHFRR, - .flags = FRR_NO_PRIVSEP | FRR_NO_TCPVTY | FRR_LIMITED_CLI - | FRR_NO_CFG_PID_DRY | FRR_NO_ZCLIENT - | FRR_DETACH_LATER, + .flags = FRR_NO_PRIVSEP | FRR_NO_TCPVTY | FRR_LIMITED_CLI + | FRR_NO_CFG_PID_DRY | FRR_NO_ZCLIENT | FRR_DETACH_LATER, - .printhelp = printhelp, - .copyright = "Copyright 2004 Andrew J. Schorr", + .printhelp = printhelp, + .copyright = "Copyright 2004 Andrew J. Schorr", - .signals = watchfrr_signals, - .n_signals = array_size(watchfrr_signals), + .signals = watchfrr_signals, + .n_signals = array_size(watchfrr_signals), - .privs = &watchfrr_privs, + .privs = &watchfrr_privs, ); +/* clang-format on */ #define DEPRECATED_OPTIONS "aAezR:" diff --git a/watchfrr/watchfrr_vty.c b/watchfrr/watchfrr_vty.c index 3afc767..95a7962 100644 --- a/watchfrr/watchfrr_vty.c +++ b/watchfrr/watchfrr_vty.c @@ -6,6 +6,8 @@ */ #include + +#include #include #include "memory.h" @@ -16,6 +18,8 @@ #include "watchfrr.h" +#include "lib/config_paths.h" + pid_t integrated_write_pid; static int integrated_result_fd; diff --git a/yang/frr-affinity-map.yang b/yang/frr-affinity-map.yang index c4377e6..f1d9e44 100644 --- a/yang/frr-affinity-map.yang +++ b/yang/frr-affinity-map.yang @@ -53,12 +53,22 @@ module frr-affinity-map { "Initial revision"; } + typedef affinity-map-ref { + type leafref { + path "/frr-affinity-map:lib/frr-affinity-map:affinity-maps/frr-affinity-map:affinity-map/frr-affinity-map:name"; + require-instance true; + } + description + "Reference to an affinity map"; + } + container lib { container affinity-maps { description "Affinity Mapping Table"; list affinity-map { key "name"; + unique "value"; description "Affinity Mapping configuration"; leaf name { @@ -69,6 +79,7 @@ module frr-affinity-map { "Affinity Name"; } leaf value { + mandatory true; type uint16 { range "0..1023"; } diff --git a/yang/frr-bgp-neighbor.yang b/yang/frr-bgp-neighbor.yang index 5a4c379..b199ab9 100644 --- a/yang/frr-bgp-neighbor.yang +++ b/yang/frr-bgp-neighbor.yang @@ -76,7 +76,7 @@ submodule frr-bgp-neighbor { leaf enforce-first-as { type boolean; - default "false"; + default "true"; description "When set to 'true' it will enforce the first AS for EBGP routes."; } diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index c50c513..c679f3b 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -379,6 +379,7 @@ identity set-extcommunity-color { grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; + default false; description "Non-Transitive Two-Octet AS-Specific Extended Community"; } @@ -769,6 +770,7 @@ identity set-extcommunity-color { + "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-extcommunity')"; container comm-list { leaf comm-list-name { + mandatory true; type bgp-filter:bgp-list-name; } @@ -872,11 +874,13 @@ identity set-extcommunity-color { description "Value of the ext-community."; leaf lb-type { + mandatory true; type frr-bgp-route-map:extcommunity-lb-type; } leaf bandwidth { when "../lb-type = 'explicit-bandwidth'"; + mandatory true; type uint16 { range "1..25600"; } @@ -1108,12 +1112,14 @@ identity set-extcommunity-color { container aggregator { leaf aggregator-asn { type asn-type; + mandatory true; description "ASN of the aggregator"; } leaf aggregator-address { type inet:ipv4-address; + mandatory true; description "IPv4 address of the aggregator"; } diff --git a/yang/frr-eigrpd.yang b/yang/frr-eigrpd.yang index e9071c8..f672dd5 100644 --- a/yang/frr-eigrpd.yang +++ b/yang/frr-eigrpd.yang @@ -22,6 +22,9 @@ module frr-eigrpd { import frr-route-types { prefix frr-route-types; } + import frr-filter { + prefix frr-filter; + } organization "FRRouting"; contact @@ -224,6 +227,8 @@ module frr-eigrpd { type inet:ipv4-address; } + uses frr-filter:distribute-list-group; + list redistribute { description "Redistribute routes learned from other routing protocols"; diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang index a1946d8..9b65fcc 100644 --- a/yang/frr-filter.yang +++ b/yang/frr-filter.yang @@ -10,6 +10,9 @@ module frr-filter { import ietf-yang-types { prefix yang; } + import frr-interface { + prefix frr-interface; + } organization "FRRouting"; contact @@ -45,35 +48,95 @@ module frr-filter { revision 2019-07-04 { description "Initial revision"; + reference "FRRouting"; } /* * Types. */ typedef access-list-name { - description "Access list name formatting"; type string { length 1..128; } + description "Access list name formatting"; } typedef access-list-sequence { - description "Access list sequence number"; type uint32 { range "1..4294967295"; } + description "Access list sequence number"; } typedef access-list-action { - description "Access list return action on match"; type enumeration { enum deny { - description "Deny an entry"; value 0; + description "Deny an entry"; } enum permit { - description "Accept an entry"; value 1; + description "Accept an entry"; + } + } + description "Access list return action on match"; + } + + typedef access-list-ref { + type leafref { + path "/frr-filter:lib/frr-filter:access-list/frr-filter:name"; + require-instance false; + } + description "IPv4 or IPv6 access list reference"; + } + + typedef prefix-list-ref { + type leafref { + path "/frr-filter:lib/frr-filter:prefix-list/frr-filter:name"; + require-instance false; + } + description "IPv4 or IPv6 prefix list reference"; + } + + /* + * Grouping. + */ + grouping distribute-list-group { + description "Distribute list grouping"; + list distribute-list { + key "interface"; + description "Distribute list configuration"; + + leaf interface { + type union { + type frr-interface:interface-ref; + type empty; + } + description + "Interface to attach list to or empty for global."; + } + + container in { + description "Inbound filter list"; + leaf access-list { + type access-list-ref; + description "inbound access list"; + } + leaf prefix-list { + type prefix-list-ref; + description "inbound prefix list"; + } + } + container out { + description "Outbound filter list"; + leaf access-list { + type access-list-ref; + description "outbound access list"; + } + leaf prefix-list { + type prefix-list-ref; + description "outbound prefix list"; + } } } } @@ -82,77 +145,74 @@ module frr-filter { * Configuration data. */ container lib { + description "Filter library"; list access-list { - description "Access list instance"; - key "type name"; + description "Access list instance"; leaf type { - description "Access list content type"; type enumeration { enum ipv4 { - description "Internet Protocol address version 4"; - value 0; - } - enum ipv6 { - description "Internet Protocol address version 6"; - value 1; + value 0; + description "Internet Protocol address version 4"; + } + enum ipv6 { + value 1; + description "Internet Protocol address version 6"; } enum mac { - description "Media Access Control address"; value 2; + description "Media Access Control address"; } } + description "Access list content type"; } leaf name { - description "Access list name"; type access-list-name; + description "Access list name"; } leaf remark { - description "Access list remark"; type string; + description "Access list remark"; } list entry { - description "Access list entry"; - key "sequence"; - + description "Access list entry"; leaf sequence { - description "Access list sequence value"; type access-list-sequence; + description "Access list sequence value"; } - leaf action { - description "Access list action on match"; type access-list-action; mandatory true; + description "Access list action on match"; } choice value { - description "Access list value to match"; mandatory true; + description "Access list value to match"; case ipv4-prefix { when "../type = 'ipv4'"; choice style { - description "Access list entry style selection: zebra or cisco."; mandatory true; + description "Access list entry style selection: zebra or cisco."; case zebra { leaf ipv4-prefix { - description "Configure IPv4 prefix to match"; type inet:ipv4-prefix; mandatory true; + description "Configure IPv4 prefix to match"; } leaf ipv4-exact-match { - description "Exact match of prefix"; type boolean; default false; + description "Exact match of prefix"; } } case cisco { @@ -160,19 +220,20 @@ module frr-filter { description "Source value to match"; leaf host { - description "Host to match"; type inet:ipv4-address; + description "Host to match"; } container network { + description "Network to match"; leaf address { + type inet:ipv4-address; mandatory true; description "Network address part."; - type inet:ipv4-address; } leaf mask { + type inet:ipv4-address; mandatory true; description "Network mask/wildcard part."; - type inet:ipv4-address; } } leaf source-any { @@ -180,8 +241,8 @@ module frr-filter { * Was `any`, however it conflicts with `any` leaf * outside this choice. */ - description "Match any"; type empty; + description "Match any"; } } @@ -189,24 +250,25 @@ module frr-filter { description "Destination value to match"; leaf destination-host { - description "Host to match"; type inet:ipv4-address; + description "Host to match"; } container destination-network { + description "Destination network to match"; leaf address { + type inet:ipv4-address; mandatory true; description "Network address part."; - type inet:ipv4-address; } leaf mask { + type inet:ipv4-address; mandatory true; description "Network mask/wildcard part."; - type inet:ipv4-address; } } leaf destination-any { - description "Match any"; type empty; + description "Match any"; } } } @@ -216,29 +278,29 @@ module frr-filter { when "../type = 'ipv6'"; leaf ipv6-prefix { - description "Configure IPv6 prefix to match"; type inet:ipv6-prefix; mandatory true; + description "Configure IPv6 prefix to match"; } leaf ipv6-exact-match { - description "Exact match of prefix"; type boolean; default false; + description "Exact match of prefix"; } } case mac { when "../type = 'mac'"; leaf mac { - description "Configure MAC address to match"; type yang:mac-address; + description "Configure MAC address to match"; } } case any { leaf any { - description "Match anything"; type empty; + description "Match anything"; } } } @@ -246,108 +308,104 @@ module frr-filter { } list prefix-list { - description "Prefix list instance"; - key "type name"; - + description "Prefix list instance"; leaf type { - description "Prefix list type"; type enumeration { enum ipv4 { - description "Internet Protocol address version 4"; value 0; + description "Internet Protocol address version 4"; } enum ipv6 { - description "Internet Protocol address version 6"; value 1; + description "Internet Protocol address version 6"; } } + description "Prefix list type"; } leaf name { - description "Prefix list name"; type access-list-name; + description "Prefix list name"; } leaf remark { - description "Prefix list user description"; type string; + description "Prefix list user description"; } list entry { - description "Prefix list entry"; - key "sequence"; - + description "Prefix list entry"; leaf sequence { - description "Prefix list sequence value"; type access-list-sequence; + description "Prefix list sequence value"; } leaf action { - description "Prefix list action on match"; type access-list-action; mandatory true; + description "Prefix list action on match"; } choice value { - description "Prefix list value to match"; mandatory true; + description "Prefix list value to match"; case ipv4-prefix { leaf ipv4-prefix { - description "Configure IPv4 prefix to match"; type inet:ipv4-prefix; mandatory true; + description "Configure IPv4 prefix to match"; } leaf ipv4-prefix-length-greater-or-equal { - description - "Specifies if matching prefixes with length greater than - or equal to value"; type uint8 { range "0..32"; } + description + "Specifies if matching prefixes with length greater than + or equal to value"; } leaf ipv4-prefix-length-lesser-or-equal { - description - "Specifies if matching prefixes with length lesser than - or equal to value"; type uint8 { range "0..32"; } + description + "Specifies if matching prefixes with length lesser than + or equal to value"; } } case ipv6-prefix { leaf ipv6-prefix { - description "Configure IPv6 prefix to match"; type inet:ipv6-prefix; mandatory true; + description "Configure IPv6 prefix to match"; } leaf ipv6-prefix-length-greater-or-equal { - description - "Specifies if matching prefixes with length greater than - or equal to value"; type uint8 { range "0..128"; } + description + "Specifies if matching prefixes with length greater than + or equal to value"; } leaf ipv6-prefix-length-lesser-or-equal { - description - "Specifies if matching prefixes with length lesser than - or equal to value"; type uint8 { range "0..128"; } + description + "Specifies if matching prefixes with length lesser than + or equal to value"; } } case any { leaf any { - description "Match anything"; type empty; + description "Match anything"; } } } diff --git a/yang/frr-interface.yang b/yang/frr-interface.yang index 012c96b..fc5a290 100644 --- a/yang/frr-interface.yang +++ b/yang/frr-interface.yang @@ -241,17 +241,18 @@ module frr-interface { } leaf mtu { - type uint16; + type uint32; description - "The size of the largest IPV4 packet that the interface - will send and receive."; + "The size of the largest IPV4 packet that the interface will send. + Normally this will never be larger than 65535; however, some devices + (e.g., vrf) can have larger values"; } leaf mtu6 { type uint32; description "The size of the largest IPV6 packet that the interface - will send and receive."; + will send."; } leaf speed { diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 5f85a4c..d65ee48 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -16,6 +16,9 @@ module frr-ripd { import frr-bfdd { prefix frr-bfdd; } + import frr-filter { + prefix frr-filter; + } import frr-interface { prefix frr-interface; } @@ -258,6 +261,9 @@ module frr-ripd { "A list of interfaces where the sending of RIP packets is enabled."; } + + uses frr-filter:distribute-list-group; + list redistribute { key "protocol"; description @@ -380,9 +386,9 @@ module frr-ripd { } leaf default-bfd-profile { + type frr-bfdd:profile-ref; description "Use this BFD profile for all peers by default."; - type frr-bfdd:profile-ref; } /* @@ -691,12 +697,13 @@ module frr-ripd { container bfd-monitoring { presence "Present if BFD is configured for RIP peers in this interface."; + description "Configure BFD use in RIPD"; leaf enable { type boolean; + default false; description "Enable/disable BFD monitoring."; - default false; } leaf profile { diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index 4aeaf36..383b45f 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -13,6 +13,9 @@ module frr-ripngd { import frr-if-rmap { prefix frr-if-rmap; } + import frr-filter { + prefix frr-filter; + } import frr-interface { prefix frr-interface; } @@ -63,6 +66,7 @@ module frr-ripngd { description "Changed interface references to use frr-interface:interface-ref typedef"; + reference "FRRouting"; } revision 2018-11-27 { description @@ -72,6 +76,7 @@ module frr-ripngd { } container ripngd { + description "ripng routing instance data"; /* * Routing instance configuration. */ @@ -169,15 +174,18 @@ module frr-ripngd { "A list of interfaces where the sending of RIPng packets is disabled."; } + + uses frr-filter:distribute-list-group; + list redistribute { key "protocol"; description "Redistributes routes learned from other routing protocols."; leaf protocol { type frr-route-types:frr-route-types-v6; + must '. != "ripng"'; description "Routing protocol."; - must '. != "ripng"'; } leaf route-map { type frr-route-map:route-map-ref; @@ -330,6 +338,7 @@ module frr-ripngd { * Per-interface configuration data */ augment "/frr-interface:lib/frr-interface:interface" { + description "RIPng interface augmentation."; container ripng { description "RIPng interface parameters."; diff --git a/yang/frr-route-types.yang b/yang/frr-route-types.yang index 728607c..aa676ce 100644 --- a/yang/frr-route-types.yang +++ b/yang/frr-route-types.yang @@ -54,44 +54,47 @@ module frr-route-types { enum connected { value 2; } - enum static { + enum local { value 3; } - enum rip { + enum static { value 4; } + enum rip { + value 5; + } enum ospf { - value 6; + value 7; } enum isis { - value 8; + value 9; } enum bgp { - value 9; + value 10; } enum eigrp { - value 11; + value 12; } enum nhrp { - value 12; + value 13; } enum table { - value 15; + value 16; } enum vnc { - value 17; + value 18; } enum vnc-direct { - value 18; + value 19; } enum babel { - value 22; + value 23; } enum sharp { - value 23; + value 24; } enum openfabric { - value 26; + value 27; } } } @@ -104,41 +107,44 @@ module frr-route-types { enum connected { value 2; } - enum static { + enum local { value 3; } + enum static { + value 4; + } enum ripng { - value 5; + value 6; } enum ospf6 { - value 7; + value 8; } enum isis { - value 8; + value 9; } enum bgp { - value 9; + value 10; } enum nhrp { - value 12; + value 13; } enum table { - value 15; + value 16; } enum vnc { - value 17; + value 18; } enum vnc-direct { - value 18; + value 19; } enum babel { - value 22; + value 23; } enum sharp { - value 23; + value 24; } enum openfabric { - value 26; + value 27; } } } diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang index d6e7188..6cc60e8 100644 --- a/yang/frr-test-module.yang +++ b/yang/frr-test-module.yang @@ -82,5 +82,23 @@ module frr-test-module { } } } + choice achoice { + description "a choice statement"; + case case1 { + leaf c1value { + type uint8; + description "A uint8 value for case 1"; + } + } + case case2 { + container c2cont { + description "case 2 container"; + leaf c2value { + type uint32; + description "A uint32 value for case 2"; + } + } + } + } } } diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 3c6e451..c338a23 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -81,6 +81,16 @@ module frr-zebra { "Initial revision."; } + feature ipv6-router-advertisements { + description + "Support for IPv6 Router Advertisements."; + } + + feature ptm-bfd { + description + "Using an external PTM daemon that implements BFD."; + } + typedef unix-timestamp { type uint32; units "seconds"; @@ -1935,19 +1945,23 @@ module frr-zebra { description "Extends interface model with Zebra-related parameters."; container zebra { - list ip-addrs { - key "address-family ip-prefix"; + list ipv4-addrs { + key "ip prefix-length"; description - "IP prefixes for an interface."; - uses frr-rt:address-family { + "The list of configured IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; description - "Address family of the RIB."; + "The IPv4 address on the interface."; } - leaf ip-prefix { - type inet:ip-prefix; + leaf prefix-length { + type uint8 { + range "0..32"; + } description - "IP address prefix."; + "The length of the subnet prefix."; } leaf label { @@ -1955,12 +1969,57 @@ module frr-zebra { description "Optional string label for the address."; } + } + + list ipv4-p2p-addrs { + key "ip peer-ip peer-prefix-length"; + description + "The list of configured peer-to-peer IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address on the interface."; + } + + leaf peer-ip { + type inet:ipv4-address-no-zone; + description + "Peer address."; + } + + leaf peer-prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the peer subnet prefix."; + } - leaf ip4-peer { - when "derived-from-or-self(../address-family, 'frr-rt:ipv4')"; - type inet:ipv4-prefix; + leaf label { + type string; description - "Peer prefix, for peer-to-peer interfaces."; + "Optional string label for the address."; + } + } + + list ipv6-addrs { + key "ip prefix-length"; + description + "The list of configured IPv6 addresses on the interface."; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address on the interface."; + } + + leaf prefix-length { + type uint8 { + range "0..128"; + } + description + "The length of the subnet prefix."; } } @@ -1972,11 +2031,12 @@ module frr-zebra { leaf link-detect { type boolean; + default "true"; description "Link-detection for the interface."; } - leaf shutdown { + leaf enabled { type boolean; description "Interface admin status."; @@ -1990,14 +2050,69 @@ module frr-zebra { leaf bandwidth { type uint32 { - range "1..100000"; + range "1..1000000"; } + units "megabits/sec"; description "Link bandwidth informational parameter, in megabits."; } container link-params { + presence "Activates link parameters on this interface."; description "link-params for Traffic-Engineering (TE) use in IGP extensions."; + leaf metric { + type uint32; + description + "Link metric for MPLS-TE purpose."; + } + leaf max-bandwidth { + type rt-types:bandwidth-ieee-float32; + description + "Maximum bandwidth."; + } + leaf max-reservable-bandwidth { + type rt-types:bandwidth-ieee-float32; + description + "Maximum reservable bandwidth."; + } + container unreserved-bandwidths { + description + "All unreserved bandwidths."; + list unreserved-bandwidth { + key "priority"; + leaf priority { + type uint8 { + range "0 .. 7"; + } + description + "Priority from 0 to 7."; + } + leaf unreserved-bandwidth { + type rt-types:bandwidth-ieee-float32; + mandatory true; + description + "Unreserved bandwidth."; + } + description + "List of unreserved bandwidths for different + priorities."; + } + } + leaf residual-bandwidth { + type rt-types:bandwidth-ieee-float32; + description + "Unidirectional residual bandwidth."; + } + leaf available-bandwidth { + type rt-types:bandwidth-ieee-float32; + description + "Unidirectional available bandwidth."; + } + leaf utilized-bandwidth { + type rt-types:bandwidth-ieee-float32; + description + "Unidirectional utilized bandwidth."; + } choice admin-group-mode { description "Admin-group mode"; case legacy { @@ -2011,8 +2126,11 @@ module frr-zebra { case affinity { container affinities { leaf-list affinity { - type string; + type frr-affinity-map:affinity-map-ref; max-elements "256"; + must '../../affinity-mode != "standard" or /frr-affinity-map:lib/frr-affinity-map:affinity-maps/frr-affinity-map:affinity-map[frr-affinity-map:name=current()]/frr-affinity-map:value < 32' { + error-message "Affinity bit-position must be less than 32 when used with standard affinity mode"; + } description "Array of Attribute Names"; } @@ -2041,9 +2159,526 @@ module frr-zebra { } } } + container neighbor { + description "Remote ASBR information (RFC 5316 & RFC 5392)"; + presence "Activates neighbor information on this interface."; + leaf remote-as { + type inet:as-number; + mandatory true; + description + "Remote AS Number (RFC 5316 & RFC 5392)"; + } + leaf ipv4-remote-id { + type inet:ipv4-address; + mandatory true; + description + "IPv4 Remote ASBR ID (RFC 5316 & RFC 5392)"; + } + } + leaf delay { + type uint32 { + range "0..16777215"; + } + description + "Average Unidirectional Link Delay"; + } + container min-max-delay { + description + "Min/Max Unidirectional Link Delay"; + presence "Activates min/max delay."; + leaf delay-min { + type uint32 { + range "0..16777215"; + } + must '. <= ../../delay' { + error-message "Min delay must be less than or equal to delay"; + } + mandatory true; + description + "Min Delay"; + } + leaf delay-max { + type uint32 { + range "0..16777215"; + } + must '. >= ../../delay' { + error-message "Max delay must be greater than or equal to delay"; + } + mandatory true; + description + "Max Delay"; + } + } + leaf delay-variation { + type uint32 { + range "0..16777215"; + } + description + "Unidirectional Delay Variation"; + } + leaf packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + description + "Unidirectional Link Packet Loss"; + } // TODO -- other link-params options // for (experimental/partial TE use in IGP extensions) } + container evpn-mh { + description "EVPN multihoming configuration"; + choice esi-choice { + description "ESI type"; + container type-0 { + leaf esi { + type yang:hex-string { + length "29"; + } + description + "10-octet ESI."; + } + } + container type-3 { + leaf system-mac { + type yang:mac-address; + description + "System MAC address."; + } + leaf local-discriminator { + type uint32 { + range "1..16777215"; + } + description + "Local discriminator."; + } + } + } + leaf df-preference { + type uint16; + default "32767"; + description + "Preference value used for DF election."; + } + leaf bypass { + type boolean; + default "false"; + description + "Bypass mode."; + } + leaf uplink { + type boolean; + default "false"; + description + "Uplink to the VxLAN core."; + } + } + container ipv6-router-advertisements { + if-feature "ipv6-router-advertisements"; + description + "Support for IPv6 Router Advertisements."; + leaf send-advertisements { + type boolean; + default "false"; + description + "A flag indicating whether or not the router sends + periodic Router Advertisements and responds to + Router Solicitations."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvSendAdvertisements"; + } + leaf max-rtr-adv-interval { + type uint32 { + range "70..1800000"; + } + units "milliseconds"; + default "600000"; + description + "The maximum time allowed between sending unsolicited + multicast Router Advertisements from the interface."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - MaxRtrAdvInterval + RFC 6275: Mobility Support in IPv6"; + } + // Setting this value is not yet supported by the actual code. + /* + leaf min-rtr-adv-interval { + type uint32 { + range "30..1350000"; + } + units "milliseconds"; + must ". <= 0.75 * ../max-rtr-adv-interval" { + description + "The value MUST NOT be greater than 75% of + 'max-rtr-adv-interval'."; + } + description + "The minimum time allowed between sending unsolicited + multicast Router Advertisements from the interface. + + The default value to be used operationally if this + leaf is not configured is determined as follows: + + - if max-rtr-adv-interval >= 9 seconds, the default + value is 0.33 * max-rtr-adv-interval; + + - otherwise, it is 0.75 * max-rtr-adv-interval."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - MaxRtrAdvInterval + RFC 6275: Mobility Support in IPv6"; + } + */ + leaf managed-flag { + type boolean; + default "false"; + description + "The value to be placed in the 'Managed address + configuration' flag field in the Router + Advertisement."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvManagedFlag"; + } + leaf other-config-flag { + type boolean; + default "false"; + description + "The value to be placed in the 'Other configuration' + flag field in the Router Advertisement."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvOtherConfigFlag"; + } + leaf home-agent-flag { + type boolean; + default "false"; + description + "The value to be placed in the 'Home Agent' + flag field in the Router Advertisement."; + reference + "RFC 6275: Mobility Support in IPv6"; + } + leaf link-mtu { + type uint32; + default "0"; + description + "The value to be placed in MTU options sent by the + router. A value of zero indicates that no MTU options + are sent."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvLinkMTU"; + } + leaf reachable-time { + type uint32 { + range "0..3600000"; + } + units "milliseconds"; + default "0"; + description + "The value to be placed in the Reachable Time field in + the Router Advertisement messages sent by the router. + A value of zero means unspecified (by this router)."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvReachableTime"; + } + leaf retrans-timer { + type uint32; + units "milliseconds"; + default "0"; + description + "The value to be placed in the Retrans Timer field in + the Router Advertisement messages sent by the router. + A value of zero means unspecified (by this router)."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvRetransTimer"; + } + leaf cur-hop-limit { + type uint8; + description + "The value to be placed in the Cur Hop Limit field in + the Router Advertisement messages sent by the router. + A value of zero means unspecified (by this router). + + If this parameter is not configured, the device SHOULD + use the IANA-specified value for the default IPv4 + Time to Live (TTL) parameter that was in effect at the + time of implementation."; + reference + "RFC 3232: Assigned Numbers: RFC 1700 is Replaced by + an On-line Database + RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvCurHopLimit + IANA: IP Parameters + (https://www.iana.org/assignments/ip-parameters)"; + } + leaf default-lifetime { + type uint16 { + range "0..9000"; + } + units "seconds"; + must ". = 0 or . * 1000 >= ../max-rtr-adv-interval" { + description + "The value MUST NOT be less than max-rtr-adv-interval."; + } + description + "The value to be placed in the Router Lifetime field of + Router Advertisements sent from the interface, in + seconds. It MUST be either zero or between + max-rtr-adv-interval and 9000 seconds. A value of zero + indicates that the router is not to be used as a + default router. These limits may be overridden by + specific documents that describe how IPv6 operates over + different link layers. + + If this parameter is not configured, the device SHOULD + use a value of 3 * max-rtr-adv-interval."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvDefaultLifetime"; + } + leaf fast-retransmit { + type boolean; + default "true"; + description + "Allow sending unsolicited multicast Router Advertisements + more frequently than once every 3 seconds as required by + RFC 4861."; + } + leaf advertisement-interval-option { + type boolean; + default "false"; + description + "Enable sending the Advertisement Interval Option in + Router Advertisements."; + reference + "RFC 6275: Mobility Support in IPv6"; + } + leaf home-agent-preference { + type uint16; + description + "The value to be placed in the Home Agent Preference + field in the Router Advertisement messages sent by the + router."; + reference + "RFC 6275: Mobility Support in IPv6"; + } + leaf home-agent-lifetime { + type uint16; + description + "The value to be placed in the Home Agent Lifetime + field in the Router Advertisement messages sent by the + router."; + reference + "RFC 6275: Mobility Support in IPv6"; + } + leaf default-router-preference { + type enumeration { + enum high { + value 1; /* 01 */ + description + "High preference."; + } + enum medium { + value 0; /* 00 */ + description + "Medium preference."; + } + enum low { + value 3; /* 11 */ + description + "Low preference."; + } + } + default "medium"; + description + "The value to be placed in the Default Router + Preference field in the Router Advertisement messages + sent by the router."; + reference + "RFC 4191: Default Router Preferences and More-Specific + Routes"; + } + container prefix-list { + description + "Support for prefixes to be placed in Prefix + Information options in Router Advertisement messages + sent from the interface. + + Prefixes that are advertised by default but do not + have their entries in the child 'prefix' list are + advertised with the default values of all parameters. + + The link-local prefix SHOULD NOT be included in the + list of advertised prefixes."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + - AdvPrefixList"; + list prefix { + key "prefix-spec"; + description + "Support for an advertised prefix entry."; + leaf prefix-spec { + type inet:ipv6-prefix; + description + "IPv6 address prefix."; + } + // FRR doesn't support 'no-advertise'. Keeping the code + // here for future reference. + /* + choice control-adv-prefixes { + default "advertise"; + description + "Either (1) the prefix is explicitly removed from the + set of advertised prefixes or (2) the parameters with + which the prefix is advertised are specified (default + case)."; + leaf no-advertise { + type empty; + description + "The prefix will not be advertised. + + This can be used for removing the prefix from + the default set of advertised prefixes."; + } + case advertise { + */ + leaf valid-lifetime { + type uint32; + units "seconds"; + default "2592000"; + description + "The value to be placed in the Valid Lifetime + in the Prefix Information option. The + designated value of all 1's (0xffffffff) + represents infinity."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 + (IPv6) - AdvValidLifetime"; + } + leaf on-link-flag { + type boolean; + default "true"; + description + "The value to be placed in the on-link flag + ('L-bit') field in the Prefix Information + option."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 + (IPv6) - AdvOnLinkFlag"; + } + leaf preferred-lifetime { + type uint32; + units "seconds"; + must ". <= ../valid-lifetime" { + description + "This value MUST NOT be greater than + valid-lifetime."; + } + default "604800"; + description + "The value to be placed in the Preferred + Lifetime in the Prefix Information option. + The designated value of all 1's (0xffffffff) + represents infinity."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 + (IPv6) - AdvPreferredLifetime"; + } + leaf autonomous-flag { + type boolean; + default "true"; + description + "The value to be placed in the Autonomous Flag + field in the Prefix Information option."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 + (IPv6) - AdvAutonomousFlag"; + } + leaf router-address-flag { + type boolean; + default "false"; + description + "The value to be placed in the Router Address + flag field in the Prefix Information option."; + reference + "RFC 6275: Mobility Support in IPv6"; + } + /* + } + } + */ + // This is closing brackets for `case advertise` and + // `choice control-adv-prefixes`. + } + } + container rdnss { + description + "A list of recursive DNS server addresses that are placed + in Recursive DNS Server (RDNSS) options in Router + Advertisement messages sent from the interface."; + reference + "RFC 8106: IPv6 Router Advertisement Options for DNS + Configuration"; + list rdnss-address { + key "address"; + description + "Recursive DNS server address."; + leaf address { + type inet:ipv6-address; + description + "IPv6 address of a recursive DNS server."; + } + leaf lifetime { + type uint32; + units "seconds"; + description + "The value that is placed in the Lifetime field in the + RDNSS option. The designated value of all 1's + (0xffffffff) represents infinity."; + } + } + } + container dnssl { + description + "A list of domain names that are placed in DNS Search List (DNSSL) + options in Router Advertisement messages sent from the interface."; + reference + "RFC 8106: IPv6 Router Advertisement Options for DNS + Configuration"; + list dnssl-domain { + key "domain"; + description + "Domain name for the search list."; + leaf domain { + type inet:domain-name; + description + "Domain name for the search list."; + } + leaf lifetime { + type uint32; + units "seconds"; + description + "The value that is placed in the Lifetime field in the + DNSSL option. The designated value of all 1's + (0xffffffff) represents infinity."; + } + } + } + } + leaf ptm-enable { + if-feature ptm-bfd; + type boolean; + default "true"; + description + "Enable PTM on the interface."; + } container state { config false; description @@ -2108,6 +2743,112 @@ module frr-zebra { container zebra { description "Zebra's vrf specific configuration and operational model."; + + leaf router-id { + type yang:dotted-quad; + description + "A 32-bit number in the form of a dotted quad that is used by + some routing protocols identifying a router."; + } + + leaf ipv6-router-id { + type inet:ipv6-address-no-zone; + description + "A 128-bit number in the form of an IPv6 address that is used by + some routing protocols identifying a router."; + } + + list filter-protocol { + key "afi-safi protocol"; + description + "Filter routing info exchanged between zebra and protocol."; + leaf afi-safi { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI-SAFI type."; + } + leaf protocol { + // This should be identityref to frr-rt:control-plane-protocol someday + type string; + description + "The protocol to filter."; + } + leaf route-map { + type frr-route-map:route-map-ref; + mandatory true; + description + "A route-map to filter routes."; + } + } + + list filter-nht { + key "afi-safi protocol"; + description + "Filter next hop tracking route resolution."; + leaf afi-safi { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI-SAFI type."; + } + leaf protocol { + // This should be identityref to frr-rt:control-plane-protocol someday + type string; + description + "The protocol to filter."; + } + leaf route-map { + type frr-route-map:route-map-ref; + mandatory true; + description + "A route-map to filter nexthops."; + } + } + + leaf resolve-via-default { + type boolean; + description + "Resolve IPv4 nexthops via the default route. This is true by default + for traditional profile and false by default for datacenter profile. + Removing the leaf sets it back to the default value for the profile."; + } + + leaf ipv6-resolve-via-default { + type boolean; + description + "Resolve IPv4 nexthops via the default route. This is true by default + for traditional profile and false by default for datacenter profile. + Removing the leaf sets it back to the default value for the profile."; + } + + container netns { + description + "Configuration for netns VRF backend."; + container table-range { + presence "Activates table-range configuration."; + description + "The range of tables to use for this netns."; + leaf start { + type uint32; + mandatory true; + description + "The first table to use."; + } + leaf end { + type uint32; + mandatory true; + must ". >= ../start" { + error-message "End table must be greater than or equal to start table"; + } + description + "The last table to use."; + } + } + } + uses ribs; uses vrf-vni-mapping; @@ -2213,6 +2954,22 @@ module frr-zebra { description "Limit on the number of updates queued to the dataplane subsystem."; } + leaf ptm-enable { + if-feature ptm-bfd; + type boolean; + default "false"; + description + "Enable PTM globally."; + } + leaf route-map-delay { + type uint32 { + range "0..600"; + } + units "seconds"; + default "5"; + description + "Time to wait before route-map updates are processed."; + } /* * Debug options */ diff --git a/yang/ietf/ietf-netconf-acm.yang b/yang/ietf/ietf-netconf-acm.yang new file mode 100644 index 0000000..f7e02f2 --- /dev/null +++ b/yang/ietf/ietf-netconf-acm.yang @@ -0,0 +1,464 @@ +module ietf-netconf-acm { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; + + prefix nacm; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: + WG List: + + Author: Andy Bierman + + + Author: Martin Bjorklund + "; + + description + "Network Configuration Access Control Model. + + Copyright (c) 2012 - 2018 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8341; see + the RFC itself for full legal notices."; + + revision 2018-02-14 { + description + "Added support for YANG 1.1 actions and notifications tied to + data nodes. Clarified how NACM extensions can be used by + other data models."; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + revision 2012-02-22 { + description + "Initial version."; + reference + "RFC 6536: Network Configuration Protocol (NETCONF) + Access Control Model"; + } + + /* + * Extension statements + */ + + extension default-deny-write { + description + "Used to indicate that the data model node + represents a sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have write access to the node. An + explicit access control rule is required for all other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-write' extension MAY appear within a data + definition statement. It is ignored otherwise."; + } + + extension default-deny-all { + description + "Used to indicate that the data model node + controls a very sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have read, write, or execute access to + the node. An explicit access control rule is required for all + other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-all' extension MAY appear within a data + definition statement, 'rpc' statement, or 'notification' + statement. It is ignored otherwise."; + } + + /* + * Derived types + */ + + typedef user-name-type { + type string { + length "1..max"; + } + description + "General-purpose username string."; + } + + typedef matchall-string-type { + type string { + pattern '\*'; + } + description + "The string containing a single asterisk '*' is used + to conceptually represent all possible values + for the particular leaf using this data type."; + } + + typedef access-operations-type { + type bits { + bit create { + description + "Any protocol operation that creates a + new data node."; + } + bit read { + description + "Any protocol operation or notification that + returns the value of a data node."; + } + bit update { + description + "Any protocol operation that alters an existing + data node."; + } + bit delete { + description + "Any protocol operation that removes a data node."; + } + bit exec { + description + "Execution access to the specified protocol operation."; + } + } + description + "Access operation."; + } + + typedef group-name-type { + type string { + length "1..max"; + pattern '[^\*].*'; + } + description + "Name of administrative group to which + users can be assigned."; + } + + typedef action-type { + type enumeration { + enum permit { + description + "Requested action is permitted."; + } + enum deny { + description + "Requested action is denied."; + } + } + description + "Action taken by the server when a particular + rule matches."; + } + + typedef node-instance-identifier { + type yang:xpath1.0; + description + "Path expression used to represent a special + data node, action, or notification instance-identifier + string. + + A node-instance-identifier value is an + unrestricted YANG instance-identifier expression. + All the same rules as an instance-identifier apply, + except that predicates for keys are optional. If a key + predicate is missing, then the node-instance-identifier + represents all possible server instances for that key. + + This XML Path Language (XPath) expression is evaluated in the + following context: + + o The set of namespace declarations are those in scope on + the leaf element where this type is used. + + o The set of variable bindings contains one variable, + 'USER', which contains the name of the user of the + current session. + + o The function library is the core function library, but + note that due to the syntax restrictions of an + instance-identifier, no functions are allowed. + + o The context node is the root node in the data tree. + + The accessible tree includes actions and notifications tied + to data nodes."; + } + + /* + * Data definition statements + */ + + container nacm { + nacm:default-deny-all; + + description + "Parameters for NETCONF access control model."; + + leaf enable-nacm { + type boolean; + default "true"; + description + "Enables or disables all NETCONF access control + enforcement. If 'true', then enforcement + is enabled. If 'false', then enforcement + is disabled."; + } + + leaf read-default { + type action-type; + default "permit"; + description + "Controls whether read access is granted if + no appropriate rule is found for a + particular read request."; + } + + leaf write-default { + type action-type; + default "deny"; + description + "Controls whether create, update, or delete access + is granted if no appropriate rule is found for a + particular write request."; + } + + leaf exec-default { + type action-type; + default "permit"; + description + "Controls whether exec access is granted if no appropriate + rule is found for a particular protocol operation request."; + } + + leaf enable-external-groups { + type boolean; + default "true"; + description + "Controls whether the server uses the groups reported by the + NETCONF transport layer when it assigns the user to a set of + NACM groups. If this leaf has the value 'false', any group + names reported by the transport layer are ignored by the + server."; + } + + leaf denied-operations { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request was denied."; + } + + leaf denied-data-writes { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request to alter + a configuration datastore was denied."; + } + + leaf denied-notifications { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that + a notification was dropped for a subscription because + access to the event type was denied."; + } + + container groups { + description + "NETCONF access control groups."; + + list group { + key name; + + description + "One NACM group entry. This list will only contain + configured entries, not any entries learned from + any transport protocols."; + + leaf name { + type group-name-type; + description + "Group name associated with this entry."; + } + + leaf-list user-name { + type user-name-type; + description + "Each entry identifies the username of + a member of the group associated with + this entry."; + } + } + } + + list rule-list { + key name; + ordered-by user; + description + "An ordered collection of access control rules."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule-list."; + } + leaf-list group { + type union { + type matchall-string-type; + type group-name-type; + } + description + "List of administrative groups that will be + assigned the associated access rights + defined by the 'rule' list. + + The string '*' indicates that all groups apply to the + entry."; + } + + list rule { + key name; + ordered-by user; + description + "One access control rule. + + Rules are processed in user-defined order until a match is + found. A rule matches if 'module-name', 'rule-type', and + 'access-operations' match the request. If a rule + matches, the 'action' leaf determines whether or not + access is granted."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule."; + } + + leaf module-name { + type union { + type matchall-string-type; + type string; + } + default "*"; + description + "Name of the module associated with this rule. + + This leaf matches if it has the value '*' or if the + object being accessed is defined in the module with the + specified module name."; + } + choice rule-type { + description + "This choice matches if all leafs present in the rule + match the request. If no leafs are present, the + choice matches all requests."; + case protocol-operation { + leaf rpc-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if + its value equals the requested protocol operation + name."; + } + } + case notification { + leaf notification-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if its + value equals the requested notification name."; + } + } + + case data-node { + leaf path { + type node-instance-identifier; + mandatory true; + description + "Data node instance-identifier associated with the + data node, action, or notification controlled by + this rule. + + Configuration data or state data + instance-identifiers start with a top-level + data node. A complete instance-identifier is + required for this type of path value. + + The special value '/' refers to all possible + datastore contents."; + } + } + } + + leaf access-operations { + type union { + type matchall-string-type; + type access-operations-type; + } + default "*"; + description + "Access operations associated with this rule. + + This leaf matches if it has the value '*' or if the + bit corresponding to the requested operation is set."; + } + + leaf action { + type action-type; + mandatory true; + description + "The access control action associated with the + rule. If a rule has been determined to match a + particular request, then this object is used + to determine whether to permit or deny the + request."; + } + + leaf comment { + type string; + description + "A textual description of the access rule."; + } + } + } + } +} diff --git a/yang/ietf/ietf-netconf-with-defaults.yang b/yang/ietf/ietf-netconf-with-defaults.yang new file mode 100644 index 0000000..05ff399 --- /dev/null +++ b/yang/ietf/ietf-netconf-with-defaults.yang @@ -0,0 +1,139 @@ +module ietf-netconf-with-defaults { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"; + + prefix ncwd; + + import ietf-netconf { prefix nc; } + + organization + "IETF NETCONF (Network Configuration Protocol) Working Group"; + + contact + "WG Web: + + WG List: + + WG Chair: Bert Wijnen + + + WG Chair: Mehmet Ersue + + + Editor: Andy Bierman + + + Editor: Balazs Lengyel + "; + + description + "This module defines an extension to the NETCONF protocol + that allows the NETCONF client to control how default + values are handled by the server in particular NETCONF + operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6243; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial version."; + reference + "RFC 6243: With-defaults Capability for NETCONF"; + } + + typedef with-defaults-mode { + description + "Possible modes to report default data."; + reference + "RFC 6243; Section 3."; + type enumeration { + enum report-all { + description + "All default data is reported."; + reference + "RFC 6243; Section 3.1"; + } + enum report-all-tagged { + description + "All default data is reported. + Any nodes considered to be default data + will contain a 'default' XML attribute, + set to 'true' or '1'."; + reference + "RFC 6243; Section 3.4"; + } + enum trim { + description + "Values are not reported if they contain the default."; + reference + "RFC 6243; Section 3.2"; + } + enum explicit { + description + "Report values that contain the definition of + explicitly set data."; + reference + "RFC 6243; Section 3.3"; + } + } + } + + grouping with-defaults-parameters { + description + "Contains the parameter for control + of defaults in NETCONF retrieval operations."; + + leaf with-defaults { + description + "The explicit defaults processing mode requested."; + reference + "RFC 6243; Section 4.5.1"; + + type with-defaults-mode; + } + } + + // extending the get-config operation + augment /nc:get-config/nc:input { + description + "Adds the parameter to the + input of the NETCONF operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the get operation + augment /nc:get/nc:input { + description + "Adds the parameter to + the input of the NETCONF operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the copy-config operation + augment /nc:copy-config/nc:input { + description + "Adds the parameter to + the input of the NETCONF operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } +} diff --git a/yang/ietf/ietf-netconf.yang b/yang/ietf/ietf-netconf.yang new file mode 100644 index 0000000..93927f1 --- /dev/null +++ b/yang/ietf/ietf-netconf.yang @@ -0,0 +1,933 @@ +module ietf-netconf { + + // the namespace for NETCONF XML definitions is unchanged + // from RFC 4741, which this document replaces + namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; + + prefix nc; + + import ietf-inet-types { + prefix inet; + } + + import ietf-netconf-acm { prefix nacm; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: Bert Wijnen + + + WG Chair: Mehmet Ersue + + + Editor: Martin Bjorklund + + + Editor: Juergen Schoenwaelder + + + Editor: Andy Bierman + "; + description + "NETCONF Protocol Data Types and Protocol Operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6241; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial revision; + 2013-09-29: Updated to include NACM attributes, + as specified in RFC 6536: sec 3.2.5 and 3.2.8"; + reference + "RFC 6241: Network Configuration Protocol"; + } + + extension get-filter-element-attributes { + description + "If this extension is present within an 'anyxml' + statement named 'filter', which must be conceptually + defined within the RPC input section for the + and protocol operations, then the + following unqualified XML attribute is supported + within the element, within a or + protocol operation: + + type : optional attribute with allowed + value strings 'subtree' and 'xpath'. + If missing, the default value is 'subtree'. + + If the 'xpath' feature is supported, then the + following unqualified XML attribute is + also supported: + + select: optional attribute containing a + string representing an XPath expression. + The 'type' attribute must be equal to 'xpath' + if this attribute is present."; + } + + // NETCONF capabilities defined as features + feature writable-running { + description + "NETCONF :writable-running capability; + If the server advertises the :writable-running + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.2"; + } + + feature candidate { + description + "NETCONF :candidate capability; + If the server advertises the :candidate + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.3"; + } + + feature confirmed-commit { + if-feature candidate; + description + "NETCONF :confirmed-commit:1.1 capability; + If the server advertises the :confirmed-commit:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + + reference "RFC 6241, Section 8.4"; + } + + feature rollback-on-error { + description + "NETCONF :rollback-on-error capability; + If the server advertises the :rollback-on-error + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.5"; + } + + feature validate { + description + "NETCONF :validate:1.1 capability; + If the server advertises the :validate:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.6"; + } + + feature startup { + description + "NETCONF :startup capability; + If the server advertises the :startup + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.7"; + } + + feature url { + description + "NETCONF :url capability; + If the server advertises the :url + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.8"; + } + + feature xpath { + description + "NETCONF :xpath capability; + If the server advertises the :xpath + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.9"; + } + + // NETCONF Simple Types + + typedef session-id-type { + type uint32 { + range "1..max"; + } + description + "NETCONF Session Id"; + } + + typedef session-id-or-zero-type { + type uint32; + description + "NETCONF Session Id or Zero to indicate none"; + } + typedef error-tag-type { + type enumeration { + enum in-use { + description + "The request requires a resource that + already is in use."; + } + enum invalid-value { + description + "The request specifies an unacceptable value for one + or more parameters."; + } + enum too-big { + description + "The request or response (that would be generated) is + too large for the implementation to handle."; + } + enum missing-attribute { + description + "An expected attribute is missing."; + } + enum bad-attribute { + description + "An attribute value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-attribute { + description + "An unexpected attribute is present."; + } + enum missing-element { + description + "An expected element is missing."; + } + enum bad-element { + description + "An element value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-element { + description + "An unexpected element is present."; + } + enum unknown-namespace { + description + "An unexpected namespace is present."; + } + enum access-denied { + description + "Access to the requested protocol operation or + data model is denied because authorization failed."; + } + enum lock-denied { + description + "Access to the requested lock is denied because the + lock is currently held by another entity."; + } + enum resource-denied { + description + "Request could not be completed because of + insufficient resources."; + } + enum rollback-failed { + description + "Request to roll back some configuration change (via + rollback-on-error or operations) + was not completed for some reason."; + + } + enum data-exists { + description + "Request could not be completed because the relevant + data model content already exists. For example, + a 'create' operation was attempted on data that + already exists."; + } + enum data-missing { + description + "Request could not be completed because the relevant + data model content does not exist. For example, + a 'delete' operation was attempted on + data that does not exist."; + } + enum operation-not-supported { + description + "Request could not be completed because the requested + operation is not supported by this implementation."; + } + enum operation-failed { + description + "Request could not be completed because the requested + operation failed for some reason not covered by + any other error condition."; + } + enum partial-operation { + description + "This error-tag is obsolete, and SHOULD NOT be sent + by servers conforming to this document."; + } + enum malformed-message { + description + "A message could not be handled because it failed to + be parsed correctly. For example, the message is not + well-formed XML or it uses an invalid character set."; + } + } + description "NETCONF Error Tag"; + reference "RFC 6241, Appendix A"; + } + + typedef error-severity-type { + type enumeration { + enum error { + description "Error severity"; + } + enum warning { + description "Warning severity"; + } + } + description "NETCONF Error Severity"; + reference "RFC 6241, Section 4.3"; + } + + typedef edit-operation-type { + type enumeration { + enum merge { + description + "The configuration data identified by the + element containing this attribute is merged + with the configuration at the corresponding + level in the configuration datastore identified + by the target parameter."; + } + enum replace { + description + "The configuration data identified by the element + containing this attribute replaces any related + configuration in the configuration datastore + identified by the target parameter. If no such + configuration data exists in the configuration + datastore, it is created. Unlike a + operation, which replaces the + entire target configuration, only the configuration + actually present in the config parameter is affected."; + } + enum create { + description + "The configuration data identified by the element + containing this attribute is added to the + configuration if and only if the configuration + data does not already exist in the configuration + datastore. If the configuration data exists, an + element is returned with an + value of 'data-exists'."; + } + enum delete { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if and only if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, an element is returned with + an value of 'data-missing'."; + } + enum remove { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, the 'remove' operation is silently ignored + by the server."; + } + } + default "merge"; + description "NETCONF 'operation' attribute values"; + reference "RFC 6241, Section 7.2"; + } + + // NETCONF Standard Protocol Operations + + rpc get-config { + description + "Retrieve all or part of a specified configuration."; + + reference "RFC 6241, Section 7.1"; + + input { + container source { + description + "Particular configuration to retrieve."; + + choice config-source { + mandatory true; + description + "The configuration to retrieve."; + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source. + This is optional-to-implement on the server because + not all servers will support filtering for this + datastore."; + } + } + } + + anyxml filter { + description + "Subtree or XPath filter to use."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the source datastore subset that matched + the filter criteria (if any). An empty data container + indicates that the request did not produce any results."; + } + } + } + + rpc edit-config { + description + "The operation loads all or part of a specified + configuration to the specified target configuration."; + + reference "RFC 6241, Section 7.2"; + + input { + container target { + description + "Particular configuration to edit."; + + choice config-target { + mandatory true; + description + "The configuration target."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config source."; + } + } + } + + leaf default-operation { + type enumeration { + enum merge { + description + "The default operation is merge."; + } + enum replace { + description + "The default operation is replace."; + } + enum none { + description + "There is no default operation."; + } + } + default "merge"; + description + "The default operation to use."; + } + + leaf test-option { + if-feature validate; + type enumeration { + enum test-then-set { + description + "The server will test and then set if no errors."; + } + enum set { + description + "The server will set without a test first."; + } + + enum test-only { + description + "The server will only test and not set, even + if there are no errors."; + } + } + default "test-then-set"; + description + "The test option to use."; + } + + leaf error-option { + type enumeration { + enum stop-on-error { + description + "The server will stop on errors."; + } + enum continue-on-error { + description + "The server may continue on errors."; + } + enum rollback-on-error { + description + "The server will roll back on errors. + This value can only be used if the 'rollback-on-error' + feature is supported."; + } + } + default "stop-on-error"; + description + "The error option to use."; + } + + choice edit-content { + mandatory true; + description + "The content for the edit operation."; + + anyxml config { + description + "Inline Config content."; + } + leaf url { + if-feature url; + type inet:uri; + description + "URL-based config content."; + } + } + } + } + + rpc copy-config { + description + "Create or replace an entire configuration datastore with the + contents of another complete configuration datastore."; + + reference "RFC 6241, Section 7.3"; + + input { + container target { + description + "Particular configuration to copy to."; + + choice config-target { + mandatory true; + description + "The configuration target of the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config target. + This is optional-to-implement on the server."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + + container source { + description + "Particular configuration to copy from."; + + choice config-source { + mandatory true; + description + "The configuration source for the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } + + rpc delete-config { + nacm:default-deny-all; + description + "Delete a configuration datastore."; + + reference "RFC 6241, Section 7.4"; + + input { + container target { + description + "Particular configuration to delete."; + + choice config-target { + mandatory true; + description + "The configuration target to delete."; + + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + } + } + + rpc lock { + description + "The lock operation allows the client to lock the configuration + system of a device."; + + reference "RFC 6241, Section 7.5"; + + input { + container target { + description + "Particular configuration to lock."; + + choice config-target { + mandatory true; + description + "The configuration target to lock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc unlock { + description + "The unlock operation is used to release a configuration lock, + previously obtained with the 'lock' operation."; + + reference "RFC 6241, Section 7.6"; + + input { + container target { + description + "Particular configuration to unlock."; + + choice config-target { + mandatory true; + description + "The configuration target to unlock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc get { + description + "Retrieve running configuration and device state information."; + + reference "RFC 6241, Section 7.7"; + + input { + anyxml filter { + description + "This parameter specifies the portion of the system + configuration and state data to retrieve."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the running datastore subset and/or state + data that matched the filter criteria (if any). + An empty data container indicates that the request did not + produce any results."; + } + } + } + + rpc close-session { + description + "Request graceful termination of a NETCONF session."; + + reference "RFC 6241, Section 7.8"; + } + + rpc kill-session { + nacm:default-deny-all; + description + "Force the termination of a NETCONF session."; + + reference "RFC 6241, Section 7.9"; + + input { + leaf session-id { + type session-id-type; + mandatory true; + description + "Particular session to kill."; + } + } + } + + rpc commit { + if-feature candidate; + + description + "Commit the candidate configuration as the device's new + current configuration."; + + reference "RFC 6241, Section 8.3.4.1"; + + input { + leaf confirmed { + if-feature confirmed-commit; + type empty; + description + "Requests a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf confirm-timeout { + if-feature confirmed-commit; + type uint32 { + range "1..max"; + } + units "seconds"; + default "600"; // 10 minutes + description + "The timeout interval for a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist { + if-feature confirmed-commit; + type string; + description + "This parameter is used to make a confirmed commit + persistent. A persistent confirmed commit is not aborted + if the NETCONF session terminates. The only way to abort + a persistent confirmed commit is to let the timer expire, + or to use the operation. + + The value of this parameter is a token that must be given + in the 'persist-id' parameter of or + operations in order to confirm or cancel + the persistent confirmed commit. + + The token should be a random string."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist-id { + if-feature confirmed-commit; + type string; + description + "This parameter is given in order to commit a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + reference "RFC 6241, Section 8.3.4.1"; + } + + } + } + + rpc discard-changes { + if-feature candidate; + + description + "Revert the candidate configuration to the current + running configuration."; + reference "RFC 6241, Section 8.3.4.2"; + } + + rpc cancel-commit { + if-feature confirmed-commit; + description + "This operation is used to cancel an ongoing confirmed commit. + If the confirmed commit is persistent, the parameter + 'persist-id' must be given, and it must match the value of the + 'persist' parameter."; + reference "RFC 6241, Section 8.4.4.1"; + + input { + leaf persist-id { + type string; + description + "This parameter is given in order to cancel a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + } + } + } + + rpc validate { + if-feature validate; + + description + "Validates the contents of the specified configuration."; + + reference "RFC 6241, Section 8.6.4.1"; + + input { + container source { + description + "Particular configuration to validate."; + + choice config-source { + mandatory true; + description + "The configuration source to validate."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } +} diff --git a/yang/subdir.am b/yang/subdir.am index eb17c38..0bdf937 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -38,6 +38,9 @@ dist_yangmodels_DATA += yang/frr-routing.yang dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang dist_yangmodels_DATA += yang/ietf/ietf-interfaces.yang dist_yangmodels_DATA += yang/ietf/ietf-bgp-types.yang +dist_yangmodels_DATA += yang/ietf/ietf-netconf-acm.yang +dist_yangmodels_DATA += yang/ietf/ietf-netconf.yang +dist_yangmodels_DATA += yang/ietf/ietf-netconf-with-defaults.yang if BFDD dist_yangmodels_DATA += yang/frr-bfdd.yang diff --git a/zebra/connected.c b/zebra/connected.c index ee0823f..404f892 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -48,7 +48,7 @@ static void connected_withdraw(struct connected *ifc) UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { - listnode_delete(ifc->ifp->connected, ifc); + if_connected_del(ifc->ifp->connected, ifc); connected_free(&ifc); } } @@ -65,7 +65,7 @@ static void connected_announce(struct interface *ifp, struct connected *ifc) UNSET_FLAG(ifc->flags, ZEBRA_IFA_UNNUMBERED); } - listnode_add(ifp->connected, ifc); + if_connected_add_tail(ifp->connected, ifc); /* Update interface address information to protocol daemon. */ if (ifc->address->family == AF_INET) @@ -84,9 +84,8 @@ struct connected *connected_check(struct interface *ifp, { const struct prefix *p = pu.p; struct connected *ifc; - struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) + frr_each (if_connected, ifp->connected, ifc) if (prefix_same(ifc->address, p)) return ifc; @@ -101,9 +100,8 @@ struct connected *connected_check_ptp(struct interface *ifp, const struct prefix *p = pu.p; const struct prefix *d = du.p; struct connected *ifc; - struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { if (!prefix_same(ifc->address, p)) continue; if (!CONNECTED_PEER(ifc) && !d) @@ -182,7 +180,7 @@ static void connected_update(struct interface *ifp, struct connected *ifc) void connected_up(struct interface *ifp, struct connected *ifc) { afi_t afi; - struct prefix p; + struct prefix p, plocal; struct nexthop nh = { .type = NEXTHOP_TYPE_IFINDEX, .ifindex = ifp->ifindex, @@ -192,8 +190,8 @@ void connected_up(struct interface *ifp, struct connected *ifc) uint32_t metric; uint32_t flags = 0; uint32_t count = 0; - struct listnode *cnode; struct connected *c; + bool install_local = true; zvrf = ifp->vrf->info; if (!zvrf) { @@ -210,6 +208,7 @@ void connected_up(struct interface *ifp, struct connected *ifc) UNSET_FLAG(ifc->conf, ZEBRA_IFC_DOWN); prefix_copy(&p, CONNECTED_PREFIX(ifc)); + prefix_copy(&plocal, ifc->address); /* Apply mask to the network. */ apply_mask(&p); @@ -224,6 +223,8 @@ void connected_up(struct interface *ifp, struct connected *ifc) */ if (prefix_ipv4_any((struct prefix_ipv4 *)&p)) return; + + plocal.prefixlen = IPV4_MAX_BITLEN; break; case AFI_IP6: #ifndef GNU_LINUX @@ -231,6 +232,11 @@ void connected_up(struct interface *ifp, struct connected *ifc) if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6)) return; #endif + + if (IN6_IS_ADDR_LINKLOCAL(&plocal.u.prefix6)) + install_local = false; + + plocal.prefixlen = IPV6_MAX_BITLEN; break; case AFI_UNSPEC: case AFI_L2VPN: @@ -262,7 +268,7 @@ void connected_up(struct interface *ifp, struct connected *ifc) * for all the addresses on an interface that * resolve to the same network and mask */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { struct prefix cp; prefix_copy(&cp, CONNECTED_PREFIX(c)); @@ -276,13 +282,24 @@ void connected_up(struct interface *ifp, struct connected *ifc) return; } - rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0, - false); + if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_NOPREFIXROUTE)) { + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, flags, &p, NULL, &nh, 0, + zvrf->table_id, metric, 0, 0, 0, false); - rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0, - false); + rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, flags, &p, NULL, &nh, 0, + zvrf->table_id, metric, 0, 0, 0, false); + } + + if (install_local) { + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_LOCAL, + 0, flags, &plocal, NULL, &nh, 0, zvrf->table_id, 0, 0, + 0, 0, false); + rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_LOCAL, 0, flags, &plocal, NULL, &nh, 0, + zvrf->table_id, 0, 0, 0, 0, false); + } /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { @@ -368,7 +385,7 @@ void connected_add_ipv4(struct interface *ifp, int flags, void connected_down(struct interface *ifp, struct connected *ifc) { afi_t afi; - struct prefix p; + struct prefix p, plocal; struct nexthop nh = { .type = NEXTHOP_TYPE_IFINDEX, .ifindex = ifp->ifindex, @@ -376,8 +393,8 @@ void connected_down(struct interface *ifp, struct connected *ifc) }; struct zebra_vrf *zvrf; uint32_t count = 0; - struct listnode *cnode; struct connected *c; + bool remove_local = true; zvrf = ifp->vrf->info; if (!zvrf) { @@ -403,6 +420,7 @@ void connected_down(struct interface *ifp, struct connected *ifc) } prefix_copy(&p, CONNECTED_PREFIX(ifc)); + prefix_copy(&plocal, ifc->address); /* Apply mask to the network. */ apply_mask(&p); @@ -417,10 +435,18 @@ void connected_down(struct interface *ifp, struct connected *ifc) */ if (prefix_ipv4_any((struct prefix_ipv4 *)&p)) return; + + plocal.prefixlen = IPV4_MAX_BITLEN; break; case AFI_IP6: if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6)) return; + + plocal.prefixlen = IPV6_MAX_BITLEN; + + if (IN6_IS_ADDR_LINKLOCAL(&plocal.u.prefix6)) + remove_local = false; + break; case AFI_UNSPEC: case AFI_L2VPN: @@ -439,7 +465,7 @@ void connected_down(struct interface *ifp, struct connected *ifc) * allow the deletion when are removing the last * one. */ - for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + frr_each (if_connected, ifp->connected, c) { struct prefix cp; prefix_copy(&cp, CONNECTED_PREFIX(c)); @@ -457,11 +483,25 @@ void connected_down(struct interface *ifp, struct connected *ifc) * Same logic as for connected_up(): push the changes into the * head. */ - rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_NOPREFIXROUTE)) { + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, 0, &p, NULL, &nh, 0, + zvrf->table_id, 0, 0, false); + + rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, 0, &p, NULL, &nh, 0, + zvrf->table_id, 0, 0, false); + } + + if (remove_local) { + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_LOCAL, 0, 0, &plocal, NULL, &nh, 0, + zvrf->table_id, 0, 0, false); - rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, - 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, + ZEBRA_ROUTE_LOCAL, 0, 0, &plocal, NULL, &nh, 0, + zvrf->table_id, 0, 0, false); + } /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { @@ -616,9 +656,8 @@ void connected_delete_ipv6(struct interface *ifp, int connected_is_unnumbered(struct interface *ifp) { struct connected *connected; - struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) && connected->address->family == AF_INET) return CHECK_FLAG(connected->flags, diff --git a/zebra/debug.c b/zebra/debug.c index 68bedaf..cf1701b 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -7,6 +7,7 @@ #include #include "command.h" #include "debug.h" +#include "mgmt_be_client.h" #include "zebra/debug_clippy.c" @@ -846,4 +847,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_pbr_cmd); install_element(CONFIG_NODE, &debug_zebra_mlag_cmd); install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd); + + /* Init mgmtd backend client debug commands. */ + mgmt_be_client_lib_vty_init(); } diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c index df0b5aa..a7cccdb 100644 --- a/zebra/debug_nl.c +++ b/zebra/debug_nl.c @@ -983,6 +983,7 @@ next_rta: zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, plen, rta_type, rta_type2str(rta_type)); switch (rta_type) { + case IFLA_IFNAME: case IFLA_IFALIAS: if (plen == 0) { zlog_debug(" invalid length"); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 2a87925..7ae1b2a 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -19,6 +19,9 @@ #include #include "lib/zebra.h" + +#include + #include "lib/json.h" #include "lib/libfrr.h" #include "lib/frratomic.h" @@ -42,7 +45,12 @@ #include "fpm/fpm.h" #define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK -#define SOUTHBOUND_DEFAULT_PORT 2620 + +/* + * Time in seconds that if the other end is not responding + * something terrible has gone wrong. Let's fix that. + */ +#define DPLANE_FPM_NL_WEDGIE_TIME 15 /** * FPM header: @@ -90,6 +98,7 @@ struct fpm_nl_ctx { struct event *t_event; struct event *t_nhg; struct event *t_dequeue; + struct event *t_wedged; /* zebra events. */ struct event *t_lspreset; @@ -207,7 +216,7 @@ DEFUN(fpm_set_address, fpm_set_address_cmd, memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_port = - port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT); + port ? htons(port) : htons(FPM_DEFAULT_PORT); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin->sin_len = sizeof(*sin); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ @@ -225,7 +234,7 @@ DEFUN(fpm_set_address, fpm_set_address_cmd, sin6 = (struct sockaddr_in6 *)&gfnc->addr; memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; - sin6->sin6_port = port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT); + sin6->sin6_port = port ? htons(port) : htons(FPM_DEFAULT_PORT); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin6->sin6_len = sizeof(*sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ @@ -392,7 +401,7 @@ static int fpm_write_config(struct vty *vty) written = 1; sin = (struct sockaddr_in *)&gfnc->addr; vty_out(vty, "fpm address %pI4", &sin->sin_addr); - if (sin->sin_port != htons(SOUTHBOUND_DEFAULT_PORT)) + if (sin->sin_port != htons(FPM_DEFAULT_PORT)) vty_out(vty, " port %d", ntohs(sin->sin_port)); vty_out(vty, "\n"); @@ -401,7 +410,7 @@ static int fpm_write_config(struct vty *vty) written = 1; sin6 = (struct sockaddr_in6 *)&gfnc->addr; vty_out(vty, "fpm address %pI6", &sin6->sin6_addr); - if (sin6->sin6_port != htons(SOUTHBOUND_DEFAULT_PORT)) + if (sin6->sin6_port != htons(FPM_DEFAULT_PORT)) vty_out(vty, " port %d", ntohs(sin6->sin6_port)); vty_out(vty, "\n"); @@ -859,7 +868,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) if (op == DPLANE_OP_ROUTE_DELETE) break; - /* FALL THROUGH */ + fallthrough; case DPLANE_OP_ROUTE_INSTALL: rv = netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, &nl_buf[nl_buf_len], @@ -969,6 +978,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: break; @@ -1364,6 +1374,18 @@ static void fpm_rmac_reset(struct event *t) &fnc->t_rmacwalk); } +static void fpm_process_wedged(struct event *t) +{ + struct fpm_nl_ctx *fnc = EVENT_ARG(t); + + zlog_warn("%s: Connection unable to write to peer for over %u seconds, resetting", + __func__, DPLANE_FPM_NL_WEDGIE_TIME); + + atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1, + memory_order_relaxed); + FPM_RECONNECT(fnc); +} + static void fpm_process_queue(struct event *t) { struct fpm_nl_ctx *fnc = EVENT_ARG(t); @@ -1408,9 +1430,13 @@ static void fpm_process_queue(struct event *t) processed_contexts, memory_order_relaxed); /* Re-schedule if we ran out of buffer space */ - if (no_bufs) - event_add_timer(fnc->fthread->master, fpm_process_queue, fnc, 0, + if (no_bufs) { + event_add_event(fnc->fthread->master, fpm_process_queue, fnc, 0, &fnc->t_dequeue); + event_add_timer(fnc->fthread->master, fpm_process_wedged, fnc, + DPLANE_FPM_NL_WEDGIE_TIME, &fnc->t_wedged); + } else + EVENT_OFF(fnc->t_wedged); /* * Let the dataplane thread know if there are items in the @@ -1614,7 +1640,7 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) if (atomic_load_explicit(&fnc->counters.ctxqueue_len, memory_order_relaxed) > 0) - event_add_timer(fnc->fthread->master, fpm_process_queue, fnc, 0, + event_add_event(fnc->fthread->master, fpm_process_queue, fnc, 0, &fnc->t_dequeue); /* Ensure dataplane thread is rescheduled if we hit the work limit */ diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c new file mode 100644 index 0000000..e7d2e6b --- /dev/null +++ b/zebra/ge_netlink.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Generic Netlink functions. + * Copyright (C) 2022, Carmine Scarpitta + */ + +#include + +#ifdef HAVE_NETLINK + +/* The following definition is to workaround an issue in the Linux kernel + * header files with redefinition of 'struct in6_addr' in both + * netinet/in.h and linux/in6.h. + * Reference - https://sourceware.org/ml/libc-alpha/2013-01/msg00599.html + */ +#define _LINUX_IN6_H + +#include +#include +#include + +#include "lib/ns.h" +#include "zebra/ge_netlink.h" +#include "zebra/debug.h" +#include "zebra/kernel_netlink.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_srv6.h" + + +/** + * This file provides an implementation of the functionality exposed by the + * kernel through the Generic Netlink mechanism. + * + * Supported features include the ability to configure the source address used + * for SRv6 encapsulation ('sr tunsrc' in kernel terminology). + * + * At the time of writing this code, the kernel does not send us any asynchronous + * notifications when someone changes the 'sr tunsrc' under us. As a result, we + * are currently unable to detect when the source address changes and update the + * SRv6 encapsulation source address configured in zebra. + * + * In the future, when the kernel supports async notifications, the implementation + * can be improved by listening on the Generic Netlink socket and adding a handler + * to process/parse incoming 'sr tunsrc' change messages and update the SRv6 zebra + * configuration with the new encap source address. + */ + + +/* + * Numeric family identifier used to configure SRv6 internal parameters through Generic Netlink. + */ +static int16_t seg6_genl_family = -1; + +static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len; + struct rtattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *ghdr = NLMSG_DATA(h); + struct rtattr *attrs; + const char *family; + + if (h->nlmsg_type != GENL_ID_CTRL) { + zlog_err( + "Not a controller message, nlmsg_len=%d nlmsg_type=0x%x", + h->nlmsg_len, h->nlmsg_type); + return 0; + } + + len = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) { + zlog_err( + "Message received from netlink is of a broken size %d %zu", + h->nlmsg_len, (size_t)NLMSG_LENGTH(GENL_HDRLEN)); + return -1; + } + + if (ghdr->cmd != CTRL_CMD_NEWFAMILY) { + zlog_err("Unknown controller command %d", ghdr->cmd); + return -1; + } + + attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN); + netlink_parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len); + + if (tb[CTRL_ATTR_FAMILY_ID] == NULL) { + zlog_err("Missing family id TLV"); + return -1; + } + + if (tb[CTRL_ATTR_FAMILY_NAME] == NULL) { + zlog_err("Missing family name TLV"); + return -1; + } + + family = (char *)RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]); + + if (strmatch(family, "SEG6")) + seg6_genl_family = + *(int16_t *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + else { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_err("Unsupported Generic Netlink family '%s'", + family); + return -1; + } + + return 0; +} + +int genl_resolve_family(const char *family) +{ + struct zebra_ns *zns; + struct genl_request req; + + memset(&req, 0, sizeof(req)); + + zns = zebra_ns_lookup(NS_DEFAULT); + + req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = GENL_ID_CTRL; + + req.n.nlmsg_pid = zns->ge_netlink_cmd.snl.nl_pid; + + req.g.cmd = CTRL_CMD_GETFAMILY; + req.g.version = 0; + + if (!nl_attr_put(&req.n, sizeof(req), CTRL_ATTR_FAMILY_NAME, family, + strlen(family) + 1)) + return -1; + + return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false); +} + +/* + * sr tunsrc change via netlink interface, using a dataplane context object + * + * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer + * otherwise the number of bytes written to buf. + */ +ssize_t netlink_sr_tunsrc_set_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + struct nlsock *nl; + const struct in6_addr *tunsrc_addr; + struct genl_request *req = buf; + + if (seg6_genl_family < 0) { + zlog_err( + "Failed to set SRv6 source address: kernel does not support 'SEG6' Generic Netlink family."); + return -1; + } + + tunsrc_addr = dplane_ctx_get_srv6_encap_srcaddr(ctx); + if (!tunsrc_addr) + return -1; + + if (buflen < sizeof(*req)) + return 0; + + nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx)); + + memset(req, 0, sizeof(*req)); + + req->n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req->n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + /* Prepare Netlink request to set tunsrc addr */ + req->n.nlmsg_type = seg6_genl_family; + req->n.nlmsg_pid = nl->snl.nl_pid; + + req->g.cmd = cmd; + req->g.version = SEG6_GENL_VERSION; + + switch (cmd) { + case SEG6_CMD_SET_TUNSRC: + if (!nl_attr_put(&req->n, buflen, SEG6_ATTR_DST, tunsrc_addr, + sizeof(struct in6_addr))) + return 0; + break; + default: + zlog_err("Unsupported command (%u)", cmd); + return -1; + } + + return NLMSG_ALIGN(req->n.nlmsg_len); +} + +ssize_t netlink_sr_tunsrc_set_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + enum dplane_op_e op; + int cmd = 0; + + op = dplane_ctx_get_op(ctx); + + /* Call to netlink layer based on type of operation */ + if (op == DPLANE_OP_SRV6_ENCAP_SRCADDR_SET) { + /* Validate */ + if (dplane_ctx_get_srv6_encap_srcaddr(ctx) == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "sr tunsrc set failed: SRv6 encap source address not set"); + return -1; + } + + cmd = SEG6_CMD_SET_TUNSRC; + } else { + /* Invalid op */ + zlog_err("Context received for kernel sr tunsrc update with incorrect OP code (%u)", + op); + return -1; + } + + return netlink_sr_tunsrc_set_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + struct zebra_ns *zns; + struct genl_request req; + + op = dplane_ctx_get_op(ctx); + assert(op == DPLANE_OP_SRV6_ENCAP_SRCADDR_SET); + + netlink_sr_tunsrc_set_msg_encoder(ctx, &req, sizeof(req)); + + zns = zebra_ns_lookup(dplane_ctx_get_ns_sock(ctx)); + + return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false); +} + +/** + * netlink_sr_tunsrc_reply_read() - Read in SR tunsrc reply from the kernel + * + * @h: Netlink message header + * @ns_id: Namspace id + * @startup: Are we reading under startup conditions? + * + * Return: Result status + */ +int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len; + struct genlmsghdr *ghdr; + struct rtattr *tb[SEG6_ATTR_MAX + 1] = {}; + struct rtattr *attrs; + + if (h->nlmsg_type != seg6_genl_family) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) { + zlog_warn("%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(GENL_HDRLEN)); + return -1; + } + + ghdr = NLMSG_DATA(h); + + if (ghdr->cmd != SEG6_CMD_GET_TUNSRC) + return 0; + + attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN); + netlink_parse_rtattr(tb, SEG6_ATTR_MAX, attrs, len); + + if (tb[SEG6_ATTR_DST] == NULL) { + zlog_err("Missing tunsrc addr"); + return -1; + } + + zebra_srv6_encap_src_addr_set( + (struct in6_addr *)RTA_DATA(tb[SEG6_ATTR_DST])); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: SRv6 encap source address received from kernel: '%pI6'", + __func__, + (struct in6_addr *)RTA_DATA(tb[SEG6_ATTR_DST])); + + return 0; +} + +/** + * netlink_request_sr_tunsrc() - Request SR tunsrc from the kernel + * @zns: Zebra namespace + * + * Return: Result status + */ +static int netlink_request_sr_tunsrc(struct zebra_ns *zns) +{ + struct genl_request req; + + if (zns->ge_netlink_cmd.sock < 0) + return -1; + + if (seg6_genl_family < 0) { + zlog_err( + "Failed to get SRv6 encap source address: kernel does not support 'SEG6' Generic Netlink family."); + return -1; + } + + /* Form the request, specifying filter (rtattr) if needed. */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = seg6_genl_family; + req.g.cmd = SEG6_CMD_GET_TUNSRC; + req.g.version = SEG6_GENL_VERSION; + + return netlink_request(&zns->ge_netlink_cmd, &req); +} + +/** + * SR tunsrc read function using netlink interface. Only called + * on bootstrap time. + */ +int netlink_sr_tunsrc_read(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + + if (zns->ge_netlink_cmd.sock < 0) + return -1; + + /* Capture info in intermediate info struct */ + dp_info.ns_id = zns->ns_id; + dp_info.is_cmd = true; + dp_info.sock = zns->ge_netlink_cmd.sock; + dp_info.seq = zns->ge_netlink_cmd.seq; + + /* Get SR tunsrc. */ + ret = netlink_request_sr_tunsrc(zns); + if (ret < 0) + return ret; + ret = netlink_parse_info(netlink_sr_tunsrc_reply_read, + &zns->ge_netlink_cmd, &dp_info, 0, true); + if (ret < 0) + return ret; + + return 0; +} + +void ge_netlink_init(struct zebra_ns *zns) +{ + if (zns->ge_netlink_cmd.sock < 0) + return; + + /* + * Resolves the 'seg6' Generic Netlink family name to the corresponding numeric family identifier. + * This will give us the numeric family identifier required to send 'seg6' commands to the kernel + * over the Generic Netlink socket. 'seg6' commands are used to configure SRv6 internal parameters + * such as the address to use as source for encapsulated packets. + */ + if (genl_resolve_family("SEG6")) + zlog_warn( + "Kernel does not support 'SEG6' Generic Netlink family. Any attempt to set the encapsulation parameters under the SRv6 configuration will fail"); + + /** + * Retrieve the actual SRv6 encap source address from the kernel + * (default namespace) and save it to zebra SRv6 config + */ + if (zns->ns_id == NS_DEFAULT) + netlink_sr_tunsrc_read(zns); +} + +#endif /* HAVE_NETLINK */ diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h new file mode 100644 index 0000000..20d0911 --- /dev/null +++ b/zebra/ge_netlink.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Header file exported by ge_netlink.c to zebra. + * Copyright (C) 2022, Carmine Scarpitta + */ + +#ifndef _ZEBRA_GE_NETLINK_H +#define _ZEBRA_GE_NETLINK_H + +#include "zebra_dplane.h" + +#ifdef HAVE_NETLINK + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Generic Netlink request message */ +struct genl_request { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[1024]; +}; + +extern int genl_resolve_family(const char *family); +extern ssize_t netlink_sr_tunsrc_set_msg_encode(int cmd, + struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); +extern ssize_t netlink_sr_tunsrc_set_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); +struct nl_batch; +extern enum netlink_msg_status +netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); + +int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup); +int netlink_sr_tunsrc_read(struct zebra_ns *zns); + +extern void ge_netlink_init(struct zebra_ns *zns); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_GE_NETLINK_H */ diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index b3cf865..d0aa216 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -5,6 +5,7 @@ */ #include +#include #ifdef OPEN_BSD diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ed2e0a2..5f096e3 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -17,6 +17,8 @@ #define _LINUX_IF_H #define _LINUX_IP_H +#include +#include #include #include #include @@ -215,6 +217,8 @@ static void netlink_determine_zebra_iftype(const char *kind, *zif_type = ZEBRA_IF_VETH; else if (strcmp(kind, "bond") == 0) *zif_type = ZEBRA_IF_BOND; + else if (strcmp(kind, "team") == 0) + *zif_type = ZEBRA_IF_BOND; else if (strcmp(kind, "gre") == 0) *zif_type = ZEBRA_IF_GRE; } @@ -257,6 +261,7 @@ static uint32_t get_iflink_speed(struct interface *interface, int *error) int sd; int rc; const char *ifname = interface->name; + uint32_t ret; if (error) *error = 0; @@ -281,7 +286,7 @@ static uint32_t get_iflink_speed(struct interface *interface, int *error) ifname, errno, safe_strerror(errno)); /* no vrf socket creation may probably mean vrf issue */ if (error) - *error = -1; + *error = INTERFACE_SPEED_ERROR_READ; return 0; } /* Get the current link state for the interface */ @@ -295,14 +300,20 @@ static uint32_t get_iflink_speed(struct interface *interface, int *error) ifname, errno, safe_strerror(errno)); /* no device means interface unreachable */ if (errno == ENODEV && error) - *error = -1; + *error = INTERFACE_SPEED_ERROR_READ; ecmd.speed_hi = 0; ecmd.speed = 0; } close(sd); - return ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed; + ret = ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed; + if (ret == UINT32_MAX) { + if (error) + *error = INTERFACE_SPEED_ERROR_UNKNOWN; + ret = 0; + } + return ret; } uint32_t kernel_get_speed(struct interface *ifp, int *error) @@ -998,68 +1009,13 @@ static ssize_t netlink_intf_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, op = dplane_ctx_get_op(ctx); - switch (op) { - case DPLANE_OP_INTF_UPDATE: + if (op == DPLANE_OP_INTF_UPDATE) cmd = RTM_SETLINK; - break; - case DPLANE_OP_INTF_INSTALL: + else if (op == DPLANE_OP_INTF_INSTALL) cmd = RTM_NEWLINK; - break; - case DPLANE_OP_INTF_DELETE: + else if (op == DPLANE_OP_INTF_DELETE) cmd = RTM_DELLINK; - break; - case DPLANE_OP_NONE: - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: + else { flog_err( EC_ZEBRA_NHG_FIB_UPDATE, "Context received for kernel interface update with incorrect OP code (%u)", @@ -1474,6 +1430,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, if (kernel_flags & IFA_F_SECONDARY) dplane_ctx_intf_set_secondary(ctx); + if (kernel_flags & IFA_F_NOPREFIXROUTE) + dplane_ctx_intf_set_noprefixroute(ctx); + /* Label */ if (tb[IFA_LABEL]) { label = (char *)RTA_DATA(tb[IFA_LABEL]); diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c index ae29268..9db9598 100644 --- a/zebra/if_sysctl.c +++ b/zebra/if_sysctl.c @@ -6,6 +6,8 @@ #include +#include + #if !defined(GNU_LINUX) && !defined(OPEN_BSD) #include "if.h" diff --git a/zebra/interface.c b/zebra/interface.c index 1afd9d5..5ce222c 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -18,7 +18,6 @@ #include "log.h" #include "zclient.h" #include "vrf.h" -#include "lib/northbound_cli.h" #include "zebra/rtadv.h" #include "zebra_ns.h" @@ -44,8 +43,6 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information"); DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), (vty, ifp)); -DEFINE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp), - (vty, ifp)); DEFINE_MTYPE(ZEBRA, ZIF_DESC, "Intf desc"); @@ -65,7 +62,7 @@ static void if_zebra_speed_update(struct event *thread) * interfaces not available. * note that loopback & virtual interfaces can return 0 as speed */ - if (error < 0) + if (error == INTERFACE_SPEED_ERROR_READ) return; if (new_speed != ifp->speed) { @@ -76,7 +73,7 @@ static void if_zebra_speed_update(struct event *thread) changed = true; } - if (changed || new_speed == UINT32_MAX) { + if (changed || error == INTERFACE_SPEED_ERROR_UNKNOWN) { #define SPEED_UPDATE_SLEEP_TIME 5 #define SPEED_UPDATE_COUNT_MAX (4 * 60 / SPEED_UPDATE_SLEEP_TIME) /* @@ -91,7 +88,7 @@ static void if_zebra_speed_update(struct event *thread) * to not update the system to keep track of that. This * is far simpler to just stop trying after 4 minutes */ - if (new_speed == UINT32_MAX && + if (error == INTERFACE_SPEED_ERROR_UNKNOWN && zif->speed_update_count == SPEED_UPDATE_COUNT_MAX) return; @@ -136,7 +133,7 @@ static int if_zebra_new_hook(struct interface *ifp) zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC; zebra_if->mpls_config = IF_ZEBRA_DATA_UNSPEC; - zebra_if->shutdown = IF_ZEBRA_DATA_OFF; + zebra_if->shutdown = IF_ZEBRA_DATA_UNSPEC; zebra_if->link_nsid = NS_UNKNOWN; @@ -148,6 +145,8 @@ static int if_zebra_new_hook(struct interface *ifp) rtadv_if_init(zebra_if); + zebra_evpn_mh_if_init(zebra_if); + memset(&zebra_if->neigh_mac[0], 0, 6); /* Initialize installed address chains tree. */ @@ -171,18 +170,13 @@ static int if_zebra_new_hook(struct interface *ifp) return 0; } -static void if_nhg_dependents_check_valid(struct nhg_hash_entry *nhe) -{ - zebra_nhg_check_valid(nhe); -} - static void if_down_nhg_dependents(const struct interface *ifp) { struct nhg_connected *rb_node_dep = NULL; struct zebra_if *zif = (struct zebra_if *)ifp->info; frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) - if_nhg_dependents_check_valid(rb_node_dep->nhe); + zebra_nhg_check_valid(rb_node_dep->nhe); } static void if_nhg_dependents_release(const struct interface *ifp) @@ -192,7 +186,7 @@ static void if_nhg_dependents_release(const struct interface *ifp) frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) { rb_node_dep->nhe->ifp = NULL; /* Null it out */ - if_nhg_dependents_check_valid(rb_node_dep->nhe); + zebra_nhg_check_valid(rb_node_dep->nhe); } } @@ -489,12 +483,11 @@ void if_flags_update(struct interface *ifp, uint64_t newflags) address. */ void if_addr_wakeup(struct interface *ifp) { - struct listnode *node, *nnode; struct connected *ifc; struct prefix *p; enum zebra_dplane_result dplane_res; - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { + frr_each_safe (if_connected, ifp->connected, ifc) { p = ifc->address; if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED) @@ -637,32 +630,24 @@ void if_add_update(struct interface *ifp) /* Install connected routes corresponding to an interface. */ static void if_install_connected(struct interface *ifp) { - struct listnode *node; - struct listnode *next; struct connected *ifc; - if (ifp->connected) { - for (ALL_LIST_ELEMENTS(ifp->connected, node, next, ifc)) { - if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) - zebra_interface_address_add_update(ifp, ifc); + frr_each (if_connected, ifp->connected, ifc) { + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + zebra_interface_address_add_update(ifp, ifc); - connected_up(ifp, ifc); - } + connected_up(ifp, ifc); } } /* Uninstall connected routes corresponding to an interface. */ static void if_uninstall_connected(struct interface *ifp) { - struct listnode *node; - struct listnode *next; struct connected *ifc; - if (ifp->connected) { - for (ALL_LIST_ELEMENTS(ifp->connected, node, next, ifc)) { - zebra_interface_address_delete_update(ifp, ifc); - connected_down(ifp, ifc); - } + frr_each_safe (if_connected, ifp->connected, ifc) { + zebra_interface_address_delete_update(ifp, ifc); + connected_down(ifp, ifc); } } @@ -670,20 +655,15 @@ static void if_uninstall_connected(struct interface *ifp) /* TODO - Check why IPv4 handling here is different from install or if_down */ static void if_delete_connected(struct interface *ifp) { - struct connected *ifc; + struct connected *ifc, *ifc_next; struct prefix cp; struct route_node *rn; struct zebra_if *zebra_if; - struct listnode *node; - struct listnode *last = NULL; zebra_if = ifp->info; - if (!ifp->connected) - return; - - while ((node = (last ? last->next : listhead(ifp->connected)))) { - ifc = listgetdata(node); + for (ifc = if_connected_first(ifp->connected); ifc; ifc = ifc_next) { + ifc_next = if_connected_next(ifp->connected, ifc); cp = *CONNECTED_PREFIX(ifc); apply_mask(&cp); @@ -732,11 +712,15 @@ static void if_delete_connected(struct interface *ifp) * (unconditionally). */ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { - listnode_delete(ifp->connected, + if (ifc == ifc_next) + ifc_next = if_connected_next( + ifp->connected, ifc); + + if_connected_del(ifp->connected, + ifc); connected_free(&ifc); - } else - last = node; + } } /* Free chain list and respective route node. */ @@ -751,14 +735,10 @@ static void if_delete_connected(struct interface *ifp) UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL); UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - last = node; - else { - listnode_delete(ifp->connected, ifc); + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { + if_connected_del(ifp->connected, ifc); connected_free(&ifc); } - } else { - last = node; } } } @@ -1068,6 +1048,8 @@ void if_up(struct interface *ifp, bool install_connected) event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 0, &zif->speed_update); event_ignore_late_timer(zif->speed_update); + + if_addr_wakeup(ifp); } /* Interface goes down. We have to manage different behavior of based @@ -1331,6 +1313,9 @@ static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx, if (dplane_ctx_intf_is_secondary(ctx)) SET_FLAG(flags, ZEBRA_IFA_SECONDARY); + if (dplane_ctx_intf_is_noprefixroute(ctx)) + SET_FLAG(flags, ZEBRA_IFA_NOPREFIXROUTE); + /* Label? */ if (dplane_ctx_intf_has_label(ctx)) label = dplane_ctx_get_intf_label(ctx); @@ -1800,6 +1785,9 @@ interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, vlanid_t vid; int i; + if (vniarray == NULL) + return; + memset(&vni_start, 0, sizeof(vni_start)); memset(&vni_end, 0, sizeof(vni_end)); @@ -2295,15 +2283,10 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) ifp = if_lookup_by_index_per_ns(zns, ifindex); - switch (op) { - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: + if (op == DPLANE_OP_INTF_ADDR_ADD || op == DPLANE_OP_INTF_ADDR_DEL) { zebra_if_addr_update_ctx(ctx, ifp); - break; - - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: + } else if (op == DPLANE_OP_INTF_INSTALL || + op == DPLANE_OP_INTF_UPDATE || op == DPLANE_OP_INTF_DELETE) { /* * Queued from the dplane means it is something * that we need to handle( create/delete the @@ -2313,62 +2296,8 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) zebra_if_dplane_ifp_handling(ctx); else zebra_if_update_ctx(ctx, ifp); - break; - - case DPLANE_OP_INTF_NETCONFIG: + } else if (op == DPLANE_OP_INTF_NETCONFIG) { zebra_if_netconf_update_ctx(ctx, ifp, ifindex); - break; - - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_NONE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: - break; /* should never hit here */ } } @@ -2407,6 +2336,12 @@ static void connected_dump_vty(struct vty *vty, json_object *json, else if (CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY)) vty_out(vty, " secondary"); + if (json) + json_object_boolean_add(json_addr, "noPrefixRoute", + CHECK_FLAG(connected->flags, ZEBRA_IFA_NOPREFIXROUTE)); + else if (CHECK_FLAG(connected->flags, ZEBRA_IFA_NOPREFIXROUTE)) + vty_out(vty, " noprefixroute"); + if (json) json_object_boolean_add( json_addr, "unnumbered", @@ -2556,34 +2491,27 @@ static void ifs_dump_brief_vty(struct vty *vty, struct vrf *vrf) } uint32_t v6_list_size = 0; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { + frr_each (if_connected, ifp->connected, connected) { if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) && (connected->address->family == AF_INET6)) v6_list_size++; } - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { - if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) - && !CHECK_FLAG(connected->flags, - ZEBRA_IFA_SECONDARY) - && (connected->address->family == AF_INET6)) { + frr_each (if_connected, ifp->connected, connected) { + if (!CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY) && + (connected->address->family == AF_INET6)) { p = connected->address; - /* Don't print link local pfx */ - if (!IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) { - if (first_pfx_printed) { - /* padding to prepare row only - * for ip addr */ - vty_out(vty, "%-40s", ""); - if (v6_list_size > 1) - vty_out(vty, "+ "); - vty_out(vty, "%pFX\n", p); - } else { - if (v6_list_size > 1) - vty_out(vty, "+ "); - vty_out(vty, "%pFX\n", p); - } - first_pfx_printed = true; - break; + if (first_pfx_printed) { + vty_out(vty, "%-40s", ""); + if (v6_list_size > 1) + vty_out(vty, "+ "); + vty_out(vty, "%pFX\n", p); + } else { + if (v6_list_size > 1) + vty_out(vty, "+ "); + vty_out(vty, "%pFX\n", p); } + first_pfx_printed = true; + break; } } if (!first_pfx_printed) @@ -2595,7 +2523,6 @@ static void ifs_dump_brief_vty(struct vty *vty, struct vrf *vrf) static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf) { struct connected *connected; - struct listnode *node; struct interface *ifp; FOR_ALL_INTERFACES (vrf, ifp) { @@ -2611,13 +2538,8 @@ static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf) json_addrs = json_object_new_array(); json_object_object_add(json_if, "addresses", json_addrs); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { - if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) - && !CHECK_FLAG(connected->flags, - ZEBRA_IFA_SECONDARY) - && !(connected->address->family == AF_INET6 - && IN6_IS_ADDR_LINKLOCAL( - &connected->address->u.prefix6))) { + frr_each (if_connected, ifp->connected, connected) { + if (!CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY)) { char buf[PREFIX2STR_BUFFER]; json_array_string_add( @@ -2824,9 +2746,8 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) connected_dump_vty(vty, NULL, connected); } - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { - if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) - && (connected->address->family == AF_INET6)) + frr_each (if_connected, ifp->connected, connected) { + if (connected->address->family == AF_INET6) connected_dump_vty(vty, NULL, connected); } @@ -2958,8 +2879,8 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) " Link Delay Variation %u (micro-sec.)\n", iflp->delay_var); if (IS_PARAM_SET(iflp, LP_PKT_LOSS)) - vty_out(vty, " Link Packet Loss %g (in %%)\n", - iflp->pkt_loss); + vty_out(vty, " Link Packet Loss %f (in %%)\n", + (double)iflp->pkt_loss * LOSS_PRECISION); if (IS_PARAM_SET(iflp, LP_AVA_BW)) vty_out(vty, " Available Bandwidth %g (Byte/s)\n", iflp->ava_bw); @@ -3201,9 +3122,8 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp, connected_dump_vty(vty, json_addrs, connected); } - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { - if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) - && (connected->address->family == AF_INET6)) + frr_each (if_connected, ifp->connected, connected) { + if (connected->address->family == AF_INET6) connected_dump_vty(vty, json_addrs, connected); } @@ -3360,7 +3280,8 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp, iflp->delay_var); if (IS_PARAM_SET(iflp, LP_PKT_LOSS)) json_object_double_add(json_te, "linkPacketLoss", - iflp->pkt_loss); + (double)iflp->pkt_loss * + LOSS_PRECISION); if (IS_PARAM_SET(iflp, LP_AVA_BW)) json_object_double_add(json_te, "availableBandwidth", iflp->ava_bw); @@ -3744,63 +3665,43 @@ DEFUN (show_interface_desc_vrf_all, return CMD_SUCCESS; } -int if_multicast_set(struct interface *ifp) +void if_arp(struct interface *ifp, bool enable) { - struct zebra_if *if_data; + int ret; - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - if (if_set_flags(ifp, IFF_MULTICAST) < 0) { - zlog_debug("Can't set multicast flag on interface %s", - ifp->name); - return -1; - } - if_refresh(ifp); + if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) + return; + + if (enable) + ret = if_unset_flags(ifp, IFF_NOARP); + else + ret = if_set_flags(ifp, IFF_NOARP); + + if (ret < 0) { + zlog_debug("Can't %sset noarp flag on interface %s", + enable ? "" : "un", ifp->name); + return; } - if_data = ifp->info; - if_data->multicast = IF_ZEBRA_DATA_ON; - return 0; + if_refresh(ifp); } -DEFUN (multicast, - multicast_cmd, - "multicast", - "Set multicast flag to interface\n") +int if_multicast_set(struct interface *ifp) { - VTY_DECLVAR_CONTEXT(interface, ifp); - int ret; struct zebra_if *if_data; if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - ret = if_set_flags(ifp, IFF_MULTICAST); - if (ret < 0) { - vty_out(vty, "Can't set multicast flag\n"); - return CMD_WARNING_CONFIG_FAILED; + if (if_set_flags(ifp, IFF_MULTICAST) < 0) { + zlog_debug("Can't set multicast flag on interface %s", + ifp->name); + return -1; } if_refresh(ifp); } if_data = ifp->info; if_data->multicast = IF_ZEBRA_DATA_ON; - return CMD_SUCCESS; -} - -DEFPY (mpls, - mpls_cmd, - "[no] mpls ", - NO_STR - MPLS_STR - "Set mpls to be on for the interface\n" - "Set mpls to be off for the interface\n") -{ - if (!no) - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", - NB_OP_CREATE, on ? "true" : "false"); - else - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", - NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return 0; } int if_multicast_unset(struct interface *ifp) @@ -3821,30 +3722,6 @@ int if_multicast_unset(struct interface *ifp) return 0; } -DEFUN (no_multicast, - no_multicast_cmd, - "no multicast", - NO_STR - "Unset multicast flag to interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - int ret; - struct zebra_if *if_data; - - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - ret = if_unset_flags(ifp, IFF_MULTICAST); - if (ret < 0) { - vty_out(vty, "Can't unset multicast flag\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if_refresh(ifp); - } - if_data = ifp->info; - if_data->multicast = IF_ZEBRA_DATA_OFF; - - return CMD_SUCCESS; -} - int if_linkdetect(struct interface *ifp, bool detect) { int if_was_operative; @@ -3868,30 +3745,6 @@ int if_linkdetect(struct interface *ifp, bool detect) return 0; } -DEFUN(linkdetect, linkdetect_cmd, "link-detect", - "Enable link detection on interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - if_linkdetect(ifp, true); - - return CMD_SUCCESS; -} - - -DEFUN (no_linkdetect, - no_linkdetect_cmd, - "no link-detect", - NO_STR - "Disable link detection on interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - if_linkdetect(ifp, false); - - return CMD_SUCCESS; -} - int if_shutdown(struct interface *ifp) { struct zebra_if *if_data; @@ -3911,31 +3764,6 @@ int if_shutdown(struct interface *ifp) return 0; } -DEFUN (shutdown_if, - shutdown_if_cmd, - "shutdown", - "Shutdown the selected interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - int ret; - struct zebra_if *if_data; - - if (ifp->ifindex != IFINDEX_INTERNAL) { - /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ - rtadv_stop_ra(ifp); - ret = if_unset_flags(ifp, IFF_UP); - if (ret < 0) { - vty_out(vty, "Can't shutdown interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if_refresh(ifp); - } - if_data = ifp->info; - if_data->shutdown = IF_ZEBRA_DATA_ON; - - return CMD_SUCCESS; -} - int if_no_shutdown(struct interface *ifp) { struct zebra_if *if_data; @@ -3960,1423 +3788,132 @@ int if_no_shutdown(struct interface *ifp) return 0; } -DEFUN (no_shutdown_if, - no_shutdown_if_cmd, - "no shutdown", - NO_STR - "Shutdown the selected interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - int ret; - struct zebra_if *if_data; - - if (ifp->ifindex != IFINDEX_INTERNAL) { - ret = if_set_flags(ifp, IFF_UP | IFF_RUNNING); - if (ret < 0) { - vty_out(vty, "Can't up interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if_refresh(ifp); - - /* Some addresses (in particular, IPv6 addresses on Linux) get - * removed when the interface goes down. They need to be - * readded. - */ - if_addr_wakeup(ifp); - } - - if_data = ifp->info; - if_data->shutdown = IF_ZEBRA_DATA_OFF; - - return CMD_SUCCESS; -} - -DEFUN (bandwidth_if, - bandwidth_if_cmd, - "bandwidth (1-100000)", - "Set bandwidth informational parameter\n" - "Bandwidth in megabits\n") -{ - int idx_number = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - unsigned int bandwidth; - - bandwidth = strtol(argv[idx_number]->arg, NULL, 10); - - /* bandwidth range is <1-100000> */ - if (bandwidth < 1 || bandwidth > 100000) { - vty_out(vty, "Bandwidth is invalid\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ifp->bandwidth = bandwidth; - - /* force protocols to recalculate routes due to cost change */ - if (if_is_operative(ifp)) - zebra_interface_up_update(ifp); - - return CMD_SUCCESS; -} - -DEFUN (no_bandwidth_if, - no_bandwidth_if_cmd, - "no bandwidth [(1-100000)]", - NO_STR - "Set bandwidth informational parameter\n" - "Bandwidth in megabits\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - ifp->bandwidth = 0; - - /* force protocols to recalculate routes due to cost change */ - if (if_is_operative(ifp)) - zebra_interface_up_update(ifp); - - return CMD_SUCCESS; -} - - -struct cmd_node link_params_node = { - .name = "link-params", - .node = LINK_PARAMS_NODE, - .parent_node = INTERFACE_NODE, - .prompt = "%s(config-link-params)# ", - .no_xpath = true, -}; - -static void link_param_cmd_set_uint32(struct interface *ifp, uint32_t *field, - uint32_t type, uint32_t value) +void link_param_cmd_set_uint32(struct interface *ifp, uint32_t *field, + uint32_t type, uint32_t value) { /* Update field as needed */ if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) { *field = value; SET_PARAM(ifp->link_params, type); - - /* force protocols to update LINK STATE due to parameters change - */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); } } -static void link_param_cmd_set_float(struct interface *ifp, float *field, - uint32_t type, float value) -{ +void link_param_cmd_set_float(struct interface *ifp, float *field, + uint32_t type, float value) +{ /* Update field as needed */ if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) { *field = value; SET_PARAM(ifp->link_params, type); - - /* force protocols to update LINK STATE due to parameters change - */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); } } -static void link_param_cmd_unset(struct interface *ifp, uint32_t type) +void link_param_cmd_unset(struct interface *ifp, uint32_t type) { if (ifp->link_params == NULL) return; /* Unset field */ UNSET_PARAM(ifp->link_params, type); - - /* force protocols to update LINK STATE due to parameters change */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); -} - -DEFUN_NOSH (link_params, - link_params_cmd, - "link-params", - LINK_PARAMS_STR) -{ - /* vty->qobj_index stays the same @ interface pointer */ - vty->node = LINK_PARAMS_NODE; - - return CMD_SUCCESS; -} - -DEFUN_NOSH (exit_link_params, - exit_link_params_cmd, - "exit-link-params", - "Exit from Link Params configuration mode\n") -{ - if (vty->node == LINK_PARAMS_NODE) - vty->node = INTERFACE_NODE; - return CMD_SUCCESS; } -/* Specific Traffic Engineering parameters commands */ -DEFUN (link_params_enable, - link_params_enable_cmd, - "enable", - "Activate link parameters on this interface\n") +void if_ip_address_install(struct interface *ifp, struct prefix *prefix, + const char *label, struct prefix *pp) { - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* This command could be issue at startup, when activate MPLS TE */ - /* on a new interface or after a ON / OFF / ON toggle */ - /* In all case, TE parameters are reset to their default factory */ - if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS) - zlog_debug( - "Link-params: enable TE link parameters on interface %s", - ifp->name); - - if (!if_link_params_get(ifp)) - if_link_params_enable(ifp); + struct zebra_if *if_data; + struct connected *ifc; - /* force protocols to update LINK STATE due to parameters change */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); + if_data = ifp->info; - return CMD_SUCCESS; -} + ifc = connected_check_ptp(ifp, prefix, pp); + if (!ifc) { + ifc = connected_new(); + ifc->ifp = ifp; -DEFUN (no_link_params_enable, - no_link_params_enable_cmd, - "no enable", - NO_STR - "Disable link parameters on this interface\n") -{ - char xpath[XPATH_MAXLEN]; - int ret; - VTY_DECLVAR_CONTEXT(interface, ifp); + /* Address. */ + ifc->address = prefix_new(); + prefix_copy(ifc->address, prefix); - if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS) - zlog_debug("MPLS-TE: disable TE link parameters on interface %s", - ifp->name); + if (pp) { + SET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + ifc->destination = prefix_new(); + prefix_copy(ifc->destination, pp); + } - if_link_params_free(ifp); + /* Label. */ + if (label) + ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities", - ifp->name); - if (yang_dnode_exists(running_config->dnode, xpath)) - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + /* Add to linked list. */ + if_connected_add_tail(ifp->connected, ifc); + } - ret = nb_cli_apply_changes(vty, NULL); + /* This address is configured from zebra. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - if (ret != CMD_SUCCESS) - return ret; + /* In case of this route need to install kernel. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) && + CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && + !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) { + /* Some system need to up the interface to set IP address. */ + if (!if_is_up(ifp)) { + if_set_flags(ifp, IFF_UP | IFF_RUNNING); + if_refresh(ifp); + } - /* force protocols to update LINK STATE due to parameters change */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); + dplane_intf_addr_set(ifp, ifc); - return CMD_SUCCESS; + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the + * notification + * from the kernel has been received. + * It will also be added to the subnet chain list, then. */ + } } -/* STANDARD TE metrics */ -DEFUN (link_params_metric, - link_params_metric_cmd, - "metric (0-4294967295)", - "Link metric for MPLS-TE purpose\n" - "Metric value in decimal\n") +void if_ip_address_uninstall(struct interface *ifp, struct prefix *prefix, + struct prefix *pp) { - int idx_number = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - uint32_t metric; - - metric = strtoul(argv[idx_number]->arg, NULL, 10); - - if (!iflp) - iflp = if_link_params_enable(ifp); + struct connected *ifc; - /* Update TE metric if needed */ - link_param_cmd_set_uint32(ifp, &iflp->te_metric, LP_TE_METRIC, metric); + ifc = connected_check_ptp(ifp, prefix, pp); + assert(ifc); - return CMD_SUCCESS; -} + UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); -DEFUN (no_link_params_metric, - no_link_params_metric_cmd, - "no metric", - NO_STR - "Disable Link Metric on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); + /* This is not real address or interface is not active. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) + || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + if_connected_del(ifp->connected, ifc); + connected_free(&ifc); + return; + } - /* Unset TE Metric */ - link_param_cmd_unset(ifp, LP_TE_METRIC); + /* This is real route. */ + dplane_intf_addr_unset(ifp, ifc); - return CMD_SUCCESS; + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); } -DEFUN (link_params_maxbw, - link_params_maxbw_cmd, - "max-bw BANDWIDTH", - "Maximum bandwidth that can be used\n" - "Bytes/second (IEEE floating point format)\n") +void if_ipv6_address_install(struct interface *ifp, struct prefix *prefix) { - int idx_bandwidth = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - - float bw; - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_maxbw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that Maximum bandwidth is not lower than other bandwidth - * parameters */ - if (iflp && ((bw <= iflp->max_rsv_bw) || (bw <= iflp->unrsv_bw[0]) || - (bw <= iflp->unrsv_bw[1]) || (bw <= iflp->unrsv_bw[2]) || - (bw <= iflp->unrsv_bw[3]) || (bw <= iflp->unrsv_bw[4]) || - (bw <= iflp->unrsv_bw[5]) || (bw <= iflp->unrsv_bw[6]) || - (bw <= iflp->unrsv_bw[7]) || (bw <= iflp->ava_bw) || - (bw <= iflp->res_bw) || (bw <= iflp->use_bw))) { - vty_out(vty, - "Maximum Bandwidth could not be lower than others bandwidth\n"); - return CMD_WARNING_CONFIG_FAILED; - } + struct zebra_if *if_data; + struct connected *ifc; - if (!iflp) - iflp = if_link_params_enable(ifp); + if_data = ifp->info; - /* Update Maximum Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->max_bw, LP_MAX_BW, bw); + ifc = connected_check(ifp, prefix); + if (!ifc) { + ifc = connected_new(); + ifc->ifp = ifp; - return CMD_SUCCESS; -} + /* Address. */ + ifc->address = prefix_new(); + prefix_copy(ifc->address, prefix); -DEFUN (link_params_max_rsv_bw, - link_params_max_rsv_bw_cmd, - "max-rsv-bw BANDWIDTH", - "Maximum bandwidth that may be reserved\n" - "Bytes/second (IEEE floating point format)\n") -{ - int idx_bandwidth = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - float bw; - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_max_rsv_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that bandwidth is not greater than maximum bandwidth parameter - */ - if (iflp && bw > iflp->max_bw) { - vty_out(vty, - "Maximum Reservable Bandwidth could not be greater than Maximum Bandwidth (%g)\n", - iflp->max_bw); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Maximum Reservable Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->max_rsv_bw, LP_MAX_RSV_BW, bw); - - return CMD_SUCCESS; -} - -DEFUN (link_params_unrsv_bw, - link_params_unrsv_bw_cmd, - "unrsv-bw (0-7) BANDWIDTH", - "Unreserved bandwidth at each priority level\n" - "Priority\n" - "Bytes/second (IEEE floating point format)\n") -{ - int idx_number = 1; - int idx_bandwidth = 2; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - int priority; - float bw; - - /* We don't have to consider about range check here. */ - if (sscanf(argv[idx_number]->arg, "%d", &priority) != 1) { - vty_out(vty, "link_params_unrsv_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_unrsv_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that bandwidth is not greater than maximum bandwidth parameter - */ - if (iflp && bw > iflp->max_bw) { - vty_out(vty, - "UnReserved Bandwidth could not be greater than Maximum Bandwidth (%g)\n", - iflp->max_bw); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Unreserved Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW, - bw); - - return CMD_SUCCESS; -} - -DEFPY_YANG(link_params_admin_grp, link_params_admin_grp_cmd, - "admin-grp BITPATTERN", - "Administrative group membership\n" - "32-bit Hexadecimal value (e.g. 0xa1)\n") -{ - char xpath[XPATH_MAXLEN]; - int idx_bitpattern = 1; - unsigned long value; - char value_str[11]; - - VTY_DECLVAR_CONTEXT(interface, ifp); - - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities", - ifp->name); - if (yang_dnode_exists(running_config->dnode, xpath)) { - vty_out(vty, - "cannot use the admin-grp command when affinity is set\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (sscanf(argv[idx_bitpattern]->arg, "0x%lx", &value) != 1) { - vty_out(vty, "link_params_admin_grp: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - if (value > 0xFFFFFFFF) { - vty_out(vty, "value must be not be superior to 0xFFFFFFFF\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - snprintf(value_str, sizeof(value_str), "%ld", value); - - nb_cli_enqueue_change( - vty, "./frr-zebra:zebra/link-params/legacy-admin-group", - NB_OP_MODIFY, value_str); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG(no_link_params_admin_grp, no_link_params_admin_grp_cmd, - "no admin-grp", - NO_STR "Disable Administrative group membership on this interface\n") -{ - nb_cli_enqueue_change( - vty, "./frr-zebra:zebra/link-params/legacy-admin-group", - NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -/* RFC5392 & RFC5316: INTER-AS */ -DEFUN (link_params_inter_as, - link_params_inter_as_cmd, - "neighbor A.B.C.D as (1-4294967295)", - "Configure remote ASBR information (Neighbor IP address and AS number)\n" - "Remote IP address in dot decimal A.B.C.D\n" - "Remote AS number\n" - "AS number in the range <1-4294967295>\n") -{ - int idx_ipv4 = 1; - int idx_number = 3; - - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - struct in_addr addr; - uint32_t as; - - if (!inet_aton(argv[idx_ipv4]->arg, &addr)) { - vty_out(vty, "Please specify Router-Addr by A.B.C.D\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - as = strtoul(argv[idx_number]->arg, NULL, 10); - - /* Update Remote IP and Remote AS fields if needed */ - if (IS_PARAM_UNSET(iflp, LP_RMT_AS) || iflp->rmt_as != as - || iflp->rmt_ip.s_addr != addr.s_addr) { - - iflp->rmt_as = as; - iflp->rmt_ip.s_addr = addr.s_addr; - SET_PARAM(iflp, LP_RMT_AS); - - /* force protocols to update LINK STATE due to parameters change - */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - } - return CMD_SUCCESS; -} - -DEFUN (no_link_params_inter_as, - no_link_params_inter_as_cmd, - "no neighbor", - NO_STR - "Remove Neighbor IP address and AS number for Inter-AS TE\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - - if (!iflp) - return CMD_SUCCESS; - - /* Reset Remote IP and AS neighbor */ - iflp->rmt_as = 0; - iflp->rmt_ip.s_addr = 0; - UNSET_PARAM(iflp, LP_RMT_AS); - - /* force protocols to update LINK STATE due to parameters change */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - - return CMD_SUCCESS; -} - -/* RFC7471: OSPF Traffic Engineering (TE) Metric extensions & - * draft-ietf-isis-metric-extensions-07.txt */ -DEFUN (link_params_delay, - link_params_delay_cmd, - "delay (0-16777215) [min (0-16777215) max (0-16777215)]", - "Unidirectional Average Link Delay\n" - "Average delay in micro-second as decimal (0...16777215)\n" - "Minimum delay\n" - "Minimum delay in micro-second as decimal (0...16777215)\n" - "Maximum delay\n" - "Maximum delay in micro-second as decimal (0...16777215)\n") -{ - /* Get and Check new delay values */ - uint32_t delay = 0, low = 0, high = 0; - delay = strtoul(argv[1]->arg, NULL, 10); - if (argc == 6) { - low = strtoul(argv[3]->arg, NULL, 10); - high = strtoul(argv[5]->arg, NULL, 10); - } - - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - uint8_t update = 0; - - if (argc == 2) { - /* - * Check new delay value against old Min and Max delays if set - * - * RFC 7471 Section 4.2.7: - * It is possible for min delay and max delay to be - * the same value. - * - * Therefore, it is also allowed that the average - * delay be equal to the min delay or max delay. - */ - if (iflp && IS_PARAM_SET(iflp, LP_MM_DELAY) && - (delay < iflp->min_delay || delay > iflp->max_delay)) { - vty_out(vty, - "Average delay should be in range Min (%d) - Max (%d) delay\n", - iflp->min_delay, iflp->max_delay); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update delay if value is not set or change */ - if (IS_PARAM_UNSET(iflp, LP_DELAY) || iflp->av_delay != delay) { - iflp->av_delay = delay; - SET_PARAM(iflp, LP_DELAY); - update = 1; - } - /* Unset Min and Max delays if already set */ - if (IS_PARAM_SET(iflp, LP_MM_DELAY)) { - iflp->min_delay = 0; - iflp->max_delay = 0; - UNSET_PARAM(iflp, LP_MM_DELAY); - update = 1; - } - } else { - /* - * Check new delays value coherency. See above note - * regarding average delay equal to min/max allowed - */ - if (delay < low || delay > high) { - vty_out(vty, - "Average delay should be in range Min (%d) - Max (%d) delay\n", - low, high); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Delays if needed */ - if (IS_PARAM_UNSET(iflp, LP_DELAY) - || IS_PARAM_UNSET(iflp, LP_MM_DELAY) - || iflp->av_delay != delay || iflp->min_delay != low - || iflp->max_delay != high) { - iflp->av_delay = delay; - SET_PARAM(iflp, LP_DELAY); - iflp->min_delay = low; - iflp->max_delay = high; - SET_PARAM(iflp, LP_MM_DELAY); - update = 1; - } - } - - /* force protocols to update LINK STATE due to parameters change */ - if (update == 1 && if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_delay, - no_link_params_delay_cmd, - "no delay", - NO_STR - "Disable Unidirectional Average, Min & Max Link Delay on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - - if (!iflp) - return CMD_SUCCESS; - - /* Unset Delays */ - iflp->av_delay = 0; - UNSET_PARAM(iflp, LP_DELAY); - iflp->min_delay = 0; - iflp->max_delay = 0; - UNSET_PARAM(iflp, LP_MM_DELAY); - - /* force protocols to update LINK STATE due to parameters change */ - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - - return CMD_SUCCESS; -} - -DEFUN (link_params_delay_var, - link_params_delay_var_cmd, - "delay-variation (0-16777215)", - "Unidirectional Link Delay Variation\n" - "delay variation in micro-second as decimal (0...16777215)\n") -{ - int idx_number = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - uint32_t value; - - value = strtoul(argv[idx_number]->arg, NULL, 10); - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Delay Variation if needed */ - link_param_cmd_set_uint32(ifp, &iflp->delay_var, LP_DELAY_VAR, value); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_delay_var, - no_link_params_delay_var_cmd, - "no delay-variation", - NO_STR - "Disable Unidirectional Delay Variation on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* Unset Delay Variation */ - link_param_cmd_unset(ifp, LP_DELAY_VAR); - - return CMD_SUCCESS; -} - -DEFUN (link_params_pkt_loss, - link_params_pkt_loss_cmd, - "packet-loss PERCENTAGE", - "Unidirectional Link Packet Loss\n" - "percentage of total traffic by 0.000003% step and less than 50.331642%\n") -{ - int idx_percentage = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - float fval; - - if (sscanf(argv[idx_percentage]->arg, "%g", &fval) != 1) { - vty_out(vty, "link_params_pkt_loss: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - if (fval > MAX_PKT_LOSS) - fval = MAX_PKT_LOSS; - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Packet Loss if needed */ - link_param_cmd_set_float(ifp, &iflp->pkt_loss, LP_PKT_LOSS, fval); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_pkt_loss, - no_link_params_pkt_loss_cmd, - "no packet-loss", - NO_STR - "Disable Unidirectional Link Packet Loss on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* Unset Packet Loss */ - link_param_cmd_unset(ifp, LP_PKT_LOSS); - - return CMD_SUCCESS; -} - -DEFUN (link_params_res_bw, - link_params_res_bw_cmd, - "res-bw BANDWIDTH", - "Unidirectional Residual Bandwidth\n" - "Bytes/second (IEEE floating point format)\n") -{ - int idx_bandwidth = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - float bw; - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_res_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that bandwidth is not greater than maximum bandwidth parameter - */ - if (iflp && bw > iflp->max_bw) { - vty_out(vty, - "Residual Bandwidth could not be greater than Maximum Bandwidth (%g)\n", - iflp->max_bw); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Residual Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->res_bw, LP_RES_BW, bw); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_res_bw, - no_link_params_res_bw_cmd, - "no res-bw", - NO_STR - "Disable Unidirectional Residual Bandwidth on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* Unset Residual Bandwidth */ - link_param_cmd_unset(ifp, LP_RES_BW); - - return CMD_SUCCESS; -} - -DEFUN (link_params_ava_bw, - link_params_ava_bw_cmd, - "ava-bw BANDWIDTH", - "Unidirectional Available Bandwidth\n" - "Bytes/second (IEEE floating point format)\n") -{ - int idx_bandwidth = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - float bw; - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_ava_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that bandwidth is not greater than maximum bandwidth parameter - */ - if (iflp && bw > iflp->max_bw) { - vty_out(vty, - "Available Bandwidth could not be greater than Maximum Bandwidth (%g)\n", - iflp->max_bw); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Residual Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->ava_bw, LP_AVA_BW, bw); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_ava_bw, - no_link_params_ava_bw_cmd, - "no ava-bw", - NO_STR - "Disable Unidirectional Available Bandwidth on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* Unset Available Bandwidth */ - link_param_cmd_unset(ifp, LP_AVA_BW); - - return CMD_SUCCESS; -} - -DEFUN (link_params_use_bw, - link_params_use_bw_cmd, - "use-bw BANDWIDTH", - "Unidirectional Utilised Bandwidth\n" - "Bytes/second (IEEE floating point format)\n") -{ - int idx_bandwidth = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct if_link_params *iflp = if_link_params_get(ifp); - float bw; - - if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) { - vty_out(vty, "link_params_use_bw: fscanf: %s\n", - safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check that bandwidth is not greater than maximum bandwidth parameter - */ - if (iflp && bw > iflp->max_bw) { - vty_out(vty, - "Utilised Bandwidth could not be greater than Maximum Bandwidth (%g)\n", - iflp->max_bw); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!iflp) - iflp = if_link_params_enable(ifp); - - /* Update Utilized Bandwidth if needed */ - link_param_cmd_set_float(ifp, &iflp->use_bw, LP_USE_BW, bw); - - return CMD_SUCCESS; -} - -DEFUN (no_link_params_use_bw, - no_link_params_use_bw_cmd, - "no use-bw", - NO_STR - "Disable Unidirectional Utilised Bandwidth on this interface\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - /* Unset Utilised Bandwidth */ - link_param_cmd_unset(ifp, LP_USE_BW); - - return CMD_SUCCESS; -} - -static int ag_change(struct vty *vty, int argc, struct cmd_token **argv, - const char *xpath, bool no, int start_idx) -{ - for (int i = start_idx; i < argc; i++) - nb_cli_enqueue_change(vty, xpath, - no ? NB_OP_DESTROY : NB_OP_CREATE, - argv[i]->arg); - return nb_cli_apply_changes(vty, NULL); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity - */ -DEFPY_YANG(link_params_affinity, link_params_affinity_cmd, - "[no] affinity NAME...", - NO_STR - "Interface affinities\n" - "Affinity names\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - char xpath[XPATH_MAXLEN]; - - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/legacy-admin-group", - ifp->name); - if (yang_dnode_exists(running_config->dnode, xpath)) { - vty_out(vty, - "cannot use the affinity command when admin-grp is set\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return ag_change(vty, argc, argv, - "./frr-zebra:zebra/link-params/affinities/affinity", - no, no ? 2 : 1); -} - - -/* - * XPath: - * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity-mode - */ -DEFPY_YANG(link_params_affinity_mode, link_params_affinity_mode_cmd, - "affinity-mode $affmode", - "Interface affinity mode\n" - "Standard Admin-Group only RFC3630,5305,5329 (default)\n" - "Extended Admin-Group only RFC7308\n" - "Standard and extended Admin-Group format\n") -{ - const char *xpath = "./frr-zebra:zebra/link-params/affinity-mode"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, affmode); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG(no_link_params_affinity_mode, no_link_params_affinity_mode_cmd, - "no affinity-mode []", - NO_STR - "Interface affinity mode\n" - "Standard Admin-Group only RFC3630,5305,5329 (default)\n" - "Extended Admin-Group only RFC7308\n" - "Standard and extended Admin-Group format\n") -{ - const char *xpath = "./frr-zebra:zebra/link-params/affinity-mode"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "standard"); - - return nb_cli_apply_changes(vty, NULL); -} - -static int ag_iter_cb(const struct lyd_node *dnode, void *arg) -{ - struct vty *vty = (struct vty *)arg; - - vty_out(vty, " %s", yang_dnode_get_string(dnode, ".")); - return YANG_ITER_CONTINUE; -} - -void cli_show_legacy_admin_group(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) -{ - if (!yang_dnode_exists(dnode, "./legacy-admin-group")) - return; - - vty_out(vty, " admin-group 0x%x\n", - yang_dnode_get_uint32(dnode, "./legacy-admin-group")); -} - -void cli_show_affinity_mode(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) -{ - enum affinity_mode affinity_mode = yang_dnode_get_enum(dnode, "."); - - if (affinity_mode == AFFINITY_MODE_STANDARD) - vty_out(vty, " affinity-mode standard\n"); - else if (affinity_mode == AFFINITY_MODE_BOTH) - vty_out(vty, " affinity-mode both\n"); -} - -void cli_show_affinity(struct vty *vty, const struct lyd_node *dnode, - bool show_defaults) -{ - if (!yang_dnode_exists(dnode, "./affinity")) - return; - - vty_out(vty, " affinity"); - yang_dnode_iterate(ag_iter_cb, vty, dnode, "./affinity"); - vty_out(vty, "\n"); -} - -int if_ip_address_install(struct interface *ifp, struct prefix *prefix, - const char *label, struct prefix *pp) -{ - struct zebra_if *if_data; - struct prefix_ipv4 lp; - struct prefix_ipv4 *p; - struct connected *ifc; - enum zebra_dplane_result dplane_res; - - if_data = ifp->info; - - lp.family = prefix->family; - lp.prefix = prefix->u.prefix4; - lp.prefixlen = prefix->prefixlen; - apply_mask_ipv4(&lp); - - ifc = connected_check_ptp(ifp, &lp, pp ? pp : NULL); - if (!ifc) { - ifc = connected_new(); - ifc->ifp = ifp; - - /* Address. */ - p = prefix_ipv4_new(); - *p = lp; - ifc->address = (struct prefix *)p; - - if (pp) { - SET_FLAG(ifc->flags, ZEBRA_IFA_PEER); - p = prefix_ipv4_new(); - *p = *(struct prefix_ipv4 *)pp; - ifc->destination = (struct prefix *)p; - } - - /* Label. */ - if (label) - ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); - - /* Add to linked list. */ - listnode_add(ifp->connected, ifc); - } - - /* This address is configured from zebra. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - - /* In case of this route need to install kernel. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) && - CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && - !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) { - /* Some system need to up the interface to set IP address. */ - if (!if_is_up(ifp)) { - if_set_flags(ifp, IFF_UP | IFF_RUNNING); - if_refresh(ifp); - } - - dplane_res = dplane_intf_addr_set(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - zlog_debug( - "dplane can't set interface IP address: %s.", - dplane_res2str(dplane_res)); - return NB_ERR; - } - - SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* The address will be advertised to zebra clients when the - * notification - * from the kernel has been received. - * It will also be added to the subnet chain list, then. */ - } - - return 0; -} - -static int ip_address_install(struct vty *vty, struct interface *ifp, - const char *addr_str, const char *peer_str, - const char *label) -{ - struct zebra_if *if_data; - struct prefix_ipv4 lp, pp; - struct connected *ifc; - struct prefix_ipv4 *p; - int ret; - enum zebra_dplane_result dplane_res; - - if_data = ifp->info; - - ret = str2prefix_ipv4(addr_str, &lp); - if (ret <= 0) { - vty_out(vty, "%% Malformed address \n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (ipv4_martian(&lp.prefix)) { - vty_out(vty, "%% Invalid address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (peer_str) { - if (lp.prefixlen != IPV4_MAX_BITLEN) { - vty_out(vty, - "%% Local prefix length for P-t-P address must be /32\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = str2prefix_ipv4(peer_str, &pp); - if (ret <= 0) { - vty_out(vty, "%% Malformed peer address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL); - if (!ifc) { - ifc = connected_new(); - ifc->ifp = ifp; - - /* Address. */ - p = prefix_ipv4_new(); - *p = lp; - ifc->address = (struct prefix *)p; - - if (peer_str) { - SET_FLAG(ifc->flags, ZEBRA_IFA_PEER); - p = prefix_ipv4_new(); - *p = pp; - ifc->destination = (struct prefix *)p; - } - - /* Label. */ - if (label) - ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); - - /* Add to linked list. */ - listnode_add(ifp->connected, ifc); - } - - /* This address is configured from zebra. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - - /* In case of this route need to install kernel. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) && - CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && - !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) { - /* Some system need to up the interface to set IP address. */ - if (!if_is_up(ifp)) { - if_set_flags(ifp, IFF_UP | IFF_RUNNING); - if_refresh(ifp); - } - - dplane_res = dplane_intf_addr_set(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - vty_out(vty, "%% Can't set interface IP address: %s.\n", - dplane_res2str(dplane_res)); - return CMD_WARNING_CONFIG_FAILED; - } - - SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* The address will be advertised to zebra clients when the - * notification - * from the kernel has been received. - * It will also be added to the subnet chain list, then. */ - } - - return CMD_SUCCESS; -} - -int if_ip_address_uinstall(struct interface *ifp, struct prefix *prefix) -{ - struct connected *ifc = NULL; - enum zebra_dplane_result dplane_res; - - if (prefix->family == AF_INET) { - /* Check current interface address. */ - ifc = connected_check_ptp(ifp, prefix, NULL); - if (!ifc) { - zlog_debug("interface %s Can't find address", - ifp->name); - return -1; - } - - } else if (prefix->family == AF_INET6) { - /* Check current interface address. */ - ifc = connected_check(ifp, prefix); - } - - if (!ifc) { - zlog_debug("interface %s Can't find address", ifp->name); - return -1; - } - UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - - /* This is not real address or interface is not active. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) - || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - listnode_delete(ifp->connected, ifc); - connected_free(&ifc); - return CMD_WARNING_CONFIG_FAILED; - } - - /* This is real route. */ - dplane_res = dplane_intf_addr_unset(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - zlog_debug("Can't unset interface IP address: %s.", - dplane_res2str(dplane_res)); - return -1; - } - UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - - return 0; -} - -static int ip_address_uninstall(struct vty *vty, struct interface *ifp, - const char *addr_str, const char *peer_str, - const char *label) -{ - struct prefix_ipv4 lp, pp; - struct connected *ifc; - int ret; - enum zebra_dplane_result dplane_res; - - /* Convert to prefix structure. */ - ret = str2prefix_ipv4(addr_str, &lp); - if (ret <= 0) { - vty_out(vty, "%% Malformed address \n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (peer_str) { - if (lp.prefixlen != IPV4_MAX_BITLEN) { - vty_out(vty, - "%% Local prefix length for P-t-P address must be /32\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = str2prefix_ipv4(peer_str, &pp); - if (ret <= 0) { - vty_out(vty, "%% Malformed peer address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - /* Check current interface address. */ - ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL); - if (!ifc) { - vty_out(vty, "%% Can't find address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* This is not configured address. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - return CMD_WARNING_CONFIG_FAILED; - - UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - - /* This is not real address or interface is not active. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) - || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - listnode_delete(ifp->connected, ifc); - connected_free(&ifc); - return CMD_WARNING_CONFIG_FAILED; - } - - /* This is real route. */ - dplane_res = dplane_intf_addr_unset(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - vty_out(vty, "%% Can't unset interface IP address: %s.\n", - dplane_res2str(dplane_res)); - return CMD_WARNING_CONFIG_FAILED; - } - UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* we will receive a kernel notification about this route being removed. - * this will trigger its removal from the connected list. */ - return CMD_SUCCESS; -} - -DEFUN (ip_address, - ip_address_cmd, - "ip address A.B.C.D/M", - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "IP address (e.g. 10.0.0.1/8)\n") -{ - int idx_ipv4_prefixlen = 2; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_install(vty, ifp, argv[idx_ipv4_prefixlen]->arg, NULL, - NULL); -} - -DEFUN (no_ip_address, - no_ip_address_cmd, - "no ip address A.B.C.D/M", - NO_STR - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "IP Address (e.g. 10.0.0.1/8)\n") -{ - int idx_ipv4_prefixlen = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_uninstall(vty, ifp, argv[idx_ipv4_prefixlen]->arg, - NULL, NULL); -} - -DEFUN(ip_address_peer, - ip_address_peer_cmd, - "ip address A.B.C.D peer A.B.C.D/M", - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "Local IP (e.g. 10.0.0.1) for P-t-P address\n" - "Specify P-t-P address\n" - "Peer IP address (e.g. 10.0.0.1/8)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_install(vty, ifp, argv[2]->arg, argv[4]->arg, NULL); -} - -DEFUN(no_ip_address_peer, - no_ip_address_peer_cmd, - "no ip address A.B.C.D peer A.B.C.D/M", - NO_STR - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "Local IP (e.g. 10.0.0.1) for P-t-P address\n" - "Specify P-t-P address\n" - "Peer IP address (e.g. 10.0.0.1/8)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_uninstall(vty, ifp, argv[3]->arg, argv[5]->arg, NULL); -} - -#ifdef HAVE_NETLINK -DEFUN (ip_address_label, - ip_address_label_cmd, - "ip address A.B.C.D/M label LINE", - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "IP address (e.g. 10.0.0.1/8)\n" - "Label of this address\n" - "Label\n") -{ - int idx_ipv4_prefixlen = 2; - int idx_line = 4; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_install(vty, ifp, argv[idx_ipv4_prefixlen]->arg, NULL, - argv[idx_line]->arg); -} - -DEFUN (no_ip_address_label, - no_ip_address_label_cmd, - "no ip address A.B.C.D/M label LINE", - NO_STR - "Interface Internet Protocol config commands\n" - "Set the IP address of an interface\n" - "IP address (e.g. 10.0.0.1/8)\n" - "Label of this address\n" - "Label\n") -{ - int idx_ipv4_prefixlen = 3; - int idx_line = 5; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ip_address_uninstall(vty, ifp, argv[idx_ipv4_prefixlen]->arg, - NULL, argv[idx_line]->arg); -} -#endif /* HAVE_NETLINK */ - -int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, - const char *label) -{ - struct zebra_if *if_data; - struct prefix_ipv6 cp; - struct connected *ifc; - struct prefix_ipv6 *p; - enum zebra_dplane_result dplane_res; - - if_data = ifp->info; - - cp.family = prefix->family; - cp.prefixlen = prefix->prefixlen; - cp.prefix = prefix->u.prefix6; - apply_mask_ipv6(&cp); - - ifc = connected_check(ifp, (struct prefix *)&cp); - if (!ifc) { - ifc = connected_new(); - ifc->ifp = ifp; - - /* Address. */ - p = prefix_ipv6_new(); - *p = cp; - ifc->address = (struct prefix *)p; - - /* Label. */ - if (label) - ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); - - /* Add to linked list. */ - listnode_add(ifp->connected, ifc); - } - - /* This address is configured from zebra. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); - - /* In case of this route need to install kernel. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) && - CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && - !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) { - /* Some system need to up the interface to set IP address. */ - if (!if_is_up(ifp)) { - if_set_flags(ifp, IFF_UP | IFF_RUNNING); - if_refresh(ifp); - } - - dplane_res = dplane_intf_addr_set(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - zlog_debug( - "dplane can't set interface IP address: %s.", - dplane_res2str(dplane_res)); - return NB_ERR; - } - - SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* The address will be advertised to zebra clients when the - * notification - * from the kernel has been received. */ - } - - return 0; -} - -static int ipv6_address_install(struct vty *vty, struct interface *ifp, - const char *addr_str, const char *peer_str, - const char *label) -{ - struct zebra_if *if_data; - struct prefix_ipv6 cp; - struct connected *ifc; - struct prefix_ipv6 *p; - int ret; - enum zebra_dplane_result dplane_res; - - if_data = ifp->info; - - ret = str2prefix_ipv6(addr_str, &cp); - if (ret <= 0) { - vty_out(vty, "%% Malformed address \n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (ipv6_martian(&cp.prefix)) { - vty_out(vty, "%% Invalid address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ifc = connected_check(ifp, (struct prefix *)&cp); - if (!ifc) { - ifc = connected_new(); - ifc->ifp = ifp; - - /* Address. */ - p = prefix_ipv6_new(); - *p = cp; - ifc->address = (struct prefix *)p; - - /* Label. */ - if (label) - ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); - - /* Add to linked list. */ - listnode_add(ifp->connected, ifc); + /* Add to linked list. */ + if_connected_add_tail(ifp->connected, ifc); } /* This address is configured from zebra. */ @@ -5393,265 +3930,36 @@ static int ipv6_address_install(struct vty *vty, struct interface *ifp, if_refresh(ifp); } - dplane_res = dplane_intf_addr_set(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - vty_out(vty, "%% Can't set interface IP address: %s.\n", - dplane_res2str(dplane_res)); - return CMD_WARNING_CONFIG_FAILED; - } + dplane_intf_addr_set(ifp, ifc); SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* The address will be advertised to zebra clients when the * notification * from the kernel has been received. */ } - - return CMD_SUCCESS; -} - -/* Return true if an ipv6 address is configured on ifp */ -int ipv6_address_configured(struct interface *ifp) -{ - struct connected *connected; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) - if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL) - && (connected->address->family == AF_INET6)) - return 1; - - return 0; } -static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp, - const char *addr_str, const char *peer_str, - const char *label) +void if_ipv6_address_uninstall(struct interface *ifp, struct prefix *prefix) { - struct prefix_ipv6 cp; struct connected *ifc; - int ret; - enum zebra_dplane_result dplane_res; - - /* Convert to prefix structure. */ - ret = str2prefix_ipv6(addr_str, &cp); - if (ret <= 0) { - vty_out(vty, "%% Malformed address \n"); - return CMD_WARNING_CONFIG_FAILED; - } - /* Check current interface address. */ - ifc = connected_check(ifp, (struct prefix *)&cp); - if (!ifc) { - vty_out(vty, "%% Can't find address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* This is not configured address. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - return CMD_WARNING_CONFIG_FAILED; + ifc = connected_check(ifp, prefix); + assert(ifc); UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); /* This is not real address or interface is not active. */ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - listnode_delete(ifp->connected, ifc); + if_connected_del(ifp->connected, ifc); connected_free(&ifc); - return CMD_WARNING_CONFIG_FAILED; + return; } /* This is real route. */ - dplane_res = dplane_intf_addr_unset(ifp, ifc); - if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { - vty_out(vty, "%% Can't unset interface IP address: %s.\n", - dplane_res2str(dplane_res)); - return CMD_WARNING_CONFIG_FAILED; - } + dplane_intf_addr_unset(ifp, ifc); UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* This information will be propagated to the zclients when the - * kernel notification is received. */ - return CMD_SUCCESS; -} - -DEFUN (ipv6_address, - ipv6_address_cmd, - "ipv6 address X:X::X:X/M", - "Interface IPv6 config commands\n" - "Set the IP address of an interface\n" - "IPv6 address (e.g. 3ffe:506::1/48)\n") -{ - int idx_ipv6_prefixlen = 2; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ipv6_address_install(vty, ifp, argv[idx_ipv6_prefixlen]->arg, - NULL, NULL); -} - -DEFUN (no_ipv6_address, - no_ipv6_address_cmd, - "no ipv6 address X:X::X:X/M", - NO_STR - "Interface IPv6 config commands\n" - "Set the IP address of an interface\n" - "IPv6 address (e.g. 3ffe:506::1/48)\n") -{ - int idx_ipv6_prefixlen = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - return ipv6_address_uninstall(vty, ifp, argv[idx_ipv6_prefixlen]->arg, - NULL, NULL); -} - -static int link_params_config_write(struct vty *vty, struct interface *ifp) -{ - const struct lyd_node *dnode; - char xpath[XPATH_MAXLEN]; - int i; - - if ((ifp == NULL) || !HAS_LINK_PARAMS(ifp)) - return -1; - - struct if_link_params *iflp = ifp->link_params; - - vty_out(vty, " link-params\n"); - vty_out(vty, " enable\n"); - if (IS_PARAM_SET(iflp, LP_TE_METRIC) && iflp->te_metric != ifp->metric) - vty_out(vty, " metric %u\n", iflp->te_metric); - if (IS_PARAM_SET(iflp, LP_MAX_BW) && iflp->max_bw != iflp->default_bw) - vty_out(vty, " max-bw %g\n", iflp->max_bw); - if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW) - && iflp->max_rsv_bw != iflp->default_bw) - vty_out(vty, " max-rsv-bw %g\n", iflp->max_rsv_bw); - if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) { - for (i = 0; i < 8; i++) - if (iflp->unrsv_bw[i] != iflp->default_bw) - vty_out(vty, " unrsv-bw %d %g\n", i, - iflp->unrsv_bw[i]); - } - - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params", - ifp->name); - dnode = yang_dnode_get(running_config->dnode, xpath); - if (dnode) - nb_cli_show_dnode_cmds(vty, dnode, false); - - if (IS_PARAM_SET(iflp, LP_DELAY)) { - vty_out(vty, " delay %u", iflp->av_delay); - if (IS_PARAM_SET(iflp, LP_MM_DELAY)) { - vty_out(vty, " min %u", iflp->min_delay); - vty_out(vty, " max %u", iflp->max_delay); - } - vty_out(vty, "\n"); - } - if (IS_PARAM_SET(iflp, LP_DELAY_VAR)) - vty_out(vty, " delay-variation %u\n", iflp->delay_var); - if (IS_PARAM_SET(iflp, LP_PKT_LOSS)) - vty_out(vty, " packet-loss %g\n", iflp->pkt_loss); - if (IS_PARAM_SET(iflp, LP_AVA_BW)) - vty_out(vty, " ava-bw %g\n", iflp->ava_bw); - if (IS_PARAM_SET(iflp, LP_RES_BW)) - vty_out(vty, " res-bw %g\n", iflp->res_bw); - if (IS_PARAM_SET(iflp, LP_USE_BW)) - vty_out(vty, " use-bw %g\n", iflp->use_bw); - if (IS_PARAM_SET(iflp, LP_RMT_AS)) - vty_out(vty, " neighbor %pI4 as %u\n", &iflp->rmt_ip, - iflp->rmt_as); - - vty_out(vty, " exit-link-params\n"); - return 0; -} - -static int if_config_write(struct vty *vty) -{ - struct vrf *vrf; - struct interface *ifp; - - zebra_ptm_write(vty); - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - FOR_ALL_INTERFACES (vrf, ifp) { - struct zebra_if *if_data; - struct listnode *addrnode; - struct connected *ifc; - struct prefix *p; - - if_data = ifp->info; - - if_vty_config_start(vty, ifp); - - if (if_data) { - if (if_data->shutdown == IF_ZEBRA_DATA_ON) - vty_out(vty, " shutdown\n"); - - zebra_ptm_if_write(vty, if_data); - } - - if (ifp->desc) - vty_out(vty, " description %s\n", ifp->desc); - - /* Assign bandwidth here to avoid unnecessary interface - flap - while processing config script */ - if (ifp->bandwidth != 0) - vty_out(vty, " bandwidth %u\n", ifp->bandwidth); - - if (!CHECK_FLAG(ifp->status, - ZEBRA_INTERFACE_LINKDETECTION)) - vty_out(vty, " no link-detect\n"); - - for (ALL_LIST_ELEMENTS_RO(ifp->connected, addrnode, - ifc)) { - if (CHECK_FLAG(ifc->conf, - ZEBRA_IFC_CONFIGURED)) { - char buf[INET6_ADDRSTRLEN]; - p = ifc->address; - vty_out(vty, " ip%s address %s", - p->family == AF_INET ? "" - : "v6", - inet_ntop(p->family, - &p->u.prefix, buf, - sizeof(buf))); - if (CONNECTED_PEER(ifc)) { - p = ifc->destination; - vty_out(vty, " peer %s", - inet_ntop(p->family, - &p->u.prefix, - buf, - sizeof(buf))); - } - vty_out(vty, "/%d", p->prefixlen); - - if (ifc->label) - vty_out(vty, " label %s", - ifc->label); - - vty_out(vty, "\n"); - } - } - - if (if_data) { - if (if_data->multicast != IF_ZEBRA_DATA_UNSPEC) - vty_out(vty, " %smulticast\n", - if_data->multicast == - IF_ZEBRA_DATA_ON - ? "" - : "no "); - - if (if_data->mpls_config == IF_ZEBRA_DATA_ON) - vty_out(vty, " mpls enable\n"); - else if (if_data->mpls_config == - IF_ZEBRA_DATA_OFF) - vty_out(vty, " mpls disable\n"); - } - - hook_call(zebra_if_config_wr, vty, ifp); - zebra_evpn_mh_if_write(vty, ifp); - link_params_config_write(vty, ifp); - - if_vty_config_end(vty); - } - return 0; } /* Allocate and initialize interface vector. */ @@ -5661,15 +3969,6 @@ void zebra_if_init(void) hook_register_prio(if_add, 0, if_zebra_new_hook); hook_register_prio(if_del, 0, if_zebra_delete_hook); - /* Install configuration write function. */ - if_cmd_init(if_config_write); - install_node(&link_params_node); - /* - * This is *intentionally* setting this to NULL, signaling - * that interface creation for zebra acts differently - */ - if_zapi_callbacks(NULL, NULL, NULL, NULL); - install_element(VIEW_NODE, &show_interface_cmd); install_element(VIEW_NODE, &show_interface_vrf_all_cmd); install_element(VIEW_NODE, &show_interface_name_vrf_cmd); @@ -5677,55 +3976,4 @@ void zebra_if_init(void) install_element(ENABLE_NODE, &show_interface_desc_cmd); install_element(ENABLE_NODE, &show_interface_desc_vrf_all_cmd); - install_element(INTERFACE_NODE, &multicast_cmd); - install_element(INTERFACE_NODE, &no_multicast_cmd); - install_element(INTERFACE_NODE, &mpls_cmd); - install_element(INTERFACE_NODE, &linkdetect_cmd); - install_element(INTERFACE_NODE, &no_linkdetect_cmd); - install_element(INTERFACE_NODE, &shutdown_if_cmd); - install_element(INTERFACE_NODE, &no_shutdown_if_cmd); - install_element(INTERFACE_NODE, &bandwidth_if_cmd); - install_element(INTERFACE_NODE, &no_bandwidth_if_cmd); - install_element(INTERFACE_NODE, &ip_address_cmd); - install_element(INTERFACE_NODE, &no_ip_address_cmd); - install_element(INTERFACE_NODE, &ip_address_peer_cmd); - install_element(INTERFACE_NODE, &no_ip_address_peer_cmd); - install_element(INTERFACE_NODE, &ipv6_address_cmd); - install_element(INTERFACE_NODE, &no_ipv6_address_cmd); -#ifdef HAVE_NETLINK - install_element(INTERFACE_NODE, &ip_address_label_cmd); - install_element(INTERFACE_NODE, &no_ip_address_label_cmd); -#endif /* HAVE_NETLINK */ - install_element(INTERFACE_NODE, &link_params_cmd); - install_default(LINK_PARAMS_NODE); - install_element(LINK_PARAMS_NODE, &link_params_enable_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_enable_cmd); - install_element(LINK_PARAMS_NODE, &link_params_metric_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_metric_cmd); - install_element(LINK_PARAMS_NODE, &link_params_maxbw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_max_rsv_bw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_unrsv_bw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_admin_grp_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_admin_grp_cmd); - install_element(LINK_PARAMS_NODE, &link_params_inter_as_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_inter_as_cmd); - install_element(LINK_PARAMS_NODE, &link_params_delay_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_delay_cmd); - install_element(LINK_PARAMS_NODE, &link_params_delay_var_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_delay_var_cmd); - install_element(LINK_PARAMS_NODE, &link_params_pkt_loss_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_pkt_loss_cmd); - install_element(LINK_PARAMS_NODE, &link_params_ava_bw_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_ava_bw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_res_bw_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_res_bw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd); - install_element(LINK_PARAMS_NODE, &link_params_affinity_cmd); - install_element(LINK_PARAMS_NODE, &link_params_affinity_mode_cmd); - install_element(LINK_PARAMS_NODE, &no_link_params_affinity_mode_cmd); - install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); - - /* setup EVPN MH elements */ - zebra_evpn_interface_init(); } diff --git a/zebra/interface.h b/zebra/interface.h index 3b67995..7d633f3 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -201,6 +201,9 @@ struct zebra_if { ifindex_t link_ifindex; struct interface *link; +#define INTERFACE_SPEED_ERROR_READ -1 +#define INTERFACE_SPEED_ERROR_UNKNOWN -2 + uint8_t speed_update_count; struct event *speed_update; @@ -218,8 +221,6 @@ struct zebra_if { DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), (vty, ifp)); -DECLARE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp), - (vty, ifp)); #define IS_ZEBRA_IF_VRF(ifp) \ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF) @@ -283,7 +284,6 @@ extern void if_refresh(struct interface *); extern void if_flags_update(struct interface *, uint64_t); extern int if_subnet_add(struct interface *, struct connected *); extern int if_subnet_delete(struct interface *, struct connected *); -extern int ipv6_address_configured(struct interface *ifp); extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id); extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, ns_id_t ns_id); @@ -308,18 +308,28 @@ extern void cli_show_affinity(struct vty *vty, const struct lyd_node *dnode, */ extern int zebra_if_set_protodown(struct interface *ifp, bool down, enum protodown_reasons new_reason); -extern int if_ip_address_install(struct interface *ifp, struct prefix *prefix, - const char *label, struct prefix *pp); -extern int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, - const char *label); -extern int if_ip_address_uinstall(struct interface *ifp, struct prefix *prefix); +extern void if_ip_address_install(struct interface *ifp, struct prefix *prefix, + const char *label, struct prefix *pp); +extern void if_ip_address_uninstall(struct interface *ifp, + struct prefix *prefix, struct prefix *pp); +extern void if_ipv6_address_install(struct interface *ifp, + struct prefix *prefix); +extern void if_ipv6_address_uninstall(struct interface *ifp, + struct prefix *prefix); extern int if_shutdown(struct interface *ifp); extern int if_no_shutdown(struct interface *ifp); +extern void if_arp(struct interface *ifp, bool enable); extern int if_multicast_set(struct interface *ifp); extern int if_multicast_unset(struct interface *ifp); extern int if_linkdetect(struct interface *ifp, bool detect); extern void if_addr_wakeup(struct interface *ifp); +void link_param_cmd_set_uint32(struct interface *ifp, uint32_t *field, + uint32_t type, uint32_t value); +void link_param_cmd_set_float(struct interface *ifp, float *field, + uint32_t type, float value); +void link_param_cmd_unset(struct interface *ifp, uint32_t type); + /* Nexthop group connected functions */ extern void if_nhg_dependents_add(struct interface *ifp, struct nhg_hash_entry *nhe); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 8da1ae3..a35784c 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -6,6 +6,8 @@ #include +#include + #include "linklist.h" #include "if.h" #include "prefix.h" diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 253e6a8..70f3f57 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -87,12 +87,12 @@ static const char *inet_2a(uint32_t a, char *b, size_t b_len) static struct prefix *irdp_get_prefix(struct interface *ifp) { - struct listnode *node; struct connected *ifc; - if (ifp->connected) - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) + frr_each (if_connected, ifp->connected, ifc) { + if (ifc->address->family == AF_INET) return ifc->address; + } return NULL; } @@ -198,7 +198,6 @@ static void irdp_if_start(struct interface *ifp, int multicast, { struct zebra_if *zi = ifp->info; struct irdp_interface *irdp = zi->irdp; - struct listnode *node; struct connected *ifc; uint32_t timer, seed; @@ -247,11 +246,12 @@ static void irdp_if_start(struct interface *ifp, int multicast, /* The spec suggests this for randomness */ seed = 0; - if (ifp->connected) - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + frr_each (if_connected, ifp->connected, ifc) { + if (ifc->address->family == AF_INET) { seed = ifc->address->u.prefix4.s_addr; break; } + } srandom(seed); timer = (frr_weak_random() % IRDP_DEFAULT_INTERVAL) + 1; @@ -694,7 +694,6 @@ DEFUN (ip_irdp_debug_disable, void irdp_if_init(void) { - hook_register(zebra_if_config_wr, irdp_config_write); hook_register(if_del, irdp_if_delete); install_element(INTERFACE_NODE, &ip_irdp_broadcast_cmd); diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index 6548790..349ae1a 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -197,7 +197,6 @@ void irdp_send_thread(struct event *t_advert) struct zebra_if *zi = ifp->info; struct irdp_interface *irdp = zi->irdp; struct prefix *p; - struct listnode *node, *nnode; struct connected *ifc; if (!irdp) @@ -205,16 +204,15 @@ void irdp_send_thread(struct event *t_advert) irdp->flags &= ~IF_SOLICIT; - if (ifp->connected) - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { - p = ifc->address; + frr_each (if_connected, ifp->connected, ifc) { + p = ifc->address; - if (p->family != AF_INET) - continue; + if (p->family != AF_INET) + continue; - irdp_advertisement(ifp, p); - irdp->irdp_sent++; - } + irdp_advertisement(ifp, p); + irdp->irdp_sent++; + } tmp = irdp->MaxAdvertInterval - irdp->MinAdvertInterval; timer = frr_weak_random() % (tmp + 1); @@ -237,7 +235,6 @@ void irdp_advert_off(struct interface *ifp) { struct zebra_if *zi = ifp->info; struct irdp_interface *irdp = zi->irdp; - struct listnode *node, *nnode; int i; struct connected *ifc; struct prefix *p; @@ -247,19 +244,21 @@ void irdp_advert_off(struct interface *ifp) EVENT_OFF(irdp->t_advertise); - if (ifp->connected) - for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { - p = ifc->address; + frr_each (if_connected, ifp->connected, ifc) { + p = ifc->address; - /* Output some packets with Lifetime 0 - we should add a wait... - */ + if (p->family != AF_INET) + continue; - for (i = 0; i < IRDP_LAST_ADVERT_MESSAGES; i++) { - irdp->irdp_sent++; - irdp_advertisement(ifp, p); - } + /* Output some packets with Lifetime 0 + we should add a wait... + */ + + for (i = 0; i < IRDP_LAST_ADVERT_MESSAGES; i++) { + irdp->irdp_sent++; + irdp_advertisement(ifp, p); } + } } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 7c934ed..8a64a1e 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -4,8 +4,12 @@ */ #include +#include #ifdef HAVE_NETLINK +#include +#include +#include #include "linklist.h" #include "if.h" @@ -35,6 +39,7 @@ #include "zebra/tc_netlink.h" #include "zebra/netconf_netlink.h" #include "zebra/zebra_errors.h" +#include "zebra/ge_netlink.h" #ifndef SO_RCVBUFFORCE #define SO_RCVBUFFORCE (33) @@ -76,43 +81,48 @@ */ #define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE) -static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, - {RTM_DELROUTE, "RTM_DELROUTE"}, - {RTM_GETROUTE, "RTM_GETROUTE"}, - {RTM_NEWLINK, "RTM_NEWLINK"}, - {RTM_SETLINK, "RTM_SETLINK"}, - {RTM_DELLINK, "RTM_DELLINK"}, - {RTM_GETLINK, "RTM_GETLINK"}, - {RTM_NEWADDR, "RTM_NEWADDR"}, - {RTM_DELADDR, "RTM_DELADDR"}, - {RTM_GETADDR, "RTM_GETADDR"}, - {RTM_NEWNEIGH, "RTM_NEWNEIGH"}, - {RTM_DELNEIGH, "RTM_DELNEIGH"}, - {RTM_GETNEIGH, "RTM_GETNEIGH"}, - {RTM_NEWRULE, "RTM_NEWRULE"}, - {RTM_DELRULE, "RTM_DELRULE"}, - {RTM_GETRULE, "RTM_GETRULE"}, - {RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"}, - {RTM_DELNEXTHOP, "RTM_DELNEXTHOP"}, - {RTM_GETNEXTHOP, "RTM_GETNEXTHOP"}, - {RTM_NEWNETCONF, "RTM_NEWNETCONF"}, - {RTM_DELNETCONF, "RTM_DELNETCONF"}, - {RTM_NEWTUNNEL, "RTM_NEWTUNNEL"}, - {RTM_DELTUNNEL, "RTM_DELTUNNEL"}, - {RTM_GETTUNNEL, "RTM_GETTUNNEL"}, - {RTM_NEWQDISC, "RTM_NEWQDISC"}, - {RTM_DELQDISC, "RTM_DELQDISC"}, - {RTM_GETQDISC, "RTM_GETQDISC"}, - {RTM_NEWTCLASS, "RTM_NEWTCLASS"}, - {RTM_DELTCLASS, "RTM_DELTCLASS"}, - {RTM_GETTCLASS, "RTM_GETTCLASS"}, - {RTM_NEWTFILTER, "RTM_NEWTFILTER"}, - {RTM_DELTFILTER, "RTM_DELTFILTER"}, - {RTM_GETTFILTER, "RTM_GETTFILTER"}, - {RTM_NEWVLAN, "RTM_NEWVLAN"}, - {RTM_DELVLAN, "RTM_DELVLAN"}, - {RTM_GETVLAN, "RTM_GETVLAN"}, - {0}}; +static const struct message nlmsg_str[] = { + { RTM_NEWROUTE, "RTM_NEWROUTE" }, + { RTM_DELROUTE, "RTM_DELROUTE" }, + { RTM_GETROUTE, "RTM_GETROUTE" }, + { RTM_NEWLINK, "RTM_NEWLINK" }, + { RTM_SETLINK, "RTM_SETLINK" }, + { RTM_DELLINK, "RTM_DELLINK" }, + { RTM_GETLINK, "RTM_GETLINK" }, + { RTM_NEWADDR, "RTM_NEWADDR" }, + { RTM_DELADDR, "RTM_DELADDR" }, + { RTM_GETADDR, "RTM_GETADDR" }, + { RTM_NEWNEIGH, "RTM_NEWNEIGH" }, + { RTM_DELNEIGH, "RTM_DELNEIGH" }, + { RTM_GETNEIGH, "RTM_GETNEIGH" }, + { RTM_NEWRULE, "RTM_NEWRULE" }, + { RTM_DELRULE, "RTM_DELRULE" }, + { RTM_GETRULE, "RTM_GETRULE" }, + { RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP" }, + { RTM_DELNEXTHOP, "RTM_DELNEXTHOP" }, + { RTM_GETNEXTHOP, "RTM_GETNEXTHOP" }, + { RTM_NEWNETCONF, "RTM_NEWNETCONF" }, + { RTM_DELNETCONF, "RTM_DELNETCONF" }, + { RTM_NEWTUNNEL, "RTM_NEWTUNNEL" }, + { RTM_DELTUNNEL, "RTM_DELTUNNEL" }, + { RTM_GETTUNNEL, "RTM_GETTUNNEL" }, + { RTM_NEWQDISC, "RTM_NEWQDISC" }, + { RTM_DELQDISC, "RTM_DELQDISC" }, + { RTM_GETQDISC, "RTM_GETQDISC" }, + { RTM_NEWTCLASS, "RTM_NEWTCLASS" }, + { RTM_DELTCLASS, "RTM_DELTCLASS" }, + { RTM_GETTCLASS, "RTM_GETTCLASS" }, + { RTM_NEWTFILTER, "RTM_NEWTFILTER" }, + { RTM_DELTFILTER, "RTM_DELTFILTER" }, + { RTM_GETTFILTER, "RTM_GETTFILTER" }, + { RTM_NEWVLAN, "RTM_NEWVLAN" }, + { RTM_DELVLAN, "RTM_DELVLAN" }, + { RTM_GETVLAN, "RTM_GETVLAN" }, + { RTM_NEWCHAIN, "RTM_NEWCHAIN" }, + { RTM_DELCHAIN, "RTM_DELCHAIN" }, + { RTM_GETCHAIN, "RTM_GETCHAIN" }, + { 0 } +}; static const struct message rtproto_str[] = { {RTPROT_REDIRECT, "redirect"}, @@ -301,7 +311,7 @@ static const char *group2str(uint32_t group) /* Make socket for Linux netlink interface. */ static int netlink_socket(struct nlsock *nl, unsigned long groups, uint32_t ext_groups[], uint8_t ext_group_size, - ns_id_t ns_id) + ns_id_t ns_id, int nl_family) { int ret; struct sockaddr_nl snl; @@ -309,7 +319,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, int namelen; frr_with_privs(&zserv_privs) { - sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id); + sock = ns_socket(AF_NETLINK, SOCK_RAW, nl_family, ns_id); if (sock < 0) { zlog_err("Can't open %s socket: %s", nl->name, safe_strerror(errno)); @@ -1219,6 +1229,33 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), return netlink_talk_info(filter, n, &dp_info, startup); } +/* + * Synchronous version of netlink_talk_info. Converts args to suit the + * common version, which is suitable for both sync and async use. + */ +int ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, struct zebra_ns *zns, bool startup) +{ + struct zebra_dplane_info dp_info; + + if (zns->ge_netlink_cmd.sock < 0) + return -1; + + /* Increment sequence number before capturing snapshot of ns socket + * info. + */ + zns->ge_netlink_cmd.seq = zebra_router_get_next_sequence(); + + /* Capture info in intermediate info struct */ + dp_info.ns_id = zns->ns_id; + + dp_info.is_cmd = true; + dp_info.sock = zns->ge_netlink_cmd.sock; + dp_info.seq = zns->ge_netlink_cmd.seq; + + return netlink_talk_info(filter, n, &dp_info, startup); +} + /* Issue request message to kernel via netlink socket. GET messages * are issued through this interface. */ @@ -1612,6 +1649,9 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: return netlink_put_tc_filter_update_msg(bth, ctx); + + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: + return netlink_put_sr_tunsrc_set_msg(bth, ctx); } return FRR_NETLINK_ERROR; @@ -1748,8 +1788,8 @@ void kernel_init(struct zebra_ns *zns) snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); zns->netlink.sock = -1; - if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id) < - 0) { + if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id, + NETLINK_ROUTE) < 0) { zlog_err("Failure to create %s socket", zns->netlink.name); exit(-1); @@ -1760,7 +1800,8 @@ void kernel_init(struct zebra_ns *zns) snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name), "netlink-cmd (NS %u)", zns->ns_id); zns->netlink_cmd.sock = -1; - if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id) < 0) { + if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id, + NETLINK_ROUTE) < 0) { zlog_err("Failure to create %s socket", zns->netlink_cmd.name); exit(-1); @@ -1773,7 +1814,8 @@ void kernel_init(struct zebra_ns *zns) sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)", zns->ns_id); zns->netlink_dplane_out.sock = -1; - if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id) < 0) { + if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id, + NETLINK_ROUTE) < 0) { zlog_err("Failure to create %s socket", zns->netlink_dplane_out.name); exit(-1); @@ -1787,7 +1829,7 @@ void kernel_init(struct zebra_ns *zns) zns->ns_id); zns->netlink_dplane_in.sock = -1; if (netlink_socket(&zns->netlink_dplane_in, dplane_groups, 0, 0, - zns->ns_id) < 0) { + zns->ns_id, NETLINK_ROUTE) < 0) { zlog_err("Failure to create %s socket", zns->netlink_dplane_in.name); exit(-1); @@ -1795,6 +1837,19 @@ void kernel_init(struct zebra_ns *zns) kernel_netlink_nlsock_insert(&zns->netlink_dplane_in); + /* Generic Netlink socket. */ + snprintf(zns->ge_netlink_cmd.name, sizeof(zns->ge_netlink_cmd.name), + "generic-netlink-cmd (NS %u)", zns->ns_id); + zns->ge_netlink_cmd.sock = -1; + if (netlink_socket(&zns->ge_netlink_cmd, 0, 0, 0, zns->ns_id, + NETLINK_GENERIC) < 0) { + zlog_warn("Failure to create %s socket", + zns->ge_netlink_cmd.name); + } + + if (zns->ge_netlink_cmd.sock >= 0) + kernel_netlink_nlsock_insert(&zns->ge_netlink_cmd); + /* * SOL_NETLINK is not available on all platforms yet * apparently. It's in bits/socket.h which I am not @@ -1833,6 +1888,15 @@ void kernel_init(struct zebra_ns *zns) zlog_notice("Registration for extended dp ACK failed : %d %s", errno, safe_strerror(errno)); + if (zns->ge_netlink_cmd.sock >= 0) { + one = 1; + ret = setsockopt(zns->ge_netlink_cmd.sock, SOL_NETLINK, + NETLINK_EXT_ACK, &one, sizeof(one)); + if (ret < 0) + zlog_err("Registration for extended generic netlink cmd ACK failed : %d %s", + errno, safe_strerror(errno)); + } + /* * Trim off the payload of the original netlink message in the * acknowledgment. This option is available since Linux 4.2, so if @@ -1865,12 +1929,22 @@ void kernel_init(struct zebra_ns *zns) zns->netlink_dplane_in.name, safe_strerror(errno), errno); + if (zns->ge_netlink_cmd.sock >= 0) { + if (fcntl(zns->ge_netlink_cmd.sock, F_SETFL, O_NONBLOCK) < 0) + zlog_err("Can't set %s socket error: %s(%d)", + zns->ge_netlink_cmd.name, safe_strerror(errno), + errno); + } + /* Set receive buffer size if it's set from command line */ if (rcvbufsize) { netlink_recvbuf(&zns->netlink, rcvbufsize); netlink_recvbuf(&zns->netlink_cmd, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_out, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_in, rcvbufsize); + + if (zns->ge_netlink_cmd.sock >= 0) + netlink_recvbuf(&zns->ge_netlink_cmd, rcvbufsize); } /* Set filter for inbound sockets, to exclude events we've generated @@ -1889,6 +1963,8 @@ void kernel_init(struct zebra_ns *zns) &zns->t_netlink); rt_netlink_init(); + + ge_netlink_init(zns); } /* Helper to clean up an nlsock */ @@ -1913,11 +1989,16 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) kernel_nlsock_fini(&zns->netlink_dplane_in); + kernel_nlsock_fini(&zns->ge_netlink_cmd); + /* During zebra shutdown, we need to leave the dataplane socket * around until all work is done. */ - if (complete) + if (complete) { kernel_nlsock_fini(&zns->netlink_dplane_out); + + XFREE(MTYPE_NL_BUF, nl_batch_tx_buf); + } } /* diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index e910f62..e37bba0 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -98,6 +98,9 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, bool startup); +extern int +ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, struct zebra_ns *zns, bool startup); extern int netlink_request(struct nlsock *nl, void *req); enum netlink_msg_status { diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index d897f4a..d50e7de 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -5,6 +5,8 @@ #include +#include + #ifndef HAVE_NETLINK #include @@ -48,11 +50,7 @@ extern struct zebra_privs_t zserv_privs; * 0). We follow this practice without questioning it, but it is a * bug if frr calls ROUNDUP with 0. */ -#ifdef __APPLE__ -#define ROUNDUP_TYPE int -#else -#define ROUNDUP_TYPE long -#endif +#define ROUNDUP_TYPE long /* * Because of these varying conventions, the only sane approach is for @@ -834,12 +832,12 @@ int ifam_read(struct ifa_msghdr *ifam) struct interface *ifp = NULL; union sockunion addr, mask, brd; bool dest_same = false; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; short ifnlen = 0; bool isalias = false; uint32_t flags = 0; - ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0'; + ifname[0] = ifname[IFNAMSIZ - 1] = '\0'; /* Allocate and read address information. */ ifam_read_mesg(ifam, &addr, &mask, &brd, ifname, &ifnlen); @@ -851,7 +849,7 @@ int ifam_read(struct ifa_msghdr *ifam) return -1; } - if (ifnlen && strncmp(ifp->name, ifname, INTERFACE_NAMSIZ)) + if (ifnlen && strncmp(ifp->name, ifname, IFNAMSIZ)) isalias = true; /* @@ -995,7 +993,7 @@ void rtm_read(struct rt_msghdr *rtm) int flags; uint32_t zebra_flags; union sockunion dest, mask, gate; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; short ifnlen = 0; struct nexthop nh; struct prefix p; @@ -1626,6 +1624,7 @@ void kernel_update_multi(struct dplane_ctx_list_head *ctx_list) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_STARTUP_STAGE: + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: zlog_err("Unhandled dplane data for %s", dplane_op2str(dplane_ctx_get_op(ctx))); res = ZEBRA_DPLANE_REQUEST_FAILURE; diff --git a/zebra/label_manager.c b/zebra/label_manager.c index fa7dbb0..c97beb6 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -51,10 +51,14 @@ DEFINE_HOOK(lm_get_chunk, DEFINE_HOOK(lm_release_chunk, (struct zserv *client, uint32_t start, uint32_t end), (client, start, end)); +/* show running-config needs an API for dynamic-block */ +DEFINE_HOOK(lm_write_label_block_config, + (struct vty *vty, struct zebra_vrf *zvrf), + (vty, zvrf)); DEFINE_HOOK(lm_cbs_inited, (), ()); -/* define wrappers to be called in zapi_msg.c (as hooks must be called in - * source file where they were defined) +/* define wrappers to be called in zapi_msg.c or zebra_mpls_vty.c (as hooks + * must be called in source file where they were defined) */ void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id) { @@ -71,6 +75,11 @@ void lm_release_chunk_call(struct zserv *client, uint32_t start, uint32_t end) hook_call(lm_release_chunk, client, start, end); } +int lm_write_label_block_config_call(struct vty *vty, struct zebra_vrf *zvrf) +{ + return hook_call(lm_write_label_block_config, vty, zvrf); +} + /* forward declarations of the static functions to be used for some hooks */ static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id); static int label_manager_disconnect(struct zserv *client); @@ -80,6 +89,8 @@ static int label_manager_get_chunk(struct label_manager_chunk **lmc, vrf_id_t vrf_id); static int label_manager_release_label_chunk(struct zserv *client, uint32_t start, uint32_t end); +static int label_manager_write_label_block_config(struct vty *vty, + struct zebra_vrf *zvrf); void delete_label_chunk(void *val) { @@ -138,6 +149,8 @@ void lm_hooks_register(void) hook_register(lm_client_disconnect, label_manager_disconnect); hook_register(lm_get_chunk, label_manager_get_chunk); hook_register(lm_release_chunk, label_manager_release_label_chunk); + hook_register(lm_write_label_block_config, + label_manager_write_label_block_config); } void lm_hooks_unregister(void) { @@ -145,24 +158,129 @@ void lm_hooks_unregister(void) hook_unregister(lm_client_disconnect, label_manager_disconnect); hook_unregister(lm_get_chunk, label_manager_get_chunk); hook_unregister(lm_release_chunk, label_manager_release_label_chunk); + hook_unregister(lm_write_label_block_config, + label_manager_write_label_block_config); +} + +static json_object *lmc_json(struct label_manager_chunk *lmc) +{ + json_object *json = json_object_new_object(); + + json_object_string_add(json, "protocol", zebra_route_string(lmc->proto)); + json_object_int_add(json, "instance", lmc->instance); + json_object_int_add(json, "sessionId", lmc->session_id); + json_object_int_add(json, "start", lmc->start); + json_object_int_add(json, "end", lmc->end); + json_object_boolean_add(json, "dynamic", lmc->is_dynamic); + return json; } -DEFPY(show_label_table, show_label_table_cmd, "show debugging label-table", +DEFPY(show_label_table, show_label_table_cmd, "show debugging label-table [json$uj]", SHOW_STR DEBUG_STR - "Display allocated label chunks\n") + "Display allocated label chunks\n" + JSON_STR) { struct label_manager_chunk *lmc; struct listnode *node; + json_object *json_array = NULL, *json_global = NULL, *json_dyn_block; + + if (uj) { + json_array = json_object_new_array(); + json_global = json_object_new_object(); + json_dyn_block = json_object_new_object(); + json_object_int_add(json_dyn_block, "lowerBound", + lbl_mgr.dynamic_block_start); + json_object_int_add(json_dyn_block, "upperBound", + lbl_mgr.dynamic_block_end); + json_object_object_add(json_global, "dynamicBlock", + json_dyn_block); + } else + vty_out(vty, "Dynamic block: lower-bound %u, upper-bound %u\n", + lbl_mgr.dynamic_block_start, lbl_mgr.dynamic_block_end); for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { + if (uj) { + json_object_array_add(json_array, lmc_json(lmc)); + continue; + } vty_out(vty, "Proto %s: [%u/%u]\n", zebra_route_string(lmc->proto), lmc->start, lmc->end); } + if (uj) { + json_object_object_add(json_global, "chunks", json_array); + vty_json(vty, json_global); + } + return CMD_SUCCESS; +} + +DEFPY(mpls_label_dynamic_block, mpls_label_dynamic_block_cmd, + "[no$no] mpls label dynamic-block [(16-1048575)$start (16-1048575)$end]", + NO_STR + MPLS_STR + "Label configuration\n" + "Configure dynamic label block\n" + "Start label\n" + "End label\n") +{ + struct listnode *node; + struct label_manager_chunk *lmc; + + /* unset dynamic range */ + if (no || + (start == MPLS_LABEL_UNRESERVED_MIN && end == MPLS_LABEL_MAX)) { + lbl_mgr.dynamic_block_start = MPLS_LABEL_UNRESERVED_MIN; + lbl_mgr.dynamic_block_end = MPLS_LABEL_MAX; + return CMD_SUCCESS; + } + if (!start || !end) { + vty_out(vty, + "%% label dynamic-block, range missing, aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (start > end) { + vty_out(vty, + "%% label dynamic-block, wrong range (%ld > %ld), aborting\n", + start, end); + return CMD_WARNING_CONFIG_FAILED; + } + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { + if (lmc->proto == NO_PROTO) + continue; + if (!lmc->is_dynamic && lmc->start >= (uint32_t)start && + lmc->end <= (uint32_t)end) { + vty_out(vty, + "%% Found a static label chunk [%u-%u] for %s in conflict with the dynamic label block\n", + lmc->start, lmc->end, + zebra_route_string(lmc->proto)); + return CMD_WARNING_CONFIG_FAILED; + } else if (lmc->is_dynamic && (lmc->end > (uint32_t)end || + lmc->start < (uint32_t)start)) { + vty_out(vty, + "%% Found a dynamic label chunk [%u-%u] for %s outside the new dynamic label block, consider restart the service\n", + lmc->start, lmc->end, + zebra_route_string(lmc->proto)); + } + } + lbl_mgr.dynamic_block_start = start; + lbl_mgr.dynamic_block_end = end; return CMD_SUCCESS; } +static int label_manager_write_label_block_config(struct vty *vty, + struct zebra_vrf *zvrf) +{ + if (zvrf_id(zvrf) != VRF_DEFAULT) + return 0; + if (lbl_mgr.dynamic_block_start == MPLS_LABEL_UNRESERVED_MIN && + lbl_mgr.dynamic_block_end == MPLS_LABEL_MAX) + return 0; + vty_out(vty, "mpls label dynamic-block %u %u\n", + lbl_mgr.dynamic_block_start, lbl_mgr.dynamic_block_end); + return 1; +} + /** * Init label manager (or proxy to an external one) */ @@ -170,6 +288,8 @@ void label_manager_init(void) { lbl_mgr.lc_list = list_new(); lbl_mgr.lc_list->del = delete_label_chunk; + lbl_mgr.dynamic_block_start = MPLS_LABEL_UNRESERVED_MIN; + lbl_mgr.dynamic_block_end = MPLS_LABEL_MAX; hook_register(zserv_client_close, lm_client_disconnect_cb); /* register default hooks for the label manager actions */ @@ -179,12 +299,18 @@ void label_manager_init(void) hook_call(lm_cbs_inited); install_element(VIEW_NODE, &show_label_table_cmd); + install_element(CONFIG_NODE, &mpls_label_dynamic_block_cmd); +} + +void label_manager_terminate(void) +{ + list_delete(&lbl_mgr.lc_list); } /* alloc and fill a label chunk */ struct label_manager_chunk * create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, - uint8_t keep, uint32_t start, uint32_t end) + uint8_t keep, uint32_t start, uint32_t end, bool is_dynamic) { /* alloc chunk, fill it and return it */ struct label_manager_chunk *lmc = @@ -196,6 +322,7 @@ create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, lmc->instance = instance; lmc->session_id = session_id; lmc->keep = keep; + lmc->is_dynamic = is_dynamic; return lmc; } @@ -223,6 +350,15 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, return NULL; } + if ((lbl_mgr.dynamic_block_start != MPLS_LABEL_UNRESERVED_MIN || + lbl_mgr.dynamic_block_end != MPLS_LABEL_MAX) && + base >= lbl_mgr.dynamic_block_start && + end <= lbl_mgr.dynamic_block_end) { + zlog_warn("Invalid LM request arguments: base: %u, size: %u for %s in conflict with the dynamic label block", + base, size, zebra_route_string(proto)); + return NULL; + } + /* Scan the existing chunks to see if the requested range of labels * falls inside any of such chunks */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { @@ -254,7 +390,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, /* insert chunk between existing chunks */ if (insert_node) { lmc = create_label_chunk(proto, instance, session_id, keep, - base, end); + base, end, false); listnode_add_before(lbl_mgr.lc_list, insert_node, lmc); return lmc; } @@ -277,7 +413,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, } lmc = create_label_chunk(proto, instance, session_id, keep, - base, end); + base, end, false); if (last_node) listnode_add_before(lbl_mgr.lc_list, last_node, lmc); else @@ -288,7 +424,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, /* create a new chunk past all the existing ones and link at * tail */ lmc = create_label_chunk(proto, instance, session_id, keep, - base, end); + base, end, false); listnode_add(lbl_mgr.lc_list, lmc); return lmc; } @@ -313,9 +449,13 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, { struct label_manager_chunk *lmc; struct listnode *node; - uint32_t prev_end = MPLS_LABEL_UNRESERVED_MIN; + uint32_t prev_end = lbl_mgr.dynamic_block_start - 1; + struct label_manager_chunk *lmc_block_last = NULL; - /* handle chunks request with a specific base label */ + /* handle chunks request with a specific base label + * - static label requests: BGP hardset value, Pathd + * - segment routing label requests + */ if (base != MPLS_LABEL_BASE_ANY) return assign_specific_label_chunk(proto, instance, session_id, keep, size, base); @@ -325,37 +465,44 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, /* first check if there's one available */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { - if (lmc->proto == NO_PROTO - && lmc->end - lmc->start + 1 == size) { + if (lmc->start <= prev_end) + continue; + if (lmc->proto == NO_PROTO && + lmc->end - lmc->start + 1 == size && + lmc->end <= lbl_mgr.dynamic_block_end) { lmc->proto = proto; lmc->instance = instance; lmc->session_id = session_id; lmc->keep = keep; + lmc->is_dynamic = true; return lmc; } /* check if we hadve a "hole" behind us that we can squeeze into */ - if ((lmc->start > prev_end) && (lmc->start - prev_end > size)) { + if (lmc->start - prev_end > size && + prev_end + 1 + size <= lbl_mgr.dynamic_block_end) { lmc = create_label_chunk(proto, instance, session_id, keep, prev_end + 1, - prev_end + size); + prev_end + size, true); listnode_add_before(lbl_mgr.lc_list, node, lmc); return lmc; } prev_end = lmc->end; + + /* check if we have a chunk that goes over the end block */ + if (lmc->end > lbl_mgr.dynamic_block_end) + continue; + lmc_block_last = lmc; } /* otherwise create a new one */ uint32_t start_free; - if (list_isempty(lbl_mgr.lc_list)) - start_free = MPLS_LABEL_UNRESERVED_MIN; + if (lmc_block_last == NULL) + start_free = lbl_mgr.dynamic_block_start; else - start_free = ((struct label_manager_chunk *)listgetdata( - listtail(lbl_mgr.lc_list))) - ->end - + 1; + start_free = lmc_block_last->end + 1; - if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) { + if (start_free > lbl_mgr.dynamic_block_end - size + 1) { flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS, "Reached max labels. Start: %u, size: %u", start_free, size); @@ -364,7 +511,7 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, /* create chunk and link at tail */ lmc = create_label_chunk(proto, instance, session_id, keep, start_free, - start_free + size - 1); + start_free + size - 1, true); listnode_add(lbl_mgr.lc_list, lmc); return lmc; } diff --git a/zebra/label_manager.h b/zebra/label_manager.h index 74f40fa..03cf6a6 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -42,6 +42,7 @@ struct label_manager_chunk { unsigned short instance; uint32_t session_id; uint8_t keep; + uint8_t is_dynamic; /* Tell if chunk is dynamic or static */ uint32_t start; /* First label of the chunk */ uint32_t end; /* Last label of the chunk */ }; @@ -61,11 +62,14 @@ DECLARE_HOOK(lm_get_chunk, DECLARE_HOOK(lm_release_chunk, (struct zserv *client, uint32_t start, uint32_t end), (client, start, end)); +DECLARE_HOOK(lm_write_label_block_config, + (struct vty *vty, struct zebra_vrf *zvrf), + (vty, zvrf)); DECLARE_HOOK(lm_cbs_inited, (), ()); -/* declare wrappers to be called in zapi_msg.c (as hooks must be called in - * source file where they were defined) +/* declare wrappers to be called in zapi_msg.c or zebra_mpls_vty.c (as hooks + * must be called in source file where they were defined) */ void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id); void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client, @@ -73,6 +77,7 @@ void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client, vrf_id_t vrf_id); void lm_release_chunk_call(struct zserv *client, uint32_t start, uint32_t end); +int lm_write_label_block_config_call(struct vty *vty, struct zebra_vrf *zvrf); /* API for an external LM to return responses for requests */ int lm_client_connect_response(uint8_t proto, uint16_t instance, @@ -82,7 +87,7 @@ int lm_client_connect_response(uint8_t proto, uint16_t instance, /* convenience function to allocate an lmc to be consumed by the above API */ struct label_manager_chunk * create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, - uint8_t keep, uint32_t start, uint32_t end); + uint8_t keep, uint32_t start, uint32_t end, bool is_dynamic); void delete_label_chunk(void *val); /* register/unregister callbacks for hooks */ @@ -95,9 +100,13 @@ void lm_hooks_unregister(void); */ struct label_manager { struct list *lc_list; + uint32_t dynamic_block_start; + uint32_t dynamic_block_end; }; void label_manager_init(void); +void label_manager_terminate(void); + struct label_manager_chunk * assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id, uint8_t keep, uint32_t size, uint32_t base); diff --git a/zebra/main.c b/zebra/main.c index 1e833ce..27e05e7 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -5,6 +5,10 @@ #include +#ifdef GNU_LINUX +#include +#endif + #include #include "getopt.h" #include "command.h" @@ -21,6 +25,7 @@ #include "affinitymap.h" #include "routemap.h" #include "routing_nb.h" +#include "mgmt_be_client.h" #include "zebra/zebra_router.h" #include "zebra/zebra_errors.h" @@ -54,13 +59,13 @@ pid_t pid; /* Pacify zclient.o in libfrr, which expects this variable. */ struct event_loop *master; +struct mgmt_be_client *mgmt_be_client; + /* Route retain mode flag. */ int retain_mode = 0; int graceful_restart; -bool v6_rr_semantics = false; - /* Receive buffer size for kernel control sockets */ #define RCVBUFSIZE_MIN 4194304 #ifdef HAVE_NETLINK @@ -140,6 +145,10 @@ static void sigint(void) zlog_notice("Terminating on signal"); + nb_oper_cancel_all_walks(); + mgmt_be_client_destroy(mgmt_be_client); + mgmt_be_client = NULL; + atomic_store_explicit(&zrouter.in_shutdown, true, memory_order_relaxed); @@ -195,6 +204,12 @@ static void sigint(void) list_delete(&zrouter.client_list); + /* + * Besides other clean-ups zebra's vrf_disable() also enqueues installed + * routes for removal from the kernel, unless ZEBRA_VRF_RETAIN is set. + */ + vrf_iterate(vrf_disable); + /* Indicate that all new dplane work has been enqueued. When that * work is complete, the dataplane will enqueue an event * with the 'finalize' function. @@ -226,8 +241,17 @@ void zebra_finalize(struct event *dummy) /* Final shutdown of ns resources */ ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); + zebra_rib_terminate(); zebra_router_terminate(); + zebra_mpls_terminate(); + + zebra_pw_terminate(); + + zebra_srv6_terminate(); + + label_manager_terminate(); + ns_terminate(); frr_fini(); exit(0); @@ -271,19 +295,23 @@ static const struct frr_yang_module_info *const zebra_yang_modules[] = { }; /* clang-format on */ -FRR_DAEMON_INFO( - zebra, ZEBRA, .vty_port = ZEBRA_VTY_PORT, .flags = FRR_NO_ZCLIENT, - +/* clang-format off */ +FRR_DAEMON_INFO(zebra, ZEBRA, + .vty_port = ZEBRA_VTY_PORT, .proghelp = "Daemon which manages kernel routing table management and\nredistribution between different routing protocols.", - .signals = zebra_signals, .n_signals = array_size(zebra_signals), + .flags = FRR_NO_ZCLIENT, + + .signals = zebra_signals, + .n_signals = array_size(zebra_signals), .privs = &zserv_privs, .yang_modules = zebra_yang_modules, .n_yang_modules = array_size(zebra_yang_modules), ); +/* clang-format on */ /* Main startup routine. */ int main(int argc, char **argv) @@ -385,7 +413,7 @@ int main(int argc, char **argv) vrf_configure_backend(VRF_BACKEND_NETNS); break; case OPTION_V6_RR_SEMANTICS: - v6_rr_semantics = true; + zrouter.v6_rr_semantics = true; break; case OPTION_ASIC_OFFLOAD: if (!strcmp(optarg, "notify_on_offload")) @@ -408,7 +436,7 @@ int main(int argc, char **argv) /* Zebra related initialize. */ zebra_router_init(asic_offload, notify_on_ack, v6_with_v4_nexthop); zserv_init(); - rib_init(); + zebra_rib_init(); zebra_if_init(); zebra_debug_init(); @@ -418,8 +446,12 @@ int main(int argc, char **argv) zebra_ns_init(); router_id_cmd_init(); zebra_vty_init(); - access_list_init(); + mgmt_be_client = mgmt_be_client_create("zebra", NULL, 0, + zrouter.master); + access_list_init_new(true); prefix_list_init(); + + rtadv_init(); rtadv_cmd_init(); /* PTM socket */ #ifdef ZEBRA_PTM_SUPPORT diff --git a/zebra/netconf_netlink.c b/zebra/netconf_netlink.c index 7352dfb..002d2c7 100644 --- a/zebra/netconf_netlink.c +++ b/zebra/netconf_netlink.c @@ -6,11 +6,14 @@ * Donald Sharp */ #include +#include #ifdef HAVE_NETLINK /* Netlink OSes only */ #include +#include +#include #include "linux/netconf.h" #include "lib/lib_errors.h" diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 7559e31..70ace35 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -16,6 +16,7 @@ #include "log.h" #include "vrf.h" #include "srcdest_table.h" +#include "frrdistance.h" #include "zebra/rib.h" #include "zebra/zebra_router.h" @@ -78,9 +79,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) RNODE_FOREACH_RE (rn, newre) { if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) - zsend_redistribute_route( - ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, - rn, newre); + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, + client, rn, newre, false); } route_unlock_node(rn); @@ -89,14 +89,26 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) /* Redistribute routes. */ static void zebra_redistribute(struct zserv *client, int type, - unsigned short instance, vrf_id_t vrf_id, + unsigned short instance, struct zebra_vrf *zvrf, int afi) { struct route_entry *newre; struct route_table *table; struct route_node *rn; + bool is_table_direct = false; + vrf_id_t vrf_id = zvrf_id(zvrf); + + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + if (vrf_id == VRF_DEFAULT) { + table = zebra_router_find_table(zvrf, instance, afi, + SAFI_UNICAST); + type = ZEBRA_ROUTE_ALL; + is_table_direct = true; + } else + return; + } else + table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); - table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); if (!table) return; @@ -126,10 +138,25 @@ static void zebra_redistribute(struct zserv *client, int type, continue; zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, newre); + client, rn, newre, is_table_direct); } } +/* + * Function to return a valid table id value if table-direct is used + * return 0 otherwise + * This function can be called only if zebra_redistribute_check returns TRUE + */ +static bool zebra_redistribute_is_table_direct(const struct route_entry *re) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(re->vrf_id); + if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) + return true; + return false; +} + /* * Function to check if prefix is candidate for * redistribute. @@ -147,8 +174,19 @@ static bool zebra_redistribute_check(const struct route_node *rn, afi = family2afi(rn->p.family); zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) + if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) { + if (re->table && + redist_check_instance(&client->mi_redist + [afi][ZEBRA_ROUTE_TABLE_DIRECT], + re->table)) { + /* table-direct redistribution only for route entries which + * are on the default vrf, and that have table id different + * from the default table. + */ + return true; + } return false; + } /* If default route and redistributed */ if (is_default_prefix(&rn->p) && @@ -186,6 +224,7 @@ void redistribute_update(const struct route_node *rn, { struct listnode *node, *nnode; struct zserv *client; + bool is_table_direct; if (IS_ZEBRA_DEBUG_RIB) zlog_debug( @@ -211,11 +250,16 @@ void redistribute_update(const struct route_node *rn, re->vrf_id, re->table, re->type, re->distance, re->metric); } + is_table_direct = zebra_redistribute_is_table_direct(re); zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, re); - } else if (zebra_redistribute_check(rn, prev_re, client)) + client, rn, re, + is_table_direct); + } else if (zebra_redistribute_check(rn, prev_re, client)) { + is_table_direct = zebra_redistribute_is_table_direct(prev_re); zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, prev_re); + client, rn, prev_re, + is_table_direct); + } } } @@ -234,6 +278,7 @@ void redistribute_delete(const struct route_node *rn, struct listnode *node, *nnode; struct zserv *client; vrf_id_t vrfid; + bool is_table_direct; if (old_re) vrfid = old_re->vrf_id; @@ -286,9 +331,20 @@ void redistribute_delete(const struct route_node *rn, continue; /* Send a delete for the 'old' re to any subscribed client. */ - if (zebra_redistribute_check(rn, old_re, client)) + if (zebra_redistribute_check(rn, old_re, client)) { + /* + * SA is complaining that old_re could be false + * SA is wrong because old_re is checked for NULL + * in zebra_redistribute_check and false is + * returned in that case. Let's just make SA + * happy. + */ + assert(old_re); + is_table_direct = zebra_redistribute_is_table_direct(old_re); zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, old_re); + client, rn, old_re, + is_table_direct); + } } } @@ -327,8 +383,7 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) instance)) { redist_add_instance(&client->mi_redist[afi][type], instance); - zebra_redistribute(client, type, instance, - zvrf_id(zvrf), afi); + zebra_redistribute(client, type, instance, zvrf, afi); } } else { if (!vrf_bitmap_check(&client->redist[afi][type], @@ -340,7 +395,7 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) zvrf_id(zvrf)); vrf_bitmap_set(&client->redist[afi][type], zvrf_id(zvrf)); - zebra_redistribute(client, type, 0, zvrf_id(zvrf), afi); + zebra_redistribute(client, type, 0, zvrf, afi); } } @@ -663,9 +718,10 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, if (CHECK_FLAG(same->status, ROUTE_ENTRY_REMOVED)) continue; - if (same->type == re->type && same->instance == re->instance - && same->table == re->table - && same->type != ZEBRA_ROUTE_CONNECT) + if (same->type == re->type && same->instance == re->instance && + same->table == re->table && + (same->type != ZEBRA_ROUTE_CONNECT && + same->type != ZEBRA_ROUTE_LOCAL)) break; } diff --git a/zebra/rib.h b/zebra/rib.h index e70b5c1..a721f4b 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -85,25 +85,12 @@ struct route_entry { */ struct nhg_hash_entry *nhe; - /* Nexthop group from FIB (optional), reflecting what is actually - * installed in the FIB if that differs. The 'backup' group is used - * when backup nexthops are present in the route's nhg. - */ - struct nexthop_group fib_ng; - struct nexthop_group fib_backup_ng; - /* Nexthop group hash entry IDs. The "installed" id is the id * used in linux/netlink, if available. */ uint32_t nhe_id; uint32_t nhe_installed_id; - /* Tag */ - route_tag_t tag; - - /* Uptime. */ - time_t uptime; - /* Type of this route. */ int type; @@ -160,7 +147,20 @@ struct route_entry { /* Distance. */ uint8_t distance; + /* Tag */ + route_tag_t tag; + + /* Uptime. */ + time_t uptime; + struct re_opaque *opaque; + + /* Nexthop group from FIB (optional), reflecting what is actually + * installed in the FIB if that differs. The 'backup' group is used + * when backup nexthops are present in the route's nhg. + */ + struct nexthop_group fib_ng; + struct nexthop_group fib_backup_ng; }; #define RIB_SYSTEM_ROUTE(R) RSYSTEM_ROUTE((R)->type) @@ -169,7 +169,7 @@ struct route_entry { /* Define route types that are equivalent to "connected". */ #define RIB_CONNECTED_ROUTE(R) \ - ((R)->type == ZEBRA_ROUTE_CONNECT || (R)->type == ZEBRA_ROUTE_NHRP) + ((R)->type == ZEBRA_ROUTE_CONNECT || (R)->type == ZEBRA_ROUTE_LOCAL || (R)->type == ZEBRA_ROUTE_NHRP) /* meta-queue structure: * sub-queue 0: nexthop group objects @@ -345,6 +345,8 @@ extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re); +void zebra_rib_route_entry_free(struct route_entry *re); + struct route_entry * zebra_rib_route_entry_new(vrf_id_t vrf_id, int type, uint8_t instance, uint32_t flags, uint32_t nhe_id, uint32_t table_id, @@ -414,7 +416,8 @@ extern void rib_update_table(struct route_table *table, extern void rib_sweep_route(struct event *t); extern void rib_sweep_table(struct route_table *table); extern void rib_close_table(struct route_table *table); -extern void rib_init(void); +extern void zebra_rib_init(void); +extern void zebra_rib_terminate(void); extern unsigned long rib_score_proto(uint8_t proto, unsigned short instance); extern unsigned long rib_score_proto_table(uint8_t proto, unsigned short instance, @@ -429,6 +432,7 @@ 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); +extern int rib_queue_nhe_del(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, @@ -627,8 +631,6 @@ extern void zebra_vty_init(void); extern pid_t pid; -extern bool v6_rr_semantics; - extern uint32_t rt_table_main_id; /* Name of hook calls */ diff --git a/zebra/router-id.c b/zebra/router-id.c index ef87d92..2f251a7 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -109,7 +109,7 @@ int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) assert(!"Reached end of function we should never hit"); } -static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) +int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct prefix after, before; struct listnode *node; @@ -241,256 +241,6 @@ void router_id_del_address(struct connected *ifc) zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } -void router_id_write(struct vty *vty, struct zebra_vrf *zvrf) -{ - char space[2]; - - memset(space, 0, sizeof(space)); - - if (zvrf_id(zvrf) != VRF_DEFAULT) - snprintf(space, sizeof(space), "%s", " "); - - if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) { - vty_out(vty, "%sip router-id %pI4\n", space, - &zvrf->rid_user_assigned.u.prefix4); - } - if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) { - vty_out(vty, "%sipv6 router-id %pI6\n", space, - &zvrf->rid_user_assigned.u.prefix6); - } -} - -DEFUN (ip_router_id, - ip_router_id_cmd, - "ip router-id A.B.C.D vrf NAME", - IP_STR - "Manually set the router-id\n" - "IP address to use for router-id\n" - VRF_CMD_HELP_STR) -{ - int idx = 0; - struct prefix rid; - vrf_id_t vrf_id; - struct zebra_vrf *zvrf; - - argv_find(argv, argc, "A.B.C.D", &idx); - - if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) - return CMD_WARNING_CONFIG_FAILED; - - rid.prefixlen = IPV4_MAX_BITLEN; - rid.family = AF_INET; - - argv_find(argv, argc, "NAME", &idx); - VRF_GET_ID(vrf_id, argv[idx]->arg, false); - - zvrf = zebra_vrf_lookup_by_id(vrf_id); - router_id_set(AFI_IP, &rid, zvrf); - - return CMD_SUCCESS; -} - -ALIAS (ip_router_id, - router_id_cmd, - "router-id A.B.C.D vrf NAME", - "Manually set the router-id\n" - "IP address to use for router-id\n" - VRF_CMD_HELP_STR); - -DEFUN (ipv6_router_id, - ipv6_router_id_cmd, - "ipv6 router-id X:X::X:X vrf NAME", - IPV6_STR - "Manually set the router-id\n" - "IPv6 address to use for router-id\n" - VRF_CMD_HELP_STR) -{ - int idx = 0; - struct prefix rid; - vrf_id_t vrf_id; - struct zebra_vrf *zvrf; - - argv_find(argv, argc, "X:X::X:X", &idx); - - if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) - return CMD_WARNING_CONFIG_FAILED; - - rid.prefixlen = IPV6_MAX_BITLEN; - rid.family = AF_INET6; - - argv_find(argv, argc, "NAME", &idx); - VRF_GET_ID(vrf_id, argv[idx]->arg, false); - - zvrf = zebra_vrf_lookup_by_id(vrf_id); - router_id_set(AFI_IP6, &rid, zvrf); - - return CMD_SUCCESS; -} - - -DEFUN (ip_router_id_in_vrf, - ip_router_id_in_vrf_cmd, - "ip router-id A.B.C.D", - IP_STR - "Manually set the router-id\n" - "IP address to use for router-id\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - int idx = 0; - struct prefix rid; - - argv_find(argv, argc, "A.B.C.D", &idx); - - if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) - return CMD_WARNING_CONFIG_FAILED; - - rid.prefixlen = IPV4_MAX_BITLEN; - rid.family = AF_INET; - - router_id_set(AFI_IP, &rid, zvrf); - - return CMD_SUCCESS; -} - -ALIAS (ip_router_id_in_vrf, - router_id_in_vrf_cmd, - "router-id A.B.C.D", - "Manually set the router-id\n" - "IP address to use for router-id\n"); - -DEFUN (ipv6_router_id_in_vrf, - ipv6_router_id_in_vrf_cmd, - "ipv6 router-id X:X::X:X", - IP6_STR - "Manually set the IPv6 router-id\n" - "IPV6 address to use for router-id\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - int idx = 0; - struct prefix rid; - - argv_find(argv, argc, "X:X::X:X", &idx); - - if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) - return CMD_WARNING_CONFIG_FAILED; - - rid.prefixlen = IPV6_MAX_BITLEN; - rid.family = AF_INET6; - - router_id_set(AFI_IP6, &rid, zvrf); - - return CMD_SUCCESS; -} - -DEFUN (no_ip_router_id, - no_ip_router_id_cmd, - "no ip router-id [A.B.C.D vrf NAME]", - NO_STR - IP_STR - "Remove the manually configured router-id\n" - "IP address to use for router-id\n" - VRF_CMD_HELP_STR) -{ - int idx = 0; - struct prefix rid; - vrf_id_t vrf_id = VRF_DEFAULT; - struct zebra_vrf *zvrf; - - rid.u.prefix4.s_addr = 0; - rid.prefixlen = 0; - rid.family = AF_INET; - - if (argv_find(argv, argc, "NAME", &idx)) - VRF_GET_ID(vrf_id, argv[idx]->arg, false); - - zvrf = zebra_vrf_lookup_by_id(vrf_id); - router_id_set(AFI_IP, &rid, zvrf); - - return CMD_SUCCESS; -} - -ALIAS (no_ip_router_id, - no_router_id_cmd, - "no router-id [A.B.C.D vrf NAME]", - NO_STR - "Remove the manually configured router-id\n" - "IP address to use for router-id\n" - VRF_CMD_HELP_STR); - -DEFUN (no_ipv6_router_id, - no_ipv6_router_id_cmd, - "no ipv6 router-id [X:X::X:X vrf NAME]", - NO_STR - IPV6_STR - "Remove the manually configured IPv6 router-id\n" - "IPv6 address to use for router-id\n" - VRF_CMD_HELP_STR) -{ - int idx = 0; - struct prefix rid; - vrf_id_t vrf_id = VRF_DEFAULT; - struct zebra_vrf *zvrf; - - memset(&rid, 0, sizeof(rid)); - rid.family = AF_INET; - - if (argv_find(argv, argc, "NAME", &idx)) - VRF_GET_ID(vrf_id, argv[idx]->arg, false); - - zvrf = zebra_vrf_lookup_by_id(vrf_id); - router_id_set(AFI_IP6, &rid, zvrf); - - return CMD_SUCCESS; -} - -DEFUN (no_ip_router_id_in_vrf, - no_ip_router_id_in_vrf_cmd, - "no ip router-id [A.B.C.D]", - NO_STR - IP_STR - "Remove the manually configured router-id\n" - "IP address to use for router-id\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - struct prefix rid; - - rid.u.prefix4.s_addr = 0; - rid.prefixlen = 0; - rid.family = AF_INET; - - router_id_set(AFI_IP, &rid, zvrf); - - return CMD_SUCCESS; -} - -ALIAS (no_ip_router_id_in_vrf, - no_router_id_in_vrf_cmd, - "no router-id [A.B.C.D]", - NO_STR - "Remove the manually configured router-id\n" - "IP address to use for router-id\n"); - -DEFUN (no_ipv6_router_id_in_vrf, - no_ipv6_router_id_in_vrf_cmd, - "no ipv6 router-id [X:X::X:X]", - NO_STR - IP6_STR - "Remove the manually configured IPv6 router-id\n" - "IPv6 address to use for router-id\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - struct prefix rid; - - memset(&rid, 0, sizeof(rid)); - rid.family = AF_INET; - - router_id_set(AFI_IP6, &rid, zvrf); - - return CMD_SUCCESS; -} - DEFUN (show_ip_router_id, show_ip_router_id_cmd, "show [ip|ipv6] router-id [vrf NAME]", @@ -557,24 +307,6 @@ static int router_id_v6_cmp(void *a, void *b) void router_id_cmd_init(void) { - install_element(CONFIG_NODE, &ip_router_id_cmd); - install_element(CONFIG_NODE, &router_id_cmd); - install_element(CONFIG_NODE, &ipv6_router_id_cmd); - install_element(CONFIG_NODE, &no_ip_router_id_cmd); - install_element(CONFIG_NODE, &no_router_id_cmd); - install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd); - install_element(VRF_NODE, &ip_router_id_in_vrf_cmd); - install_element(CONFIG_NODE, &router_id_in_vrf_cmd); - install_element(VRF_NODE, &router_id_in_vrf_cmd); - install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd); - install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd); - install_element(CONFIG_NODE, &no_ipv6_router_id_cmd); - install_element(CONFIG_NODE, &no_ip_router_id_in_vrf_cmd); - install_element(VRF_NODE, &no_ip_router_id_in_vrf_cmd); - install_element(CONFIG_NODE, &no_router_id_in_vrf_cmd); - install_element(VRF_NODE, &no_router_id_in_vrf_cmd); - install_element(CONFIG_NODE, &no_ipv6_router_id_in_vrf_cmd); - install_element(VRF_NODE, &no_ipv6_router_id_in_vrf_cmd); install_element(VIEW_NODE, &show_ip_router_id_cmd); } diff --git a/zebra/router-id.h b/zebra/router-id.h index 45860d8..09ad4ec 100644 --- a/zebra/router-id.h +++ b/zebra/router-id.h @@ -25,8 +25,8 @@ extern void router_id_add_address(struct connected *c); extern void router_id_del_address(struct connected *c); extern void router_id_init(struct zebra_vrf *zvrf); extern void router_id_cmd_init(void); -extern void router_id_write(struct vty *vty, struct zebra_vrf *zvrf); extern int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf); +extern int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf); #ifdef __cplusplus } diff --git a/zebra/rt.h b/zebra/rt.h index af170a2..e5dc261 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -25,7 +25,8 @@ extern "C" { #define RKERNEL_ROUTE(type) ((type) == ZEBRA_ROUTE_KERNEL) #define RSYSTEM_ROUTE(type) \ - ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT) + ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT || \ + (type) == ZEBRA_ROUTE_LOCAL) #ifndef HAVE_NETLINK /* diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b8362bb..f092fc5 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -278,6 +278,7 @@ int zebra2proto(int proto) proto = RTPROT_ZEBRA; break; case ZEBRA_ROUTE_CONNECT: + case ZEBRA_ROUTE_LOCAL: case ZEBRA_ROUTE_KERNEL: proto = RTPROT_KERNEL; break; @@ -366,7 +367,8 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) proto = ZEBRA_ROUTE_NHG; break; } - /* Intentional fall thru */ + proto = ZEBRA_ROUTE_KERNEL; + break; default: /* * When a user adds a new protocol this will show up @@ -1025,6 +1027,8 @@ int netlink_route_change_read_unicast_internal(struct nlmsghdr *h, re, ng, startup, ctx); if (ng) nexthop_group_delete(&ng); + if (ctx) + zebra_rib_route_entry_free(re); } else { /* * I really don't see how this is possible @@ -2208,7 +2212,8 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; if (((cmd == RTM_NEWROUTE) && - ((p->family == AF_INET) || v6_rr_semantics)) || + ((p->family == AF_INET) || kernel_nexthops_supported() || + zrouter.v6_rr_semantics)) || force_rr) req->n.nlmsg_flags |= NLM_F_REPLACE; @@ -2622,7 +2627,7 @@ static bool _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, if (IS_ZEBRA_DEBUG_KERNEL) { if (i == 0) - snprintf(buf, sizeof(buf1), "group %u", + snprintf(buf, sizeof(buf), "group %u", grp[i].id); else { snprintf(buf1, sizeof(buf1), "/%u", @@ -3087,8 +3092,8 @@ netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { cmd = RTM_NEWROUTE; } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { - - if (p->family == AF_INET || v6_rr_semantics) { + if (p->family == AF_INET || kernel_nexthops_supported() || + zrouter.v6_rr_semantics) { /* Single 'replace' operation */ /* @@ -4237,11 +4242,11 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) * - struct ethaddr mac; (for NEW) */ if (h->nlmsg_type == RTM_NEWNEIGH) - cmd = ZEBRA_NHRP_NEIGH_ADDED; + cmd = ZEBRA_NEIGH_ADDED; else if (h->nlmsg_type == RTM_GETNEIGH) - cmd = ZEBRA_NHRP_NEIGH_GET; + cmd = ZEBRA_NEIGH_GET; else if (h->nlmsg_type == RTM_DELNEIGH) - cmd = ZEBRA_NHRP_NEIGH_REMOVED; + cmd = ZEBRA_NEIGH_REMOVED; else { zlog_debug("%s(): unknown nlmsg type %u", __func__, h->nlmsg_type); @@ -4251,20 +4256,18 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) /* copy LLADDR information */ l2_len = RTA_PAYLOAD(tb[NDA_LLADDR]); } - if (l2_len == IPV4_MAX_BYTELEN || l2_len == 0) { - union sockunion link_layer_ipv4; - if (l2_len) { - sockunion_family(&link_layer_ipv4) = AF_INET; - memcpy((void *)sockunion_get_addr(&link_layer_ipv4), - RTA_DATA(tb[NDA_LLADDR]), l2_len); - } else - sockunion_family(&link_layer_ipv4) = AF_UNSPEC; - zsend_nhrp_neighbor_notify( - cmd, ifp, &ip, - netlink_nbr_entry_state_to_zclient(ndm->ndm_state), - &link_layer_ipv4); - } + union sockunion link_layer_ipv4; + + if (l2_len) { + sockunion_family(&link_layer_ipv4) = AF_INET; + memcpy((void *)sockunion_get_addr(&link_layer_ipv4), + RTA_DATA(tb[NDA_LLADDR]), l2_len); + } else + sockunion_family(&link_layer_ipv4) = AF_UNSPEC; + zsend_neighbor_notify(cmd, ifp, &ip, + netlink_nbr_entry_state_to_zclient(ndm->ndm_state), + &link_layer_ipv4, l2_len); if (h->nlmsg_type == RTM_GETNEIGH) return 0; @@ -4717,77 +4720,24 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, size_t buflen) { ssize_t ret = 0; + enum dplane_op_e op; - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_NEIGH_IP_INSTALL: + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_NEIGH_INSTALL || op == DPLANE_OP_NEIGH_UPDATE || + op == DPLANE_OP_NEIGH_DISCOVER || op == DPLANE_OP_NEIGH_IP_INSTALL) ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); - break; - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_NEIGH_IP_DELETE: + else if (op == DPLANE_OP_NEIGH_DELETE || op == DPLANE_OP_NEIGH_IP_DELETE) ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); - break; - case DPLANE_OP_VTEP_ADD: + else if (op == DPLANE_OP_VTEP_ADD) ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); - break; - case DPLANE_OP_VTEP_DELETE: + else if (op == DPLANE_OP_VTEP_DELETE) ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); - break; - case DPLANE_OP_NEIGH_TABLE_UPDATE: + else if (op == DPLANE_OP_NEIGH_TABLE_UPDATE) ret = netlink_neigh_table_update_ctx(ctx, buf, buflen); - break; - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_NONE: - case DPLANE_OP_STARTUP_STAGE: + else ret = -1; - } return ret; } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index f9888b1..0bfcd51 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -6,6 +6,8 @@ #include +#include + #ifndef HAVE_NETLINK #ifdef __OpenBSD__ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 9af41cb..6aca643 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -6,6 +6,7 @@ */ #include +#include #include "memory.h" #include "sockopt.h" @@ -33,6 +34,7 @@ extern struct zebra_privs_t zserv_privs; static uint32_t interfaces_configured_for_ra_from_bgp; +#define RTADV_ADATA_SIZE 1024 #if defined(HAVE_RTADV) @@ -58,7 +60,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface"); /* adv list node */ struct adv_if { - char name[INTERFACE_NAMSIZ]; + char name[IFNAMSIZ]; struct adv_if_list_item list_item; }; @@ -187,8 +189,9 @@ static void rtadv_send_packet(int sock, struct interface *ifp, struct cmsghdr *cmsgptr; struct in6_pktinfo *pkt; struct sockaddr_in6 addr; - static void *adata = NULL; unsigned char buf[RTADV_MSG_SIZE]; + char adata[RTADV_ADATA_SIZE]; + struct nd_router_advert *rtadv; int ret; int len = 0; @@ -199,22 +202,6 @@ static void rtadv_send_packet(int sock, struct interface *ifp, struct listnode *node; uint16_t pkt_RouterLifetime; - /* - * Allocate control message bufffer. This is dynamic because - * CMSG_SPACE is not guaranteed not to call a function. Note that - * the size will be different on different architectures due to - * differing alignment rules. - */ - if (adata == NULL) { - /* XXX Free on shutdown. */ - adata = calloc(1, CMSG_SPACE(sizeof(struct in6_pktinfo))); - - if (adata == NULL) { - zlog_debug("%s: can't malloc control data", __func__); - exit(-1); - } - } - /* Logging of packet. */ if (IS_ZEBRA_DEBUG_PACKET) zlog_debug("%s(%s:%u): Tx RA, socket %u", ifp->name, @@ -1147,7 +1134,8 @@ static void rtadv_prefix_set_defaults(struct rtadv_prefix *rp) rp->AdvValidLifetime = RTADV_VALID_LIFETIME; } -static void rtadv_prefix_set(struct zebra_if *zif, struct rtadv_prefix *rp) +static struct rtadv_prefix *rtadv_prefix_set(struct zebra_if *zif, + struct rtadv_prefix *rp) { struct rtadv_prefix *rprefix; @@ -1180,13 +1168,16 @@ static void rtadv_prefix_set(struct zebra_if *zif, struct rtadv_prefix *rp) rtadv_prefix_set_defaults(rprefix); } } + + return rprefix; } -static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp) +static void rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp, + struct rtadv_prefix *rprefix) { - struct rtadv_prefix *rprefix; + if (!rprefix) + rprefix = rtadv_prefixes_find(zif->rtadv.prefixes, rp); - rprefix = rtadv_prefixes_find(zif->rtadv.prefixes, rp); if (rprefix != NULL) { /* @@ -1200,20 +1191,35 @@ static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp) if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) { rprefix->AdvPrefixCreate = PREFIX_SRC_AUTO; rtadv_prefix_set_defaults(rprefix); - return 1; + return; } } else if (rp->AdvPrefixCreate == PREFIX_SRC_AUTO) { if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) { rprefix->AdvPrefixCreate = PREFIX_SRC_MANUAL; - return 1; + return; } } rtadv_prefixes_del(zif->rtadv.prefixes, rprefix); rtadv_prefix_free(rprefix); - return 1; - } else - return 0; + } +} + +struct rtadv_prefix *rtadv_add_prefix_manual(struct zebra_if *zif, + struct rtadv_prefix *rp) +{ + rp->AdvPrefixCreate = PREFIX_SRC_MANUAL; + return rtadv_prefix_set(zif, rp); +} + +void rtadv_delete_prefix_manual(struct zebra_if *zif, + struct rtadv_prefix *rprefix) +{ + struct rtadv_prefix rp; + + rp.AdvPrefixCreate = PREFIX_SRC_MANUAL; + + rtadv_prefix_reset(zif, &rp, rprefix); } /* Add IPv6 prefixes learned from the kernel to the RA prefix list */ @@ -1235,7 +1241,7 @@ void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p) rp.prefix = *((struct prefix_ipv6 *)p); apply_mask_ipv6(&rp.prefix); rp.AdvPrefixCreate = PREFIX_SRC_AUTO; - rtadv_prefix_reset(zif, &rp); + rtadv_prefix_reset(zif, &rp, NULL); } static void rtadv_start_interface_events(struct zebra_vrf *zvrf, @@ -1261,8 +1267,8 @@ static void rtadv_start_interface_events(struct zebra_vrf *zvrf, rtadv_event(zvrf, RTADV_START, 0); } -static void ipv6_nd_suppress_ra_set(struct interface *ifp, - enum ipv6_nd_suppress_ra_status status) +void ipv6_nd_suppress_ra_set(struct interface *ifp, + enum ipv6_nd_suppress_ra_status status) { struct zebra_if *zif; struct zebra_vrf *zvrf; @@ -1310,6 +1316,36 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, } } +void ipv6_nd_interval_set(struct interface *ifp, uint32_t interval) +{ + struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = rtadv_interface_get_zvrf(ifp); + struct adv_if *adv_if; + + if (zif->rtadv.MaxRtrAdvInterval % 1000) { + adv_if = adv_msec_if_del(zvrf, ifp->name); + if (adv_if != NULL) + adv_if_free(adv_if); + } + + if (interval % 1000) + (void)adv_msec_if_add(zvrf, ifp->name); + + zif->rtadv.MaxRtrAdvInterval = interval; + zif->rtadv.MinRtrAdvInterval = 0.33 * interval; + + if (interval != RTADV_MAX_RTR_ADV_INTERVAL) { + SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); + zif->rtadv.AdvIntervalTimer = 0; + } else { + if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) + zif->rtadv.MaxRtrAdvInterval = 10000; + + UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); + zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; + } +} + /* * Handle client (BGP) message to enable or disable IPv6 RA on an interface. * Note that while the client could request RA on an interface on which the @@ -1427,7 +1463,7 @@ void rtadv_stop_ra_all(void) frr_each_safe (rtadv_prefixes, zif->rtadv.prefixes, rprefix) - rtadv_prefix_reset(zif, rprefix); + rtadv_prefix_reset(zif, rprefix, rprefix); rtadv_stop_ra(ifp); } @@ -1512,777 +1548,6 @@ DEFPY(show_ipv6_nd_ra_if, show_ipv6_nd_ra_if_cmd, return CMD_SUCCESS; } -DEFUN (ipv6_nd_ra_fast_retrans, - ipv6_nd_ra_fast_retrans_cmd, - "ipv6 nd ra-fast-retrans", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Fast retransmit of RA packets\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.UseFastRexmit = true; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_ra_fast_retrans, - no_ipv6_nd_ra_fast_retrans_cmd, - "no ipv6 nd ra-fast-retrans", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Fast retransmit of RA packets\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.UseFastRexmit = false; - - return CMD_SUCCESS; -} - -DEFPY (ipv6_nd_ra_hop_limit, - ipv6_nd_ra_hop_limit_cmd, - "ipv6 nd ra-hop-limit (0-255)$hopcount", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Hop Limit\n" - "Advertisement Hop Limit in hops (default:64)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.AdvCurHopLimit = hopcount; - - return CMD_SUCCESS; -} - -DEFPY (no_ipv6_nd_ra_hop_limit, - no_ipv6_nd_ra_hop_limit_cmd, - "no ipv6 nd ra-hop-limit [(0-255)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Hop Limit\n" - "Advertisement Hop Limit in hops\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT; - - return CMD_SUCCESS; -} - -DEFPY (ipv6_nd_ra_retrans_interval, - ipv6_nd_ra_retrans_interval_cmd, - "ipv6 nd ra-retrans-interval (0-4294967295)$interval", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Retransmit Interval\n" - "Advertisement Retransmit Interval in msec\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on loopback interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.AdvRetransTimer = interval; - - return CMD_SUCCESS; -} - -DEFPY (no_ipv6_nd_ra_retrans_interval, - no_ipv6_nd_ra_retrans_interval_cmd, - "no ipv6 nd ra-retrans-interval [(0-4294967295)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Retransmit Interval\n" - "Advertisement Retransmit Interval in msec\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot remove IPv6 Router Advertisements on loopback interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.AdvRetransTimer = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_suppress_ra, - ipv6_nd_suppress_ra_cmd, - "ipv6 nd suppress-ra", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Suppress Router Advertisement\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) - ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS); - - UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_suppress_ra, - no_ipv6_nd_suppress_ra_cmd, - "no ipv6 nd suppress-ra", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Suppress Router Advertisement\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - if (if_is_loopback(ifp)) { - vty_out(vty, - "Cannot configure IPv6 Router Advertisements on this interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); - SET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_ra_interval_msec, - ipv6_nd_ra_interval_msec_cmd, - "ipv6 nd ra-interval msec (70-1800000)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Router Advertisement interval\n" - "Router Advertisement interval in milliseconds\n" - "Router Advertisement interval in milliseconds\n") -{ - int idx_number = 4; - VTY_DECLVAR_CONTEXT(interface, ifp); - unsigned interval; - struct zebra_if *zif = ifp->info; - struct zebra_vrf *zvrf; - struct adv_if *adv_if; - - zvrf = rtadv_interface_get_zvrf(ifp); - - interval = strtoul(argv[idx_number]->arg, NULL, 10); - if ((zif->rtadv.AdvDefaultLifetime != -1 - && interval > (unsigned)zif->rtadv.AdvDefaultLifetime * 1000)) { - vty_out(vty, - "This ra-interval would conflict with configured ra-lifetime!\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (zif->rtadv.MaxRtrAdvInterval % 1000) { - adv_if = adv_msec_if_del(zvrf, ifp->name); - if (adv_if != NULL) - adv_if_free(adv_if); - } - - if (interval % 1000) - (void)adv_msec_if_add(zvrf, ifp->name); - - SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); - zif->rtadv.MaxRtrAdvInterval = interval; - zif->rtadv.MinRtrAdvInterval = 0.33 * interval; - zif->rtadv.AdvIntervalTimer = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_ra_interval, - ipv6_nd_ra_interval_cmd, - "ipv6 nd ra-interval (1-1800)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Router Advertisement interval\n" - "Router Advertisement interval in seconds\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - unsigned interval; - struct zebra_if *zif = ifp->info; - struct zebra_vrf *zvrf; - struct adv_if *adv_if; - - zvrf = rtadv_interface_get_zvrf(ifp); - - interval = strtoul(argv[idx_number]->arg, NULL, 10); - if ((zif->rtadv.AdvDefaultLifetime != -1 - && interval > (unsigned)zif->rtadv.AdvDefaultLifetime)) { - vty_out(vty, - "This ra-interval would conflict with configured ra-lifetime!\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (zif->rtadv.MaxRtrAdvInterval % 1000) { - adv_if = adv_msec_if_del(zvrf, ifp->name); - if (adv_if != NULL) - adv_if_free(adv_if); - } - - /* convert to milliseconds */ - interval = interval * 1000; - - SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); - zif->rtadv.MaxRtrAdvInterval = interval; - zif->rtadv.MinRtrAdvInterval = 0.33 * interval; - zif->rtadv.AdvIntervalTimer = 0; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_ra_interval, - no_ipv6_nd_ra_interval_cmd, - "no ipv6 nd ra-interval [<(1-1800)|msec (1-1800000)>]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Router Advertisement interval\n" - "Router Advertisement interval in seconds\n" - "Specify millisecond router advertisement interval\n" - "Router Advertisement interval in milliseconds\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - struct zebra_vrf *zvrf = NULL; - struct adv_if *adv_if; - - zvrf = rtadv_interface_get_zvrf(ifp); - - if (zif->rtadv.MaxRtrAdvInterval % 1000) { - adv_if = adv_msec_if_del(zvrf, ifp->name); - if (adv_if != NULL) - adv_if_free(adv_if); - } - - UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); - - if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) - zif->rtadv.MaxRtrAdvInterval = 10000; - else - zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; - - zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; - zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_ra_lifetime, - ipv6_nd_ra_lifetime_cmd, - "ipv6 nd ra-lifetime (0-9000)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Router lifetime\n" - "Router lifetime in seconds (0 stands for a non-default gw)\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - int lifetime; - - lifetime = strtoul(argv[idx_number]->arg, NULL, 10); - - /* The value to be placed in the Router Lifetime field - * of Router Advertisements sent from the interface, - * in seconds. MUST be either zero or between - * MaxRtrAdvInterval and 9000 seconds. -- RFC4861, 6.2.1 */ - if ((lifetime != 0 && lifetime * 1000 < zif->rtadv.MaxRtrAdvInterval)) { - vty_out(vty, - "This ra-lifetime would conflict with configured ra-interval\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - zif->rtadv.AdvDefaultLifetime = lifetime; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_ra_lifetime, - no_ipv6_nd_ra_lifetime_cmd, - "no ipv6 nd ra-lifetime [(0-9000)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Router lifetime\n" - "Router lifetime in seconds (0 stands for a non-default gw)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvDefaultLifetime = -1; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_reachable_time, - ipv6_nd_reachable_time_cmd, - "ipv6 nd reachable-time (1-3600000)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Reachable time\n" - "Reachable time in milliseconds\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - zif->rtadv.AdvReachableTime = strtoul(argv[idx_number]->arg, NULL, 10); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_reachable_time, - no_ipv6_nd_reachable_time_cmd, - "no ipv6 nd reachable-time [(1-3600000)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Reachable time\n" - "Reachable time in milliseconds\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvReachableTime = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_homeagent_preference, - ipv6_nd_homeagent_preference_cmd, - "ipv6 nd home-agent-preference (0-65535)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent preference\n" - "preference value (default is 0, least preferred)\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - zif->rtadv.HomeAgentPreference = - strtoul(argv[idx_number]->arg, NULL, 10); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_homeagent_preference, - no_ipv6_nd_homeagent_preference_cmd, - "no ipv6 nd home-agent-preference [(0-65535)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent preference\n" - "preference value (default is 0, least preferred)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.HomeAgentPreference = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_homeagent_lifetime, - ipv6_nd_homeagent_lifetime_cmd, - "ipv6 nd home-agent-lifetime (0-65520)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent lifetime\n" - "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - zif->rtadv.HomeAgentLifetime = strtoul(argv[idx_number]->arg, NULL, 10); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_homeagent_lifetime, - no_ipv6_nd_homeagent_lifetime_cmd, - "no ipv6 nd home-agent-lifetime [(0-65520)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent lifetime\n" - "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.HomeAgentLifetime = -1; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_managed_config_flag, - ipv6_nd_managed_config_flag_cmd, - "ipv6 nd managed-config-flag", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Managed address configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvManagedFlag = 1; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_managed_config_flag, - no_ipv6_nd_managed_config_flag_cmd, - "no ipv6 nd managed-config-flag", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Managed address configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvManagedFlag = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_homeagent_config_flag, - ipv6_nd_homeagent_config_flag_cmd, - "ipv6 nd home-agent-config-flag", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvHomeAgentFlag = 1; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_homeagent_config_flag, - no_ipv6_nd_homeagent_config_flag_cmd, - "no ipv6 nd home-agent-config-flag", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Home Agent configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvHomeAgentFlag = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_adv_interval_config_option, - ipv6_nd_adv_interval_config_option_cmd, - "ipv6 nd adv-interval-option", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Interval Option\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvIntervalOption = 1; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_adv_interval_config_option, - no_ipv6_nd_adv_interval_config_option_cmd, - "no ipv6 nd adv-interval-option", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertisement Interval Option\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvIntervalOption = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_other_config_flag, - ipv6_nd_other_config_flag_cmd, - "ipv6 nd other-config-flag", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Other statefull configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvOtherConfigFlag = 1; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_other_config_flag, - no_ipv6_nd_other_config_flag_cmd, - "no ipv6 nd other-config-flag", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Other statefull configuration flag\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.AdvOtherConfigFlag = 0; - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_prefix, - ipv6_nd_prefix_cmd, - "ipv6 nd prefix X:X::X:X/M [<(0-4294967295)|infinite> <(0-4294967295)|infinite>] []", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Prefix information\n" - "IPv6 prefix\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n" - "Preferred lifetime in seconds\n" - "Infinite preferred lifetime\n" - "Set Router Address flag\n" - "Do not use prefix for onlink determination\n" - "Do not use prefix for autoconfiguration\n" - "Do not use prefix for autoconfiguration\n" - "Do not use prefix for onlink determination\n") -{ - /* prelude */ - char *prefix = argv[3]->arg; - int lifetimes = (argc > 4) && (argv[4]->type == RANGE_TKN - || strmatch(argv[4]->text, "infinite")); - int routeropts = lifetimes ? argc > 6 : argc > 4; - - int idx_routeropts = routeropts ? (lifetimes ? 6 : 4) : 0; - - char *lifetime = NULL, *preflifetime = NULL; - int routeraddr = 0, offlink = 0, noautoconf = 0; - if (lifetimes) { - lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg - : argv[4]->text; - preflifetime = argv[5]->type == RANGE_TKN ? argv[5]->arg - : argv[5]->text; - } - if (routeropts) { - routeraddr = - strmatch(argv[idx_routeropts]->text, "router-address"); - if (!routeraddr) { - offlink = (argc > idx_routeropts + 1 - || strmatch(argv[idx_routeropts]->text, - "off-link")); - noautoconf = (argc > idx_routeropts + 1 - || strmatch(argv[idx_routeropts]->text, - "no-autoconfig")); - } - } - - /* business */ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zebra_if = ifp->info; - int ret; - struct rtadv_prefix rp; - - ret = str2prefix_ipv6(prefix, &rp.prefix); - if (!ret) { - vty_out(vty, "Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - apply_mask_ipv6(&rp.prefix); /* RFC4861 4.6.2 */ - rp.AdvOnLinkFlag = !offlink; - rp.AdvAutonomousFlag = !noautoconf; - rp.AdvRouterAddressFlag = routeraddr; - rp.AdvValidLifetime = RTADV_VALID_LIFETIME; - rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME; - rp.AdvPrefixCreate = PREFIX_SRC_MANUAL; - - if (lifetimes) { - rp.AdvValidLifetime = strmatch(lifetime, "infinite") - ? UINT32_MAX - : strtoll(lifetime, NULL, 10); - rp.AdvPreferredLifetime = - strmatch(preflifetime, "infinite") - ? UINT32_MAX - : strtoll(preflifetime, NULL, 10); - if (rp.AdvPreferredLifetime > rp.AdvValidLifetime) { - vty_out(vty, "Invalid preferred lifetime\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - rtadv_prefix_set(zebra_if, &rp); - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_prefix, - no_ipv6_nd_prefix_cmd, - "no ipv6 nd prefix X:X::X:X/M [<(0-4294967295)|infinite> <(0-4294967295)|infinite>] []", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Prefix information\n" - "IPv6 prefix\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n" - "Preferred lifetime in seconds\n" - "Infinite preferred lifetime\n" - "Set Router Address flag\n" - "Do not use prefix for onlink determination\n" - "Do not use prefix for autoconfiguration\n" - "Do not use prefix for autoconfiguration\n" - "Do not use prefix for onlink determination\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zebra_if = ifp->info; - int ret; - struct rtadv_prefix rp; - char *prefix = argv[4]->arg; - - ret = str2prefix_ipv6(prefix, &rp.prefix); - if (!ret) { - vty_out(vty, "Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - apply_mask_ipv6(&rp.prefix); /* RFC4861 4.6.2 */ - rp.AdvPrefixCreate = PREFIX_SRC_MANUAL; - - ret = rtadv_prefix_reset(zebra_if, &rp); - if (!ret) { - vty_out(vty, "Non-existant IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_router_preference, - ipv6_nd_router_preference_cmd, - "ipv6 nd router-preference ", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Default router preference\n" - "High default router preference\n" - "Medium default router preference (default)\n" - "Low default router preference\n") -{ - int idx_high_medium_low = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - int i = 0; - - while (0 != rtadv_pref_strs[i]) { - if (strncmp(argv[idx_high_medium_low]->arg, rtadv_pref_strs[i], - 1) - == 0) { - zif->rtadv.DefaultPreference = i; - return CMD_SUCCESS; - } - i++; - } - - return CMD_ERR_NO_MATCH; -} - -DEFUN (no_ipv6_nd_router_preference, - no_ipv6_nd_router_preference_cmd, - "no ipv6 nd router-preference []", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Default router preference\n" - "High default router preference\n" - "Medium default router preference (default)\n" - "Low default router preference\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - - zif->rtadv.DefaultPreference = - RTADV_PREF_MEDIUM; /* Default per RFC4191. */ - - return CMD_SUCCESS; -} - -DEFUN (ipv6_nd_mtu, - ipv6_nd_mtu_cmd, - "ipv6 nd mtu (1-65535)", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertised MTU\n" - "MTU in bytes\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - zif->rtadv.AdvLinkMTU = strtoul(argv[idx_number]->arg, NULL, 10); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nd_mtu, - no_ipv6_nd_mtu_cmd, - "no ipv6 nd mtu [(1-65535)]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Advertised MTU\n" - "MTU in bytes\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - zif->rtadv.AdvLinkMTU = 0; - return CMD_SUCCESS; -} - static struct rtadv_rdnss *rtadv_rdnss_new(void) { return XCALLOC(MTYPE_RTADV_RDNSS, sizeof(struct rtadv_rdnss)); @@ -2293,55 +1558,22 @@ static void rtadv_rdnss_free(struct rtadv_rdnss *rdnss) XFREE(MTYPE_RTADV_RDNSS, rdnss); } -static struct rtadv_rdnss *rtadv_rdnss_lookup(struct list *list, - struct rtadv_rdnss *rdnss) +struct rtadv_rdnss *rtadv_rdnss_set(struct zebra_if *zif, + struct rtadv_rdnss *rdnss) { - struct listnode *node; struct rtadv_rdnss *p; - for (ALL_LIST_ELEMENTS_RO(list, node, p)) - if (IPV6_ADDR_SAME(&p->addr, &rdnss->addr)) - return p; - return NULL; -} - -static struct rtadv_rdnss *rtadv_rdnss_get(struct list *list, - struct rtadv_rdnss *rdnss) -{ - struct rtadv_rdnss *p; - - p = rtadv_rdnss_lookup(list, rdnss); - if (p) - return p; - p = rtadv_rdnss_new(); memcpy(p, rdnss, sizeof(struct rtadv_rdnss)); - listnode_add(list, p); + listnode_add(zif->rtadv.AdvRDNSSList, p); return p; } -static void rtadv_rdnss_set(struct zebra_if *zif, struct rtadv_rdnss *rdnss) +void rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *p) { - struct rtadv_rdnss *p; - - p = rtadv_rdnss_get(zif->rtadv.AdvRDNSSList, rdnss); - p->lifetime = rdnss->lifetime; - p->lifetime_set = rdnss->lifetime_set; -} - -static int rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *rdnss) -{ - struct rtadv_rdnss *p; - - p = rtadv_rdnss_lookup(zif->rtadv.AdvRDNSSList, rdnss); - if (p) { - listnode_delete(zif->rtadv.AdvRDNSSList, p); - rtadv_rdnss_free(p); - return 1; - } - - return 0; + listnode_delete(zif->rtadv.AdvRDNSSList, p); + rtadv_rdnss_free(p); } static struct rtadv_dnssl *rtadv_dnssl_new(void) @@ -2354,54 +1586,22 @@ static void rtadv_dnssl_free(struct rtadv_dnssl *dnssl) XFREE(MTYPE_RTADV_DNSSL, dnssl); } -static struct rtadv_dnssl *rtadv_dnssl_lookup(struct list *list, - struct rtadv_dnssl *dnssl) -{ - struct listnode *node; - struct rtadv_dnssl *p; - - for (ALL_LIST_ELEMENTS_RO(list, node, p)) - if (!strcasecmp(p->name, dnssl->name)) - return p; - return NULL; -} - -static struct rtadv_dnssl *rtadv_dnssl_get(struct list *list, - struct rtadv_dnssl *dnssl) +struct rtadv_dnssl *rtadv_dnssl_set(struct zebra_if *zif, + struct rtadv_dnssl *dnssl) { struct rtadv_dnssl *p; - p = rtadv_dnssl_lookup(list, dnssl); - if (p) - return p; - p = rtadv_dnssl_new(); memcpy(p, dnssl, sizeof(struct rtadv_dnssl)); - listnode_add(list, p); + listnode_add(zif->rtadv.AdvDNSSLList, p); return p; } -static void rtadv_dnssl_set(struct zebra_if *zif, struct rtadv_dnssl *dnssl) -{ - struct rtadv_dnssl *p; - - p = rtadv_dnssl_get(zif->rtadv.AdvDNSSLList, dnssl); - memcpy(p, dnssl, sizeof(struct rtadv_dnssl)); -} - -static int rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *dnssl) +void rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *p) { - struct rtadv_dnssl *p; - - p = rtadv_dnssl_lookup(zif->rtadv.AdvDNSSLList, dnssl); - if (p) { - listnode_delete(zif->rtadv.AdvDNSSLList, p); - rtadv_dnssl_free(p); - return 1; - } - - return 0; + listnode_delete(zif->rtadv.AdvDNSSLList, p); + rtadv_dnssl_free(p); } /* @@ -2412,7 +1612,7 @@ static int rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *dnssl) * Returns the number of octets written to out or -1 if in does not constitute * a valid domain name. */ -static int rtadv_dnssl_encode(uint8_t *out, const char *in) +int rtadv_dnssl_encode(uint8_t *out, const char *in) { const char *label_start, *label_end; size_t outp; @@ -2443,148 +1643,6 @@ static int rtadv_dnssl_encode(uint8_t *out, const char *in) return outp; } -DEFUN(ipv6_nd_rdnss, - ipv6_nd_rdnss_cmd, - "ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Recursive DNS server information\n" - "IPv6 address\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - struct rtadv_rdnss rdnss = {}; - - if (inet_pton(AF_INET6, argv[3]->arg, &rdnss.addr) != 1) { - vty_out(vty, "Malformed IPv6 address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (argc > 4) { - char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg - : argv[4]->text; - rdnss.lifetime = strmatch(lifetime, "infinite") - ? UINT32_MAX - : strtoll(lifetime, NULL, 10); - rdnss.lifetime_set = 1; - } - - rtadv_rdnss_set(zif, &rdnss); - - return CMD_SUCCESS; -} - -DEFUN(no_ipv6_nd_rdnss, - no_ipv6_nd_rdnss_cmd, - "no ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "Recursive DNS server information\n" - "IPv6 address\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - struct rtadv_rdnss rdnss = {}; - - if (inet_pton(AF_INET6, argv[4]->arg, &rdnss.addr) != 1) { - vty_out(vty, "Malformed IPv6 address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (rtadv_rdnss_reset(zif, &rdnss) != 1) { - vty_out(vty, "Non-existant RDNSS address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN(ipv6_nd_dnssl, - ipv6_nd_dnssl_cmd, - "ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]", - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "DNS search list information\n" - "Domain name suffix\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - struct rtadv_dnssl dnssl = {}; - size_t len; - int ret; - - len = strlcpy(dnssl.name, argv[3]->arg, sizeof(dnssl.name)); - if (len == 0 || len >= sizeof(dnssl.name)) { - vty_out(vty, "Malformed DNS search domain\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (dnssl.name[len - 1] == '.') { - /* - * Allow, but don't require, a trailing dot signifying the root - * zone. Canonicalize by cutting it off if present. - */ - dnssl.name[len - 1] = '\0'; - len--; - } - if (argc > 4) { - char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg - : argv[4]->text; - dnssl.lifetime = strmatch(lifetime, "infinite") - ? UINT32_MAX - : strtoll(lifetime, NULL, 10); - dnssl.lifetime_set = 1; - } - - ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name); - if (ret < 0) { - vty_out(vty, "Malformed DNS search domain\n"); - return CMD_WARNING_CONFIG_FAILED; - } - dnssl.encoded_len = ret; - rtadv_dnssl_set(zif, &dnssl); - - return CMD_SUCCESS; -} - -DEFUN(no_ipv6_nd_dnssl, - no_ipv6_nd_dnssl_cmd, - "no ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]", - NO_STR - "Interface IPv6 config commands\n" - "Neighbor discovery\n" - "DNS search list information\n" - "Domain name suffix\n" - "Valid lifetime in seconds\n" - "Infinite valid lifetime\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif = ifp->info; - struct rtadv_dnssl dnssl = {}; - size_t len; - - len = strlcpy(dnssl.name, argv[4]->arg, sizeof(dnssl.name)); - if (len == 0 || len >= sizeof(dnssl.name)) { - vty_out(vty, "Malformed DNS search domain\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (dnssl.name[len - 1] == '.') { - dnssl.name[len - 1] = '\0'; - len--; - } - if (rtadv_dnssl_reset(zif, &dnssl) != 1) { - vty_out(vty, "Non-existant DNS search domain\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - - /* Dump interface ND information to vty. */ static int nd_dump_vty(struct vty *vty, struct interface *ifp) { @@ -2655,136 +1713,6 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) return 0; } - -/* Write configuration about router advertisement. */ -static int rtadv_config_write(struct vty *vty, struct interface *ifp) -{ - struct zebra_if *zif; - struct listnode *node; - struct rtadv_prefix *rprefix; - struct rtadv_rdnss *rdnss; - struct rtadv_dnssl *dnssl; - int interval; - - zif = ifp->info; - - if (!if_is_loopback(ifp)) { - if (zif->rtadv.AdvSendAdvertisements - && CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED)) - vty_out(vty, " no ipv6 nd suppress-ra\n"); - } - - interval = zif->rtadv.MaxRtrAdvInterval; - if (CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) { - if (interval % 1000) - vty_out(vty, " ipv6 nd ra-interval msec %d\n", - interval); - else if (interval != RTADV_MAX_RTR_ADV_INTERVAL) - vty_out(vty, " ipv6 nd ra-interval %d\n", - interval / 1000); - } - - if (zif->rtadv.AdvIntervalOption) - vty_out(vty, " ipv6 nd adv-interval-option\n"); - - if (!zif->rtadv.UseFastRexmit) - vty_out(vty, " no ipv6 nd ra-fast-retrans\n"); - - if (zif->rtadv.AdvRetransTimer != 0) - vty_out(vty, " ipv6 nd ra-retrans-interval %u\n", - zif->rtadv.AdvRetransTimer); - - if (zif->rtadv.AdvCurHopLimit != RTADV_DEFAULT_HOPLIMIT) - vty_out(vty, " ipv6 nd ra-hop-limit %d\n", - zif->rtadv.AdvCurHopLimit); - - if (zif->rtadv.AdvDefaultLifetime != -1) - vty_out(vty, " ipv6 nd ra-lifetime %d\n", - zif->rtadv.AdvDefaultLifetime); - - if (zif->rtadv.HomeAgentPreference) - vty_out(vty, " ipv6 nd home-agent-preference %u\n", - zif->rtadv.HomeAgentPreference); - - if (zif->rtadv.HomeAgentLifetime != -1) - vty_out(vty, " ipv6 nd home-agent-lifetime %u\n", - zif->rtadv.HomeAgentLifetime); - - if (zif->rtadv.AdvHomeAgentFlag) - vty_out(vty, " ipv6 nd home-agent-config-flag\n"); - - if (zif->rtadv.AdvReachableTime) - vty_out(vty, " ipv6 nd reachable-time %d\n", - zif->rtadv.AdvReachableTime); - - if (zif->rtadv.AdvManagedFlag) - vty_out(vty, " ipv6 nd managed-config-flag\n"); - - if (zif->rtadv.AdvOtherConfigFlag) - vty_out(vty, " ipv6 nd other-config-flag\n"); - - if (zif->rtadv.DefaultPreference != RTADV_PREF_MEDIUM) - vty_out(vty, " ipv6 nd router-preference %s\n", - rtadv_pref_strs[zif->rtadv.DefaultPreference]); - - if (zif->rtadv.AdvLinkMTU) - vty_out(vty, " ipv6 nd mtu %d\n", zif->rtadv.AdvLinkMTU); - - frr_each (rtadv_prefixes, zif->rtadv.prefixes, rprefix) { - if ((rprefix->AdvPrefixCreate == PREFIX_SRC_MANUAL) - || (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH)) { - vty_out(vty, " ipv6 nd prefix %pFX", &rprefix->prefix); - if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) - || (rprefix->AdvPreferredLifetime - != RTADV_PREFERRED_LIFETIME)) { - if (rprefix->AdvValidLifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", - rprefix->AdvValidLifetime); - if (rprefix->AdvPreferredLifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", - rprefix->AdvPreferredLifetime); - } - if (!rprefix->AdvOnLinkFlag) - vty_out(vty, " off-link"); - if (!rprefix->AdvAutonomousFlag) - vty_out(vty, " no-autoconfig"); - if (rprefix->AdvRouterAddressFlag) - vty_out(vty, " router-address"); - vty_out(vty, "\n"); - } - } - - for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) { - char buf[INET6_ADDRSTRLEN]; - - vty_out(vty, " ipv6 nd rdnss %s", - inet_ntop(AF_INET6, &rdnss->addr, buf, sizeof(buf))); - if (rdnss->lifetime_set) { - if (rdnss->lifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", rdnss->lifetime); - } - vty_out(vty, "\n"); - } - for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) { - vty_out(vty, " ipv6 nd dnssl %s", dnssl->name); - if (dnssl->lifetime_set) { - if (dnssl->lifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", dnssl->lifetime); - } - vty_out(vty, "\n"); - } - return 0; -} - - static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) { struct rtadv *rtadv; @@ -2930,49 +1858,8 @@ void rtadv_cmd_init(void) interfaces_configured_for_ra_from_bgp = 0; hook_register(zebra_if_extra_info, nd_dump_vty); - hook_register(zebra_if_config_wr, rtadv_config_write); install_element(VIEW_NODE, &show_ipv6_nd_ra_if_cmd); - - install_element(INTERFACE_NODE, &ipv6_nd_ra_fast_retrans_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_ra_fast_retrans_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_ra_retrans_interval_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_ra_retrans_interval_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_ra_hop_limit_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_ra_hop_limit_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_msec_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_ra_interval_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_reachable_time_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_reachable_time_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_managed_config_flag_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_other_config_flag_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_config_flag_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_cmd); - install_element(INTERFACE_NODE, - &ipv6_nd_adv_interval_config_option_cmd); - install_element(INTERFACE_NODE, - &no_ipv6_nd_adv_interval_config_option_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_prefix_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_prefix_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_router_preference_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_mtu_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_mtu_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_rdnss_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_rdnss_cmd); - install_element(INTERFACE_NODE, &ipv6_nd_dnssl_cmd); - install_element(INTERFACE_NODE, &no_ipv6_nd_dnssl_cmd); } static int if_join_all_router(int sock, struct interface *ifp) @@ -3069,3 +1956,13 @@ uint32_t rtadv_get_interfaces_configured_from_bgp(void) { return interfaces_configured_for_ra_from_bgp; } + +void rtadv_init(void) +{ + if (CMSG_SPACE(sizeof(struct in6_pktinfo)) > RTADV_ADATA_SIZE) { + zlog_debug("%s: RTADV_ADATA_SIZE choosen will not work on this platform, please use a larger size", + __func__); + + exit(-1); + } +} diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 1ec376a..0983ea5 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -385,6 +385,30 @@ extern void rtadv_if_fini(struct zebra_if *zif); extern void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p); extern void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p); +/* returns created prefix */ +struct rtadv_prefix *rtadv_add_prefix_manual(struct zebra_if *zif, + struct rtadv_prefix *rp); +/* rprefix must be the one returned by rtadv_add_prefix_manual */ +void rtadv_delete_prefix_manual(struct zebra_if *zif, + struct rtadv_prefix *rprefix); + +/* returns created address */ +struct rtadv_rdnss *rtadv_rdnss_set(struct zebra_if *zif, + struct rtadv_rdnss *rdnss); +/* p must be the one returned by rtadv_rdnss_set */ +void rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *p); + +/* returns created domain */ +struct rtadv_dnssl *rtadv_dnssl_set(struct zebra_if *zif, + struct rtadv_dnssl *dnssl); +/* p must be the one returned by rtadv_dnssl_set */ +void rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *p); +int rtadv_dnssl_encode(uint8_t *out, const char *in); + +void ipv6_nd_suppress_ra_set(struct interface *ifp, + enum ipv6_nd_suppress_ra_status status); +void ipv6_nd_interval_set(struct interface *ifp, uint32_t interval); + #else /* !HAVE_RTADV */ struct rtadv { /* empty structs aren't valid ISO C */ @@ -435,6 +459,7 @@ extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); extern uint32_t rtadv_get_interfaces_configured_from_bgp(void); extern bool rtadv_compiled_in(void); +extern void rtadv_init(void); #ifdef __cplusplus } diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index ef1e21b..8e2d13f 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -6,6 +6,8 @@ #include +#include + #if !defined(GNU_LINUX) #include "memory.h" diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index f00aef5..0528279 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -9,6 +9,9 @@ #ifdef HAVE_NETLINK +#include +#include + #include "if.h" #include "prefix.h" #include "vrf.h" diff --git a/zebra/subdir.am b/zebra/subdir.am index b3bd9be..d9c8d90 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -28,7 +28,7 @@ man8 += $(MANBUILD)/frr-zebra.8 ## endif ZEBRA endif -zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS) +zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(LIBYANG_LIBS) $(UST_LIBS) if HAVE_PROTOBUF3 zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) zebra/zebra_mlag.$(OBJEXT): mlag/mlag.pb-c.h @@ -52,6 +52,7 @@ zebra_zebra_SOURCES = \ zebra/redistribute.c \ zebra/router-id.c \ zebra/rt_netlink.c \ + zebra/ge_netlink.c \ zebra/rt_socket.c \ zebra/rtadv.c \ zebra/rtread_netlink.c \ @@ -117,7 +118,6 @@ clippy_scan += \ zebra/debug.c \ zebra/interface.c \ zebra/rtadv.c \ - zebra/zebra_evpn_mh.c \ zebra/zebra_mlag_vty.c \ zebra/zebra_routemap.c \ zebra/zebra_vty.c \ @@ -125,6 +125,7 @@ clippy_scan += \ zebra/zebra_vrf.c \ zebra/dpdk/zebra_dplane_dpdk_vty.c \ zebra/label_manager.c \ + zebra/zebra_cli.c \ # end noinst_HEADERS += \ @@ -144,6 +145,7 @@ noinst_HEADERS += \ zebra/router-id.h \ zebra/rt.h \ zebra/rt_netlink.h \ + zebra/ge_netlink.h \ zebra/rtadv.h \ zebra/rule_netlink.h \ zebra/table_manager.h \ diff --git a/zebra/table_manager.c b/zebra/table_manager.c index 512508b..8417a22 100644 --- a/zebra/table_manager.c +++ b/zebra/table_manager.c @@ -24,21 +24,6 @@ #include "zebra/table_manager.h" #include "zebra/zebra_errors.h" -/* routing table identifiers - * - */ -#if !defined(GNU_LINUX) -/* BSD systems - */ -#else -/* Linux Systems - */ -#define RT_TABLE_ID_LOCAL 255 -#define RT_TABLE_ID_MAIN 254 -#define RT_TABLE_ID_DEFAULT 253 -#define RT_TABLE_ID_COMPAT 252 -#define RT_TABLE_ID_UNSPEC 0 -#endif /* !def(GNU_LINUX) */ #define RT_TABLE_ID_UNRESERVED_MIN 1 #define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff @@ -279,52 +264,11 @@ void table_manager_disable(struct zebra_vrf *zvrf) zvrf->tbl_mgr = NULL; } -int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf, - const char *start_table_str, const char *end_table_str) +void table_manager_range(bool add, struct zebra_vrf *zvrf, uint32_t start, + uint32_t end) { - uint32_t start; - uint32_t end; - - if (add) { - if (!start_table_str || !end_table_str) { - vty_out(vty, "%% Labels not specified\n"); - return CMD_WARNING_CONFIG_FAILED; - } - start = atoi(start_table_str); - end = atoi(end_table_str); - if (end < start) { - vty_out(vty, "%% End table is less than Start table\n"); - return CMD_WARNING_CONFIG_FAILED; - } - -#if !defined(GNU_LINUX) -/* BSD systems - */ -#else - /* Linux Systems - */ - if ((start >= RT_TABLE_ID_COMPAT && start <= RT_TABLE_ID_LOCAL) - || (end >= RT_TABLE_ID_COMPAT - && end <= RT_TABLE_ID_LOCAL)) { - vty_out(vty, "%% Values forbidden in range [%u;%u]\n", - RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL); - return CMD_WARNING_CONFIG_FAILED; - } - if (start < RT_TABLE_ID_COMPAT && end > RT_TABLE_ID_LOCAL) { - vty_out(vty, - "%% Range overlaps range [%u;%u] forbidden\n", - RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL); - return CMD_WARNING_CONFIG_FAILED; - } -#endif - if (zvrf->tbl_mgr - && ((zvrf->tbl_mgr->start && zvrf->tbl_mgr->start != start) - || (zvrf->tbl_mgr->end && zvrf->tbl_mgr->end != end))) { - vty_out(vty, - "%% New range will be taken into account at restart\n"); - } + if (add) table_range_add(zvrf, start, end); - } else + else table_range_add(zvrf, 0, 0); - return CMD_SUCCESS; } diff --git a/zebra/table_manager.h b/zebra/table_manager.h index f8e99a3..2169199 100644 --- a/zebra/table_manager.h +++ b/zebra/table_manager.h @@ -18,6 +18,22 @@ extern "C" { #endif +/* routing table identifiers + * + */ +#if !defined(GNU_LINUX) +/* BSD systems + */ +#else +/* Linux Systems + */ +#define RT_TABLE_ID_LOCAL 255 +#define RT_TABLE_ID_MAIN 254 +#define RT_TABLE_ID_DEFAULT 253 +#define RT_TABLE_ID_COMPAT 252 +#define RT_TABLE_ID_UNSPEC 0 +#endif /* !def(GNU_LINUX) */ + /* * Table chunk struct * Client daemon which the chunk belongs to can be identified by either @@ -56,8 +72,8 @@ int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start, uint32_t end, struct zebra_vrf *zvrf); int release_daemon_table_chunks(struct zserv *client); void table_manager_disable(struct zebra_vrf *zvrf); -int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf, - const char *min, const char *max); +void table_manager_range(bool add, struct zebra_vrf *zvrf, uint32_t start, + uint32_t end); #ifdef __cplusplus } diff --git a/zebra/tc_netlink.c b/zebra/tc_netlink.c index d633c07..19667e6 100644 --- a/zebra/tc_netlink.c +++ b/zebra/tc_netlink.c @@ -9,6 +9,7 @@ #ifdef HAVE_NETLINK +#include #include #include #include diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 6754238..76cabd1 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -24,6 +24,7 @@ #include "lib/vrf.h" #include "lib/libfrr.h" #include "lib/lib_errors.h" +#include "lib/frrdistance.h" #include "zebra/zebra_router.h" #include "zebra/rib.h" @@ -63,7 +64,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) /* Interface information. */ struct zebra_if *zif = ifp->info; - stream_put(s, ifp->name, INTERFACE_NAMSIZ); + stream_put(s, ifp->name, IFNAMSIZ); stream_putl(s, ifp->ifindex); stream_putc(s, ifp->status); stream_putq(s, ifp->flags); @@ -414,7 +415,7 @@ int zsend_interface_addresses(struct zserv *client, struct interface *ifp) struct nbr_connected *nc; /* Send interface addresses. */ - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + frr_each (if_connected, ifp->connected, c) { if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) continue; @@ -510,7 +511,7 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn, - const struct route_entry *re) + const struct route_entry *re, bool is_table_direct) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -526,7 +527,11 @@ int zsend_redistribute_route(int cmd, struct zserv *client, api.vrf_id = re->vrf_id; api.type = re->type; api.safi = SAFI_UNICAST; - api.instance = re->instance; + if (is_table_direct) { + api.instance = re->table; + api.type = ZEBRA_ROUTE_TABLE_DIRECT; + } else + api.instance = re->instance; api.flags = re->flags; afi = family2afi(p->family); @@ -593,7 +598,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client, /* Attributes. */ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); - api.distance = re->distance; + if (is_table_direct) + api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT; + else + api.distance = re->distance; SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); api.metric = re->metric; if (re->tag) { @@ -834,7 +842,7 @@ void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, stream_putl(s, dplane_ctx_rule_get_seq(ctx)); stream_putl(s, dplane_ctx_rule_get_priority(ctx)); stream_putl(s, dplane_ctx_rule_get_unique(ctx)); - stream_put(s, dplane_ctx_rule_get_ifname(ctx), INTERFACE_NAMSIZ); + stream_put(s, dplane_ctx_rule_get_ifname(ctx), IFNAMSIZ); stream_putw_at(s, 0, stream_get_endp(s)); @@ -960,9 +968,9 @@ void zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, zserv_send_message(client, s); } -void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, - struct ipaddr *ipaddr, int ndm_state, - union sockunion *link_layer_ipv4) +void zsend_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4, int ip_len) { struct stream *s; struct listnode *node, *nnode; @@ -979,13 +987,13 @@ void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, family2addrsize(sockunion_family(&ip))); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { - if (!vrf_bitmap_check(&client->nhrp_neighinfo[afi], + if (!vrf_bitmap_check(&client->neighinfo[afi], ifp->vrf->vrf_id)) continue; s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_neigh_ip_encode(s, cmd, &ip, link_layer_ipv4, ifp, - ndm_state); + ndm_state, ip_len); stream_putw_at(s, 0, stream_get_endp(s)); zserv_send_message(client, s); } @@ -1028,7 +1036,7 @@ int zsend_pw_update(struct zserv *client, struct zebra_pw *pw) struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); - stream_write(s, pw->ifname, INTERFACE_NAMSIZ); + stream_write(s, pw->ifname, IFNAMSIZ); stream_putl(s, pw->ifindex); stream_putl(s, pw->status); @@ -1679,10 +1687,14 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, struct nexthop_group **png, struct nhg_backup_info **pbnhg) { + struct zapi_nexthop *znh; struct nexthop_group *ng = NULL; struct nhg_backup_info *bnhg = NULL; uint16_t i; struct nexthop *last_nh = NULL; + bool same_weight = true; + uint64_t max_weight = 0; + uint64_t tmp; assert(!(png && pbnhg)); @@ -1697,6 +1709,41 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, bnhg = zebra_nhg_backup_alloc(); } + for (i = 0; i < nexthop_num; i++) { + znh = &nhops[i]; + + if (max_weight < znh->weight) { + if (i != 0 || znh->weight != 1) + same_weight = false; + + max_weight = znh->weight; + } + } + + /* + * Let's convert the weights to a scaled value + * between 1 and zrouter.nexthop_weight_scale_value + * This is a simple application of a ratio: + * scaled_weight/zrouter.nexthop_weight_scale_value = + * weight/max_weight + * This translates to: + * scaled_weight = weight * zrouter.nexthop_weight_scale_value + * ------------------------------------------- + * max_weight + * + * This same formula is applied to both the nexthops + * and the backup nexthops + */ + if (!same_weight) { + for (i = 0; i < nexthop_num; i++) { + znh = &nhops[i]; + + tmp = (uint64_t)znh->weight * + zrouter.nexthop_weight_scale_value; + znh->weight = MAX(1, ((uint32_t)(tmp / max_weight))); + } + } + /* * TBD should _all_ of the nexthop add operations use * api_nh->vrf_id instead of re->vrf_id ? I only changed @@ -1912,20 +1959,21 @@ static void zread_nhg_del(ZAPI_HANDLER_ARGS) return; } - /* - * Delete the received nhg id - */ - nhe = zebra_nhg_proto_del(api_nhg.id, api_nhg.proto); + /* Create a temporary nhe */ + nhe = zebra_nhg_alloc(); + nhe->id = api_nhg.id; + nhe->type = api_nhg.proto; + nhe->zapi_instance = client->instance; + nhe->zapi_session = client->session_id; - if (nhe) { - zebra_nhg_decrement_ref(nhe); - zsend_nhg_notify(api_nhg.proto, client->instance, - client->session_id, api_nhg.id, - ZAPI_NHG_REMOVED); - } else - zsend_nhg_notify(api_nhg.proto, client->instance, - client->session_id, api_nhg.id, - ZAPI_NHG_REMOVE_FAIL); + /* Sanity check - Empty nexthop and group */ + nhe->nhg.nexthop = NULL; + + /* Enqueue to workqueue for processing */ + rib_queue_nhe_del(nhe); + + /* Stats */ + client->nhg_del_cnt++; } static void zread_nhg_add(ZAPI_HANDLER_ARGS) @@ -1934,7 +1982,7 @@ static void zread_nhg_add(ZAPI_HANDLER_ARGS) struct zapi_nhg api_nhg = {}; struct nexthop_group *nhg = NULL; struct nhg_backup_info *bnhg = NULL; - struct nhg_hash_entry *nhe; + struct nhg_hash_entry *nhe, *nhe_tmp; s = msg; if (zapi_nhg_decode(s, hdr->command, &api_nhg) < 0) { @@ -1992,6 +2040,12 @@ static void zread_nhg_add(ZAPI_HANDLER_ARGS) nexthop_group_delete(&nhg); zebra_nhg_backup_free(&bnhg); + /* Stats */ + nhe_tmp = zebra_nhg_lookup_id(api_nhg.id); + if (nhe_tmp) + client->nhg_upd8_cnt++; + else + client->nhg_add_cnt++; } static void zread_route_add(ZAPI_HANDLER_ARGS) @@ -2329,23 +2383,19 @@ static void zread_hello(ZAPI_HANDLER_ARGS) /* type of protocol (lib/zebra.h) */ uint8_t proto; unsigned short instance; - uint8_t notify; uint8_t synchronous; uint32_t session_id; STREAM_GETC(msg, proto); STREAM_GETW(msg, instance); STREAM_GETL(msg, session_id); - STREAM_GETC(msg, notify); STREAM_GETC(msg, synchronous); - if (notify) - client->notify_owner = true; if (synchronous) client->synchronous = true; /* accept only dynamic routing protocols */ - if ((proto < ZEBRA_ROUTE_MAX) && (proto > ZEBRA_ROUTE_CONNECT)) { + if ((proto < ZEBRA_ROUTE_MAX) && (proto > ZEBRA_ROUTE_LOCAL)) { zlog_notice( "client %d says hello and bids fair to announce only %s routes vrf=%u", client->sock, zebra_route_string(proto), @@ -2381,7 +2431,7 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) zvrf_id(zvrf)); vrf_bitmap_unset(&client->redist_default[afi], zvrf_id(zvrf)); vrf_bitmap_unset(&client->ridinfo[afi], zvrf_id(zvrf)); - vrf_bitmap_unset(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->neighinfo[afi], zvrf_id(zvrf)); } } @@ -2961,7 +3011,7 @@ static void zread_srv6_manager_request(ZAPI_HANDLER_ARGS) static void zread_pseudowire(ZAPI_HANDLER_ARGS) { struct stream *s; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int type; int af; @@ -2977,8 +3027,8 @@ static void zread_pseudowire(ZAPI_HANDLER_ARGS) s = msg; /* Get data. */ - STREAM_GET(ifname, s, INTERFACE_NAMSIZ); - ifname[INTERFACE_NAMSIZ - 1] = '\0'; + STREAM_GET(ifname, s, IFNAMSIZ); + ifname[IFNAMSIZ - 1] = '\0'; STREAM_GETL(s, ifindex); STREAM_GETL(s, type); STREAM_GETL(s, af); @@ -3074,6 +3124,28 @@ stream_failure: } +static void zread_interface_set_arp(ZAPI_HANDLER_ARGS) +{ + struct stream *s = msg; + struct interface *ifp; + bool arp_enable; + vrf_id_t vrf_id = zvrf->vrf->vrf_id; + int ifindex; + + STREAM_GETL(s, ifindex); + STREAM_GETC(s, arp_enable); + ifp = if_lookup_by_index(ifindex, vrf_id); + + if (!ifp) + return; + + if_arp(ifp, arp_enable); + +stream_failure: + return; +} + + static void zread_vrf_label(ZAPI_HANDLER_ARGS) { struct interface *ifp; @@ -3513,7 +3585,7 @@ static inline void zebra_neigh_register(ZAPI_HANDLER_ARGS) afi); goto stream_failure; } - vrf_bitmap_set(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_set(&client->neighinfo[afi], zvrf_id(zvrf)); stream_failure: return; } @@ -3529,7 +3601,7 @@ static inline void zebra_neigh_unregister(ZAPI_HANDLER_ARGS) afi); goto stream_failure; } - vrf_bitmap_unset(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->neighinfo[afi], zvrf_id(zvrf)); stream_failure: return; } @@ -3855,6 +3927,7 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_REMOTE_MACIP_DEL] = zebra_vxlan_remote_macip_del, [ZEBRA_DUPLICATE_ADDR_DETECTION] = zebra_vxlan_dup_addr_detection, [ZEBRA_INTERFACE_SET_MASTER] = zread_interface_set_master, + [ZEBRA_INTERFACE_SET_ARP] = zread_interface_set_arp, [ZEBRA_PW_ADD] = zread_pseudowire, [ZEBRA_PW_DELETE] = zread_pseudowire, [ZEBRA_PW_SET] = zread_pseudowire, @@ -3886,8 +3959,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_EVPN_REMOTE_NH_DEL] = zebra_evpn_proc_remote_nh, [ZEBRA_NEIGH_IP_ADD] = zebra_neigh_ip_add, [ZEBRA_NEIGH_IP_DEL] = zebra_neigh_ip_del, - [ZEBRA_NHRP_NEIGH_REGISTER] = zebra_neigh_register, - [ZEBRA_NHRP_NEIGH_UNREGISTER] = zebra_neigh_unregister, + [ZEBRA_NEIGH_REGISTER] = zebra_neigh_register, + [ZEBRA_NEIGH_UNREGISTER] = zebra_neigh_unregister, [ZEBRA_CONFIGURE_ARP] = zebra_configure_arp, [ZEBRA_GRE_GET] = zebra_gre_get, [ZEBRA_GRE_SOURCE_SET] = zebra_gre_source_set, diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index a01cbf6..43f734d 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -53,7 +53,8 @@ extern int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp); extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn, - const struct route_entry *re); + const struct route_entry *re, + bool is_table_direct); extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, struct prefix *p, vrf_id_t vrf_id); @@ -90,9 +91,9 @@ extern int zsend_label_manager_connect_response(struct zserv *client, extern int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, char *name, int status); -extern void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, - struct ipaddr *ipaddr, int ndm_state, - union sockunion *link_layer_ipv4); +extern void zsend_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4, int ip_len); extern int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client); diff --git a/zebra/zebra_affinitymap.c b/zebra/zebra_affinitymap.c index ae0f9a8..79bc78a 100644 --- a/zebra/zebra_affinitymap.c +++ b/zebra/zebra_affinitymap.c @@ -26,102 +26,26 @@ #include "zebra/redistribute.h" #include "zebra/zebra_affinitymap.h" -static bool zebra_affinity_map_check_use(const char *affmap_name) -{ - char xpath[XPATH_MAXLEN]; - struct interface *ifp; - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - FOR_ALL_INTERFACES (vrf, ifp) { - snprintf(xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']", - ifp->name); - if (!yang_dnode_exists(running_config->dnode, xpath)) - continue; - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities[affinity='%s']", - ifp->name, affmap_name); - if (yang_dnode_exists(running_config->dnode, xpath)) - return true; - } - } - return false; -} - -static bool zebra_affinity_map_check_update(const char *affmap_name, - uint16_t new_pos) -{ - char xpath[XPATH_MAXLEN]; - struct interface *ifp; - struct vrf *vrf; - - /* check whether the affinity-map new bit position is upper than 31 - * but is used on an interface on which affinity-mode is standard. - * Return false if the change is not possible. - */ - if (new_pos < 32) - return true; - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - FOR_ALL_INTERFACES (vrf, ifp) { - snprintf(xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']", - ifp->name); - if (!yang_dnode_exists(running_config->dnode, xpath)) - continue; - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities[affinity='%s']", - ifp->name, affmap_name); - if (!yang_dnode_exists(running_config->dnode, xpath)) - continue; - if (yang_dnode_get_enum( - running_config->dnode, - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinity-mode", - ifp->name) == AFFINITY_MODE_STANDARD) - return false; - } - } - return true; -} - static void zebra_affinity_map_update(const char *affmap_name, uint16_t old_pos, uint16_t new_pos) { struct if_link_params *iflp; - enum affinity_mode aff_mode; - char xpath[XPATH_MAXLEN]; struct interface *ifp; struct vrf *vrf; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { FOR_ALL_INTERFACES (vrf, ifp) { - snprintf(xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']", - ifp->name); - if (!yang_dnode_exists(running_config->dnode, xpath)) - continue; - snprintf( - xpath, sizeof(xpath), - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities[affinity='%s']", - ifp->name, affmap_name); - if (!yang_dnode_exists(running_config->dnode, xpath)) - continue; - aff_mode = yang_dnode_get_enum( - running_config->dnode, - "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinity-mode", - ifp->name); iflp = if_link_params_get(ifp); - if (aff_mode == AFFINITY_MODE_EXTENDED || - aff_mode == AFFINITY_MODE_BOTH) { + if (!iflp) + continue; + if (IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP) && + admin_group_get(&iflp->ext_admin_grp, old_pos)) { admin_group_unset(&iflp->ext_admin_grp, old_pos); admin_group_set(&iflp->ext_admin_grp, new_pos); } - if (aff_mode == AFFINITY_MODE_STANDARD || - aff_mode == AFFINITY_MODE_BOTH) { + if (IS_PARAM_SET(iflp, LP_ADM_GRP) && + (iflp->admin_grp & (1 << old_pos))) { iflp->admin_grp &= ~(1 << old_pos); if (new_pos < 32) iflp->admin_grp |= 1 << new_pos; @@ -138,7 +62,5 @@ void zebra_affinity_map_init(void) { affinity_map_init(); - affinity_map_set_check_use_hook(zebra_affinity_map_check_use); - affinity_map_set_check_update_hook(zebra_affinity_map_check_update); affinity_map_set_update_hook(zebra_affinity_map_update); } diff --git a/zebra/zebra_cli.c b/zebra/zebra_cli.c new file mode 100644 index 0000000..00e0a49 --- /dev/null +++ b/zebra/zebra_cli.c @@ -0,0 +1,2984 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "command.h" +#include "defaults.h" +#include "northbound_cli.h" +#include "vrf.h" + +#include "zebra_cli.h" +#include "zebra/zebra_cli_clippy.c" + +#define EVPN_MH_VTY_STR "Multihoming\n" + +FRR_CFG_DEFAULT_BOOL(ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT, + { .val_bool = true, .match_profile = "traditional", }, + { .val_bool = false }, +); + +#if HAVE_BFDD == 0 +DEFPY_YANG (zebra_ptm_enable, + zebra_ptm_enable_cmd, + "[no] ptm-enable", + NO_STR + "Enable neighbor check with specified topology\n") +{ + nb_cli_enqueue_change(vty, "/frr-zebra:zebra/ptm-enable", NB_OP_MODIFY, + no ? "false" : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +static void zebra_ptm_enable_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + bool enable = yang_dnode_get_bool(dnode, NULL); + + if (enable) + vty_out(vty, "ptm-enable\n"); + else if (show_defaults) + vty_out(vty, "no ptm-enable\n"); +} +#endif + +DEFPY_YANG (zebra_route_map_timer, + zebra_route_map_timer_cmd, + "[no] zebra route-map delay-timer ![(0-600)$delay]", + NO_STR + ZEBRA_STR + "Set route-map parameters\n" + "Time to wait before route-map updates are processed\n" + "0 means route-map changes are run immediately instead of delaying\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "/frr-zebra:zebra/route-map-delay", + NB_OP_MODIFY, delay_str); + else + nb_cli_enqueue_change(vty, "/frr-zebra:zebra/route-map-delay", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void zebra_route_map_delay_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t delay = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, "zebra route-map delay-timer %u\n", delay); +} + +DEFPY_YANG (multicast_new, + multicast_new_cmd, + "[no] multicast ", + NO_STR + "Control multicast flag on interface\n" + "Set multicast flag on interface\n" + "Unset multicast flag on interface\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/multicast", + NB_OP_CREATE, on ? "true" : "false"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/multicast", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_multicast_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + bool multicast = yang_dnode_get_bool(dnode, NULL); + + if (multicast) + vty_out(vty, " multicast enable\n"); + else + vty_out(vty, " multicast disable\n"); +} + +/* Deprecated multicast commands */ + +DEFPY_YANG_HIDDEN (multicast, + multicast_cmd, + "[no] multicast", + NO_STR + "Set multicast flag to interface\n") +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/multicast", + NB_OP_CREATE, no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (mpls, + mpls_cmd, + "[no] mpls ", + NO_STR + MPLS_STR + "Set mpls to be on for the interface\n" + "Set mpls to be off for the interface\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", + NB_OP_CREATE, on ? "true" : "false"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_mpls_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + bool mpls = yang_dnode_get_bool(dnode, NULL); + + if (mpls) + vty_out(vty, " mpls enable\n"); + else + vty_out(vty, " mpls disable\n"); +} + +DEFPY_YANG (linkdetect, + linkdetect_cmd, + "[no] link-detect", + NO_STR + "Enable link detection on interface\n") +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/link-detect", + NB_OP_CREATE, no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_detect_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool link_detect = yang_dnode_get_bool(dnode, NULL); + + if (!link_detect) + vty_out(vty, " no link-detect\n"); + else if (show_defaults) + vty_out(vty, " link-detect\n"); +} + +DEFPY_YANG (shutdown_if, + shutdown_if_cmd, + "[no] shutdown", + NO_STR + "Shutdown the selected interface\n") +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/enabled", NB_OP_CREATE, + no ? "true" : "false"); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_enabled_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + bool enabled = yang_dnode_get_bool(dnode, NULL); + + if (!enabled) + vty_out(vty, " shutdown\n"); + else if (show_defaults) + vty_out(vty, " no shutdown\n"); +} + +DEFPY_YANG (bandwidth_if, + bandwidth_if_cmd, + "[no] bandwidth ![(1-1000000)]$bw", + NO_STR + "Set bandwidth informational parameter\n" + "Bandwidth in megabits\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/bandwidth", + NB_OP_CREATE, bw_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/bandwidth", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_bandwidth_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t bandwidth = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " bandwidth %u\n", bandwidth); +} + +DEFUN_YANG_NOSH (link_params, + link_params_cmd, + "link-params", + LINK_PARAMS_STR) +{ + int ret; + + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/link-params", + NB_OP_CREATE, NULL); + + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) { + char *xpath; + + xpath = asprintfrr(MTYPE_TMP, "%s/frr-zebra:zebra/link-params", + VTY_CURR_XPATH); + VTY_PUSH_XPATH(LINK_PARAMS_NODE, xpath); + XFREE(MTYPE_TMP, xpath); + } + + return ret; +} + +DEFUN_NOSH (exit_link_params, + exit_link_params_cmd, + "exit-link-params", + "Exit from Link Params configuration mode\n") +{ + cmd_exit(vty); + return CMD_SUCCESS; +} + +static void lib_interface_zebra_link_params_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " link-params\n"); +} + +static void +lib_interface_zebra_link_params_cli_write_end(struct vty *vty, + const struct lyd_node *dnode) +{ + vty_out(vty, " exit-link-params\n"); +} + +DEFUN_YANG (no_link_params, + no_link_params_cmd, + "no link-params", + NO_STR + LINK_PARAMS_STR) +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/link-params", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* [no] enable is deprecated, link-params is enabled when entering the node. */ + +DEFUN_YANG_HIDDEN (link_params_enable, + link_params_enable_cmd, + "enable", + "Activate link parameters on this interface\n") +{ + vty_out(vty, "This command is deprecated. Link parameters are activated when \"link-params\" node is entered.\n"); + + return CMD_SUCCESS; +} + +DEFUN_YANG_NOSH (no_link_params_enable, + no_link_params_enable_cmd, + "no enable", + NO_STR + "Disable link parameters on this interface\n") +{ + int ret; + + vty_out(vty, "This command is deprecated. To disable link parameters use \"no link-params\" in the interface node.\n"); + + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + cmd_exit(vty); + + return ret; +} + +DEFPY_YANG (link_params_metric, + link_params_metric_cmd, + "[no] metric ![(0-4294967295)]$metric", + NO_STR + "Link metric for MPLS-TE purpose\n" + "Metric value in decimal\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./metric", NB_OP_MODIFY, metric_str); + else + nb_cli_enqueue_change(vty, "./metric", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_metric_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t metric = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " metric %u\n", metric); +} + +DEFPY_YANG (link_params_maxbw, + link_params_maxbw_cmd, + "max-bw BANDWIDTH", + "Maximum bandwidth that can be used\n" + "Bytes/second (IEEE floating point format)\n") +{ + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, "./max-bandwidth", NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_max_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + float max_bandwidth = yang_dnode_get_bandwidth_ieee_float32(dnode, NULL); + + vty_out(vty, " max-bw %g\n", max_bandwidth); +} + +DEFPY_YANG (link_params_max_rsv_bw, + link_params_max_rsv_bw_cmd, + "max-rsv-bw BANDWIDTH", + "Maximum bandwidth that may be reserved\n" + "Bytes/second (IEEE floating point format)\n") +{ + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, "./max-reservable-bandwidth", NB_OP_MODIFY, + value); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_max_reservable_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + float max_reservable_bandwidth = + yang_dnode_get_bandwidth_ieee_float32(dnode, NULL); + + vty_out(vty, " max-rsv-bw %g\n", max_reservable_bandwidth); +} + +DEFPY_YANG (link_params_unrsv_bw, + link_params_unrsv_bw_cmd, + "unrsv-bw (0-7)$priority BANDWIDTH", + "Unreserved bandwidth at each priority level\n" + "Priority\n" + "Bytes/second (IEEE floating point format)\n") +{ + char xpath[XPATH_MAXLEN]; + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(xpath, sizeof(xpath), + "./unreserved-bandwidths/unreserved-bandwidth[priority='%s']/unreserved-bandwidth", + priority_str); + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint8_t priority = yang_dnode_get_uint8(dnode, "priority"); + float unreserved_bandwidth = + yang_dnode_get_bandwidth_ieee_float32(dnode, + "unreserved-bandwidth"); + + vty_out(vty, " unrsv-bw %u %g\n", priority, unreserved_bandwidth); +} + +DEFPY_YANG (link_params_admin_grp, + link_params_admin_grp_cmd, + "[no] admin-grp ![BITPATTERN]", + NO_STR + "Administrative group membership\n" + "32-bit Hexadecimal value (e.g. 0xa1)\n") +{ + uint32_t value; + char value_str[YANG_VALUE_MAXLEN]; + + if (!no) { + assert(bitpattern); + + if (bitpattern[0] != '0' || bitpattern[1] != 'x' || + strlen(bitpattern) > 10) { + vty_out(vty, "Invalid bitpattern value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (sscanf(bitpattern, "%x", &value) != 1) { + vty_out(vty, "Invalid bitpattern value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value_str, sizeof(value_str), "%u", value); + + nb_cli_enqueue_change(vty, "./legacy-admin-group", NB_OP_MODIFY, + value_str); + } else { + nb_cli_enqueue_change(vty, "./legacy-admin-group", + NB_OP_DESTROY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_legacy_admin_group_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " admin-grp %#x\n", yang_dnode_get_uint32(dnode, NULL)); +} + +/* RFC5392 & RFC5316: INTER-AS */ +DEFPY_YANG (link_params_inter_as, + link_params_inter_as_cmd, + "[no] neighbor ![A.B.C.D$ip as (1-4294967295)$as]", + NO_STR + "Configure remote ASBR information (Neighbor IP address and AS number)\n" + "Remote IP address in dot decimal A.B.C.D\n" + "Remote AS number\n" + "AS number in the range <1-4294967295>\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./neighbor/remote-as", NB_OP_MODIFY, + as_str); + nb_cli_enqueue_change(vty, "./neighbor/ipv4-remote-id", + NB_OP_MODIFY, ip_str); + } else { + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_DESTROY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_neighbor_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t remote_as = yang_dnode_get_uint32(dnode, "remote-as"); + const char *ipv4_remote_id = yang_dnode_get_string(dnode, + "ipv4-remote-id"); + + vty_out(vty, " neighbor %s as %u\n", ipv4_remote_id, remote_as); +} + +/* RFC7471 & RFC8570 */ +DEFPY_YANG (link_params_delay, + link_params_delay_cmd, + "[no] delay ![(0-16777215)$delay [min (0-16777215)$min max (0-16777215)$max]]", + NO_STR + "Unidirectional Average Link Delay\n" + "Average delay in micro-second as decimal (0...16777215)\n" + "Minimum delay\n" + "Minimum delay in micro-second as decimal (0...16777215)\n" + "Maximum delay\n" + "Maximum delay in micro-second as decimal (0...16777215)\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, "./delay", NB_OP_MODIFY, delay_str); + if (min_str && max_str) { + nb_cli_enqueue_change(vty, "./min-max-delay", + NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./min-max-delay/delay-min", + NB_OP_MODIFY, min_str); + nb_cli_enqueue_change(vty, "./min-max-delay/delay-max", + NB_OP_MODIFY, max_str); + } else { + nb_cli_enqueue_change(vty, "./min-max-delay", + NB_OP_DESTROY, NULL); + } + } else { + nb_cli_enqueue_change(vty, "./delay", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./min-max-delay", NB_OP_DESTROY, + NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_delay_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t delay = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " delay %u", delay); + + if (yang_dnode_exists(dnode, "../min-max-delay")) { + uint32_t delay_min = + yang_dnode_get_uint32(dnode, + "../min-max-delay/delay-min"); + uint32_t delay_max = + yang_dnode_get_uint32(dnode, + "../min-max-delay/delay-max"); + + vty_out(vty, " min %u max %u", delay_min, delay_max); + } + + vty_out(vty, "\n"); +} + +DEFPY_YANG (link_params_delay_var, + link_params_delay_var_cmd, + "[no] delay-variation ![(0-16777215)$delay_var]", + NO_STR + "Unidirectional Link Delay Variation\n" + "delay variation in micro-second as decimal (0...16777215)\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./delay-variation", NB_OP_MODIFY, + delay_var_str); + else + nb_cli_enqueue_change(vty, "./delay-variation", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_delay_variation_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t delay_variation = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " delay-variation %u\n", delay_variation); +} + +DEFPY_YANG( + link_params_pkt_loss, link_params_pkt_loss_cmd, + "[no] packet-loss ![PERCENTAGE]", + NO_STR + "Unidirectional Link Packet Loss\n" + "percentage of total traffic by 0.000003% step and less than 50.331642%\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./packet-loss", NB_OP_MODIFY, + percentage); + else + nb_cli_enqueue_change(vty, "./packet-loss", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_packet_loss_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + double packet_loss = yang_dnode_get_dec64(dnode, NULL); + + vty_out(vty, " packet-loss %lf\n", packet_loss); +} + +DEFPY_YANG (link_params_res_bw, + link_params_res_bw_cmd, + "[no] res-bw ![BANDWIDTH]", + NO_STR + "Unidirectional Residual Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (!no) { + assert(bandwidth); + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, "./residual-bandwidth", NB_OP_MODIFY, + value); + } else { + nb_cli_enqueue_change(vty, "./residual-bandwidth", + NB_OP_DESTROY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_residual_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + float residual_bandwidth = yang_dnode_get_bandwidth_ieee_float32(dnode, + NULL); + + vty_out(vty, " res-bw %g\n", residual_bandwidth); +} + +DEFPY_YANG (link_params_ava_bw, + link_params_ava_bw_cmd, + "[no] ava-bw ![BANDWIDTH]", + NO_STR + "Unidirectional Available Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (!no) { + assert(bandwidth); + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, "./available-bandwidth", + NB_OP_MODIFY, value); + } else { + nb_cli_enqueue_change(vty, "./available-bandwidth", + NB_OP_DESTROY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_available_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + float available_bandwidth = yang_dnode_get_bandwidth_ieee_float32(dnode, + NULL); + + vty_out(vty, " ava-bw %g\n", available_bandwidth); +} + +DEFPY_YANG (link_params_use_bw, + link_params_use_bw_cmd, + "[no] use-bw ![BANDWIDTH]", + NO_STR + "Unidirectional Utilised Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + char value[YANG_VALUE_MAXLEN]; + float bw; + + if (!no) { + assert(bandwidth); + + if (sscanf(bandwidth, "%g", &bw) != 1) { + vty_out(vty, "Invalid bandwidth value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%a", bw); + + nb_cli_enqueue_change(vty, "./utilized-bandwidth", NB_OP_MODIFY, + value); + } else { + nb_cli_enqueue_change(vty, "./utilized-bandwidth", + NB_OP_DESTROY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_utilized_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + float utilized_bandwidth = yang_dnode_get_bandwidth_ieee_float32(dnode, + NULL); + + vty_out(vty, " use-bw %g\n", utilized_bandwidth); +} + +DEFPY_YANG (link_params_affinity, + link_params_affinity_cmd, + "[no] affinity NAME...", + NO_STR + "Interface affinities\n" + "Affinity names\n") +{ + char xpath[XPATH_MAXLEN]; + int i; + + for (i = no ? 2 : 1; i < argc; i++) { + snprintf(xpath, XPATH_MAXLEN, "./affinities/affinity[.='%s']", + argv[i]->arg); + nb_cli_enqueue_change(vty, xpath, + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +static int ag_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct vty *vty = arg; + + vty_out(vty, " %s", yang_dnode_get_string(dnode, NULL)); + return YANG_ITER_CONTINUE; +} + +static void lib_interface_zebra_link_params_affinities_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " affinity"); + yang_dnode_iterate(ag_iter_cb, vty, dnode, "affinity"); + vty_out(vty, "\n"); +} + +DEFPY_YANG (link_params_affinity_mode, + link_params_affinity_mode_cmd, + "[no] affinity-mode ![$mode]", + NO_STR + "Interface affinity mode\n" + "Standard Admin-Group only RFC3630,5305,5329\n" + "Extended Admin-Group only RFC7308 (default)\n" + "Standard and extended Admin-Group format\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./affinity-mode", NB_OP_MODIFY, + mode); + else + nb_cli_enqueue_change(vty, "./affinity-mode", NB_OP_DESTROY, + NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_link_params_affinity_mode_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + enum affinity_mode affinity_mode = yang_dnode_get_enum(dnode, NULL); + + if (affinity_mode == AFFINITY_MODE_STANDARD) + vty_out(vty, " affinity-mode standard\n"); + else if (affinity_mode == AFFINITY_MODE_BOTH) + vty_out(vty, " affinity-mode both\n"); + else if (affinity_mode == AFFINITY_MODE_EXTENDED && show_defaults) + vty_out(vty, " affinity-mode extended\n"); +} + +#ifdef HAVE_NETLINK +DEFPY_YANG (ip_address, + ip_address_cmd, + "[no] ip address A.B.C.D/M [label LINE$label]", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP address (e.g. 10.0.0.1/8)\n" + "Label of this address\n" + "Label\n") +#else +DEFPY_YANG (ip_address, + ip_address_cmd, + "[no] ip address A.B.C.D/M", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP address (e.g. 10.0.0.1/8)\n") +#endif +{ + char ip[INET_ADDRSTRLEN + 3]; + char *mask; + + if (no) { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); +#ifdef HAVE_NETLINK + if (label) + nb_cli_enqueue_change(vty, "./label", NB_OP_MODIFY, + label); + else + nb_cli_enqueue_change(vty, "./label", NB_OP_DESTROY, + NULL); +#endif + } + + strlcpy(ip, address_str, sizeof(ip)); + + mask = strchr(ip, '/'); + assert(mask); + *mask = 0; + mask++; + + return nb_cli_apply_changes(vty, + "./frr-zebra:zebra/ipv4-addrs[ip='%s'][prefix-length='%s']", + ip, mask); +} + +static void lib_interface_zebra_ipv4_addrs_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *ip = yang_dnode_get_string(dnode, "ip"); + uint8_t prefix_length = yang_dnode_get_uint8(dnode, "prefix-length"); + + vty_out(vty, " ip address %s/%u", ip, prefix_length); + + if (yang_dnode_exists(dnode, "label")) { + const char *label = yang_dnode_get_string(dnode, "label"); + + vty_out(vty, " label %s", label); + } + + vty_out(vty, "\n"); +} + +#ifdef HAVE_NETLINK +DEFPY_YANG (ip_address_peer, + ip_address_peer_cmd, + "[no] ip address A.B.C.D peer A.B.C.D/M [label LINE$label]", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "Local IP (e.g. 10.0.0.1) for P-t-P address\n" + "Specify P-t-P address\n" + "Peer IP address (e.g. 10.0.0.1/8)\n" + "Label of this address\n" + "Label\n") +#else +DEFPY_YANG (ip_address_peer, + ip_address_peer_cmd, + "[no] ip address A.B.C.D peer A.B.C.D/M", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "Local IP (e.g. 10.0.0.1) for P-t-P address\n" + "Specify P-t-P address\n" + "Peer IP address (e.g. 10.0.0.1/8)\n") +#endif +{ + char peer_ip[INET_ADDRSTRLEN + 3]; + char *peer_mask; + + if (no) { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); +#ifdef HAVE_NETLINK + if (label) + nb_cli_enqueue_change(vty, "./label", NB_OP_MODIFY, + label); + else + nb_cli_enqueue_change(vty, "./label", NB_OP_DESTROY, + NULL); +#endif + } + + strlcpy(peer_ip, peer_str, sizeof(peer_ip)); + + peer_mask = strchr(peer_ip, '/'); + assert(peer_mask); + *peer_mask = 0; + peer_mask++; + + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/ipv4-p2p-addrs[ip='%s'][peer-ip='%s'][peer-prefix-length='%s']", + address_str, peer_ip, peer_mask); +} + +static void lib_interface_zebra_ipv4_p2p_addrs_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *ip = yang_dnode_get_string(dnode, "ip"); + const char *peer_ip = yang_dnode_get_string(dnode, "peer-ip"); + uint8_t peer_prefix_length = yang_dnode_get_uint8(dnode, + "peer-prefix-length"); + + vty_out(vty, " ip address %s peer %s/%u", ip, peer_ip, + peer_prefix_length); + + if (yang_dnode_exists(dnode, "label")) { + const char *label = yang_dnode_get_string(dnode, "label"); + + vty_out(vty, " label %s", label); + } + + vty_out(vty, "\n"); +} + +DEFPY_YANG (ipv6_address, + ipv6_address_cmd, + "[no] ipv6 address X:X::X:X/M", + NO_STR + "Interface IPv6 config commands\n" + "Set the IP address of an interface\n" + "IPv6 address (e.g. 3ffe:506::1/48)\n") +{ + char ip[INET6_ADDRSTRLEN + 4]; + char *mask; + + if (no) + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + + strlcpy(ip, address_str, sizeof(ip)); + + mask = strchr(ip, '/'); + assert(mask); + *mask = 0; + mask++; + + return nb_cli_apply_changes(vty, + "./frr-zebra:zebra/ipv6-addrs[ip='%s'][prefix-length='%s']", + ip, mask); +} + +static void lib_interface_zebra_ipv6_addrs_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *ip = yang_dnode_get_string(dnode, "ip"); + uint8_t prefix_length = yang_dnode_get_uint8(dnode, "prefix-length"); + + vty_out(vty, " ipv6 address %s/%u\n", ip, prefix_length); +} + +/* CLI for setting an ES in bypass mode */ +DEFPY_YANG_HIDDEN (zebra_evpn_es_bypass, + zebra_evpn_es_bypass_cmd, + "[no] evpn mh bypass", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Set bypass mode\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/evpn-mh/bypass", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/evpn-mh/bypass", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_evpn_mh_bypass_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool bypass = yang_dnode_get_bool(dnode, NULL); + + if (bypass) + vty_out(vty, " evpn mh bypass\n"); + else if (show_defaults) + vty_out(vty, " no evpn mh bypass\n"); +} + +/* CLI for configuring DF preference part for an ES */ +DEFPY_YANG (zebra_evpn_es_pref, + zebra_evpn_es_pref_cmd, + "[no$no] evpn mh es-df-pref ![(1-65535)$df_pref]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Preference value used for DF election\n" + "Preference\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/df-preference", + NB_OP_MODIFY, df_pref_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/df-preference", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_evpn_mh_df_preference_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint16_t df_pref = yang_dnode_get_uint16(dnode, NULL); + + vty_out(vty, " evpn mh es-df-pref %u\n", df_pref); +} + +/* CLI for setting up sysmac part of ESI on an access port */ +DEFPY_YANG (zebra_evpn_es_sys_mac, + zebra_evpn_es_sys_mac_cmd, + "[no$no] evpn mh es-sys-mac ![X:X:X:X:X:X$mac]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment system MAC\n" + MAC_STR) +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/system-mac", + NB_OP_MODIFY, mac_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/system-mac", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_evpn_mh_type_3_system_mac_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + char buf[ETHER_ADDR_STRLEN]; + struct ethaddr mac; + + yang_dnode_get_mac(&mac, dnode, NULL); + + vty_out(vty, " evpn mh es-sys-mac %s\n", + prefix_mac2str(&mac, buf, sizeof(buf))); +} + +/* CLI for setting up local-ID part of ESI on an access port */ +DEFPY_YANG (zebra_evpn_es_id, + zebra_evpn_es_id_cmd, + "[no$no] evpn mh es-id ![(1-16777215)$es_lid | NAME$esi_str]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment identifier\n" + "local discriminator\n" + "10-byte ID - 00:AA:BB:CC:DD:EE:FF:GG:HH:II\n") +{ + if (no) { + /* We don't know which one is configured, so detroy both types. */ + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-0/esi", + NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + NB_OP_DESTROY, NULL); + } else { + if (esi_str) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-0/esi", + NB_OP_MODIFY, esi_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + NB_OP_MODIFY, es_lid_str); + } + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_evpn_mh_type_0_esi_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *esi_str = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " evpn mh es-id %s\n", esi_str); +} + +static void lib_interface_zebra_evpn_mh_type_3_local_discriminator_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t es_lid = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " evpn mh es-id %u\n", es_lid); +} + +/* CLI for tagging an interface as an uplink */ +DEFPY_YANG (zebra_evpn_mh_uplink, + zebra_evpn_mh_uplink_cmd, + "[no] evpn mh uplink", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Uplink to the VxLAN core\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/evpn-mh/uplink", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/evpn-mh/uplink", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_evpn_mh_uplink_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool uplink = yang_dnode_get_bool(dnode, NULL); + + if (uplink) + vty_out(vty, " evpn mh uplink\n"); + else if (show_defaults) + vty_out(vty, " no evpn mh uplink\n"); +} + +#if defined(HAVE_RTADV) +DEFPY_YANG (ipv6_nd_ra_fast_retrans, + ipv6_nd_ra_fast_retrans_cmd, + "[no] ipv6 nd ra-fast-retrans", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Fast retransmit of RA packets\n") +{ + if (no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/fast-retransmit", + NB_OP_MODIFY, "false"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/fast-retransmit", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_fast_retransmit_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool fast_retransmit = yang_dnode_get_bool(dnode, NULL); + + if (!fast_retransmit) + vty_out(vty, " no ipv6 nd ra-fast-retrans\n"); + else if (show_defaults) + vty_out(vty, " ipv6 nd ra-fast-retrans\n"); +} + +DEFPY_YANG (ipv6_nd_ra_hop_limit, + ipv6_nd_ra_hop_limit_cmd, + "[no] ipv6 nd ra-hop-limit ![(0-255)$hopcount]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Hop Limit\n" + "Advertisement Hop Limit in hops (default:64)\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/cur-hop-limit", + NB_OP_MODIFY, hopcount_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/cur-hop-limit", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint8_t hop_limit = yang_dnode_get_uint8(dnode, NULL); + + vty_out(vty, " ipv6 nd ra-hop-limit %u\n", hop_limit); +} + +DEFPY_YANG (ipv6_nd_ra_retrans_interval, + ipv6_nd_ra_retrans_interval_cmd, + "[no] ipv6 nd ra-retrans-interval ![(0-4294967295)$interval]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Retransmit Interval\n" + "Advertisement Retransmit Interval in msec\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/retrans-timer", + NB_OP_MODIFY, interval_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/retrans-timer", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_retrans_timer_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t retrans_timer = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " ipv6 nd ra-retrans-interval %u\n", retrans_timer); +} + +DEFPY_YANG (ipv6_nd_suppress_ra, + ipv6_nd_suppress_ra_cmd, + "[no] ipv6 nd suppress-ra", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Suppress Router Advertisement\n") +{ + if (no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/send-advertisements", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/send-advertisements", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_send_advertisements_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool send_advertisements = yang_dnode_get_bool(dnode, NULL); + + if (send_advertisements) + vty_out(vty, " no ipv6 nd suppress-ra\n"); + else if (show_defaults) + vty_out(vty, " ipv6 nd suppress-ra\n"); +} + +DEFPY_YANG (ipv6_nd_ra_interval, + ipv6_nd_ra_interval_cmd, + "[no] ipv6 nd ra-interval ![<(1-1800)$sec|msec (70-1800000)$msec>]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n" + "Router Advertisement interval in seconds\n" + "Router Advertisement interval in milliseconds\n" + "Router Advertisement interval in milliseconds\n") +{ + char value[YANG_VALUE_MAXLEN]; + + if (!no) { + if (sec) + snprintf(value, sizeof(value), "%lu", sec * 1000); + else + snprintf(value, sizeof(value), "%lu", msec); + + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/max-rtr-adv-interval", + NB_OP_MODIFY, value); + } else { + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/max-rtr-adv-interval", + NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_max_rtr_adv_interval_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t max_rtr_adv_interval = yang_dnode_get_uint32(dnode, NULL); + + if (max_rtr_adv_interval % 1000) + vty_out(vty, " ipv6 nd ra-interval msec %u\n", + max_rtr_adv_interval); + else + vty_out(vty, " ipv6 nd ra-interval %u\n", + max_rtr_adv_interval / 1000); +} + +DEFPY_YANG (ipv6_nd_ra_lifetime, + ipv6_nd_ra_lifetime_cmd, + "[no] ipv6 nd ra-lifetime ![(0-9000)$lifetime]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router lifetime\n" + "Router lifetime in seconds (0 stands for a non-default gw)\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/default-lifetime", + NB_OP_MODIFY, lifetime_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/default-lifetime", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_default_lifetime_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint16_t default_lifetime = yang_dnode_get_uint16(dnode, NULL); + + vty_out(vty, " ipv6 nd ra-lifetime %u\n", default_lifetime); +} + +DEFPY_YANG (ipv6_nd_reachable_time, + ipv6_nd_reachable_time_cmd, + "[no] ipv6 nd reachable-time ![(1-3600000)$msec]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Reachable time\n" + "Reachable time in milliseconds\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/reachable-time", + NB_OP_MODIFY, msec_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/reachable-time", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_reachable_time_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t reachable_time = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " ipv6 nd reachable-time %u\n", reachable_time); +} + +DEFPY_YANG (ipv6_nd_homeagent_preference, + ipv6_nd_homeagent_preference_cmd, + "[no] ipv6 nd home-agent-preference ![(0-65535)$pref]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent preference\n" + "preference value (default is 0, least preferred)\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-preference", + NB_OP_MODIFY, pref_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-preference", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint16_t home_agent_preference = yang_dnode_get_uint16(dnode, NULL); + + vty_out(vty, " ipv6 nd home-agent-preference %u\n", + home_agent_preference); +} + +DEFPY_YANG (ipv6_nd_homeagent_lifetime, + ipv6_nd_homeagent_lifetime_cmd, + "[no] ipv6 nd home-agent-lifetime ![(1-65520)$lifetime]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent lifetime\n" + "Home Agent lifetime in seconds\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-lifetime", + NB_OP_MODIFY, lifetime_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-lifetime", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint16_t home_agent_lifetime = yang_dnode_get_uint16(dnode, NULL); + + vty_out(vty, " ipv6 nd home-agent-lifetime %u\n", home_agent_lifetime); +} + +DEFPY_YANG (ipv6_nd_managed_config_flag, + ipv6_nd_managed_config_flag_cmd, + "[no] ipv6 nd managed-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Managed address configuration flag\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/managed-flag", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/managed-flag", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_ipv6_router_advertisements_managed_flag_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool managed_flag = yang_dnode_get_bool(dnode, NULL); + + if (managed_flag) + vty_out(vty, " ipv6 nd managed-config-flag\n"); + else if (show_defaults) + vty_out(vty, " no ipv6 nd managed-config-flag\n"); +} + +DEFPY_YANG (ipv6_nd_homeagent_config_flag, + ipv6_nd_homeagent_config_flag_cmd, + "[no] ipv6 nd home-agent-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent configuration flag\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-flag", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/home-agent-flag", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_home_agent_flag_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool home_agent_flag = yang_dnode_get_bool(dnode, NULL); + + if (home_agent_flag) + vty_out(vty, " ipv6 nd home-agent-config-flag\n"); + else if (show_defaults) + vty_out(vty, " no ipv6 nd home-agent-config-flag\n"); +} + +DEFPY_YANG (ipv6_nd_adv_interval_config_option, + ipv6_nd_adv_interval_config_option_cmd, + "[no] ipv6 nd adv-interval-option", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Interval Option\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/advertisement-interval-option", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/advertisement-interval-option", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_advertisement_interval_option_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool advertisement_interval_option = yang_dnode_get_bool(dnode, NULL); + + if (advertisement_interval_option) + vty_out(vty, " ipv6 nd adv-interval-option\n"); + else if (show_defaults) + vty_out(vty, " no ipv6 nd adv-interval-option\n"); +} + +DEFPY_YANG (ipv6_nd_other_config_flag, + ipv6_nd_other_config_flag_cmd, + "[no] ipv6 nd other-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Other statefull configuration flag\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/other-config-flag", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/other-config-flag", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_other_config_flag_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool other_config_flag = yang_dnode_get_bool(dnode, NULL); + + if (other_config_flag) + vty_out(vty, " ipv6 nd other-config-flag\n"); + else if (show_defaults) + vty_out(vty, " no ipv6 nd other-config-flag\n"); +} + +DEFPY_YANG (ipv6_nd_prefix, + ipv6_nd_prefix_cmd, + "[no] ipv6 nd prefix X:X::X:X/M$prefix [<(0-4294967295)|infinite>$valid <(0-4294967295)|infinite>$preferred] [{router-address$routeraddr|off-link$offlink|no-autoconfig$noautoconf}]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Set Router Address flag\n" + "Do not use prefix for onlink determination\n" + "Do not use prefix for autoconfiguration\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + if (valid) { + if (strmatch(valid, "infinite")) + valid = "4294967295"; + nb_cli_enqueue_change(vty, "./valid-lifetime", + NB_OP_MODIFY, valid); + } else { + nb_cli_enqueue_change(vty, "./valid-lifetime", + NB_OP_DESTROY, NULL); + } + if (preferred) { + if (strmatch(preferred, "infinite")) + preferred = "4294967295"; + nb_cli_enqueue_change(vty, "./preferred-lifetime", + NB_OP_MODIFY, preferred); + } else { + nb_cli_enqueue_change(vty, "./preferred-lifetime", + NB_OP_DESTROY, NULL); + } + if (routeraddr) + nb_cli_enqueue_change(vty, "./router-address-flag", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, "./router-address-flag", + NB_OP_DESTROY, NULL); + if (offlink) + nb_cli_enqueue_change(vty, "./on-link-flag", + NB_OP_MODIFY, "false"); + else + nb_cli_enqueue_change(vty, "./on-link-flag", + NB_OP_DESTROY, NULL); + if (noautoconf) + nb_cli_enqueue_change(vty, "./autonomous-flag", + NB_OP_MODIFY, "false"); + else + nb_cli_enqueue_change(vty, "./autonomous-flag", + NB_OP_DESTROY, NULL); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix[prefix-spec='%s']", + prefix_str); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *prefix = yang_dnode_get_string(dnode, "prefix-spec"); + struct lyd_node *valid = yang_dnode_get(dnode, "valid-lifetime"); + struct lyd_node *preferred = yang_dnode_get(dnode, "preferred-lifetime"); + bool router_address_flag = yang_dnode_get_bool(dnode, + "router-address-flag"); + bool on_link_flag = yang_dnode_get_bool(dnode, "on-link-flag"); + bool autonomous_flag = yang_dnode_get_bool(dnode, "autonomous-flag"); + + vty_out(vty, " ipv6 nd prefix %s", prefix); + + if (!yang_dnode_is_default(valid, NULL) || + !yang_dnode_is_default(preferred, NULL) || show_defaults) { + uint32_t valid_lifetime = yang_dnode_get_uint32(valid, NULL); + uint32_t preferred_lifetime = yang_dnode_get_uint32(preferred, + NULL); + + if (valid_lifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", valid_lifetime); + if (preferred_lifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", preferred_lifetime); + } + + if (!on_link_flag) + vty_out(vty, " off-link"); + + if (!autonomous_flag) + vty_out(vty, " no-autoconfig"); + + if (router_address_flag) + vty_out(vty, " router-address"); + + vty_out(vty, "\n"); +} + +DEFPY_YANG (ipv6_nd_router_preference, + ipv6_nd_router_preference_cmd, + "[no] ipv6 nd router-preference ![$pref]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Default router preference\n" + "High default router preference\n" + "Medium default router preference (default)\n" + "Low default router preference\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/default-router-preference", + NB_OP_MODIFY, pref); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/default-router-preference", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_default_router_preference_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *default_router_preference = yang_dnode_get_string(dnode, + NULL); + + vty_out(vty, " ipv6 nd router-preference %s\n", + default_router_preference); +} + +DEFPY_YANG (ipv6_nd_mtu, + ipv6_nd_mtu_cmd, + "[no] ipv6 nd mtu ![(1-65535)]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertised MTU\n" + "MTU in bytes\n") +{ + if (!no) + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/link-mtu", + NB_OP_MODIFY, mtu_str); + else + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/ipv6-router-advertisements/link-mtu", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_ipv6_router_advertisements_link_mtu_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint16_t link_mtu = yang_dnode_get_uint32(dnode, NULL); + + vty_out(vty, " ipv6 nd mtu %u\n", link_mtu); +} + +DEFPY_YANG (ipv6_nd_rdnss, + ipv6_nd_rdnss_cmd, + "[no] ipv6 nd rdnss X:X::X:X$addr [<(0-4294967295)|infinite>]$lifetime", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Recursive DNS server information\n" + "IPv6 address\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + if (lifetime) { + if (strmatch(lifetime, "infinite")) + lifetime = "4294967295"; + nb_cli_enqueue_change(vty, "./lifetime", NB_OP_MODIFY, + lifetime); + } else { + nb_cli_enqueue_change(vty, "./lifetime", NB_OP_DESTROY, + NULL); + } + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address[address='%s']", + addr_str); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *address = yang_dnode_get_string(dnode, "address"); + + vty_out(vty, " ipv6 nd rdnss %s", address); + + if (yang_dnode_exists(dnode, "lifetime")) { + uint32_t lifetime = yang_dnode_get_uint32(dnode, "lifetime"); + + if (lifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", lifetime); + } + + vty_out(vty, "\n"); +} + +DEFPY_YANG (ipv6_nd_dnssl, + ipv6_nd_dnssl_cmd, + "[no] ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]$lifetime", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "DNS search list information\n" + "Domain name suffix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n") +{ + char domain[254]; + size_t len; + + len = strlcpy(domain, suffix, sizeof(domain)); + if (len == 0 || len >= sizeof(domain)) { + vty_out(vty, "Malformed DNS search domain\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (domain[len - 1] == '.') { + /* + * Allow, but don't require, a trailing dot signifying the root + * zone. Canonicalize by cutting it off if present. + */ + domain[len - 1] = '\0'; + len--; + } + + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + if (lifetime) { + if (strmatch(lifetime, "infinite")) + lifetime = "4294967295"; + nb_cli_enqueue_change(vty, "./lifetime", NB_OP_MODIFY, + lifetime); + } else { + nb_cli_enqueue_change(vty, "./lifetime", NB_OP_DESTROY, + NULL); + } + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain[domain='%s']", + domain); +} + +static void +lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + const char *domain = yang_dnode_get_string(dnode, "domain"); + + vty_out(vty, " ipv6 nd dnssl %s", domain); + + if (yang_dnode_exists(dnode, "lifetime")) { + uint32_t lifetime = yang_dnode_get_uint32(dnode, "lifetime"); + + if (lifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", lifetime); + } + + vty_out(vty, "\n"); +} +#endif /* HAVE_RTADV */ + +#if HAVE_BFDD == 0 +DEFPY_YANG (zebra_ptm_enable_if, + zebra_ptm_enable_if_cmd, + "[no] ptm-enable", + NO_STR + "Enable neighbor check with specified topology\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ptm-enable", + NB_OP_MODIFY, "false"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ptm-enable", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_interface_zebra_ptm_enable_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool enable = yang_dnode_get_bool(dnode, NULL); + + if (!enable) + vty_out(vty, " no ptm-enable\n"); + else if (show_defaults) + vty_out(vty, " ptm-enable\n"); +} +#endif /* HAVE_BFDD == 0 */ + +/* + * VRF commands + */ + +static void zebra_vrf_indent_cli_write(struct vty *vty, + const struct lyd_node *dnode) +{ + const struct lyd_node *vrf = yang_dnode_get_parent(dnode, "vrf"); + + if (vrf && strcmp(yang_dnode_get_string(vrf, "name"), VRF_DEFAULT_NAME)) + vty_out(vty, " "); +} + +DEFPY_YANG (ip_router_id, + ip_router_id_cmd, + "[no] ip router-id A.B.C.D$id vrf NAME", + NO_STR + IP_STR + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/router-id", NB_OP_MODIFY, + id_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/router-id", NB_OP_DESTROY, + NULL); + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", vrf); +} + +ALIAS_YANG (ip_router_id, + router_id_cmd, + "[no] router-id A.B.C.D$id vrf NAME", + NO_STR + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFPY_YANG (ipv6_router_id, + ipv6_router_id_cmd, + "[no] ipv6 router-id X:X::X:X$id vrf NAME", + NO_STR + IPV6_STR + "Manually set the router-id\n" + "IPv6 address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ipv6-router-id", + NB_OP_MODIFY, id_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ipv6-router-id", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", vrf); +} + +DEFPY_YANG (ip_router_id_in_vrf, + ip_router_id_in_vrf_cmd, + "[no] ip router-id ![A.B.C.D$id]", + NO_STR + IP_STR + "Manually set the router-id\n" + "IP address to use for router-id\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/router-id", NB_OP_MODIFY, + id_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/router-id", NB_OP_DESTROY, + NULL); + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS_YANG (ip_router_id_in_vrf, + router_id_in_vrf_cmd, + "[no] router-id ![A.B.C.D$id]", + NO_STR + "Manually set the router-id\n" + "IP address to use for router-id\n"); + +DEFPY_YANG (ipv6_router_id_in_vrf, + ipv6_router_id_in_vrf_cmd, + "[no] ipv6 router-id ![X:X::X:X$id]", + NO_STR + IP6_STR + "Manually set the IPv6 router-id\n" + "IPV6 address to use for router-id\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ipv6-router-id", + NB_OP_MODIFY, id_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ipv6-router-id", + NB_OP_DESTROY, NULL); + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_vrf_zebra_router_id_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *id = yang_dnode_get_string(dnode, NULL); + + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "ip router-id %s\n", id); +} + +static void lib_vrf_zebra_ipv6_router_id_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *id = yang_dnode_get_string(dnode, NULL); + + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "ipv6 router-id %s\n", id); +} + +DEFPY_YANG (ip_protocol, + ip_protocol_cmd, + "[no] ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA + " $proto ![route-map ROUTE-MAP$rmap]", + NO_STR + IP_STR + "Filter routing info exchanged between zebra and protocol\n" + FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA + "Specify route-map\n" + "Route map name\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes( + vty, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/filter-protocol[afi-safi='%s'][protocol='%s']", + VRF_DEFAULT_NAME, + yang_afi_safi_value2identity(AFI_IP, SAFI_UNICAST), + proto); + + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/filter-protocol[afi-safi='%s'][protocol='%s']", + yang_afi_safi_value2identity(AFI_IP, SAFI_UNICAST), proto); +} + +DEFPY_YANG (ipv6_protocol, + ipv6_protocol_cmd, + "[no] ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA + " $proto ![route-map ROUTE-MAP$rmap]", + NO_STR + IP6_STR + "Filter IPv6 routing info exchanged between zebra and protocol\n" + FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA + "Specify route-map\n" + "Route map name\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes( + vty, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/filter-protocol[afi-safi='%s'][protocol='%s']", + VRF_DEFAULT_NAME, + yang_afi_safi_value2identity(AFI_IP6, SAFI_UNICAST), + proto); + + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/filter-protocol[afi-safi='%s'][protocol='%s']", + yang_afi_safi_value2identity(AFI_IP6, SAFI_UNICAST), proto); +} + +static void lib_vrf_zebra_filter_protocol_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *afi_safi = yang_dnode_get_string(dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(dnode, "protocol"); + const char *rmap = yang_dnode_get_string(dnode, "route-map"); + afi_t afi; + safi_t safi; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (safi != SAFI_UNICAST) + return; + + zebra_vrf_indent_cli_write(vty, dnode); + + if (afi == AFI_IP) + vty_out(vty, "ip protocol %s route-map %s\n", proto, rmap); + else + vty_out(vty, "ipv6 protocol %s route-map %s\n", proto, rmap); +} + +DEFPY_YANG (ip_protocol_nht_rmap, + ip_protocol_nht_rmap_cmd, + "[no] ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA + " $proto ![route-map ROUTE-MAP$rmap]", + NO_STR + IP_STR + "Filter Next Hop tracking route resolution\n" + FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA + "Specify route map\n" + "Route map name\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes( + vty, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/filter-nht[afi-safi='%s'][protocol='%s']", + VRF_DEFAULT_NAME, + yang_afi_safi_value2identity(AFI_IP, SAFI_UNICAST), + proto); + + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/filter-nht[afi-safi='%s'][protocol='%s']", + yang_afi_safi_value2identity(AFI_IP, SAFI_UNICAST), proto); +} + +DEFPY_YANG (ipv6_protocol_nht_rmap, + ipv6_protocol_nht_rmap_cmd, + "[no] ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA + " $proto ![route-map ROUTE-MAP$rmap]", + NO_STR + IP6_STR + "Filter Next Hop tracking route resolution\n" + FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA + "Specify route map\n" + "Route map name\n") +{ + if (!no) { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap); + } else { + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes( + vty, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/filter-nht[afi-safi='%s'][protocol='%s']", + VRF_DEFAULT_NAME, + yang_afi_safi_value2identity(AFI_IP6, SAFI_UNICAST), + proto); + + return nb_cli_apply_changes( + vty, + "./frr-zebra:zebra/filter-nht[afi-safi='%s'][protocol='%s']", + yang_afi_safi_value2identity(AFI_IP6, SAFI_UNICAST), proto); +} + +static void lib_vrf_zebra_filter_nht_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *afi_safi = yang_dnode_get_string(dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(dnode, "protocol"); + const char *rmap = yang_dnode_get_string(dnode, "route-map"); + afi_t afi; + safi_t safi; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (safi != SAFI_UNICAST) + return; + + zebra_vrf_indent_cli_write(vty, dnode); + + if (afi == AFI_IP) + vty_out(vty, "ip nht %s route-map %s\n", proto, rmap); + else + vty_out(vty, "ipv6 nht %s route-map %s\n", proto, rmap); +} + +DEFPY_YANG (ip_nht_default_route, + ip_nht_default_route_cmd, + "[no] ip nht resolve-via-default", + NO_STR + IP_STR + "Filter Next Hop tracking route resolution\n" + "Resolve via default route\n") +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/resolve-via-default", + NB_OP_MODIFY, no ? "false" : "true"); + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_vrf_zebra_resolve_via_default_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool resolve_via_default = yang_dnode_get_bool(dnode, NULL); + + if (resolve_via_default != SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT || + show_defaults) { + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "%sip nht resolve-via-default\n", + resolve_via_default ? "" : "no "); + } +} + +DEFPY_YANG (ipv6_nht_default_route, + ipv6_nht_default_route_cmd, + "[no] ipv6 nht resolve-via-default", + NO_STR + IP6_STR + "Filter Next Hop tracking route resolution\n" + "Resolve via default route\n") +{ + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/ipv6-resolve-via-default", + NB_OP_MODIFY, no ? "false" : "true"); + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_vrf_zebra_ipv6_resolve_via_default_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + bool resolve_via_default = yang_dnode_get_bool(dnode, NULL); + + if (resolve_via_default != SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT || + show_defaults) { + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "%sipv6 nht resolve-via-default\n", + resolve_via_default ? "" : "no "); + } +} + +DEFPY_YANG (vrf_netns, + vrf_netns_cmd, + "[no] netns ![NAME$netns_name]", + NO_STR + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + vty_out(vty, "%% This command doesn't do anything.\n"); + vty_out(vty, + "%% VRF is linked to a netns automatically based on its name.\n"); + return CMD_WARNING; +} + +DEFPY_YANG (ip_table_range, ip_table_range_cmd, + "[no] ip table range ![(1-4294967295)$start (1-4294967295)$end]", + NO_STR IP_STR + "table configuration\n" + "Configure table range\n" + "Start Routing Table\n" + "End Routing Table\n") +{ + if (!no) { + const struct lyd_node *start_node; + const struct lyd_node *end_node; + + if (vty->node == CONFIG_NODE) { + start_node = + yang_dnode_getf(vty->candidate_config->dnode, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/netns/table-range/start", + VRF_DEFAULT_NAME); + end_node = + yang_dnode_getf(vty->candidate_config->dnode, + "/frr-vrf:lib/vrf[name='%s']/frr-zebra:zebra/netns/table-range/end", + VRF_DEFAULT_NAME); + } else { + start_node = + yang_dnode_getf(vty->candidate_config->dnode, + "%s/frr-zebra:zebra/netns/table-range/start", + VTY_CURR_XPATH); + end_node = + yang_dnode_getf(vty->candidate_config->dnode, + "%s/frr-zebra:zebra/netns/table-range/end", + VTY_CURR_XPATH); + } + + if (start_node && end_node) { + if (yang_dnode_get_uint32(start_node, NULL) != + (uint32_t)start || + yang_dnode_get_uint32(end_node, NULL) != + (uint32_t)end) { + vty_out(vty, + "%% New range will be taken into account at restart.\n"); + vty_out(vty, + "%% Don't forget to save your configuration.\n"); + } + } + + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/netns/table-range", + NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/netns/table-range/start", + NB_OP_MODIFY, start_str); + nb_cli_enqueue_change(vty, + "./frr-zebra:zebra/netns/table-range/end", + NB_OP_MODIFY, end_str); + } else { + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/netns/table-range", + NB_OP_DESTROY, NULL); + } + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_vrf_zebra_netns_table_range_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + uint32_t start = yang_dnode_get_uint32(dnode, "start"); + uint32_t end = yang_dnode_get_uint32(dnode, "end"); + + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "ip table range %u %u\n", start, end); +} + +DEFPY_YANG (vni_mapping, + vni_mapping_cmd, + "[no] vni ![" CMD_VNI_RANGE "[prefix-routes-only$filter]]", + NO_STR + "VNI corresponding to tenant VRF\n" + "VNI-ID\n" + "prefix-routes-only\n") +{ + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY, + vni_str); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY, + NULL); + + if (filter) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_MODIFY, "true"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_DESTROY, NULL); + + if (vty->node == CONFIG_NODE) + return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']", + VRF_DEFAULT_NAME); + + return nb_cli_apply_changes(vty, NULL); +} + +static void lib_vrf_zebra_l3vni_id_cli_write(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vni_t vni = yang_dnode_get_uint32(dnode, NULL); + bool prefix_only = yang_dnode_get_bool(dnode, "../prefix-only"); + + zebra_vrf_indent_cli_write(vty, dnode); + + vty_out(vty, "vni %u", vni); + + if (prefix_only) + vty_out(vty, " prefix-routes-only"); + + vty_out(vty, "\n"); +} + +DEFPY_YANG( + match_ip_address_prefix_len, match_ip_address_prefix_len_cmd, + "match ip address prefix-len (0-32)$length", + MATCH_STR + IP_STR + "Match prefix length of IP address\n" + "Match prefix length of IP address\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_address_prefix_len, no_match_ip_address_prefix_len_cmd, + "no match ip address prefix-len [(0-32)]", + NO_STR + MATCH_STR + IP_STR + "Match prefix length of IP address\n" + "Match prefix length of IP address\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ipv6_address_prefix_len, match_ipv6_address_prefix_len_cmd, + "match ipv6 address prefix-len (0-128)$length", + MATCH_STR + IPV6_STR + "Match prefix length of IPv6 address\n" + "Match prefix length of IPv6 address\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ipv6_address_prefix_len, no_match_ipv6_address_prefix_len_cmd, + "no match ipv6 address prefix-len [(0-128)]", + NO_STR + MATCH_STR + IPV6_STR + "Match prefix length of IPv6 address\n" + "Match prefix length of IPv6 address\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_nexthop_prefix_len, match_ip_nexthop_prefix_len_cmd, + "match ip next-hop prefix-len (0-32)$length", + MATCH_STR + IP_STR + "Match prefixlen of nexthop IP address\n" + "Match prefixlen of given nexthop\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_nexthop_prefix_len, no_match_ip_nexthop_prefix_len_cmd, + "no match ip next-hop prefix-len [(0-32)]", + NO_STR + MATCH_STR + IP_STR + "Match prefixlen of nexthop IP address\n" + "Match prefix length of nexthop\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_source_protocol, match_source_protocol_cmd, + "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto", + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-protocol']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:source-protocol", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_source_protocol, no_match_source_protocol_cmd, + "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]", + NO_STR + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-protocol']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_source_instance, match_source_instance_cmd, + "match source-instance (0-255)$instance", + MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-instance']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:source-instance", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, instance_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_source_instance, no_match_source_instance_cmd, + "no match source-instance [(0-255)]", + NO_STR MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-instance']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* set functions */ + +DEFPY_YANG( + set_src, set_src_cmd, + "set src ", + SET_STR + "src address for route\n" + "IPv4 src address\n" + "IPv6 src address\n") +{ + const char *xpath = + "./set-action[action='frr-zebra-route-map:src-address']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (addrv4_str) { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-zebra-route-map:ipv4-src-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv4_str); + } else { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-zebra-route-map:ipv6-src-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv6_str); + } + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_set_src, no_set_src_cmd, + "no set src []", + NO_STR + SET_STR + "Source address for route\n" + "IPv4 address\n" + "IPv6 address\n") +{ + const char *xpath = + "./set-action[action='frr-zebra-route-map:src-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +const char *features[] = { +#if HAVE_BFDD == 0 + "ptm-bfd", +#endif +#if defined(HAVE_RTADV) + "ipv6-router-advertisements", +#endif + NULL +}; + +/* clang-format off */ +const struct frr_yang_module_info frr_zebra_cli_info = { + .name = "frr-zebra", + .ignore_cfg_cbs = true, + .features = features, + .nodes = { +#if HAVE_BFDD == 0 + { + .xpath = "/frr-zebra:zebra/ptm-enable", + .cbs.cli_show = zebra_ptm_enable_cli_write, + }, +#endif + { + .xpath = "/frr-zebra:zebra/route-map-delay", + .cbs.cli_show = zebra_route_map_delay_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-addrs", + .cbs.cli_show = lib_interface_zebra_ipv4_addrs_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-p2p-addrs", + .cbs.cli_show = lib_interface_zebra_ipv4_p2p_addrs_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-addrs", + .cbs.cli_show = lib_interface_zebra_ipv6_addrs_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/multicast", + .cbs.cli_show = lib_interface_zebra_multicast_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-detect", + .cbs.cli_show = lib_interface_zebra_link_detect_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/enabled", + .cbs.cli_show = lib_interface_zebra_enabled_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/bandwidth", + .cbs.cli_show = lib_interface_zebra_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/mpls", + .cbs.cli_show = lib_interface_zebra_mpls_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params", + .cbs.cli_show = lib_interface_zebra_link_params_cli_write, + .cbs.cli_show_end = lib_interface_zebra_link_params_cli_write_end, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/metric", + .cbs.cli_show = lib_interface_zebra_link_params_metric_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/max-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_max_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/max-reservable-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_max_reservable_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/unreserved-bandwidths/unreserved-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/residual-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_residual_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/available-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_available_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/utilized-bandwidth", + .cbs.cli_show = lib_interface_zebra_link_params_utilized_bandwidth_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/legacy-admin-group", + .cbs.cli_show = lib_interface_zebra_link_params_legacy_admin_group_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities", + .cbs.cli_show = lib_interface_zebra_link_params_affinities_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/affinity-mode", + .cbs.cli_show = lib_interface_zebra_link_params_affinity_mode_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor", + .cbs.cli_show = lib_interface_zebra_link_params_neighbor_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/delay", + .cbs.cli_show = lib_interface_zebra_link_params_delay_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/delay-variation", + .cbs.cli_show = lib_interface_zebra_link_params_delay_variation_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/packet-loss", + .cbs.cli_show = lib_interface_zebra_link_params_packet_loss_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi", + .cbs.cli_show = lib_interface_zebra_evpn_mh_type_0_esi_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac", + .cbs.cli_show = lib_interface_zebra_evpn_mh_type_3_system_mac_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + .cbs.cli_show = lib_interface_zebra_evpn_mh_type_3_local_discriminator_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/df-preference", + .cbs.cli_show = lib_interface_zebra_evpn_mh_df_preference_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/bypass", + .cbs.cli_show = lib_interface_zebra_evpn_mh_bypass_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/uplink", + .cbs.cli_show = lib_interface_zebra_evpn_mh_uplink_cli_write, + }, +#if defined(HAVE_RTADV) + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/send-advertisements", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_send_advertisements_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/max-rtr-adv-interval", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_max_rtr_adv_interval_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/managed-flag", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_managed_flag_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/other-config-flag", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_other_config_flag_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-flag", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_home_agent_flag_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/link-mtu", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_link_mtu_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/reachable-time", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_reachable_time_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/retrans-timer", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_retrans_timer_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/cur-hop-limit", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-lifetime", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_default_lifetime_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/fast-retransmit", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_fast_retransmit_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/advertisement-interval-option", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_advertisement_interval_option_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-preference", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-lifetime", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-router-preference", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_default_router_preference_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_cli_write, + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address", + .cbs.cli_show = lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_cli_write, + }, +#endif /* defined(HAVE_RTADV) */ +#if HAVE_BFDD == 0 + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ptm-enable", + .cbs.cli_show = lib_interface_zebra_ptm_enable_cli_write, + }, +#endif + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/router-id", + .cbs.cli_show = lib_vrf_zebra_router_id_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-router-id", + .cbs.cli_show = lib_vrf_zebra_ipv6_router_id_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-protocol", + .cbs.cli_show = lib_vrf_zebra_filter_protocol_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-nht", + .cbs.cli_show = lib_vrf_zebra_filter_nht_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/resolve-via-default", + .cbs.cli_show = lib_vrf_zebra_resolve_via_default_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-resolve-via-default", + .cbs.cli_show = lib_vrf_zebra_ipv6_resolve_via_default_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range", + .cbs.cli_show = lib_vrf_zebra_netns_table_range_cli_write, + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id", + .cbs.cli_show = lib_vrf_zebra_l3vni_id_cli_write, + }, + { + .xpath = NULL, + }, + } +}; + +struct cmd_node link_params_node = { + .name = "link-params", + .node = LINK_PARAMS_NODE, + .parent_node = INTERFACE_NODE, + .prompt = "%s(config-link-params)# ", +}; + +void zebra_cli_init(void) +{ + install_node(&link_params_node); + + install_element(INTERFACE_NODE, &multicast_new_cmd); + install_element(INTERFACE_NODE, &multicast_cmd); + install_element(INTERFACE_NODE, &mpls_cmd); + install_element(INTERFACE_NODE, &linkdetect_cmd); + install_element(INTERFACE_NODE, &shutdown_if_cmd); + install_element(INTERFACE_NODE, &bandwidth_if_cmd); + install_element(INTERFACE_NODE, &ip_address_cmd); + install_element(INTERFACE_NODE, &ip_address_peer_cmd); + install_element(INTERFACE_NODE, &ipv6_address_cmd); + install_element(INTERFACE_NODE, &link_params_cmd); + install_element(INTERFACE_NODE, &no_link_params_cmd); + install_default(LINK_PARAMS_NODE); + install_element(LINK_PARAMS_NODE, &link_params_enable_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_enable_cmd); + install_element(LINK_PARAMS_NODE, &link_params_metric_cmd); + install_element(LINK_PARAMS_NODE, &link_params_maxbw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_max_rsv_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_unrsv_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_admin_grp_cmd); + install_element(LINK_PARAMS_NODE, &link_params_inter_as_cmd); + install_element(LINK_PARAMS_NODE, &link_params_delay_cmd); + install_element(LINK_PARAMS_NODE, &link_params_delay_var_cmd); + install_element(LINK_PARAMS_NODE, &link_params_pkt_loss_cmd); + install_element(LINK_PARAMS_NODE, &link_params_ava_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_res_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_affinity_cmd); + install_element(LINK_PARAMS_NODE, &link_params_affinity_mode_cmd); + install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); + + install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_es_pref_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_es_bypass_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_mh_uplink_cmd); + +#if defined(HAVE_RTADV) + install_element(INTERFACE_NODE, &ipv6_nd_ra_fast_retrans_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_retrans_interval_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_hop_limit_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_reachable_time_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_adv_interval_config_option_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_prefix_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_router_preference_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_mtu_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_rdnss_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_dnssl_cmd); +#endif +#if HAVE_BFDD == 0 + install_element(INTERFACE_NODE, &zebra_ptm_enable_if_cmd); +#endif + + install_element(CONFIG_NODE, &ip_router_id_cmd); + install_element(CONFIG_NODE, &router_id_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_cmd); + install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &router_id_in_vrf_cmd); + install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd); + + install_element(CONFIG_NODE, &ip_protocol_cmd); + install_element(VRF_NODE, &ip_protocol_cmd); + install_element(CONFIG_NODE, &ipv6_protocol_cmd); + install_element(VRF_NODE, &ipv6_protocol_cmd); + install_element(CONFIG_NODE, &ip_protocol_nht_rmap_cmd); + install_element(VRF_NODE, &ip_protocol_nht_rmap_cmd); + install_element(CONFIG_NODE, &ipv6_protocol_nht_rmap_cmd); + install_element(VRF_NODE, &ipv6_protocol_nht_rmap_cmd); + install_element(CONFIG_NODE, &zebra_route_map_timer_cmd); + + install_element(CONFIG_NODE, &ip_nht_default_route_cmd); + install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd); + install_element(VRF_NODE, &ip_nht_default_route_cmd); + install_element(VRF_NODE, &ipv6_nht_default_route_cmd); + + install_element(CONFIG_NODE, &vni_mapping_cmd); + install_element(VRF_NODE, &vni_mapping_cmd); + + if (vrf_is_backend_netns()) + install_element(VRF_NODE, &vrf_netns_cmd); + + install_element(CONFIG_NODE, &ip_table_range_cmd); + install_element(VRF_NODE, &ip_table_range_cmd); +#if HAVE_BFDD == 0 + install_element(CONFIG_NODE, &zebra_ptm_enable_cmd); +#endif + install_element(RMAP_NODE, &match_ip_nexthop_prefix_len_cmd); + install_element(RMAP_NODE, &no_match_ip_nexthop_prefix_len_cmd); + install_element(RMAP_NODE, &match_ip_address_prefix_len_cmd); + install_element(RMAP_NODE, &match_ipv6_address_prefix_len_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_len_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_len_cmd); + install_element(RMAP_NODE, &match_source_protocol_cmd); + install_element(RMAP_NODE, &no_match_source_protocol_cmd); + install_element(RMAP_NODE, &match_source_instance_cmd); + install_element(RMAP_NODE, &no_match_source_instance_cmd); + + install_element(RMAP_NODE, &set_src_cmd); + install_element(RMAP_NODE, &no_set_src_cmd); +} diff --git a/zebra/zebra_cli.h b/zebra/zebra_cli.h new file mode 100644 index 0000000..01931a4 --- /dev/null +++ b/zebra/zebra_cli.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef _ZEBRA_CLI_H +#define _ZEBRA_CLI_H 1 + +extern const struct frr_yang_module_info frr_zebra_cli_info; + +void zebra_cli_init(void); + +#endif diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 0b40631..99693a5 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -230,6 +230,7 @@ struct dplane_intf_info { #define DPLANE_INTF_BROADCAST (1 << 2) #define DPLANE_INTF_HAS_DEST DPLANE_INTF_CONNECTED #define DPLANE_INTF_HAS_LABEL (1 << 4) +#define DPLANE_INTF_NOPREFIXROUTE (1 << 5) /* Interface address/prefix */ struct prefix prefix; @@ -355,6 +356,13 @@ struct dplane_tc_filter_info { uint32_t classid; }; +/* + * SRv6 encapsulation params context for the dataplane + */ +struct dplane_srv6_encap_ctx { + struct in6_addr srcaddr; +}; + /* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the @@ -391,7 +399,7 @@ struct zebra_dplane_ctx { vrf_id_t zd_vrf_id; uint32_t zd_table_id; - char zd_ifname[INTERFACE_NAMSIZ]; + char zd_ifname[IFNAMSIZ]; ifindex_t zd_ifindex; /* Support info for different kinds of updates */ @@ -417,6 +425,7 @@ struct zebra_dplane_ctx { struct dplane_gre_ctx gre; struct dplane_netconf_info netconf; enum zebra_dplane_startup_notifications spot; + struct dplane_srv6_encap_ctx srv6_encap; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -598,6 +607,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_tcs_in; _Atomic uint32_t dg_tcs_errors; + _Atomic uint32_t dg_srv6_encap_srcaddr_set_in; + _Atomic uint32_t dg_srv6_encap_srcaddr_set_errors; + /* Dataplane pthread */ struct frr_pthread *dg_pthread; @@ -860,6 +872,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_NETCONFIG: case DPLANE_OP_STARTUP_STAGE: + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: break; } } @@ -1185,6 +1198,11 @@ const char *dplane_op2str(enum dplane_op_e op) break; case DPLANE_OP_STARTUP_STAGE: ret = "STARTUP_STAGE"; + break; + + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: + ret = "SRV6_ENCAP_SRCADDR_SET"; + break; } return ret; @@ -2541,6 +2559,13 @@ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED); } +bool dplane_ctx_intf_is_noprefixroute(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_NOPREFIXROUTE); +} + bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -2569,6 +2594,13 @@ void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx) ctx->u.intf.flags |= DPLANE_INTF_SECONDARY; } +void dplane_ctx_intf_set_noprefixroute(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.flags |= DPLANE_INTF_NOPREFIXROUTE; +} + void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -2584,6 +2616,16 @@ const struct prefix *dplane_ctx_get_intf_addr( return &(ctx->u.intf.prefix); } + +/* Accessors for SRv6 encapsulation source address information */ +const struct in6_addr * +dplane_ctx_get_srv6_encap_srcaddr(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.srv6_encap.srcaddr); +} + void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx, const struct prefix *p) { @@ -5857,6 +5899,59 @@ done: return result; } +/* + * Common helper api for SRv6 encapsulation source address set + */ +enum zebra_dplane_result +dplane_srv6_encap_srcaddr_set(const struct in6_addr *addr, ns_id_t ns_id) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + struct zebra_dplane_ctx *ctx = NULL; + enum dplane_op_e op = DPLANE_OP_SRV6_ENCAP_SRCADDR_SET; + int ret; + struct zebra_ns *zns; + + if (!addr) + return result; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("init dplane ctx %s: addr %pI6", dplane_op2str(op), + addr); + } + + zns = zebra_ns_lookup(ns_id); + if (!zns) + return result; + + ctx = dplane_ctx_alloc(); + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + + dplane_ctx_ns_init(ctx, zns, false); + + /* Init the SRv6 encap source address specific data area */ + memcpy(&ctx->u.srv6_encap.srcaddr, addr, + sizeof(ctx->u.srv6_encap.srcaddr)); + + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_srv6_encap_srcaddr_set_in, 1, + memory_order_relaxed); + + /* Enqueue context for processing */ + ret = dplane_update_enqueue(ctx); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info + .dg_srv6_encap_srcaddr_set_errors, + 1, memory_order_relaxed); + dplane_ctx_free(&ctx); + } + return result; +} + /* * Handler for 'show dplane' */ @@ -6582,6 +6677,12 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_UPDATE: case DPLANE_OP_STARTUP_STAGE: break; + + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: + zlog_debug("Dplane SRv6 encap source address set op %s, addr %pI6", + dplane_op2str(dplane_ctx_get_op(ctx)), + &ctx->u.srv6_encap.srcaddr); + break; } } @@ -6752,6 +6853,13 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_NETCONFIG: break; + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info + .dg_srv6_encap_srcaddr_set_errors, + 1, memory_order_relaxed); + break; + case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) @@ -6860,6 +6968,31 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) return 0; } +static int kernel_dplane_shutdown_func(struct zebra_dplane_provider *prov, + bool early) +{ + struct zebra_dplane_ctx *ctx; + + if (early) + return 1; + + ctx = dplane_provider_dequeue_in_ctx(prov); + while (ctx) { + dplane_ctx_free(&ctx); + + ctx = dplane_provider_dequeue_in_ctx(prov); + } + + ctx = dplane_provider_dequeue_out_ctx(prov); + while (ctx) { + dplane_ctx_free(&ctx); + + ctx = dplane_provider_dequeue_out_ctx(prov); + } + + return 1; +} + #ifdef DPLANE_TEST_PROVIDER /* @@ -6932,12 +7065,10 @@ static void dplane_provider_init(void) { int ret; - ret = dplane_provider_register("Kernel", - DPLANE_PRIO_KERNEL, + ret = dplane_provider_register("Kernel", DPLANE_PRIO_KERNEL, DPLANE_PROV_FLAGS_DEFAULT, NULL, kernel_dplane_process_func, - NULL, - NULL, NULL); + kernel_dplane_shutdown_func, NULL, NULL); if (ret != AOK) zlog_err("Unable to register kernel dplane provider: %d", @@ -7338,6 +7469,7 @@ static void dplane_thread_loop(struct event *event) void zebra_dplane_shutdown(void) { struct zebra_dplane_provider *dp; + struct zebra_dplane_ctx *ctx; if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane shutdown called"); @@ -7365,8 +7497,25 @@ void zebra_dplane_shutdown(void) } /* TODO -- Clean-up provider objects */ + dp = dplane_prov_list_first(&zdplane_info.dg_providers); + while (dp) { + dplane_prov_list_del(&zdplane_info.dg_providers, dp); + XFREE(MTYPE_DP_PROV, dp); + + dp = dplane_prov_list_first(&zdplane_info.dg_providers); + } /* TODO -- Clean queue(s), free memory */ + DPLANE_LOCK(); + { + ctx = dplane_ctx_list_pop(&zdplane_info.dg_update_list); + while (ctx) { + dplane_ctx_free(&ctx); + + ctx = dplane_ctx_list_pop(&zdplane_info.dg_update_list); + } + } + DPLANE_UNLOCK(); } /* diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 87c2e03..2f7d218 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -18,6 +18,7 @@ #include "zebra/zserv.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_nhg.h" +#include "zebra/ge_netlink.h" #ifdef __cplusplus extern "C" { @@ -198,6 +199,9 @@ enum dplane_op_e { /* Startup Control */ DPLANE_OP_STARTUP_STAGE, + + /* Source address for SRv6 encapsulation */ + DPLANE_OP_SRV6_ENCAP_SRCADDR_SET, }; /* @@ -658,10 +662,14 @@ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx); void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx); bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx); void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx); +bool dplane_ctx_intf_is_noprefixroute(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_intf_set_noprefixroute(struct zebra_dplane_ctx *ctx); bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx); void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx); const struct prefix *dplane_ctx_get_intf_addr( const struct zebra_dplane_ctx *ctx); +const struct in6_addr * +dplane_ctx_get_srv6_encap_srcaddr(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx, const struct prefix *p); bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx); @@ -990,6 +998,13 @@ enum zebra_dplane_result dplane_gre_set(struct interface *ifp, struct interface *ifp_link, unsigned int mtu, const struct zebra_l2info_gre *gre_info); +/* + * Enqueue an SRv6 encap source address set + */ +enum zebra_dplane_result +dplane_srv6_encap_srcaddr_set(const struct in6_addr *addr, ns_id_t ns_id); + + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index ce5e639..147f5b9 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -310,13 +310,12 @@ void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data) int zebra_evpn_del_macip_for_intf(struct interface *ifp, struct zebra_evpn *zevpn) { - struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + frr_each_safe (if_connected, ifp->connected, c) { struct ipaddr ip; memset(&ip, 0, sizeof(struct ipaddr)); @@ -344,13 +343,12 @@ int zebra_evpn_del_macip_for_intf(struct interface *ifp, int zebra_evpn_add_macip_for_intf(struct interface *ifp, struct zebra_evpn *zevpn) { - struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + frr_each_safe (if_connected, ifp->connected, c) { struct ipaddr ip; if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) @@ -409,13 +407,12 @@ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, int zebra_evpn_advertise_subnet(struct zebra_evpn *zevpn, struct interface *ifp, int advertise) { - struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + frr_each (if_connected, ifp->connected, c) { struct prefix p; memcpy(&p, c->address, sizeof(struct prefix)); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index a5092c6..35d5027 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -52,10 +52,9 @@ static void zebra_evpn_es_get_one_base_evpn(void); static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, struct zebra_evpn *zevpn, bool add); static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp); -static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi); +static void zebra_evpn_local_es_update(struct zebra_if *zif); static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es, const char *caller); -static void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set); static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es, bool resync_dplane); static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es); @@ -1139,7 +1138,7 @@ void zebra_evpn_if_init(struct zebra_if *zif) /* if an es_id and sysmac are already present against the interface * activate it */ - zebra_evpn_local_es_update(zif, &zif->es_info.esi); + zebra_evpn_local_es_update(zif); } /* handle deletion of an access port by removing it from all associated @@ -2250,8 +2249,7 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, /* attach es to interface */ zif->es_info.es = es; - es->df_pref = zif->es_info.df_pref ? zif->es_info.df_pref - : EVPN_MH_DF_PREF_DEFAULT; + es->df_pref = zif->es_info.df_pref; /* attach interface to es */ es->zif = zif; @@ -2402,73 +2400,63 @@ static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es **esp) } } +void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi) +{ + int offset = 0; + int field_bytes = 0; + + /* build 10-byte type-3-ESI - + * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) + */ + field_bytes = 1; + esi->val[offset] = ESI_TYPE_MAC; + offset += field_bytes; + + field_bytes = ETH_ALEN; + memcpy(&esi->val[offset], (uint8_t *)mac, field_bytes); + offset += field_bytes; + + esi->val[offset++] = (uint8_t)(lid >> 16); + esi->val[offset++] = (uint8_t)(lid >> 8); + esi->val[offset++] = (uint8_t)lid; +} + /* A new local es is created when a local-es-id and sysmac is configured * against an interface. */ -static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi) +static void zebra_evpn_local_es_update(struct zebra_if *zif) { struct zebra_evpn_es *old_es = zif->es_info.es; struct zebra_evpn_es *es; + esi_t _esi, *esi; + + if (!zebra_evpn_is_if_es_capable(zif)) + return; + + if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) { + esi = &zif->es_info.esi; + } else if (zif->es_info.lid && !is_zero_mac(&zif->es_info.sysmac)) { + zebra_build_type3_esi(zif->es_info.lid, &zif->es_info.sysmac, + &_esi); + esi = &_esi; + } else { + esi = zero_esi; + } if (old_es && !memcmp(&old_es->esi, esi, sizeof(*esi))) /* dup - nothing to be done */ - return 0; + return; /* release the old_es against the zif */ if (old_es) zebra_evpn_local_es_del(&old_es); es = zebra_evpn_es_find(esi); - if (es) { - /* if it exists against another interface flag an error */ - if (es->zif && es->zif != zif) - return -1; - } else { - /* create new es */ + if (!es) es = zebra_evpn_es_new(esi); - } - memcpy(&zif->es_info.esi, esi, sizeof(*esi)); if (es) zebra_evpn_es_local_info_set(es, zif); - - return 0; -} - -static int zebra_evpn_type3_esi_update(struct zebra_if *zif, uint32_t lid, - struct ethaddr *sysmac) -{ - struct zebra_evpn_es *old_es = zif->es_info.es; - esi_t esi; - int offset = 0; - int field_bytes = 0; - - /* Complete config of the ES-ID bootstraps the ES */ - if (!lid || is_zero_mac(sysmac)) { - /* clear old esi */ - memset(&zif->es_info.esi, 0, sizeof(zif->es_info.esi)); - /* if in ES is attached to zif delete it */ - if (old_es) - zebra_evpn_local_es_del(&old_es); - return 0; - } - - /* build 10-byte type-3-ESI - - * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) - */ - field_bytes = 1; - esi.val[offset] = ESI_TYPE_MAC; - offset += field_bytes; - - field_bytes = ETH_ALEN; - memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes); - offset += field_bytes; - - esi.val[offset++] = (uint8_t)(lid >> 16); - esi.val[offset++] = (uint8_t)(lid >> 8); - esi.val[offset++] = (uint8_t)lid; - - return zebra_evpn_local_es_update(zif, &esi); } int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip) @@ -2673,44 +2661,33 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, } /* sysmac part of a local ESI has changed */ -static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif, - struct ethaddr *sysmac) +void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac) { - int rv; - - rv = zebra_evpn_type3_esi_update(zif, zif->es_info.lid, sysmac); - if (!rv) + if (sysmac) memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr)); + else + memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr)); - return rv; + zebra_evpn_local_es_update(zif); } /* local-ID part of ESI has changed */ -static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) +void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) { - int rv; - - rv = zebra_evpn_type3_esi_update(zif, lid, &zif->es_info.sysmac); - if (!rv) - zif->es_info.lid = lid; + zif->es_info.lid = lid; - return rv; + zebra_evpn_local_es_update(zif); } /* type-0 esi has changed */ -static int zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi) +void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi) { - int rv; - - rv = zebra_evpn_local_es_update(zif, esi); - - /* clear the old es_lid, es_sysmac - type-0 is being set so old - * type-3 params need to be flushed - */ - memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr)); - zif->es_info.lid = 0; + if (esi) + memcpy(&zif->es_info.esi, esi, sizeof(*esi)); + else + memset(&zif->es_info.esi, 0, sizeof(*esi)); - return rv; + zebra_evpn_local_es_update(zif); } void zebra_evpn_es_cleanup(void) @@ -2726,10 +2703,9 @@ void zebra_evpn_es_cleanup(void) } } -static void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref) +void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref) { struct zebra_evpn_es *es; - uint16_t tmp_pref; if (zif->es_info.df_pref == df_pref) return; @@ -2740,13 +2716,10 @@ static void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref) if (!es) return; - tmp_pref = zif->es_info.df_pref ? zif->es_info.df_pref - : EVPN_MH_DF_PREF_DEFAULT; - - if (es->df_pref == tmp_pref) + if (es->df_pref == zif->es_info.df_pref) return; - es->df_pref = tmp_pref; + es->df_pref = zif->es_info.df_pref; /* run df election */ zebra_evpn_es_run_df_election(es, __func__); /* notify bgp */ @@ -2833,7 +2806,7 @@ void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, zebra_evpn_es_br_port_dplane_update(es, __func__); } -static void zebra_evpn_es_bypass_cfg_update(struct zebra_if *zif, bool bypass) +void zebra_evpn_es_bypass_cfg_update(struct zebra_if *zif, bool bypass) { bool old_bypass = !!(zif->es_info.flags & ZIF_CFG_ES_FLAG_BYPASS); @@ -3326,208 +3299,9 @@ void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi) vty_json(vty, json); } -int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp) +void zebra_evpn_mh_if_init(struct zebra_if *zif) { - struct zebra_if *zif = ifp->info; - char buf[ETHER_ADDR_STRLEN]; - bool type_3_esi = false; - char esi_buf[ESI_STR_LEN]; - - if (zif->es_info.lid) { - vty_out(vty, " evpn mh es-id %u\n", zif->es_info.lid); - type_3_esi = true; - } - - if (!is_zero_mac(&zif->es_info.sysmac)) { - vty_out(vty, " evpn mh es-sys-mac %s\n", - prefix_mac2str(&zif->es_info.sysmac, - buf, sizeof(buf))); - type_3_esi = true; - } - - if (!type_3_esi - && memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) - vty_out(vty, " evpn mh es-id %s\n", - esi_to_str(&zif->es_info.esi, esi_buf, sizeof(esi_buf))); - - if (zif->es_info.df_pref) - vty_out(vty, " evpn mh es-df-pref %u\n", zif->es_info.df_pref); - - if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK) - vty_out(vty, " evpn mh uplink\n"); - - return 0; -} - -#include "zebra/zebra_evpn_mh_clippy.c" -/* CLI for setting an ES in bypass mode */ -DEFPY_HIDDEN(zebra_evpn_es_bypass, zebra_evpn_es_bypass_cmd, - "[no] evpn mh bypass", - NO_STR "EVPN\n" EVPN_MH_VTY_STR "set bypass mode\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - - zif = ifp->info; - - if (no) { - zebra_evpn_es_bypass_cfg_update(zif, false); - } else { - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% DF bypass cannot be associated with this interface type\n"); - return CMD_WARNING; - } - zebra_evpn_es_bypass_cfg_update(zif, true); - } - return CMD_SUCCESS; -} - -/* CLI for configuring DF preference part for an ES */ -DEFPY(zebra_evpn_es_pref, zebra_evpn_es_pref_cmd, - "[no$no] evpn mh es-df-pref [(1-65535)$df_pref]", - NO_STR "EVPN\n" EVPN_MH_VTY_STR - "preference value used for DF election\n" - "pref\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - - zif = ifp->info; - - if (no) { - zebra_evpn_es_df_pref_update(zif, 0); - } else { - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% DF preference cannot be associated with this interface type\n"); - return CMD_WARNING; - } - zebra_evpn_es_df_pref_update(zif, df_pref); - } - return CMD_SUCCESS; -} - -/* CLI for setting up sysmac part of ESI on an access port */ -DEFPY(zebra_evpn_es_sys_mac, - zebra_evpn_es_sys_mac_cmd, - "[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]", - NO_STR - "EVPN\n" - EVPN_MH_VTY_STR - "Ethernet segment system MAC\n" - MAC_STR -) -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - int ret = 0; - - zif = ifp->info; - - if (no) { - static struct ethaddr zero_mac; - - ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac); - if (ret == -1) { - vty_out(vty, "%% Failed to clear ES sysmac\n"); - return CMD_WARNING; - } - } else { - - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% ESI cannot be associated with this interface type\n"); - return CMD_WARNING; - } - - if (!mac || is_zero_mac(&mac->eth_addr)) { - vty_out(vty, "%% ES sysmac value is invalid\n"); - return CMD_WARNING; - } - - ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr); - if (ret == -1) { - vty_out(vty, - "%% ESI already exists on a different interface\n"); - return CMD_WARNING; - } - } - return CMD_SUCCESS; -} - -/* CLI for setting up local-ID part of ESI on an access port */ -DEFPY(zebra_evpn_es_id, - zebra_evpn_es_id_cmd, - "[no$no] evpn mh es-id [(1-16777215)$es_lid | NAME$esi_str]", - NO_STR - "EVPN\n" - EVPN_MH_VTY_STR - "Ethernet segment identifier\n" - "local discriminator\n" - "10-byte ID - 00:AA:BB:CC:DD:EE:FF:GG:HH:II\n" -) -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - int ret = 0; - esi_t esi; - - zif = ifp->info; - - if (no) { - if (zif->es_info.lid) - ret = zebra_evpn_es_lid_update(zif, 0); - else if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) - ret = zebra_evpn_es_type0_esi_update(zif, zero_esi); - - if (ret == -1) { - vty_out(vty, - "%% Failed to clear ES local id or ESI name\n"); - return CMD_WARNING; - } - } else { - if (!zebra_evpn_is_if_es_capable(zif)) { - vty_out(vty, - "%% ESI cannot be associated with this interface type\n"); - return CMD_WARNING; - } - - if (esi_str) { - if (!str_to_esi(esi_str, &esi)) { - vty_out(vty, "%% Malformed ESI name\n"); - return CMD_WARNING; - } - ret = zebra_evpn_es_type0_esi_update(zif, &esi); - } else { - if (!es_lid) { - vty_out(vty, - "%% Specify ES local id or ESI name\n"); - return CMD_WARNING; - } - ret = zebra_evpn_es_lid_update(zif, es_lid); - } - - if (ret == -1) { - vty_out(vty, - "%% ESI already exists on a different interface\n"); - return CMD_WARNING; - } - } - return CMD_SUCCESS; -} - -/* CLI for tagging an interface as an uplink */ -DEFPY(zebra_evpn_mh_uplink, zebra_evpn_mh_uplink_cmd, "[no] evpn mh uplink", - NO_STR "EVPN\n" EVPN_MH_VTY_STR "uplink to the VxLAN core\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *zif; - - zif = ifp->info; - zebra_evpn_mh_uplink_cfg_update(zif, no ? false : true); - - return CMD_SUCCESS; + zif->es_info.df_pref = EVPN_MH_DF_PREF_DEFAULT; } void zebra_evpn_mh_json(json_object *json) @@ -3864,7 +3638,7 @@ static void zebra_evpn_mh_uplink_oper_flags_update(struct zebra_if *zif, } } -static void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set) +void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set) { bool old_protodown = zebra_evpn_mh_is_all_uplinks_down(); bool new_protodown; @@ -4087,15 +3861,6 @@ int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off) return 0; } -void zebra_evpn_interface_init(void) -{ - install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); - install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd); - install_element(INTERFACE_NODE, &zebra_evpn_es_pref_cmd); - install_element(INTERFACE_NODE, &zebra_evpn_es_bypass_cmd); - install_element(INTERFACE_NODE, &zebra_evpn_mh_uplink_cmd); -} - void zebra_evpn_mh_init(void) { zrouter.mh_info = XCALLOC(MTYPE_ZMH_INFO, sizeof(*zrouter.mh_info)); diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 59a41d0..34ef79f 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -17,8 +17,7 @@ #include "zebra_vxlan.h" #include "zebra_vxlan_private.h" #include "zebra_nhg.h" - -#define EVPN_MH_VTY_STR "Multihoming\n" +#include "zebra_nb.h" /* Ethernet Segment entry - * - Local and remote ESs are maintained in a global RB tree, @@ -155,7 +154,7 @@ struct zebra_evpn_es_vtep { /* Parameters for DF election */ uint8_t df_alg; - uint32_t df_pref; + uint16_t df_pref; /* XXX - maintain a backpointer to struct zebra_vtep */ }; @@ -336,8 +335,6 @@ extern bool zebra_evpn_es_mac_ref_entry(struct zebra_mac *mac, struct zebra_evpn_es *es); extern bool zebra_evpn_es_mac_ref(struct zebra_mac *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); extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj); extern void zebra_evpn_if_es_print(struct vty *vty, json_object *json, @@ -382,4 +379,16 @@ extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); extern struct zebra_evpn_es_evi * zebra_evpn_es_evi_find(struct zebra_evpn_es *es, struct zebra_evpn *zevpn); +void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi); + +void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac); +void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid); +void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi); + +void zebra_evpn_es_df_pref_update(struct zebra_if *zif, uint16_t df_pref); +void zebra_evpn_es_bypass_cfg_update(struct zebra_if *zif, bool bypass); +void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set); + +void zebra_evpn_mh_if_init(struct zebra_if *zif); + #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index a00d8c9..81705d4 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -6,6 +6,10 @@ #include +#ifdef GNU_LINUX +#include +#endif + #include "hash.h" #include "interface.h" #include "jhash.h" diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 699f3ed..92dc591 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -8,6 +8,10 @@ #include +#ifdef GNU_LINUX +#include +#endif + #include "log.h" #include "libfrr.h" #include "stream.h" diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c index 94308a9..ce5eb6f 100644 --- a/zebra/zebra_fpm_dt.c +++ b/zebra/zebra_fpm_dt.c @@ -22,6 +22,10 @@ */ #include + +#ifdef GNU_LINUX +#include +#endif #include "log.h" #include "vrf.h" diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 1279762..1dd9634 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -11,6 +11,9 @@ #ifdef HAVE_NETLINK +#include +#include + #include "log.h" #include "rib.h" #include "vty.h" diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c index 39fd864..f4241f1 100644 --- a/zebra/zebra_gr.c +++ b/zebra/zebra_gr.c @@ -327,7 +327,7 @@ void zread_client_capabilities(ZAPI_HANDLER_ARGS) return; /* GR only for dynamic clients */ - if (client->proto <= ZEBRA_ROUTE_CONNECT) { + if (client->proto <= ZEBRA_ROUTE_LOCAL) { LOG_GR("%s: GR capabilities for client %s not supported", __func__, zebra_route_string(client->proto)); return; diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 7715eab..8fd373c 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -627,6 +627,8 @@ void zebra_mlag_init(void) void zebra_mlag_terminate(void) { + stream_fifo_free(zrouter.mlag_info.mlag_fifo); + zrouter.mlag_info.mlag_fifo = NULL; } @@ -983,8 +985,7 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, /* No Batching */ stream_putw(s, MLAG_MSG_NO_BATCH); /* Actual Data */ - zebra_fill_protobuf_msg(s, msg->peerlink, - INTERFACE_NAMSIZ); + zebra_fill_protobuf_msg(s, msg->peerlink, IFNAMSIZ); stream_putl(s, msg->my_role); stream_putl(s, msg->peer_state); zebra_mlag_status_update__free_unpacked(msg, NULL); @@ -1032,9 +1033,9 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putl(s, msg->vrf_id); if (msg->owner_id == MLAG_OWNER_INTERFACE) zebra_fill_protobuf_msg(s, msg->intf_name, - INTERFACE_NAMSIZ); + IFNAMSIZ); else - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); zebra_mlag_mroute_add__free_unpacked(msg, NULL); } break; case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL: { @@ -1059,9 +1060,9 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putl(s, msg->vrf_id); if (msg->owner_id == MLAG_OWNER_INTERFACE) zebra_fill_protobuf_msg(s, msg->intf_name, - INTERFACE_NAMSIZ); + IFNAMSIZ); else - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); zebra_mlag_mroute_del__free_unpacked(msg, NULL); } break; case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: { @@ -1083,8 +1084,7 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, /* Actual Data */ for (i = 0; i < Bulk_msg->n_mroute_add; i++) { - if (STREAM_SIZE(s) - < VRF_NAMSIZ + 22 + INTERFACE_NAMSIZ) { + if (STREAM_SIZE(s) < VRF_NAMSIZ + 22 + IFNAMSIZ) { zlog_warn( "We have received more messages than we can parse at this point in time: %zu", Bulk_msg->n_mroute_add); @@ -1103,11 +1103,11 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putc(s, msg->am_i_dual_active); stream_putl(s, msg->vrf_id); if (msg->owner_id == MLAG_OWNER_INTERFACE) - zebra_fill_protobuf_msg( - s, msg->intf_name, - INTERFACE_NAMSIZ); + zebra_fill_protobuf_msg(s, + msg->intf_name, + IFNAMSIZ); else - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); } stream_putw_at(s, length_spot, i + 1); @@ -1134,8 +1134,7 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, /* Actual Data */ for (i = 0; i < Bulk_msg->n_mroute_del; i++) { - if (STREAM_SIZE(s) - < VRF_NAMSIZ + 16 + INTERFACE_NAMSIZ) { + if (STREAM_SIZE(s) < VRF_NAMSIZ + 16 + IFNAMSIZ) { zlog_warn( "We have received more messages than we can parse at this time"); break; @@ -1150,11 +1149,11 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putl(s, msg->owner_id); stream_putl(s, msg->vrf_id); if (msg->owner_id == MLAG_OWNER_INTERFACE) - zebra_fill_protobuf_msg( - s, msg->intf_name, - INTERFACE_NAMSIZ); + zebra_fill_protobuf_msg(s, + msg->intf_name, + IFNAMSIZ); else - stream_put(s, NULL, INTERFACE_NAMSIZ); + stream_put(s, NULL, IFNAMSIZ); } stream_putw_at(s, length_spot, i + 1); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index eac4fcc..15e36ac 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -616,8 +616,9 @@ static int nhlfe_nexthop_active_ipv4(struct zebra_nhlfe *nhlfe, for (match_nh = match->nhe->nhg.nexthop; match_nh; match_nh = match_nh->next) { - if (match->type == ZEBRA_ROUTE_CONNECT - || nexthop->ifindex == match_nh->ifindex) { + if ((match->type == ZEBRA_ROUTE_CONNECT || + match->type == ZEBRA_ROUTE_LOCAL) || + nexthop->ifindex == match_nh->ifindex) { nexthop->ifindex = match_nh->ifindex; return 1; } @@ -659,9 +660,10 @@ static int nhlfe_nexthop_active_ipv6(struct zebra_nhlfe *nhlfe, /* Locate a valid connected route. */ RNODE_FOREACH_RE (rn, match) { - if ((match->type == ZEBRA_ROUTE_CONNECT) - && !CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED) - && CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) + if (((match->type == ZEBRA_ROUTE_CONNECT || + match->type == ZEBRA_ROUTE_LOCAL)) && + !CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED) && + CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) break; } @@ -1182,6 +1184,7 @@ static char *nhlfe2str(const struct zebra_nhlfe *nhlfe, char *buf, int size) break; case NEXTHOP_TYPE_IFINDEX: snprintf(buf, size, "Ifindex: %u", nexthop->ifindex); + break; case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -1769,9 +1772,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) label = dplane_ctx_get_in_label(ctx); - switch (op) { - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: + if (op == DPLANE_OP_LSP_INSTALL || op == DPLANE_OP_LSP_UPDATE) { /* Look for zebra LSP object */ zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); lsp_table = zvrf->lsp_table; @@ -1782,7 +1783,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("LSP ctx %p: in-label %u not found", ctx, dplane_ctx_get_in_label(ctx)); - break; + return; } /* TODO -- Confirm that this result is still 'current' */ @@ -1793,7 +1794,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) flog_warn(EC_ZEBRA_LSP_INSTALL_FAILURE, "LSP Install Failure: in-label %u", lsp->ile.in_label); - break; + return; } /* Update zebra object */ @@ -1814,74 +1815,16 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) ? ZEBRA_SR_POLICY_LABEL_CREATED : ZEBRA_SR_POLICY_LABEL_UPDATED; zebra_sr_policy_label_update(label, update_mode); - break; - - case DPLANE_OP_LSP_DELETE: + } else if (op == DPLANE_OP_LSP_DELETE) { if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE, "LSP Deletion Failure: in-label %u", dplane_ctx_get_in_label(ctx)); - break; + return; } zebra_sr_policy_label_update(label, ZEBRA_SR_POLICY_LABEL_REMOVED); - break; - - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_NONE: - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: - break; - - } /* Switch */ + } } /* @@ -4093,10 +4036,12 @@ void zebra_mpls_turned_on(void) if (!mpls_enabled) { mpls_processq_init(); mpls_enabled = true; - } - hook_register(zserv_client_close, zebra_mpls_cleanup_fecs_for_client); - hook_register(zserv_client_close, zebra_mpls_cleanup_zclient_labels); + hook_register(zserv_client_close, + zebra_mpls_cleanup_fecs_for_client); + hook_register(zserv_client_close, + zebra_mpls_cleanup_zclient_labels); + } } /* @@ -4115,3 +4060,9 @@ void zebra_mpls_init(void) zebra_mpls_turned_on(); } + +void zebra_mpls_terminate(void) +{ + hook_unregister(zserv_client_close, zebra_mpls_cleanup_fecs_for_client); + hook_unregister(zserv_client_close, zebra_mpls_cleanup_zclient_labels); +} diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 7feace5..1ed2f9b 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -400,9 +400,10 @@ void zebra_mpls_init_tables(struct zebra_vrf *zvrf); void zebra_mpls_turned_on(void); /* - * Global MPLS initialization. + * Global MPLS initialization/termination. */ void zebra_mpls_init(void); +void zebra_mpls_terminate(void); /* * MPLS VTY. diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index 4bc676f..f0f2c4b 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -4,9 +4,13 @@ */ #include +#include #ifdef HAVE_NETLINK +#include +#include + #include "zebra/debug.h" #include "zebra/rt.h" #include "zebra/rt_netlink.h" diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 5015f2e..9cbe6a2 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -4,6 +4,8 @@ */ #include +#include +#include #ifdef OPEN_BSD @@ -229,71 +231,18 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) const struct nexthop *nexthop = NULL; unsigned int nexthop_num = 0; int action; + enum dplane_op_e op; - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_LSP_DELETE: + op = dplane_ctx_get_op(ctx); + + if (op == DPLANE_OP_LSP_DELETE) action = RTM_DELETE; - break; - case DPLANE_OP_LSP_INSTALL: + else if (op == DPLANE_OP_LSP_INSTALL) action = RTM_ADD; - break; - case DPLANE_OP_LSP_UPDATE: + else if (op == DPLANE_OP_LSP_UPDATE) action = RTM_CHANGE; - break; - case DPLANE_OP_NONE: - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: + else return -1; - } head = dplane_ctx_get_nhlfe_list(ctx); frr_each(nhlfe_list_const, head, nhlfe) { @@ -443,69 +392,14 @@ static enum zebra_dplane_result kmpw_uninstall(struct zebra_dplane_ctx *ctx) enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + enum dplane_op_e op; - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_PW_INSTALL: + op = dplane_ctx_get_op(ctx); + + if (op == DPLANE_OP_PW_INSTALL) result = kmpw_install(ctx); - break; - case DPLANE_OP_PW_UNINSTALL: + else if (op == DPLANE_OP_PW_UNINSTALL) result = kmpw_uninstall(ctx); - break; - case DPLANE_OP_NONE: - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: - break; - } return result; } diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index e64e700..fd09e6b 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -22,6 +22,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/redistribute.h" #include "zebra/zebra_routemap.h" +#include "zebra/label_manager.h" static int zebra_mpls_transit_lsp(struct vty *vty, int add_cmd, const char *inlabel_str, const char *gate_str, @@ -270,6 +271,8 @@ static int zebra_mpls_config(struct vty *vty) write += zebra_mpls_write_lsp_config(vty, zvrf); write += zebra_mpls_write_fec_config(vty, zvrf); write += zebra_mpls_write_label_block_config(vty, zvrf); + write += lm_write_label_block_config_call(vty, zvrf); + return write; } diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index a93dbbb..e1ca5ec 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -10,9 +10,20 @@ #include "libfrr.h" #include "zebra_nb.h" +const char *features[] = { +#if HAVE_BFDD == 0 + "ptm-bfd", +#endif +#if defined(HAVE_RTADV) + "ipv6-router-advertisements", +#endif + NULL +}; + /* clang-format off */ const struct frr_yang_module_info frr_zebra_info = { .name = "frr-zebra", + .features = features, .nodes = { { .xpath = "/frr-zebra:zebra/mcast-rpf-lookup", @@ -79,6 +90,20 @@ const struct frr_yang_module_info frr_zebra_info = { .modify = zebra_dplane_queue_limit_modify, } }, +#if HAVE_BFDD == 0 + { + .xpath = "/frr-zebra:zebra/ptm-enable", + .cbs = { + .modify = zebra_ptm_enable_modify, + } + }, +#endif + { + .xpath = "/frr-zebra:zebra/route-map-delay", + .cbs = { + .modify = zebra_route_map_delay_modify, + } + }, { .xpath = "/frr-zebra:zebra/debugs/debug-events", .cbs = { @@ -290,24 +315,38 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs", + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-addrs", + .cbs = { + .create = lib_interface_zebra_ipv4_addrs_create, + .destroy = lib_interface_zebra_ipv4_addrs_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-addrs/label", + .cbs = { + .modify = lib_interface_zebra_ipv4_addrs_label_modify, + .destroy = lib_interface_zebra_ipv4_addrs_label_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-p2p-addrs", .cbs = { - .create = lib_interface_zebra_ip_addrs_create, - .destroy = lib_interface_zebra_ip_addrs_destroy, + .create = lib_interface_zebra_ipv4_p2p_addrs_create, + .destroy = lib_interface_zebra_ipv4_p2p_addrs_destroy, } }, { - .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/label", + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv4-p2p-addrs/label", .cbs = { - .modify = lib_interface_zebra_ip_addrs_label_modify, - .destroy = lib_interface_zebra_ip_addrs_label_destroy, + .modify = lib_interface_zebra_ipv4_p2p_addrs_label_modify, + .destroy = lib_interface_zebra_ipv4_p2p_addrs_label_destroy, } }, { - .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/ip4-peer", + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-addrs", .cbs = { - .modify = lib_interface_zebra_ip_addrs_ip4_peer_modify, - .destroy = lib_interface_zebra_ip_addrs_ip4_peer_destroy, + .create = lib_interface_zebra_ipv6_addrs_create, + .destroy = lib_interface_zebra_ipv6_addrs_destroy, } }, { @@ -321,14 +360,13 @@ const struct frr_yang_module_info frr_zebra_info = { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-detect", .cbs = { .modify = lib_interface_zebra_link_detect_modify, - .destroy = lib_interface_zebra_link_detect_destroy, } }, { - .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/shutdown", + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/enabled", .cbs = { - .modify = lib_interface_zebra_shutdown_modify, - .destroy = lib_interface_zebra_shutdown_destroy, + .modify = lib_interface_zebra_enabled_modify, + .destroy = lib_interface_zebra_enabled_destroy, } }, { @@ -346,10 +384,66 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/bandwidth", + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params", .cbs = { - .modify = lib_interface_zebra_bandwidth_modify, - .destroy = lib_interface_zebra_bandwidth_destroy, + .create = lib_interface_zebra_link_params_create, + .destroy = lib_interface_zebra_link_params_destroy, + .apply_finish = lib_interface_zebra_link_params_apply_finish, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/metric", + .cbs = { + .modify = lib_interface_zebra_link_params_metric_modify, + .destroy = lib_interface_zebra_link_params_metric_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/max-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_max_bandwidth_modify, + .destroy = lib_interface_zebra_link_params_max_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/max-reservable-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_max_reservable_bandwidth_modify, + .destroy = lib_interface_zebra_link_params_max_reservable_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/unreserved-bandwidths/unreserved-bandwidth", + .cbs = { + .create = lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_create, + .destroy = lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/unreserved-bandwidths/unreserved-bandwidth/unreserved-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_unreserved_bandwidth_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/residual-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_residual_bandwidth_modify, + .destroy = lib_interface_zebra_link_params_residual_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/available-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_available_bandwidth_modify, + .destroy = lib_interface_zebra_link_params_available_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/utilized-bandwidth", + .cbs = { + .modify = lib_interface_zebra_link_params_utilized_bandwidth_modify, + .destroy = lib_interface_zebra_link_params_utilized_bandwidth_destroy, } }, { @@ -357,13 +451,13 @@ const struct frr_yang_module_info frr_zebra_info = { .cbs = { .modify = lib_interface_zebra_legacy_admin_group_modify, .destroy = lib_interface_zebra_legacy_admin_group_destroy, - .cli_show = cli_show_legacy_admin_group, }, }, { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities", .cbs = { - .cli_show = cli_show_affinity, + .create = lib_interface_zebra_affinities_create, + .destroy = lib_interface_zebra_affinities_destroy, }, }, { @@ -377,9 +471,289 @@ const struct frr_yang_module_info frr_zebra_info = { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/affinity-mode", .cbs = { .modify = lib_interface_zebra_affinity_mode_modify, - .cli_show = cli_show_affinity_mode, }, }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor", + .cbs = { + .create = lib_interface_zebra_link_params_neighbor_create, + .destroy = lib_interface_zebra_link_params_neighbor_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor/remote-as", + .cbs = { + .modify = lib_interface_zebra_link_params_neighbor_remote_as_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor/ipv4-remote-id", + .cbs = { + .modify = lib_interface_zebra_link_params_neighbor_ipv4_remote_id_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/delay", + .cbs = { + .modify = lib_interface_zebra_link_params_delay_modify, + .destroy = lib_interface_zebra_link_params_delay_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay", + .cbs = { + .create = lib_interface_zebra_link_params_min_max_delay_create, + .destroy = lib_interface_zebra_link_params_min_max_delay_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay/delay-min", + .cbs = { + .modify = lib_interface_zebra_link_params_min_max_delay_delay_min_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay/delay-max", + .cbs = { + .modify = lib_interface_zebra_link_params_min_max_delay_delay_max_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/delay-variation", + .cbs = { + .modify = lib_interface_zebra_link_params_delay_variation_modify, + .destroy = lib_interface_zebra_link_params_delay_variation_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-params/packet-loss", + .cbs = { + .modify = lib_interface_zebra_link_params_packet_loss_modify, + .destroy = lib_interface_zebra_link_params_packet_loss_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0", + .cbs = { + .create = lib_interface_zebra_evpn_mh_type_0_create, + .destroy = lib_interface_zebra_evpn_mh_type_0_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_0_esi_modify, + .destroy = lib_interface_zebra_evpn_mh_type_0_esi_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3", + .cbs = { + .create = lib_interface_zebra_evpn_mh_type_3_create, + .destroy = lib_interface_zebra_evpn_mh_type_3_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_3_system_mac_modify, + .destroy = lib_interface_zebra_evpn_mh_type_3_system_mac_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify, + .destroy = lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/df-preference", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_df_preference_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/bypass", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_bypass_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/uplink", + .cbs = { + .modify = lib_interface_zebra_evpn_mh_uplink_modify, + } + }, +#if defined(HAVE_RTADV) + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/send-advertisements", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_send_advertisements_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/max-rtr-adv-interval", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_max_rtr_adv_interval_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/managed-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_managed_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/other-config-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_other_config_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_home_agent_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/link-mtu", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_link_mtu_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/reachable-time", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_reachable_time_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/retrans-timer", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_retrans_timer_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/cur-hop-limit", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_default_lifetime_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_default_lifetime_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/fast-retransmit", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_fast_retransmit_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/advertisement-interval-option", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_advertisement_interval_option_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-preference", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-router-preference", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_default_router_preference_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix", + .cbs = { + .create = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_create, + .destroy = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain", + .cbs = { + .create = lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_create, + .destroy = lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain/lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/valid-lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_valid_lifetime_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/on-link-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_on_link_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/preferred-lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_preferred_lifetime_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/autonomous-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_autonomous_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/router-address-flag", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_router_address_flag_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address", + .cbs = { + .create = lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_create, + .destroy = lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address/lifetime", + .cbs = { + .modify = lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_modify, + .destroy = lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_destroy, + } + }, +#endif /* defined(HAVE_RTADV) */ +#if HAVE_BFDD == 0 + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ptm-enable", + .cbs = { + .modify = lib_interface_zebra_ptm_enable_modify, + } + }, +#endif { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/up-count", .cbs = { @@ -428,12 +802,88 @@ const struct frr_yang_module_info frr_zebra_info = { .get_elem = lib_interface_zebra_state_mcast_group_get_elem, } }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/router-id", + .cbs = { + .modify = lib_vrf_zebra_router_id_modify, + .destroy = lib_vrf_zebra_router_id_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-router-id", + .cbs = { + .modify = lib_vrf_zebra_ipv6_router_id_modify, + .destroy = lib_vrf_zebra_ipv6_router_id_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-protocol", + .cbs = { + .create = lib_vrf_zebra_filter_protocol_create, + .destroy = lib_vrf_zebra_filter_protocol_destroy, + .apply_finish = lib_vrf_zebra_filter_protocol_apply_finish, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-protocol/route-map", + .cbs = { + .modify = lib_vrf_zebra_filter_protocol_route_map_modify, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-nht", + .cbs = { + .create = lib_vrf_zebra_filter_nht_create, + .destroy = lib_vrf_zebra_filter_nht_destroy, + .apply_finish = lib_vrf_zebra_filter_nht_apply_finish, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/filter-nht/route-map", + .cbs = { + .modify = lib_vrf_zebra_filter_nht_route_map_modify, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/resolve-via-default", + .cbs = { + .modify = lib_vrf_zebra_resolve_via_default_modify, + .destroy = lib_vrf_zebra_resolve_via_default_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-resolve-via-default", + .cbs = { + .modify = lib_vrf_zebra_ipv6_resolve_via_default_modify, + .destroy = lib_vrf_zebra_ipv6_resolve_via_default_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range", + .cbs = { + .create = lib_vrf_zebra_netns_table_range_create, + .destroy = lib_vrf_zebra_netns_table_range_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range/start", + .cbs = { + .modify = lib_vrf_zebra_netns_table_range_start_modify, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range/end", + .cbs = { + .modify = lib_vrf_zebra_netns_table_range_end_modify, + } + }, { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib", .cbs = { .get_next = lib_vrf_zebra_ribs_rib_get_next, .get_keys = lib_vrf_zebra_ribs_rib_get_keys, .lookup_entry = lib_vrf_zebra_ribs_rib_lookup_entry, + .lookup_next = lib_vrf_zebra_ribs_rib_lookup_next, } }, { @@ -454,6 +904,7 @@ const struct frr_yang_module_info frr_zebra_info = { .get_next = lib_vrf_zebra_ribs_rib_route_get_next, .get_keys = lib_vrf_zebra_ribs_rib_route_get_keys, .lookup_entry = lib_vrf_zebra_ribs_rib_route_lookup_entry, + .lookup_next = lib_vrf_zebra_ribs_rib_route_lookup_next, } }, { diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index 80d2aaa..d7cf5f4 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -44,6 +44,10 @@ int zebra_import_kernel_table_route_map_destroy( int zebra_allow_external_route_update_create(struct nb_cb_create_args *args); int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args); int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args); +#if HAVE_BFDD == 0 +int zebra_ptm_enable_modify(struct nb_cb_modify_args *args); +#endif +int zebra_route_map_delay_modify(struct nb_cb_modify_args *args); int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args); int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args); int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args); @@ -80,31 +84,191 @@ int zebra_debugs_debug_dplane_detail_modify(struct nb_cb_modify_args *args); int zebra_debugs_debug_dplane_detail_destroy(struct nb_cb_destroy_args *args); int zebra_debugs_debug_mlag_modify(struct nb_cb_modify_args *args); int zebra_debugs_debug_mlag_destroy(struct nb_cb_destroy_args *args); -int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args); -int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args); -int lib_interface_zebra_ip_addrs_label_modify(struct nb_cb_modify_args *args); -int lib_interface_zebra_ip_addrs_label_destroy(struct nb_cb_destroy_args *args); -int lib_interface_zebra_ip_addrs_ip4_peer_modify( +int lib_interface_zebra_ipv4_addrs_create(struct nb_cb_create_args *args); +int lib_interface_zebra_ipv4_addrs_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv4_addrs_label_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv4_addrs_label_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv4_p2p_addrs_create(struct nb_cb_create_args *args); +int lib_interface_zebra_ipv4_p2p_addrs_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv4_p2p_addrs_label_modify( struct nb_cb_modify_args *args); -int lib_interface_zebra_ip_addrs_ip4_peer_destroy( +int lib_interface_zebra_ipv4_p2p_addrs_label_destroy( struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_addrs_create(struct nb_cb_create_args *args); +int lib_interface_zebra_ipv6_addrs_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_multicast_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_multicast_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_link_detect_modify(struct nb_cb_modify_args *args); -int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args); -int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args); -int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_enabled_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_enabled_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_mpls_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_mpls_destroy(struct nb_cb_destroy_args *args); -int lib_interface_zebra_legacy_admin_group_modify( +int lib_interface_zebra_link_params_create(struct nb_cb_create_args *args); +int lib_interface_zebra_link_params_destroy(struct nb_cb_destroy_args *args); +void lib_interface_zebra_link_params_apply_finish( + struct nb_cb_apply_finish_args *args); +int lib_interface_zebra_link_params_metric_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_metric_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_max_bandwidth_modify( struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_max_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_max_reservable_bandwidth_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_max_reservable_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_create( + struct nb_cb_create_args *args); +void lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_unreserved_bandwidth_modify( + struct nb_cb_modify_args *args); +void lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_unreserved_bandwidth_cli_write( + struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +int lib_interface_zebra_link_params_residual_bandwidth_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_residual_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_available_bandwidth_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_available_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_utilized_bandwidth_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_utilized_bandwidth_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_legacy_admin_group_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_legacy_admin_group_destroy( struct nb_cb_destroy_args *args); +int lib_interface_zebra_affinities_create(struct nb_cb_create_args *args); +int lib_interface_zebra_affinities_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_affinity_create(struct nb_cb_create_args *args); int lib_interface_zebra_affinity_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_affinity_mode_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_neighbor_create( + struct nb_cb_create_args *args); +int lib_interface_zebra_link_params_neighbor_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_neighbor_remote_as_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_neighbor_ipv4_remote_id_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_delay_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_delay_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_min_max_delay_create( + struct nb_cb_create_args *args); +int lib_interface_zebra_link_params_min_max_delay_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_min_max_delay_delay_min_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_min_max_delay_delay_max_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_delay_variation_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_delay_variation_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_params_packet_loss_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_link_params_packet_loss_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_0_create(struct nb_cb_create_args *args); +int lib_interface_zebra_evpn_mh_type_0_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_0_esi_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_3_create(struct nb_cb_create_args *args); +int lib_interface_zebra_evpn_mh_type_3_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_3_system_mac_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_evpn_mh_df_preference_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_bypass_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_evpn_mh_uplink_modify(struct nb_cb_modify_args *args); +#if defined(HAVE_RTADV) +int lib_interface_zebra_ipv6_router_advertisements_send_advertisements_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_max_rtr_adv_interval_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_managed_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_other_config_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_home_agent_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_link_mtu_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_reachable_time_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_retrans_timer_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_default_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_default_lifetime_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_fast_retransmit_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_advertisement_interval_option_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_default_router_preference_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_create( + struct nb_cb_create_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_valid_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_on_link_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_preferred_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_autonomous_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_router_address_flag_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_create( + struct nb_cb_create_args *args); +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_create( + struct nb_cb_create_args *args); +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_destroy( + struct nb_cb_destroy_args *args); +#endif /* defined(HAVE_RTADV) */ +#if HAVE_BFDD == 0 +int lib_interface_zebra_ptm_enable_modify(struct nb_cb_modify_args *args); +#endif struct yang_data * lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args); struct yang_data * @@ -121,10 +285,35 @@ struct yang_data *lib_interface_zebra_state_remote_vtep_get_elem( struct nb_cb_get_elem_args *args); struct yang_data *lib_interface_zebra_state_mcast_group_get_elem( struct nb_cb_get_elem_args *args); +int lib_vrf_zebra_router_id_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_router_id_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_ipv6_router_id_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_ipv6_router_id_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_filter_protocol_create(struct nb_cb_create_args *args); +int lib_vrf_zebra_filter_protocol_destroy(struct nb_cb_destroy_args *args); +void lib_vrf_zebra_filter_protocol_apply_finish( + struct nb_cb_apply_finish_args *args); +int lib_vrf_zebra_filter_protocol_route_map_modify( + struct nb_cb_modify_args *args); +int lib_vrf_zebra_filter_nht_create(struct nb_cb_create_args *args); +int lib_vrf_zebra_filter_nht_destroy(struct nb_cb_destroy_args *args); +void lib_vrf_zebra_filter_nht_apply_finish(struct nb_cb_apply_finish_args *args); +int lib_vrf_zebra_filter_nht_route_map_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_resolve_via_default_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_resolve_via_default_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_ipv6_resolve_via_default_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_ipv6_resolve_via_default_destroy( + struct nb_cb_destroy_args *args); +int lib_vrf_zebra_netns_table_range_create(struct nb_cb_create_args *args); +int lib_vrf_zebra_netns_table_range_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_netns_table_range_start_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_netns_table_range_end_modify(struct nb_cb_modify_args *args); const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args); const void * lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args); +const void * +lib_vrf_zebra_ribs_rib_lookup_next(struct nb_cb_lookup_entry_args *args); struct yang_data * lib_vrf_zebra_ribs_rib_afi_safi_name_get_elem(struct nb_cb_get_elem_args *args); struct yang_data * @@ -134,6 +323,8 @@ lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args); const void * lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_lookup_next(struct nb_cb_lookup_entry_args *args); struct yang_data * lib_vrf_zebra_ribs_rib_route_prefix_get_elem(struct nb_cb_get_elem_args *args); struct yang_data *lib_vrf_zebra_ribs_rib_route_protocol_get_elem( diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index 5ea0311..5cb9985 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -23,6 +23,12 @@ #include "zebra/debug.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_ptm.h" +#include "zebra/router-id.h" +#include "zebra/zebra_routemap.h" +#include "zebra/zebra_rnh.h" +#include "zebra/table_manager.h" /* * XPath: /frr-zebra:zebra/mcast-rpf-lookup @@ -264,6 +270,43 @@ int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args) return NB_OK; } +#if HAVE_BFDD == 0 +/* + * XPath: /frr-zebra:zebra/ptm-enable + */ +int zebra_ptm_enable_modify(struct nb_cb_modify_args *args) +{ + bool ptm; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ptm = yang_dnode_get_bool(args->dnode, NULL); + + if (ptm) + zebra_global_ptm_enable(); + else + zebra_global_ptm_disable(); + + return NB_OK; +} +#endif + +/* + * XPath: /frr-zebra:zebra/route-map-delay + */ +int zebra_route_map_delay_modify(struct nb_cb_modify_args *args) +{ + uint32_t delay = yang_dnode_get_uint32(args->dnode, NULL); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + zebra_route_map_set_delay_timer(delay); + + return NB_OK; +} + /* * XPath: /frr-zebra:zebra/debugs/debug-events */ @@ -823,28 +866,26 @@ int zebra_debugs_debug_mlag_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv4-addrs */ -int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) +int lib_interface_zebra_ipv4_addrs_create(struct nb_cb_create_args *args) { struct interface *ifp; - struct prefix prefix; + struct prefix p; + const char *label = NULL; - // addr_family = yang_dnode_get_enum(dnode, "./address-family"); - yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); - apply_mask(&prefix); + p.family = AF_INET; + yang_dnode_get_ipv4(&p.u.prefix4, args->dnode, "ip"); + p.prefixlen = yang_dnode_get_uint8(args->dnode, "prefix-length"); + + if (yang_dnode_exists(args->dnode, "label")) + label = yang_dnode_get_string(args->dnode, "label"); switch (args->event) { case NB_EV_VALIDATE: - if (prefix.family == AF_INET - && ipv4_martian(&prefix.u.prefix4)) { - snprintfrr(args->errmsg, args->errmsg_len, - "invalid address %pFX", &prefix); - return NB_ERR_VALIDATION; - } else if (prefix.family == AF_INET6 - && ipv6_martian(&prefix.u.prefix6)) { + if (ipv4_martian(&p.u.prefix4)) { snprintfrr(args->errmsg, args->errmsg_len, - "invalid address %pFX", &prefix); + "invalid address %pFX", &p); return NB_ERR_VALIDATION; } break; @@ -853,65 +894,105 @@ int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - if (prefix.family == AF_INET) - if_ip_address_install(ifp, &prefix, NULL, NULL); - else if (prefix.family == AF_INET6) - if_ipv6_address_install(ifp, &prefix, NULL); + if_ip_address_install(ifp, &p, label, NULL); + /* set something for checking on label modify */ + nb_running_set_entry(args->dnode, (void *)0x1); break; } return NB_OK; } -int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) +int lib_interface_zebra_ipv4_addrs_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - struct prefix prefix; - struct connected *ifc; + struct prefix p; - yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); - apply_mask(&prefix); + p.family = AF_INET; + yang_dnode_get_ipv4(&p.u.prefix4, args->dnode, "ip"); + p.prefixlen = yang_dnode_get_uint8(args->dnode, "prefix-length"); switch (args->event) { case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nb_running_unset_entry(args->dnode); + ifp = nb_running_get_entry(args->dnode, NULL, false); - if (!ifp) - return NB_OK; - - if (prefix.family == AF_INET) { - /* Check current interface address. */ - ifc = connected_check_ptp(ifp, &prefix, NULL); - if (!ifc) { - snprintf(args->errmsg, args->errmsg_len, - "interface %s Can't find address\n", - ifp->name); - return NB_ERR_VALIDATION; - } - } else if (prefix.family == AF_INET6) { - /* Check current interface address. */ - ifc = connected_check(ifp, &prefix); - if (!ifc) { - snprintf(args->errmsg, args->errmsg_len, - "interface can't find address %s", - ifp->name); - return NB_ERR_VALIDATION; - } - } else - return NB_ERR_VALIDATION; + if_ip_address_uninstall(ifp, &p, NULL); + break; + } + + return NB_OK; +} - /* This is not configured address. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv4-addrs/label + */ +int lib_interface_zebra_ipv4_addrs_label_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + if (nb_running_get_entry_non_rec(lyd_parent(args->dnode), NULL, + false)) { snprintf(args->errmsg, args->errmsg_len, - "interface %s not configured", ifp->name); + "Changing label is not allowed"); return NB_ERR_VALIDATION; } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int lib_interface_zebra_ipv4_addrs_label_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + snprintf(args->errmsg, args->errmsg_len, + "Removing label is not allowed"); + return NB_ERR_VALIDATION; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv4-p2p-addrs + */ +int lib_interface_zebra_ipv4_p2p_addrs_create(struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct prefix p, pp; + const char *label = NULL; + + p.family = AF_INET; + yang_dnode_get_ipv4(&p.u.prefix4, args->dnode, "ip"); + p.prefixlen = 32; + + pp.family = AF_INET; + yang_dnode_get_ipv4(&pp.u.prefix4, args->dnode, "peer-ip"); + pp.prefixlen = yang_dnode_get_uint8(args->dnode, "peer-prefix-length"); + + if (yang_dnode_exists(args->dnode, "label")) + label = yang_dnode_get_string(args->dnode, "label"); - /* This is not real address or interface is not active. */ - if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) - || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - listnode_delete(ifp->connected, ifc); - connected_free(&ifc); + switch (args->event) { + case NB_EV_VALIDATE: + if (ipv4_martian(&p.u.prefix4)) { + snprintfrr(args->errmsg, args->errmsg_len, + "invalid address %pFX", &p); return NB_ERR_VALIDATION; } break; @@ -920,7 +1001,39 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - if_ip_address_uinstall(ifp, &prefix); + if_ip_address_install(ifp, &p, label, &pp); + + /* set something for checking on label modify */ + nb_running_set_entry(args->dnode, (void *)0x1); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_ipv4_p2p_addrs_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct prefix p, pp; + + p.family = AF_INET; + yang_dnode_get_ipv4(&p.u.prefix4, args->dnode, "ip"); + p.prefixlen = 32; + + pp.family = AF_INET; + yang_dnode_get_ipv4(&pp.u.prefix4, args->dnode, "peer-ip"); + pp.prefixlen = yang_dnode_get_uint8(args->dnode, "peer-prefix-length"); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nb_running_unset_entry(args->dnode); + + ifp = nb_running_get_entry(args->dnode, NULL, false); + if_ip_address_uninstall(ifp, &p, &pp); break; } @@ -928,30 +1041,39 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/label + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv4-p2p-addrs/label */ -int lib_interface_zebra_ip_addrs_label_modify(struct nb_cb_modify_args *args) +int lib_interface_zebra_ipv4_p2p_addrs_label_modify(struct nb_cb_modify_args *args) { switch (args->event) { case NB_EV_VALIDATE: + if (nb_running_get_entry_non_rec(lyd_parent(args->dnode), NULL, + false)) { + snprintf(args->errmsg, args->errmsg_len, + "Changing label is not allowed"); + return NB_ERR_VALIDATION; + } + break; case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_APPLY: - /* TODO: implement me. */ break; } return NB_OK; } -int lib_interface_zebra_ip_addrs_label_destroy(struct nb_cb_destroy_args *args) +int lib_interface_zebra_ipv4_p2p_addrs_label_destroy( + struct nb_cb_destroy_args *args) { switch (args->event) { case NB_EV_VALIDATE: + snprintf(args->errmsg, args->errmsg_len, + "Removing label is not allowed"); + return NB_ERR_VALIDATION; case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_APPLY: - /* TODO: implement me. */ break; } @@ -959,31 +1081,54 @@ int lib_interface_zebra_ip_addrs_label_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/ip4-peer + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-addrs */ -int lib_interface_zebra_ip_addrs_ip4_peer_modify(struct nb_cb_modify_args *args) +int lib_interface_zebra_ipv6_addrs_create(struct nb_cb_create_args *args) { + struct interface *ifp; + struct prefix p; + + p.family = AF_INET6; + yang_dnode_get_ipv6(&p.u.prefix6, args->dnode, "ip"); + p.prefixlen = yang_dnode_get_uint8(args->dnode, "prefix-length"); + switch (args->event) { case NB_EV_VALIDATE: + if (ipv6_martian(&p.u.prefix6)) { + snprintfrr(args->errmsg, args->errmsg_len, + "invalid address %pFX", &p); + return NB_ERR_VALIDATION; + } + break; case NB_EV_PREPARE: case NB_EV_ABORT: + break; case NB_EV_APPLY: - /* TODO: implement me. */ + ifp = nb_running_get_entry(args->dnode, NULL, true); + if_ipv6_address_install(ifp, &p); break; } return NB_OK; } -int lib_interface_zebra_ip_addrs_ip4_peer_destroy( - struct nb_cb_destroy_args *args) +int lib_interface_zebra_ipv6_addrs_destroy(struct nb_cb_destroy_args *args) { + struct interface *ifp; + struct prefix p; + + p.family = AF_INET6; + yang_dnode_get_ipv6(&p.u.prefix6, args->dnode, "ip"); + p.prefixlen = yang_dnode_get_uint8(args->dnode, "prefix-length"); + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: + break; case NB_EV_APPLY: - /* TODO: implement me. */ + ifp = nb_running_get_entry(args->dnode, NULL, false); + if_ipv6_address_uninstall(ifp, &p); break; } @@ -999,10 +1144,14 @@ int lib_interface_zebra_multicast_modify(struct nb_cb_modify_args *args) return NB_OK; struct interface *ifp; + bool multicast = yang_dnode_get_bool(args->dnode, NULL); ifp = nb_running_get_entry(args->dnode, NULL, true); - if_multicast_set(ifp); + if (multicast) + if_multicast_set(ifp); + else + if_multicast_unset(ifp); return NB_OK; } @@ -1013,10 +1162,12 @@ int lib_interface_zebra_multicast_destroy(struct nb_cb_destroy_args *args) return NB_OK; struct interface *ifp; + struct zebra_if *zif; ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; - if_multicast_unset(ifp); + zif->multicast = IF_ZEBRA_DATA_UNSPEC; return NB_OK; } @@ -1033,23 +1184,7 @@ int lib_interface_zebra_link_detect_modify(struct nb_cb_modify_args *args) bool link_detect; ifp = nb_running_get_entry(args->dnode, NULL, true); - link_detect = yang_dnode_get_bool(args->dnode, "./link-detect"); - - if_linkdetect(ifp, link_detect); - - return NB_OK; -} - -int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args) -{ - if (args->event != NB_EV_APPLY) - return NB_OK; - - struct interface *ifp; - bool link_detect; - - ifp = nb_running_get_entry(args->dnode, NULL, true); - link_detect = yang_dnode_get_bool(args->dnode, "./link-detect"); + link_detect = yang_dnode_get_bool(args->dnode, NULL); if_linkdetect(ifp, link_detect); @@ -1057,32 +1192,39 @@ int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: /frr-interface:lib/interface/frr-zebra:zebra/shutdown + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/enabled */ -int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args) +int lib_interface_zebra_enabled_modify(struct nb_cb_modify_args *args) { if (args->event != NB_EV_APPLY) return NB_OK; struct interface *ifp; + bool enabled; ifp = nb_running_get_entry(args->dnode, NULL, true); + enabled = yang_dnode_get_bool(args->dnode, NULL); - if_shutdown(ifp); + if (enabled) + if_no_shutdown(ifp); + else + if_shutdown(ifp); return NB_OK; } -int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args) +int lib_interface_zebra_enabled_destroy(struct nb_cb_destroy_args *args) { if (args->event != NB_EV_APPLY) return NB_OK; struct interface *ifp; + struct zebra_if *zif; ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; - if_no_shutdown(ifp); + zif->shutdown = IF_ZEBRA_DATA_UNSPEC; return NB_OK; } @@ -1143,7 +1285,7 @@ int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args) uint32_t bandwidth; ifp = nb_running_get_entry(args->dnode, NULL, true); - bandwidth = yang_dnode_get_uint32(args->dnode, "./bandwidth"); + bandwidth = yang_dnode_get_uint32(args->dnode, NULL); ifp->bandwidth = bandwidth; @@ -1173,391 +1315,2524 @@ int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: - * /frr-interface:lib/interface/frr-zebra:zebra/link-params/legacy-admin-group + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params */ -int lib_interface_zebra_legacy_admin_group_modify( - struct nb_cb_modify_args *args) +int lib_interface_zebra_link_params_create(struct nb_cb_create_args *args) { struct interface *ifp; - struct if_link_params *iflp; - uint32_t admin_group_value; + + if (args->event != NB_EV_APPLY) + return NB_OK; ifp = nb_running_get_entry(args->dnode, NULL, true); - admin_group_value = yang_dnode_get_uint32(args->dnode, "."); + if_link_params_enable(ifp); - if (!ifp) - return NB_ERR_RESOURCE; + /* + * The interface is updated in the apply_finish callback after all + * parameters are set in the corresponding callbacks. + */ - iflp = if_link_params_get(ifp); + return NB_OK; +} - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (!iflp) - iflp = if_link_params_enable(ifp); +int lib_interface_zebra_link_params_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; - iflp->admin_grp = admin_group_value; - SET_PARAM(iflp, LP_ADM_GRP); + if (args->event != NB_EV_APPLY) + return NB_OK; - admin_group_clear(&iflp->ext_admin_grp); - UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); + ifp = nb_running_get_entry(args->dnode, NULL, true); + if_link_params_free(ifp); + if (if_is_operative(ifp)) + zebra_interface_parameters_update(ifp); - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - break; - } return NB_OK; } -int lib_interface_zebra_legacy_admin_group_destroy( - struct nb_cb_destroy_args *args) +void lib_interface_zebra_link_params_apply_finish( + struct nb_cb_apply_finish_args *args) { struct interface *ifp; - struct if_link_params *iflp; ifp = nb_running_get_entry(args->dnode, NULL, true); + if (if_is_operative(ifp)) + zebra_interface_parameters_update(ifp); +} - if (!ifp) - return NB_ERR_RESOURCE; +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/metric + */ +int lib_interface_zebra_link_params_metric_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t metric; - iflp = if_link_params_get(ifp); + if (args->event != NB_EV_APPLY) + return NB_OK; - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (!iflp) - iflp = if_link_params_enable(ifp); + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + metric = yang_dnode_get_uint32(args->dnode, NULL); - iflp->admin_grp = 0; - UNSET_PARAM(iflp, LP_ADM_GRP); + link_param_cmd_set_uint32(ifp, &iflp->te_metric, LP_TE_METRIC, metric); - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - break; - } return NB_OK; } -/* - * XPath: - * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity - */ -int lib_interface_zebra_affinity_create(struct nb_cb_create_args *args) +int lib_interface_zebra_link_params_metric_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - const char *affname; - struct if_link_params *iflp; - struct affinity_map *affmap; - enum affinity_mode affinity_mode; + if (args->event != NB_EV_APPLY) + return NB_OK; ifp = nb_running_get_entry(args->dnode, NULL, true); - affname = yang_dnode_get_string(args->dnode, "."); - affinity_mode = yang_dnode_get_enum(args->dnode, "../../affinity-mode"); - if (!ifp) - return NB_ERR_RESOURCE; + link_param_cmd_unset(ifp, LP_TE_METRIC); - affmap = affinity_map_get(affname); - iflp = if_link_params_get(ifp); + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/max-bandwidth + */ +int lib_interface_zebra_link_params_max_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + float max_bw, res_bw, ava_bw, use_bw; + + max_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); switch (args->event) { case NB_EV_VALIDATE: - if (!affmap) { - snprintf(args->errmsg, args->errmsg_len, - "affinity-map %s not found.", affname); - return NB_ERR_VALIDATION; + if (yang_dnode_exists(args->dnode, "../residual-bandwidth")) { + res_bw = yang_dnode_get_bandwidth_ieee_float32( + args->dnode, "../residual-bandwidth"); + if (max_bw < res_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than residual-bandwidth %f", + max_bw, res_bw); + return NB_ERR_VALIDATION; + } } - if (affinity_mode == AFFINITY_MODE_STANDARD && - affmap->bit_position > 31) { - snprintf( - args->errmsg, args->errmsg_len, - "affinity %s bit-position %d is not compatible with affinity-mode standard (bit-position > 31).", - affname, affmap->bit_position); - return NB_ERR_VALIDATION; + if (yang_dnode_exists(args->dnode, "../available-bandwidth")) { + ava_bw = yang_dnode_get_bandwidth_ieee_float32( + args->dnode, "../available-bandwidth"); + if (max_bw < ava_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than available-bandwidth %f", + max_bw, ava_bw); + return NB_ERR_VALIDATION; + } + } + if (yang_dnode_exists(args->dnode, "../utilized-bandwidth")) { + use_bw = yang_dnode_get_bandwidth_ieee_float32( + args->dnode, "../utilized-bandwidth"); + if (max_bw < use_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than utilized-bandwidth %f", + max_bw, use_bw); + return NB_ERR_VALIDATION; + } } break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: - if (!iflp) - iflp = if_link_params_enable(ifp); + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->max_bw, LP_MAX_BW, max_bw); + break; + } - if (affmap->bit_position < 32 && - (affinity_mode == AFFINITY_MODE_STANDARD || - affinity_mode == AFFINITY_MODE_BOTH)) { - iflp->admin_grp |= 1 << affmap->bit_position; - SET_PARAM(iflp, LP_ADM_GRP); - } - if (affinity_mode == AFFINITY_MODE_EXTENDED || - affinity_mode == AFFINITY_MODE_BOTH) { - admin_group_set(&iflp->ext_admin_grp, - affmap->bit_position); - SET_PARAM(iflp, LP_EXTEND_ADM_GRP); - } + return NB_OK; +} - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); - break; +int lib_interface_zebra_link_params_max_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + snprintfrr(args->errmsg, args->errmsg_len, + "Removing max-bandwidth is not allowed"); + return NB_ERR_VALIDATION; } + return NB_OK; } -int lib_interface_zebra_affinity_destroy(struct nb_cb_destroy_args *args) +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/max-reservable-bandwidth + */ +int lib_interface_zebra_link_params_max_reservable_bandwidth_modify( + struct nb_cb_modify_args *args) { struct interface *ifp; - const char *affname; struct if_link_params *iflp; - struct affinity_map *affmap; - enum affinity_mode affinity_mode; + float max_rsv_bw; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + max_rsv_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); ifp = nb_running_get_entry(args->dnode, NULL, true); - affname = yang_dnode_get_string(args->dnode, "."); - affinity_mode = yang_dnode_get_enum(args->dnode, "../../affinity-mode"); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->max_rsv_bw, LP_MAX_RSV_BW, + max_rsv_bw); + + return NB_OK; +} + +int lib_interface_zebra_link_params_max_reservable_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + snprintfrr(args->errmsg, args->errmsg_len, + "Removing max-reservable-bandwidth is not allowed"); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/unreserved-bandwidths/unreserved-bandwidth + */ +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_create( + struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint8_t priority; + float unrsv_bw; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + priority = yang_dnode_get_uint8(args->dnode, "priority"); + unrsv_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, + "unreserved-bandwidth"); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW, + unrsv_bw); + + return NB_OK; +} + +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + snprintfrr(args->errmsg, args->errmsg_len, + "Removing unreserved-bandwidth is not allowed"); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/unreserved-bandwidths/unreserved-bandwidth/unreserved-bandwidth + */ +int lib_interface_zebra_link_params_unreserved_bandwidths_unreserved_bandwidth_unreserved_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint8_t priority; + float unrsv_bw; + + if (args->event != NB_EV_APPLY) + return NB_OK; - if (!ifp) - return NB_ERR_RESOURCE; + priority = yang_dnode_get_uint8(args->dnode, "../priority"); + unrsv_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); - affmap = affinity_map_get(affname); + ifp = nb_running_get_entry(args->dnode, NULL, true); iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW, + unrsv_bw); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/residual-bandwidth + */ +int lib_interface_zebra_link_params_residual_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + float max_bw, res_bw; + + res_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); switch (args->event) { case NB_EV_VALIDATE: - if (!affmap) { - snprintf(args->errmsg, args->errmsg_len, - "affinity-map %s not found.", affname); - return NB_ERR_VALIDATION; + if (yang_dnode_exists(args->dnode, "../max-bandwidth")) { + max_bw = + yang_dnode_get_bandwidth_ieee_float32(args->dnode, + "../max-bandwidth"); + if (max_bw < res_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than residual-bandwidth %f", + max_bw, res_bw); + return NB_ERR_VALIDATION; + } } break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: - if (!iflp) - return NB_OK; - if (affmap->bit_position < 32 && - (affinity_mode == AFFINITY_MODE_STANDARD || - affinity_mode == AFFINITY_MODE_BOTH)) { - iflp->admin_grp &= ~(1 << affmap->bit_position); - if (iflp->admin_grp == 0) - UNSET_PARAM(iflp, LP_ADM_GRP); - } - if (affinity_mode == AFFINITY_MODE_EXTENDED || - affinity_mode == AFFINITY_MODE_BOTH) { - admin_group_unset(&iflp->ext_admin_grp, - affmap->bit_position); - if (admin_group_zero(&iflp->ext_admin_grp)) - UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); - } - - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->res_bw, LP_RES_BW, res_bw); break; } + return NB_OK; } -/* - * XPath: - * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinity-mode - */ -int lib_interface_zebra_affinity_mode_modify(struct nb_cb_modify_args *args) +int lib_interface_zebra_link_params_residual_bandwidth_destroy( + struct nb_cb_destroy_args *args) { struct interface *ifp; - struct if_link_params *iflp; - enum affinity_mode affinity_mode; + if (args->event != NB_EV_APPLY) + return NB_OK; ifp = nb_running_get_entry(args->dnode, NULL, true); - affinity_mode = yang_dnode_get_enum(args->dnode, "."); + link_param_cmd_unset(ifp, LP_RES_BW); - if (!ifp) - return NB_ERR_RESOURCE; + return NB_OK; +} - iflp = if_link_params_get(ifp); +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/available-bandwidth + */ +int lib_interface_zebra_link_params_available_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + float max_bw, ava_bw; + + ava_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); switch (args->event) { case NB_EV_VALIDATE: - if (affinity_mode == AFFINITY_MODE_STANDARD && - admin_group_nb_words(&iflp->ext_admin_grp) > 1) { - snprintf( - args->errmsg, args->errmsg_len, - "affinity-mode standard cannot be set when a bit-position > 31 is set."); - return NB_ERR_VALIDATION; + if (yang_dnode_exists(args->dnode, "../max-bandwidth")) { + max_bw = + yang_dnode_get_bandwidth_ieee_float32(args->dnode, + "../max-bandwidth"); + if (max_bw < ava_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than available-bandwidth %f", + max_bw, ava_bw); + return NB_ERR_VALIDATION; + } } break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: - if (!iflp) - iflp = if_link_params_enable(ifp); - if (affinity_mode == AFFINITY_MODE_STANDARD) { - if (!IS_PARAM_SET(iflp, LP_ADM_GRP) && - IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP)) { - iflp->admin_grp = admin_group_get_offset( - &iflp->ext_admin_grp, 0); - SET_PARAM(iflp, LP_ADM_GRP); - } - admin_group_clear(&iflp->ext_admin_grp); - UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); - } - if (affinity_mode == AFFINITY_MODE_EXTENDED) { - if (!IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP) && - IS_PARAM_SET(iflp, LP_ADM_GRP)) { - admin_group_bulk_set(&iflp->ext_admin_grp, - iflp->admin_grp, 0); - SET_PARAM(iflp, LP_EXTEND_ADM_GRP); - } - iflp->admin_grp = 0; - UNSET_PARAM(iflp, LP_ADM_GRP); - } - if (affinity_mode == AFFINITY_MODE_BOTH) { - if (!IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP) && - IS_PARAM_SET(iflp, LP_ADM_GRP)) { - admin_group_bulk_set(&iflp->ext_admin_grp, - iflp->admin_grp, 0); - SET_PARAM(iflp, LP_EXTEND_ADM_GRP); - } else if (!IS_PARAM_SET(iflp, LP_ADM_GRP) && - IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP)) { - iflp->admin_grp = admin_group_get_offset( - &iflp->ext_admin_grp, 0); - SET_PARAM(iflp, LP_ADM_GRP); - } - } - - if (if_is_operative(ifp)) - zebra_interface_parameters_update(ifp); + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->ava_bw, LP_AVA_BW, ava_bw); break; } + + return NB_OK; +} + +int lib_interface_zebra_link_params_available_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + link_param_cmd_unset(ifp, LP_AVA_BW); + return NB_OK; } /* - * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/utilized-bandwidth */ -int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) +int lib_interface_zebra_link_params_utilized_bandwidth_modify( + struct nb_cb_modify_args *args) { - struct vrf *vrf; - struct zebra_vrf *zvrf; - vni_t vni = 0; - struct zebra_l3vni *zl3vni = NULL; - char err[ERR_STR_SZ]; - bool pfx_only = false; - const struct lyd_node *pn_dnode; - const char *vrfname; + struct interface *ifp; + struct if_link_params *iflp; + float max_bw, use_bw; + + use_bw = yang_dnode_get_bandwidth_ieee_float32(args->dnode, NULL); switch (args->event) { + case NB_EV_VALIDATE: + if (yang_dnode_exists(args->dnode, "../max-bandwidth")) { + max_bw = + yang_dnode_get_bandwidth_ieee_float32(args->dnode, + "../max-bandwidth"); + if (max_bw < use_bw) { + snprintfrr(args->errmsg, args->errmsg_len, + "max-bandwidth %f is less than utilized-bandwidth %f", + max_bw, use_bw); + return NB_ERR_VALIDATION; + } + } + break; case NB_EV_PREPARE: case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + link_param_cmd_set_float(ifp, &iflp->use_bw, LP_USE_BW, use_bw); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_link_params_utilized_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) return NB_OK; - case NB_EV_VALIDATE: - vni = yang_dnode_get_uint32(args->dnode, NULL); - /* Get vrf info from parent node, reject configuration - * if zebra vrf already mapped to different vni id. - */ - pn_dnode = yang_dnode_get_parent(args->dnode, "vrf"); - vrfname = yang_dnode_get_string(pn_dnode, "./name"); - zvrf = zebra_vrf_lookup_by_name(vrfname); - if (!zvrf) { - snprintf(args->errmsg, args->errmsg_len, - "zebra vrf info not found for vrf:%s.", - vrfname); - return NB_ERR_VALIDATION; - } - if (zvrf->l3vni && zvrf->l3vni != vni) { - snprintf( - args->errmsg, args->errmsg_len, - "vni %u cannot be configured as vni %u is already configured under the vrf", - vni, zvrf->l3vni); - return NB_ERR_VALIDATION; - } - /* Check if this VNI is already present in the system */ - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - snprintf(args->errmsg, args->errmsg_len, - "VNI %u is already configured as L3-VNI", vni); - return NB_ERR_VALIDATION; - } + ifp = nb_running_get_entry(args->dnode, NULL, true); + link_param_cmd_unset(ifp, LP_USE_BW); - break; - case NB_EV_APPLY: + return NB_OK; +} - vrf = nb_running_get_entry(args->dnode, NULL, true); - zvrf = zebra_vrf_lookup_by_name(vrf->name); - vni = yang_dnode_get_uint32(args->dnode, NULL); - /* Note: This covers lib_vrf_zebra_prefix_only_modify() config - * along with l3vni config - */ - pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only"); +/* + * XPath: + * /frr-interface:lib/interface/frr-zebra:zebra/link-params/legacy-admin-group + */ +int lib_interface_zebra_legacy_admin_group_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t admin_group_value; - if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - pfx_only ? 1 : 0, 1) - != 0) { - if (IS_ZEBRA_DEBUG_VXLAN) - snprintf( - args->errmsg, args->errmsg_len, - "vrf vni %u mapping failed with error: %s", - vni, err); - return NB_ERR; - } + admin_group_value = yang_dnode_get_uint32(args->dnode, "."); + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: break; - } + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + iflp->admin_grp = admin_group_value; + SET_PARAM(iflp, LP_ADM_GRP); + break; + } return NB_OK; } -int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) +int lib_interface_zebra_legacy_admin_group_destroy( + struct nb_cb_destroy_args *args) { - struct vrf *vrf; - struct zebra_vrf *zvrf; - vni_t vni = 0; - char err[ERR_STR_SZ]; - uint8_t filter = 0; + struct interface *ifp; + struct if_link_params *iflp; switch (args->event) { + case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: - case NB_EV_VALIDATE: - return NB_OK; + break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - zvrf = zebra_vrf_lookup_by_name(vrf->name); - vni = yang_dnode_get_uint32(args->dnode, NULL); - - if (!zl3vni_lookup(vni)) - return NB_OK; + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); - if (zvrf->l3vni != vni) { - snprintf(args->errmsg, args->errmsg_len, - "vrf %s has different vni %u mapped", - vrf->name, zvrf->l3vni); - return NB_ERR; - } + iflp->admin_grp = 0; + UNSET_PARAM(iflp, LP_ADM_GRP); + break; + } + return NB_OK; +} - if (is_l3vni_for_prefix_routes_only(zvrf->l3vni)) - filter = 1; - - if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - filter, 0) - != 0) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "vrf vni %u unmapping failed with error: %s", - vni, err); - return NB_ERR; - } +/* + * XPath: + * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities + */ +int lib_interface_zebra_affinities_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_interface_zebra_affinities_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->admin_grp = 0; + UNSET_PARAM(iflp, LP_ADM_GRP); + + admin_group_clear(&iflp->ext_admin_grp); + UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity + */ +int lib_interface_zebra_affinity_create(struct nb_cb_create_args *args) +{ + struct interface *ifp; + const char *affname; + struct if_link_params *iflp; + struct affinity_map *affmap; + enum affinity_mode affinity_mode; + + affname = yang_dnode_get_string(args->dnode, "."); + affinity_mode = yang_dnode_get_enum(args->dnode, "../../affinity-mode"); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + affmap = affinity_map_get(affname); + + if (affmap->bit_position < 32 && + (affinity_mode == AFFINITY_MODE_STANDARD || + affinity_mode == AFFINITY_MODE_BOTH)) { + iflp->admin_grp |= 1 << affmap->bit_position; + SET_PARAM(iflp, LP_ADM_GRP); + } + if (affinity_mode == AFFINITY_MODE_EXTENDED || + affinity_mode == AFFINITY_MODE_BOTH) { + admin_group_set(&iflp->ext_admin_grp, + affmap->bit_position); + SET_PARAM(iflp, LP_EXTEND_ADM_GRP); + } + break; + } + return NB_OK; +} + +int lib_interface_zebra_affinity_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + const char *affname; + struct if_link_params *iflp; + struct affinity_map *affmap; + enum affinity_mode affinity_mode; + + affname = yang_dnode_get_string(args->dnode, "."); + affinity_mode = yang_dnode_get_enum(args->dnode, "../../affinity-mode"); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + affmap = affinity_map_get(affname); + + if (affmap->bit_position < 32 && + (affinity_mode == AFFINITY_MODE_STANDARD || + affinity_mode == AFFINITY_MODE_BOTH)) { + iflp->admin_grp &= ~(1 << affmap->bit_position); + if (iflp->admin_grp == 0) + UNSET_PARAM(iflp, LP_ADM_GRP); + } + if (affinity_mode == AFFINITY_MODE_EXTENDED || + affinity_mode == AFFINITY_MODE_BOTH) { + admin_group_unset(&iflp->ext_admin_grp, + affmap->bit_position); + if (admin_group_zero(&iflp->ext_admin_grp)) + UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); + } + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinity-mode + */ +int lib_interface_zebra_affinity_mode_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + enum affinity_mode affinity_mode; + + affinity_mode = yang_dnode_get_enum(args->dnode, "."); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + if (affinity_mode == AFFINITY_MODE_STANDARD) { + if (!IS_PARAM_SET(iflp, LP_ADM_GRP) && + IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP)) { + iflp->admin_grp = admin_group_get_offset( + &iflp->ext_admin_grp, 0); + SET_PARAM(iflp, LP_ADM_GRP); + } + admin_group_clear(&iflp->ext_admin_grp); + UNSET_PARAM(iflp, LP_EXTEND_ADM_GRP); + } + if (affinity_mode == AFFINITY_MODE_EXTENDED) { + if (!IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP) && + IS_PARAM_SET(iflp, LP_ADM_GRP)) { + admin_group_bulk_set(&iflp->ext_admin_grp, + iflp->admin_grp, 0); + SET_PARAM(iflp, LP_EXTEND_ADM_GRP); + } + iflp->admin_grp = 0; + UNSET_PARAM(iflp, LP_ADM_GRP); + } + if (affinity_mode == AFFINITY_MODE_BOTH) { + if (!IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP) && + IS_PARAM_SET(iflp, LP_ADM_GRP)) { + admin_group_bulk_set(&iflp->ext_admin_grp, + iflp->admin_grp, 0); + SET_PARAM(iflp, LP_EXTEND_ADM_GRP); + } else if (!IS_PARAM_SET(iflp, LP_ADM_GRP) && + IS_PARAM_SET(iflp, LP_EXTEND_ADM_GRP)) { + iflp->admin_grp = admin_group_get_offset( + &iflp->ext_admin_grp, 0); + SET_PARAM(iflp, LP_ADM_GRP); + } + } + break; + } + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor + */ +int lib_interface_zebra_link_params_neighbor_create(struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + struct in_addr ip; + uint32_t as; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + as = yang_dnode_get_uint32(args->dnode, "remote-as"); + yang_dnode_get_ipv4(&ip, args->dnode, "ipv4-remote-id"); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->rmt_as = as; + iflp->rmt_ip = ip; + SET_PARAM(iflp, LP_RMT_AS); + + return NB_OK; +} + +int lib_interface_zebra_link_params_neighbor_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->rmt_as = 0; + iflp->rmt_ip.s_addr = 0; + UNSET_PARAM(iflp, LP_RMT_AS); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor/remote-as + */ +int lib_interface_zebra_link_params_neighbor_remote_as_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t as; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + as = yang_dnode_get_uint32(args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->rmt_as = as; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/neighbor/ipv4-remote-id + */ +int lib_interface_zebra_link_params_neighbor_ipv4_remote_id_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + struct in_addr ip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + yang_dnode_get_ipv4(&ip, args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->rmt_ip = ip; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/delay + */ +int lib_interface_zebra_link_params_delay_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t delay; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + delay = yang_dnode_get_uint32(args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + link_param_cmd_set_uint32(ifp, &iflp->av_delay, LP_DELAY, delay); + + return NB_OK; +} + +int lib_interface_zebra_link_params_delay_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->av_delay = 0; + link_param_cmd_unset(ifp, LP_DELAY); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay + */ +int lib_interface_zebra_link_params_min_max_delay_create( + struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t delay_min, delay_max; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + delay_min = yang_dnode_get_uint32(args->dnode, "delay-min"); + delay_max = yang_dnode_get_uint32(args->dnode, "delay-max"); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->min_delay = delay_min; + iflp->max_delay = delay_max; + SET_PARAM(iflp, LP_MM_DELAY); + + return NB_OK; +} + +int lib_interface_zebra_link_params_min_max_delay_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->min_delay = 0; + iflp->max_delay = 0; + UNSET_PARAM(iflp, LP_MM_DELAY); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay/delay-min + */ +int lib_interface_zebra_link_params_min_max_delay_delay_min_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t delay_min; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + delay_min = yang_dnode_get_uint32(args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->min_delay = delay_min; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/min-max-delay/delay-max + */ +int lib_interface_zebra_link_params_min_max_delay_delay_max_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t delay_max; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + delay_max = yang_dnode_get_uint32(args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + iflp->max_delay = delay_max; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/delay-variation + */ +int lib_interface_zebra_link_params_delay_variation_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + uint32_t delay_var; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + delay_var = yang_dnode_get_uint32(args->dnode, NULL); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + link_param_cmd_set_uint32(ifp, &iflp->delay_var, LP_DELAY_VAR, + delay_var); + + return NB_OK; +} + +int lib_interface_zebra_link_params_delay_variation_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + link_param_cmd_unset(ifp, LP_DELAY_VAR); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-params/packet-loss + */ +int lib_interface_zebra_link_params_packet_loss_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct if_link_params *iflp; + double packet_loss; + uint32_t value; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + packet_loss = yang_dnode_get_dec64(args->dnode, NULL); + value = (uint32_t)(packet_loss / LOSS_PRECISION); + + ifp = nb_running_get_entry(args->dnode, NULL, true); + iflp = if_link_params_get(ifp); + + link_param_cmd_set_uint32(ifp, &iflp->pkt_loss, LP_PKT_LOSS, value); + + return NB_OK; +} + +int lib_interface_zebra_link_params_packet_loss_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + link_param_cmd_unset(ifp, LP_PKT_LOSS); + + return NB_OK; +} + +static bool evpn_mh_dnode_to_esi(const struct lyd_node *dnode, esi_t *esi) +{ + if (yang_dnode_exists(dnode, "type-0/esi")) { + if (!str_to_esi(yang_dnode_get_string(dnode, "type-0/esi"), esi)) + assert(false); + } else if (yang_dnode_exists(dnode, "type-3/system-mac") && + yang_dnode_exists(dnode, "type-3/local-discriminator")) { + struct ethaddr mac; + uint32_t lid; + + yang_dnode_get_mac(&mac, dnode, "type-3/system-mac"); + lid = yang_dnode_get_uint32(dnode, "type-3/local-discriminator"); + + zebra_build_type3_esi(lid, &mac, esi); + } else { + return false; + } + + return true; +} + +struct esi_cmp_iter_arg { + struct lyd_node *dnode; + esi_t esi; + bool exists; +}; + +static int esi_cmp_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct esi_cmp_iter_arg *iter = arg; + esi_t esi; + + if (dnode == iter->dnode) + return YANG_ITER_CONTINUE; + + if (!evpn_mh_dnode_to_esi(dnode, &esi)) + return YANG_ITER_CONTINUE; + + if (!memcmp(&esi, &iter->esi, ESI_BYTES)) { + iter->exists = true; + return YANG_ITER_STOP; + } + + return YANG_ITER_CONTINUE; +} + +/* evpn-mh should be passed to this function */ +static bool esi_unique(struct lyd_node *dnode) +{ + struct esi_cmp_iter_arg iter; + + iter.dnode = dnode; + evpn_mh_dnode_to_esi(dnode, &iter.esi); + iter.exists = false; + + yang_dnode_iterate(esi_cmp_iter_cb, &iter, dnode, + "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh"); + + if (iter.exists) + return false; + + return true; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0 + */ +int lib_interface_zebra_evpn_mh_type_0_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_0_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_type0_esi_update(ifp->info, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi + */ +int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + esi_t esi; + + switch (args->event) { + case NB_EV_VALIDATE: + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + if (!str_to_esi(yang_dnode_get_string(args->dnode, NULL), &esi)) + assert(false); + zebra_evpn_es_type0_esi_update(ifp->info, &esi); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_0_esi_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_type0_esi_update(ifp->info, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3 + */ +int lib_interface_zebra_evpn_mh_type_3_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_3_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_sys_mac_update(ifp->info, NULL); + zebra_evpn_es_lid_update(ifp->info, 0); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac + */ +int lib_interface_zebra_evpn_mh_type_3_system_mac_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct ethaddr mac; + + yang_dnode_get_mac(&mac, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (is_zero_mac(&mac)) { + snprintfrr(args->errmsg, args->errmsg_len, + "MAC cannot be all-zeroes"); + return NB_ERR_VALIDATION; + } + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_sys_mac_update(ifp->info, &mac); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_sys_mac_update(ifp->info, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator + */ +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + uint32_t lid; + + switch (args->event) { + case NB_EV_VALIDATE: + if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) { + snprintfrr(args->errmsg, args->errmsg_len, + "ESI already exists on a different interface"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + lid = yang_dnode_get_uint32(args->dnode, NULL); + zebra_evpn_es_lid_update(ifp->info, lid); + break; + } + + return NB_OK; +} + +int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zebra_evpn_es_lid_update(ifp->info, 0); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/df-preference + */ +int lib_interface_zebra_evpn_mh_df_preference_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + uint16_t df_pref; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + df_pref = yang_dnode_get_uint16(args->dnode, NULL); + zebra_evpn_es_df_pref_update(ifp->info, df_pref); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/bypass + */ +int lib_interface_zebra_evpn_mh_bypass_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + bool bypass; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + bypass = yang_dnode_get_bool(args->dnode, NULL); + zebra_evpn_es_bypass_cfg_update(ifp->info, bypass); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/uplink + */ +int lib_interface_zebra_evpn_mh_uplink_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + bool uplink; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + uplink = yang_dnode_get_bool(args->dnode, NULL); + zebra_evpn_mh_uplink_cfg_update(ifp->info, uplink); + + return NB_OK; +} + +#if defined(HAVE_RTADV) +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/send-advertisements + */ +int lib_interface_zebra_ipv6_router_advertisements_send_advertisements_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool send_adv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + send_adv = yang_dnode_get_bool(args->dnode, NULL); + + if (send_adv) { + ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); + SET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); + } else { + if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) + ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS); + UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/max-rtr-adv-interval + */ +int lib_interface_zebra_ipv6_router_advertisements_max_rtr_adv_interval_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + uint32_t interval; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + interval = yang_dnode_get_uint32(args->dnode, NULL); + + ipv6_nd_interval_set(ifp, interval); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/managed-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_managed_flag_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool managed_flag; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + managed_flag = yang_dnode_get_bool(args->dnode, NULL); + + zif->rtadv.AdvManagedFlag = managed_flag; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/other-config-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_other_config_flag_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool other_config_flag; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + other_config_flag = yang_dnode_get_bool(args->dnode, NULL); + + zif->rtadv.AdvOtherConfigFlag = other_config_flag; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_home_agent_flag_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool home_agent_flag; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + home_agent_flag = yang_dnode_get_bool(args->dnode, NULL); + + zif->rtadv.AdvHomeAgentFlag = home_agent_flag; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/link-mtu + */ +int lib_interface_zebra_ipv6_router_advertisements_link_mtu_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint32_t mtu; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + mtu = yang_dnode_get_uint32(args->dnode, NULL); + + zif->rtadv.AdvLinkMTU = mtu; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/reachable-time + */ +int lib_interface_zebra_ipv6_router_advertisements_reachable_time_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint32_t time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + time = yang_dnode_get_uint32(args->dnode, NULL); + + zif->rtadv.AdvReachableTime = time; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/retrans-timer + */ +int lib_interface_zebra_ipv6_router_advertisements_retrans_timer_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint32_t timer; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + timer = yang_dnode_get_uint32(args->dnode, NULL); + + zif->rtadv.AdvRetransTimer = timer; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/cur-hop-limit + */ +int lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint8_t limit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + limit = yang_dnode_get_uint8(args->dnode, NULL); + + zif->rtadv.AdvCurHopLimit = limit; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_cur_hop_limit_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + zif->rtadv.AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_default_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint16_t lifetime; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + lifetime = yang_dnode_get_uint16(args->dnode, NULL); + + zif->rtadv.AdvDefaultLifetime = lifetime; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_default_lifetime_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + zif->rtadv.AdvDefaultLifetime = -1; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/fast-retransmit + */ +int lib_interface_zebra_ipv6_router_advertisements_fast_retransmit_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool fast_retransmit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + fast_retransmit = yang_dnode_get_bool(args->dnode, NULL); + + zif->rtadv.UseFastRexmit = fast_retransmit; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/advertisement-interval-option + */ +int lib_interface_zebra_ipv6_router_advertisements_advertisement_interval_option_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + bool option; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + option = yang_dnode_get_bool(args->dnode, NULL); + + zif->rtadv.AdvIntervalOption = option; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-preference + */ +int lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint16_t preference; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + preference = yang_dnode_get_uint16(args->dnode, NULL); + + zif->rtadv.HomeAgentPreference = preference; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_home_agent_preference_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + zif->rtadv.HomeAgentPreference = 0; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/home-agent-lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + uint16_t lifetime; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + lifetime = yang_dnode_get_uint16(args->dnode, NULL); + + zif->rtadv.HomeAgentLifetime = lifetime; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_home_agent_lifetime_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + zif->rtadv.HomeAgentLifetime = -1; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/default-router-preference + */ +int lib_interface_zebra_ipv6_router_advertisements_default_router_preference_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + int preference; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + preference = yang_dnode_get_enum(args->dnode, NULL); + + zif->rtadv.DefaultPreference = preference; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_create( + struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct rtadv_prefix rp, *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + yang_dnode_get_ipv6p(&rp.prefix, args->dnode, "prefix-spec"); + rp.AdvOnLinkFlag = yang_dnode_get_bool(args->dnode, "on-link-flag"); + rp.AdvAutonomousFlag = yang_dnode_get_bool(args->dnode, + "autonomous-flag"); + rp.AdvRouterAddressFlag = yang_dnode_get_bool(args->dnode, + "router-address-flag"); + rp.AdvValidLifetime = yang_dnode_get_uint32(args->dnode, + "valid-lifetime"); + rp.AdvPreferredLifetime = yang_dnode_get_uint32(args->dnode, + "preferred-lifetime"); + + prefix = rtadv_add_prefix_manual(ifp->info, &rp); + nb_running_set_entry(args->dnode, prefix); + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_unset_entry(args->dnode); + ifp = nb_running_get_entry(args->dnode, NULL, true); + + rtadv_delete_prefix_manual(ifp->info, prefix); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/valid-lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_valid_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_get_entry(args->dnode, NULL, true); + + prefix->AdvValidLifetime = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/on-link-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_on_link_flag_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_get_entry(args->dnode, NULL, true); + + prefix->AdvOnLinkFlag = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/preferred-lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_preferred_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_get_entry(args->dnode, NULL, true); + + prefix->AdvPreferredLifetime = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/autonomous-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_autonomous_flag_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_get_entry(args->dnode, NULL, true); + + prefix->AdvAutonomousFlag = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/prefix-list/prefix/router-address-flag + */ +int lib_interface_zebra_ipv6_router_advertisements_prefix_list_prefix_router_address_flag_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_prefix *prefix; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + prefix = nb_running_get_entry(args->dnode, NULL, true); + + prefix->AdvRouterAddressFlag = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address + */ +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_create( + struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct rtadv_rdnss rdnss = {0}, *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + yang_dnode_get_ipv6(&rdnss.addr, args->dnode, "address"); + if (yang_dnode_exists(args->dnode, "lifetime")) { + rdnss.lifetime = yang_dnode_get_uint32(args->dnode, "lifetime"); + rdnss.lifetime_set = 1; + } else { + rdnss.lifetime_set = 0; + } + + p = rtadv_rdnss_set(ifp->info, &rdnss); + nb_running_set_entry(args->dnode, p); + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rtadv_rdnss *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_unset_entry(args->dnode); + ifp = nb_running_get_entry(args->dnode, NULL, true); + + rtadv_rdnss_reset(ifp->info, p); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/rdnss/rdnss-address/lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_rdnss *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_get_entry(args->dnode, NULL, true); + + p->lifetime = yang_dnode_get_uint32(args->dnode, NULL); + p->lifetime_set = 1; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_lifetime_destroy( + struct nb_cb_destroy_args *args) +{ + struct rtadv_rdnss *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_get_entry(args->dnode, NULL, true); + + p->lifetime_set = 0; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain + */ +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_create( + struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct rtadv_dnssl dnssl = {0}, *p; + int ret; + + strlcpy(dnssl.name, yang_dnode_get_string(args->dnode, "domain"), + sizeof(dnssl.name)); + ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name); + + if (args->event == NB_EV_VALIDATE) { + if (ret < 0) { + snprintfrr(args->errmsg, args->errmsg_len, + "Malformed DNS search domain"); + return NB_ERR_VALIDATION; + } + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + if (yang_dnode_exists(args->dnode, "lifetime")) { + dnssl.lifetime = yang_dnode_get_uint32(args->dnode, "lifetime"); + dnssl.lifetime_set = 1; + } else { + dnssl.lifetime_set = 0; + } + + p = rtadv_dnssl_set(ifp->info, &dnssl); + nb_running_set_entry(args->dnode, p); + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rtadv_dnssl *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_unset_entry(args->dnode); + ifp = nb_running_get_entry(args->dnode, NULL, true); + + rtadv_dnssl_reset(ifp->info, p); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ipv6-router-advertisements/dnssl/dnssl-domain/lifetime + */ +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_modify( + struct nb_cb_modify_args *args) +{ + struct rtadv_dnssl *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_get_entry(args->dnode, NULL, true); + + p->lifetime = yang_dnode_get_uint32(args->dnode, NULL); + p->lifetime_set = 1; + + return NB_OK; +} + +int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_lifetime_destroy( + struct nb_cb_destroy_args *args) +{ + struct rtadv_dnssl *p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + p = nb_running_get_entry(args->dnode, NULL, true); + + p->lifetime_set = 0; + + return NB_OK; +} +#endif /* defined(HAVE_RTADV) */ + +#if HAVE_BFDD == 0 +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ptm-enable + */ +int lib_interface_zebra_ptm_enable_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + bool ptm; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + ptm = yang_dnode_get_bool(args->dnode, NULL); + if (ptm) + zebra_if_ptm_enable(ifp); + else + zebra_if_ptm_disable(ifp); + + return NB_OK; +} +#endif + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/router-id + */ +int lib_vrf_zebra_router_id_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + + router_id_set(AFI_IP, &p, vrf->info); + + return NB_OK; +} + +int lib_vrf_zebra_router_id_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + memset(&p, 0, sizeof(p)); + p.family = AF_INET; + + router_id_set(AFI_IP, &p, vrf->info); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-router-id + */ +int lib_vrf_zebra_ipv6_router_id_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + + router_id_set(AFI_IP6, &p, vrf->info); + + return NB_OK; +} + +int lib_vrf_zebra_ipv6_router_id_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + memset(&p, 0, sizeof(p)); + p.family = AF_INET6; + + router_id_set(AFI_IP6, &p, vrf->info); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/filter-protocol + */ +int lib_vrf_zebra_filter_protocol_create(struct nb_cb_create_args *args) +{ + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + int rtype; + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + if (args->event == NB_EV_VALIDATE) + if (rtype < 0) { + snprintfrr(args->errmsg, args->errmsg_len, + "invalid protocol name \"%s\"", proto); + return NB_ERR_VALIDATION; + } + + /* the creation finishes in the apply_finish callback */ + + return NB_OK; +} + +int lib_vrf_zebra_filter_protocol_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + const char *afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + const char *rmap = yang_dnode_get_string(args->dnode, "route-map"); + afi_t afi; + safi_t safi; + int rtype; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + /* deleting an existing entry, it can't be invalid */ + assert(rtype >= 0); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + ip_protocol_rm_del(vrf->info, rmap, rtype, afi, safi); + + return NB_OK; +} + +void lib_vrf_zebra_filter_protocol_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct vrf *vrf; + const char *afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + const char *rmap = yang_dnode_get_string(args->dnode, "route-map"); + afi_t afi; + safi_t safi; + int rtype; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + /* finishing apply for a validated entry, it can't be invalid */ + assert(rtype >= 0); + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + ip_protocol_rm_add(vrf->info, rmap, rtype, afi, safi); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/filter-protocol/route-map + */ +int lib_vrf_zebra_filter_protocol_route_map_modify(struct nb_cb_modify_args *args) +{ + /* the update is done in the apply_finish callback */ + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/filter-nht + */ +int lib_vrf_zebra_filter_nht_create(struct nb_cb_create_args *args) +{ + const char *afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + afi_t afi; + safi_t safi; + int rtype; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + if (args->event == NB_EV_VALIDATE) { + if (rtype < 0) { + snprintfrr(args->errmsg, args->errmsg_len, + "invalid protocol name \"%s\"", proto); + return NB_ERR_VALIDATION; + } + if (safi != SAFI_UNICAST) { + snprintfrr(args->errmsg, args->errmsg_len, + "only SAFI unicast is supported"); + return NB_ERR_VALIDATION; + } + } + + /* the creation finishes in the apply_finish callback */ + + return NB_OK; +} + +int lib_vrf_zebra_filter_nht_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + const char *afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + const char *rmap = yang_dnode_get_string(args->dnode, "route-map"); + afi_t afi; + safi_t safi; + int rtype; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + /* deleting an existing entry, it can't be invalid */ + assert(rtype >= 0); + assert(safi == SAFI_UNICAST); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + ip_nht_rm_del(vrf->info, rmap, rtype, afi); + + return NB_OK; +} + +void lib_vrf_zebra_filter_nht_apply_finish(struct nb_cb_apply_finish_args *args) +{ + struct vrf *vrf; + const char *afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); + const char *proto = yang_dnode_get_string(args->dnode, "protocol"); + const char *rmap = yang_dnode_get_string(args->dnode, "route-map"); + afi_t afi; + safi_t safi; + int rtype; + + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (strcasecmp(proto, "any") == 0) + rtype = ZEBRA_ROUTE_MAX; + else + rtype = proto_name2num(proto); + + /* finishing apply for an existing entry, it can't be invalid */ + assert(rtype >= 0); + assert(safi == SAFI_UNICAST); + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + ip_nht_rm_add(vrf->info, rmap, rtype, afi); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/filter-nht/route-map + */ +int lib_vrf_zebra_filter_nht_route_map_modify(struct nb_cb_modify_args *args) +{ + /* the update is done in the apply_finish callback */ + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/resolve-via-default + */ +int lib_vrf_zebra_resolve_via_default_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + bool resolve_via_default; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = vrf->info; + + resolve_via_default = yang_dnode_get_bool(args->dnode, NULL); + + if (zvrf->zebra_rnh_ip_default_route == resolve_via_default) + return NB_OK; + + zvrf->zebra_rnh_ip_default_route = resolve_via_default; + + zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST); + + return NB_OK; +} + +int lib_vrf_zebra_resolve_via_default_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + bool resolve_via_default; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = vrf->info; + + resolve_via_default = DFLT_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT; + + if (zvrf->zebra_rnh_ip_default_route == resolve_via_default) + return NB_OK; + + zvrf->zebra_rnh_ip_default_route = resolve_via_default; + + zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ipv6-resolve-via-default + */ +int lib_vrf_zebra_ipv6_resolve_via_default_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + bool resolve_via_default; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = vrf->info; + + resolve_via_default = yang_dnode_get_bool(args->dnode, NULL); + + if (zvrf->zebra_rnh_ipv6_default_route == resolve_via_default) + return NB_OK; + + zvrf->zebra_rnh_ipv6_default_route = resolve_via_default; + + zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST); + + return NB_OK; +} + +int lib_vrf_zebra_ipv6_resolve_via_default_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + bool resolve_via_default; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = vrf->info; + + resolve_via_default = DFLT_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT; + + if (zvrf->zebra_rnh_ipv6_default_route == resolve_via_default) + return NB_OK; + + zvrf->zebra_rnh_ipv6_default_route = resolve_via_default; + + zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range + */ +static int table_range_validate(uint32_t start, uint32_t end, char *errmsg, + size_t errmsg_len) +{ +#if defined(GNU_LINUX) + if ((start >= RT_TABLE_ID_COMPAT && start <= RT_TABLE_ID_LOCAL) || + (end >= RT_TABLE_ID_COMPAT && end <= RT_TABLE_ID_LOCAL)) { + snprintfrr(errmsg, errmsg_len, + "Values forbidden in range [%u;%u]", + RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL); + return NB_ERR_VALIDATION; + } + if (start < RT_TABLE_ID_COMPAT && end > RT_TABLE_ID_LOCAL) { + snprintfrr(errmsg, errmsg_len, + "Range overlaps range [%u;%u] forbidden", + RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL); + return NB_ERR_VALIDATION; + } +#endif + return NB_OK; +} + +int lib_vrf_zebra_netns_table_range_create(struct nb_cb_create_args *args) +{ + struct vrf *vrf; + uint32_t start, end; + const char *vrf_name; + + start = yang_dnode_get_uint32(args->dnode, "start"); + end = yang_dnode_get_uint32(args->dnode, "end"); + + if (args->event == NB_EV_VALIDATE) { + vrf_name = yang_dnode_get_string(args->dnode, "../../../name"); + if (!vrf_is_backend_netns() && + strcmp(vrf_name, VRF_DEFAULT_NAME)) { + snprintfrr(args->errmsg, args->errmsg_len, + "Configuration is not available in non-default VRFs when using VRF-lite backend."); + return NB_ERR_VALIDATION; + } + return table_range_validate(start, end, args->errmsg, + args->errmsg_len); + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + table_manager_range(true, vrf->info, start, end); + + return NB_OK; +} + +int lib_vrf_zebra_netns_table_range_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + table_manager_range(false, vrf->info, 0, 0); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range/start + */ +int lib_vrf_zebra_netns_table_range_start_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + uint32_t start, end; + + start = yang_dnode_get_uint32(args->dnode, NULL); + end = yang_dnode_get_uint32(args->dnode, "../end"); + + if (args->event == NB_EV_VALIDATE) + return table_range_validate(start, end, args->errmsg, + args->errmsg_len); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + table_manager_range(true, vrf->info, start, end); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range/end + */ +int lib_vrf_zebra_netns_table_range_end_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + uint32_t start, end; + + start = yang_dnode_get_uint32(args->dnode, "../start"); + end = yang_dnode_get_uint32(args->dnode, NULL); + + if (args->event == NB_EV_VALIDATE) + return table_range_validate(start, end, args->errmsg, + args->errmsg_len); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrf = nb_running_get_entry(args->dnode, NULL, true); + + table_manager_range(true, vrf->info, start, end); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id + */ +int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + vni_t vni = 0; + bool pfx_only = false; + uint32_t count; + + vni = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_VALIDATE: + count = yang_dnode_count(args->dnode, + "/frr-vrf:lib/vrf/frr-zebra:zebra[l3vni-id='%u']", + vni); + if (count > 1) { + snprintfrr(args->errmsg, args->errmsg_len, + "vni %u is already mapped to another vrf", + vni); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only"); + + zebra_vxlan_process_vrf_vni_cmd(vrf->info, vni, + pfx_only ? 1 : 0, 1); + break; + } + + return NB_OK; +} + +int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + vni_t vni = 0; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_VALIDATE: + return NB_OK; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + vni = yang_dnode_get_uint32(args->dnode, NULL); + zebra_vxlan_process_vrf_vni_cmd(vrf->info, vni, 0, 0); break; } diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index ba53747..00df9bf 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -156,6 +156,8 @@ const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args) safi_t safi; zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + if (!zvrf) + return NULL; if (args->list_entry == NULL) { afi = AFI_IP; @@ -167,7 +169,8 @@ const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args) } else { zrt = RB_NEXT(zebra_router_table_head, zrt); /* vrf_id/ns_id do not match, only walk for the given VRF */ - while (zrt && zrt->ns_id != zvrf->zns->ns_id) + while (zrt && (zrt->tableid != zvrf->table_id || + zrt->ns_id != zvrf->zns->ns_id)) zrt = RB_NEXT(zebra_router_table_head, zrt); } @@ -198,6 +201,8 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args) uint32_t table_id = 0; zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + if (!zvrf) + return NULL; yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi); table_id = yang_str2uint32(args->keys->key[1]); @@ -208,6 +213,28 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args) return zebra_router_find_zrt(zvrf, table_id, afi, safi); } +const void * +lib_vrf_zebra_ribs_rib_lookup_next(struct nb_cb_lookup_entry_args *args) +{ + struct vrf *vrf = (struct vrf *)args->parent_list_entry; + struct zebra_vrf *zvrf; + afi_t afi; + safi_t safi; + uint32_t table_id = 0; + + zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + if (!zvrf) + return NULL; + + yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi); + table_id = yang_str2uint32(args->keys->key[1]); + /* table_id 0 assume vrf's table_id. */ + if (!table_id) + table_id = zvrf->table_id; + + return zebra_router_find_next_zrt(zvrf, table_id, afi, safi); +} + /* * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/afi-safi-name */ @@ -285,6 +312,25 @@ lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args) return rn; } +const void * +lib_vrf_zebra_ribs_rib_route_lookup_next(struct nb_cb_lookup_entry_args *args) +{ + const struct zebra_router_table *zrt = args->parent_list_entry; + struct prefix p; + struct route_node *rn; + + yang_str2prefix(args->keys->key[0], &p); + + rn = route_table_get_next(zrt->table, &p); + + if (!rn) + return NULL; + + route_unlock_node(rn); + + return rn; +} + /* * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/prefix */ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 0531ab9..1af3a3e 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -5,6 +5,12 @@ */ #include +#include +#include + +#ifdef GNU_LINUX +#include +#endif #include "ns.h" #include "vrf.h" diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 4260d29..1bb1292 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -5,6 +5,7 @@ */ #include +#include #ifdef HAVE_NETLINK #ifdef HAVE_NETNS diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1879baf..dc403b8 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -72,25 +72,13 @@ static uint32_t nhg_get_next_id(void) while (1) { id_counter++; - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: ID %u checking", __func__, id_counter); - if (id_counter == ZEBRA_NHG_PROTO_LOWER) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: ID counter wrapped", __func__); - id_counter = 0; continue; } - if (zebra_nhg_lookup_id(id_counter)) { - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: ID already exists", __func__); - - continue; - } - - break; + if (!zebra_nhg_lookup_id(id_counter)) + break; } return id_counter; @@ -690,12 +678,6 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ struct nhg_hash_entry *newnhe, *backup_nhe; struct nexthop *nh = NULL; - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug( - "%s: id %u, lookup %p, vrf %d, type %d, depends %p%s", - __func__, lookup->id, lookup, lookup->vrf_id, - lookup->type, nhg_depends, - (from_dplane ? " (from dplane)" : "")); if (lookup->id) (*nhe) = zebra_nhg_lookup_id(lookup->id); @@ -703,7 +685,10 @@ static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ (*nhe) = hash_lookup(zrouter.nhgs, lookup); if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: lookup => %p (%pNG)", __func__, *nhe, *nhe); + zlog_debug("%s: id %u, lookup %p, vrf %d, type %d, depends %p%s => Found %p(%pNG)", + __func__, lookup->id, lookup, lookup->vrf_id, + lookup->type, nhg_depends, + (from_dplane ? " (from dplane)" : ""), *nhe, *nhe); /* If we found an existing object, we're done */ if (*nhe) @@ -1082,11 +1067,10 @@ void zebra_nhg_check_valid(struct nhg_hash_entry *nhe) frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { if (CHECK_FLAG(rb_node_dep->nhe->flags, NEXTHOP_GROUP_VALID)) { valid = true; - goto done; + break; } } -done: if (valid) zebra_nhg_set_valid(nhe); else @@ -1310,6 +1294,7 @@ int nhg_ctx_process(struct nhg_ctx *ctx) break; case NHG_CTX_OP_DEL: ret = nhg_ctx_process_del(ctx); + break; case NHG_CTX_OP_NONE: break; } @@ -1525,19 +1510,23 @@ zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi) { struct nhg_hash_entry *nhe = NULL; - if (!(rt_nhe && rt_nhe->nhg.nexthop)) { + if (!rt_nhe) { flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, - "No nexthop passed to %s", __func__); + "No nhg_hash_entry passed to %s", __func__); return NULL; } - if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: rt_nhe %p (%pNG)", __func__, rt_nhe, rt_nhe); + if (!rt_nhe->nhg.nexthop) { + flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, + "No nexthop passed to %s", __func__); + return NULL; + } zebra_nhe_find(&nhe, rt_nhe, NULL, rt_afi, false); if (IS_ZEBRA_DEBUG_NHG_DETAIL) - zlog_debug("%s: => nhe %p (%pNG)", __func__, nhe, nhe); + zlog_debug("%s: rt_nhe %p(%pNG) => nhe %p(%pNG)", __func__, + rt_nhe, rt_nhe, nhe, nhe); return nhe; } @@ -2128,7 +2117,8 @@ zebra_nhg_connected_ifindex(struct route_node *rn, struct route_entry *match, * of those ifindexes match as well. */ RNODE_FOREACH_RE (rn, re) { - if (re->type != ZEBRA_ROUTE_CONNECT) + if (re->type != ZEBRA_ROUTE_CONNECT && + re->type != ZEBRA_ROUTE_LOCAL) continue; if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) @@ -2247,20 +2237,6 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, return 1; } - if (top && - ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN && - nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) || - (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN && - memcmp(&nexthop->gate.ipv6, &top->u.prefix6, IPV6_MAX_BYTELEN) == - 0)) && - nexthop->vrf_id == vrf_id) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " :%s: Attempting to install a max prefixlength route through itself", - __func__); - return 0; - } - /* Validation for ipv4 mapped ipv6 nexthop. */ if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { afi = AFI_IP; @@ -2363,7 +2339,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, zlog_debug( " %s: Matched against ourself and prefix length is not max bit length", __func__); - return 0; + goto continue_up_tree; } /* Pick up selected route. */ @@ -2390,20 +2366,12 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, /* If there is no selected route or matched route is EGP, go up * tree. */ - if (!match) { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node(rn); - continue; - } /* If the candidate match's type is considered "connected", * we consider it first. */ - if (RIB_CONNECTED_ROUTE(match) || - (RIB_SYSTEM_ROUTE(match) && RSYSTEM_ROUTE(type))) { + if (match && (RIB_CONNECTED_ROUTE(match) || + (RIB_SYSTEM_ROUTE(match) && RSYSTEM_ROUTE(type)))) { match = zebra_nhg_connected_ifindex(rn, match, nexthop->ifindex); @@ -2419,11 +2387,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, zlog_debug( "%s: %pNHv given ifindex does not match nexthops ifindex found: %pNHv", __func__, nexthop, newhop); - /* - * NEXTHOP_TYPE_*_IFINDEX but ifindex - * doesn't match what we found. - */ - return 0; + goto continue_up_tree; } /* NHRP special case: need to indicate onlink */ @@ -2436,7 +2400,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, __func__, match, match->nhe, newhop); return 1; - } else if (CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + } else if (match && CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) { struct nexthop_group *nhg; struct nexthop *resolver; struct backup_nh_map_s map = {}; @@ -2472,6 +2436,10 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, "%s: match %p (%pNG) not installed or being Route Replaced", __func__, match, match->nhe); + if (CHECK_FLAG(match->status, + ROUTE_ENTRY_QUEUED)) + goto continue_up_tree; + goto done_with_match; } @@ -2540,25 +2508,37 @@ done_with_match: if (pmtu) *pmtu = match->mtu; - } else if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " %s: Recursion failed to find", - __func__); - - return resolved; - } else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - zlog_debug( - " %s: Route Type %s has not turned on recursion", - __func__, zebra_route_string(type)); - if (type == ZEBRA_ROUTE_BGP - && !CHECK_FLAG(flags, ZEBRA_FLAG_IBGP)) + } else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - " EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); + " %s: Recursion failed to find while looking at %pRN", + __func__, rn); + goto continue_up_tree; } - return 0; + + return 1; + } else if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + zlog_debug( + " %s: Route Type %s has not turned on recursion %pRN failed to match", + __func__, zebra_route_string(type), rn); + if (type == ZEBRA_ROUTE_BGP + && !CHECK_FLAG(flags, ZEBRA_FLAG_IBGP)) + zlog_debug( + " EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); } + + continue_up_tree: + /* + * If there is no selected route or matched route is EGP, go up + * tree. + */ + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node(rn); } + if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug(" %s: Nexthop did not lookup in table", __func__); @@ -3182,8 +3162,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s", ctx, dplane_op2str(op), id, dplane_res2str(status)); - switch (op) { - case DPLANE_OP_NH_DELETE: + if (op == DPLANE_OP_NH_DELETE) { if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) flog_err( EC_ZEBRA_DP_DELETE_FAIL, @@ -3191,18 +3170,15 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) id); /* We already free'd the data, nothing to do */ - break; - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: + } else if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) { nhe = zebra_nhg_lookup_id(id); if (!nhe) { if (IS_ZEBRA_DEBUG_NHG) - zlog_debug( - "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table", - dplane_op2str(op), id); + zlog_debug("%s operation performed on Nexthop ID (%u) in the kernel, that we no longer have in our table", + dplane_op2str(op), id); - break; + return; } UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); @@ -3230,61 +3206,6 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) "Failed to install Nexthop (%pNG) into the kernel", nhe); } - break; - - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_NONE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: - break; } } diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 6179be3..4eddecb 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -143,7 +143,7 @@ struct nhg_hash_entry { /* * Track FPM installation status.. */ -#define NEXTHOP_GROUP_FPM (1 << 6) +#define NEXTHOP_GROUP_FPM (1 << 7) }; /* Upper 4 bits of the NHG are reserved for indicating the NHG type */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index ffdb9df..803d8f0 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -42,11 +42,6 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) return (info == NULL) ? dzns : info; } -static struct zebra_ns *zebra_ns_alloc(void) -{ - return XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); -} - static int zebra_ns_new(struct ns *ns) { struct zebra_ns *zns; @@ -57,7 +52,7 @@ static int zebra_ns_new(struct ns *ns) if (IS_ZEBRA_DEBUG_EVENT) zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id); - zns = zebra_ns_alloc(); + zns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); ns->info = zns; zns->ns = ns; zns->ns_id = ns->ns_id; @@ -194,6 +189,8 @@ int zebra_ns_final_shutdown(struct ns *ns, kernel_terminate(zns, true); + zebra_ns_delete(ns); + return NS_WALK_CONTINUE; } @@ -241,10 +238,3 @@ int zebra_ns_init(void) return 0; } - -int zebra_ns_config_write(struct vty *vty, struct ns *ns) -{ - if (ns && ns->name != NULL) - vty_out(vty, " netns %s\n", ns->name); - return 0; -} diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index edf2611..8d988c3 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -18,6 +18,8 @@ extern "C" { #endif #ifdef HAVE_NETLINK +#include + /* Socket interface to kernel */ struct nlsock { int sock; @@ -47,6 +49,8 @@ struct zebra_ns { struct nlsock netlink_dplane_out; struct nlsock netlink_dplane_in; struct event *t_netlink; + + struct nlsock ge_netlink_cmd; /* command channel for generic netlink */ #endif struct route_table *if_table; @@ -66,7 +70,6 @@ int zebra_ns_early_shutdown(struct ns *ns, int zebra_ns_final_shutdown(struct ns *ns, void *param_in __attribute__((unused)), void **param_out __attribute__((unused))); -int zebra_ns_config_write(struct vty *vty, struct ns *ns); void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index c04c5f5..7f36357 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -234,7 +234,7 @@ struct pbr_rule_unique_lookup { struct zebra_pbr_rule *rule; int sock; uint32_t unique; - char ifname[INTERFACE_NAMSIZ + 1]; + char ifname[IFNAMSIZ + 1]; vrf_id_t vrf_id; }; @@ -259,7 +259,7 @@ pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule) struct pbr_rule_unique_lookup pul; pul.unique = zrule->rule.unique; - strlcpy(pul.ifname, zrule->rule.ifname, INTERFACE_NAMSIZ); + strlcpy(pul.ifname, zrule->rule.ifname, IFNAMSIZ); pul.rule = NULL; pul.vrf_id = zrule->vrf_id; pul.sock = zrule->sock; diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index ddc1460..1e4b5cd 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -46,7 +46,7 @@ struct zebra_pbr_rule { struct pbr_rule rule; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; struct zebra_pbr_action action; diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index a678e71..d7d752f 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -86,7 +86,6 @@ struct zebra_ptm_cb ptm_cb; static int zebra_ptm_socket_init(void); void zebra_ptm_sock_read(struct event *thread); -static void zebra_ptm_install_commands(void); static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt); void zebra_bfd_peer_replay_req(void); void zebra_ptm_send_status_req(void); @@ -115,7 +114,6 @@ void zebra_ptm_init(void) } ptm_cb.pid = getpid(); - zebra_ptm_install_commands(); snprintf(buf, sizeof(buf), "%s", FRR_PTM_NAME); ptm_hdl = ptm_lib_register(buf, NULL, zebra_ptm_handle_msg_cb, @@ -240,10 +238,7 @@ void zebra_ptm_connect(struct event *t) } } -DEFUN (zebra_ptm_enable, - zebra_ptm_enable_cmd, - "ptm-enable", - "Enable neighbor check with specified topology\n") +void zebra_global_ptm_enable(void) { struct vrf *vrf; struct interface *ifp; @@ -266,27 +261,16 @@ DEFUN (zebra_ptm_enable, } zebra_ptm_connect(NULL); - - return CMD_SUCCESS; } -DEFUN (no_zebra_ptm_enable, - no_zebra_ptm_enable_cmd, - "no ptm-enable", - NO_STR - "Enable neighbor check with specified topology\n") +void zebra_global_ptm_disable(void) { ptm_cb.ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; zebra_ptm_reset_status(1); - return CMD_SUCCESS; } -DEFUN (zebra_ptm_enable_if, - zebra_ptm_enable_if_cmd, - "ptm-enable", - "Enable neighbor check with specified topology\n") +void zebra_if_ptm_enable(struct interface *ifp) { - VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *if_data; int old_ptm_enable; int send_linkdown = 0; @@ -295,7 +279,7 @@ DEFUN (zebra_ptm_enable_if, if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC; if (ifp->ifindex == IFINDEX_INTERNAL) { - return CMD_SUCCESS; + return; } old_ptm_enable = ifp->ptm_enable; @@ -312,19 +296,12 @@ DEFUN (zebra_ptm_enable_if, if_down(ifp); } } - - return CMD_SUCCESS; } -DEFUN (no_zebra_ptm_enable_if, - no_zebra_ptm_enable_if_cmd, - "no ptm-enable", - NO_STR - "Enable neighbor check with specified topology\n") +void zebra_if_ptm_disable(struct interface *ifp) { - VTY_DECLVAR_CONTEXT(interface, ifp); - int send_linkup = 0; struct zebra_if *if_data; + int send_linkup = 0; if ((ifp->ifindex != IFINDEX_INTERNAL) && (ifp->ptm_enable)) { if (!if_is_operative(ifp)) @@ -341,17 +318,6 @@ DEFUN (no_zebra_ptm_enable_if, if_data = ifp->info; if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; - - return CMD_SUCCESS; -} - - -void zebra_ptm_write(struct vty *vty) -{ - if (ptm_cb.ptm_enable) - vty_out(vty, "ptm-enable\n"); - - return; } static int zebra_ptm_socket_init(void) @@ -394,14 +360,6 @@ static int zebra_ptm_socket_init(void) return sock; } -static void zebra_ptm_install_commands(void) -{ - install_element(CONFIG_NODE, &zebra_ptm_enable_cmd); - install_element(CONFIG_NODE, &no_zebra_ptm_enable_cmd); - install_element(INTERFACE_NODE, &zebra_ptm_enable_if_cmd); - install_element(INTERFACE_NODE, &no_zebra_ptm_enable_if_cmd); -} - /* BFD session goes down, send message to the protocols. */ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, struct prefix *sp, int status, @@ -678,7 +636,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) uint8_t detect_mul; unsigned int min_rx_timer; unsigned int min_tx_timer; - char if_name[INTERFACE_NAMSIZ]; + char if_name[IFNAMSIZ]; uint8_t len; void *out_ctxt; char buf[INET6_ADDRSTRLEN]; @@ -841,7 +799,7 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) struct prefix src_p; struct prefix dst_p; uint8_t multi_hop; - char if_name[INTERFACE_NAMSIZ]; + char if_name[IFNAMSIZ]; uint8_t len; char buf[INET6_ADDRSTRLEN]; char tmp_buf[64]; @@ -1165,12 +1123,6 @@ void zebra_ptm_if_set_ptm_state(struct interface *ifp, ifp->ptm_enable = zebra_ifp->ptm_enable; } -void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp) -{ - if (zebra_ifp->ptm_enable == ZEBRA_IF_PTM_ENABLE_OFF) - vty_out(vty, " no ptm-enable\n"); -} - #else /* HAVE_BFDD */ /* @@ -1533,16 +1485,6 @@ void zebra_ptm_show_status(struct vty *vty __attribute__((__unused__)), /* NOTHING */ } -void zebra_ptm_write(struct vty *vty __attribute__((__unused__))) -{ - /* NOTHING */ -} - -void zebra_ptm_if_write(struct vty *vty __attribute__((__unused__)), - struct zebra_if *zifp __attribute__((__unused__))) -{ - /* NOTHING */ -} void zebra_ptm_if_set_ptm_state(struct interface *i __attribute__((__unused__)), struct zebra_if *zi __attribute__((__unused__))) { diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h index 4d09474..20a53e2 100644 --- a/zebra/zebra_ptm.h +++ b/zebra/zebra_ptm.h @@ -58,9 +58,15 @@ struct zebra_ptm_cb { void zebra_ptm_init(void); void zebra_ptm_finish(void); void zebra_ptm_connect(struct event *t); -void zebra_ptm_write(struct vty *vty); int zebra_ptm_get_enable_state(void); +#if HAVE_BFDD == 0 +void zebra_global_ptm_enable(void); +void zebra_global_ptm_disable(void); +void zebra_if_ptm_enable(struct interface *ifp); +void zebra_if_ptm_disable(struct interface *ifp); +#endif + /* ZAPI message handlers */ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS); void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS); @@ -74,7 +80,6 @@ void zebra_ptm_show_status(struct vty *vty, json_object *json, void zebra_ptm_if_init(struct zebra_if *zebra_ifp); void zebra_ptm_if_set_ptm_state(struct interface *ifp, struct zebra_if *zebra_ifp); -void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp); #ifdef __cplusplus } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index f76bf74..deed3b6 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -377,15 +377,18 @@ static int zebra_pw_client_close(struct zserv *client) return 0; } -void zebra_pw_init(struct zebra_vrf *zvrf) +static void zebra_pw_init(void) +{ + hook_register(zserv_client_close, zebra_pw_client_close); +} + +void zebra_pw_init_vrf(struct zebra_vrf *zvrf) { RB_INIT(zebra_pw_head, &zvrf->pseudowires); RB_INIT(zebra_static_pw_head, &zvrf->static_pseudowires); - - hook_register(zserv_client_close, zebra_pw_client_close); } -void zebra_pw_exit(struct zebra_vrf *zvrf) +void zebra_pw_exit_vrf(struct zebra_vrf *zvrf) { struct zebra_pw *pw; @@ -396,6 +399,11 @@ void zebra_pw_exit(struct zebra_vrf *zvrf) } } +void zebra_pw_terminate(void) +{ + hook_unregister(zserv_client_close, zebra_pw_client_close); +} + DEFUN_NOSH (pseudowire_if, pseudowire_if_cmd, "pseudowire IFNAME", @@ -837,4 +845,6 @@ void zebra_pw_vty_init(void) install_element(VIEW_NODE, &show_pseudowires_cmd); install_element(VIEW_NODE, &show_pseudowires_detail_cmd); + + zebra_pw_init(); } diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h index fd94d5e..431d663 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -24,7 +24,7 @@ extern "C" { struct zebra_pw { RB_ENTRY(zebra_pw) pw_entry, static_pw_entry; vrf_id_t vrf_id; - char ifname[INTERFACE_NAMSIZ]; + char ifname[IFNAMSIZ]; ifindex_t ifindex; int type; int af; @@ -60,8 +60,9 @@ void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); void zebra_pw_update(struct zebra_pw *); void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus); -void zebra_pw_init(struct zebra_vrf *); -void zebra_pw_exit(struct zebra_vrf *); +void zebra_pw_init_vrf(struct zebra_vrf *); +void zebra_pw_exit_vrf(struct zebra_vrf *); +void zebra_pw_terminate(void); void zebra_pw_vty_init(void); #ifdef __cplusplus diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6538b5e..5b95d86 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -5,6 +5,10 @@ #include +#ifdef GNU_LINUX +#include +#endif + #include "command.h" #include "if.h" #include "linklist.h" @@ -25,6 +29,7 @@ #include "frr_pthread.h" #include "printfrr.h" #include "frrscript.h" +#include "frrdistance.h" #include "zebra/zebra_router.h" #include "zebra/connected.h" @@ -103,6 +108,9 @@ static const struct { [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, ZEBRA_CONNECT_DISTANCE_DEFAULT, META_QUEUE_CONNECTED}, + [ZEBRA_ROUTE_LOCAL] = {ZEBRA_ROUTE_LOCAL, + ZEBRA_CONNECT_DISTANCE_DEFAULT, + META_QUEUE_CONNECTED}, [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, ZEBRA_STATIC_DISTANCE_DEFAULT, META_QUEUE_STATIC}, @@ -130,6 +138,7 @@ static const struct { [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, ZEBRA_MAX_DISTANCE_DEFAULT, META_QUEUE_OTHER}, [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, ZEBRA_TABLE_DISTANCE_DEFAULT, META_QUEUE_STATIC}, + [ZEBRA_ROUTE_TABLE_DIRECT] = {ZEBRA_ROUTE_TABLE_DIRECT, ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT, META_QUEUE_STATIC}, [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, ZEBRA_LDP_DISTANCE_DEFAULT, META_QUEUE_OTHER}, [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, ZEBRA_EBGP_DISTANCE_DEFAULT, @@ -177,6 +186,7 @@ struct wq_nhg_wrapper { struct nhg_ctx *ctx; struct nhg_hash_entry *nhe; } u; + bool deletion; }; #define WQ_NHG_WRAPPER_TYPE_CTX 0x01 @@ -330,7 +340,7 @@ static char *_dump_re_status(const struct route_entry *re, char *buf, : "", CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED) ? "Queued " : "", CHECK_FLAG(re->status, ROUTE_ENTRY_ROUTE_REPLACING) - ? "Replacing" + ? "Replacing " : "", CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) ? "Installed " : "", @@ -521,7 +531,8 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, if (rn) route_lock_node(rn); } else { - if (match->type != ZEBRA_ROUTE_CONNECT) { + if (match->type != ZEBRA_ROUTE_CONNECT && + match->type != ZEBRA_ROUTE_LOCAL) { if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) return NULL; @@ -623,7 +634,8 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) if (!match) return NULL; - if (match->type == ZEBRA_ROUTE_CONNECT) + if (match->type == ZEBRA_ROUTE_CONNECT || + match->type == ZEBRA_ROUTE_LOCAL) return match; if (CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) @@ -1122,27 +1134,15 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); } -/* Check if 'alternate' RIB entry is better than 'current'. */ -static struct route_entry *rib_choose_best(struct route_entry *current, - struct route_entry *alternate) +static struct route_entry *rib_choose_best_type(uint8_t route_type, + struct route_entry *current, + struct route_entry *alternate) { - if (current == NULL) - return alternate; - - /* filter route selection in following order: - * - connected beats other types - * - if both connected, loopback or vrf wins - * - lower distance beats higher - * - lower metric beats higher for equal distance - * - last, hence oldest, route wins tie break. - */ - - /* Connected routes. Check to see if either are a vrf - * or loopback interface. If not, pick the last connected - * route of the set of lowest metric connected routes. + /* + * We know that alternate and current are now non-NULL */ - if (alternate->type == ZEBRA_ROUTE_CONNECT) { - if (current->type != ZEBRA_ROUTE_CONNECT) + if (alternate->type == route_type) { + if (current->type != route_type) return alternate; /* both are connected. are either loop or vrf? */ @@ -1171,7 +1171,41 @@ static struct route_entry *rib_choose_best(struct route_entry *current, return current; } - if (current->type == ZEBRA_ROUTE_CONNECT) + return NULL; +} + +/* Check if 'alternate' RIB entry is better than 'current'. */ +static struct route_entry *rib_choose_best(struct route_entry *current, + struct route_entry *alternate) +{ + struct route_entry *possible; + + if (current == NULL) + return alternate; + + /* filter route selection in following order: + * - Local beats Connected + * - connected beats other types + * - if both connected, loopback or vrf wins + * - lower distance beats higher + * - lower metric beats higher for equal distance + * - last, hence oldest, route wins tie break. + */ + + /* Connected or Local routes. Check to see if either are a vrf + * or loopback interface. If not, pick the last connected + * route of the set of lowest metric connected routes. + */ + possible = rib_choose_best_type(ZEBRA_ROUTE_LOCAL, current, alternate); + if (possible) + return possible; + + possible = rib_choose_best_type(ZEBRA_ROUTE_CONNECT, current, alternate); + if (possible) + return possible; + + if (current->type == ZEBRA_ROUTE_CONNECT || + current->type == ZEBRA_ROUTE_LOCAL) return current; /* higher distance loses */ @@ -1200,6 +1234,7 @@ static void rib_process(struct route_node *rn) rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; struct vrf *vrf; + struct route_entry *proto_re_changed = NULL; vrf_id_t vrf_id = VRF_UNKNOWN; @@ -1269,6 +1304,7 @@ static void rib_process(struct route_node *rn) * skip it. */ if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { + proto_re_changed = re; if (!nexthop_active_update(rn, re)) { const struct prefix *p; struct rib_table_info *info; @@ -1354,6 +1390,8 @@ static void rib_process(struct route_node *rn) * new_selected --- RE entry that is newly SELECTED * old_fib --- RE entry currently in kernel FIB * new_fib --- RE entry that is newly to be in kernel FIB + * proto_re_changed -- RE that is the last changed entry in the + * list of RE's. * * new_selected will get SELECTED flag, and is going to be redistributed * the zclients. new_fib (which can be new_selected) will be installed @@ -1408,6 +1446,22 @@ static void rib_process(struct route_node *rn) } } + /* + * If zebra has a new_selected and a proto_re_changed + * entry that was not the old selected and the protocol + * is different, zebra should notify the upper level + * protocol that the sent down entry was not selected + */ + if (new_selected && proto_re_changed && + proto_re_changed != old_selected && + new_selected->type != proto_re_changed->type) { + struct rib_table_info *info = srcdest_rnode_table_info(rn); + + zsend_route_notify_owner(rn, proto_re_changed, + ZAPI_ROUTE_BETTER_ADMIN_WON, info->afi, + info->safi); + } + /* Update fib according to selection results */ if (new_fib && old_fib) rib_process_update_fib(zvrf, rn, old_fib, new_fib); @@ -1506,7 +1560,8 @@ static bool rib_route_match_ctx(const struct route_entry *re, } else if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != dplane_ctx_get_metric(ctx)) { result = false; - } else if (re->type == ZEBRA_ROUTE_CONNECT) { + } else if (re->type == ZEBRA_ROUTE_CONNECT || + re->type == ZEBRA_ROUTE_LOCAL) { result = nexthop_group_equal_no_recurse( &re->nhe->nhg, dplane_ctx_get_ng(ctx)); } @@ -1564,7 +1619,7 @@ static bool rib_compare_routes(const struct route_entry *re1, * v6 link-locals, and we also support multiple addresses in the same * subnet on a single interface. */ - if (re1->type != ZEBRA_ROUTE_CONNECT) + if (re1->type != ZEBRA_ROUTE_CONNECT && re1->type != ZEBRA_ROUTE_LOCAL) return true; return false; @@ -2014,9 +2069,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) UNSET_FLAG(old_re->status, ROUTE_ENTRY_QUEUED); } - switch (op) { - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: + if (op == DPLANE_OP_ROUTE_INSTALL || op == DPLANE_OP_ROUTE_UPDATE) { if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { if (re) { UNSET_FLAG(re->status, ROUTE_ENTRY_FAILED); @@ -2107,8 +2160,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), dplane_ctx_get_table(ctx), rn); } - break; - case DPLANE_OP_ROUTE_DELETE: + } else if (op == DPLANE_OP_ROUTE_DELETE) { rt_delete = true; if (re) SET_FLAG(re->status, ROUTE_ENTRY_FAILED); @@ -2147,61 +2199,6 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if ((re && RIB_SYSTEM_ROUTE(re)) || (old_re && RIB_SYSTEM_ROUTE(old_re))) zebra_rib_fixup_system(rn); - break; - - case DPLANE_OP_NONE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - case DPLANE_OP_NEIGH_DISCOVER: - case DPLANE_OP_BR_PORT_UPDATE: - case DPLANE_OP_IPTABLE_ADD: - case DPLANE_OP_IPTABLE_DELETE: - case DPLANE_OP_IPSET_ADD: - case DPLANE_OP_IPSET_DELETE: - case DPLANE_OP_IPSET_ENTRY_ADD: - case DPLANE_OP_IPSET_ENTRY_DELETE: - case DPLANE_OP_NEIGH_IP_INSTALL: - case DPLANE_OP_NEIGH_IP_DELETE: - case DPLANE_OP_NEIGH_TABLE_UPDATE: - case DPLANE_OP_GRE_SET: - case DPLANE_OP_INTF_ADDR_ADD: - case DPLANE_OP_INTF_ADDR_DEL: - case DPLANE_OP_INTF_NETCONFIG: - case DPLANE_OP_INTF_INSTALL: - case DPLANE_OP_INTF_UPDATE: - case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_QDISC_INSTALL: - case DPLANE_OP_TC_QDISC_UNINSTALL: - case DPLANE_OP_TC_CLASS_ADD: - case DPLANE_OP_TC_CLASS_DELETE: - case DPLANE_OP_TC_CLASS_UPDATE: - case DPLANE_OP_TC_FILTER_ADD: - case DPLANE_OP_TC_FILTER_DELETE: - case DPLANE_OP_TC_FILTER_UPDATE: - case DPLANE_OP_STARTUP_STAGE: - break; } zebra_rib_evaluate_rn_nexthops(rn, seq, rt_delete); @@ -2535,7 +2532,7 @@ static void process_subq_evpn(struct listnode *lnode) static void process_subq_nhg(struct listnode *lnode) { struct nhg_ctx *ctx; - struct nhg_hash_entry *nhe, *newnhe; + struct nhg_hash_entry *nhe, *newnhe, *oldnhe; struct wq_nhg_wrapper *w; uint8_t qindex = META_QUEUE_NHG; @@ -2567,15 +2564,33 @@ static void process_subq_nhg(struct listnode *lnode) subqueue2str(qindex)); /* 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); + if (w->deletion) { + /* + * Delete the received nhg id + */ + oldnhe = zebra_nhg_proto_del(nhe->id, nhe->type); + if (oldnhe) { + zsend_nhg_notify(nhe->type, nhe->zapi_instance, + nhe->zapi_session, nhe->id, + ZAPI_NHG_REMOVED); + zebra_nhg_decrement_ref(oldnhe); + } else + zsend_nhg_notify(nhe->type, nhe->zapi_instance, + nhe->zapi_session, nhe->id, + ZAPI_NHG_REMOVE_FAIL); - /* Report error to daemon via ZAPI */ - if (newnhe == NULL) - zsend_nhg_notify(nhe->type, nhe->zapi_instance, - nhe->zapi_session, nhe->id, - ZAPI_NHG_FAIL_INSTALL); + } else { + newnhe = zebra_nhg_proto_add(nhe->id, nhe->type, + nhe->zapi_instance, + nhe->zapi_session, + &nhe->nhg, 0); + + /* Report error to daemon via ZAPI */ + if (newnhe == NULL) + zsend_nhg_notify(nhe->type, nhe->zapi_instance, + nhe->zapi_session, nhe->id, + ZAPI_NHG_FAIL_INSTALL); + } /* Free temp nhe - we own that memory. */ zebra_nhg_free(nhe); @@ -2678,6 +2693,7 @@ static void early_route_memory_free(struct zebra_early_route *ere) if (ere->re_nhe) zebra_nhg_free(ere->re_nhe); + zapi_re_opaque_free(ere->re->opaque); XFREE(MTYPE_RE, ere->re); XFREE(MTYPE_WQ_WRAPPER, ere); } @@ -2859,11 +2875,10 @@ static void process_subq_early_route_add(struct zebra_early_route *ere) SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); rib_addnode(rn, re, 1); + dest = rib_dest_from_rnode(rn); /* Free implicit route.*/ if (same) { - rib_dest_t *dest = rn->info; - - if (same == dest->selected_fib) + if (dest && same == dest->selected_fib) SET_FLAG(same->status, ROUTE_ENTRY_ROUTE_REPLACING); rib_delnode(rn, same); } @@ -2871,7 +2886,6 @@ static void process_subq_early_route_add(struct zebra_early_route *ere) /* See if we can remove some RE entries that are queued for * removal, but won't be considered in rib processing. */ - dest = rib_dest_from_rnode(rn); RNODE_FOREACH_RE_SAFE (rn, re, same) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) { /* If the route was used earlier, must retain it. */ @@ -2967,7 +2981,8 @@ static void process_subq_early_route_delete(struct zebra_early_route *ere) if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != ere->re->metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && + if ((re->type == ZEBRA_ROUTE_CONNECT || + re->type == ZEBRA_ROUTE_LOCAL) && (rtnh = re->nhe->nhg.nexthop) && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { if (rtnh->ifindex != nh->ifindex) @@ -3342,7 +3357,8 @@ static int rib_meta_queue_nhg_ctx_add(struct meta_queue *mq, void *data) return 0; } -static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) +static int rib_meta_queue_nhg_process(struct meta_queue *mq, void *data, + bool deletion) { struct nhg_hash_entry *nhe = NULL; uint8_t qindex = META_QUEUE_NHG; @@ -3357,6 +3373,7 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) w->type = WQ_NHG_WRAPPER_TYPE_NHG; w->u.nhe = nhe; + w->deletion = deletion; listnode_add(mq->subq[qindex], w); mq->size++; @@ -3368,6 +3385,16 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) return 0; } +static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) +{ + return rib_meta_queue_nhg_process(mq, data, false); +} + +static int rib_meta_queue_nhg_del(struct meta_queue *mq, void *data) +{ + return rib_meta_queue_nhg_process(mq, data, true); +} + static int rib_meta_queue_evpn_add(struct meta_queue *mq, void *data) { listnode_add(mq->subq[META_QUEUE_EVPN], data); @@ -3475,6 +3502,17 @@ int rib_queue_nhe_add(struct nhg_hash_entry *nhe) return mq_add_handler(nhe, rib_meta_queue_nhg_add); } +/* + * Enqueue incoming nhg from proto daemon for processing + */ +int rib_queue_nhe_del(struct nhg_hash_entry *nhe) +{ + if (nhe == NULL) + return -1; + + return mq_add_handler(nhe, rib_meta_queue_nhg_del); +} + /* * Enqueue evpn route for processing */ @@ -4083,7 +4121,6 @@ static void _route_entry_dump_nh(const struct route_entry *re, ifp ? ifp->name : "Unknown"); break; case NEXTHOP_TYPE_IPV4: - /* fallthrough */ case NEXTHOP_TYPE_IPV4_IFINDEX: inet_ntop(AF_INET, &nexthop->gate, nhname, INET6_ADDRSTRLEN); break; @@ -4267,6 +4304,12 @@ struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type, return re; } + +void zebra_rib_route_entry_free(struct route_entry *re) +{ + XFREE(MTYPE_RE, re); +} + /* * Internal route-add implementation; there are a couple of different public * signatures. Callers in this path are responsible for the memory they @@ -4917,6 +4960,7 @@ static void rib_process_dplane_results(struct event *thread) case DPLANE_OP_BR_PORT_UPDATE: case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: break; case DPLANE_OP_STARTUP_STAGE: @@ -4979,7 +5023,7 @@ static void check_route_info(void) } /* Routing information base initialize. */ -void rib_init(void) +void zebra_rib_init(void) { check_route_info(); @@ -4991,6 +5035,20 @@ void rib_init(void) zebra_dplane_init(rib_dplane_results); } +void zebra_rib_terminate(void) +{ + struct zebra_dplane_ctx *ctx; + + EVENT_OFF(t_dplane); + + ctx = dplane_ctx_dequeue(&rib_dplane_q); + while (ctx) { + dplane_ctx_fini(&ctx); + + ctx = dplane_ctx_dequeue(&rib_dplane_q); + } +} + /* * vrf_id_get_next * @@ -5044,7 +5102,7 @@ struct route_table *rib_tables_iter_next(rib_tables_iter_t *iter) iter->vrf_id = VRF_DEFAULT; iter->afi_safi_ix = -1; - /* Fall through */ + fallthrough; case RIB_TABLES_ITER_S_ITERATING: iter->afi_safi_ix++; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index aeb7ae3..b387e99 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -513,10 +513,14 @@ static bool rnh_check_re_nexthops(const struct route_entry *re, goto done; } - /* Some special checks if registration asked for them. */ + /* + * Some special checks if registration asked for them. + * LOCAL routes are by their definition not CONNECTED + * and as such should not be considered here + */ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) { - if ((re->type == ZEBRA_ROUTE_CONNECT) - || (re->type == ZEBRA_ROUTE_STATIC)) + if ((re->type == ZEBRA_ROUTE_CONNECT) || + (re->type == ZEBRA_ROUTE_STATIC)) ret = true; if (re->type == ZEBRA_ROUTE_NHRP) { diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 21aaf1d..95da789 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -14,7 +14,6 @@ #include "filter.h" #include "plist.h" #include "nexthop.h" -#include "northbound_cli.h" #include "lib/route_types.h" #include "vrf.h" #include "frrstr.h" @@ -24,6 +23,7 @@ #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" +#include "zebra/zebra_vrf.h" #include "zebra/zebra_routemap_clippy.c" @@ -36,8 +36,6 @@ struct zebra_rmap_obj { struct route_entry *re; }; -static void zebra_route_map_set_delay_timer(uint32_t value); - /* 'match tag TAG' * Match function return 1 if match is success else return 0 */ @@ -284,8 +282,8 @@ static const struct route_map_rule_cmd route_match_interface_cmd = { route_match_interface_free }; -static int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, - int rtype, afi_t afi, safi_t safi) +int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, + afi_t afi, safi_t safi) { struct route_table *table; @@ -317,8 +315,8 @@ static int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, return CMD_SUCCESS; } -static int ip_protocol_rm_del(struct zebra_vrf *zvrf, const char *rmap, - int rtype, afi_t afi, safi_t safi) +int ip_protocol_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, + afi_t afi, safi_t safi) { struct route_table *table; @@ -346,8 +344,7 @@ static int ip_protocol_rm_del(struct zebra_vrf *zvrf, const char *rmap, return CMD_SUCCESS; } -static int ip_nht_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, - int afi) +int ip_nht_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, int afi) { if (NHT_RM_NAME(zvrf, afi, rtype)) { @@ -368,8 +365,7 @@ static int ip_nht_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, return CMD_SUCCESS; } -static int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, - int afi) +int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, int afi) { if (!NHT_RM_NAME(zvrf, afi, rtype)) @@ -391,351 +387,7 @@ static int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, return CMD_SUCCESS; } -DEFPY_YANG( - match_ip_address_prefix_len, match_ip_address_prefix_len_cmd, - "match ip address prefix-len (0-32)$length", - MATCH_STR - IP_STR - "Match prefix length of IP address\n" - "Match prefix length of IP address\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf( - xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_match_ip_address_prefix_len, no_match_ip_address_prefix_len_cmd, - "no match ip address prefix-len [(0-32)]", - NO_STR - MATCH_STR - IP_STR - "Match prefix length of IP address\n" - "Match prefix length of IP address\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - match_ipv6_address_prefix_len, match_ipv6_address_prefix_len_cmd, - "match ipv6 address prefix-len (0-128)$length", - MATCH_STR - IPV6_STR - "Match prefix length of IPv6 address\n" - "Match prefix length of IPv6 address\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf( - xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_match_ipv6_address_prefix_len, no_match_ipv6_address_prefix_len_cmd, - "no match ipv6 address prefix-len [(0-128)]", - NO_STR - MATCH_STR - IPV6_STR - "Match prefix length of IPv6 address\n" - "Match prefix length of IPv6 address\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - match_ip_nexthop_prefix_len, match_ip_nexthop_prefix_len_cmd, - "match ip next-hop prefix-len (0-32)$length", - MATCH_STR - IP_STR - "Match prefixlen of nexthop IP address\n" - "Match prefixlen of given nexthop\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf( - xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_match_ip_nexthop_prefix_len, no_match_ip_nexthop_prefix_len_cmd, - "no match ip next-hop prefix-len [(0-32)]", - NO_STR - MATCH_STR - IP_STR - "Match prefixlen of nexthop IP address\n" - "Match prefix length of nexthop\n" - "Prefix length\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - match_source_protocol, match_source_protocol_cmd, - "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto", - MATCH_STR - "Match protocol via which the route was learnt\n" - FRR_REDIST_HELP_STR_ZEBRA) -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:source-protocol']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/frr-zebra-route-map:source-protocol", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_match_source_protocol, no_match_source_protocol_cmd, - "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]", - NO_STR - MATCH_STR - "Match protocol via which the route was learnt\n" - FRR_REDIST_HELP_STR_ZEBRA) -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:source-protocol']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - match_source_instance, match_source_instance_cmd, - "match source-instance (0-255)$instance", - MATCH_STR - "Match the protocol's instance number\n" - "The instance number\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:source-instance']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/frr-zebra-route-map:source-instance", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, instance_str); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_match_source_instance, no_match_source_instance_cmd, - "no match source-instance [(0-255)]", - NO_STR MATCH_STR - "Match the protocol's instance number\n" - "The instance number\n") -{ - const char *xpath = - "./match-condition[condition='frr-zebra-route-map:source-instance']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -/* set functions */ - -DEFPY_YANG( - set_src, set_src_cmd, - "set src ", - SET_STR - "src address for route\n" - "IPv4 src address\n" - "IPv6 src address\n") -{ - const char *xpath = - "./set-action[action='frr-zebra-route-map:src-address']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - if (addrv4_str) { - snprintf( - xpath_value, sizeof(xpath_value), - "%s/rmap-set-action/frr-zebra-route-map:ipv4-src-address", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, - addrv4_str); - } else { - snprintf( - xpath_value, sizeof(xpath_value), - "%s/rmap-set-action/frr-zebra-route-map:ipv6-src-address", - xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, - addrv6_str); - } - - return nb_cli_apply_changes(vty, NULL); -} - -DEFPY_YANG( - no_set_src, no_set_src_cmd, - "no set src []", - NO_STR - SET_STR - "Source address for route\n" - "IPv4 address\n" - "IPv6 address\n") -{ - const char *xpath = - "./set-action[action='frr-zebra-route-map:src-address']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN_YANG (zebra_route_map_timer, - zebra_route_map_timer_cmd, - "zebra route-map delay-timer (0-600)", - ZEBRA_STR - "Set route-map parameters\n" - "Time to wait before route-map updates are processed\n" - "0 means route-map changes are run immediately instead of delaying\n") -{ - int idx_number = 3; - uint32_t rmap_delay_timer; - - rmap_delay_timer = strtoul(argv[idx_number]->arg, NULL, 10); - zebra_route_map_set_delay_timer(rmap_delay_timer); - - return (CMD_SUCCESS); -} - -DEFUN_YANG (no_zebra_route_map_timer, - no_zebra_route_map_timer_cmd, - "no zebra route-map delay-timer [(0-600)]", - NO_STR - ZEBRA_STR - "Set route-map parameters\n" - "Reset delay-timer to default value, 30 secs\n" - "0 means route-map changes are run immediately instead of delaying\n") -{ - zebra_route_map_set_delay_timer(ZEBRA_RMAP_DEFAULT_UPDATE_TIMER); - - return (CMD_SUCCESS); -} - -DEFPY_YANG (ip_protocol, - ip_protocol_cmd, - "ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA - " $proto route-map ROUTE-MAP$rmap", - IP_STR - "Filter routing info exchanged between zebra and protocol\n" - FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route-map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(proto); - assert(rmap); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_protocol_rm_add(zvrf, rmap, rtype, AFI_IP, SAFI_UNICAST); - - return ret; -} - -DEFPY_YANG (no_ip_protocol, - no_ip_protocol_cmd, - "no ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA - " $proto [route-map ROUTE-MAP$rmap]", - NO_STR - IP_STR - "Stop filtering routing info between zebra and protocol\n" - FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route-map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_protocol_rm_del(zvrf, rmap, rtype, AFI_IP, SAFI_UNICAST); - - return ret; -} - -DEFPY_YANG (show_ip_protocol, +DEFPY (show_ip_protocol, show_ip_protocol_cmd, "show ip protocol [vrf ]", SHOW_STR @@ -748,75 +400,7 @@ DEFPY_YANG (show_ip_protocol, return ret; } -DEFPY_YANG (ipv6_protocol, - ipv6_protocol_cmd, - "ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA - " $proto route-map ROUTE-MAP$rmap", - IP6_STR - "Filter IPv6 routing info exchanged between zebra and protocol\n" - FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route-map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(rmap); - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_protocol_rm_add(zvrf, rmap, rtype, AFI_IP6, SAFI_UNICAST); - - return ret; -} - -DEFPY_YANG (no_ipv6_protocol, - no_ipv6_protocol_cmd, - "no ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA - " $proto [route-map ROUTE-MAP$rmap]", - NO_STR - IP6_STR - "Stop filtering IPv6 routing info between zebra and protocol\n" - FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route-map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_protocol_rm_del(zvrf, rmap, rtype, AFI_IP6, SAFI_UNICAST); - - return ret; -} - -DEFPY_YANG (show_ipv6_protocol, +DEFPY (show_ipv6_protocol, show_ipv6_protocol_cmd, "show ipv6 protocol [vrf ]", SHOW_STR @@ -829,76 +413,7 @@ DEFPY_YANG (show_ipv6_protocol, return ret; } -DEFPY_YANG (ip_protocol_nht_rmap, - ip_protocol_nht_rmap_cmd, - "ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA - " $proto route-map ROUTE-MAP$rmap", - IP_STR - "Filter Next Hop tracking route resolution\n" - FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route map\n" - "Route map name\n") -{ - - int ret, rtype; - - assert(proto); - assert(rmap); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_nht_rm_add(zvrf, rmap, rtype, AFI_IP); - - return ret; -} - -DEFPY_YANG (no_ip_protocol_nht_rmap, - no_ip_protocol_nht_rmap_cmd, - "no ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA - " $proto route-map [ROUTE-MAP$rmap]", - NO_STR - IP_STR - "Filter Next Hop tracking route resolution\n" - FRR_IP_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_nht_rm_del(zvrf, rmap, rtype, AFI_IP); - - return ret; -} - -DEFPY_YANG (show_ip_protocol_nht, +DEFPY (show_ip_protocol_nht, show_ip_protocol_nht_cmd, "show ip nht route-map [vrf ] [json]", SHOW_STR @@ -917,75 +432,7 @@ DEFPY_YANG (show_ip_protocol_nht, return ret; } -DEFPY_YANG (ipv6_protocol_nht_rmap, - ipv6_protocol_nht_rmap_cmd, - "ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA - " $proto route-map ROUTE-MAP$rmap", - IP6_STR - "Filter Next Hop tracking route resolution\n" - FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(rmap); - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_nht_rm_add(zvrf, rmap, rtype, AFI_IP6); - - return ret; -} - -DEFPY_YANG (no_ipv6_protocol_nht_rmap, - no_ipv6_protocol_nht_rmap_cmd, - "no ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA - " $proto [route-map ROUTE-MAP$rmap]", - NO_STR - IP6_STR - "Filter Next Hop tracking route resolution\n" - FRR_IP6_PROTOCOL_MAP_HELP_STR_ZEBRA - "Specify route map\n" - "Route map name\n") -{ - int ret, rtype; - - assert(proto); - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (strcasecmp(proto, "any") == 0) - rtype = ZEBRA_ROUTE_MAX; - else - rtype = proto_name2num(proto); - if (rtype < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = ip_nht_rm_del(zvrf, rmap, rtype, AFI_IP6); - - return ret; -} - -DEFPY_YANG (show_ipv6_protocol_nht, +DEFPY (show_ipv6_protocol_nht, show_ipv6_protocol_nht_cmd, "show ipv6 nht route-map [vrf ] [json]", SHOW_STR @@ -1736,7 +1183,7 @@ static void zebra_route_map_update_timer(struct event *thread) */ } -static void zebra_route_map_set_delay_timer(uint32_t value) +void zebra_route_map_set_delay_timer(uint32_t value) { zebra_rmap_update_timer = value; if (!value && zebra_t_rmap_update) { @@ -1903,88 +1350,14 @@ void zebra_routemap_vrf_delete(struct zebra_vrf *zvrf) } } -/* ip protocol configuration write function */ -void zebra_routemap_config_write_protocol(struct vty *vty, - struct zebra_vrf *zvrf) -{ - int i; - char space[2]; - - memset(space, 0, sizeof(space)); - - if (zvrf_id(zvrf) != VRF_DEFAULT) - snprintf(space, sizeof(space), "%s", " "); - - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (PROTO_RM_NAME(zvrf, AFI_IP, i)) - vty_out(vty, "%sip protocol %s route-map %s\n", space, - zebra_route_string(i), - PROTO_RM_NAME(zvrf, AFI_IP, i)); - - if (PROTO_RM_NAME(zvrf, AFI_IP6, i)) - vty_out(vty, "%sipv6 protocol %s route-map %s\n", space, - zebra_route_string(i), - PROTO_RM_NAME(zvrf, AFI_IP6, i)); - - if (NHT_RM_NAME(zvrf, AFI_IP, i)) - vty_out(vty, "%sip nht %s route-map %s\n", space, - zebra_route_string(i), - NHT_RM_NAME(zvrf, AFI_IP, i)); - - if (NHT_RM_NAME(zvrf, AFI_IP6, i)) - vty_out(vty, "%sipv6 nht %s route-map %s\n", space, - zebra_route_string(i), - NHT_RM_NAME(zvrf, AFI_IP6, i)); - } - - if (PROTO_RM_NAME(zvrf, AFI_IP, ZEBRA_ROUTE_MAX)) - vty_out(vty, "%sip protocol %s route-map %s\n", space, "any", - PROTO_RM_NAME(zvrf, AFI_IP, ZEBRA_ROUTE_MAX)); - - if (PROTO_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX)) - vty_out(vty, "%sipv6 protocol %s route-map %s\n", space, "any", - PROTO_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX)); - - if (NHT_RM_NAME(zvrf, AFI_IP, ZEBRA_ROUTE_MAX)) - vty_out(vty, "%sip nht %s route-map %s\n", space, "any", - NHT_RM_NAME(zvrf, AFI_IP, ZEBRA_ROUTE_MAX)); - - if (NHT_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX)) - vty_out(vty, "%sipv6 nht %s route-map %s\n", space, "any", - NHT_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX)); - - if (zvrf_id(zvrf) == VRF_DEFAULT - && zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER) - vty_out(vty, "zebra route-map delay-timer %d\n", - zebra_rmap_update_timer); -} - void zebra_route_map_init(void) { - install_element(CONFIG_NODE, &ip_protocol_cmd); - install_element(CONFIG_NODE, &no_ip_protocol_cmd); - install_element(VRF_NODE, &ip_protocol_cmd); - install_element(VRF_NODE, &no_ip_protocol_cmd); install_element(VIEW_NODE, &show_ip_protocol_cmd); - install_element(CONFIG_NODE, &ipv6_protocol_cmd); - install_element(CONFIG_NODE, &no_ipv6_protocol_cmd); - install_element(VRF_NODE, &ipv6_protocol_cmd); - install_element(VRF_NODE, &no_ipv6_protocol_cmd); install_element(VIEW_NODE, &show_ipv6_protocol_cmd); - install_element(CONFIG_NODE, &ip_protocol_nht_rmap_cmd); - install_element(CONFIG_NODE, &no_ip_protocol_nht_rmap_cmd); - install_element(VRF_NODE, &ip_protocol_nht_rmap_cmd); - install_element(VRF_NODE, &no_ip_protocol_nht_rmap_cmd); install_element(VIEW_NODE, &show_ip_protocol_nht_cmd); - install_element(CONFIG_NODE, &ipv6_protocol_nht_rmap_cmd); - install_element(CONFIG_NODE, &no_ipv6_protocol_nht_rmap_cmd); - install_element(VRF_NODE, &ipv6_protocol_nht_rmap_cmd); - install_element(VRF_NODE, &no_ipv6_protocol_nht_rmap_cmd); install_element(VIEW_NODE, &show_ipv6_protocol_nht_cmd); - install_element(CONFIG_NODE, &zebra_route_map_timer_cmd); - install_element(CONFIG_NODE, &no_zebra_route_map_timer_cmd); - route_map_init(); + route_map_init_new(true); route_map_add_hook(zebra_route_map_add); route_map_delete_hook(zebra_route_map_delete); @@ -2038,19 +1411,4 @@ void zebra_route_map_init(void) /* */ route_map_install_set(&route_set_src_cmd); - /* */ - install_element(RMAP_NODE, &match_ip_nexthop_prefix_len_cmd); - install_element(RMAP_NODE, &no_match_ip_nexthop_prefix_len_cmd); - install_element(RMAP_NODE, &match_ip_address_prefix_len_cmd); - install_element(RMAP_NODE, &match_ipv6_address_prefix_len_cmd); - install_element(RMAP_NODE, &no_match_ipv6_address_prefix_len_cmd); - install_element(RMAP_NODE, &no_match_ip_address_prefix_len_cmd); - install_element(RMAP_NODE, &match_source_protocol_cmd); - install_element(RMAP_NODE, &no_match_source_protocol_cmd); - install_element(RMAP_NODE, &match_source_instance_cmd); - install_element(RMAP_NODE, &no_match_source_instance_cmd); - - /* */ - install_element(RMAP_NODE, &set_src_cmd); - install_element(RMAP_NODE, &no_set_src_cmd); } diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index fceb53c..2039e80 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -14,8 +14,6 @@ extern "C" { #endif extern void zebra_route_map_init(void); -extern void zebra_routemap_config_write_protocol(struct vty *vty, - struct zebra_vrf *vrf); extern char *zebra_get_import_table_route_map(afi_t afi, uint32_t table); extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, uint32_t table); @@ -35,6 +33,16 @@ extern route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, struct route_entry *re, struct nexthop *nexthop); +extern void zebra_route_map_set_delay_timer(uint32_t value); +extern int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, + int rtype, afi_t afi, safi_t safi); +extern int ip_protocol_rm_del(struct zebra_vrf *zvrf, const char *rmap, + int rtype, afi_t afi, safi_t safi); +extern int ip_nht_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, + int afi); +extern int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, + int afi); + extern void zebra_routemap_vrf_delete(struct zebra_vrf *zvrf); #ifdef __cplusplus diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 4caaf8a..3fd4e6e 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -70,6 +70,26 @@ struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf, return zrt; } +struct zebra_router_table *zebra_router_find_next_zrt(struct zebra_vrf *zvrf, + uint32_t tableid, + afi_t afi, safi_t safi) +{ + struct zebra_router_table finder; + struct zebra_router_table *zrt; + + memset(&finder, 0, sizeof(finder)); + finder.afi = afi; + finder.safi = safi; + finder.tableid = tableid; + finder.ns_id = zvrf->zns->ns_id; + zrt = RB_NFIND(zebra_router_table_head, &zrouter.tables, &finder); + if (zrt->afi == afi && zrt->safi == safi && zrt->tableid == tableid && + zrt->ns_id == finder.ns_id) + zrt = RB_NEXT(zebra_router_table_head, zrt); + + return zrt; +} + struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, safi_t safi) @@ -241,11 +261,15 @@ void zebra_router_terminate(void) zebra_pbr_ipset_entry_free); hash_clean_and_free(&zrouter.ipset_hash, zebra_pbr_ipset_free); hash_clean_and_free(&zrouter.iptable_hash, zebra_pbr_iptable_free); + hash_clean_and_free(&zrouter.filter_hash, (void (*)(void *)) zebra_tc_filter_free); + hash_clean_and_free(&zrouter.qdisc_hash, (void (*)(void *)) zebra_tc_qdisc_free); + hash_clean_and_free(&zrouter.class_hash, (void (*)(void *)) zebra_tc_class_free); #ifdef HAVE_SCRIPTING zebra_script_destroy(); #endif + zebra_vxlan_terminate(); /* OS-specific deinit */ kernel_router_terminate(); } @@ -295,10 +319,6 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack, hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal, "Zebra Router Nexthop Groups ID index"); - zrouter.rules_hash = - hash_create_size(8, zebra_pbr_rules_hash_key, - zebra_pbr_rules_hash_equal, "Rules Hash"); - zrouter.qdisc_hash = hash_create_size(8, zebra_tc_qdisc_hash_key, zebra_tc_qdisc_hash_equal, "TC (qdisc) Hash"); @@ -323,6 +343,8 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack, #endif zrouter.asic_notification_nexthop_control = false; + zrouter.nexthop_weight_scale_value = 255; + #ifdef HAVE_SCRIPTING zebra_script_init(); #endif diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index bd86cfb..3041707 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -209,6 +209,8 @@ struct zebra_router { bool notify_on_ack; bool v6_with_v4_nexthop; + bool v6_rr_semantics; + /* * If the asic is notifying us about successful nexthop * allocation/control. Some developers have made their @@ -231,6 +233,8 @@ struct zebra_router { bool allow_delete; uint8_t protodown_r_bit; + + uint64_t nexthop_weight_scale_value; }; #define GRACEFUL_RESTART_TIME 60 @@ -246,6 +250,9 @@ extern void zebra_router_terminate(void); extern struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, safi_t safi); +extern struct zebra_router_table * +zebra_router_find_next_zrt(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, + safi_t safi); extern struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, safi_t safi); diff --git a/zebra/zebra_script.c b/zebra/zebra_script.c index 9f8490a..6c34d12 100644 --- a/zebra/zebra_script.c +++ b/zebra/zebra_script.c @@ -396,6 +396,7 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx) lua_setfield(L, -2, "mtu"); } lua_setfield(L, -2, "gre"); + break; case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: @@ -414,6 +415,7 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_UPDATE: /* Not currently handled */ case DPLANE_OP_INTF_NETCONFIG: /*NYI*/ + case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: break; diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 8cab184..1c6d581 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -223,6 +223,8 @@ static int proto_trans(int type) return 1; /* other */ case ZEBRA_ROUTE_CONNECT: return 2; /* local interface */ + case ZEBRA_ROUTE_LOCAL: + return 2; case ZEBRA_ROUTE_STATIC: return 3; /* static route */ case ZEBRA_ROUTE_RIP: diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 94b93e5..bb872ef 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -17,6 +17,7 @@ #include "zebra/zebra_router.h" #include "zebra/zebra_srv6.h" #include "zebra/zebra_errors.h" +#include "zebra/ge_netlink.h" #include #include #include @@ -216,9 +217,10 @@ void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) } } +struct zebra_srv6 srv6; + struct zebra_srv6 *zebra_srv6_get_default(void) { - static struct zebra_srv6 srv6; static bool first_execution = true; if (first_execution) { @@ -408,6 +410,40 @@ int release_daemon_srv6_locator_chunks(struct zserv *client) return count; } +void zebra_srv6_encap_src_addr_set(struct in6_addr *encap_src_addr) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + + if (!encap_src_addr) + return; + + memcpy(&srv6->encap_src_addr, encap_src_addr, sizeof(struct in6_addr)); +} + +void zebra_srv6_encap_src_addr_unset(void) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + + memset(&srv6->encap_src_addr, 0, sizeof(struct in6_addr)); +} + +void zebra_srv6_terminate(void) +{ + struct srv6_locator *locator; + + if (!srv6.locators) + return; + + while (listcount(srv6.locators)) { + locator = listnode_head(srv6.locators); + + listnode_delete(srv6.locators, locator); + srv6_locator_free(locator); + } + + list_delete(&srv6.locators); +} + void zebra_srv6_init(void) { hook_register(zserv_client_close, zebra_srv6_cleanup); diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h index 51db83d..21936c3 100644 --- a/zebra/zebra_srv6.h +++ b/zebra/zebra_srv6.h @@ -19,6 +19,9 @@ /* SRv6 instance structure. */ struct zebra_srv6 { struct list *locators; + + /* Source address for SRv6 encapsulation */ + struct in6_addr encap_src_addr; }; /* declare hooks for the basic API, so that it can be specialized or served @@ -52,6 +55,7 @@ void zebra_notify_srv6_locator_add(struct srv6_locator *locator); void zebra_notify_srv6_locator_delete(struct srv6_locator *locator); extern void zebra_srv6_init(void); +extern void zebra_srv6_terminate(void); extern struct zebra_srv6 *zebra_srv6_get_default(void); extern bool zebra_srv6_is_enable(void); @@ -67,4 +71,7 @@ extern void srv6_manager_release_locator_chunk_call(struct zserv *client, extern int srv6_manager_client_disconnect_cb(struct zserv *client); extern int release_daemon_srv6_locator_chunks(struct zserv *client); +extern void zebra_srv6_encap_src_addr_set(struct in6_addr *src_addr); +extern void zebra_srv6_encap_src_addr_unset(void); + #endif /* _ZEBRA_SRV6_H */ diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index 3775d3d..c5b8505 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -61,6 +61,52 @@ static struct cmd_node srv6_loc_node = { .prompt = "%s(config-srv6-locator)# " }; +static struct cmd_node srv6_encap_node = { + .name = "srv6-encap", + .node = SRV6_ENCAP_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-encap)# " +}; + +DEFPY (show_srv6_manager, + show_srv6_manager_cmd, + "show segment-routing srv6 manager [json]", + SHOW_STR + "Segment Routing\n" + "Segment Routing SRv6\n" + "Verify SRv6 Manager\n" + JSON_STR) +{ + const bool uj = use_json(argc, argv); + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + json_object *json = NULL; + json_object *json_parameters = NULL; + json_object *json_encapsulation = NULL; + json_object *json_source_address = NULL; + + if (uj) { + json = json_object_new_object(); + json_parameters = json_object_new_object(); + json_object_object_add(json, "parameters", json_parameters); + json_encapsulation = json_object_new_object(); + json_object_object_add(json_parameters, "encapsulation", + json_encapsulation); + json_source_address = json_object_new_object(); + json_object_object_add(json_encapsulation, "sourceAddress", + json_source_address); + json_object_string_addf(json_source_address, "configured", + "%pI6", &srv6->encap_src_addr); + vty_json(vty, json); + } else { + vty_out(vty, "Parameters:\n"); + vty_out(vty, " Encapsulation:\n"); + vty_out(vty, " Source Address:\n"); + vty_out(vty, " Configured: %pI6\n", &srv6->encap_src_addr); + } + + return CMD_SUCCESS; +} + DEFUN (show_srv6_locator, show_srv6_locator_cmd, "show segment-routing srv6 locator [json]", @@ -391,6 +437,38 @@ DEFPY (locator_behavior, return CMD_SUCCESS; } +DEFUN_NOSH (srv6_encap, + srv6_encap_cmd, + "encapsulation", + "Segment Routing SRv6 encapsulation\n") +{ + vty->node = SRV6_ENCAP_NODE; + return CMD_SUCCESS; +} + +DEFPY (srv6_src_addr, + srv6_src_addr_cmd, + "source-address X:X::X:X$encap_src_addr", + "Segment Routing SRv6 source address\n" + "Specify source address for SRv6 encapsulation\n") +{ + zebra_srv6_encap_src_addr_set(&encap_src_addr); + dplane_srv6_encap_srcaddr_set(&encap_src_addr, NS_DEFAULT); + return CMD_SUCCESS; +} + +DEFPY (no_srv6_src_addr, + no_srv6_src_addr_cmd, + "no source-address [X:X::X:X$encap_src_addr]", + NO_STR + "Segment Routing SRv6 source address\n" + "Specify source address for SRv6 encapsulation\n") +{ + zebra_srv6_encap_src_addr_unset(); + dplane_srv6_encap_srcaddr_set(&in6addr_any, NS_DEFAULT); + return CMD_SUCCESS; +} + static int zebra_sr_config(struct vty *vty) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); @@ -402,6 +480,11 @@ static int zebra_sr_config(struct vty *vty) if (zebra_srv6_is_enable()) { vty_out(vty, "segment-routing\n"); vty_out(vty, " srv6\n"); + if (!IPV6_ADDR_SAME(&srv6->encap_src_addr, &in6addr_any)) { + vty_out(vty, " encapsulation\n"); + vty_out(vty, " source-address %pI6\n", + &srv6->encap_src_addr); + } vty_out(vty, " locators\n"); for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) { inet_ntop(AF_INET6, &locator->prefix.prefix, @@ -444,24 +527,30 @@ void zebra_srv6_vty_init(void) install_node(&srv6_node); install_node(&srv6_locs_node); install_node(&srv6_loc_node); + install_node(&srv6_encap_node); install_default(SEGMENT_ROUTING_NODE); install_default(SRV6_NODE); install_default(SRV6_LOCS_NODE); install_default(SRV6_LOC_NODE); + install_default(SRV6_ENCAP_NODE); /* Command for change node */ install_element(CONFIG_NODE, &segment_routing_cmd); install_element(SEGMENT_ROUTING_NODE, &srv6_cmd); install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd); install_element(SRV6_NODE, &srv6_locators_cmd); + install_element(SRV6_NODE, &srv6_encap_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd); /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); install_element(SRV6_LOC_NODE, &locator_behavior_cmd); + install_element(SRV6_ENCAP_NODE, &srv6_src_addr_cmd); + install_element(SRV6_ENCAP_NODE, &no_srv6_src_addr_cmd); /* Command for operation */ install_element(VIEW_NODE, &show_srv6_locator_cmd); install_element(VIEW_NODE, &show_srv6_locator_detail_cmd); + install_element(VIEW_NODE, &show_srv6_manager_cmd); } diff --git a/zebra/zebra_tc.c b/zebra/zebra_tc.c index 3d7e03b..1b5a57a 100644 --- a/zebra/zebra_tc.c +++ b/zebra/zebra_tc.c @@ -132,13 +132,18 @@ static void *tc_qdisc_alloc_intern(void *arg) return new; } +void zebra_tc_qdisc_free(struct zebra_tc_qdisc *qdisc) +{ + XFREE(MTYPE_TC_QDISC, qdisc); +} + static struct zebra_tc_qdisc *tc_qdisc_free(struct zebra_tc_qdisc *hash_data, bool free_data) { hash_release(zrouter.qdisc_hash, hash_data); if (free_data) { - XFREE(MTYPE_TC_QDISC, hash_data); + zebra_tc_qdisc_free(hash_data); return NULL; } @@ -178,7 +183,7 @@ void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc) new = hash_get(zrouter.qdisc_hash, qdisc, tc_qdisc_alloc_intern); (void)dplane_tc_qdisc_install(new); - XFREE(MTYPE_TC_QDISC, old); + zebra_tc_qdisc_free(old); } } else { new = hash_get(zrouter.qdisc_hash, qdisc, @@ -243,13 +248,18 @@ static void *tc_class_alloc_intern(void *arg) return new; } +void zebra_tc_class_free(struct zebra_tc_class *class) +{ + XFREE(MTYPE_TC_CLASS, class); +} + static struct zebra_tc_class *tc_class_free(struct zebra_tc_class *hash_data, bool free_data) { hash_release(zrouter.class_hash, hash_data); if (free_data) { - XFREE(MTYPE_TC_CLASS, hash_data); + zebra_tc_class_free(hash_data); return NULL; } @@ -353,13 +363,18 @@ bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2) return true; } +void zebra_tc_filter_free(struct zebra_tc_filter *filter) +{ + XFREE(MTYPE_TC_FILTER, filter); +} + static struct zebra_tc_filter *tc_filter_free(struct zebra_tc_filter *hash_data, bool free_data) { hash_release(zrouter.filter_hash, hash_data); if (free_data) { - XFREE(MTYPE_TC_FILTER, hash_data); + zebra_tc_filter_free(hash_data); return NULL; } diff --git a/zebra/zebra_tc.h b/zebra/zebra_tc.h index 335430c..814b453 100644 --- a/zebra/zebra_tc.h +++ b/zebra/zebra_tc.h @@ -42,16 +42,19 @@ uint32_t zebra_tc_qdisc_hash_key(const void *arg); bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2); void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc); void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc); +void zebra_tc_qdisc_free(struct zebra_tc_qdisc *qdisc); uint32_t zebra_tc_class_hash_key(const void *arg); bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2); void zebra_tc_class_add(struct zebra_tc_class *class); void zebra_tc_class_delete(struct zebra_tc_class *class); +void zebra_tc_class_free(struct zebra_tc_class *class); const char *tc_filter_kind2str(uint32_t type); enum tc_qdisc_kind tc_filter_str2kind(const char *type); void zebra_tc_filter_add(struct zebra_tc_filter *filter); void zebra_tc_filter_delete(struct zebra_tc_filter *filter); +void zebra_tc_filter_free(struct zebra_tc_filter *filter); void zebra_tc_filters_free(void *arg); uint32_t zebra_tc_filter_hash_key(const void *arg); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 25e6513..e464e47 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -195,7 +195,7 @@ static int zebra_vrf_disable(struct vrf *vrf) /* Cleanup Vxlan, MPLS and PW tables. */ zebra_vxlan_cleanup_tables(zvrf); zebra_mpls_cleanup_tables(zvrf); - zebra_pw_exit(zvrf); + zebra_pw_exit_vrf(zvrf); /* Remove link-local IPv4 addresses created for BGP unnumbered peering. */ @@ -265,6 +265,12 @@ static int zebra_vrf_delete(struct vrf *vrf) otable_fini(&zvrf->other_tables); XFREE(MTYPE_ZEBRA_VRF, zvrf); + + if (vrf->ns_ctxt) { + ns_delete(vrf->ns_ctxt); + vrf->ns_ctxt = NULL; + } + vrf->info = NULL; return 0; @@ -370,7 +376,7 @@ struct zebra_vrf *zebra_vrf_alloc(struct vrf *vrf) zebra_vxlan_init_tables(zvrf); zebra_mpls_init_tables(zvrf); - zebra_pw_init(zvrf); + zebra_pw_init_vrf(zvrf); zvrf->table_id = rt_table_main_id; /* by default table ID is default one */ @@ -444,141 +450,6 @@ struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id) return zvrf->table[afi][safi]; } -static int vrf_config_write(struct vty *vty) -{ - struct vrf *vrf; - struct zebra_vrf *zvrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - zvrf = vrf->info; - - if (!zvrf) - continue; - - if (zvrf_id(zvrf) == VRF_DEFAULT) { - if (zvrf->l3vni) - vty_out(vty, "vni %u%s\n", zvrf->l3vni, - is_l3vni_for_prefix_routes_only( - zvrf->l3vni) - ? " prefix-routes-only" - : ""); - - if (zvrf->zebra_rnh_ip_default_route != - SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT) - vty_out(vty, "%sip nht resolve-via-default\n", - zvrf->zebra_rnh_ip_default_route - ? "" - : "no "); - - if (zvrf->zebra_rnh_ipv6_default_route != - SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT) - vty_out(vty, "%sipv6 nht resolve-via-default\n", - zvrf->zebra_rnh_ipv6_default_route - ? "" - : "no "); - - if (zvrf->tbl_mgr - && (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)) - vty_out(vty, "ip table range %u %u\n", - zvrf->tbl_mgr->start, - zvrf->tbl_mgr->end); - } else { - vty_frame(vty, "vrf %s\n", zvrf_name(zvrf)); - if (zvrf->l3vni) - vty_out(vty, " vni %u%s\n", zvrf->l3vni, - is_l3vni_for_prefix_routes_only( - zvrf->l3vni) - ? " prefix-routes-only" - : ""); - zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); - if (zvrf->zebra_rnh_ip_default_route != - SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT) - vty_out(vty, " %sip nht resolve-via-default\n", - zvrf->zebra_rnh_ip_default_route - ? "" - : "no "); - - if (zvrf->zebra_rnh_ipv6_default_route != - SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT) - vty_out(vty, " %sipv6 nht resolve-via-default\n", - zvrf->zebra_rnh_ipv6_default_route - ? "" - : "no "); - - if (zvrf->tbl_mgr && vrf_is_backend_netns() - && (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)) - vty_out(vty, " ip table range %u %u\n", - zvrf->tbl_mgr->start, - zvrf->tbl_mgr->end); - } - - - zebra_routemap_config_write_protocol(vty, zvrf); - router_id_write(vty, zvrf); - - if (zvrf_id(zvrf) != VRF_DEFAULT) - vty_endframe(vty, "exit-vrf\n!\n"); - else - vty_out(vty, "!\n"); - } - return 0; -} - -DEFPY (vrf_netns, - vrf_netns_cmd, - "netns NAME$netns_name", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - char *pathname = ns_netns_pathname(vty, netns_name); - int ret; - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - frr_with_privs(&zserv_privs) { - ret = zebra_vrf_netns_handler_create( - vty, vrf, pathname, NS_UNKNOWN, NS_UNKNOWN, NS_UNKNOWN); - } - - return ret; -} - -DEFUN (no_vrf_netns, - no_vrf_netns_cmd, - "no netns [NAME]", - NO_STR - "Detach VRF from a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - struct ns *ns = NULL; - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!vrf_is_backend_netns()) { - vty_out(vty, "VRF backend is not Netns. Aborting\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!vrf->ns_ctxt) { - vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", - vrf->name, vrf->vrf_id); - return CMD_WARNING_CONFIG_FAILED; - } - - ns = (struct ns *)vrf->ns_ctxt; - - ns->vrf_ctxt = NULL; - vrf_disable(vrf); - /* vrf ID from VRF is necessary for Zebra - * so that propagate to other clients is done - */ - ns_delete(ns); - vrf->ns_ctxt = NULL; - return CMD_SUCCESS; -} - /* if ns_id is different and not VRF_UNKNOWN, * then update vrf identifier, and enable VRF */ @@ -677,12 +548,4 @@ void zebra_vrf_init(void) zebra_vrf_delete); hook_register(zserv_client_close, release_daemon_table_chunks); - - vrf_cmd_init(vrf_config_write); - - if (vrf_is_backend_netns() && ns_have_netns()) { - /* Install NS commands. */ - install_element(VRF_NODE, &vrf_netns_cmd); - install_element(VRF_NODE, &no_vrf_netns_cmd); - } } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index d36c2f8..9a68d5a 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -20,6 +20,7 @@ #include "vxlan.h" #include "termtable.h" #include "affinitymap.h" +#include "frrdistance.h" #include "zebra/zebra_router.h" #include "zebra/zserv.h" @@ -50,6 +51,7 @@ #include "zebra/zebra_script.h" #include "zebra/rtadv.h" #include "zebra/zebra_neigh.h" +#include "zebra/zebra_ptm.h" /* context to manage dumps in multiple tables or vrfs */ struct route_show_ctx { @@ -58,8 +60,8 @@ struct route_show_ctx { }; static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, - safi_t safi, bool use_fib, bool use_json, - route_tag_t tag, + safi_t safi, bool use_fib, json_object *vrf_json, + bool use_json, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, unsigned short ospf_instance_id, uint32_t tableid, @@ -146,8 +148,8 @@ DEFPY (show_ip_rpf, }; return do_show_ip_route(vty, VRF_DEFAULT_NAME, ip ? AFI_IP : AFI_IP6, - SAFI_MULTICAST, false, uj, 0, NULL, false, 0, 0, - 0, false, &ctx); + SAFI_MULTICAST, false, NULL, uj, 0, NULL, false, + 0, 0, 0, false, &ctx); } DEFPY (show_ip_rpf_addr, @@ -764,9 +766,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } /* Distance and metric display. */ - if (((re->type == ZEBRA_ROUTE_CONNECT) && + if (((re->type == ZEBRA_ROUTE_CONNECT || + re->type == ZEBRA_ROUTE_LOCAL) && (re->distance || re->metric)) || - (re->type != ZEBRA_ROUTE_CONNECT)) + (re->type != ZEBRA_ROUTE_CONNECT && re->type != ZEBRA_ROUTE_LOCAL)) len += vty_out(vty, " [%u/%u]", re->distance, re->metric); @@ -853,14 +856,13 @@ static void vty_show_ip_route_detail_json(struct vty *vty, vty_json(vty, json); } -static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, - struct route_table *table, afi_t afi, - bool use_fib, route_tag_t tag, - const struct prefix *longer_prefix_p, - bool supernets_only, int type, - unsigned short ospf_instance_id, bool use_json, - uint32_t tableid, bool show_ng, - struct route_show_ctx *ctx) +static void +do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, + struct route_table *table, afi_t afi, bool use_fib, + json_object *vrf_json, route_tag_t tag, + const struct prefix *longer_prefix_p, bool supernets_only, + int type, unsigned short ospf_instance_id, bool use_json, + uint32_t tableid, bool show_ng, struct route_show_ctx *ctx) { struct route_node *rn; struct route_entry *re; @@ -882,7 +884,7 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, * => display the VRF and table if specific */ - if (use_json) + if (use_json && !vrf_json) json = json_object_new_object(); /* Show all routes. */ @@ -957,7 +959,11 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, if (json_prefix) { prefix2str(&rn->p, buf, sizeof(buf)); - json_object_object_add(json, buf, json_prefix); + if (!vrf_json) + json_object_object_add(json, buf, json_prefix); + else + json_object_object_add(vrf_json, buf, + json_prefix); json_prefix = NULL; } } @@ -966,13 +972,15 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, * This is an extremely expensive operation at scale * and non-pretty reduces memory footprint significantly. */ - if (use_json) + if (use_json && !vrf_json) { vty_json_no_pretty(vty, json); + json = NULL; + } } static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, - afi_t afi, bool use_fib, bool use_json, - route_tag_t tag, + afi_t afi, bool use_fib, json_object *vrf_json, + bool use_json, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, unsigned short ospf_instance_id, bool show_ng, @@ -992,15 +1000,15 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, continue; do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST, - use_fib, use_json, tag, longer_prefix_p, - supernets_only, type, ospf_instance_id, - zrt->tableid, show_ng, ctx); + use_fib, vrf_json, use_json, tag, + longer_prefix_p, supernets_only, type, + ospf_instance_id, zrt->tableid, show_ng, ctx); } } static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, - safi_t safi, bool use_fib, bool use_json, - route_tag_t tag, + safi_t safi, bool use_fib, json_object *vrf_json, + bool use_json, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, unsigned short ospf_instance_id, uint32_t tableid, @@ -1035,7 +1043,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, return CMD_SUCCESS; } - do_show_route_helper(vty, zvrf, table, afi, use_fib, tag, + do_show_route_helper(vty, zvrf, table, afi, use_fib, vrf_json, tag, longer_prefix_p, supernets_only, type, ospf_instance_id, use_json, tableid, show_ng, ctx); @@ -1161,27 +1169,6 @@ DEFPY (show_ip_nht, return CMD_SUCCESS; } -DEFUN (ip_nht_default_route, - ip_nht_default_route_cmd, - "ip nht resolve-via-default", - IP_STR - "Filter Next Hop tracking route resolution\n" - "Resolve via default route\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (zvrf->zebra_rnh_ip_default_route) - return CMD_SUCCESS; - - zvrf->zebra_rnh_ip_default_route = true; - - zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST); - return CMD_SUCCESS; -} - static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, json_object *json_nhe_hdr) { @@ -1680,68 +1667,6 @@ DEFPY_HIDDEN(backup_nexthop_recursive_use_enable, return CMD_SUCCESS; } -DEFUN (no_ip_nht_default_route, - no_ip_nht_default_route_cmd, - "no ip nht resolve-via-default", - NO_STR - IP_STR - "Filter Next Hop tracking route resolution\n" - "Resolve via default route\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (!zvrf->zebra_rnh_ip_default_route) - return CMD_SUCCESS; - - zvrf->zebra_rnh_ip_default_route = false; - zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST); - return CMD_SUCCESS; -} - -DEFUN (ipv6_nht_default_route, - ipv6_nht_default_route_cmd, - "ipv6 nht resolve-via-default", - IP6_STR - "Filter Next Hop tracking route resolution\n" - "Resolve via default route\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (zvrf->zebra_rnh_ipv6_default_route) - return CMD_SUCCESS; - - zvrf->zebra_rnh_ipv6_default_route = true; - zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST); - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_nht_default_route, - no_ipv6_nht_default_route_cmd, - "no ipv6 nht resolve-via-default", - NO_STR - IP6_STR - "Filter Next Hop tracking route resolution\n" - "Resolve via default route\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (!zvrf->zebra_rnh_ipv6_default_route) - return CMD_SUCCESS; - - zvrf->zebra_rnh_ipv6_default_route = false; - zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST); - return CMD_SUCCESS; -} - DEFPY_HIDDEN(rnh_hide_backups, rnh_hide_backups_cmd, "[no] ip nht hide-backup-events", NO_STR @@ -1815,6 +1740,7 @@ DEFPY (show_route, struct route_show_ctx ctx = { .multi = vrf_all || table_all, }; + json_object *root_json = NULL; if (!vrf_is_backend_netns()) { if ((vrf_all || vrf_name) && (table || table_all)) { @@ -1836,24 +1762,42 @@ DEFPY (show_route, } if (vrf_all) { + if (!!json) + root_json = json_object_new_object(); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + json_object *vrf_json = NULL; + if ((zvrf = vrf->info) == NULL || (zvrf->table[afi][SAFI_UNICAST] == NULL)) continue; + if (!!json) + vrf_json = json_object_new_object(); + if (table_all) - do_show_ip_route_all( - vty, zvrf, afi, !!fib, !!json, tag, - prefix_str ? prefix : NULL, - !!supernets_only, type, - ospf_instance_id, !!ng, &ctx); + do_show_ip_route_all(vty, zvrf, afi, !!fib, + vrf_json, !!json, tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, !!ng, + &ctx); else - do_show_ip_route( - vty, zvrf_name(zvrf), afi, SAFI_UNICAST, - !!fib, !!json, tag, - prefix_str ? prefix : NULL, - !!supernets_only, type, - ospf_instance_id, table, !!ng, &ctx); + do_show_ip_route(vty, zvrf_name(zvrf), afi, + SAFI_UNICAST, !!fib, vrf_json, + !!json, tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, table, !!ng, + &ctx); + + if (!!json) + json_object_object_add(root_json, + zvrf_name(zvrf), + vrf_json); + } + if (!!json) { + vty_json_no_pretty(vty, root_json); + root_json = NULL; } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1869,13 +1813,13 @@ DEFPY (show_route, return CMD_SUCCESS; if (table_all) - do_show_ip_route_all(vty, zvrf, afi, !!fib, !!json, tag, - prefix_str ? prefix : NULL, + do_show_ip_route_all(vty, zvrf, afi, !!fib, NULL, !!json, + tag, prefix_str ? prefix : NULL, !!supernets_only, type, ospf_instance_id, !!ng, &ctx); else do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST, - !!fib, !!json, tag, + !!fib, NULL, !!json, tag, prefix_str ? prefix : NULL, !!supernets_only, type, ospf_instance_id, table, !!ng, &ctx); @@ -2251,7 +2195,8 @@ static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table) vrf_id_to_name(re->vrf_id)); vty_out(vty, " flags: %u\n", re->flags); - if (re->type != ZEBRA_ROUTE_CONNECT) { + if (re->type != ZEBRA_ROUTE_CONNECT && + re->type != ZEBRA_ROUTE_LOCAL) { vty_out(vty, " distance: %u\n", re->distance); vty_out(vty, " metric: %u\n", re->metric); } @@ -2724,146 +2669,6 @@ DEFPY(evpn_mh_redirect_off, evpn_mh_redirect_off_cmd, return zebra_evpn_mh_redirect_off(vty, redirect_off); } -DEFUN (default_vrf_vni_mapping, - default_vrf_vni_mapping_cmd, - "vni " CMD_VNI_RANGE "[prefix-routes-only]", - "VNI corresponding to the DEFAULT VRF\n" - "VNI-ID\n" - "Prefix routes only \n") -{ - char xpath[XPATH_MAXLEN]; - int filter = 0; - - if (argc == 3) - filter = 1; - - snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - - snprintf(xpath, sizeof(xpath), - FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, argv[1]->arg); - - if (filter) { - snprintf(xpath, sizeof(xpath), - FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true"); - } - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN (no_default_vrf_vni_mapping, - no_default_vrf_vni_mapping_cmd, - "no vni " CMD_VNI_RANGE "[prefix-routes-only]", - NO_STR - "VNI corresponding to DEFAULT VRF\n" - "VNI-ID\n" - "Prefix routes only \n") -{ - char xpath[XPATH_MAXLEN]; - int filter = 0; - vni_t vni = strtoul(argv[2]->arg, NULL, 10); - struct zebra_vrf *zvrf = NULL; - - zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); - - if (argc == 4) - filter = 1; - - if (zvrf->l3vni != vni) { - vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, - zvrf->vrf->name); - return CMD_WARNING; - } - - snprintf(xpath, sizeof(xpath), - FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, argv[2]->arg); - - if (filter) { - snprintf(xpath, sizeof(xpath), - FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, "true"); - } - - snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", - VRF_DEFAULT_NAME); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN (vrf_vni_mapping, - vrf_vni_mapping_cmd, - "vni " CMD_VNI_RANGE "[prefix-routes-only]", - "VNI corresponding to tenant VRF\n" - "VNI-ID\n" - "prefix-routes-only\n") -{ - int filter = 0; - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - assert(vrf); - assert(zvrf); - - if (argc == 3) - filter = 1; - - nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL); - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY, - argv[1]->arg); - - if (filter) - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", - NB_OP_MODIFY, "true"); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN (no_vrf_vni_mapping, - no_vrf_vni_mapping_cmd, - "no vni " CMD_VNI_RANGE "[prefix-routes-only]", - NO_STR - "VNI corresponding to tenant VRF\n" - "VNI-ID\n" - "prefix-routes-only\n") -{ - int filter = 0; - - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - vni_t vni = strtoul(argv[2]->arg, NULL, 10); - - assert(vrf); - assert(zvrf); - - if (argc == 4) - filter = 1; - - if (zvrf->l3vni != vni) { - vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, - zvrf->vrf->name); - return CMD_WARNING; - } - - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY, - argv[2]->arg); - - if (filter) - nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", - NB_OP_DESTROY, "true"); - - nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); -} - /* show vrf */ DEFPY (show_vrf_vni, show_vrf_vni_cmd, @@ -4028,6 +3833,17 @@ static int config_write_protocol(struct vty *vty) return 1; } +static inline bool zebra_vty_v6_rr_semantics_used(void) +{ + if (zebra_nhg_kernel_nexthops_enabled()) + return true; + + if (zrouter.v6_rr_semantics) + return true; + + return false; +} + DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -4047,7 +3863,9 @@ DEFUN (show_zebra, ttable_add_row(table, "MPLS|%s", mpls_enabled ? "On" : "Off"); ttable_add_row(table, "EVPN|%s", is_evpn_enabled() ? "On" : "Off"); ttable_add_row(table, "Kernel socket buffer size|%d", rcvbufsize); - + ttable_add_row(table, "v6 Route Replace Semantics|%s", + zebra_vty_v6_rr_semantics_used() ? "Replace" + : "Delete then Add"); #ifdef GNU_LINUX if (!vrf_is_backend_netns()) @@ -4482,31 +4300,6 @@ DEFPY (no_zebra_protodown_bit, #endif /* HAVE_NETLINK */ -DEFUN(ip_table_range, ip_table_range_cmd, - "[no] ip table range (1-4294967295) (1-4294967295)", - NO_STR IP_STR - "table configuration\n" - "Configure table range\n" - "Start Routing Table\n" - "End Routing Table\n") -{ - ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf); - - if (!zvrf) - return CMD_WARNING; - - if (zvrf_id(zvrf) != VRF_DEFAULT && !vrf_is_backend_netns()) { - vty_out(vty, - "VRF subcommand does not make any sense in l3mdev based vrf's\n"); - return CMD_WARNING; - } - - if (strmatch(argv[0]->text, "no")) - return table_manager_range(vty, false, zvrf, NULL, NULL); - - return table_manager_range(vty, true, zvrf, argv[3]->arg, argv[4]->arg); -} - #ifdef HAVE_SCRIPTING DEFUN(zebra_on_rib_process_script, zebra_on_rib_process_script_cmd, @@ -4618,14 +4411,6 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_ip_rpf_addr_cmd); install_element(VIEW_NODE, &show_ipv6_rpf_addr_cmd); - install_element(CONFIG_NODE, &ip_nht_default_route_cmd); - install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd); - install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd); - install_element(CONFIG_NODE, &no_ipv6_nht_default_route_cmd); - install_element(VRF_NODE, &ip_nht_default_route_cmd); - install_element(VRF_NODE, &no_ip_nht_default_route_cmd); - install_element(VRF_NODE, &ipv6_nht_default_route_cmd); - install_element(VRF_NODE, &no_ipv6_nht_default_route_cmd); install_element(CONFIG_NODE, &rnh_hide_backups_cmd); install_element(VIEW_NODE, &show_frr_cmd); @@ -4677,19 +4462,12 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd); install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd); install_element(CONFIG_NODE, &evpn_mh_redirect_off_cmd); - install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd); - install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd); - install_element(VRF_NODE, &vrf_vni_mapping_cmd); - install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); install_element(VIEW_NODE, &show_dataplane_cmd); install_element(VIEW_NODE, &show_dataplane_providers_cmd); install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd); install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd); - install_element(CONFIG_NODE, &ip_table_range_cmd); - install_element(VRF_NODE, &ip_table_range_cmd); - #ifdef HAVE_NETLINK install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd); install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 50a7462..2ffaefd 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -82,7 +82,7 @@ static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); static struct zebra_neigh *svd_nh_add(const struct ipaddr *vtep_ip, const struct ethaddr *rmac); -static int svd_nh_del(struct zebra_neigh *n); +static void svd_nh_del(struct zebra_neigh *n); static int svd_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); static int svd_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); @@ -308,7 +308,7 @@ static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket, zevpn = (struct zebra_evpn *)bucket->data; if (!zevpn) { if (json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, json); return; } num_neigh = hashcount(zevpn->neigh_table); @@ -515,7 +515,7 @@ static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket, zevpn = (struct zebra_evpn *)bucket->data; if (!zevpn) { if (json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, json); return; } wctx->zevpn = zevpn; @@ -1590,17 +1590,24 @@ static struct zebra_neigh *svd_nh_add(const struct ipaddr *ip, /* * Del Single VXlan Device neighbor entry. */ -static int svd_nh_del(struct zebra_neigh *n) +static void svd_nh_del(struct zebra_neigh *n) { if (n->refcnt > 0) - return -1; + return; hash_release(svd_nh_table, n); XFREE(MTYPE_L3NEIGH, n); +} - return 0; +static void svd_nh_del_terminate(void *ptr) +{ + struct zebra_neigh *n = ptr; + + n->refcnt = 0; + svd_nh_del(n); } + /* * Common code to install remote nh as neigh into the kernel. */ @@ -2258,14 +2265,13 @@ static int zl3vni_send_add_to_client(struct zebra_l3vni *zl3vni) stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Send L3_VNI_ADD %u VRF %s RMAC %pEA VRR %pEA local-ip %pI4 filter %s to %s", - zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), - &svi_rmac, &vrr_rmac, &zl3vni->local_vtep_ip, - CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) - ? "prefix-routes-only" - : "none", - zebra_route_string(client->proto)); + zlog_debug("Send L3VNI ADD %u VRF %s RMAC %pEA VRR %pEA local-ip %pI4 filter %s to %s", + zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + &svi_rmac, &vrr_rmac, &zl3vni->local_vtep_ip, + CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) + ? "prefix-routes-only" + : "none", + zebra_route_string(client->proto)); client->l3vniadd_cnt++; return zserv_send_message(client, s); @@ -2293,7 +2299,7 @@ static int zl3vni_send_del_to_client(struct zebra_l3vni *zl3vni) stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send L3_VNI_DEL %u VRF %s to %s", zl3vni->vni, + zlog_debug("Send L3VNI DEL %u VRF %s to %s", zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), zebra_route_string(client->proto)); @@ -2590,14 +2596,15 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni); return; @@ -2631,14 +2638,15 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); return; @@ -2672,7 +2680,8 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json) json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -2698,7 +2707,8 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -2709,7 +2719,7 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); @@ -2721,7 +2731,7 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, if (!n) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, json); else vty_out(vty, "%% Requested next-hop not present for L3-VNI %u\n", @@ -2742,13 +2752,16 @@ static void l3vni_print_nh_table(struct hash *nh_table, struct vty *vty, struct nh_walk_ctx wctx; json_object *json = NULL; - num_nh = hashcount(nh_table); - if (!num_nh) - return; - if (use_json) json = json_object_new_object(); + num_nh = hashcount(nh_table); + if (!num_nh) { + if (use_json) + vty_json_empty(vty, json); + return; + } + wctx.vty = vty; wctx.json = json; if (!use_json) { @@ -2770,14 +2783,14 @@ void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json) if (!is_evpn_enabled()) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); return; } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); return; @@ -2790,7 +2803,7 @@ void zebra_vxlan_print_nh_svd(struct vty *vty, bool use_json) { if (!is_evpn_enabled()) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); return; } @@ -2806,7 +2819,8 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json) json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -2834,14 +2848,15 @@ void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json) json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zl3vni = zl3vni_lookup(vni); if (!zl3vni) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -2905,14 +2920,15 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -2959,7 +2975,8 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -2989,7 +3006,8 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3020,14 +3038,15 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3064,14 +3083,15 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3113,14 +3133,15 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_json(vty, json); + vty_json_empty(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3177,21 +3198,24 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = num_valid_macs(zevpn); - if (!num_macs) + if (!num_macs) { + if (use_json) + vty_json_empty(vty, NULL); return; + } if (use_json) { json = json_object_new_object(); @@ -3250,7 +3274,8 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3278,7 +3303,8 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3307,7 +3333,8 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3337,7 +3364,8 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3377,22 +3405,34 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_json_empty(vty, NULL); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_json_empty(vty, NULL); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = num_valid_macs(zevpn); - if (!num_macs) + if (!num_macs) { + if (use_json) + vty_json_empty(vty, NULL); return; + } num_macs = num_dup_detected_macs(zevpn); - if (!num_macs) + if (!num_macs) { + if (use_json) + vty_json_empty(vty, NULL); return; + } if (use_json) { json = json_object_new_object(); @@ -3727,21 +3767,25 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json_object *json_mac = NULL; if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, NULL); return; } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = num_valid_macs(zevpn); - if (!num_macs) + if (!num_macs) { + if (use_json) + vty_json_empty(vty, NULL); return; + } if (use_json) { json = json_object_new_object(); @@ -3785,7 +3829,8 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3829,7 +3874,8 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (uj) + vty_json(vty, json); return; } @@ -3906,7 +3952,8 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); if (!is_evpn_enabled()) { - vty_json(vty, json); + if (use_json) + vty_json_empty(vty, json); return; } @@ -3990,7 +4037,7 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) { if (use_json) - vty_out(vty, "{}\n"); + vty_json_empty(vty, NULL); return; } @@ -5110,7 +5157,21 @@ void zebra_vxlan_macvlan_up(struct interface *ifp) zif = ifp->info; assert(zif); + + if (zif->link_nsid) + /* the link interface is another namespace */ + return; + link_ifp = zif->link; + if (!link_ifp) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "macvlan parent link is not found. Parent index %d ifp %s", + zif->link_ifindex, + ifindex2ifname(zif->link_ifindex, + ifp->vrf->vrf_id)); + return; + } link_zif = link_ifp->info; assert(link_zif); @@ -5127,9 +5188,8 @@ void zebra_vxlan_macvlan_up(struct interface *ifp) } } -int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, - char *err, int err_str_sz, int filter, - int add) +void zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, + int filter, int add) { struct zebra_l3vni *zl3vni = NULL; struct zebra_vrf *zvrf_evpn = NULL; @@ -5141,21 +5201,6 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, add ? "ADD" : "DEL"); if (add) { - /* check if the vni is already present under zvrf */ - if (zvrf->l3vni) { - snprintf(err, err_str_sz, - "VNI is already configured under the vrf"); - return -1; - } - - /* check if this VNI is already present in the system */ - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - snprintf(err, err_str_sz, - "VNI is already configured as L3-VNI"); - return -1; - } - /* Remove L2VNI if present */ zebra_vxlan_handle_vni_transition(zvrf, vni, add); @@ -5201,23 +5246,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, } else { zl3vni = zl3vni_lookup(vni); - if (!zl3vni) { - snprintf(err, err_str_sz, "VNI doesn't exist"); - return -1; - } - - if (zvrf->l3vni != vni) { - snprintf(err, err_str_sz, - "VNI %d doesn't exist in VRF: %s", - vni, zvrf->vrf->name); - return -1; - } - - if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) { - snprintf(err, ERR_STR_SZ, - "prefix-routes-only is not set for the vni"); - return -1; - } + assert(zl3vni); zebra_vxlan_process_l3vni_oper_down(zl3vni); @@ -5235,7 +5264,6 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, /* Add L2VNI for this VNI */ zebra_vxlan_handle_vni_transition(zvrf, vni, add); } - return 0; } int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf) @@ -5772,6 +5800,11 @@ void zebra_vxlan_init(void) zebra_evpn_mh_init(); } +void zebra_vxlan_terminate(void) +{ + hash_clean_and_free(&svd_nh_table, svd_nh_del_terminate); +} + /* free l3vni table */ void zebra_vxlan_disable(void) { diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 98c2767..eb02de6 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -178,13 +178,13 @@ extern int zebra_vxlan_if_add(struct interface *ifp); extern int zebra_vxlan_if_update(struct interface *ifp, struct zebra_vxlan_if_update_ctx *ctx); extern int zebra_vxlan_if_del(struct interface *ifp); -extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, - char *err, int err_str_sz, - int filter, int add); +extern void zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, + int filter, int add); extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf); extern void zebra_vxlan_close_tables(struct zebra_vrf *); extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *); extern void zebra_vxlan_init(void); +extern void zebra_vxlan_terminate(void); extern void zebra_vxlan_disable(void); extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac, diff --git a/zebra/zserv.c b/zebra/zserv.c index 2db228b..6a64176 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -637,7 +637,7 @@ static void zserv_client_free(struct zserv *client) vrf_bitmap_free(&client->redist_default[afi]); vrf_bitmap_free(&client->ridinfo[afi]); - vrf_bitmap_free(&client->nhrp_neighinfo[afi]); + vrf_bitmap_free(&client->neighinfo[afi]); } /* @@ -758,7 +758,7 @@ static struct zserv *zserv_client_create(int sock) vrf_bitmap_init(&client->redist[afi][i]); vrf_bitmap_init(&client->redist_default[afi]); vrf_bitmap_init(&client->ridinfo[afi]); - vrf_bitmap_init(&client->nhrp_neighinfo[afi]); + vrf_bitmap_init(&client->neighinfo[afi]); } /* Add this client to linked list. */ @@ -1061,6 +1061,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) 0, client->redist_v4_del_cnt); vty_out(vty, "Redist:v6 %-12u%-12u%-12u\n", client->redist_v6_add_cnt, 0, client->redist_v6_del_cnt); + vty_out(vty, "NHG %-12u%-12u%-12u\n", client->nhg_add_cnt, + client->nhg_upd8_cnt, client->nhg_del_cnt); vty_out(vty, "VRF %-12u%-12u%-12u\n", client->vrfadd_cnt, 0, client->vrfdel_cnt); vty_out(vty, "Connected %-12u%-12u%-12u\n", client->ifadd_cnt, 0, diff --git a/zebra/zserv.h b/zebra/zserv.h index 90aa4d5..e1c990f 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,9 +31,6 @@ extern "C" { struct zebra_vrf; -/* Default port information. */ -#define ZEBRA_VTY_PORT 2601 - /* Default configuration filename. */ #define DEFAULT_CONFIG_FILE "zebra.conf" @@ -121,7 +118,7 @@ struct zserv { vrf_bitmap_t ridinfo[AFI_MAX]; /* Router-id information. */ - vrf_bitmap_t nhrp_neighinfo[AFI_MAX]; + vrf_bitmap_t neighinfo[AFI_MAX]; bool notify_owner; @@ -185,6 +182,9 @@ struct zserv { uint32_t local_es_evi_add_cnt; uint32_t local_es_evi_del_cnt; uint32_t error_cnt; + uint32_t nhg_add_cnt; + uint32_t nhg_upd8_cnt; + uint32_t nhg_del_cnt; time_t nh_reg_time; time_t nh_dereg_time; @@ -237,8 +237,7 @@ DECLARE_HOOK(zserv_client_connect, (struct zserv *client), (client)); DECLARE_KOOH(zserv_client_close, (struct zserv *client), (client)); #define DYNAMIC_CLIENT_GR_DISABLED(_client) \ - ((_client->proto <= ZEBRA_ROUTE_CONNECT) \ - || !(_client->gr_instance_count)) + ((_client->proto <= ZEBRA_ROUTE_LOCAL) || !(_client->gr_instance_count)) /* * Initialize Zebra API server. -- cgit v1.2.3