summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am11
-rw-r--r--babeld/babel_interface.c26
-rw-r--r--babeld/babel_zebra.c5
-rw-r--r--bfdd/bfd.c284
-rw-r--r--bfdd/bfd.h43
-rw-r--r--bfdd/bfd_packet.c189
-rw-r--r--bfdd/bfdctl.h2
-rw-r--r--bfdd/bfdd.c71
-rw-r--r--bfdd/bfdd_vty.c137
-rw-r--r--bfdd/bsd.c48
-rw-r--r--bfdd/config.c2
-rw-r--r--bfdd/linux.c52
-rw-r--r--bfdd/ptm_adapter.c152
-rw-r--r--bfdd/subdir.am2
-rw-r--r--bgpd/bgp_advertise.c10
-rw-r--r--bgpd/bgp_advertise.h53
-rw-r--r--bgpd/bgp_aspath.c6
-rw-r--r--bgpd/bgp_aspath.h2
-rw-r--r--bgpd/bgp_attr.c118
-rw-r--r--bgpd/bgp_attr.h9
-rw-r--r--bgpd/bgp_bfd.c136
-rw-r--r--bgpd/bgp_clist.c10
-rw-r--r--bgpd/bgp_community.c6
-rw-r--r--bgpd/bgp_community.h2
-rw-r--r--bgpd/bgp_ecommunity.c2
-rw-r--r--bgpd/bgp_ecommunity.h3
-rw-r--r--bgpd/bgp_errors.c6
-rw-r--r--bgpd/bgp_errors.h1
-rw-r--r--bgpd/bgp_evpn.c20
-rw-r--r--bgpd/bgp_evpn_vty.c148
-rw-r--r--bgpd/bgp_fsm.c2
-rw-r--r--bgpd/bgp_keepalives.c4
-rw-r--r--bgpd/bgp_labelpool.c62
-rw-r--r--bgpd/bgp_labelpool.h4
-rw-r--r--bgpd/bgp_lcommunity.c2
-rw-r--r--bgpd/bgp_lcommunity.h2
-rw-r--r--bgpd/bgp_mac.c4
-rw-r--r--bgpd/bgp_main.c9
-rw-r--r--bgpd/bgp_mplsvpn.c3
-rw-r--r--bgpd/bgp_network.c6
-rw-r--r--bgpd/bgp_nexthop.c75
-rw-r--r--bgpd/bgp_nexthop.h10
-rw-r--r--bgpd/bgp_nht.c3
-rw-r--r--bgpd/bgp_packet.c11
-rw-r--r--bgpd/bgp_pbr.c20
-rw-r--r--bgpd/bgp_pbr.h8
-rw-r--r--bgpd/bgp_route.c493
-rw-r--r--bgpd/bgp_route.h6
-rw-r--r--bgpd/bgp_routemap.c56
-rw-r--r--bgpd/bgp_rpki.c23
-rw-r--r--bgpd/bgp_table.h27
-rw-r--r--bgpd/bgp_updgrp.c8
-rw-r--r--bgpd/bgp_updgrp.h7
-rw-r--r--bgpd/bgp_updgrp_adv.c12
-rw-r--r--bgpd/bgp_updgrp_packet.c14
-rw-r--r--bgpd/bgp_vty.c65
-rw-r--r--bgpd/bgp_zebra.c107
-rw-r--r--bgpd/bgpd.c30
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c22
-rw-r--r--bgpd/rfapi/rfapi_import.c40
-rw-r--r--bgpd/rfapi/rfapi_private.h2
-rw-r--r--bgpd/rfapi/rfapi_vty.c4
-rw-r--r--bgpd/rfapi/vnc_zebra.c5
-rwxr-xr-xconfigure.ac130
-rw-r--r--debian/copyright13
-rw-r--r--doc/developer/building-frr-for-debian8.rst3
-rw-r--r--doc/developer/building-frr-for-debian9.rst2
-rw-r--r--doc/developer/building-frr-for-ubuntu1804.rst4
-rw-r--r--doc/developer/library.rst3
-rw-r--r--doc/developer/lists.rst626
-rw-r--r--doc/developer/subdir.am3
-rw-r--r--doc/developer/topotests.rst15
-rw-r--r--doc/extra/spelling_wordlist.txt6
-rw-r--r--doc/manpages/common-options.rst1
-rw-r--r--doc/manpages/conf.py1
-rw-r--r--doc/manpages/defines.rst2
-rw-r--r--doc/manpages/index.rst1
-rw-r--r--doc/manpages/subdir.am1
-rw-r--r--doc/manpages/vrrpd.rst40
-rw-r--r--doc/user/basic.rst20
-rw-r--r--doc/user/bfd.rst31
-rw-r--r--doc/user/bgp.rst26
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/ospf6d.rst4
-rw-r--r--doc/user/setup.rst35
-rw-r--r--doc/user/static.rst2
-rw-r--r--doc/user/subdir.am1
-rw-r--r--doc/user/vrrp.rst506
-rw-r--r--doc/user/zebra.rst65
-rw-r--r--eigrpd/eigrp_zebra.c55
-rw-r--r--grpc/Makefile10
-rw-r--r--grpc/frr-northbound.proto412
-rw-r--r--grpc/subdir.am30
-rw-r--r--isisd/dict.c1510
-rw-r--r--isisd/dict.h121
-rw-r--r--isisd/fabricd.c4
-rw-r--r--isisd/isis_adjacency.c1
-rw-r--r--isisd/isis_bfd.c15
-rw-r--r--isisd/isis_bpf.c1
-rw-r--r--isisd/isis_circuit.c10
-rw-r--r--isisd/isis_circuit.h3
-rw-r--r--isisd/isis_cli.c138
-rw-r--r--isisd/isis_csm.c1
-rw-r--r--isisd/isis_dlpi.c1
-rw-r--r--isisd/isis_dr.c1
-rw-r--r--isisd/isis_dynhn.c1
-rw-r--r--isisd/isis_events.c1
-rw-r--r--isisd/isis_lsp.c301
-rw-r--r--isisd/isis_lsp.h31
-rw-r--r--isisd/isis_main.c1
-rw-r--r--isisd/isis_misc.c1
-rw-r--r--isisd/isis_northbound.c93
-rw-r--r--isisd/isis_pdu.c45
-rw-r--r--isisd/isis_pfpacket.c1
-rw-r--r--isisd/isis_redist.c1
-rw-r--r--isisd/isis_route.c1
-rw-r--r--isisd/isis_routemap.c1
-rw-r--r--isisd/isis_spf.c13
-rw-r--r--isisd/isis_spf_private.h8
-rw-r--r--isisd/isis_te.c201
-rw-r--r--isisd/isis_te.h14
-rw-r--r--isisd/isis_tlvs.c63
-rw-r--r--isisd/isis_tlvs.h5
-rw-r--r--isisd/isis_tx_queue.c5
-rw-r--r--isisd/isis_vty_fabricd.c10
-rw-r--r--isisd/isis_zebra.c39
-rw-r--r--isisd/isisd.c53
-rw-r--r--isisd/isisd.h8
-rw-r--r--isisd/subdir.am2
-rw-r--r--ldpd/ldp_zebra.c56
-rw-r--r--lib/atomlist.c348
-rw-r--r--lib/atomlist.h356
-rw-r--r--lib/bfd.c19
-rw-r--r--lib/bfd.h10
-rw-r--r--lib/checksum.c18
-rw-r--r--lib/checksum.h27
-rw-r--r--lib/command.c21
-rw-r--r--lib/command.h1
-rw-r--r--lib/compiler.h94
-rw-r--r--lib/distribute.c2
-rw-r--r--lib/ferr.c4
-rw-r--r--lib/fifo.h66
-rw-r--r--lib/frratomic.h7
-rw-r--r--lib/frrlua.c (renamed from lib/lua.c)2
-rw-r--r--lib/frrlua.h (renamed from lib/lua.h)6
-rw-r--r--lib/frrstr.c42
-rw-r--r--lib/frrstr.h41
-rw-r--r--lib/grammar_sandbox_main.c2
-rw-r--r--lib/hash.c4
-rw-r--r--lib/hash.h6
-rw-r--r--lib/if.c60
-rw-r--r--lib/if.h7
-rw-r--r--lib/if_rmap.c2
-rw-r--r--lib/ipaddr.h3
-rw-r--r--lib/json.c5
-rw-r--r--lib/json.h2
-rw-r--r--lib/lib_errors.c6
-rw-r--r--lib/lib_errors.h1
-rw-r--r--lib/libfrr.c7
-rw-r--r--lib/linklist.c15
-rw-r--r--lib/linklist.h20
-rw-r--r--lib/memory.h2
-rw-r--r--lib/nexthop.c206
-rw-r--r--lib/nexthop.h9
-rw-r--r--lib/northbound.c251
-rw-r--r--lib/northbound.h75
-rw-r--r--lib/northbound_cli.c154
-rw-r--r--lib/northbound_confd.c8
-rw-r--r--lib/northbound_grpc.cpp936
-rw-r--r--lib/northbound_sysrepo.c12
-rw-r--r--lib/openbsd-tree.c12
-rw-r--r--lib/openbsd-tree.h54
-rw-r--r--lib/prefix.c2
-rw-r--r--lib/prefix.h2
-rw-r--r--lib/privs.c2
-rw-r--r--lib/qobj.c46
-rw-r--r--lib/qobj.h7
-rw-r--r--lib/route_types.txt2
-rw-r--r--lib/routemap.c89
-rw-r--r--lib/routemap.h10
-rw-r--r--lib/seqlock.c167
-rw-r--r--lib/seqlock.h106
-rw-r--r--lib/sockunion.c19
-rw-r--r--lib/sockunion.h2
-rw-r--r--lib/subdir.am29
-rw-r--r--lib/table.c75
-rw-r--r--lib/table.h53
-rw-r--r--lib/thread.c142
-rw-r--r--lib/thread.h15
-rw-r--r--lib/typerb.c472
-rw-r--r--lib/typerb.h190
-rw-r--r--lib/typesafe.c555
-rw-r--r--lib/typesafe.h866
-rw-r--r--lib/vrf.c21
-rw-r--r--lib/vty.c47
-rw-r--r--lib/vty.h5
-rw-r--r--lib/wheel.c2
-rw-r--r--lib/wheel.h8
-rw-r--r--lib/workqueue.c2
-rw-r--r--lib/yang.c45
-rw-r--r--lib/yang_translator.c53
-rw-r--r--lib/yang_wrappers.c8
-rw-r--r--lib/zclient.c29
-rw-r--r--lib/zclient.h103
-rw-r--r--lib/zebra.h47
-rw-r--r--m4/ax_lua.m4664
-rw-r--r--nhrpd/nhrp_cache.c4
-rw-r--r--nhrpd/nhrp_interface.c30
-rw-r--r--nhrpd/nhrp_main.c2
-rw-r--r--nhrpd/nhrp_peer.c4
-rw-r--r--nhrpd/nhrp_route.c3
-rw-r--r--nhrpd/nhrp_vc.c10
-rw-r--r--nhrpd/nhrpd.h21
-rw-r--r--nhrpd/reqid.c4
-rw-r--r--nhrpd/resolver.c2
-rw-r--r--nhrpd/zbuf.c2
-rw-r--r--ospf6d/ospf6_asbr.c2
-rw-r--r--ospf6d/ospf6_bfd.c18
-rw-r--r--ospf6d/ospf6_spf.c30
-rw-r--r--ospf6d/ospf6_spf.h4
-rw-r--r--ospf6d/ospf6_zebra.c33
-rw-r--r--ospfd/ospf_bfd.c23
-rw-r--r--ospfd/ospf_lsa.h7
-rw-r--r--ospfd/ospf_lsdb.c15
-rw-r--r--ospfd/ospf_lsdb.h2
-rw-r--r--ospfd/ospf_packet.c1
-rw-r--r--ospfd/ospf_routemap.c2
-rw-r--r--ospfd/ospf_spf.c107
-rw-r--r--ospfd/ospf_spf.h6
-rw-r--r--ospfd/ospf_sr.c2
-rw-r--r--ospfd/ospf_te.c195
-rw-r--r--ospfd/ospf_zebra.c40
-rw-r--r--pbrd/pbr_nht.c11
-rw-r--r--pbrd/pbr_zebra.c46
-rw-r--r--pimd/pim_bfd.c18
-rw-r--r--pimd/pim_cmd.c37
-rw-r--r--pimd/pim_ifchannel.c25
-rw-r--r--pimd/pim_ifchannel.h2
-rw-r--r--pimd/pim_igmp.c4
-rw-r--r--pimd/pim_msdp.c8
-rw-r--r--pimd/pim_nht.c5
-rw-r--r--pimd/pim_nht.h3
-rw-r--r--pimd/pim_oil.c4
-rw-r--r--pimd/pim_routemap.c2
-rw-r--r--pimd/pim_rpf.c4
-rw-r--r--pimd/pim_rpf.h2
-rw-r--r--pimd/pim_upstream.c16
-rw-r--r--pimd/pim_upstream.h2
-rw-r--r--pimd/pim_vxlan.c4
-rw-r--r--pimd/pim_zebra.c49
-rw-r--r--redhat/frr.spec.in20
-rw-r--r--ripd/rip_interface.c21
-rw-r--r--ripd/rip_interface.h3
-rw-r--r--ripd/rip_main.c10
-rw-r--r--ripd/rip_zebra.c7
-rw-r--r--ripngd/ripng_interface.c21
-rw-r--r--ripngd/ripng_main.c10
-rw-r--r--ripngd/ripng_zebra.c5
-rw-r--r--ripngd/ripngd.h21
-rw-r--r--sharpd/sharp_zebra.c28
-rw-r--r--staticd/static_zebra.c32
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/isisd/test_fuzz_isis_tlv_tests.h.gzbin222030 -> 221946 bytes
-rw-r--r--tests/isisd/test_isis_lspdb.c17
-rw-r--r--tests/lib/cxxcompat.c3
-rw-r--r--tests/lib/test_atomlist.c404
-rw-r--r--tests/lib/test_atomlist.py6
-rw-r--r--tests/lib/test_seqlock.c122
-rw-r--r--tests/lib/test_srcdest_table.c4
-rw-r--r--tests/lib/test_typelist.c169
-rw-r--r--tests/lib/test_typelist.h562
-rw-r--r--tests/lib/test_typelist.py19
-rw-r--r--tests/subdir.am18
-rw-r--r--tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref4
-rwxr-xr-xtests/topotests/all-protocol-startup/test_all_protocol_startup.py2
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/__init__.py0
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json103
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf20
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json80
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json16
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json14
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf8
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf9
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json55
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf25
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json80
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json16
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json14
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf7
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot58
-rwxr-xr-xtests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py248
-rw-r--r--tests/topotests/bfd-vrf-topo1/__init__.py0
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/bfdd.conf6
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json11
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/bgpd.conf8
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/peers.json8
-rw-r--r--tests/topotests/bfd-vrf-topo1/r1/zebra.conf3
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/bfdd.conf12
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json19
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/bgpd.conf11
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/peers.json17
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/zebra.conf9
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/bfdd.conf7
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json11
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/bgpd.conf7
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/peers.json6
-rw-r--r--tests/topotests/bfd-vrf-topo1/r3/zebra.conf3
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/bfdd.conf7
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json11
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/bgpd.conf7
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/peers.json6
-rw-r--r--tests/topotests/bfd-vrf-topo1/r4/zebra.conf3
-rw-r--r--tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot73
-rw-r--r--tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpgbin0 -> 25713 bytes
-rwxr-xr-xtests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py292
-rwxr-xr-x[-rw-r--r--]tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py0
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/__init__.py0
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf2
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf2
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py113
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf10
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf2
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf6
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py20
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py66
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py18
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py3
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py0
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf4
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py109
-rwxr-xr-xtests/topotests/conftest.py14
-rw-r--r--tests/topotests/lib/ltemplate.py25
-rw-r--r--tests/topotests/lib/topogen.py19
-rwxr-xr-xtools/checkpatch.pl29
-rw-r--r--tools/coccinelle/hash_const.cocci76
-rw-r--r--tools/etc/frr/daemons10
-rw-r--r--tools/etc/rsyslog.d/45-frr.conf2
-rwxr-xr-xtools/frr-reload.py24
-rwxr-xr-xtools/frr.in2
-rw-r--r--tools/frrcommon.sh.in2
-rw-r--r--vrrpd/.gitignore2
-rw-r--r--vrrpd/Makefile10
-rw-r--r--vrrpd/subdir.am39
-rw-r--r--vrrpd/vrrp.c2378
-rw-r--r--vrrpd/vrrp.h570
-rw-r--r--vrrpd/vrrp_arp.c219
-rw-r--r--vrrpd/vrrp_arp.h36
-rw-r--r--vrrpd/vrrp_debug.c131
-rw-r--r--vrrpd/vrrp_debug.h87
-rw-r--r--vrrpd/vrrp_main.c159
-rw-r--r--vrrpd/vrrp_memory.c29
-rw-r--r--vrrpd/vrrp_memory.h32
-rw-r--r--vrrpd/vrrp_ndisc.c245
-rw-r--r--vrrpd/vrrp_ndisc.h74
-rw-r--r--vrrpd/vrrp_packet.c321
-rw-r--r--vrrpd/vrrp_packet.h202
-rw-r--r--vrrpd/vrrp_vty.c751
-rw-r--r--vrrpd/vrrp_vty.h25
-rw-r--r--vrrpd/vrrp_zebra.c252
-rw-r--r--vrrpd/vrrp_zebra.h32
-rwxr-xr-xvtysh/extract.pl.in2
-rw-r--r--vtysh/vtysh.c15
-rw-r--r--vtysh/vtysh.h5
-rw-r--r--vtysh/vtysh_config.c4
-rw-r--r--yang/frr-isisd.yang16
-rw-r--r--yang/libyang_plugins/subdir.am13
-rw-r--r--zebra/connected.c51
-rw-r--r--zebra/if_netlink.c49
-rw-r--r--zebra/if_netlink.h14
-rw-r--r--zebra/interface.c31
-rw-r--r--zebra/interface.h4
-rw-r--r--zebra/kernel_socket.c8
-rw-r--r--zebra/main.c52
-rw-r--r--zebra/redistribute.c25
-rw-r--r--zebra/rib.h79
-rw-r--r--zebra/rt.h6
-rw-r--r--zebra/rt_netlink.c2
-rw-r--r--zebra/zapi_msg.c46
-rw-r--r--zebra/zebra_dplane.c32
-rw-r--r--zebra/zebra_fpm_netlink.c143
-rw-r--r--zebra/zebra_fpm_protobuf.c5
-rw-r--r--zebra/zebra_mpls.c33
-rw-r--r--zebra/zebra_mpls_openbsd.c3
-rw-r--r--zebra/zebra_pbr.c20
-rw-r--r--zebra/zebra_pbr.h8
-rw-r--r--zebra/zebra_ptm.c24
-rw-r--r--zebra/zebra_rib.c136
-rw-r--r--zebra/zebra_rnh.c6
-rw-r--r--zebra/zebra_rnh.h34
-rw-r--r--zebra/zebra_routemap.c23
-rw-r--r--zebra/zebra_router.c18
-rw-r--r--zebra/zebra_router.h14
-rw-r--r--zebra/zebra_vrf.c88
-rw-r--r--zebra/zebra_vrf.h35
-rw-r--r--zebra/zebra_vty.c138
-rw-r--r--zebra/zebra_vxlan.c55
-rw-r--r--zebra/zebra_vxlan_private.h2
-rw-r--r--zebra/zserv.c11
-rw-r--r--zebra/zserv.h21
419 files changed, 20426 insertions, 5226 deletions
diff --git a/.clang-format b/.clang-format
index a65a29f8c9..4bd962747f 100644
--- a/.clang-format
+++ b/.clang-format
@@ -25,6 +25,9 @@ CommentPragmas: '\$(FRR|clippy)'
ContinuationIndentWidth: 8
ForEachMacros:
# lib
+ - frr_each
+ - frr_each_safe
+ - frr_each_from
- LIST_FOREACH
- LIST_FOREACH_SAFE
- SLIST_FOREACH
diff --git a/.gitignore b/.gitignore
index 5003c97572..6cfe23e921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@
*.pb.h
*.pb-c.h
*.pb-c.c
+*.pb.cc
*_clippy.c
### dist
@@ -87,5 +88,7 @@ GSYMS
GRTAGS
GPATH
compile_commands.json
+.ccls-cache
.dirstamp
refix
+.vscode
diff --git a/Makefile.am b/Makefile.am
index 9c6c8663ee..166ac29d1c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,9 @@ AM_CFLAGS = \
# end
AM_CPPFLAGS = \
-I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \
- -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib
+ -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib \
+ $(LUA_INCLUDE) \
+ # end
AM_LDFLAGS = \
-export-dynamic \
$(AC_LDFLAGS) \
@@ -96,7 +98,6 @@ noinst_LIBRARIES =
nodist_noinst_DATA =
lib_LTLIBRARIES =
module_LTLIBRARIES =
-libyang_plugins_LTLIBRARIES =
pkginclude_HEADERS =
nodist_pkginclude_HEADERS =
dist_examples_DATA =
@@ -111,7 +112,6 @@ vtysh_scan =
$(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES
$(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES
$(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES
-$(AUTOMAKE_DUMMY)install-libyang_pluginsLTLIBRARIES: install-libLTLIBRARIES
include doc/subdir.am
include doc/user/subdir.am
@@ -123,6 +123,7 @@ include zebra/subdir.am
include watchfrr/subdir.am
include qpb/subdir.am
include fpm/subdir.am
+include grpc/subdir.am
include tools/subdir.am
include solaris/subdir.am
@@ -146,6 +147,7 @@ include staticd/subdir.am
include bfdd/subdir.am
include yang/subdir.am
include yang/libyang_plugins/subdir.am
+include vrrpd/subdir.am
include vtysh/subdir.am
include tests/subdir.am
@@ -187,7 +189,6 @@ EXTRA_DIST += \
snapcraft/defaults \
snapcraft/helpers \
snapcraft/snap \
- \
babeld/Makefile \
bgpd/Makefile \
bgpd/rfp-example/librfp/Makefile \
@@ -198,6 +199,7 @@ EXTRA_DIST += \
doc/user/Makefile \
eigrpd/Makefile \
fpm/Makefile \
+ grpc/Makefile \
isisd/Makefile \
ldpd/Makefile \
lib/Makefile \
@@ -216,6 +218,7 @@ EXTRA_DIST += \
vtysh/Makefile \
watchfrr/Makefile \
zebra/Makefile \
+ vrrpd/Makefile \
# end
noinst_HEADERS += defaults.h
diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c
index 0ff89abc49..b84bc39cd8 100644
--- a/babeld/babel_interface.c
+++ b/babeld/babel_interface.c
@@ -66,7 +66,7 @@ static struct cmd_node babel_interface_node = /* babeld's interface node. */
int
-babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf)
+babel_interface_up (ZAPI_CALLBACK_ARGS)
{
struct stream *s = NULL;
struct interface *ifp = NULL;
@@ -74,7 +74,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id
debugf(BABEL_DEBUG_IF, "receive a 'interface up'");
s = zclient->ibuf;
- ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */
+ ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */
if (ifp == NULL) {
return 0;
@@ -85,7 +85,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id
}
int
-babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf)
+babel_interface_down (ZAPI_CALLBACK_ARGS)
{
struct stream *s = NULL;
struct interface *ifp = NULL;
@@ -93,7 +93,7 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_
debugf(BABEL_DEBUG_IF, "receive a 'interface down'");
s = zclient->ibuf;
- ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */
+ ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */
if (ifp == NULL) {
return 0;
@@ -104,14 +104,14 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_
}
int
-babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf)
+babel_interface_add (ZAPI_CALLBACK_ARGS)
{
struct interface *ifp = NULL;
debugf(BABEL_DEBUG_IF, "receive a 'interface add'");
/* read and add the interface in the iflist. */
- ifp = zebra_interface_add_read (zclient->ibuf, vrf);
+ ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
if (ifp == NULL) {
return 0;
@@ -122,7 +122,7 @@ babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_i
}
int
-babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf)
+babel_interface_delete (ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -130,7 +130,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr
debugf(BABEL_DEBUG_IF, "receive a 'interface delete'");
s = zclient->ibuf;
- ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */
+ ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */
if (ifp == NULL)
return 0;
@@ -146,8 +146,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr
}
int
-babel_interface_address_add (int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf)
+babel_interface_address_add (ZAPI_CALLBACK_ARGS)
{
babel_interface_nfo *babel_ifp;
struct connected *ifc;
@@ -156,7 +155,7 @@ babel_interface_address_add (int cmd, struct zclient *client,
debugf(BABEL_DEBUG_IF, "receive a 'interface address add'");
ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD,
- zclient->ibuf, vrf);
+ zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -183,8 +182,7 @@ babel_interface_address_add (int cmd, struct zclient *client,
}
int
-babel_interface_address_delete (int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf)
+babel_interface_address_delete (ZAPI_CALLBACK_ARGS)
{
babel_interface_nfo *babel_ifp;
struct connected *ifc;
@@ -193,7 +191,7 @@ babel_interface_address_delete (int cmd, struct zclient *client,
debugf(BABEL_DEBUG_IF, "receive a 'interface address delete'");
ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE,
- zclient->ibuf, vrf);
+ zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c
index e909f8ea7a..d70823544a 100644
--- a/babeld/babel_zebra.c
+++ b/babeld/babel_zebra.c
@@ -56,8 +56,7 @@ static struct {
/* Zebra route add and delete treatment. */
static int
-babel_zebra_read_route (int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf)
+babel_zebra_read_route (ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
@@ -68,7 +67,7 @@ babel_zebra_read_route (int command, struct zclient *zclient,
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
return 0;
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
babel_route_add(&api);
} else {
babel_route_delete(&api);
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index e9645824f2..4354431820 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -128,15 +128,8 @@ int bfd_session_enable(struct bfd_session *bs)
* If the interface or VRF doesn't exist, then we must register
* the session but delay its start.
*/
- if (bs->key.ifname[0]) {
- ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
- if (ifp == NULL) {
- log_error(
- "session-enable: specified interface doesn't exists.");
- return 0;
- }
-
- vrf = vrf_lookup_by_id(ifp->vrf_id);
+ if (bs->key.vrfname[0]) {
+ vrf = vrf_lookup_by_name(bs->key.vrfname);
if (vrf == NULL) {
log_error(
"session-enable: specified VRF doesn't exists.");
@@ -144,13 +137,24 @@ int bfd_session_enable(struct bfd_session *bs)
}
}
- if (bs->key.vrfname[0]) {
- vrf = vrf_lookup_by_name(bs->key.vrfname);
- if (vrf == NULL) {
+ if (bs->key.ifname[0]) {
+ if (vrf)
+ ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
+ else
+ ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
+ if (ifp == NULL) {
log_error(
- "session-enable: specified VRF doesn't exists.");
+ "session-enable: specified interface doesn't exists.");
return 0;
}
+ if (bs->key.ifname[0] && !vrf) {
+ vrf = vrf_lookup_by_id(ifp->vrf_id);
+ if (vrf == NULL) {
+ log_error(
+ "session-enable: specified VRF doesn't exists.");
+ return 0;
+ }
+ }
}
/* Assign interface/VRF pointers. */
@@ -164,7 +168,7 @@ int bfd_session_enable(struct bfd_session *bs)
/* Sanity check: don't leak open sockets. */
if (bs->sock != -1) {
- zlog_debug("session-enable: previous socket open");
+ log_debug("session-enable: previous socket open");
close(bs->sock);
bs->sock = -1;
}
@@ -291,7 +295,7 @@ void ptm_bfd_echo_start(struct bfd_session *bfd)
ptm_bfd_echo_xmt_TO(bfd);
}
-void ptm_bfd_ses_up(struct bfd_session *bfd)
+void ptm_bfd_sess_up(struct bfd_session *bfd)
{
int old_state = bfd->ses_state;
@@ -315,7 +319,7 @@ void ptm_bfd_ses_up(struct bfd_session *bfd)
}
}
-void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag)
+void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
{
int old_state = bfd->ses_state;
@@ -432,7 +436,7 @@ int bfd_recvtimer_cb(struct thread *t)
switch (bs->ses_state) {
case PTM_BFD_INIT:
case PTM_BFD_UP:
- ptm_bfd_ses_dn(bs, BD_CONTROL_EXPIRED);
+ ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
bfd_recvtimer_update(bs);
break;
@@ -455,7 +459,7 @@ int bfd_echo_recvtimer_cb(struct thread *t)
switch (bs->ses_state) {
case PTM_BFD_INIT:
case PTM_BFD_UP:
- ptm_bfd_ses_dn(bs, BD_ECHO_FAILED);
+ ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
break;
}
@@ -596,6 +600,17 @@ skip_echo:
bfd_recvtimer_update(bs);
bfd_xmttimer_update(bs, bs->xmt_TO);
}
+ if (bpc->bpc_cbit) {
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
+ return;
+
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
+ } else {
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
+ return;
+
+ BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
+ }
}
static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
@@ -725,7 +740,7 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
return bfd;
}
-int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc)
+int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
{
struct bfd_session *bs;
@@ -805,7 +820,7 @@ static void bs_down_handler(struct bfd_session *bs, int nstate)
* Remote peer told us his path is up, lets turn
* activate the session.
*/
- ptm_bfd_ses_up(bs);
+ ptm_bfd_sess_up(bs);
break;
default:
@@ -832,7 +847,7 @@ static void bs_init_handler(struct bfd_session *bs, int nstate)
case PTM_BFD_INIT:
case PTM_BFD_UP:
/* We agreed on the settings and the path is up. */
- ptm_bfd_ses_up(bs);
+ ptm_bfd_sess_up(bs);
break;
default:
@@ -847,7 +862,7 @@ static void bs_up_handler(struct bfd_session *bs, int nstate)
case PTM_BFD_ADM_DOWN:
case PTM_BFD_DOWN:
/* Peer lost or asked to shutdown connection. */
- ptm_bfd_ses_dn(bs, BD_NEIGHBOR_DOWN);
+ ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
break;
case PTM_BFD_INIT:
@@ -1197,10 +1212,6 @@ int bs_observer_add(struct bfd_session *bs)
if (bso->bso_isinterface)
strlcpy(bso->bso_entryname, bs->key.ifname,
sizeof(bso->bso_entryname));
- else
- strlcpy(bso->bso_entryname, bs->key.vrfname,
- sizeof(bso->bso_entryname));
-
/* Handle socket binding failures caused by missing local addresses. */
if (bs->sock == -1) {
bso->bso_isaddress = true;
@@ -1271,16 +1282,16 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
static struct hash *bfd_id_hash;
static struct hash *bfd_key_hash;
-static unsigned int bfd_id_hash_do(void *p);
-static unsigned int bfd_key_hash_do(void *p);
+static unsigned int bfd_id_hash_do(const void *p);
+static unsigned int bfd_key_hash_do(const void *p);
static void _bfd_free(struct hash_bucket *hb,
void *arg __attribute__((__unused__)));
/* BFD hash for our discriminator. */
-static unsigned int bfd_id_hash_do(void *p)
+static unsigned int bfd_id_hash_do(const void *p)
{
- struct bfd_session *bs = p;
+ const struct bfd_session *bs = p;
return jhash_1word(bs->discrs.my_discr, 0);
}
@@ -1293,9 +1304,9 @@ static bool bfd_id_hash_cmp(const void *n1, const void *n2)
}
/* BFD hash for single hop. */
-static unsigned int bfd_key_hash_do(void *p)
+static unsigned int bfd_key_hash_do(const void *p)
{
- struct bfd_session *bs = p;
+ const struct bfd_session *bs = p;
return jhash(&bs->key, sizeof(bs->key), 0);
}
@@ -1322,32 +1333,105 @@ struct bfd_session *bfd_id_lookup(uint32_t id)
return hash_lookup(bfd_id_hash, &bs);
}
+struct bfd_key_walk_partial_lookup {
+ struct bfd_session *given;
+ struct bfd_session *result;
+};
+
+/* ignore some parameters */
+static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b, void *data)
+{
+ struct bfd_key_walk_partial_lookup *ctx =
+ (struct bfd_key_walk_partial_lookup *)data;
+ struct bfd_session *given = ctx->given;
+ struct bfd_session *parsed = b->data;
+
+ if (given->key.family != parsed->key.family)
+ return HASHWALK_CONTINUE;
+ if (given->key.mhop != parsed->key.mhop)
+ return HASHWALK_CONTINUE;
+ if (memcmp(&given->key.peer, &parsed->key.peer, sizeof(struct in6_addr)))
+ return HASHWALK_CONTINUE;
+ if (memcmp(given->key.vrfname, parsed->key.vrfname, MAXNAMELEN))
+ return HASHWALK_CONTINUE;
+ ctx->result = parsed;
+ /* ignore localaddr or interface */
+ return HASHWALK_ABORT;
+}
+
struct bfd_session *bfd_key_lookup(struct bfd_key key)
{
struct bfd_session bs, *bsp;
+ struct bfd_key_walk_partial_lookup ctx;
+ char peer_buf[INET6_ADDRSTRLEN];
bs.key = key;
bsp = hash_lookup(bfd_key_hash, &bs);
+ if (bsp)
+ return bsp;
+ inet_ntop(bs.key.family, &bs.key.peer, peer_buf,
+ sizeof(peer_buf));
/* Handle cases where local-address is optional. */
- if (bsp == NULL && bs.key.family == AF_INET) {
+ if (bs.key.family == AF_INET) {
memset(&bs.key.local, 0, sizeof(bs.key.local));
bsp = hash_lookup(bfd_key_hash, &bs);
+ if (bsp) {
+ char addr_buf[INET6_ADDRSTRLEN];
+
+ inet_ntop(bs.key.family, &key.local, addr_buf,
+ sizeof(addr_buf));
+ log_debug(" peer %s found, but loc-addr %s ignored",
+ peer_buf, addr_buf);
+ return bsp;
+ }
}
- /* Handle cases where ifname is optional. */
bs.key = key;
- if (bsp == NULL && bs.key.ifname[0]) {
+ /* Handle cases where ifname is optional. */
+ if (bs.key.ifname[0]) {
memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
bsp = hash_lookup(bfd_key_hash, &bs);
+ if (bsp) {
+ log_debug(" peer %s found, but ifp %s ignored",
+ peer_buf, key.ifname);
+ return bsp;
+ }
+ }
- /* Handle cases where local-address and ifname are optional. */
- if (bsp == NULL && bs.key.family == AF_INET) {
- memset(&bs.key.local, 0, sizeof(bs.key.local));
- bsp = hash_lookup(bfd_key_hash, &bs);
+ /* Handle cases where local-address and ifname are optional. */
+ if (bs.key.family == AF_INET) {
+ memset(&bs.key.local, 0, sizeof(bs.key.local));
+ bsp = hash_lookup(bfd_key_hash, &bs);
+ if (bsp) {
+ char addr_buf[INET6_ADDRSTRLEN];
+
+ inet_ntop(bs.key.family, &bs.key.local, addr_buf,
+ sizeof(addr_buf));
+ log_debug(" peer %s found, but ifp %s"
+ " and loc-addr %s ignored",
+ peer_buf, key.ifname,
+ addr_buf);
+ return bsp;
}
}
+ bs.key = key;
+ /* Handle case where a context more complex ctx is present.
+ * input has no iface nor local-address, but a context may
+ * exist
+ */
+ ctx.result = NULL;
+ ctx.given = &bs;
+ hash_walk(bfd_key_hash,
+ &bfd_key_lookup_ignore_partial_walker,
+ &ctx);
+ /* change key */
+ if (ctx.result) {
+ bsp = ctx.result;
+ log_debug(" peer %s found, but ifp"
+ " and/or loc-addr params ignored");
+ }
return bsp;
}
@@ -1443,3 +1527,125 @@ void bfd_shutdown(void)
hash_free(bfd_id_hash);
hash_free(bfd_key_hash);
}
+
+static int bfd_vrf_new(struct vrf *vrf)
+{
+ log_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
+ return 0;
+}
+
+static int bfd_vrf_delete(struct vrf *vrf)
+{
+ log_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
+ return 0;
+}
+
+static int bfd_vrf_enable(struct vrf *vrf)
+{
+ struct bfd_vrf_global *bvrf;
+
+ /* a different name */
+ if (!vrf->info) {
+ bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
+ bvrf->vrf = vrf;
+ vrf->info = (void *)bvrf;
+ } else
+ bvrf = vrf->info;
+ log_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
+
+ /* create sockets if needed */
+ if (!bvrf->bg_shop)
+ bvrf->bg_shop = bp_udp_shop(vrf->vrf_id);
+ if (!bvrf->bg_mhop)
+ bvrf->bg_mhop = bp_udp_mhop(vrf->vrf_id);
+ if (!bvrf->bg_shop6)
+ bvrf->bg_shop6 = bp_udp6_shop(vrf->vrf_id);
+ if (!bvrf->bg_mhop6)
+ bvrf->bg_mhop6 = bp_udp6_mhop(vrf->vrf_id);
+ if (!bvrf->bg_echo)
+ bvrf->bg_echo = bp_echo_socket(vrf->vrf_id);
+ if (!bvrf->bg_echov6)
+ bvrf->bg_echov6 = bp_echov6_socket(vrf->vrf_id);
+
+ /* Add descriptors to the event loop. */
+ if (!bvrf->bg_ev[0])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
+ &bvrf->bg_ev[0]);
+ if (!bvrf->bg_ev[1])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
+ &bvrf->bg_ev[1]);
+ if (!bvrf->bg_ev[2])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
+ &bvrf->bg_ev[2]);
+ if (!bvrf->bg_ev[3])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
+ &bvrf->bg_ev[3]);
+ if (!bvrf->bg_ev[4])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
+ &bvrf->bg_ev[4]);
+ if (!bvrf->bg_ev[5])
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
+ &bvrf->bg_ev[5]);
+
+ if (vrf->vrf_id != VRF_DEFAULT) {
+ bfdd_zclient_register(vrf->vrf_id);
+ bfdd_sessions_enable_vrf(vrf);
+ }
+ return 0;
+}
+
+static int bfd_vrf_disable(struct vrf *vrf)
+{
+ struct bfd_vrf_global *bvrf;
+
+ if (!vrf->info)
+ return 0;
+ bvrf = vrf->info;
+
+ if (vrf->vrf_id != VRF_DEFAULT) {
+ bfdd_sessions_disable_vrf(vrf);
+ bfdd_zclient_unregister(vrf->vrf_id);
+ }
+
+ log_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
+ /* Close all descriptors. */
+ socket_close(&bvrf->bg_echo);
+ socket_close(&bvrf->bg_shop);
+ socket_close(&bvrf->bg_mhop);
+ socket_close(&bvrf->bg_shop6);
+ socket_close(&bvrf->bg_mhop6);
+
+ /* free context */
+ XFREE(MTYPE_BFDD_VRF, bvrf);
+ vrf->info = NULL;
+
+ return 0;
+}
+
+void bfd_vrf_init(void)
+{
+ vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable,
+ bfd_vrf_delete, NULL);
+}
+
+void bfd_vrf_terminate(void)
+{
+ vrf_terminate();
+}
+
+struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
+{
+ struct vrf *vrf;
+
+ if (!vrf_is_backend_netns()) {
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ if (vrf)
+ return (struct bfd_vrf_global *)vrf->info;
+ return NULL;
+ }
+ if (!bfd)
+ return NULL;
+ if (!bfd->vrf)
+ return NULL;
+ return bfd->vrf->info;
+}
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index 28832e4c48..213e905bf0 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -48,6 +48,7 @@ DECLARE_MTYPE(BFDD_LABEL);
DECLARE_MTYPE(BFDD_CONTROL);
DECLARE_MTYPE(BFDD_SESSION_OBSERVER);
DECLARE_MTYPE(BFDD_NOTIFICATION);
+DECLARE_MTYPE(BFDD_VRF);
struct bfd_timers {
uint32_t desired_min_tx;
@@ -128,6 +129,12 @@ struct bfd_echo_pkt {
flags |= (val & 0x3) << 6; \
}
#define BFD_GETSTATE(flags) ((flags >> 6) & 0x3)
+#define BFD_SETCBIT(flags, val) \
+ { \
+ if ((val)) \
+ flags |= val; \
+ }
+#define BFD_GETCBIT(flags) (flags & BFD_FBIT)
#define BFD_ECHO_VERSION 1
#define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt)
@@ -166,6 +173,8 @@ enum bfd_session_flags {
* expires
*/
BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */
+ BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */
+ BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */
};
#define BFD_SET_FLAG(field, flag) (field |= flag)
@@ -208,6 +217,7 @@ struct bfd_session {
uint8_t detect_mult;
uint8_t remote_detect_mult;
uint8_t mh_ttl;
+ uint8_t remote_cbit;
/* Timers */
struct bfd_timers timers;
@@ -378,15 +388,19 @@ int control_accept(struct thread *t);
*
* Daemon specific code.
*/
-struct bfd_global {
+struct bfd_vrf_global {
int bg_shop;
int bg_mhop;
int bg_shop6;
int bg_mhop6;
int bg_echo;
int bg_echov6;
+ struct vrf *vrf;
+
struct thread *bg_ev[6];
+};
+struct bfd_global {
int bg_csock;
struct thread *bg_csockev;
struct bcslist bg_bcslist;
@@ -394,6 +408,8 @@ struct bfd_global {
struct pllist bg_pllist;
struct obslist bg_obslist;
+
+ struct zebra_privs_t bfdd_privs;
};
extern struct bfd_global bglobal;
extern struct bfd_diag_str_list diag_list[];
@@ -458,14 +474,14 @@ int bp_set_tosv6(int sd, uint8_t value);
int bp_set_tos(int sd, uint8_t value);
int bp_bind_dev(int sd, const char *dev);
-int bp_udp_shop(void);
-int bp_udp_mhop(void);
-int bp_udp6_shop(void);
-int bp_udp6_mhop(void);
+int bp_udp_shop(vrf_id_t vrf_id);
+int bp_udp_mhop(vrf_id_t vrf_id);
+int bp_udp6_shop(vrf_id_t vrf_id);
+int bp_udp6_mhop(vrf_id_t vrf_id);
int bp_peer_socket(const struct bfd_session *bs);
int bp_peer_socketv6(const struct bfd_session *bs);
-int bp_echo_socket(void);
-int bp_echov6_socket(void);
+int bp_echo_socket(vrf_id_t vrf_id);
+int bp_echov6_socket(vrf_id_t vrf_id);
void ptm_bfd_snd(struct bfd_session *bfd, int fbit);
void ptm_bfd_echo_snd(struct bfd_session *bfd);
@@ -504,9 +520,9 @@ void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb);
int bfd_session_enable(struct bfd_session *bs);
void bfd_session_disable(struct bfd_session *bs);
struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc);
-int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc);
-void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag);
-void ptm_bfd_ses_up(struct bfd_session *bfd);
+int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc);
+void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag);
+void ptm_bfd_sess_up(struct bfd_session *bfd);
void ptm_bfd_echo_stop(struct bfd_session *bfd);
void ptm_bfd_echo_start(struct bfd_session *bfd);
void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit);
@@ -538,6 +554,9 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
/* BFD hash data structures interface */
void bfd_initialize(void);
void bfd_shutdown(void);
+void bfd_vrf_init(void);
+void bfd_vrf_terminate(void);
+struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd);
struct bfd_session *bfd_id_lookup(uint32_t id);
struct bfd_session *bfd_key_lookup(struct bfd_key key);
@@ -575,6 +594,10 @@ void bfdd_vty_init(void);
*/
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv);
void bfdd_zclient_stop(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);
+void bfdd_sessions_disable_vrf(struct vrf *vrf);
int ptm_bfd_notify(struct bfd_session *bs);
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index 93677ec85a..f3acfa4167 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -37,15 +37,14 @@
#include "bfd.h"
-
/*
* Prototypes
*/
-static int ptm_bfd_process_echo_pkt(int s);
+static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s);
int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
size_t datalen);
-static void bfd_sd_reschedule(int sd);
+static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd);
ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
ifindex_t *ifindex, struct sockaddr_any *local,
struct sockaddr_any *peer);
@@ -54,7 +53,8 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
struct sockaddr_any *peer);
int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
struct sockaddr *to, socklen_t tolen);
-int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr);
+int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd,
+ uint8_t *ttl, uint32_t *my_discr);
/* socket related prototypes */
static void bp_set_ipopts(int sd);
@@ -129,7 +129,10 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
struct bfd_echo_pkt bep;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
+ struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
+ if (!bvrf)
+ return;
if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
@@ -139,7 +142,7 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
bep.my_discr = htonl(bfd->discrs.my_discr);
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
- sd = bglobal.bg_echov6;
+ sd = bvrf->bg_echov6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
@@ -154,7 +157,7 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
sa = (struct sockaddr *)&sin6;
salen = sizeof(sin6);
} else {
- sd = bglobal.bg_echo;
+ sd = bvrf->bg_echo;
memset(&sin6, 0, sizeof(sin6));
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
@@ -174,14 +177,14 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
bfd->stats.tx_echo_pkt++;
}
-static int ptm_bfd_process_echo_pkt(int s)
+static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
{
struct bfd_session *bfd;
uint32_t my_discr = 0;
uint8_t ttl = 0;
/* Receive and parse echo packet. */
- if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1)
+ if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr) == -1)
return 0;
/* Your discriminator not zero - use it to find session */
@@ -218,6 +221,10 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
BFD_SETVER(cp.diag, BFD_VERSION);
cp.flags = 0;
BFD_SETSTATE(cp.flags, bfd->ses_state);
+
+ if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT))
+ BFD_SETCBIT(cp.flags, BFD_CBIT);
+
BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
/*
@@ -441,32 +448,32 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
return mlen;
}
-static void bfd_sd_reschedule(int sd)
+static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
{
- if (sd == bglobal.bg_shop) {
- THREAD_OFF(bglobal.bg_ev[0]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
- &bglobal.bg_ev[0]);
- } else if (sd == bglobal.bg_mhop) {
- THREAD_OFF(bglobal.bg_ev[1]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
- &bglobal.bg_ev[1]);
- } else if (sd == bglobal.bg_shop6) {
- THREAD_OFF(bglobal.bg_ev[2]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
- &bglobal.bg_ev[2]);
- } else if (sd == bglobal.bg_mhop6) {
- THREAD_OFF(bglobal.bg_ev[3]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
- &bglobal.bg_ev[3]);
- } else if (sd == bglobal.bg_echo) {
- THREAD_OFF(bglobal.bg_ev[4]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
- &bglobal.bg_ev[4]);
- } else if (sd == bglobal.bg_echov6) {
- THREAD_OFF(bglobal.bg_ev[5]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
- &bglobal.bg_ev[5]);
+ if (sd == bvrf->bg_shop) {
+ THREAD_OFF(bvrf->bg_ev[0]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
+ &bvrf->bg_ev[0]);
+ } else if (sd == bvrf->bg_mhop) {
+ THREAD_OFF(bvrf->bg_ev[1]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
+ &bvrf->bg_ev[1]);
+ } else if (sd == bvrf->bg_shop6) {
+ THREAD_OFF(bvrf->bg_ev[2]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
+ &bvrf->bg_ev[2]);
+ } else if (sd == bvrf->bg_mhop6) {
+ THREAD_OFF(bvrf->bg_ev[3]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
+ &bvrf->bg_ev[3]);
+ } else if (sd == bvrf->bg_echo) {
+ THREAD_OFF(bvrf->bg_ev[4]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
+ &bvrf->bg_ev[4]);
+ } else if (sd == bvrf->bg_echov6) {
+ THREAD_OFF(bvrf->bg_ev[5]);
+ thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
+ &bvrf->bg_ev[5]);
}
}
@@ -518,13 +525,16 @@ int bfd_recv_cb(struct thread *t)
ifindex_t ifindex = IFINDEX_INTERNAL;
struct sockaddr_any local, peer;
uint8_t msgbuf[1516];
+ struct bfd_vrf_global *bvrf = THREAD_ARG(t);
+ if (bvrf)
+ vrfid = bvrf->vrf->vrf_id;
/* Schedule next read. */
- bfd_sd_reschedule(sd);
+ bfd_sd_reschedule(bvrf, sd);
/* Handle echo packets. */
- if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) {
- ptm_bfd_process_echo_pkt(sd);
+ if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) {
+ ptm_bfd_process_echo_pkt(bvrf, sd);
return 0;
}
@@ -534,12 +544,12 @@ int bfd_recv_cb(struct thread *t)
/* Handle control packets. */
is_mhop = false;
- if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
- is_mhop = sd == bglobal.bg_mhop;
+ if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) {
+ is_mhop = sd == bvrf->bg_mhop;
mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
&local, &peer);
- } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
- is_mhop = sd == bglobal.bg_mhop6;
+ } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) {
+ is_mhop = sd == bvrf->bg_mhop6;
mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
&local, &peer);
}
@@ -640,6 +650,11 @@ int bfd_recv_cb(struct thread *t)
ntohl(cp->timers.required_min_echo);
bfd->remote_detect_mult = cp->detect_mult;
+ if (BFD_GETCBIT(cp->flags))
+ bfd->remote_cbit = 1;
+ else
+ bfd->remote_cbit = 0;
+
/* State switch from section 6.2. */
bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
@@ -682,7 +697,8 @@ int bfd_recv_cb(struct thread *t)
*
* Returns -1 on error or loopback or 0 on success.
*/
-int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
+int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd,
+ uint8_t *ttl, uint32_t *my_discr)
{
struct bfd_echo_pkt *bep;
ssize_t rlen;
@@ -691,7 +707,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
vrf_id_t vrfid = VRF_DEFAULT;
uint8_t msgbuf[1516];
- if (sd == bglobal.bg_echo)
+ if (sd == bvrf->bg_echo)
rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
&local, &peer);
else
@@ -709,7 +725,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
if (*ttl == BFD_TTL_VAL) {
bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
(struct sockaddr *)&peer,
- (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin)
+ (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin)
: sizeof(peer.sa_sin6));
return -1;
}
@@ -872,25 +888,28 @@ static void bp_bind_ip(int sd, uint16_t port)
log_fatal("bind-ip: bind: %s", strerror(errno));
}
-int bp_udp_shop(void)
+int bp_udp_shop(vrf_id_t vrf_id)
{
int sd;
- sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);
+ }
if (sd == -1)
log_fatal("udp-shop: socket: %s", strerror(errno));
bp_set_ipopts(sd);
bp_bind_ip(sd, BFD_DEFDESTPORT);
-
return sd;
}
-int bp_udp_mhop(void)
+int bp_udp_mhop(vrf_id_t vrf_id)
{
int sd;
- sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);
+ }
if (sd == -1)
log_fatal("udp-mhop: socket: %s", strerror(errno));
@@ -905,8 +924,18 @@ int bp_peer_socket(const struct bfd_session *bs)
int sd, pcount;
struct sockaddr_in sin;
static int srcPort = BFD_SRCPORTINIT;
+ const char *device_to_bind = NULL;
- sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
+ if (bs->key.ifname[0])
+ device_to_bind = (const char *)bs->key.ifname;
+ else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
+ && bs->key.vrfname[0])
+ device_to_bind = (const char *)bs->key.vrfname;
+
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,
+ bs->vrf->vrf_id, device_to_bind);
+ }
if (sd == -1) {
log_error("ipv4-new: failed to create socket: %s",
strerror(errno));
@@ -925,19 +954,6 @@ int bp_peer_socket(const struct bfd_session *bs)
return -1;
}
- if (bs->key.ifname[0]) {
- if (bp_bind_dev(sd, bs->key.ifname) != 0) {
- close(sd);
- return -1;
- }
- } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
- && bs->key.vrfname[0]) {
- if (bp_bind_dev(sd, bs->key.vrfname) != 0) {
- close(sd);
- return -1;
- }
- }
-
/* Find an available source port in the proper range */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
@@ -975,8 +991,18 @@ int bp_peer_socketv6(const struct bfd_session *bs)
int sd, pcount;
struct sockaddr_in6 sin6;
static int srcPort = BFD_SRCPORTINIT;
+ const char *device_to_bind = NULL;
- sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
+ if (bs->key.ifname[0])
+ device_to_bind = (const char *)bs->key.ifname;
+ else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
+ && bs->key.vrfname[0])
+ device_to_bind = (const char *)bs->key.vrfname;
+
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,
+ bs->vrf->vrf_id, device_to_bind);
+ }
if (sd == -1) {
log_error("ipv6-new: failed to create socket: %s",
strerror(errno));
@@ -1005,19 +1031,6 @@ int bp_peer_socketv6(const struct bfd_session *bs)
if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
sin6.sin6_scope_id = bs->ifp->ifindex;
- if (bs->key.ifname[0]) {
- if (bp_bind_dev(sd, bs->key.ifname) != 0) {
- close(sd);
- return -1;
- }
- } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
- && bs->key.vrfname[0]) {
- if (bp_bind_dev(sd, bs->key.vrfname) != 0) {
- close(sd);
- return -1;
- }
- }
-
pcount = 0;
do {
if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
@@ -1102,11 +1115,13 @@ static void bp_bind_ipv6(int sd, uint16_t port)
log_fatal("bind-ipv6: bind: %s", strerror(errno));
}
-int bp_udp6_shop(void)
+int bp_udp6_shop(vrf_id_t vrf_id)
{
int sd;
- sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);
+ }
if (sd == -1)
log_fatal("udp6-shop: socket: %s", strerror(errno));
@@ -1116,11 +1131,13 @@ int bp_udp6_shop(void)
return sd;
}
-int bp_udp6_mhop(void)
+int bp_udp6_mhop(vrf_id_t vrf_id)
{
int sd;
- sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);
+ }
if (sd == -1)
log_fatal("udp6-mhop: socket: %s", strerror(errno));
@@ -1130,11 +1147,13 @@ int bp_udp6_mhop(void)
return sd;
}
-int bp_echo_socket(void)
+int bp_echo_socket(vrf_id_t vrf_id)
{
int s;
- s = socket(AF_INET, SOCK_DGRAM, 0);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);
+ }
if (s == -1)
log_fatal("echo-socket: socket: %s", strerror(errno));
@@ -1144,11 +1163,13 @@ int bp_echo_socket(void)
return s;
}
-int bp_echov6_socket(void)
+int bp_echov6_socket(vrf_id_t vrf_id)
{
int s;
- s = socket(AF_INET6, SOCK_DGRAM, 0);
+ frr_elevate_privs(&bglobal.bfdd_privs) {
+ s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf_id, NULL);
+ }
if (s == -1)
log_fatal("echov6-socket: socket: %s", strerror(errno));
diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h
index 0da1ca8df6..4ce23a8f27 100644
--- a/bfdd/bfdctl.h
+++ b/bfdd/bfdctl.h
@@ -88,6 +88,8 @@ struct bfd_peer_cfg {
bool bpc_createonly;
bool bpc_shutdown;
+ bool bpc_cbit;
+
/* Status information */
enum bfd_peer_status bpc_bps;
uint32_t bpc_id;
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
index 6023b5e4f0..218f0883c5 100644
--- a/bfdd/bfdd.c
+++ b/bfdd/bfdd.c
@@ -34,25 +34,13 @@ DEFINE_MTYPE(BFDD, BFDD_LABEL, "long-lived label memory");
DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory");
DEFINE_MTYPE(BFDD, BFDD_SESSION_OBSERVER, "Session observer");
DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data");
+DEFINE_MTYPE(BFDD, BFDD_VRF, "BFD VRF");
/* Master of threads. */
struct thread_master *master;
/* BFDd privileges */
-static zebra_capabilities_t _caps_p[] = {ZCAP_BIND};
-
-struct zebra_privs_t bfdd_privs = {
-#if defined(FRR_USER) && defined(FRR_GROUP)
- .user = FRR_USER,
- .group = FRR_GROUP,
-#endif
-#if defined(VTY_GROUP)
- .vty_group = VTY_GROUP,
-#endif
- .caps_p = _caps_p,
- .cap_num_p = array_size(_caps_p),
- .cap_num_i = 0,
-};
+static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW};
void socket_close(int *s)
{
@@ -85,12 +73,7 @@ static void sigterm_handler(void)
/* Shutdown and free all protocol related memory. */
bfd_shutdown();
- /* Close all descriptors. */
- socket_close(&bglobal.bg_echo);
- socket_close(&bglobal.bg_shop);
- socket_close(&bglobal.bg_mhop);
- socket_close(&bglobal.bg_shop6);
- socket_close(&bglobal.bg_mhop6);
+ bfd_vrf_terminate();
/* Terminate and free() FRR related memory. */
frr_fini();
@@ -116,7 +99,7 @@ static struct quagga_signal_t bfd_signals[] = {
FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617,
.proghelp = "Implementation of the BFD protocol.",
.signals = bfd_signals, .n_signals = array_size(bfd_signals),
- .privs = &bfdd_privs)
+ .privs = &bglobal.bfdd_privs)
#define OPTION_CTLSOCK 1001
static struct option longopts[] = {
@@ -153,15 +136,24 @@ struct bfd_state_str_list state_list[] = {
static void bg_init(void)
{
+ struct zebra_privs_t bfdd_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0,
+ };
+
TAILQ_INIT(&bglobal.bg_bcslist);
TAILQ_INIT(&bglobal.bg_obslist);
- bglobal.bg_shop = bp_udp_shop();
- bglobal.bg_mhop = bp_udp_mhop();
- bglobal.bg_shop6 = bp_udp6_shop();
- bglobal.bg_mhop6 = bp_udp6_mhop();
- bglobal.bg_echo = bp_echo_socket();
- bglobal.bg_echov6 = bp_echov6_socket();
+ memcpy(&bglobal.bfdd_privs, &bfdd_privs,
+ sizeof(bfdd_privs));
}
int main(int argc, char *argv[])
@@ -169,6 +161,9 @@ int main(int argc, char *argv[])
const char *ctl_path = BFDD_CONTROL_SOCKET;
int opt;
+ /* Initialize system sockets. */
+ bg_init();
+
frr_preinit(&bfdd_di, argc, argv);
frr_opt_add("", longopts,
" --bfdctl Specify bfdd control socket\n");
@@ -196,9 +191,6 @@ int main(int argc, char *argv[])
/* Initialize logging API. */
log_init(1, BLOG_DEBUG, &bfdd_di);
- /* Initialize system sockets. */
- bg_init();
-
/* Initialize control socket. */
control_init(ctl_path);
@@ -208,22 +200,11 @@ int main(int argc, char *argv[])
/* Initialize BFD data structures. */
bfd_initialize();
+ bfd_vrf_init();
+
/* Initialize zebra connection. */
- bfdd_zclient_init(&bfdd_privs);
-
- /* Add descriptors to the event loop. */
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
- &bglobal.bg_ev[0]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
- &bglobal.bg_ev[1]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
- &bglobal.bg_ev[2]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
- &bglobal.bg_ev[3]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
- &bglobal.bg_ev[4]);
- thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
- &bglobal.bg_ev[5]);
+ bfdd_zclient_init(&bglobal.bfdd_privs);
+
thread_add_read(master, control_accept, NULL, bglobal.bg_csock,
&bglobal.bg_csockev);
diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c
index c139492076..75f6632db0 100644
--- a/bfdd/bfdd_vty.c
+++ b/bfdd/bfdd_vty.c
@@ -64,7 +64,7 @@ static struct json_object *__display_peer_json(struct bfd_session *bs);
static struct json_object *_peer_json_header(struct bfd_session *bs);
static void _display_peer_json(struct vty *vty, struct bfd_session *bs);
static void _display_peer(struct vty *vty, struct bfd_session *bs);
-static void _display_all_peers(struct vty *vty, bool use_json);
+static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json);
static void _display_peer_iter(struct hash_bucket *hb, void *arg);
static void _display_peer_json_iter(struct hash_bucket *hb, void *arg);
static void _display_peer_counter(struct vty *vty, struct bfd_session *bs);
@@ -72,7 +72,7 @@ static struct json_object *__display_peer_counters_json(struct bfd_session *bs);
static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs);
static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg);
static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg);
-static void _display_peers_counter(struct vty *vty, bool use_json);
+static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json);
static struct bfd_session *
_find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv,
const char *label, const char *peer_str,
@@ -90,7 +90,7 @@ DEFUN_NOSH(bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n")
DEFUN_NOSH(
bfd_peer_enter, bfd_peer_enter_cmd,
- "peer <A.B.C.D|X:X::X:X> [{[multihop] local-address <A.B.C.D|X:X::X:X>|interface IFNAME|vrf NAME}]",
+ "peer <A.B.C.D|X:X::X:X> [{multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME|vrf NAME}]",
PEER_STR PEER_IPV4_STR PEER_IPV6_STR
MHOP_STR
LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR
@@ -126,15 +126,6 @@ DEFUN_NOSH(
if (argv_find(argv, argc, "vrf", &idx))
vrfname = argv[idx + 1]->arg;
- if (vrfname && ifname) {
- vty_out(vty, "%% VRF is not mixable with interface\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- if (vrfname && !mhop) {
- vty_out(vty, "%% VRF only applies with multihop.\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
strtosa(peer, &psa);
if (local) {
strtosa(local, &lsa);
@@ -158,6 +149,12 @@ DEFUN_NOSH(
}
}
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) {
+ if (bs->refcount)
+ vty_out(vty, "%% session peer is now configurable via bfd daemon.\n");
+ BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ }
+
VTY_PUSH_CONTEXT(BFD_PEER_NODE, bs);
return CMD_SUCCESS;
@@ -354,7 +351,7 @@ DEFPY(bfd_no_peer, bfd_no_peer_cmd,
return CMD_WARNING_CONFIG_FAILED;
}
- if (ptm_bfd_ses_del(&bpc) != 0) {
+ if (ptm_bfd_sess_del(&bpc) != 0) {
vty_out(vty, "%% Failed to remove peer.\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -543,19 +540,46 @@ static void _display_peer_json(struct vty *vty, struct bfd_session *bs)
json_object_free(jo);
}
+struct bfd_vrf_tuple {
+ char *vrfname;
+ struct vty *vty;
+ struct json_object *jo;
+};
+
static void _display_peer_iter(struct hash_bucket *hb, void *arg)
{
- struct vty *vty = arg;
+ struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg;
+ struct vty *vty;
struct bfd_session *bs = hb->data;
+ if (!bvt)
+ return;
+ vty = bvt->vty;
+
+ if (bvt->vrfname) {
+ if (!bs->key.vrfname[0] ||
+ !strmatch(bs->key.vrfname, bvt->vrfname))
+ return;
+ }
_display_peer(vty, bs);
}
static void _display_peer_json_iter(struct hash_bucket *hb, void *arg)
{
- struct json_object *jo = arg, *jon = NULL;
+ struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg;
+ struct json_object *jo, *jon = NULL;
struct bfd_session *bs = hb->data;
+ if (!bvt)
+ return;
+ jo = bvt->jo;
+
+ if (bvt->vrfname) {
+ if (!bs->key.vrfname[0] ||
+ !strmatch(bs->key.vrfname, bvt->vrfname))
+ return;
+ }
+
jon = __display_peer_json(bs);
if (jon == NULL) {
log_warning("%s: not enough memory", __func__);
@@ -565,18 +589,24 @@ static void _display_peer_json_iter(struct hash_bucket *hb, void *arg)
json_object_array_add(jo, jon);
}
-static void _display_all_peers(struct vty *vty, bool use_json)
+static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json)
{
struct json_object *jo;
+ struct bfd_vrf_tuple bvt;
+
+ memset(&bvt, 0, sizeof(bvt));
+ bvt.vrfname = vrfname;
if (!use_json) {
+ bvt.vty = vty;
vty_out(vty, "BFD Peers:\n");
- bfd_id_iterate(_display_peer_iter, vty);
+ bfd_id_iterate(_display_peer_iter, &bvt);
return;
}
jo = json_object_new_array();
- bfd_id_iterate(_display_peer_json_iter, jo);
+ bvt.jo = jo;
+ bfd_id_iterate(_display_peer_json_iter, &bvt);
vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
json_object_free(jo);
@@ -628,16 +658,38 @@ static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs)
static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg)
{
- struct vty *vty = arg;
+ struct bfd_vrf_tuple *bvt = arg;
+ struct vty *vty;
struct bfd_session *bs = hb->data;
+ if (!bvt)
+ return;
+ vty = bvt->vty;
+
+ if (bvt->vrfname) {
+ if (!bs->key.vrfname[0] ||
+ !strmatch(bs->key.vrfname, bvt->vrfname))
+ return;
+ }
+
_display_peer_counter(vty, bs);
}
static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg)
{
- struct json_object *jo = arg, *jon = NULL;
+ struct json_object *jo, *jon = NULL;
struct bfd_session *bs = hb->data;
+ struct bfd_vrf_tuple *bvt = arg;
+
+ if (!bvt)
+ return;
+ jo = bvt->jo;
+
+ if (bvt->vrfname) {
+ if (!bs->key.vrfname[0] ||
+ !strmatch(bs->key.vrfname, bvt->vrfname))
+ return;
+ }
jon = __display_peer_counters_json(bs);
if (jon == NULL) {
@@ -648,17 +700,22 @@ static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg)
json_object_array_add(jo, jon);
}
-static void _display_peers_counter(struct vty *vty, bool use_json)
+static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json)
{
struct json_object *jo;
+ struct bfd_vrf_tuple bvt;
+ memset(&bvt, 0, sizeof(struct bfd_vrf_tuple));
+ bvt.vrfname = vrfname;
if (!use_json) {
+ bvt.vty = vty;
vty_out(vty, "BFD Peers:\n");
- bfd_id_iterate(_display_peer_counter_iter, vty);
+ bfd_id_iterate(_display_peer_counter_iter, &bvt);
return;
}
jo = json_object_new_array();
+ bvt.jo = jo;
bfd_id_iterate(_display_peer_counter_json_iter, jo);
vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
@@ -728,24 +785,31 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv,
/*
* Show commands.
*/
-DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd peers [json]",
+DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd [vrf <NAME>] peers [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
+ VRF_CMD_HELP_STR
"BFD peers status\n" JSON_STR)
{
- _display_all_peers(vty, use_json(argc, argv));
+ char *vrf_name = NULL;
+ int idx_vrf = 0;
+
+ if (argv_find(argv, argc, "vrf", &idx_vrf))
+ vrf_name = argv[idx_vrf + 1]->arg;
+
+ _display_all_peers(vty, vrf_name, use_json(argc, argv));
return CMD_SUCCESS;
}
DEFPY(bfd_show_peer, bfd_show_peer_cmd,
- "show bfd peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]",
+ "show bfd [vrf <NAME$vrfname>] peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname}]> [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
+ VRF_CMD_HELP_STR
"BFD peers status\n"
"Peer label\n" PEER_IPV4_STR PEER_IPV6_STR MHOP_STR LOCAL_STR
- LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR VRF_STR
- VRF_NAME_STR JSON_STR)
+ LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR JSON_STR)
{
struct bfd_session *bs;
@@ -766,9 +830,10 @@ DEFPY(bfd_show_peer, bfd_show_peer_cmd,
}
DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd,
- "show bfd peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]> counters [json]",
+ "show bfd [vrf <NAME$vrfname>] peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname}]> counters [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
+ VRF_CMD_HELP_STR
"BFD peers status\n"
"Peer label\n"
PEER_IPV4_STR
@@ -779,8 +844,6 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd,
LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
- VRF_STR
- VRF_NAME_STR
"Show BFD peer counters information\n"
JSON_STR)
{
@@ -801,14 +864,21 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd,
}
DEFPY(bfd_show_peers_counters, bfd_show_peers_counters_cmd,
- "show bfd peers counters [json]",
+ "show bfd [vrf <NAME>] peers counters [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
+ VRF_CMD_HELP_STR
"BFD peers status\n"
"Show BFD peer counters information\n"
JSON_STR)
{
- _display_peers_counter(vty, use_json(argc, argv));
+ char *vrf_name = NULL;
+ int idx_vrf = 0;
+
+ if (argv_find(argv, argc, "vrf", &idx_vrf))
+ vrf_name = argv[idx_vrf + 1]->arg;
+
+ _display_peers_counter(vty, vrf_name, use_json(argc, argv));
return CMD_SUCCESS;
}
@@ -984,6 +1054,9 @@ static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg)
struct vty *vty = arg;
struct bfd_session *bs = hb->data;
+ if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG))
+ return;
+
_bfdd_peer_write_config(vty, bs);
}
diff --git a/bfdd/bsd.c b/bfdd/bsd.c
deleted file mode 100644
index 923fbd909e..0000000000
--- a/bfdd/bsd.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * *BSD specific code
- *
- * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
- *
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include <zebra.h>
-
-#ifdef BFD_BSD
-
-#include <net/if.h>
-#include <net/if_types.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <ifaddrs.h>
-
-#include "bfd.h"
-
-/*
- * Definitions.
- */
-int bp_bind_dev(int sd, const char *dev)
-{
- /*
- * *BSDs don't support `SO_BINDTODEVICE`, instead you must
- * manually specify the main address of the interface or use
- * BPF on the socket descriptor.
- */
- return 0;
-}
-
-#endif /* BFD_BSD */
diff --git a/bfdd/config.c b/bfdd/config.c
index cd57ea9fe3..74e7d63d0c 100644
--- a/bfdd/config.c
+++ b/bfdd/config.c
@@ -68,7 +68,7 @@ static int config_add(struct bfd_peer_cfg *bpc,
static int config_del(struct bfd_peer_cfg *bpc,
void *arg __attribute__((unused)))
{
- return ptm_bfd_ses_del(bpc) != 0;
+ return ptm_bfd_sess_del(bpc) != 0;
}
static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg)
diff --git a/bfdd/linux.c b/bfdd/linux.c
deleted file mode 100644
index 3a76b459d7..0000000000
--- a/bfdd/linux.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Linux specific code
- *
- * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
- *
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include <zebra.h>
-
-#ifdef BFD_LINUX
-
-#include "bfd.h"
-
-
-/*
- * Definitions.
- */
-int bp_bind_dev(int sd __attribute__((__unused__)),
- const char *dev __attribute__((__unused__)))
-{
- /*
- * TODO: implement this differently. It is not possible to
- * SO_BINDTODEVICE after the daemon has dropped its privileges.
- */
-#if 0
- size_t devlen = strlen(dev) + 1;
-
- if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, devlen) == -1) {
- log_warning("%s: setsockopt(SO_BINDTODEVICE, \"%s\"): %s",
- __func__, dev, strerror(errno));
- return -1;
- }
-#endif
-
- return 0;
-}
-
-#endif /* BFD_LINUX */
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
index 8d80b9468d..3e2ace6ea6 100644
--- a/bfdd/ptm_adapter.c
+++ b/bfdd/ptm_adapter.c
@@ -58,7 +58,7 @@ static struct zclient *zclient;
static int _ptm_msg_address(struct stream *msg, int family, const void *addr);
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
-static int _ptm_msg_read(struct stream *msg, int command,
+static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
struct bfd_peer_cfg *bpc, struct ptm_client **pc);
static struct ptm_client *pc_lookup(uint32_t pid);
@@ -72,8 +72,8 @@ static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
static void pcn_free(struct ptm_client_notification *pcn);
-static void bfdd_dest_register(struct stream *msg);
-static void bfdd_dest_deregister(struct stream *msg);
+static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id);
+static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id);
static void bfdd_client_register(struct stream *msg);
static void bfdd_client_deregister(struct stream *msg);
@@ -89,6 +89,7 @@ static void debug_printbpc(const char *func, unsigned int line,
{
char addr[3][128];
char timers[3][128];
+ char cbit_str[10];
addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] =
timers[2][0] = 0;
@@ -117,9 +118,11 @@ static void debug_printbpc(const char *func, unsigned int line,
snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d",
bpc->bpc_detectmultiplier);
- log_debug("%s:%d: %s %s%s%s%s%s%s", func, line,
+ sprintf(cbit_str, "CB %x", bpc->bpc_cbit);
+
+ log_debug("%s:%d: %s %s%s%s%s%s%s %s", func, line,
bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1],
- addr[2], timers[0], timers[1], timers[2]);
+ addr[2], timers[0], timers[1], timers[2], cbit_str);
}
#define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc))
@@ -173,6 +176,7 @@ int ptm_bfd_notify(struct bfd_session *bs)
* - AF_INET6:
* - 16 bytes: ipv6
* - c: prefix length
+ * - c: cbit
*
* Commands: ZEBRA_BFD_DEST_REPLAY
*
@@ -182,7 +186,10 @@ int ptm_bfd_notify(struct bfd_session *bs)
stream_reset(msg);
/* TODO: VRF handling */
- zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
+ if (bs->vrf)
+ zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id);
+ else
+ zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
/* This header will be handled by `zebra_ptm.c`. */
stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
@@ -216,6 +223,8 @@ int ptm_bfd_notify(struct bfd_session *bs)
/* BFD source prefix information. */
_ptm_msg_address(msg, bs->key.family, &bs->key.local);
+ stream_putc(msg, bs->remote_cbit);
+
/* Write packet size. */
stream_putw_at(msg, 0, stream_get_endp(msg));
@@ -256,7 +265,7 @@ stream_failure:
memset(sa, 0, sizeof(*sa));
}
-static int _ptm_msg_read(struct stream *msg, int command,
+static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
{
uint32_t pid;
@@ -290,6 +299,7 @@ static int _ptm_msg_read(struct stream *msg, int command,
* - 16 bytes: ipv6 address
* - c: ifname length
* - X bytes: interface name
+ * - c: bfd_cbit
*
* q(64), l(32), w(16), c(8)
*/
@@ -355,6 +365,20 @@ static int _ptm_msg_read(struct stream *msg, int command,
bpc->bpc_localif[ifnamelen] = 0;
}
}
+ if (vrf_id != VRF_DEFAULT) {
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (vrf) {
+ bpc->bpc_has_vrfname = true;
+ strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname));
+ } else {
+ log_error("ptm-read: vrf id %u could not be identified", vrf_id);
+ return -1;
+ }
+ }
+
+ STREAM_GETC(msg, bpc->bpc_cbit);
/* Sanity check: peer and local address must match IP types. */
if (bpc->bpc_local.sa_sin.sin_family != 0
@@ -370,7 +394,7 @@ stream_failure:
return -1;
}
-static void bfdd_dest_register(struct stream *msg)
+static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
{
struct ptm_client *pc;
struct ptm_client_notification *pcn;
@@ -378,7 +402,7 @@ static void bfdd_dest_register(struct stream *msg)
struct bfd_peer_cfg bpc;
/* Read the client context and peer data. */
- if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1)
+ if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1)
return;
DEBUG_PRINTBPC(&bpc);
@@ -408,7 +432,7 @@ static void bfdd_dest_register(struct stream *msg)
ptm_bfd_notify(bs);
}
-static void bfdd_dest_deregister(struct stream *msg)
+static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
{
struct ptm_client *pc;
struct ptm_client_notification *pcn;
@@ -416,7 +440,7 @@ static void bfdd_dest_deregister(struct stream *msg)
struct bfd_peer_cfg bpc;
/* Read the client context and peer data. */
- if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1)
+ if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1)
return;
DEBUG_PRINTBPC(&bpc);
@@ -431,6 +455,10 @@ static void bfdd_dest_deregister(struct stream *msg)
/* Unregister client peer notification. */
pcn = pcn_lookup(pc, bs);
pcn_free(pcn);
+ if (bs->refcount ||
+ BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG))
+ return;
+ ptm_bfd_sess_del(&bpc);
}
/*
@@ -483,9 +511,9 @@ stream_failure:
log_error("ptm-del-client: failed to deregister client");
}
-static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid)
+static int bfdd_replay(ZAPI_CALLBACK_ARGS)
{
- struct stream *msg = zc->ibuf;
+ struct stream *msg = zclient->ibuf;
uint32_t rcmd;
STREAM_GETL(msg, rcmd);
@@ -493,10 +521,10 @@ static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid)
switch (rcmd) {
case ZEBRA_BFD_DEST_REGISTER:
case ZEBRA_BFD_DEST_UPDATE:
- bfdd_dest_register(msg);
+ bfdd_dest_register(msg, vrf_id);
break;
case ZEBRA_BFD_DEST_DEREGISTER:
- bfdd_dest_deregister(msg);
+ bfdd_dest_deregister(msg, vrf_id);
break;
case ZEBRA_BFD_CLIENT_REGISTER:
bfdd_client_register(msg);
@@ -544,15 +572,21 @@ static void bfdd_sessions_enable_interface(struct interface *ifp)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
+ struct vrf *vrf;
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+ bs = bso->bso_bs;
if (bso->bso_isinterface == false)
continue;
-
/* Interface name mismatch. */
- bs = bso->bso_bs;
if (strcmp(ifp->name, bs->key.ifname))
continue;
+ vrf = vrf_lookup_by_id(ifp->vrf_id);
+ if (!vrf)
+ continue;
+ if (bs->key.vrfname[0] &&
+ strcmp(vrf->name, bs->key.vrfname))
+ continue;
/* Skip enabled sessions. */
if (bs->sock != -1)
continue;
@@ -582,13 +616,56 @@ static void bfdd_sessions_disable_interface(struct interface *ifp)
/* Try to enable it. */
bfd_session_disable(bs);
- TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry);
}
}
-static int bfdd_interface_update(int cmd, struct zclient *zc,
- uint16_t len __attribute__((__unused__)),
- vrf_id_t vrfid)
+void bfdd_sessions_enable_vrf(struct vrf *vrf)
+{
+ struct bfd_session_observer *bso;
+ struct bfd_session *bs;
+
+ /* it may affect configs without interfaces */
+ TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+ bs = bso->bso_bs;
+ if (bs->vrf)
+ continue;
+ if (bs->key.vrfname[0] &&
+ strcmp(vrf->name, bs->key.vrfname))
+ continue;
+ /* need to update the vrf information on
+ * bs so that callbacks are handled
+ */
+ bs->vrf = vrf;
+ /* Skip enabled sessions. */
+ if (bs->sock != -1)
+ continue;
+ /* Try to enable it. */
+ bfd_session_enable(bs);
+ }
+}
+
+void bfdd_sessions_disable_vrf(struct vrf *vrf)
+{
+ struct bfd_session_observer *bso;
+ struct bfd_session *bs;
+
+ TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+ if (bso->bso_isinterface)
+ continue;
+ bs = bso->bso_bs;
+ if (bs->key.vrfname[0] &&
+ strcmp(vrf->name, bs->key.vrfname))
+ continue;
+ /* Skip disabled sessions. */
+ if (bs->sock == -1)
+ continue;
+
+ /* Try to enable it. */
+ bfd_session_disable(bs);
+ }
+}
+
+static int bfdd_interface_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -598,7 +675,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc,
* rolling our own.
*/
if (cmd == ZEBRA_INTERFACE_ADD) {
- ifp = zebra_interface_add_read(zc->ibuf, vrfid);
+ ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
if (ifp == NULL)
return 0;
@@ -607,7 +684,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc,
}
/* Update interface information. */
- ifp = zebra_interface_state_read(zc->ibuf, vrfid);
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
if (ifp == NULL)
return 0;
@@ -618,16 +695,12 @@ static int bfdd_interface_update(int cmd, struct zclient *zc,
return 0;
}
-static int bfdd_interface_vrf_update(int command __attribute__((__unused__)),
- struct zclient *zclient,
- zebra_size_t length
- __attribute__((__unused__)),
- vrf_id_t vrfid)
+static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t nvrfid;
- ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrfid, &nvrfid);
+ ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid);
if (ifp == NULL)
return 0;
@@ -662,14 +735,11 @@ static void bfdd_sessions_enable_address(struct connected *ifc)
}
}
-static int bfdd_interface_address_update(int cmd, struct zclient *zc,
- zebra_size_t len
- __attribute__((__unused__)),
- vrf_id_t vrfid)
+static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
- ifc = zebra_interface_address_read(cmd, zc->ibuf, vrfid);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -706,6 +776,20 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
zclient->interface_address_delete = bfdd_interface_address_update;
}
+void bfdd_zclient_register(vrf_id_t vrf_id)
+{
+ if (!zclient || zclient->sock < 0)
+ return;
+ zclient_send_reg_requests(zclient, vrf_id);
+}
+
+void bfdd_zclient_unregister(vrf_id_t vrf_id)
+{
+ if (!zclient || zclient->sock < 0)
+ return;
+ zclient_send_dereg_requests(zclient, vrf_id);
+}
+
void bfdd_zclient_stop(void)
{
zclient_stop(zclient);
diff --git a/bfdd/subdir.am b/bfdd/subdir.am
index 334e974b04..e88b982ec3 100644
--- a/bfdd/subdir.am
+++ b/bfdd/subdir.am
@@ -14,11 +14,9 @@ bfdd_libbfd_a_SOURCES = \
bfdd/bfd.c \
bfdd/bfdd_vty.c \
bfdd/bfd_packet.c \
- bfdd/bsd.c \
bfdd/config.c \
bfdd/control.c \
bfdd/event.c \
- bfdd/linux.c \
bfdd/log.c \
bfdd/ptm_adapter.c \
# end
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c
index 05eeeca156..497fb0749e 100644
--- a/bgpd/bgp_advertise.c
+++ b/bgpd/bgp_advertise.c
@@ -64,9 +64,9 @@ static void *baa_hash_alloc(void *p)
return baa;
}
-unsigned int baa_hash_key(void *p)
+unsigned int baa_hash_key(const void *p)
{
- struct bgp_advertise_attr *baa = (struct bgp_advertise_attr *)p;
+ const struct bgp_advertise_attr *baa = p;
return attrhash_key_make(baa->attr);
}
@@ -241,9 +241,9 @@ void bgp_sync_init(struct peer *peer)
FOREACH_AFI_SAFI (afi, safi) {
sync = XCALLOC(MTYPE_BGP_SYNCHRONISE,
sizeof(struct bgp_synchronize));
- BGP_ADV_FIFO_INIT(&sync->update);
- BGP_ADV_FIFO_INIT(&sync->withdraw);
- BGP_ADV_FIFO_INIT(&sync->withdraw_low);
+ bgp_adv_fifo_init(&sync->update);
+ bgp_adv_fifo_init(&sync->withdraw);
+ bgp_adv_fifo_init(&sync->withdraw_low);
peer->sync[afi][safi] = sync;
}
}
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index 9aa5a0eaff..1b55b6e64b 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -21,16 +21,11 @@
#ifndef _QUAGGA_BGP_ADVERTISE_H
#define _QUAGGA_BGP_ADVERTISE_H
-#include <lib/fifo.h>
+#include "lib/typesafe.h"
-struct update_subgroup;
+PREDECL_DLIST(bgp_adv_fifo)
-/* BGP advertise FIFO. */
-struct bgp_advertise_fifo {
- struct bgp_advertise *next;
- struct bgp_advertise *prev;
- uint32_t count;
-};
+struct update_subgroup;
/* BGP advertise attribute. */
struct bgp_advertise_attr {
@@ -46,7 +41,7 @@ struct bgp_advertise_attr {
struct bgp_advertise {
/* FIFO for advertisement. */
- struct bgp_advertise_fifo fifo;
+ struct bgp_adv_fifo_item fifo;
/* Link list for same attribute advertise. */
struct bgp_advertise *next;
@@ -65,6 +60,8 @@ struct bgp_advertise {
struct bgp_path_info *pathi;
};
+DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo)
+
/* BGP adjacency out. */
struct bgp_adj_out {
/* RB Tree of adjacency entries */
@@ -110,9 +107,9 @@ struct bgp_adj_in {
/* BGP advertisement list. */
struct bgp_synchronize {
- struct bgp_advertise_fifo update;
- struct bgp_advertise_fifo withdraw;
- struct bgp_advertise_fifo withdraw_low;
+ struct bgp_adv_fifo_head update;
+ struct bgp_adv_fifo_head withdraw;
+ struct bgp_adv_fifo_head withdraw_low;
};
/* BGP adjacency linked list. */
@@ -138,36 +135,6 @@ struct bgp_synchronize {
#define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in)
#define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in)
-#define BGP_ADV_FIFO_ADD(F, N) \
- do { \
- FIFO_ADD((F), (N)); \
- (F)->count++; \
- } while (0)
-
-#define BGP_ADV_FIFO_DEL(F, N) \
- do { \
- FIFO_DEL((N)); \
- (F)->count--; \
- } while (0)
-
-#define BGP_ADV_FIFO_INIT(F) \
- do { \
- FIFO_INIT((F)); \
- (F)->count = 0; \
- } while (0)
-
-#define BGP_ADV_FIFO_COUNT(F) (F)->count
-
-#define BGP_ADV_FIFO_EMPTY(F) \
- (((struct bgp_advertise_fifo *)(F))->next \
- == (struct bgp_advertise *)(F))
-
-#define BGP_ADV_FIFO_HEAD(F) \
- ((((struct bgp_advertise_fifo *)(F))->next \
- == (struct bgp_advertise *)(F)) \
- ? NULL \
- : (F)->next)
-
/* Prototypes. */
extern int bgp_adj_out_lookup(struct peer *, struct bgp_node *, uint32_t);
extern void bgp_adj_in_set(struct bgp_node *, struct peer *, struct attr *,
@@ -177,7 +144,7 @@ extern void bgp_adj_in_remove(struct bgp_node *, struct bgp_adj_in *);
extern void bgp_sync_init(struct peer *);
extern void bgp_sync_delete(struct peer *);
-extern unsigned int baa_hash_key(void *p);
+extern unsigned int baa_hash_key(const void *p);
extern bool baa_hash_cmp(const void *p1, const void *p2);
extern void bgp_advertise_add(struct bgp_advertise_attr *baa,
struct bgp_advertise *adv);
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 92c37fabd2..05577cb8bd 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -2008,13 +2008,13 @@ struct aspath *aspath_str2aspath(const char *str)
}
/* Make hash value by raw aspath data. */
-unsigned int aspath_key_make(void *p)
+unsigned int aspath_key_make(const void *p)
{
- struct aspath *aspath = (struct aspath *)p;
+ const struct aspath *aspath = p;
unsigned int key = 0;
if (!aspath->str)
- aspath_str_update(aspath, false);
+ aspath_str_update((struct aspath *)aspath, false);
key = jhash(aspath->str, aspath->str_len, 2334325);
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index be5725c1ae..6f3d94cdb3 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -102,7 +102,7 @@ extern const char *aspath_print(struct aspath *);
extern void aspath_print_vty(struct vty *, const char *, struct aspath *,
const char *);
extern void aspath_print_all_vty(struct vty *);
-extern unsigned int aspath_key_make(void *);
+extern unsigned int aspath_key_make(const void *);
extern unsigned int aspath_get_first_as(struct aspath *);
extern unsigned int aspath_get_last_as(struct aspath *);
extern int aspath_loop_check(struct aspath *, as_t);
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 167ad89a59..5a4105b400 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -51,7 +51,6 @@
#include "bgp_encap_types.h"
#include "bgp_vnc_types.h"
#endif
-#include "bgp_encap_types.h"
#include "bgp_evpn.h"
#include "bgp_flowspec_private.h"
#include "bgp_mac.h"
@@ -140,7 +139,7 @@ int cluster_loop_check(struct cluster_list *cluster, struct in_addr originator)
return 0;
}
-static unsigned int cluster_hash_key_make(void *p)
+static unsigned int cluster_hash_key_make(const void *p)
{
const struct cluster_list *cluster = p;
@@ -348,7 +347,7 @@ static void encap_unintern(struct bgp_attr_encap_subtlv **encapp,
}
}
-static unsigned int encap_hash_key_make(void *p)
+static unsigned int encap_hash_key_make(const void *p)
{
const struct bgp_attr_encap_subtlv *encap = p;
@@ -433,7 +432,7 @@ void transit_unintern(struct transit *transit)
}
}
-static unsigned int transit_hash_key_make(void *p)
+static unsigned int transit_hash_key_make(const void *p)
{
const struct transit *transit = p;
@@ -484,7 +483,7 @@ unsigned long int attr_unknown_count(void)
return transit_hash->count;
}
-unsigned int attrhash_key_make(void *p)
+unsigned int attrhash_key_make(const void *p)
{
const struct attr *attr = (struct attr *)p;
uint32_t key = 0;
@@ -1256,6 +1255,32 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
return BGP_ATTR_PARSE_PROCEED;
}
+/*
+ * Check that the nexthop attribute is valid.
+ */
+bgp_attr_parse_ret_t
+bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr)
+{
+ in_addr_t nexthop_h;
+
+ nexthop_h = ntohl(attr->nexthop.s_addr);
+ if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h)
+ || IPV4_CLASS_DE(nexthop_h))
+ && !BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) {
+ char buf[INET_ADDRSTRLEN];
+
+ inet_ntop(AF_INET, &attr->nexthop.s_addr, buf,
+ INET_ADDRSTRLEN);
+ flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s",
+ buf);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP);
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/* Nexthop attribute. */
static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
{
@@ -1263,8 +1288,6 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
- in_addr_t nexthop_h, nexthop_n;
-
/* Check nexthop attribute length. */
if (length != 4) {
flog_err(EC_BGP_ATTR_LEN,
@@ -1274,30 +1297,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
args->total);
}
- /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP
- attribute must result in a NOTIFICATION message (this is implemented
- below).
- At the same time, semantically incorrect NEXT_HOP is more likely to
- be just
- logged locally (this is implemented somewhere else). The UPDATE
- message
- gets ignored in any of these cases. */
- nexthop_n = stream_get_ipv4(peer->curr);
- nexthop_h = ntohl(nexthop_n);
- if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h)
- || IPV4_CLASS_DE(nexthop_h))
- && !BGP_DEBUG(
- allow_martians,
- ALLOW_MARTIANS)) /* loopbacks may be used in testing */
- {
- char buf[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN);
- flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s", buf);
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, args->total);
- }
-
- attr->nexthop.s_addr = nexthop_n;
+ attr->nexthop.s_addr = stream_get_ipv4(peer->curr);
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
return BGP_ATTR_PARSE_PROCEED;
@@ -1956,6 +1956,10 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
}
+ /* Get the tunnel type from encap extended community */
+ bgp_attr_extcom_tunnel_type(attr,
+ (bgp_encap_types *)&attr->encap_tunneltype);
+
return BGP_ATTR_PARSE_PROCEED;
}
@@ -2681,6 +2685,26 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
return BGP_ATTR_PARSE_ERROR;
}
+ /*
+ * RFC4271: If the NEXT_HOP attribute field is syntactically incorrect,
+ * then the Error Subcode MUST be set to Invalid NEXT_HOP Attribute.
+ * This is implemented below and will result in a NOTIFICATION. If the
+ * NEXT_HOP attribute is semantically incorrect, the error SHOULD be
+ * logged, and the route SHOULD be ignored. In this case, a NOTIFICATION
+ * message SHOULD NOT be sent. This is implemented elsewhere.
+ *
+ * RFC4760: An UPDATE message that carries no NLRI, other than the one
+ * encoded in the MP_REACH_NLRI attribute, SHOULD NOT carry the NEXT_HOP
+ * attribute. If such a message contains the NEXT_HOP attribute, the BGP
+ * speaker that receives the message SHOULD ignore this attribute.
+ */
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))
+ && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) {
+ if (bgp_attr_nexthop_valid(peer, attr) < 0) {
+ return BGP_ATTR_PARSE_ERROR;
+ }
+ }
+
/* Check all mandatory well-known attributes are present */
if ((ret = bgp_attr_check(peer, attr)) < 0) {
if (as4_path)
@@ -2755,6 +2779,38 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
return BGP_ATTR_PARSE_PROCEED;
}
+/*
+ * Extract the tunnel type from extended community
+ */
+void bgp_attr_extcom_tunnel_type(struct attr *attr,
+ bgp_encap_types *tunnel_type)
+{
+ struct ecommunity *ecom;
+ int i;
+ if (!attr)
+ return;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return;
+
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = pnt[0];
+ sub_type = pnt[1];
+ if (!(type == ECOMMUNITY_ENCODE_OPAQUE &&
+ sub_type == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP))
+ continue;
+ *tunnel_type = ((pnt[6] << 8) | pnt[7]);
+ return;
+ }
+
+ return;
+}
+
size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
safi_t safi, struct bpacket_attr_vec_arr *vecarr,
struct attr *attr)
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 6d5c647b21..1592a8df4e 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -23,6 +23,7 @@
#include "mpls.h"
#include "bgp_attr_evpn.h"
+#include "bgpd/bgp_encap_types.h"
/* Simple bit mapping. */
#define BITMAP_NBBY 8
@@ -282,7 +283,7 @@ extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *,
extern void bgp_dump_routes_attr(struct stream *, struct attr *,
struct prefix *);
extern bool attrhash_cmp(const void *arg1, const void *arg2);
-extern unsigned int attrhash_key_make(void *);
+extern unsigned int attrhash_key_make(const void *);
extern void attr_show_all(struct vty *);
extern unsigned long int attr_count(void);
extern unsigned long int attr_unknown_count(void);
@@ -317,6 +318,9 @@ encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
extern void bgp_attr_flush_encap(struct attr *attr);
+extern void bgp_attr_extcom_tunnel_type(struct attr *attr,
+ bgp_encap_types *tunnel_type);
+
/**
* Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
* Typical call sequence is to call _start(), followed by multiple _prefix(),
@@ -344,6 +348,9 @@ extern void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p,
uint32_t, int, uint32_t, struct attr *);
extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
+extern bgp_attr_parse_ret_t bgp_attr_nexthop_valid(struct peer *peer,
+ struct attr *attr);
+
static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
uint32_t in_rmap_flags)
{
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index dadf124eec..57fef8e913 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -96,13 +96,12 @@ int bgp_bfd_is_peer_multihop(struct peer *peer)
static void bgp_bfd_peer_sendmsg(struct peer *peer, int command)
{
struct bfd_info *bfd_info;
- vrf_id_t vrf_id = VRF_DEFAULT;
- int multihop;
+ int multihop, cbit = 0;
+ vrf_id_t vrf_id;
bfd_info = (struct bfd_info *)peer->bfd_info;
- if (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
- vrf_id = peer->bgp->vrf_id;
+ vrf_id = peer->bgp->vrf_id;
if (command == ZEBRA_BFD_DEST_DEREGISTER) {
multihop =
@@ -113,20 +112,30 @@ static void bgp_bfd_peer_sendmsg(struct peer *peer, int command)
if ((command == ZEBRA_BFD_DEST_REGISTER) && multihop)
SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP);
}
+ /* while graceful restart with fwd path preserved
+ * and bfd controlplane check not configured is not kept
+ * keep bfd independent controlplane bit set to 1
+ */
+ if (!bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART)
+ && !bgp_flag_check(peer->bgp, BGP_FLAG_GR_PRESERVE_FWD)
+ && !CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE))
+ SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
+
+ cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
if (peer->su.sa.sa_family == AF_INET)
bfd_peer_sendmsg(
zclient, bfd_info, AF_INET, &peer->su.sin.sin_addr,
(peer->su_local) ? &peer->su_local->sin.sin_addr : NULL,
(peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL,
- peer->ttl, multihop, command, 1, vrf_id);
+ peer->ttl, multihop, cbit, command, 1, vrf_id);
else if (peer->su.sa.sa_family == AF_INET6)
bfd_peer_sendmsg(
zclient, bfd_info, AF_INET6, &peer->su.sin6.sin6_addr,
(peer->su_local) ? &peer->su_local->sin6.sin6_addr
: NULL,
(peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL,
- peer->ttl, multihop, command, 1, vrf_id);
+ peer->ttl, multihop, cbit, command, 1, vrf_id);
}
/*
@@ -234,8 +243,7 @@ static void bgp_bfd_update_type(struct peer *peer)
* bgp_bfd_dest_replay - Replay all the peers that have BFD enabled
* to zebra
*/
-static int bgp_bfd_dest_replay(int command, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS)
{
struct listnode *mnode, *node, *nnode;
struct bgp *bgp;
@@ -245,7 +253,7 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client,
zlog_debug("Zebra: BFD Dest replay request");
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
/* Replay the peer, if BFD is enabled in BGP */
@@ -262,7 +270,8 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client,
* down the peer if the BFD session went down from
* * up.
*/
-static void bgp_bfd_peer_status_update(struct peer *peer, int status)
+static void bgp_bfd_peer_status_update(struct peer *peer, int status,
+ int remote_cbit)
{
struct bfd_info *bfd_info;
int old_status;
@@ -276,7 +285,20 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status)
bfd_info->status = status;
bfd_info->last_update = bgp_clock();
+ if (status != old_status) {
+ if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
+ zlog_debug("[%s]: BFD %s", peer->host,
+ bfd_get_status_str(status));
+ }
if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) {
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) &&
+ CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) &&
+ !remote_cbit) {
+ zlog_info("%s BFD DOWN message ignored in the process"
+ " of graceful restart when C bit is cleared",
+ peer->host);
+ return;
+ }
peer->last_reset = PEER_DOWN_BFD_DOWN;
BGP_EVENT_ADD(peer, BGP_Stop);
}
@@ -294,30 +316,33 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status)
* has changed and bring down the peer
* connectivity if the BFD session went down.
*/
-static int bgp_bfd_dest_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct prefix dp;
struct prefix sp;
int status;
+ int remote_cbit;
- ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id);
+ ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status,
+ &remote_cbit, vrf_id);
if (BGP_DEBUG(zebra, ZEBRA)) {
char buf[2][PREFIX2STR_BUFFER];
prefix2str(&dp, buf[0], sizeof(buf[0]));
if (ifp) {
zlog_debug(
- "Zebra: vrf %u interface %s bfd destination %s %s",
+ "Zebra: vrf %u interface %s bfd destination %s %s %s",
vrf_id, ifp->name, buf[0],
- bfd_get_status_str(status));
+ bfd_get_status_str(status),
+ remote_cbit ? "(cbit on)" : "");
} else {
prefix2str(&sp, buf[1], sizeof(buf[1]));
zlog_debug(
- "Zebra: vrf %u source %s bfd destination %s %s",
+ "Zebra: vrf %u source %s bfd destination %s %s %s",
vrf_id, buf[1], buf[0],
- bfd_get_status_str(status));
+ bfd_get_status_str(status),
+ remote_cbit ? "(cbit on)" : "");
}
}
@@ -349,7 +374,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient,
if (ifp && (ifp == peer->nexthop.ifp)) {
bgp_bfd_peer_status_update(peer,
- status);
+ status,
+ remote_cbit);
} else {
if (!peer->su_local)
continue;
@@ -379,7 +405,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient,
continue;
bgp_bfd_peer_status_update(peer,
- status);
+ status,
+ remote_cbit);
}
}
}
@@ -532,6 +559,9 @@ void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr)
if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)
&& (bfd_info->type == BFD_TYPE_NOT_CONFIGURED))
vty_out(vty, " neighbor %s bfd\n", addr);
+
+ if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE))
+ vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", addr);
}
/*
@@ -642,6 +672,73 @@ DEFUN_HIDDEN (neighbor_bfd_type,
return CMD_SUCCESS;
}
+static int bgp_bfd_set_check_controlplane_failure_peer(struct vty *vty, struct peer *peer,
+ const char *no)
+{
+ struct bfd_info *bfd_info;
+
+ if (!peer->bfd_info) {
+ if (no)
+ return CMD_SUCCESS;
+ vty_out(vty, "%% Specify bfd command first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ bfd_info = (struct bfd_info *)peer->bfd_info;
+ if (!no) {
+ if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) {
+ SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE);
+ bgp_bfd_update_peer(peer);
+ }
+ } else {
+ if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) {
+ UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE);
+ bgp_bfd_update_peer(peer);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (neighbor_bfd_check_controlplane_failure,
+ neighbor_bfd_check_controlplane_failure_cmd,
+ "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BFD support\n"
+ "Link dataplane status with BGP controlplane\n")
+{
+ const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
+ int idx_peer = 0;
+ struct peer *peer;
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int ret = CMD_SUCCESS;
+
+ if (no)
+ idx_peer = 2;
+ else
+ idx_peer = 1;
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer) {
+ vty_out(vty, "%% Specify remote-as or peer-group commands first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!peer->bfd_info) {
+ if (no)
+ return CMD_SUCCESS;
+ vty_out(vty, "%% Specify bfd command first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer))
+ ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no);
+ } else
+ ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no);
+ return ret;
+ }
+
DEFUN (no_neighbor_bfd,
no_neighbor_bfd_cmd,
#if HAVE_BFDD > 0
@@ -716,6 +813,7 @@ void bgp_bfd_init(void)
install_element(BGP_NODE, &neighbor_bfd_cmd);
install_element(BGP_NODE, &neighbor_bfd_param_cmd);
install_element(BGP_NODE, &neighbor_bfd_type_cmd);
+ install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd);
install_element(BGP_NODE, &no_neighbor_bfd_cmd);
install_element(BGP_NODE, &no_neighbor_bfd_type_cmd);
}
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 7b64f349d2..b9a5784799 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -36,9 +36,9 @@
#include "bgpd/bgp_regex.h"
#include "bgpd/bgp_clist.h"
-static uint32_t bgp_clist_hash_key_community_list(void *data)
+static uint32_t bgp_clist_hash_key_community_list(const void *data)
{
- struct community_list *cl = data;
+ struct community_list *cl = (struct community_list *) data;
if (cl->name_hash)
return cl->name_hash;
@@ -1049,8 +1049,10 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
/* Do not put duplicated community entry. */
if (community_list_dup_check(list, entry))
community_entry_free(entry);
- else
+ else {
community_list_entry_add(list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
+ }
return 0;
}
@@ -1075,6 +1077,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
/* Delete all of entry belongs to this community-list. */
if (!str) {
community_list_delete(cm, list);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
return 0;
}
@@ -1100,6 +1103,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
community_list_entry_delete(cm, list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
return 0;
}
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index 67cd2be214..82762072df 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -574,7 +574,7 @@ char *community_str(struct community *com, bool make_json)
/* Make hash value of community attribute. This function is used by
hash package.*/
-unsigned int community_hash_make(struct community *com)
+unsigned int community_hash_make(const struct community *com)
{
uint32_t *pnt = (uint32_t *)com->val;
@@ -897,7 +897,7 @@ struct hash *community_hash(void)
void community_init(void)
{
comhash =
- hash_create((unsigned int (*)(void *))community_hash_make,
+ hash_create((unsigned int (*)(const void *))community_hash_make,
(bool (*)(const void *, const void *))community_cmp,
"BGP Community Hash");
}
@@ -957,7 +957,7 @@ void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
*/
if (aggregate->community_hash == NULL)
aggregate->community_hash = hash_create(
- (unsigned int (*)(void *))community_hash_make,
+ (unsigned int (*)(const void *))community_hash_make,
(bool (*)(const void *, const void *))community_cmp,
"BGP Aggregator community hash");
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index 4ff4d214a5..f761a8f5e0 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -75,7 +75,7 @@ extern struct community *community_parse(uint32_t *, unsigned short);
extern struct community *community_intern(struct community *);
extern void community_unintern(struct community **);
extern char *community_str(struct community *, bool make_json);
-extern unsigned int community_hash_make(struct community *);
+extern unsigned int community_hash_make(const struct community *);
extern struct community *community_str2com(const char *);
extern int community_match(const struct community *, const struct community *);
extern bool community_cmp(const struct community *c1,
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 8ef398952d..76bd0e815e 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -241,7 +241,7 @@ void ecommunity_unintern(struct ecommunity **ecom)
}
/* Utinity function to make hash key. */
-unsigned int ecommunity_hash_make(void *arg)
+unsigned int ecommunity_hash_make(const void *arg)
{
const struct ecommunity *ecom = arg;
int size = ecom->size * ECOMMUNITY_SIZE;
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 62b2137753..79be4ee422 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -22,6 +22,7 @@
#define _QUAGGA_BGP_ECOMMUNITY_H
#include "bgpd/bgp_route.h"
+#include "bgpd/bgpd.h"
/* High-order octet of the Extended Communities type field. */
#define ECOMMUNITY_ENCODE_AS 0x00
@@ -162,7 +163,7 @@ extern struct ecommunity *ecommunity_uniq_sort(struct ecommunity *);
extern struct ecommunity *ecommunity_intern(struct ecommunity *);
extern bool ecommunity_cmp(const void *arg1, const void *arg2);
extern void ecommunity_unintern(struct ecommunity **);
-extern unsigned int ecommunity_hash_make(void *);
+extern unsigned int ecommunity_hash_make(const void *);
extern struct ecommunity *ecommunity_str2com(const char *, int, int);
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
extern void ecommunity_strfree(char **s);
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c
index 753ee6baf1..6e181697d6 100644
--- a/bgpd/bgp_errors.c
+++ b/bgpd/bgp_errors.c
@@ -122,12 +122,6 @@ static struct log_ref ferr_bgp_warn[] = {
.suggestion = "Please collect log files and open Issue",
},
{
- .code = EC_BGP_NO_SOCKOPT_MARK,
- .title = "Unable to set socket MARK option",
- .description = "BGP attempted to set the SO_MARK option for a socket and was unable to do so",
- .suggestion = "Please collect log files and open Issue",
- },
- {
.code = EC_BGP_EVPN_PMSI_PRESENT,
.title = "BGP Received a EVPN NLRI with PMSI included",
.description = "BGP has received a type-3 NLRI with PMSI information. At this time FRR is not capable of properly handling this NLRI type",
diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h
index 13bd318e27..39d043ff13 100644
--- a/bgpd/bgp_errors.h
+++ b/bgpd/bgp_errors.h
@@ -88,7 +88,6 @@ enum bgp_log_refs {
EC_BGP_UPDATE_PACKET_LONG,
EC_BGP_UNRECOGNIZED_CAPABILITY,
EC_BGP_NO_TCP_MD5,
- EC_BGP_NO_SOCKOPT_MARK,
EC_BGP_EVPN_PMSI_PRESENT,
EC_BGP_EVPN_VPN_VNI,
EC_BGP_EVPN_ESI,
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 52aa923959..112e4b836c 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -83,9 +83,9 @@ static int evpn_vtep_ip_cmp(void *p1, void *p2)
/*
* Make hash key for ESI.
*/
-static unsigned int esi_hash_keymake(void *p)
+static unsigned int esi_hash_keymake(const void *p)
{
- struct evpnes *pes = p;
+ const struct evpnes *pes = p;
const void *pnt = (void *)pes->esi.val;
return jhash(pnt, ESI_BYTES, 0xa5a5a55a);
@@ -111,9 +111,9 @@ static bool esi_cmp(const void *p1, const void *p2)
/*
* Make vni hash key.
*/
-static unsigned int vni_hash_key_make(void *p)
+static unsigned int vni_hash_key_make(const void *p)
{
- struct bgpevpn *vpn = p;
+ const struct bgpevpn *vpn = p;
return (jhash_1word(vpn->vni, 0));
}
@@ -143,10 +143,10 @@ static int vni_list_cmp(void *p1, void *p2)
/*
* Make vrf import route target hash key.
*/
-static unsigned int vrf_import_rt_hash_key_make(void *p)
+static unsigned int vrf_import_rt_hash_key_make(const void *p)
{
- struct vrf_irt_node *irt = p;
- char *pnt = irt->rt.val;
+ const struct vrf_irt_node *irt = p;
+ const char *pnt = irt->rt.val;
return jhash(pnt, 8, 0x5abc1234);
}
@@ -259,10 +259,10 @@ static int is_vrf_present_in_irt_vrfs(struct list *vrfs, struct bgp *bgp_vrf)
/*
* Make import route target hash key.
*/
-static unsigned int import_rt_hash_key_make(void *p)
+static unsigned int import_rt_hash_key_make(const void *p)
{
- struct irt_node *irt = p;
- char *pnt = irt->rt.val;
+ const struct irt_node *irt = p;
+ const char *pnt = irt->rt.val;
return jhash(pnt, 8, 0xdeadbeef);
}
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 4dccc89f52..67b0079c37 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -681,7 +681,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
json_path = json_object_new_array();
if (detail)
- route_vty_out_detail(vty, bgp, &rn->p, pi,
+ route_vty_out_detail(vty, bgp, rn, pi,
AFI_L2VPN, SAFI_EVPN,
json_path);
else
@@ -1010,14 +1010,17 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
struct bgp_path_info *pi;
int rd_header;
int header = 1;
+ char rd_str[BUFSIZ];
+ char buf[BUFSIZ];
unsigned long output_count = 0;
unsigned long total_count = 0;
json_object *json = NULL;
json_object *json_nroute = NULL;
json_object *json_array = NULL;
- json_object *json_scode = NULL;
- json_object *json_ocode = NULL;
+ json_object *json_prefix_info = NULL;
+
+ memset(rd_str, 0, BUFSIZ);
bgp = bgp_get_evpn();
if (bgp == NULL) {
@@ -1028,31 +1031,13 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
return CMD_WARNING;
}
- if (use_json) {
- json_scode = json_object_new_object();
- json_ocode = json_object_new_object();
+ if (use_json)
json = json_object_new_object();
- json_nroute = 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 (rn = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); rn;
rn = bgp_route_next(rn)) {
uint64_t tbl_ver;
- if (use_json)
- continue; /* XXX json TODO */
-
if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0)
continue;
@@ -1063,7 +1048,20 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
rd_header = 1;
tbl_ver = table->version;
- for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm))
+ for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) {
+ if (use_json) {
+ json_array = json_object_new_array();
+ json_prefix_info = json_object_new_object();
+
+ json_object_string_add(json_prefix_info,
+ "prefix", bgp_evpn_route2str(
+ (struct prefix_evpn *)&rm->p, buf,
+ BUFSIZ));
+
+ json_object_int_add(json_prefix_info,
+ "prefixLen", rm->p.prefixlen);
+ }
+
for (pi = bgp_node_get_bgp_path_info(rm); pi;
pi = pi->next) {
total_count++;
@@ -1075,28 +1073,23 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
pi->peer->su_remote, su))
continue;
}
- if (header == 0) {
+ if (header) {
if (use_json) {
- if (option
- == SHOW_DISPLAY_TAGS) {
- json_object_int_add(
- json,
- "bgpTableVersion",
- tbl_ver);
- json_object_string_add(
- json,
- "bgpLocalRouterId",
- inet_ntoa(
- bgp->router_id));
- json_object_object_add(
- json,
- "bgpStatusCodes",
- json_scode);
- json_object_object_add(
- json,
- "bgpOriginCodes",
- json_ocode);
- }
+ json_object_int_add(
+ json, "bgpTableVersion",
+ tbl_ver);
+ json_object_string_add(
+ json,
+ "bgpLocalRouterId",
+ inet_ntoa(
+ bgp->router_id));
+ json_object_int_add(
+ json,
+ "defaultLocPrf",
+ bgp->default_local_pref);
+ json_object_int_add(
+ json, "localAS",
+ bgp->as);
} else {
if (option == SHOW_DISPLAY_TAGS)
vty_out(vty,
@@ -1139,21 +1132,23 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
else if (type == RD_TYPE_IP)
decode_rd_ip(pnt + 2, &rd_ip);
if (use_json) {
- char buffer[BUFSIZ];
+ json_nroute =
+ json_object_new_object();
if (type == RD_TYPE_AS
|| type == RD_TYPE_AS4)
- sprintf(buffer, "%u:%d",
+ sprintf(rd_str, "%u:%d",
rd_as.as,
rd_as.val);
else if (type == RD_TYPE_IP)
- sprintf(buffer, "%s:%d",
+ sprintf(rd_str, "%s:%d",
inet_ntoa(
rd_ip.ip),
rd_ip.val);
json_object_string_add(
json_nroute,
- "routeDistinguisher",
- buffer);
+ "rd",
+ rd_str);
+
} else {
vty_out(vty,
"Route Distinguisher: ");
@@ -1176,10 +1171,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
}
rd_header = 0;
}
- if (use_json)
- json_array = json_object_new_array();
- else
- json_array = NULL;
+
if (option == SHOW_DISPLAY_TAGS)
route_vty_out_tag(vty, &rm->p, pi, 0,
SAFI_EVPN,
@@ -1192,13 +1184,33 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
SAFI_EVPN, json_array);
output_count++;
}
- /* XXX json */
+ if (use_json) {
+ json_object_object_add(json_prefix_info,
+ "paths", json_array);
+ json_object_object_add(json_nroute, buf,
+ json_prefix_info);
+ }
+ }
+
+ if (use_json)
+ json_object_object_add(json, rd_str, json_nroute);
+ }
+
+ if (use_json) {
+ json_object_int_add(json, "numPrefix", output_count);
+ json_object_int_add(json, "totalPrefix", total_count);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ } else {
+ if (output_count == 0)
+ vty_out(vty, "No prefixes displayed, %ld exist\n",
+ total_count);
+ else
+ vty_out(vty,
+ "\nDisplayed %ld out of %ld total prefixes\n",
+ output_count, total_count);
}
- if (output_count == 0)
- vty_out(vty, "No prefixes displayed, %ld exist\n", total_count);
- else
- vty_out(vty, "\nDisplayed %ld out of %ld total prefixes\n",
- output_count, total_count);
return CMD_SUCCESS;
}
@@ -1542,14 +1554,15 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay,
show_ip_bgp_l2vpn_evpn_all_overlay_cmd,
- "show [ip] bgp l2vpn evpn all overlay",
+ "show [ip] bgp l2vpn evpn all overlay [json]",
SHOW_STR
IP_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
"Display information about all EVPN NLRIs\n"
- "Display BGP Overlay Information for prefixes\n")
+ "Display BGP Overlay Information for prefixes\n"
+ JSON_STR)
{
return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
SHOW_DISPLAY_OVERLAY,
@@ -2077,7 +2090,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi,
+ route_vty_out_detail(vty, bgp, rn, pi, afi, safi,
json_path);
if (json)
@@ -2147,7 +2160,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi,
+ route_vty_out_detail(vty, bgp, rn, pi, afi, safi,
json_path);
if (json)
@@ -2254,7 +2267,7 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi,
+ route_vty_out_detail(vty, bgp, rn, pi, afi, safi,
json_path);
if (json)
@@ -2359,7 +2372,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi,
+ route_vty_out_detail(vty, bgp, rn, pi, afi, safi,
json_path);
if (json)
@@ -2509,7 +2522,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
if (detail) {
route_vty_out_detail(
- vty, bgp, &rn->p, pi, AFI_L2VPN,
+ vty, bgp, rn, pi, AFI_L2VPN,
SAFI_EVPN, json_path);
} else
route_vty_out(vty, &rn->p, pi, 0,
@@ -3259,9 +3272,6 @@ DEFPY(bgp_evpn_advertise_svi_ip_vni,
if (!bgp)
return CMD_WARNING;
- if (!vpn)
- return CMD_WARNING;
-
if (no)
evpn_set_advertise_svi_macip(bgp, vpn, 0);
else
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 9e37a60188..12ae1f841a 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -1115,8 +1115,6 @@ int bgp_stop(struct peer *peer)
/* Reset peer synctime */
peer->synctime = 0;
-
- bgp_bfd_deregister_peer(peer);
}
/* stop keepalives */
diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c
index c2f0baff76..bec3bdcb8d 100644
--- a/bgpd/bgp_keepalives.c
+++ b/bgpd/bgp_keepalives.c
@@ -131,9 +131,9 @@ static bool peer_hash_cmp(const void *f, const void *s)
return p1->peer == p2->peer;
}
-static unsigned int peer_hash_key(void *arg)
+static unsigned int peer_hash_key(const void *arg)
{
- struct pkat *pkat = arg;
+ const struct pkat *pkat = arg;
return (uintptr_t)pkat->peer;
}
diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c
index 69dd0f9dac..71c0c8c7c6 100644
--- a/bgpd/bgp_labelpool.c
+++ b/bgpd/bgp_labelpool.c
@@ -25,7 +25,6 @@
#include "stream.h"
#include "mpls.h"
#include "vty.h"
-#include "fifo.h"
#include "linklist.h"
#include "skiplist.h"
#include "workqueue.h"
@@ -50,34 +49,10 @@ static struct labelpool *lp;
#define LP_CHUNK_SIZE 50
DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk")
-DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item")
DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment")
DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback")
-#define LABEL_FIFO_ADD(F, N) \
- do { \
- FIFO_ADD((F), (N)); \
- (F)->count++; \
- } while (0)
-
-#define LABEL_FIFO_DEL(F, N) \
- do { \
- FIFO_DEL((N)); \
- (F)->count--; \
- } while (0)
-
-#define LABEL_FIFO_INIT(F) \
- do { \
- FIFO_INIT((F)); \
- (F)->count = 0; \
- } while (0)
-
-#define LABEL_FIFO_COUNT(F) ((F)->count)
-
-#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F)
-
-#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next)
-
struct lp_chunk {
uint32_t first;
uint32_t last;
@@ -98,15 +73,13 @@ struct lp_lcb {
int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
};
-/* XXX same first elements as "struct fifo" */
struct lp_fifo {
- struct lp_fifo *next;
- struct lp_fifo *prev;
-
- uint32_t count;
+ struct lp_fifo_item fifo;
struct lp_lcb lcb;
};
+DECLARE_LIST(lp_fifo, struct lp_fifo, fifo)
+
struct lp_cbq_item {
int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
int type;
@@ -199,8 +172,7 @@ void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
lp->inuse = skiplist_new(0, NULL, NULL);
lp->chunks = list_new();
lp->chunks->del = lp_chunk_free;
- lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo));
- LABEL_FIFO_INIT(lp->requests);
+ lp_fifo_init(&lp->requests);
lp->callback_q = work_queue_new(master, "label callbacks");
lp->callback_q->spec.workfunc = lp_cbq_docallback;
@@ -223,13 +195,9 @@ void bgp_lp_finish(void)
list_delete(&lp->chunks);
- while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
-
- LABEL_FIFO_DEL(lp->requests, lf);
+ while ((lf = lp_fifo_pop(&lp->requests)))
XFREE(MTYPE_BGP_LABEL_FIFO, lf);
- }
- XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests);
- lp->requests = NULL;
+ lp_fifo_fini(&lp->requests);
work_queue_free_and_null(&lp->callback_q);
@@ -385,9 +353,9 @@ void bgp_lp_get(
sizeof(struct lp_fifo));
lf->lcb = *lcb;
- LABEL_FIFO_ADD(lp->requests, lf);
+ lp_fifo_add_tail(&lp->requests, lf);
- if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) {
+ if (lp_fifo_count(&lp->requests) > lp->pending_count) {
if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) {
lp->pending_count += LP_CHUNK_SIZE;
return;
@@ -441,11 +409,11 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
lp->pending_count -= (last - first + 1);
if (debug) {
- zlog_debug("%s: %u pending requests", __func__,
- LABEL_FIFO_COUNT(lp->requests));
+ zlog_debug("%s: %zu pending requests", __func__,
+ lp_fifo_count(&lp->requests));
}
- while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+ while ((lf = lp_fifo_first(&lp->requests))) {
struct lp_lcb *lcb;
void *labelid = lf->lcb.labelid;
@@ -504,7 +472,7 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
work_queue_add(lp->callback_q, q);
finishedrequest:
- LABEL_FIFO_DEL(lp->requests, lf);
+ lp_fifo_del(&lp->requests, lf);
XFREE(MTYPE_BGP_LABEL_FIFO, lf);
}
}
@@ -533,7 +501,7 @@ void bgp_lp_event_zebra_up(void)
/*
* Get label chunk allocation request dispatched to zebra
*/
- labels_needed = LABEL_FIFO_COUNT(lp->requests) +
+ labels_needed = lp_fifo_count(&lp->requests) +
skiplist_count(lp->inuse);
/* round up */
@@ -588,7 +556,7 @@ void bgp_lp_event_zebra_up(void)
sizeof(struct lp_fifo));
lf->lcb = *lcb;
- LABEL_FIFO_ADD(lp->requests, lf);
+ lp_fifo_add_tail(&lp->requests, lf);
}
skiplist_delete_first(lp->inuse);
diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h
index 0507e65489..eaa3fce20b 100644
--- a/bgpd/bgp_labelpool.h
+++ b/bgpd/bgp_labelpool.h
@@ -31,11 +31,13 @@
#define LP_TYPE_VRF 0x00000001
#define LP_TYPE_BGP_LU 0x00000002
+PREDECL_LIST(lp_fifo)
+
struct labelpool {
struct skiplist *ledger; /* all requests */
struct skiplist *inuse; /* individual labels */
struct list *chunks; /* granted by zebra */
- struct lp_fifo *requests; /* blocked on zebra */
+ struct lp_fifo_head requests; /* blocked on zebra */
struct work_queue *callback_q;
uint32_t pending_count; /* requested from zebra */
};
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
index 44766c9b6e..098374fa9f 100644
--- a/bgpd/bgp_lcommunity.c
+++ b/bgpd/bgp_lcommunity.c
@@ -301,7 +301,7 @@ char *lcommunity_str(struct lcommunity *lcom, bool make_json)
}
/* Utility function to make hash key. */
-unsigned int lcommunity_hash_make(void *arg)
+unsigned int lcommunity_hash_make(const void *arg)
{
const struct lcommunity *lcom = arg;
int size = lcom_length(lcom);
diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h
index aa4e8c69fe..a512395492 100644
--- a/bgpd/bgp_lcommunity.h
+++ b/bgpd/bgp_lcommunity.h
@@ -63,7 +63,7 @@ extern struct lcommunity *lcommunity_uniq_sort(struct lcommunity *);
extern struct lcommunity *lcommunity_intern(struct lcommunity *);
extern bool lcommunity_cmp(const void *arg1, const void *arg2);
extern void lcommunity_unintern(struct lcommunity **);
-extern unsigned int lcommunity_hash_make(void *);
+extern unsigned int lcommunity_hash_make(const void *);
extern struct hash *lcommunity_hash(void);
extern struct lcommunity *lcommunity_str2com(const char *);
extern int lcommunity_match(const struct lcommunity *,
diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c
index 49b5854020..f19453fecb 100644
--- a/bgpd/bgp_mac.c
+++ b/bgpd/bgp_mac.c
@@ -40,9 +40,9 @@ struct bgp_self_mac {
struct list *ifp_list;
};
-static unsigned int bgp_mac_hash_key_make(void *data)
+static unsigned int bgp_mac_hash_key_make(const void *data)
{
- struct bgp_self_mac *bsm = data;
+ const struct bgp_self_mac *bsm = data;
return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead);
}
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index adba73e404..abd8586f4c 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -70,10 +70,6 @@
static const struct option longopts[] = {
{"bgp_port", required_argument, NULL, 'p'},
{"listenon", required_argument, NULL, 'l'},
-#if CONFDATE > 20190521
- CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
- {"retain", no_argument, NULL, 'r'},
{"no_kernel", no_argument, NULL, 'n'},
{"skip_runas", no_argument, NULL, 'S'},
{"ecmp", required_argument, NULL, 'e'},
@@ -367,10 +363,7 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT,
.privs = &bgpd_privs, .yang_modules = bgpd_yang_modules,
.n_yang_modules = array_size(bgpd_yang_modules), )
-#if CONFDATE > 20190521
-CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
-#define DEPRECATED_OPTIONS "r"
+#define DEPRECATED_OPTIONS ""
/* Main routine of bgpd. Treatment of argument and start bgp finite
state machine is handled at here. */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index d7cb84c323..6eddd0e1e3 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -319,9 +319,6 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi)
bgp->name_pretty, bgp->vrf_id);
}
- if (label == BGP_PREVENT_VRF_2_VRF_LEAK)
- label = MPLS_LABEL_NONE;
-
zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP);
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
}
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 6a5c2c4b38..8e18ed7529 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -588,8 +588,6 @@ static int bgp_update_source(struct peer *peer)
return ret;
}
-#define DATAPLANE_MARK 254 /* main table ID */
-
/* BGP try to connect to the peer. */
int bgp_connect(struct peer *peer)
{
@@ -619,10 +617,6 @@ int bgp_connect(struct peer *peer)
sockopt_reuseaddr(peer->fd);
sockopt_reuseport(peer->fd);
- if (sockopt_mark_default(peer->fd, DATAPLANE_MARK, &bgpd_privs) < 0)
- flog_warn(EC_BGP_NO_SOCKOPT_MARK,
- "Unable to set mark on FD for peer %s, err=%s",
- peer->host, safe_strerror(errno));
#ifdef IPTOS_PREC_INTERNETCONTROL
frr_elevate_privs(&bgpd_privs) {
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index de97b73c72..a8c507832c 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -114,7 +114,7 @@ static void bgp_tip_hash_free(void *addr)
XFREE(MTYPE_TIP_ADDR, addr);
}
-static unsigned int bgp_tip_hash_key_make(void *p)
+static unsigned int bgp_tip_hash_key_make(const void *p)
{
const struct tip_addr *addr = p;
@@ -237,7 +237,7 @@ static void bgp_address_hash_free(void *data)
XFREE(MTYPE_BGP_ADDR, addr);
}
-static unsigned int bgp_address_hash_key_make(void *p)
+static unsigned int bgp_address_hash_key_make(const void *p)
{
const struct bgp_addr *addr = p;
@@ -505,6 +505,77 @@ int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer)
return (ret);
}
+int bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer)
+{
+ struct bgp_node *rn1;
+ struct bgp_node *rn2;
+ struct prefix p;
+ int ret;
+
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = nexthop;
+
+ rn1 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p);
+ if (!rn1)
+ return 0;
+
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = peer->su.sin6.sin6_addr;
+
+ rn2 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p);
+ if (!rn2) {
+ bgp_unlock_node(rn1);
+ return 0;
+ }
+
+ ret = (rn1 == rn2) ? 1 : 0;
+
+ bgp_unlock_node(rn1);
+ bgp_unlock_node(rn2);
+
+ return ret;
+}
+
+int bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop,
+ struct update_subgroup *subgrp)
+{
+ struct bgp_node *rn1 = NULL, *rn2 = NULL;
+ struct peer_af *paf = NULL;
+ struct prefix p = {0}, np = {0};
+ struct bgp *bgp = NULL;
+
+ np.family = AF_INET6;
+ np.prefixlen = IPV6_MAX_BITLEN;
+ np.u.prefix6 = nexthop;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV6_MAX_BITLEN;
+
+ bgp = SUBGRP_INST(subgrp);
+ rn1 = bgp_node_match(bgp->connected_table[AFI_IP6], &np);
+ if (!rn1)
+ return 0;
+
+ SUBGRP_FOREACH_PEER (subgrp, paf) {
+
+ p.u.prefix6 = paf->peer->su.sin6.sin6_addr;
+ rn2 = bgp_node_match(bgp->connected_table[AFI_IP6], &p);
+ if (rn1 == rn2) {
+ bgp_unlock_node(rn1);
+ bgp_unlock_node(rn2);
+ return 1;
+ }
+
+ if (rn2)
+ bgp_unlock_node(rn2);
+ }
+
+ bgp_unlock_node(rn1);
+ return 0;
+}
+
int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop,
struct update_subgroup *subgrp)
{
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index f06fae5706..d35f1ad520 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -74,11 +74,19 @@ struct tip_addr {
int refcnt;
};
+struct bgp_addrv6 {
+ struct in6_addr addrv6;
+ struct list *ifp_name_list;
+};
+
extern void bgp_connected_add(struct bgp *bgp, struct connected *c);
extern void bgp_connected_delete(struct bgp *bgp, struct connected *c);
extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop,
struct update_subgroup *subgrp);
-extern int bgp_multiaccess_check_v4(struct in_addr, struct peer *);
+extern int bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop,
+ struct update_subgroup *subgrp);
+extern int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer);
+extern int bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer);
extern int bgp_config_write_scan_time(struct vty *);
extern int bgp_nexthop_self(struct bgp *, struct in_addr);
extern struct bgp_nexthop_cache *bnc_new(void);
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 7e721db49d..fdfa15b445 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -474,8 +474,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
continue;
for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next)
- if (nexthop_same_no_recurse(oldnh, nexthop) &&
- nexthop_labels_match(oldnh, nexthop))
+ if (nexthop_same(oldnh, nexthop))
break;
if (!oldnh)
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 130e06a6cf..b5934fb56e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1533,6 +1533,17 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
nlris[NLRI_UPDATE].nlri = stream_pnt(s);
nlris[NLRI_UPDATE].length = update_len;
stream_forward_getp(s, update_len);
+
+ if (CHECK_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) {
+ /*
+ * We skipped nexthop attribute validation earlier so
+ * validate the nexthop now.
+ */
+ if (bgp_attr_nexthop_valid(peer, &attr) < 0) {
+ bgp_attr_unintern_sub(&attr);
+ return BGP_Stop;
+ }
+ }
}
if (BGP_DEBUG(update, UPDATE_IN))
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
index 0fddfa75a1..5eef6ac6cc 100644
--- a/bgpd/bgp_pbr.c
+++ b/bgpd/bgp_pbr.c
@@ -964,9 +964,9 @@ static void *bgp_pbr_match_entry_alloc_intern(void *arg)
return new;
}
-uint32_t bgp_pbr_match_hash_key(void *arg)
+uint32_t bgp_pbr_match_hash_key(const void *arg)
{
- struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
+ const struct bgp_pbr_match *pbm = arg;
uint32_t key;
key = jhash_1word(pbm->vrf_id, 0x4312abde);
@@ -1019,9 +1019,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
return true;
}
-uint32_t bgp_pbr_rule_hash_key(void *arg)
+uint32_t bgp_pbr_rule_hash_key(const void *arg)
{
- struct bgp_pbr_rule *pbr = (struct bgp_pbr_rule *)arg;
+ const struct bgp_pbr_rule *pbr = arg;
uint32_t key;
key = prefix_hash_key(&pbr->src);
@@ -1057,12 +1057,12 @@ bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2)
return true;
}
-uint32_t bgp_pbr_match_entry_hash_key(void *arg)
+uint32_t bgp_pbr_match_entry_hash_key(const void *arg)
{
- struct bgp_pbr_match_entry *pbme;
+ const struct bgp_pbr_match_entry *pbme;
uint32_t key;
- pbme = (struct bgp_pbr_match_entry *)arg;
+ pbme = arg;
key = prefix_hash_key(&pbme->src);
key = jhash_1word(prefix_hash_key(&pbme->dst), key);
key = jhash(&pbme->dst_port_min, 2, key);
@@ -1111,12 +1111,12 @@ bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
return true;
}
-uint32_t bgp_pbr_action_hash_key(void *arg)
+uint32_t bgp_pbr_action_hash_key(const void *arg)
{
- struct bgp_pbr_action *pbra;
+ const struct bgp_pbr_action *pbra;
uint32_t key;
- pbra = (struct bgp_pbr_action *)arg;
+ pbra = arg;
key = jhash_1word(pbra->table_id, 0x4312abde);
key = jhash_1word(pbra->fwmark, key);
return key;
diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h
index f7fddac7fb..b368d8892d 100644
--- a/bgpd/bgp_pbr.h
+++ b/bgpd/bgp_pbr.h
@@ -273,16 +273,16 @@ extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
extern void bgp_pbr_cleanup(struct bgp *bgp);
extern void bgp_pbr_init(struct bgp *bgp);
-extern uint32_t bgp_pbr_rule_hash_key(void *arg);
+extern uint32_t bgp_pbr_rule_hash_key(const void *arg);
extern bool bgp_pbr_rule_hash_equal(const void *arg1,
const void *arg2);
-extern uint32_t bgp_pbr_action_hash_key(void *arg);
+extern uint32_t bgp_pbr_action_hash_key(const void *arg);
extern bool bgp_pbr_action_hash_equal(const void *arg1,
const void *arg2);
-extern uint32_t bgp_pbr_match_entry_hash_key(void *arg);
+extern uint32_t bgp_pbr_match_entry_hash_key(const void *arg);
extern bool bgp_pbr_match_entry_hash_equal(const void *arg1,
const void *arg2);
-extern uint32_t bgp_pbr_match_hash_key(void *arg);
+extern uint32_t bgp_pbr_match_hash_key(const void *arg);
extern bool bgp_pbr_match_hash_equal(const void *arg1,
const void *arg2);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 063cc24dc1..31243c899d 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -434,7 +434,8 @@ void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf)
static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
struct bgp_path_info *exist, int *paths_eq,
struct bgp_maxpaths_cfg *mpath_cfg, int debug,
- char *pfx_buf, afi_t afi, safi_t safi)
+ char *pfx_buf, afi_t afi, safi_t safi,
+ enum bgp_path_selection_reason *reason)
{
struct attr *newattr, *existattr;
bgp_peer_sort_t new_sort;
@@ -463,6 +464,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
/* 0. Null check. */
if (new == NULL) {
+ *reason = bgp_path_selection_none;
if (debug)
zlog_debug("%s: new is NULL", pfx_buf);
return 0;
@@ -472,6 +474,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
bgp_path_info_path_with_addpath_rx_str(new, new_buf);
if (exist == NULL) {
+ *reason = bgp_path_selection_first;
if (debug)
zlog_debug("%s: %s is the initial bestpath", pfx_buf,
new_buf);
@@ -514,6 +517,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (newattr->sticky && !existattr->sticky) {
+ *reason = bgp_path_selection_evpn_sticky_mac;
if (debug)
zlog_debug(
"%s: %s wins over %s due to sticky MAC flag",
@@ -522,6 +526,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (!newattr->sticky && existattr->sticky) {
+ *reason = bgp_path_selection_evpn_sticky_mac;
if (debug)
zlog_debug(
"%s: %s loses to %s due to sticky MAC flag",
@@ -534,6 +539,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_mm_seq = mac_mobility_seqnum(existattr);
if (new_mm_seq > exist_mm_seq) {
+ *reason = bgp_path_selection_evpn_seq;
if (debug)
zlog_debug(
"%s: %s wins over %s due to MM seq %u > %u",
@@ -543,6 +549,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (new_mm_seq < exist_mm_seq) {
+ *reason = bgp_path_selection_evpn_seq;
if (debug)
zlog_debug(
"%s: %s loses to %s due to MM seq %u < %u",
@@ -557,6 +564,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
*/
nh_cmp = bgp_path_info_nexthop_cmp(new, exist);
if (nh_cmp < 0) {
+ *reason = bgp_path_selection_evpn_lower_ip;
if (debug)
zlog_debug(
"%s: %s wins over %s due to same MM seq %u and lower IP %s",
@@ -565,6 +573,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
return 1;
}
if (nh_cmp > 0) {
+ *reason = bgp_path_selection_evpn_lower_ip;
if (debug)
zlog_debug(
"%s: %s loses to %s due to same MM seq %u and higher IP %s",
@@ -579,6 +588,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_weight = existattr->weight;
if (new_weight > exist_weight) {
+ *reason = bgp_path_selection_weight;
if (debug)
zlog_debug("%s: %s wins over %s due to weight %d > %d",
pfx_buf, new_buf, exist_buf, new_weight,
@@ -587,6 +597,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (new_weight < exist_weight) {
+ *reason = bgp_path_selection_weight;
if (debug)
zlog_debug("%s: %s loses to %s due to weight %d < %d",
pfx_buf, new_buf, exist_buf, new_weight,
@@ -603,6 +614,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_pref = existattr->local_pref;
if (new_pref > exist_pref) {
+ *reason = bgp_path_selection_local_pref;
if (debug)
zlog_debug(
"%s: %s wins over %s due to localpref %d > %d",
@@ -612,6 +624,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (new_pref < exist_pref) {
+ *reason = bgp_path_selection_local_pref;
if (debug)
zlog_debug(
"%s: %s loses to %s due to localpref %d < %d",
@@ -627,6 +640,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
*/
if (!(new->sub_type == BGP_ROUTE_NORMAL ||
new->sub_type == BGP_ROUTE_IMPORTED)) {
+ *reason = bgp_path_selection_local_route;
if (debug)
zlog_debug(
"%s: %s wins over %s due to preferred BGP_ROUTE type",
@@ -636,6 +650,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (!(exist->sub_type == BGP_ROUTE_NORMAL ||
exist->sub_type == BGP_ROUTE_IMPORTED)) {
+ *reason = bgp_path_selection_local_route;
if (debug)
zlog_debug(
"%s: %s loses to %s due to preferred BGP_ROUTE type",
@@ -655,6 +670,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
aspath_hops += aspath_count_confeds(newattr->aspath);
if (aspath_hops < (exist_hops + exist_confeds)) {
+ *reason = bgp_path_selection_confed_as_path;
if (debug)
zlog_debug(
"%s: %s wins over %s due to aspath (with confeds) hopcount %d < %d",
@@ -665,6 +681,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (aspath_hops > (exist_hops + exist_confeds)) {
+ *reason = bgp_path_selection_confed_as_path;
if (debug)
zlog_debug(
"%s: %s loses to %s due to aspath (with confeds) hopcount %d > %d",
@@ -677,6 +694,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
int newhops = aspath_count_hops(newattr->aspath);
if (newhops < exist_hops) {
+ *reason = bgp_path_selection_as_path;
if (debug)
zlog_debug(
"%s: %s wins over %s due to aspath hopcount %d < %d",
@@ -686,6 +704,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (newhops > exist_hops) {
+ *reason = bgp_path_selection_as_path;
if (debug)
zlog_debug(
"%s: %s loses to %s due to aspath hopcount %d > %d",
@@ -698,6 +717,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
/* 5. Origin check. */
if (newattr->origin < existattr->origin) {
+ *reason = bgp_path_selection_origin;
if (debug)
zlog_debug("%s: %s wins over %s due to ORIGIN %s < %s",
pfx_buf, new_buf, exist_buf,
@@ -707,6 +727,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (newattr->origin > existattr->origin) {
+ *reason = bgp_path_selection_origin;
if (debug)
zlog_debug("%s: %s loses to %s due to ORIGIN %s > %s",
pfx_buf, new_buf, exist_buf,
@@ -732,6 +753,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_med = bgp_med_value(exist->attr, bgp);
if (new_med < exist_med) {
+ *reason = bgp_path_selection_med;
if (debug)
zlog_debug(
"%s: %s wins over %s due to MED %d < %d",
@@ -741,6 +763,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (new_med > exist_med) {
+ *reason = bgp_path_selection_med;
if (debug)
zlog_debug(
"%s: %s loses to %s due to MED %d > %d",
@@ -756,6 +779,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (new_sort == BGP_PEER_EBGP
&& (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) {
+ *reason = bgp_path_selection_peer;
if (debug)
zlog_debug(
"%s: %s wins over %s due to eBGP peer > iBGP peer",
@@ -765,6 +789,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (exist_sort == BGP_PEER_EBGP
&& (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) {
+ *reason = bgp_path_selection_peer;
if (debug)
zlog_debug(
"%s: %s loses to %s due to iBGP peer < eBGP peer",
@@ -834,6 +859,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
if (new_sort == BGP_PEER_CONFED
&& exist_sort == BGP_PEER_IBGP) {
+ *reason = bgp_path_selection_confed;
if (debug)
zlog_debug(
"%s: %s wins over %s due to confed-external peer > confed-internal peer",
@@ -843,6 +869,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (exist_sort == BGP_PEER_CONFED
&& new_sort == BGP_PEER_IBGP) {
+ *reason = bgp_path_selection_confed;
if (debug)
zlog_debug(
"%s: %s loses to %s due to confed-internal peer < confed-external peer",
@@ -918,6 +945,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
"%s: %s loses to %s after IGP metric comparison",
pfx_buf, new_buf, exist_buf);
}
+ *reason = bgp_path_selection_igp_metric;
return ret;
}
@@ -928,6 +956,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (!bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)
&& new_sort == BGP_PEER_EBGP && exist_sort == BGP_PEER_EBGP) {
if (CHECK_FLAG(new->flags, BGP_PATH_SELECTED)) {
+ *reason = bgp_path_selection_older;
if (debug)
zlog_debug(
"%s: %s wins over %s due to oldest external",
@@ -936,6 +965,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (CHECK_FLAG(exist->flags, BGP_PATH_SELECTED)) {
+ *reason = bgp_path_selection_older;
if (debug)
zlog_debug(
"%s: %s loses to %s due to oldest external",
@@ -959,6 +989,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_id.s_addr = exist->peer->remote_id.s_addr;
if (ntohl(new_id.s_addr) < ntohl(exist_id.s_addr)) {
+ *reason = bgp_path_selection_router_id;
if (debug)
zlog_debug(
"%s: %s wins over %s due to Router-ID comparison",
@@ -967,6 +998,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (ntohl(new_id.s_addr) > ntohl(exist_id.s_addr)) {
+ *reason = bgp_path_selection_router_id;
if (debug)
zlog_debug(
"%s: %s loses to %s due to Router-ID comparison",
@@ -979,6 +1011,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
exist_cluster = BGP_CLUSTER_LIST_LENGTH(exist->attr);
if (new_cluster < exist_cluster) {
+ *reason = bgp_path_selection_cluster_length;
if (debug)
zlog_debug(
"%s: %s wins over %s due to CLUSTER_LIST length %d < %d",
@@ -988,6 +1021,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (new_cluster > exist_cluster) {
+ *reason = bgp_path_selection_cluster_length;
if (debug)
zlog_debug(
"%s: %s loses to %s due to CLUSTER_LIST length %d > %d",
@@ -1001,6 +1035,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
* valid peer information (as the connection may or may not be up).
*/
if (CHECK_FLAG(exist->flags, BGP_PATH_STALE)) {
+ *reason = bgp_path_selection_stale;
if (debug)
zlog_debug(
"%s: %s wins over %s due to latter path being STALE",
@@ -1009,6 +1044,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (CHECK_FLAG(new->flags, BGP_PATH_STALE)) {
+ *reason = bgp_path_selection_stale;
if (debug)
zlog_debug(
"%s: %s loses to %s due to former path being STALE",
@@ -1017,14 +1053,19 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
/* locally configured routes to advertise do not have su_remote */
- if (new->peer->su_remote == NULL)
+ if (new->peer->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
return 0;
- if (exist->peer->su_remote == NULL)
+ }
+ if (exist->peer->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
return 1;
+ }
ret = sockunion_cmp(new->peer->su_remote, exist->peer->su_remote);
if (ret == 1) {
+ *reason = bgp_path_selection_neighbor_ip;
if (debug)
zlog_debug(
"%s: %s loses to %s due to Neighor IP comparison",
@@ -1033,6 +1074,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (ret == -1) {
+ *reason = bgp_path_selection_neighbor_ip;
if (debug)
zlog_debug(
"%s: %s wins over %s due to Neighor IP comparison",
@@ -1040,6 +1082,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
return 1;
}
+ *reason = bgp_path_selection_default;
if (debug)
zlog_debug("%s: %s wins over %s due to nothing left to compare",
pfx_buf, new_buf, exist_buf);
@@ -1053,12 +1096,13 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
* This version is compatible with */
int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new,
struct bgp_path_info *exist, char *pfx_buf,
- afi_t afi, safi_t safi)
+ afi_t afi, safi_t safi,
+ enum bgp_path_selection_reason *reason)
{
int paths_eq;
int ret;
ret = bgp_path_info_cmp(bgp, new, exist, &paths_eq, NULL, 0, pfx_buf,
- afi, safi);
+ afi, safi, reason);
if (paths_eq)
ret = 0;
@@ -1219,20 +1263,6 @@ static int bgp_input_modifier(struct peer *peer, struct prefix *p,
}
}
- /* RFC 8212 to prevent route leaks.
- * This specification intends to improve this situation by requiring the
- * explicit configuration of both BGP Import and Export Policies for any
- * External BGP (EBGP) session such as customers, peers, or
- * confederation boundaries for all enabled address families. Through
- * codification of the aforementioned requirement, operators will
- * benefit from consistent behavior across different BGP
- * implementations.
- */
- if (peer->bgp->ebgp_requires_policy
- == DEFAULT_EBGP_POLICY_ENABLED)
- if (!bgp_inbound_policy_exists(peer, filter))
- return RMAP_DENY;
-
/* Route map apply. */
if (rmap) {
memset(&rmap_path, 0, sizeof(struct bgp_path_info));
@@ -1781,6 +1811,10 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
peer->rmap_type = 0;
if (ret == RMAP_DENYMATCH) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug("%s [Update:SEND] %s is filtered by route-map",
+ peer->host, prefix2str(p, buf, sizeof(buf)));
+
bgp_attr_flush(attr);
return 0;
}
@@ -1854,13 +1888,28 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
* Note: 3rd party nexthop currently implemented for
* IPv4 only.
*/
- if (!bgp_subgrp_multiaccess_check_v4(piattr->nexthop,
- subgrp))
+ if ((p->family == AF_INET) &&
+ (!bgp_subgrp_multiaccess_check_v4(
+ piattr->nexthop,
+ subgrp)))
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
? AF_INET6
: p->family),
- attr);
+ attr);
+
+ if ((p->family == AF_INET6) &&
+ (!bgp_subgrp_multiaccess_check_v6(
+ piattr->mp_nexthop_global,
+ subgrp)))
+ subgroup_announce_reset_nhop(
+ (peer_cap_enhe(peer, afi, safi)
+ ? AF_INET6
+ : p->family),
+ attr);
+
+
+
} else if (CHECK_FLAG(pi->flags, BGP_PATH_ANNC_NH_SELF)) {
/*
* This flag is used for leaked vpn-vrf routes
@@ -1965,7 +2014,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
if (bgp_path_info_cmp(
bgp, pi2, new_select,
&paths_eq, mpath_cfg, debug,
- pfx_buf, afi, safi)) {
+ pfx_buf, afi, safi,
+ &rn->reason)) {
bgp_path_info_unset_flag(
rn, new_select,
BGP_PATH_DMED_SELECTED);
@@ -2038,7 +2088,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
bgp_path_info_unset_flag(rn, pi, BGP_PATH_DMED_CHECK);
if (bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, mpath_cfg,
- debug, pfx_buf, afi, safi)) {
+ debug, pfx_buf, afi, safi, &rn->reason)) {
new_select = pi;
}
}
@@ -2094,7 +2144,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
}
bgp_path_info_cmp(bgp, pi, new_select, &paths_eq,
- mpath_cfg, debug, pfx_buf, afi, safi);
+ mpath_cfg, debug, pfx_buf, afi, safi,
+ &rn->reason);
if (paths_eq) {
if (debug)
@@ -3049,6 +3100,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
goto filtered;
}
+ /* RFC 8212 to prevent route leaks.
+ * This specification intends to improve this situation by requiring the
+ * explicit configuration of both BGP Import and Export Policies for any
+ * External BGP (EBGP) session such as customers, peers, or
+ * confederation boundaries for all enabled address families. Through
+ * codification of the aforementioned requirement, operators will
+ * benefit from consistent behavior across different BGP
+ * implementations.
+ */
+ if (peer->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED)
+ if (!bgp_inbound_policy_exists(peer,
+ &peer->filter[afi][safi])) {
+ reason = "inbound policy missing";
+ goto filtered;
+ }
+
bgp_attr_dup(&new_attr, attr);
/* Apply incoming route-map.
@@ -6863,6 +6930,13 @@ static void route_vty_short_status_out(struct vty *vty,
vty_out(vty, " ");
}
+static char *bgp_nexthop_fqdn(struct peer *peer)
+{
+ if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME))
+ return peer->hostname;
+ return NULL;
+}
+
/* called from terminal list command */
void route_vty_out(struct vty *vty, struct prefix *p,
struct bgp_path_info *path, int display, safi_t safi,
@@ -6879,6 +6953,7 @@ void route_vty_out(struct vty *vty, struct prefix *p,
bool nexthop_othervrf = false;
vrf_id_t nexthop_vrfid = VRF_DEFAULT;
const char *nexthop_vrfname = VRF_DEFAULT_NAME;
+ char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
if (json_paths)
json_path = json_object_new_object();
@@ -6974,42 +7049,58 @@ void route_vty_out(struct vty *vty, struct prefix *p,
if (json_paths) {
json_nexthop_global = json_object_new_object();
- json_object_string_add(json_nexthop_global, "afi",
- (af == AF_INET) ? "ip" : "ipv6");
- json_object_string_add(json_nexthop_global,
- (af == AF_INET) ? "ip" : "ipv6",
- nexthop);
+ json_object_string_add(
+ json_nexthop_global, "afi",
+ nexthop_fqdn ? "fqdn"
+ : (af == AF_INET) ? "ip" : "ipv6");
+ json_object_string_add(
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn"
+ : (af == AF_INET) ? "ip" : "ipv6",
+ nexthop_fqdn ? nexthop_fqdn : nexthop);
json_object_boolean_true_add(json_nexthop_global,
"used");
} else
- vty_out(vty, "%s%s", nexthop, vrf_id_str);
+ vty_out(vty, "%s%s",
+ nexthop_fqdn ? nexthop_fqdn : nexthop,
+ vrf_id_str);
} else if (safi == SAFI_EVPN) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
- json_object_string_add(json_nexthop_global, "ip",
- inet_ntoa(attr->nexthop));
+ json_object_string_add(
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop));
json_object_string_add(json_nexthop_global, "afi",
"ipv4");
json_object_boolean_true_add(json_nexthop_global,
"used");
} else
- vty_out(vty, "%-16s%s", inet_ntoa(attr->nexthop),
+ vty_out(vty, "%-16s%s",
+ nexthop_fqdn ?: inet_ntoa(attr->nexthop),
vrf_id_str);
} else if (safi == SAFI_FLOWSPEC) {
if (attr->nexthop.s_addr != 0) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntoa(attr->nexthop));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop));
json_object_string_add(json_nexthop_global,
"afi", "ipv4");
json_object_boolean_true_add(
json_nexthop_global,
"used");
} else {
- vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
+ vty_out(vty, "%-16s",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop));
}
}
} else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
@@ -7018,12 +7109,19 @@ void route_vty_out(struct vty *vty, struct prefix *p,
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntoa(attr->mp_nexthop_global_in));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(
+ attr->mp_nexthop_global_in));
else
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntoa(attr->nexthop));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop));
json_object_string_add(json_nexthop_global, "afi",
"ipv4");
@@ -7033,7 +7131,9 @@ void route_vty_out(struct vty *vty, struct prefix *p,
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), "%s%s",
- inet_ntoa(attr->nexthop), vrf_id_str);
+ nexthop_fqdn ? nexthop_fqdn
+ : inet_ntoa(attr->nexthop),
+ vrf_id_str);
vty_out(vty, "%-16s", buf);
}
}
@@ -7046,9 +7146,13 @@ void route_vty_out(struct vty *vty, struct prefix *p,
if (json_paths) {
json_nexthop_global = json_object_new_object();
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntop(AF_INET6, &attr->mp_nexthop_global,
- buf, BUFSIZ));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global,
+ buf, BUFSIZ));
json_object_string_add(json_nexthop_global, "afi",
"ipv6");
json_object_string_add(json_nexthop_global, "scope",
@@ -7060,10 +7164,14 @@ void route_vty_out(struct vty *vty, struct prefix *p,
|| (path->peer->conf_if)) {
json_nexthop_ll = json_object_new_object();
json_object_string_add(
- json_nexthop_ll, "ip",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_local, buf,
- BUFSIZ));
+ json_nexthop_ll,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_local,
+ buf, BUFSIZ));
json_object_string_add(json_nexthop_ll, "afi",
"ipv6");
json_object_string_add(json_nexthop_ll, "scope",
@@ -7102,10 +7210,12 @@ void route_vty_out(struct vty *vty, struct prefix *p,
} else {
len = vty_out(
vty, "%s%s",
- inet_ntop(
- AF_INET6,
- &attr->mp_nexthop_local,
- buf, BUFSIZ),
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_local,
+ buf, BUFSIZ),
vrf_id_str);
len = 16 - len;
@@ -7117,10 +7227,13 @@ void route_vty_out(struct vty *vty, struct prefix *p,
} else {
len = vty_out(
vty, "%s%s",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_global, buf,
- BUFSIZ),
- vrf_id_str);
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_global,
+ buf, BUFSIZ),
+ vrf_id_str);
len = 16 - len;
if (len < 1)
@@ -7519,21 +7632,26 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p,
json_object *json_paths)
{
struct attr *attr;
- char buf[BUFSIZ];
+ char buf[BUFSIZ] = {0};
json_object *json_path = NULL;
-
- if (json_paths)
- json_path = json_object_new_object();
+ json_object *json_nexthop = NULL;
+ json_object *json_overlay = NULL;
if (!path->extra)
return;
+ if (json_paths) {
+ json_path = json_object_new_object();
+ json_overlay = json_object_new_object();
+ json_nexthop = json_object_new_object();
+ }
+
/* short status lead text */
route_vty_short_status_out(vty, path, json_path);
/* print prefix and mask */
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, json_path);
else
vty_out(vty, "%*s", 17, " ");
@@ -7545,35 +7663,69 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p,
switch (af) {
case AF_INET:
- vty_out(vty, "%-16s",
- inet_ntop(af, &attr->mp_nexthop_global_in, buf,
- BUFSIZ));
+ inet_ntop(af, &attr->mp_nexthop_global_in, buf, BUFSIZ);
+ if (!json_path) {
+ vty_out(vty, "%-16s", buf);
+ } else {
+ json_object_string_add(json_nexthop, "ip", buf);
+
+ json_object_string_add(json_nexthop, "afi",
+ "ipv4");
+
+ json_object_object_add(json_path, "nexthop",
+ json_nexthop);
+ }
break;
case AF_INET6:
- vty_out(vty, "%s(%s)",
- inet_ntop(af, &attr->mp_nexthop_global, buf,
- BUFSIZ),
- inet_ntop(af, &attr->mp_nexthop_local, buf1,
- BUFSIZ));
+ inet_ntop(af, &attr->mp_nexthop_global, buf, BUFSIZ);
+ inet_ntop(af, &attr->mp_nexthop_local, buf1, BUFSIZ);
+ if (!json_path) {
+ vty_out(vty, "%s(%s)", buf, buf1);
+ } else {
+ json_object_string_add(json_nexthop,
+ "ipv6Global", buf);
+
+ json_object_string_add(json_nexthop,
+ "ipv6LinkLocal", buf1);
+
+ json_object_string_add(json_nexthop, "afi",
+ "ipv6");
+
+ json_object_object_add(json_path, "nexthop",
+ json_nexthop);
+ }
break;
default:
- vty_out(vty, "?");
+ if (!json_path) {
+ vty_out(vty, "?");
+ } else {
+ json_object_string_add(json_nexthop, "Error",
+ "Unsupported address-family");
+ }
}
char *str = esi2str(&(attr->evpn_overlay.eth_s_id));
- vty_out(vty, "%s", str);
+ if (!json_path)
+ vty_out(vty, "%s", str);
+ else
+ json_object_string_add(json_overlay, "esi", str);
+
XFREE(MTYPE_TMP, str);
if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s",
- inet_ntoa(attr->evpn_overlay.gw_ip.ipv4));
+ inet_ntop(AF_INET, &(attr->evpn_overlay.gw_ip.ipv4),
+ buf, BUFSIZ);
} else if (is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s",
- inet_ntop(AF_INET6,
- &(attr->evpn_overlay.gw_ip.ipv6), buf,
- BUFSIZ));
+ inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6),
+ buf, BUFSIZ);
}
+
+ if (!json_path)
+ vty_out(vty, "/%s", buf);
+ else
+ json_object_string_add(json_overlay, "gw", buf);
+
if (attr->ecommunity) {
char *mac = NULL;
struct ecommunity_val *routermac = ecommunity_lookup(
@@ -7582,13 +7734,25 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p,
if (routermac)
mac = ecom_mac2str((char *)routermac->val);
if (mac) {
- vty_out(vty, "/%s", (char *)mac);
+ if (!json_path) {
+ vty_out(vty, "/%s", (char *)mac);
+ } else {
+ json_object_string_add(json_overlay,
+ "rmac", mac);
+ }
XFREE(MTYPE_TMP, mac);
}
}
- vty_out(vty, "\n");
- }
+ if (!json_path) {
+ vty_out(vty, "\n");
+ } else {
+ json_object_object_add(json_path, "overlay",
+ json_overlay);
+
+ json_object_array_add(json_paths, json_path);
+ }
+ }
}
/* dampening route */
@@ -7816,9 +7980,83 @@ static void route_vty_out_tx_ids(struct vty *vty,
}
}
-void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
- struct bgp_path_info *path, afi_t afi, safi_t safi,
- json_object *json_paths)
+static const char *bgp_path_selection_reason2str(
+ enum bgp_path_selection_reason reason)
+{
+ switch (reason) {
+ case bgp_path_selection_none:
+ return "Nothing to Select";
+ break;
+ case bgp_path_selection_first:
+ return "First path received";
+ break;
+ case bgp_path_selection_evpn_sticky_mac:
+ return "EVPN Sticky Mac";
+ break;
+ case bgp_path_selection_evpn_seq:
+ return "EVPN sequence number";
+ break;
+ case bgp_path_selection_evpn_lower_ip:
+ return "EVPN lower IP";
+ break;
+ case bgp_path_selection_weight:
+ return "Weight";
+ break;
+ case bgp_path_selection_local_pref:
+ return "Local Pref";
+ break;
+ case bgp_path_selection_local_route:
+ return "Local Route";
+ break;
+ case bgp_path_selection_confed_as_path:
+ return "Confederation based AS Path";
+ break;
+ case bgp_path_selection_as_path:
+ return "AS Path";
+ break;
+ case bgp_path_selection_origin:
+ return "Origin";
+ break;
+ case bgp_path_selection_med:
+ return "MED";
+ break;
+ case bgp_path_selection_peer:
+ return "Peer Type";
+ break;
+ case bgp_path_selection_confed:
+ return "Confed Peer Type";
+ break;
+ case bgp_path_selection_igp_metric:
+ return "IGP Metric";
+ break;
+ case bgp_path_selection_older:
+ return "Older Path";
+ break;
+ case bgp_path_selection_router_id:
+ return "Router ID";
+ break;
+ case bgp_path_selection_cluster_length:
+ return "Cluser length";
+ break;
+ case bgp_path_selection_stale:
+ return "Path Staleness";
+ break;
+ case bgp_path_selection_local_configured:
+ return "Locally configured route";
+ break;
+ case bgp_path_selection_neighbor_ip:
+ return "Neighbor IP";
+ break;
+ case bgp_path_selection_default:
+ return "Nothing left to compare";
+ break;
+ }
+ return "Invalid (internal error)";
+}
+
+void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+ struct bgp_node *bn, struct bgp_path_info *path,
+ afi_t afi, safi_t safi, json_object *json_paths)
{
char buf[INET6_ADDRSTRLEN];
char buf1[BUFSIZ];
@@ -7848,6 +8086,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
bool nexthop_self =
CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
int i;
+ char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
if (json_paths) {
json_path = json_object_new_object();
@@ -7858,7 +8097,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
if (!json_paths && safi == SAFI_EVPN) {
char tag_buf[30];
- bgp_evpn_route2str((struct prefix_evpn *)p, buf2, sizeof(buf2));
+ bgp_evpn_route2str((struct prefix_evpn *)&bn->p,
+ buf2, sizeof(buf2));
vty_out(vty, " Route %s", buf2);
tag_buf[0] = '\0';
if (path->extra && path->extra->num_labels) {
@@ -7973,8 +8213,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
/* Line2 display Next-hop, Neighbor, Router-id */
/* Display the nexthop */
- if ((p->family == AF_INET || p->family == AF_ETHERNET
- || p->family == AF_EVPN)
+ if ((bn->p.family == AF_INET || bn->p.family == AF_ETHERNET
+ || bn->p.family == AF_EVPN)
&& (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN
|| !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
@@ -7982,21 +8222,33 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
|| safi == SAFI_EVPN) {
if (json_paths)
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntoa(
- attr->mp_nexthop_global_in));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(
+ attr->mp_nexthop_global_in));
else
vty_out(vty, " %s",
- inet_ntoa(
- attr->mp_nexthop_global_in));
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(
+ attr->mp_nexthop_global_in));
} else {
if (json_paths)
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntoa(attr->nexthop));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(
+ attr->nexthop));
else
vty_out(vty, " %s",
- inet_ntoa(attr->nexthop));
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntoa(
+ attr->nexthop));
}
if (json_paths)
@@ -8005,19 +8257,28 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
} else {
if (json_paths) {
json_object_string_add(
- json_nexthop_global, "ip",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_global, buf,
- INET6_ADDRSTRLEN));
+ json_nexthop_global,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_global,
+ buf,
+ INET6_ADDRSTRLEN));
json_object_string_add(json_nexthop_global,
"afi", "ipv6");
json_object_string_add(json_nexthop_global,
"scope", "global");
} else {
vty_out(vty, " %s",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_global, buf,
- INET6_ADDRSTRLEN));
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_global,
+ buf,
+ INET6_ADDRSTRLEN));
}
}
@@ -8056,7 +8317,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
if (path->peer == bgp->peer_self) {
if (safi == SAFI_EVPN
- || (p->family == AF_INET
+ || (bn->p.family == AF_INET
&& !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
if (json_paths)
json_object_string_add(
@@ -8199,10 +8460,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
if (json_paths) {
json_nexthop_ll = json_object_new_object();
json_object_string_add(
- json_nexthop_ll, "ip",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_local, buf,
- INET6_ADDRSTRLEN));
+ json_nexthop_ll,
+ nexthop_fqdn ? "fqdn" : "ip",
+ nexthop_fqdn
+ ? nexthop_fqdn
+ : inet_ntop(
+ AF_INET6,
+ &attr->mp_nexthop_local,
+ buf,
+ INET6_ADDRSTRLEN));
json_object_string_add(json_nexthop_ll, "afi",
"ipv6");
json_object_string_add(json_nexthop_ll, "scope",
@@ -8410,8 +8676,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
json_object_new_object();
json_object_boolean_true_add(json_bestpath,
"overall");
- } else
+ json_object_string_add(json_bestpath,
+ "selectionReason",
+ bgp_path_selection_reason2str(bn->reason));
+ } else {
vty_out(vty, ", best");
+ vty_out(vty, " (%s)",
+ bgp_path_selection_reason2str(bn->reason));
+ }
}
if (json_bestpath)
@@ -9012,6 +9284,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
unsigned long i;
for (i = 0; i < *json_header_depth; ++i)
vty_out(vty, " } ");
+ vty_out(vty, "\n");
}
} else {
if (is_last) {
@@ -9422,7 +9695,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
BGP_PATH_MULTIPATH)
|| CHECK_FLAG(pi->flags,
BGP_PATH_SELECTED))))
- route_vty_out_detail(vty, bgp, &rm->p,
+ route_vty_out_detail(vty, bgp, rm,
pi, AFI_IP, safi,
json_paths);
}
@@ -9466,7 +9739,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
pi->flags,
BGP_PATH_SELECTED))))
route_vty_out_detail(
- vty, bgp, &rn->p, pi,
+ vty, bgp, rn, pi,
afi, safi, json_paths);
}
}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 7bbc14b46f..f715084450 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -588,7 +588,8 @@ extern void bgp_path_info_restore(struct bgp_node *rn,
extern int bgp_path_info_cmp_compatible(struct bgp *bgp,
struct bgp_path_info *new,
struct bgp_path_info *exist,
- char *pfx_buf, afi_t afi, safi_t safi);
+ char *pfx_buf, afi_t afi, safi_t safi,
+ enum bgp_path_selection_reason *reason);
extern void bgp_attr_add_gshut_community(struct attr *attr);
extern void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
@@ -604,7 +605,8 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
struct prefix_rd *prd, afi_t afi,
safi_t safi, json_object *json);
extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
- struct prefix *p, struct bgp_path_info *path,
+ struct bgp_node *bn,
+ struct bgp_path_info *path,
afi_t afi, safi_t safi,
json_object *json_paths);
extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index c276f5ef7b..b0ae9d78d1 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -28,7 +28,7 @@
#include "plist.h"
#include "memory.h"
#include "log.h"
-#include "lua.h"
+#include "frrlua.h"
#ifdef HAVE_LIBPCREPOSIX
#include <pcreposix.h>
#else
@@ -3090,8 +3090,6 @@ static void bgp_route_map_process_peer(const char *rmap_name,
struct route_map *map, struct peer *peer,
int afi, int safi, int route_update)
{
-
- int update;
struct bgp_filter *filter;
if (!peer || !rmap_name)
@@ -3102,52 +3100,16 @@ static void bgp_route_map_process_peer(const char *rmap_name,
* in is for non-route-server clients,
* out is for all peers
*/
- if (!CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) {
- if (filter->map[RMAP_IN].name
- && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) {
- filter->map[RMAP_IN].map = map;
-
- if (route_update && peer->status == Established) {
- if (CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_SOFT_RECONFIG)) {
- if (bgp_debug_update(peer, NULL, NULL,
- 1))
- zlog_debug(
- "Processing route_map %s update on "
- "peer %s (inbound, soft-reconfig)",
- rmap_name, peer->host);
-
- bgp_soft_reconfig_in(peer, afi, safi);
- } else if (
- CHECK_FLAG(peer->cap,
- PEER_CAP_REFRESH_OLD_RCV)
- || CHECK_FLAG(
- peer->cap,
- PEER_CAP_REFRESH_NEW_RCV)) {
-
- if (bgp_debug_update(peer, NULL, NULL,
- 1))
- zlog_debug(
- "Processing route_map %s update on "
- "peer %s (inbound, route-refresh)",
- rmap_name, peer->host);
- bgp_route_refresh_send(peer, afi, safi,
- 0, 0, 0);
- }
- }
- }
- }
-
- if (CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) {
- update = 0;
+ if (filter->map[RMAP_IN].name
+ && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) {
+ filter->map[RMAP_IN].map = map;
- if (update && route_update && peer->status == Established) {
+ if (route_update && peer->status == Established) {
if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_SOFT_RECONFIG)) {
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug(
- "Processing route_map %s update on "
- "peer %s (import, soft-reconfig)",
+ "Processing route_map %s update on peer %s (inbound, soft-reconfig)",
rmap_name, peer->host);
bgp_soft_reconfig_in(peer, afi, safi);
@@ -3157,13 +3119,11 @@ static void bgp_route_map_process_peer(const char *rmap_name,
PEER_CAP_REFRESH_NEW_RCV)) {
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug(
- "Processing route_map %s update on "
- "peer %s (import, route-refresh)",
+ "Processing route_map %s update on peer %s (inbound, route-refresh)",
rmap_name, peer->host);
bgp_route_refresh_send(peer, afi, safi, 0, 0,
0);
}
- /* DD: Else, what else do we do ? Reset peer ? */
}
}
@@ -3447,7 +3407,7 @@ static void bgp_route_map_delete(const char *rmap_name)
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
}
-static void bgp_route_map_event(route_map_event_t event, const char *rmap_name)
+static void bgp_route_map_event(const char *rmap_name)
{
if (route_map_mark_updated(rmap_name) == 0)
bgp_route_map_mark_update(rmap_name);
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index c63d4f9ad2..aa09026b78 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -737,28 +737,27 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
prefix->prefixlen, &result);
// Print Debug output
- prefix_string =
- inet_ntop(prefix->family, &prefix->u.prefix, buf, BUFSIZ);
+ prefix_string = prefix2str(prefix, buf, sizeof(buf));
switch (result) {
case BGP_PFXV_STATE_VALID:
RPKI_DEBUG(
- "Validating Prefix %s/%hhu from asn %u Result: VALID",
- prefix_string, prefix->prefixlen, as_number);
+ "Validating Prefix %s from asn %u Result: VALID",
+ prefix_string, as_number);
return RPKI_VALID;
case BGP_PFXV_STATE_NOT_FOUND:
RPKI_DEBUG(
- "Validating Prefix %s/%hhu from asn %u Result: NOT FOUND",
- prefix_string, prefix->prefixlen, as_number);
+ "Validating Prefix %s from asn %u Result: NOT FOUND",
+ prefix_string, as_number);
return RPKI_NOTFOUND;
case BGP_PFXV_STATE_INVALID:
RPKI_DEBUG(
- "Validating Prefix %s/%hhu from asn %u Result: INVALID",
- prefix_string, prefix->prefixlen, as_number);
+ "Validating Prefix %s from asn %u Result: INVALID",
+ prefix_string, as_number);
return RPKI_INVALID;
default:
RPKI_DEBUG(
- "Validating Prefix %s/%hhu from asn %u Result: CANNOT VALIDATE",
- prefix_string, prefix->prefixlen, as_number);
+ "Validating Prefix %s from asn %u Result: CANNOT VALIDATE",
+ prefix_string, as_number);
break;
}
return 0;
@@ -1112,7 +1111,7 @@ DEFPY (rpki_cache,
vty_out(vty,
"ssh sockets are not supported. "
"Please recompile rtrlib and frr with ssh support. "
- "If you want to use it");
+ "If you want to use it\n");
#endif
} else { // use tcp connection
return_value = add_tcp_cache(cache, tcpport, preference);
@@ -1253,6 +1252,7 @@ DEFUN (show_rpki_cache_server,
cache->tr_config.tcp_config->host,
cache->tr_config.tcp_config->port);
+#if defined(FOUND_SSH)
} else if (cache->type == SSH) {
vty_out(vty,
"host: %s port: %d username: %s "
@@ -1264,6 +1264,7 @@ DEFUN (show_rpki_cache_server,
->server_hostkey_path,
cache->tr_config.ssh_config
->client_privkey_path);
+#endif
}
}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 040e83a8cd..b3542e7848 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -42,6 +42,31 @@ struct bgp_table {
uint64_t version;
};
+enum bgp_path_selection_reason {
+ bgp_path_selection_none,
+ bgp_path_selection_first,
+ bgp_path_selection_evpn_sticky_mac,
+ bgp_path_selection_evpn_seq,
+ bgp_path_selection_evpn_lower_ip,
+ bgp_path_selection_weight,
+ bgp_path_selection_local_pref,
+ bgp_path_selection_local_route,
+ bgp_path_selection_confed_as_path,
+ bgp_path_selection_as_path,
+ bgp_path_selection_origin,
+ bgp_path_selection_med,
+ bgp_path_selection_peer,
+ bgp_path_selection_confed,
+ bgp_path_selection_igp_metric,
+ bgp_path_selection_older,
+ bgp_path_selection_router_id,
+ bgp_path_selection_cluster_length,
+ bgp_path_selection_stale,
+ bgp_path_selection_local_configured,
+ bgp_path_selection_neighbor_ip,
+ bgp_path_selection_default,
+};
+
struct bgp_node {
/*
* CAUTION
@@ -72,6 +97,8 @@ struct bgp_node {
#define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3)
struct bgp_addpath_node_data tx_addpath;
+
+ enum bgp_path_selection_reason reason;
};
/*
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index 49a435120d..d0be2471af 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -83,9 +83,9 @@ static void sync_init(struct update_subgroup *subgrp)
{
subgrp->sync =
XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize));
- BGP_ADV_FIFO_INIT(&subgrp->sync->update);
- BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw);
- BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw_low);
+ bgp_adv_fifo_init(&subgrp->sync->update);
+ bgp_adv_fifo_init(&subgrp->sync->withdraw);
+ bgp_adv_fifo_init(&subgrp->sync->withdraw_low);
subgrp->hash =
hash_create(baa_hash_key, baa_hash_cmp, "BGP SubGroup Hash");
@@ -288,7 +288,7 @@ static void *updgrp_hash_alloc(void *p)
* 16. Local-as should match, if configured.
* )
*/
-static unsigned int updgrp_hash_key_make(void *p)
+static unsigned int updgrp_hash_key_make(const void *p)
{
const struct update_group *updgrp;
const struct peer *peer;
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
index 6b3bf9d1f7..bb547454f2 100644
--- a/bgpd/bgp_updgrp.h
+++ b/bgpd/bgp_updgrp.h
@@ -56,6 +56,7 @@
#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 \
@@ -590,9 +591,9 @@ static inline void bgp_announce_peer(struct peer *peer)
*/
static inline int advertise_list_is_empty(struct update_subgroup *subgrp)
{
- if (!BGP_ADV_FIFO_EMPTY(&subgrp->sync->update)
- || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw)
- || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw_low)) {
+ if (bgp_adv_fifo_count(&subgrp->sync->update)
+ || bgp_adv_fifo_count(&subgrp->sync->withdraw)
+ || bgp_adv_fifo_count(&subgrp->sync->withdraw_low)) {
return 0;
}
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index 3870df593f..b64c51f341 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -422,7 +422,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
struct bgp_advertise *adv;
struct bgp_advertise_attr *baa;
struct bgp_advertise *next;
- struct bgp_advertise_fifo *fhead;
+ struct bgp_adv_fifo_head *fhead;
adv = adj->adv;
baa = adv->baa;
@@ -444,7 +444,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
/* Unlink myself from advertisement FIFO. */
- BGP_ADV_FIFO_DEL(fhead, adv);
+ bgp_adv_fifo_del(fhead, adv);
/* Free memory. */
bgp_advertise_free(adj->adv);
@@ -507,7 +507,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn,
* If the update adv list is empty, trigger the member peers'
* mrai timers so the socket writes can happen.
*/
- if (BGP_ADV_FIFO_EMPTY(&subgrp->sync->update)) {
+ if (!bgp_adv_fifo_count(&subgrp->sync->update)) {
struct peer_af *paf;
SUBGRP_FOREACH_PEER (subgrp, paf) {
@@ -515,7 +515,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn,
}
}
- BGP_ADV_FIFO_ADD(&subgrp->sync->update, &adv->fifo);
+ bgp_adv_fifo_add_tail(&subgrp->sync->update, adv);
subgrp->version = max(subgrp->version, rn->version);
}
@@ -550,11 +550,11 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn,
/* Note if we need to trigger a packet write */
trigger_write =
- BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw);
+ !bgp_adv_fifo_count(&subgrp->sync->withdraw);
/* Add to synchronization entry for withdraw
* announcement. */
- BGP_ADV_FIFO_ADD(&subgrp->sync->withdraw, &adv->fifo);
+ bgp_adv_fifo_add_tail(&subgrp->sync->withdraw, adv);
if (trigger_write)
subgroup_trigger_write(subgrp);
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 66e306cba2..688abae0e4 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -554,9 +554,9 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
mod_v6nhg = &peer->nexthop.v6_global;
gnh_modified = 1;
} else if (
- peer->sort == BGP_PEER_EBGP
- && !CHECK_FLAG(
- vec->flags,
+ (peer->sort == BGP_PEER_EBGP)
+ && (!bgp_multiaccess_check_v6(v6nhglobal, peer))
+ && !CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
&& !peer_af_flag_check(
peer, nhafi, paf->safi,
@@ -664,11 +664,11 @@ int subgroup_packets_to_build(struct update_subgroup *subgrp)
if (!subgrp)
return 0;
- adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw);
+ adv = bgp_adv_fifo_first(&subgrp->sync->withdraw);
if (adv)
return 1;
- adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update);
+ adv = bgp_adv_fifo_first(&subgrp->sync->update);
if (adv)
return 1;
@@ -725,7 +725,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
addpath_encode = bgp_addpath_encode_tx(peer, afi, safi);
addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0;
- adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update);
+ adv = bgp_adv_fifo_first(&subgrp->sync->update);
while (adv) {
assert(adv->rn);
rn = adv->rn;
@@ -966,7 +966,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
addpath_encode = bgp_addpath_encode_tx(peer, afi, safi);
addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0;
- while ((adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw)) != NULL) {
+ while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) {
assert(adv->rn);
adj = adv->adj;
rn = adv->rn;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 01144f5c78..ae51f1d780 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -119,6 +119,7 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi)
case AFI_L2VPN:
return BGP_EVPN_NODE;
break;
+ case AFI_UNSPEC:
case AFI_MAX:
// We should never be here but to clarify the switch statement..
return BGP_IPV4_NODE;
@@ -295,6 +296,7 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index,
* afi -> The parsed afi if it was included in the show command, returned here
* safi -> The parsed safi if it was included in the show command, returned here
* bgp -> Pointer to the bgp data structure we need to fill in.
+ * use_json -> json is configured or not
*
* The function returns the correct location in the parse tree for the
* last token found.
@@ -329,8 +331,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
else {
*bgp = bgp_lookup_by_name(vrf_name);
if (!*bgp) {
- if (use_json)
- vty_out(vty, "{}\n");
+ if (use_json) {
+ json_object *json = NULL;
+ json = json_object_new_object();
+ json_object_string_add(
+ json, "warning",
+ "View/Vrf is unknown");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(json,
+ JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
else
vty_out(vty, "View/Vrf %s is unknown\n",
vrf_name);
@@ -341,8 +352,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
} else {
*bgp = bgp_get_default();
if (!*bgp) {
- if (use_json)
- vty_out(vty, "{}\n");
+ if (use_json) {
+ json_object *json = NULL;
+ json = json_object_new_object();
+ json_object_string_add(
+ json, "warning",
+ "Default BGP instance not found");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(json,
+ JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
else
vty_out(vty,
"Default BGP instance not found\n");
@@ -2123,28 +2143,6 @@ DEFUN (no_bgp_fast_external_failover,
return CMD_SUCCESS;
}
-/* "bgp enforce-first-as" configuration. */
-#if CONFDATE > 20190517
-CPP_NOTICE("bgpd: remove deprecated '[no] bgp enforce-first-as' commands")
-#endif
-
-DEFUN_HIDDEN (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 (strmatch(argv[0]->text, "no"))
- bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
- else
- bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
-
- return CMD_SUCCESS;
-}
-
/* "bgp bestpath compare-routerid" configuration. */
DEFUN (bgp_bestpath_compare_router_id,
bgp_bestpath_compare_router_id_cmd,
@@ -10911,11 +10909,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
if (p->password)
vty_out(vty, "Peer Authentication Enabled\n");
- vty_out(vty, "Read thread: %s Write thread: %s\n",
+ vty_out(vty, "Read thread: %s Write thread: %s FD used: %d\n",
p->t_read ? "on" : "off",
CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON)
? "on"
- : "off");
+ : "off", p->fd);
}
if (p->notify.code == BGP_NOTIFY_OPEN_ERR
@@ -13016,9 +13014,6 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &bgp_fast_external_failover_cmd);
install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd);
- /* "bgp enforce-first-as" commands */
- install_element(BGP_NODE, &bgp_enforce_first_as_cmd);
-
/* "bgp bestpath compare-routerid" commands */
install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd);
install_element(BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd);
@@ -14596,7 +14591,7 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc,
vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n");
vty_out(vty, "if you are using this please migrate to the below command.\n");
vty_out(vty, "'no bgp large-community-list <(1-99)|(100-500)|standard|expanded> <deny|permit> <LINE|AA:BB:CC>'\n");
- zlog_warn("Deprecated option: 'no ip large-community-list <(1-99)|(100-500)|standard|expanded> <deny|permit> <LINE|AA:BB:CC>' being used");
+ zlog_warn("Deprecated option: 'no ip large-community-list <(1-99)|(100-500)|standard|expanded> <deny|permit> <LINE|AA:BB:CC>' being used");
}
argv_find(argv, argc, "permit", &idx);
argv_find(argv, argc, "deny", &idx);
@@ -14951,14 +14946,16 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list)
if (entry == list->head) {
if (all_digit(list->name))
vty_out(vty, "Large community %s list %s\n",
- entry->style == EXTCOMMUNITY_LIST_STANDARD
+ entry->style ==
+ LARGE_COMMUNITY_LIST_STANDARD
? "standard"
: "(expanded) access",
list->name);
else
vty_out(vty,
"Named large community %s list %s\n",
- entry->style == EXTCOMMUNITY_LIST_STANDARD
+ entry->style ==
+ LARGE_COMMUNITY_LIST_STANDARD
? "standard"
: "expanded",
list->name);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index e42d6ee260..a45480fdc2 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -83,8 +83,7 @@ static inline int bgp_install_info_to_zebra(struct bgp *bgp)
int zclient_num_connects;
/* Router-id update message from zebra. */
-static int bgp_router_id_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_router_id_update(ZAPI_CALLBACK_ARGS)
{
struct prefix router_id;
@@ -101,17 +100,15 @@ static int bgp_router_id_update(int command, struct zclient *zclient,
}
/* Nexthop update message from zebra. */
-static int bgp_read_nexthop_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS)
{
- bgp_parse_nexthop_update(command, vrf_id);
+ bgp_parse_nexthop_update(cmd, vrf_id);
return 0;
}
-static int bgp_read_import_check_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_read_import_check_update(ZAPI_CALLBACK_ARGS)
{
- bgp_parse_nexthop_update(command, vrf_id);
+ bgp_parse_nexthop_update(cmd, vrf_id);
return 0;
}
@@ -206,8 +203,7 @@ static void bgp_nbr_connected_delete(struct bgp *bgp, struct nbr_connected *ifc,
}
/* Inteface addition message from zebra. */
-static int bgp_interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct bgp *bgp;
@@ -229,8 +225,7 @@ static int bgp_interface_add(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct interface *ifp;
@@ -255,8 +250,7 @@ static int bgp_interface_delete(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_up(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct interface *ifp;
@@ -290,8 +284,7 @@ static int bgp_interface_up(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_down(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct interface *ifp;
@@ -350,15 +343,14 @@ static int bgp_interface_down(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct bgp *bgp;
bgp = bgp_lookup_by_vrf_id(vrf_id);
- ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -388,15 +380,14 @@ static int bgp_interface_address_add(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct bgp *bgp;
bgp = bgp_lookup_by_vrf_id(vrf_id);
- ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -417,13 +408,12 @@ static int bgp_interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_nbr_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_nbr_address_add(ZAPI_CALLBACK_ARGS)
{
struct nbr_connected *ifc = NULL;
struct bgp *bgp;
- ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -444,15 +434,12 @@ static int bgp_interface_nbr_address_add(int command, struct zclient *zclient,
return 0;
}
-static int bgp_interface_nbr_address_delete(int command,
- struct zclient *zclient,
- zebra_size_t length,
- vrf_id_t vrf_id)
+static int bgp_interface_nbr_address_delete(ZAPI_CALLBACK_ARGS)
{
struct nbr_connected *ifc = NULL;
struct bgp *bgp;
- ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -476,8 +463,7 @@ static int bgp_interface_nbr_address_delete(int command,
}
/* VRF update for an interface. */
-static int bgp_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t new_vrf_id;
@@ -532,8 +518,7 @@ static int bgp_interface_vrf_update(int command, struct zclient *zclient,
}
/* Zebra route add and delete treatment. */
-static int zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int zebra_read_route(ZAPI_CALLBACK_ARGS)
{
enum nexthop_types_t nhtype;
struct zapi_route api;
@@ -562,7 +547,7 @@ static int zebra_read_route(int command, struct zclient *zclient,
ifindex = api.nexthops[0].ifindex;
nhtype = api.nexthops[0].type;
- add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+ add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
if (add) {
/*
* The ADD message is actually an UPDATE and there is no
@@ -2101,8 +2086,7 @@ int bgp_zebra_dup_addr_detection(struct bgp *bgp)
return zclient_send_message(zclient);
}
-static int rule_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
{
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
@@ -2171,8 +2155,7 @@ static int rule_notify_owner(int command, struct zclient *zclient,
return 0;
}
-static int ipset_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ipset_notify_owner(ZAPI_CALLBACK_ARGS)
{
uint32_t unique;
enum zapi_ipset_notify_owner note;
@@ -2217,8 +2200,7 @@ static int ipset_notify_owner(int command, struct zclient *zclient,
return 0;
}
-static int ipset_entry_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ipset_entry_notify_owner(ZAPI_CALLBACK_ARGS)
{
uint32_t unique;
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
@@ -2275,8 +2257,7 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient,
return 0;
}
-static int iptable_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int iptable_notify_owner(ZAPI_CALLBACK_ARGS)
{
uint32_t unique;
enum zapi_iptable_notify_owner note;
@@ -2322,7 +2303,7 @@ static int iptable_notify_owner(int command, struct zclient *zclient,
/* this function is used to forge ip rule,
* - either for iptable/ipset using fwmark id
- * - or for sample ip rule command
+ * - or for sample ip rule cmd
*/
static void bgp_encode_pbr_rule_action(struct stream *s,
struct bgp_pbr_action *pbra,
@@ -2460,7 +2441,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
bgp_zebra_instance_register(bgp);
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, bgp->vrf_id);
/* tell label pool that zebra is connected */
bgp_lp_event_zebra_up();
@@ -2470,8 +2451,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
*/
}
-static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS)
{
esi_t esi;
struct bgp *bgp = NULL;
@@ -2504,8 +2484,7 @@ static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient,
return 0;
}
-static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS)
{
int filter = 0;
char buf[ETHER_ADDR_STRLEN];
@@ -2545,8 +2524,7 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
return 0;
}
-static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
vni_t vni;
@@ -2557,7 +2535,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
s = zclient->ibuf;
vni = stream_getl(s);
- if (command == ZEBRA_VNI_ADD) {
+ if (cmd == ZEBRA_VNI_ADD) {
vtep_ip.s_addr = stream_get_ipv4(s);
stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
mcast_grp.s_addr = stream_get_ipv4(s);
@@ -2569,11 +2547,11 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
- (command == ZEBRA_VNI_ADD) ? "add" : "del",
+ (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
vrf_id_to_name(vrf_id), vni,
vrf_id_to_name(tenant_vrf_id));
- if (command == ZEBRA_VNI_ADD)
+ if (cmd == ZEBRA_VNI_ADD)
return bgp_evpn_local_vni_add(
bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id,
tenant_vrf_id, mcast_grp);
@@ -2581,8 +2559,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
return bgp_evpn_local_vni_del(bgp, vni);
}
-static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
vni_t vni;
@@ -2605,7 +2582,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
&& ipa_len != IPV6_MAX_BYTELEN) {
flog_err(EC_BGP_MACIP_LEN,
"%u:Recv MACIP %s with invalid IP addr length %d",
- vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
ipa_len);
return -1;
}
@@ -2615,7 +2592,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
(ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6;
stream_get(&ip.ip.addr, s, ipa_len);
}
- if (command == ZEBRA_MACIP_ADD) {
+ if (cmd == ZEBRA_MACIP_ADD) {
flags = stream_getc(s);
seqnum = stream_getl(s);
} else {
@@ -2628,21 +2605,19 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u state %d",
- vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(&mac, buf, sizeof(buf)),
ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum,
state);
- if (command == ZEBRA_MACIP_ADD)
+ if (cmd == ZEBRA_MACIP_ADD)
return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip,
flags, seqnum);
else
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state);
}
-static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient,
- zebra_size_t length,
- vrf_id_t vrf_id)
+static void bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS)
{
struct stream *s = NULL;
struct bgp *bgp_vrf = NULL;
@@ -2682,11 +2657,7 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient,
}
}
-static void bgp_zebra_process_label_chunk(
- int cmd,
- struct zclient *zclient,
- zebra_size_t length,
- vrf_id_t vrf_id)
+static void bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS)
{
struct stream *s = NULL;
uint8_t response_keep;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index b2925cd512..2e648af1bb 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -812,9 +812,9 @@ int peer_cmp(struct peer *p1, struct peer *p2)
return sockunion_cmp(&p1->su, &p2->su);
}
-static unsigned int peer_hash_key_make(void *p)
+static unsigned int peer_hash_key_make(const void *p)
{
- struct peer *peer = p;
+ const struct peer *peer = p;
return sockunion_hash(&peer->su);
}
@@ -2232,6 +2232,8 @@ int peer_delete(struct peer *peer)
SET_FLAG(peer->flags, PEER_FLAG_DELETE);
+ bgp_bfd_deregister_peer(peer);
+
/* If this peer belongs to peer group, clear up the
relationship. */
if (peer->group) {
@@ -6186,8 +6188,15 @@ int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
/* Set configuration on peer. */
filter = &peer->filter[afi][safi];
- if (filter->map[direct].name)
+ if (filter->map[direct].name) {
+ /* If the neighbor is configured with the same route-map
+ * again then, ignore the duplicate configuration.
+ */
+ if (strcmp(filter->map[direct].name, name) == 0)
+ return 0;
+
XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
route_map_counter_decrement(filter->map[direct].map);
filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
filter->map[direct].map = route_map;
@@ -7550,12 +7559,6 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
vty_endframe(vty, " exit-address-family\n");
}
-/* clang-format off */
-#if CONFDATE > 20190517
-CPP_NOTICE("bgpd: remove 'bgp enforce-first-as' config migration from bgp_config_write")
-#endif
-/* clang-format on */
-
int bgp_config_write(struct vty *vty)
{
int write = 0;
@@ -7591,15 +7594,6 @@ int bgp_config_write(struct vty *vty)
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
continue;
- /* Migrate deprecated 'bgp enforce-first-as'
- * config to 'neighbor * enforce-first-as' configs
- */
- if (bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS)) {
- for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
- peer_flag_set(peer, PEER_FLAG_ENFORCE_FIRST_AS);
- bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS);
- }
-
/* Router bgp ASN */
vty_out(vty, "router bgp %u", bgp->as);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index b0f6567534..c600d9f3f3 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -348,7 +348,6 @@ struct bgp {
#define BGP_FLAG_MED_CONFED (1 << 3)
#define BGP_FLAG_NO_DEFAULT_IPV4 (1 << 4)
#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 5)
-#define BGP_FLAG_ENFORCE_FIRST_AS (1 << 6)
#define BGP_FLAG_COMPARE_ROUTER_ID (1 << 7)
#define BGP_FLAG_ASPATH_IGNORE (1 << 8)
#define BGP_FLAG_IMPORT_CHECK (1 << 9)
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
index 2220f0ed9a..cad33404fa 100644
--- a/bgpd/rfapi/bgp_rfapi_cfg.c
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -2208,24 +2208,6 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused)
vnc_zlog_debug_verbose("%s done", __func__);
}
-#if 0 /* superseded */
-static void vnc_routemap_event(route_map_event_t type, /* ignored */
- const char *rmap_name) /* ignored */
-{
- struct listnode *mnode, *mnnode;
- struct bgp *bgp;
-
- vnc_zlog_debug_verbose("%s(event type=%d)", __func__, type);
- if (bm->bgp == NULL) /* may be called during cleanup */
- return;
-
- for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp))
- vnc_routemap_update(bgp, rmap_name);
-
- vnc_zlog_debug_verbose("%s: done", __func__);
-}
-#endif
-
/*-------------------------------------------------------------------------
* nve-group
*-----------------------------------------------------------------------*/
@@ -3699,10 +3681,6 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import,
void bgp_rfapi_cfg_init(void)
{
- /* main bgpd code does not use this hook, but vnc does */
- /* superseded by bgp_route_map_process_update_cb() */
- /* bgp_route_map_event_hook_add(vnc_routemap_event); */
-
install_node(&bgp_vnc_defaults_node, NULL);
install_node(&bgp_vnc_nve_group_node, NULL);
install_node(&bgp_vrf_policy_node, NULL);
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index 568f8d68e8..ad0900c2b8 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -375,41 +375,14 @@ int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime)
}
/*
- * Extract the tunnel type from the extended community
- */
-int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type)
-{
- *type = BGP_ENCAP_TYPE_MPLS; /* default to MPLS */
- if (attr && attr->ecommunity) {
- struct ecommunity *ecom = attr->ecommunity;
- int i;
-
- for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE);
- i += ECOMMUNITY_SIZE) {
- uint8_t *ep;
-
- ep = ecom->val + i;
- if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE
- && ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
- *type = (ep[6] << 8) + ep[7];
- return 0;
- }
- }
- }
-
- return ENOENT;
-}
-
-
-/*
* Look for UN address in Encap attribute
*/
int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p)
{
struct bgp_attr_encap_subtlv *pEncap;
- bgp_encap_types tun_type;
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/
- rfapiGetTunnelType(attr, &tun_type);
+ bgp_attr_extcom_tunnel_type(attr, &tun_type);
if (tun_type == BGP_ENCAP_TYPE_MPLS) {
if (!p)
return 0;
@@ -1350,7 +1323,7 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix,
}
if (bpi->attr) {
- bgp_encap_types tun_type;
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/
new->prefix.cost = rfapiRfpCost(bpi->attr);
struct bgp_attr_encap_subtlv *pEncap;
@@ -1390,7 +1363,7 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix,
}
}
- rfapiGetTunnelType(bpi->attr, &tun_type);
+ bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
if (tun_type == BGP_ENCAP_TYPE_MPLS) {
struct prefix p;
/* MPLS carries UN address in next hop */
@@ -2011,11 +1984,14 @@ static void rfapiBgpInfoAttachSorted(struct agg_node *rn,
for (prev = NULL, next = rn->info; next;
prev = next, next = next->next) {
+ enum bgp_path_selection_reason reason;
+
if (!bgp
|| (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED)
&& CHECK_FLAG(next->flags, BGP_PATH_REMOVED))
|| bgp_path_info_cmp_compatible(bgp, info_new, next,
- pfx_buf, afi, safi)
+ pfx_buf, afi, safi,
+ &reason)
== -1) { /* -1 if 1st is better */
break;
}
diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h
index 87d9a32f67..ff1cf7ef42 100644
--- a/bgpd/rfapi/rfapi_private.h
+++ b/bgpd/rfapi/rfapi_private.h
@@ -306,8 +306,6 @@ extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str,
extern int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime);
-extern int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type);
-
extern int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p);
extern int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp);
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index ea82c254bc..46161b4f38 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -1020,7 +1020,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream,
struct prefix pfx_vn;
uint8_t cost;
uint32_t lifetime;
- bgp_encap_types tun_type;
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/
char buf_pfx[BUFSIZ];
char buf_ntop[BUFSIZ];
@@ -1055,7 +1055,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream,
BUFSIZ));
}
- rfapiGetTunnelType(bpi->attr, &tun_type);
+ bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
/*
* VN addr
*/
diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c
index b08e922962..481500dfb4 100644
--- a/bgpd/rfapi/vnc_zebra.c
+++ b/bgpd/rfapi/vnc_zebra.c
@@ -344,8 +344,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type)
*
* Assumes 1 nexthop
*/
-static int vnc_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
int add;
@@ -357,7 +356,7 @@ static int vnc_zebra_read_route(int command, struct zclient *zclient,
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
return 0;
- add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+ add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
if (add)
vnc_redistribute_add(&api.prefix, api.metric, api.type);
else
diff --git a/configure.ac b/configure.ac
index 9ae196fcb1..c228ff0c91 100755
--- a/configure.ac
+++ b/configure.ac
@@ -7,7 +7,7 @@
##
AC_PREREQ([2.60])
-AC_INIT([frr], [7.1-dev], [https://github.com/frrouting/frr/issues])
+AC_INIT([frr], [7.2-dev], [https://github.com/frrouting/frr/issues])
PACKAGE_URL="https://frrouting.org/"
AC_SUBST([PACKAGE_URL])
PACKAGE_FULLNAME="FRRouting"
@@ -126,12 +126,15 @@ dnl Check CC and friends
dnl --------------------
dnl note orig_cflags is also used further down
orig_cflags="$CFLAGS"
+orig_cxxflags="$CXXFLAGS"
AC_LANG([C])
AC_PROG_CC
AC_PROG_CPP
+AC_PROG_CXX
AM_PROG_CC_C_O
dnl remove autoconf default "-g -O2"
CFLAGS="$orig_cflags"
+CXXFLAGS="$orig_cxxflags"
AC_PROG_CC_C99
dnl NB: see C11 below
@@ -219,9 +222,12 @@ elif test "x${enable_dev_build}" = "xyes"; then
AC_C_FLAG([-O0])
fi
if test "x${enable_lua}" = "xyes"; then
- AC_CHECK_LIB([lua], [lua_newstate],
- [LIBS="$LIBS -llua"])
- AC_DEFINE([HAVE_LUA], [1], [Lua enabled for development])
+ AX_PROG_LUA([5.3])
+ AX_LUA_HEADERS
+ AX_LUA_LIBS([
+ AC_DEFINE([HAVE_LUA], [1], [Have support for Lua interpreter])
+ LIBS="$LIBS $LUA_LIB"
+ ])
fi
else
if test "x${enable_lua}" = "xyes"; then
@@ -437,6 +443,8 @@ AC_ARG_ENABLE([fabricd],
AS_HELP_STRING([--disable-fabricd], [do not build fabricd]))
AC_ARG_ENABLE([bgp-announce],
AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))
+AC_ARG_ENABLE([vrrpd],
+ AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd]))
AC_ARG_ENABLE([bgp-vnc],
AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support]))
AC_ARG_ENABLE([snmp],
@@ -447,6 +455,8 @@ AC_ARG_ENABLE([confd],
AS_HELP_STRING([--enable-confd=ARG], [enable confd integration]))
AC_ARG_ENABLE([sysrepo],
AS_HELP_STRING([--enable-sysrepo], [enable sysrepo integration]))
+AC_ARG_ENABLE([grpc],
+ AS_HELP_STRING([--enable-grpc], [enable the gRPC northbound plugin]))
AC_ARG_ENABLE([zeromq],
AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)]))
AC_ARG_WITH([libpam],
@@ -926,6 +936,80 @@ AC_CHECK_HEADERS([pthread_np.h],,, [
])
AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np])
+needsync=true
+
+AS_IF([$needsync], [
+ dnl Linux
+ AC_MSG_CHECKING([for Linux futex() support])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <linux/futex.h>
+
+int main(void);
+],
+[
+{
+ return syscall(SYS_futex, NULL, FUTEX_WAIT, 0, NULL, NULL, 0);
+}
+])], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_SYNC_LINUX_FUTEX,,Have Linux futex support)
+ needsync=false
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AS_IF([$needsync], [
+ dnl FreeBSD
+ AC_MSG_CHECKING([for FreeBSD _umtx_op() support])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/umtx.h>
+int main(void);
+],
+[
+{
+ return _umtx_op(NULL, UMTX_OP_WAIT_UINT, 0, NULL, NULL);
+}
+])], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_SYNC_UMTX_OP,,Have FreeBSD _umtx_op() support)
+ needsync=false
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AS_IF([$needsync], [
+ dnl OpenBSD patch (not upstream at the time of writing this)
+ dnl https://marc.info/?l=openbsd-tech&m=147299508409549&w=2
+ AC_MSG_CHECKING([for OpenBSD futex() support])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([
+#include <sys/futex.h>
+int main(void);
+],
+[
+{
+ return futex(NULL, FUTEX_WAIT, 0, NULL, NULL, 0);
+}
+])], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_SYNC_OPENBSD_FUTEX,,Have OpenBSD futex support)
+ needsync=false
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+
dnl Utility macro to avoid retyping includes all the time
m4_define([FRR_INCLUDES],
[#ifdef SUNOS_5
@@ -1520,6 +1604,7 @@ AM_CONDITIONAL([PBRD], [test "${enable_pbrd}" != "no"])
AM_CONDITIONAL([SHARPD], [test "${enable_sharpd}" = "yes"])
AM_CONDITIONAL([STATICD], [test "${enable_staticd}" != "no"])
AM_CONDITIONAL([FABRICD], [test "${enable_fabricd}" != "no"])
+AM_CONDITIONAL([VRRPD], [test "${enable_vrrpd}" != "no"])
if test "${enable_bgp_announce}" = "no";then
AC_DEFINE([DISABLE_BGP_ANNOUNCE], [1], [Disable BGP installation to zebra])
@@ -1616,24 +1701,6 @@ AC_CHECK_MEMBER([struct lyd_node.priv], [], [
], [[#include <libyang/libyang.h>]])
CFLAGS="$ac_cflags_save"
-ac_libs_save="$LIBS"
-LIBS="$LIBS $LIBYANG_LIBS"
-AC_CHECK_FUNC([ly_register_types], [
- libyang_ext_builtin=true
- AC_DEFINE([LIBYANG_EXT_BUILTIN], [1], [have ly_register_types()])
-], [
- libyang_ext_builtin=false
- AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====])
- AC_MSG_WARN([The available version of libyang does not seem to support])
- AC_MSG_WARN([built-in YANG extension modules. This will cause "make check"])
- AC_MSG_WARN([to fail and may create installation and version mismatch issues.])
- AC_MSG_WARN([Support for the old mechanism will be removed at some point.])
- AC_MSG_WARN([Please update libyang to version 0.16.74 or newer.])
- AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====])
-])
-AM_CONDITIONAL([LIBYANG_EXT_BUILTIN], [$libyang_ext_builtin])
-LIBS="$ac_libs_save"
-
dnl ---------------
dnl configuration rollbacks
dnl ---------------
@@ -1679,6 +1746,25 @@ fi
AM_CONDITIONAL([SYSREPO], [test "x$enable_sysrepo" = "xyes"])
dnl ---------------
+dnl gRPC
+dnl ---------------
+if test "$enable_grpc" = "yes"; then
+ PKG_CHECK_MODULES([GRPC], [grpc grpc++ protobuf], [
+ AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false])
+ if test "$PROTOC" = "/bin/false"; then
+ AC_MSG_FAILURE([grpc requested but protoc not found.])
+ fi
+
+ AC_DEFINE([HAVE_GRPC], [1], [Enable the gRPC northbound plugin])
+ GRPC=true
+ ], [
+ GRPC=false
+ AC_MSG_ERROR([grpc/grpc++ were not found on your system.])
+ ])
+fi
+AM_CONDITIONAL([GRPC], [test "x$enable_grpc" = "xyes"])
+
+dnl ---------------
dnl math
dnl ---------------
AC_SEARCH_LIBS([sqrt], [m])
diff --git a/debian/copyright b/debian/copyright
index 61d87260d8..d1f28a65a2 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -324,19 +324,6 @@ Copyright:
Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
-Files: isisd/dict.*
-Copyright: Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
-License: custom-BSD-like
- All rights are reserved by the author, with the following exceptions:
- Permission is granted to freely reproduce and distribute this software,
- possibly in exchange for a fee, provided that this copyright notice appears
- intact. Permission is also granted to adapt this software to produce
- derivative works, as long as the modified versions carry this copyright
- notice and additional notices stating that the work has been modified.
- This source code may be translated into executable form and incorporated
- into proprietary software; there is no requirement for such software to
- contain a copyright notice related to this source.
-
Files: qpb/qpb.proto fpm/fpm.proto
License: ISC
Copyright: Copyright (C) 2016 Sproute Networks, Inc.
diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst
index 9c6e48a6f8..a26d055bc2 100644
--- a/doc/developer/building-frr-for-debian8.rst
+++ b/doc/developer/building-frr-for-debian8.rst
@@ -17,7 +17,8 @@ Add packages:
sudo apt-get install git autoconf automake libtool make \
libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \
- libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev
+ libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \
+ libsnmp-dev
Install newer pytest (>3.0) from pip
diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst
index 39e7488cd7..2c5a9681af 100644
--- a/doc/developer/building-frr-for-debian9.rst
+++ b/doc/developer/building-frr-for-debian9.rst
@@ -11,7 +11,7 @@ Add packages:
sudo apt-get install git autoconf automake libtool make \
libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \
libc-ares-dev python3-dev python-pytest python3-sphinx build-essential \
- libsystemd-dev
+ libsnmp-dev libsystemd-dev
.. include:: building-libyang.rst
diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst
index 1320bda577..8bdc2b9c76 100644
--- a/doc/developer/building-frr-for-ubuntu1804.rst
+++ b/doc/developer/building-frr-for-ubuntu1804.rst
@@ -104,6 +104,10 @@ And load the kernel modules on the running system:
sudo modprobe mpls-router mpls-iptunnel
+If the above command returns an error, you may need to install the appropriate
+or latest linux-modules-extra-<kernel-version>-generic package. For example
+``apt-get install linux-modules-extra-`uname -r`-generic``
+
Enable MPLS Forwarding
""""""""""""""""""""""
diff --git a/doc/developer/library.rst b/doc/developer/library.rst
index 77b2f229b7..4ba0c0ebc6 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -7,8 +7,9 @@ Library Facilities (libfrr)
.. toctree::
:maxdepth: 2
- logging
memtypes
+ lists
+ logging
hooks
cli
modules
diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst
new file mode 100644
index 0000000000..fc47a67e42
--- /dev/null
+++ b/doc/developer/lists.rst
@@ -0,0 +1,626 @@
+List implementations
+====================
+
+.. note::
+
+ The term *list* is used generically for lists, skiplists, trees and hash
+ tables in this document.
+
+Common list interface
+---------------------
+
+FRR includes a set of list-like data structure implementations with abstracted
+common APIs. The purpose of this is easily allow swapping out one
+data structure for another while also making the code easier to read and write.
+There is one API for unsorted lists and a similar but not identical API for
+sorted lists - and heaps use a middle ground of both.
+
+For unsorted lists, the following implementations exist:
+
+- single-linked list with tail pointer (e.g. STAILQ in BSD)
+
+- double-linked list
+
+- atomic single-linked list with tail pointer
+
+
+Being partially sorted, the oddball structure:
+
+- an 8-ary heap
+
+
+For sorted lists, these data structures are implemented:
+
+- single-linked list
+
+- atomic single-linked list
+
+- skiplist
+
+- red-black tree (based on OpenBSD RB_TREE)
+
+- hash table (note below)
+
+Except for hash tables, each of the sorted data structures has a variant with
+unique and non-unique list items. Hash tables always require unique items
+and mostly follow the "sorted" API but use the hash value as sorting
+key. Also, iterating while modifying does not work with hash tables.
+Conversely, the heap always has non-unique items, but iterating while modifying
+doesn't work either.
+
+
+The following sorted structures are likely to be implemented at some point
+in the future:
+
+- atomic skiplist
+
+- atomic hash table (note below)
+
+
+The APIs are all designed to be as type-safe as possible. This means that
+there will be a compiler warning when an item doesn't match the list, or
+the return value has a different type, or other similar situations. **You
+should never use casts with these APIs.** If a cast is neccessary in relation
+to these APIs, there is probably something wrong with the overall design.
+
+Only the following pieces use dynamically allocated memory:
+
+- the hash table itself is dynamically grown and shrunk
+
+- skiplists store up to 4 next pointers inline but will dynamically allocate
+ memory to hold an item's 5th up to 16th next pointer (if they exist)
+
+- the heap uses a dynamically grown and shrunk array of items
+
+Cheat sheet
+-----------
+
+Available types:
+
+::
+
+ DECLARE_LIST
+ DECLARE_ATOMLIST
+ DECLARE_DLIST
+
+ DECLARE_HEAP
+
+ DECLARE_SORTLIST_UNIQ
+ DECLARE_SORTLIST_NONUNIQ
+ DECLARE_ATOMLIST_UNIQ
+ DECLARE_ATOMLIST_NONUNIQ
+ DECLARE_SKIPLIST_UNIQ
+ DECLARE_SKIPLIST_NONUNIQ
+ DECLARE_RBTREE_UNIQ
+ DECLARE_RBTREE_NONUNIQ
+
+ DECLARE_HASH
+
+Functions provided:
+
++------------------------------------+------+------+------+---------+------------+
+| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ |
++====================================+======+======+======+=========+============+
+| _init, _fini | yes | yes | yes | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+| _first, _next, _next_safe | yes | yes | yes | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- |
++------------------------------------+------+------+------+---------+------------+
+| _add | -- | yes | yes | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+| _del, _pop | yes | yes | yes | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+| _find | -- | -- | yes | yes | -- |
++------------------------------------+------+------+------+---------+------------+
+| _find_lt, _find_gteq | -- | -- | -- | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+| use with frr_each() macros | yes | yes | yes | yes | yes |
++------------------------------------+------+------+------+---------+------------+
+
+
+
+Datastructure type setup
+------------------------
+
+Each of the data structures has a ``PREDECL_*`` and a ``DECLARE_*`` macro to
+set up an "instantiation" of the list. This works somewhat similar to C++
+templating, though much simpler.
+
+**In all following text, the Z prefix is replaced with a name choosen
+for the instance of the datastructure.**
+
+The common setup pattern will look like this:
+
+.. code-block:: c
+
+ #include <typesafe.h>
+
+ PREDECL_XXX(Z)
+ struct item {
+ int otherdata;
+ struct Z_item mylistitem;
+ }
+
+ struct Z_head mylisthead;
+
+ /* unsorted: */
+ DECLARE_XXX(Z, struct item, mylistitem)
+
+ /* sorted, items that compare as equal cannot be added to list */
+ int compare_func(const struct item *a, const struct item *b);
+ DECLARE_XXX_UNIQ(Z, struct item, mylistitem, compare_func)
+
+ /* sorted, items that compare as equal can be added to list */
+ int compare_func(const struct item *a, const struct item *b);
+ DECLARE_XXX_NONUNIQ(Z, struct item, mylistitem, compare_func)
+
+ /* hash tables: */
+ int compare_func(const struct item *a, const struct item *b);
+ uint32_t hash_func(const struct item *a);
+ DECLARE_XXX(Z, struct item, mylistitem, compare_func, hash_func)
+
+``XXX`` is replaced with the name of the data structure, e.g. ``SKIPLIST``
+or ``ATOMLIST``. The ``DECLARE_XXX`` invocation can either occur in a `.h`
+file (if the list needs to be accessed from several C files) or it can be
+placed in a `.c` file (if the list is only accessed from that file.) The
+``PREDECL_XXX`` invocation defines the ``struct Z_item`` and ``struct
+Z_head`` types and must therefore occur before these are used.
+
+To switch between compatible data structures, only these two lines need to be
+changes. To switch to a data structure with a different API, some source
+changes are necessary.
+
+Common iteration macros
+-----------------------
+
+The following iteration macros work across all data structures:
+
+.. c:function:: frr_each(Z, &head, item)
+
+ Equivalent to:
+
+ .. code-block:: c
+
+ for (item = Z_first(&head); item; item = Z_next(&head, item))
+
+ Note that this will fail if the list is modified while being iterated
+ over.
+
+.. c:function:: frr_each_safe(Z, &head, item)
+
+ Same as the previous, but the next element is pre-loaded into a "hidden"
+ variable (named ``Z_safe``.) Equivalent to:
+
+ .. code-block:: c
+
+ for (item = Z_first(&head); item; item = next) {
+ next = Z_next_safe(&head, item);
+ ...
+ }
+
+ .. warning::
+
+ Iterating over hash tables while adding or removing items is not
+ possible. The iteration position will be corrupted when the hash
+ tables is resized while iterating. This will cause items to be
+ skipped or iterated over twice.
+
+.. c:function:: frr_each_from(Z, &head, item, from)
+
+ Iterates over the list, starting at item ``from``. This variant is "safe"
+ as in the previous macro. Equivalent to:
+
+ .. code-block:: c
+
+ for (item = from; item; item = from) {
+ from = Z_next_safe(&head, item);
+ ...
+ }
+
+ .. note::
+
+ The ``from`` variable is written to. This is intentional - you can
+ resume iteration after breaking out of the loop by keeping the ``from``
+ value persistent and reusing it for the next loop.
+
+Common API
+----------
+
+The following documentation assumes that a list has been defined using
+``Z`` as the name, and ``itemtype`` being the type of the list items (e.g.
+``struct item``.)
+
+.. c:function:: void Z_init(struct Z_head *)
+
+ Initializes the list for use. For most implementations, this just sets
+ some values. Hash tables are the only implementation that allocates
+ memory in this call.
+
+.. c:function:: void Z_fini(struct Z_head *)
+
+ Reverse the effects of :c:func:`Z_init()`. The list must be empty
+ when this function is called.
+
+ .. warning::
+
+ This function may ``assert()`` if the list is not empty.
+
+.. c:function:: size_t Z_count(struct Z_head *)
+
+ Returns the number of items in a structure. All structures store a
+ counter in their `Z_head` so that calling this function completes
+ in O(1).
+
+ .. note::
+
+ For atomic lists with concurrent access, the value will already be
+ outdated by the time this function returns and can therefore only be
+ used as an estimate.
+
+.. c:function:: itemtype *Z_first(struct Z_head *)
+
+ Returns the first item in the structure, or ``NULL`` if the structure is
+ empty. This is O(1) for all data structures except red-black trees
+ where it is O(log n).
+
+.. c:function:: itemtype *Z_pop(struct Z_head *)
+
+ Remove and return the first item in the structure, or ``NULL`` if the
+ structure is empty. Like :c:func:`Z_first`, this is O(1) for all
+ data structures except red-black trees where it is O(log n) again.
+
+ This function can be used to build queues (with unsorted structures) or
+ priority queues (with sorted structures.)
+
+ Another common pattern is deleting all list items:
+
+ .. code-block:: c
+
+ while ((item = Z_pop(head)))
+ item_free(item);
+
+ .. note::
+
+ This function can - and should - be used with hash tables. It is not
+ affected by the "modification while iterating" problem. To remove
+ all items from a hash table, use the loop demonstrated above.
+
+.. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev)
+
+ Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is
+ the last item.
+
+ .. warning::
+
+ ``prev`` must not be ``NULL``! Use :c:func:`Z_next_safe()` if
+ ``prev`` might be ``NULL``.
+
+.. c:function:: itemtype *Z_next_safe(struct Z_head *, itemtype *prev)
+
+ Same as :c:func:`Z_next()`, except that ``NULL`` is returned if
+ ``prev`` is ``NULL``.
+
+.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item)
+
+ Remove ``item`` from the list and return it.
+
+ .. note::
+
+ This function's behaviour is undefined if ``item`` is not actually
+ on the list. Some structures return ``NULL`` in this case while others
+ return ``item``. The function may also call ``assert()`` (but most
+ don't.)
+
+.. todo::
+
+ ``Z_del_after()`` / ``Z_del_hint()``?
+
+API for unsorted structures
+---------------------------
+
+Since the insertion position is not pre-defined for unsorted data, there
+are several functions exposed to insert data:
+
+.. note::
+
+ ``item`` must not be ``NULL`` for any of the following functions.
+
+.. c:function:: DECLARE_XXX(Z, type, field)
+
+ :param listtype XXX: ``LIST``, ``DLIST`` or ``ATOMLIST`` to select a data
+ structure implementation.
+ :param token Z: Gives the name prefix that is used for the functions
+ created for this instantiation. ``DECLARE_XXX(foo, ...)``
+ gives ``struct foo_item``, ``foo_add_head()``, ``foo_count()``, etc. Note
+ that this must match the value given in ``PREDECL_XXX(foo)``.
+ :param typename type: Specifies the data type of the list items, e.g.
+ ``struct item``. Note that ``struct`` must be added here, it is not
+ automatically added.
+ :param token field: References a struct member of ``type`` that must be
+ typed as ``struct foo_item``. This struct member is used to
+ store "next" pointers or other data structure specific data.
+
+.. c:function:: void Z_add_head(struct Z_head *, itemtype *item)
+
+ Insert an item at the beginning of the structure, before the first item.
+ This is an O(1) operation for non-atomic lists.
+
+.. c:function:: void Z_add_tail(struct Z_head *, itemtype *item)
+
+ Insert an item at the end of the structure, after the last item.
+ This is also an O(1) operation for non-atomic lists.
+
+.. c:function:: void Z_add_after(struct Z_head *, itemtype *after, itemtype *item)
+
+ Insert ``item`` behind ``after``. If ``after`` is ``NULL``, the item is
+ inserted at the beginning of the list as with :c:func:`Z_add_head`.
+ This is also an O(1) operation for non-atomic lists.
+
+ A common pattern is to keep a "previous" pointer around while iterating:
+
+ .. code-block:: c
+
+ itemtype *prev = NULL, *item;
+
+ frr_each_safe(Z, head, item) {
+ if (something) {
+ Z_add_after(head, prev, item);
+ break;
+ }
+ prev = item;
+ }
+
+ .. todo::
+
+ maybe flip the order of ``item`` & ``after``?
+ ``Z_add_after(head, item, after)``
+
+API for sorted structures
+-------------------------
+
+Sorted data structures do not need to have an insertion position specified,
+therefore the insertion calls are different from unsorted lists. Also,
+sorted lists can be searched for a value.
+
+.. c:function:: DECLARE_XXX_UNIQ(Z, type, field, compare_func)
+
+ :param listtype XXX: One of the following:
+ ``SORTLIST`` (single-linked sorted list), ``SKIPLIST`` (skiplist),
+ ``RBTREE`` (RB-tree) or ``ATOMSORT`` (atomic single-linked list).
+ :param token Z: Gives the name prefix that is used for the functions
+ created for this instantiation. ``DECLARE_XXX(foo, ...)``
+ gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note
+ that this must match the value given in ``PREDECL_XXX(foo)``.
+ :param typename type: Specifies the data type of the list items, e.g.
+ ``struct item``. Note that ``struct`` must be added here, it is not
+ automatically added.
+ :param token field: References a struct member of ``type`` that must be
+ typed as ``struct foo_item``. This struct member is used to
+ store "next" pointers or other data structure specific data.
+ :param funcptr compare_func: Item comparison function, must have the
+ following function signature:
+ ``int function(const itemtype *, const itemtype*)``. This function
+ may be static if the list is only used in one file.
+
+.. c:function:: DECLARE_XXX_NONUNIQ(Z, type, field, compare_func)
+
+ Same as above, but allow adding multiple items to the list that compare
+ as equal in ``compare_func``. Ordering between these items is undefined
+ and depends on the list implementation.
+
+.. c:function:: itemtype *Z_add(struct Z_head *, itemtype *item)
+
+ Insert an item at the appropriate sorted position. If another item exists
+ in the list that compares as equal (``compare_func()`` == 0), ``item`` is
+ not inserted into the list and the already-existing item in the list is
+ returned. Otherwise, on successful insertion, ``NULL`` is returned.
+
+ For ``_NONUNIQ`` lists, this function always returns NULL since ``item``
+ can always be successfully added to the list.
+
+.. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref)
+
+ Search the list for an item that compares equal to ``ref``. If no equal
+ item is found, return ``NULL``.
+
+ This function is likely used with a temporary stack-allocated value for
+ ``ref`` like so:
+
+ .. code-block:: c
+
+ itemtype searchfor = { .foo = 123 };
+
+ itemtype *item = Z_find(head, &searchfor);
+
+ .. note::
+
+ The ``Z_find()`` function is only available for lists that contain
+ unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a list
+ containing non-unique items, more than one item may compare as equal to
+ the item that is searched for.
+
+.. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref)
+
+ Search the list for an item that compares greater or equal to
+ ``ref``. See :c:func:`Z_find()` above.
+
+.. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref)
+
+ Search the list for an item that compares less than
+ ``ref``. See :c:func:`Z_find()` above.
+
+
+API for hash tables
+-------------------
+
+.. c:function:: DECLARE_XXX(Z, type, field, compare_func, hash_func)
+
+ :param listtype XXX: Only ``HASH`` is currently available.
+ :param token Z: Gives the name prefix that is used for the functions
+ created for this instantiation. ``DECLARE_XXX(foo, ...)``
+ gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note
+ that this must match the value given in ``PREDECL_XXX(foo)``.
+ :param typename type: Specifies the data type of the list items, e.g.
+ ``struct item``. Note that ``struct`` must be added here, it is not
+ automatically added.
+ :param token field: References a struct member of ``type`` that must be
+ typed as ``struct foo_item``. This struct member is used to
+ store "next" pointers or other data structure specific data.
+ :param funcptr compare_func: Item comparison function, must have the
+ following function signature:
+ ``int function(const itemtype *, const itemtype*)``. This function
+ may be static if the list is only used in one file. For hash tables,
+ this function is only used to check for equality, the ordering is
+ ignored.
+ :param funcptr hash_func: Hash calculation function, must have the
+ following function signature:
+ ``uint32_t function(const itemtype *)``. The hash value for items
+ stored in a hash table is cached in each item, so this value need not
+ be cached by the user code.
+
+ .. warning::
+
+ Items that compare as equal cannot be inserted. Refer to the notes
+ about sorted structures in the previous section.
+
+.. c:function:: void Z_init_size(struct Z_head *, size_t size)
+
+ Same as :c:func:`Z_init()` but preset the minimum hash table to
+ ``size``.
+
+Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with
+the same semantics as noted above. :c:func:`Z_find_gteq()` and
+:c:func:`Z_find_lt()` are **not** provided for hash tables.
+
+
+API for heaps
+-------------
+
+Heaps provide the same API as the sorted data structures, except:
+
+* none of the find functions (:c:func:`Z_find()`, :c:func:`Z_find_gteq()`
+ or :c:func:`Z_find_lt()`) are available.
+* iterating over the heap yields the items in semi-random order, only the
+ first item is guaranteed to be in order and actually the "lowest" item
+ on the heap. Being a heap, only the rebalancing performed on removing the
+ first item (either through :c:func:`Z_pop()` or :c:func:`Z_del()`) causes
+ the new lowest item to bubble up to the front.
+* all heap modifications are O(log n). However, cacheline efficiency and
+ latency is likely quite a bit better than with other data structures.
+
+Atomic lists
+------------
+
+`atomlist.h` provides an unsorted and a sorted atomic single-linked list.
+Since atomic memory accesses can be considerably slower than plain memory
+accessses (depending on the CPU type), these lists should only be used where
+neccessary.
+
+The following guarantees are provided regarding concurrent access:
+
+- the operations are lock-free but not wait-free.
+
+ Lock-free means that it is impossible for all threads to be blocked. Some
+ thread will always make progress, regardless of what other threads do. (This
+ even includes a random thread being stopped by a debugger in a random
+ location.)
+
+ Wait-free implies that the time any single thread might spend in one of the
+ calls is bounded. This is not provided here since it is not normally
+ relevant to practical operations. What this means is that if some thread is
+ hammering a particular list with requests, it is possible that another
+ thread is blocked for an extended time. The lock-free guarantee still
+ applies since the hammering thread is making progress.
+
+- without a RCU mechanism in place, the point of contention for atomic lists
+ is memory deallocation. As it is, **a rwlock is required for correct
+ operation**. The *read* lock must be held for all accesses, including
+ reading the list, adding items to the list, and removing items from the
+ list. The *write* lock must be acquired and released before deallocating
+ any list element. If this is not followed, an use-after-free can occur
+ as a MT race condition when an element gets deallocated while another
+ thread is accessing the list.
+
+ .. note::
+
+ The *write* lock does not need to be held for deleting items from the
+ list, and there should not be any instructions between the
+ ``pthread_rwlock_wrlock`` and ``pthread_rwlock_unlock``. The write lock
+ is used as a sequence point, not as an exclusion mechanism.
+
+- insertion operations are always safe to do with the read lock held.
+ Added items are immediately visible after the insertion call returns and
+ should not be touched anymore.
+
+- when removing a *particular* (pre-determined) item, the caller must ensure
+ that no other thread is attempting to remove that same item. If this cannot
+ be guaranteed by architecture, a separate lock might need to be added.
+
+- concurrent `pop` calls are always safe to do with only the read lock held.
+ This does not fall under the previous rule since the `pop` call will select
+ the next item if the first is already being removed by another thread.
+
+ **Deallocation locking still applies.** Assume another thread starts
+ reading the list, but gets task-switched by the kernel while reading the
+ first item. `pop` will happily remove and return that item. If it is
+ deallocated without acquiring and releasing the write lock, the other thread
+ will later resume execution and try to access the now-deleted element.
+
+- the list count should be considered an estimate. Since there might be
+ concurrent insertions or removals in progress, it might already be outdated
+ by the time the call returns. No attempt is made to have it be correct even
+ for a nanosecond.
+
+Overall, atomic lists are well-suited for MT queues; concurrent insertion,
+iteration and removal operations will work with the read lock held.
+
+Code snippets
+^^^^^^^^^^^^^
+
+Iteration:
+
+.. code-block:: c
+
+ struct item *i;
+
+ pthread_rwlock_rdlock(&itemhead_rwlock);
+ frr_each(itemlist, &itemhead, i) {
+ /* lock must remain held while iterating */
+ ...
+ }
+ pthread_rwlock_unlock(&itemhead_rwlock);
+
+Head removal (pop) and deallocation:
+
+.. code-block:: c
+
+ struct item *i;
+
+ pthread_rwlock_rdlock(&itemhead_rwlock);
+ i = itemlist_pop(&itemhead);
+ pthread_rwlock_unlock(&itemhead_rwlock);
+
+ /* i might still be visible for another thread doing an
+ * frr_each() (but won't be returned by another pop()) */
+ ...
+
+ pthread_rwlock_wrlock(&itemhead_rwlock);
+ pthread_rwlock_unlock(&itemhead_rwlock);
+ /* i now guaranteed to be gone from the list.
+ * note nothing between wrlock() and unlock() */
+ XFREE(MTYPE_ITEM, i);
+
+FRR lists
+---------
+
+.. TODO::
+
+ document
+
+BSD lists
+---------
+
+.. TODO::
+
+ refer to external docs
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 7ae48881ab..996f12d47f 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -30,6 +30,7 @@ dev_RSTFILES = \
doc/developer/include-compile.rst \
doc/developer/index.rst \
doc/developer/library.rst \
+ doc/developer/lists.rst \
doc/developer/logging.rst \
doc/developer/maintainer-release-build.rst \
doc/developer/memtypes.rst \
@@ -39,7 +40,7 @@ dev_RSTFILES = \
doc/developer/ospf-sr.rst \
doc/developer/ospf.rst \
doc/developer/packaging-debian.rst \
- doc/developer/packaging-redhat.rst
+ doc/developer/packaging-redhat.rst \
doc/developer/packaging.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 605b9c9a0c..09f12ec436 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -105,6 +105,8 @@ Execute all tests with output to console
py.test -s -v --tb=no
+The above command must be executed from inside the topotests directory.
+
All test\_\* scripts in subdirectories are detected and executed (unless
disabled in ``pytest.ini`` file).
@@ -119,6 +121,13 @@ Execute single test
cd test_to_be_run
./test_to_be_run.py
+For example, and assuming you are inside the frr directory:
+
+.. code:: shell
+
+ cd tests/topotests/bgp_l3vpn_to_bgp_vrf
+ ./test_bgp_l3vpn_to_bgp_vrf.py
+
For further options, refer to pytest documentation.
Test will set exit code which can be used with ``git bisect``.
@@ -180,13 +189,12 @@ If found, then this is added with context (calling test) to
Compiling for GCC AddressSanitizer requires to use ``gcc`` as a linker as well
(instead of ``ld``). Here is a suggest way to compile frr with AddressSanitizer
-for ``stable/3.0`` branch:
+for ``master`` branch:
.. code:: shell
git clone https://github.com/FRRouting/frr.git
cd frr
- git checkout stable/3.0
./bootstrap.sh
export CC=gcc
export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
@@ -199,7 +207,8 @@ for ``stable/3.0`` branch:
--enable-exampledir=/usr/lib/frr/examples \
--with-moduledir=/usr/lib/frr/modules \
--enable-multipath=0 --enable-rtadv \
- --enable-tcp-zebra --enable-fpm --enable-pimd
+ --enable-tcp-zebra --enable-fpm --enable-pimd \
+ --enable-sharpd
make
sudo make install
# Create symlink for vtysh, so topotest finds it in /usr/lib/frr
diff --git a/doc/extra/spelling_wordlist.txt b/doc/extra/spelling_wordlist.txt
index 2944592962..271f5e49f1 100644
--- a/doc/extra/spelling_wordlist.txt
+++ b/doc/extra/spelling_wordlist.txt
@@ -80,6 +80,9 @@ IP
iptables
ipv
IPv
+IPvX
+IPv4
+IPv6
isis
isisd
lan
@@ -99,6 +102,8 @@ LSAs
Masaki
Mbit
Mbits
+macvlan
+macvlans
mib
motd
mpls
@@ -227,6 +232,7 @@ VN
VNC
vrf
vrfs
+vrrp
vty
Vty
vtysh
diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst
index a5977a6ebb..a47a233c08 100644
--- a/doc/manpages/common-options.rst
+++ b/doc/manpages/common-options.rst
@@ -126,6 +126,7 @@ These following options control the daemon's VTY (interactive command line) inte
staticd 2616
bfdd 2617
fabricd 2618
+ vrrpd 2619
Port 2607 is used for ospfd's Opaque LSA API.
diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py
index 46240de1c0..e7813d8176 100644
--- a/doc/manpages/conf.py
+++ b/doc/manpages/conf.py
@@ -334,6 +334,7 @@ man_pages = [
('frr', 'frr', 'a systemd interaction script', [], 1),
('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8),
('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8),
+ ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8),
]
# -- Options for Texinfo output -------------------------------------------
diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst
index cdf5e1967e..2a6a9fd1bd 100644
--- a/doc/manpages/defines.rst
+++ b/doc/manpages/defines.rst
@@ -1,3 +1,3 @@
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
.. |synopsis-options-hv| replace:: [-h] [-v]
-.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), mtracebis(8)
+.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8)
diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst
index 053555c4e4..40f06efdfe 100644
--- a/doc/manpages/index.rst
+++ b/doc/manpages/index.rst
@@ -26,4 +26,5 @@
watchfrr
zebra
vtysh
+ vrrpd
frr
diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am
index a4457c9c45..19d2d8d6ae 100644
--- a/doc/manpages/subdir.am
+++ b/doc/manpages/subdir.am
@@ -30,6 +30,7 @@ man_RSTFILES = \
doc/manpages/zebra.rst \
doc/manpages/bfdd.rst \
doc/manpages/bfd-options.rst \
+ doc/manpages/vrrpd.rst \
# end
EXTRA_DIST += $(man_RSTFILES)
diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/vrrpd.rst
new file mode 100644
index 0000000000..0e73b07cda
--- /dev/null
+++ b/doc/manpages/vrrpd.rst
@@ -0,0 +1,40 @@
+*****
+VRRPD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: vrrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
+and VRRPv3 is present.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/user/basic.rst b/doc/user/basic.rst
index 8fbea29ee7..3df60169f7 100644
--- a/doc/user/basic.rst
+++ b/doc/user/basic.rst
@@ -287,8 +287,8 @@ Terminal Mode Commands
Write current configuration to configuration file.
-.. index:: configure terminal
-.. clicmd:: configure terminal
+.. index:: configure [terminal]
+.. clicmd:: configure [terminal]
Change to configuration mode. This command is the first step to
configuration.
@@ -414,6 +414,22 @@ Terminal Mode Commands
(view) show [ip] bgp l2vpn evpn all overlay
...
+.. _common-show-commands:
+
+.. index:: show thread cpu
+.. clicmd:: show thread 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.
+
+.. index:: show thread poll
+.. clicmd:: show thread 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.
.. _common-invocation-options:
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index 986d1494a5..33bc77049e 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -72,8 +72,7 @@ BFDd Commands
peer listener to and the address we should use to send the packets.
This option is mandatory for IPv6.
- `interface` selects which interface we should use. This option
- conflicts with `vrf`.
+ `interface` selects which interface we should use.
`vrf` selects which domain we want to use.
@@ -82,13 +81,13 @@ BFDd Commands
Stops and removes the selected peer.
-.. index:: show bfd peers [json]
-.. clicmd:: show bfd peers [json]
+.. index:: show bfd [vrf NAME] peers [json]
+.. clicmd:: show bfd [vrf NAME] peers [json]
Show all configured BFD peers information and current status.
-.. index:: show bfd peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]
-.. clicmd:: show bfd peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]
+.. index:: show bfd [vrf NAME$vrfname] peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname}]> [json]
+.. clicmd:: show bfd [vrf NAME$vrfname] peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname}]> [json]
Show status for a specific BFD peer.
@@ -175,6 +174,21 @@ The following commands are available inside the BGP configuration node.
Removes any notification registration for this neighbor.
+.. index:: neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure
+.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure
+
+ Allow to write CBIT independence in BFD outgoing packets. Also allow to
+ read both C-BIT value of BFD and lookup BGP peer status. This command is
+ useful when a BFD down event is caught, while the BGP peer requested that
+ local BGP keeps the remote BGP entries as staled if such issue is detected.
+ This is the case when graceful restart is enabled, and it is wished to
+ ignore the BD event while waiting for the remote router to restart.
+
+.. index:: no neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure
+.. clicmd:: no neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure
+
+ Disallow to write CBIT independence in BFD outgoing packets. Also disallow
+ to ignore BFD down notification. This is the default behaviour.
.. _bfd-ospf-peer-config:
@@ -296,6 +310,11 @@ Here are the available peer configurations:
shutdown
!
+ ! configure a peer on an interface from a separate vrf
+ peer 192.168.0.5 interface eth1 vrf vrf2
+ no shutdown
+ !
+
! remove a peer
no peer 192.168.0.3 vrf foo
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 35e42d95cb..768f22c873 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -942,14 +942,18 @@ Configuring Peers
.. index:: [no] neighbor PEER maximum-prefix NUMBER
.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER
-.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend
-.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend
+ Sets a maximum number of prefixes we can receive from a given peer. If this
+ number is exceeded, the BGP session will be destroyed.
-.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as
-.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as
+ In practice, it is generally preferable to use a prefix-list to limit what
+ prefixes are received from the peer instead of using this knob. Tearing down
+ the BGP session when a limit is exceeded is far more destructive than merely
+ rejecting undesired prefixes. The prefix-list method is also much more
+ granular and offers much smarter matching criterion than number of received
+ prefixes, making it more suited to implementing policy.
-.. index:: [no] neighbor PEER local-as AS-NUMBER
-.. clicmd:: [no] neighbor PEER local-as AS-NUMBER
+.. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
+.. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
Specify an alternate AS for this BGP process when interacting with the
specified peer. With no modifiers, the specified local-as is prepended to
@@ -1858,11 +1862,11 @@ address-family:
.. index:: label vpn export (0..1048575)|auto
.. clicmd:: label vpn export (0..1048575)|auto
- Specifies an optional MPLS label to be attached to a route exported from the
- current unicast VRF to VPN. If label is specified as ``auto``, the label
- value is automatically assigned from a pool maintained by the zebra
- daemon. If zebra is not running, automatic label assignment will not
- complete, which will block corresponding route export.
+ Enables an MPLS label to be attached to a route exported from the current
+ unicast VRF to VPN. If the value specified is ``auto``, the label value is
+ automatically assigned from a pool maintained by the Zebra daemon. If Zebra
+ is not running, or if this command is not configured, automatic label
+ assignment will not complete, which will block corresponding route export.
.. index:: no label vpn export [(0..1048575)|auto]
.. clicmd:: no label vpn export [(0..1048575)|auto]
diff --git a/doc/user/index.rst b/doc/user/index.rst
index 4c218c6580..4e14de6737 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -56,6 +56,7 @@ Protocols
sharp
static
vnc
+ vrrp
########
Appendix
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 71bc047720..6413c62995 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -15,8 +15,8 @@ OSPF6 router
.. index:: router ospf6
.. clicmd:: router ospf6
-.. index:: router-id A.B.C.D
-.. clicmd:: router-id A.B.C.D
+.. index:: ospf6 router-id A.B.C.D
+.. clicmd:: ospf6 router-id A.B.C.D
Set router's Router-ID.
diff --git a/doc/user/setup.rst b/doc/user/setup.rst
index ffefe37905..2cdd3a7c7f 100644
--- a/doc/user/setup.rst
+++ b/doc/user/setup.rst
@@ -6,8 +6,8 @@ Basic Setup
After installing FRR, some basic configuration must be completed before it is
ready to use.
-Daemons File
-------------
+Daemons Configuration File
+--------------------------
After a fresh install, starting FRR will do nothing. This is because daemons
must be explicitly enabled by editing a file in your configuration directory.
This file is usually located at :file:`/etc/frr/daemons` and determines which
@@ -34,19 +34,6 @@ systemd. The file initially looks like this:
bfdd=no
fabricd=no
-To enable a particular daemon, simply change the corresponding 'no' to 'yes'.
-Subsequent service restarts should start the daemon.
-
-Daemons Configuration File
---------------------------
-There is another file that controls the default options passed to daemons when
-starting FRR as a service. This file is located in your configuration
-directory, usually at :file:`/etc/frr/daemons`.
-
-This file has several parts. Here is an example:
-
-::
-
#
# If this option is set the /etc/init.d/frr script automatically loads
# the config via "vtysh -b" when the servers are started.
@@ -71,6 +58,7 @@ This file has several parts. Here is an example:
bfdd_options=" --daemon -A 127.0.0.1"
fabricd_options=" --daemon -A 127.0.0.1"
+ #MAX_FDS=1024
# The list of daemons to watch is automatically generated by the init script.
#watchfrr_options=""
@@ -85,6 +73,13 @@ Breaking this file down:
::
+ bgpd=yes
+
+To enable a particular daemon, simply change the corresponding 'no' to 'yes'.
+Subsequent service restarts should start the daemon.
+
+::
+
vtysh_enable=yes
As the comment says, this causes :ref:`VTYSH <vty-shell>` to apply
@@ -93,6 +88,16 @@ reasons touched on in the VTYSH documentation and should generally be enabled.
::
+ MAX_FDS=1024
+
+This allows the operator to control the number of open file descriptors
+each daemon is allowed to start with. The current assumed value on
+most operating systems is 1024. If the operator plans to run bgp with
+several thousands of peers than this is where we would modify FRR to
+allow this to happen.
+
+::
+
zebra_options=" -s 90000000 --daemon -A 127.0.0.1"
bgpd_options=" --daemon -A 127.0.0.1"
...
diff --git a/doc/user/static.rst b/doc/user/static.rst
index 1705b6379e..09bdc9cbea 100644
--- a/doc/user/static.rst
+++ b/doc/user/static.rst
@@ -123,7 +123,7 @@ but this time, the route command will apply to the VRF.
.. code-block:: frr
# case with VRF
- configure terminal
+ configure
vrf r1-cust1
ip route 10.0.0.0/24 10.0.0.2
exit-vrf
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index 08b5dc954c..1e4d86c722 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -37,6 +37,7 @@ user_RSTFILES = \
doc/user/snmptrap.rst \
doc/user/static.rst \
doc/user/vnc.rst \
+ doc/user/vrrp.rst \
doc/user/vtysh.rst \
doc/user/zebra.rst \
doc/user/bfd.rst \
diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst
new file mode 100644
index 0000000000..a2dd950987
--- /dev/null
+++ b/doc/user/vrrp.rst
@@ -0,0 +1,506 @@
+.. _vrrp:
+
+****
+VRRP
+****
+
+:abbr:`VRRP` stands for Virtual Router Redundancy Protocol. This protocol is
+used to allow multiple backup routers on the same segment to take over
+operation of each others' IP addresses if the primary router fails. This is
+typically used to provide fault-tolerant gateways to hosts on the segment.
+
+FRR implements VRRPv2 (:rfc:`3768`) and VRRPv3 (:rfc:`5798`). For VRRPv2, no
+authentication methods are supported; these are deprecated in the VRRPv2
+specification as they do not provide any additional security over the base
+protocol.
+
+.. note::
+
+ - VRRP is supported on Linux 5.1+
+ - VRRP does not implement Accept_Mode
+
+.. _vrrp-starting:
+
+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.
+
+.. program:: vrrpd
+
+:abbr:`VRRP` supports all the common FRR daemon start options which are
+documented elsewhere.
+
+.. _vrrp-protocol-overview:
+
+Protocol Overview
+=================
+
+From :rfc:`5798`:
+
+ VRRP specifies an election protocol that dynamically assigns responsibility
+ for a virtual router to one of the VRRP routers on a LAN. The VRRP router
+ controlling the IPv4 or IPv6 address(es) associated with a virtual router is
+ called the Master, and it forwards packets sent to these IPv4 or IPv6
+ addresses. VRRP Master routers are configured with virtual IPv4 or IPv6
+ addresses, and VRRP Backup routers infer the address family of the virtual
+ addresses being carried based on the transport protocol. Within a VRRP
+ router, the virtual routers in each of the IPv4 and IPv6 address families
+ are a domain unto themselves and do not overlap. The election process
+ provides dynamic failover in the forwarding responsibility should the Master
+ become unavailable. For IPv4, the advantage gained from using VRRP is a
+ higher-availability default path without requiring configuration of dynamic
+ routing or router discovery protocols on every end-host. For IPv6, the
+ advantage gained from using VRRP for IPv6 is a quicker switchover to Backup
+ routers than can be obtained with standard IPv6 Neighbor Discovery
+ mechanisms.
+
+VRRP accomplishes these goals primarily by using a virtual MAC address shared
+between the physical routers participating in a VRRP virtual router. This
+reduces churn in the neighbor tables of hosts and downstream switches and makes
+router failover theoretically transparent to these devices.
+
+FRR implements the election protocol and handles changing the operating system
+interface configuration in response to protocol state changes.
+
+As a consequence of the shared virtual MAC requirement, VRRP is currently
+supported only on Linux, as Linux is the only operating system that provides
+the necessary features in its network stack to make implementing this protocol
+feasible.
+
+When a VRRP router is acting as the Master router, FRR allows the interface(s)
+with the backed-up IP addresses to remain up and functional. When the router
+transitions to Backup state, these interfaces are set into ``protodown`` mode.
+This is an interface mode that is functionally equivalent to ``NO-CARRIER``.
+Physical drivers typically use this state indication to drop traffic on an
+interface. In the case of VRRP, the interfaces in question are macvlan devices,
+which are virtual interfaces. Since the IP addresses managed by VRRP are on
+these interfaces, this has the same effect as removing these addresses from the
+interface, but is implemented as a state flag.
+
+.. _vrrp-configuration:
+
+Configuring VRRP
+================
+
+VRRP is configured on a per-interface basis, with some global defaults
+accessible outside the interface context.
+
+.. _vrrp-system-configuration:
+
+System Configuration
+--------------------
+
+FRR's VRRP implementation uses Linux macvlan devices to to implement the shared
+virtual MAC feature of the protocol. Currently, it does not create those system
+interfaces - they must be configured outside of FRR before VRRP can be enabled
+on them.
+
+Each interface on which VRRP will be enabled must have at least one macvlan
+device configured with the virtual MAC and placed in the proper operation mode.
+The addresses backed up by VRRP are assigned to these interfaces.
+
+Suppose you have an interface ``eth0`` with the following configuration:
+
+.. code-block:: console
+
+ $ ip link show eth0
+ 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
+ link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff
+ inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
+ valid_lft 72532sec preferred_lft 72532sec
+ inet 10.0.2.16/24 brd 10.0.2.255 scope global dynamic eth0
+ valid_lft 72532sec preferred_lft 72532sec
+ inet6 fe80::17:45ff:fe00:aaaa/64 scope link
+ valid_lft forever preferred_lft forever
+
+Suppose the address you want to back up is ``10.0.2.16``, and will be managed
+by the virtual router with id ``5``. A macvlan device with the appropriate MAC
+address must be created before VRRP can begin to operate.
+
+If you are using ``ifupdown2``, the configuration is as follows:
+
+.. code-block:: console
+
+ iface eth0
+ ...
+ vrrp 5 10.0.2.16/24 2001:0db8::0370:7334/64
+
+Applying this configuration with ``ifreload -a`` will create the appropriate
+macvlan device. If you are using ``iproute2``, the equivalent configuration is:
+
+.. code-block:: console
+
+ ip link add vrrp4-2-1 link eth0 addrgenmode random type macvlan mode bridge
+ ip link set dev vrrp4-2-1 address 00:00:5e:00:01:05
+ ip addr add 10.0.2.16/24 dev vrrp4-2-1
+ ip link set dev vrrp4-2-1 up
+
+ ip link add vrrp6-2-1 link eth0 addrgenmode random type macvlan mode bridge
+ ip link set dev vrrp4-2-1 address 00:00:5e:00:02:05
+ ip addr add 2001:db8::370:7334/64 dev vrrp6-2-1
+ ip link set dev vrrp6-2-1 up
+
+In either case, the created interfaces will look like this:
+
+.. code-block:: console
+
+ $ ip addr show vrrp4-2-1
+ 5: vrrp4-2-1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 00:00:5e:00:01:05 brd ff:ff:ff:ff:ff:ff
+ inet 10.0.2.16/24 scope global vrrp4-2-1
+ valid_lft forever preferred_lft forever
+ inet6 fe80::dc56:d11a:e69d:ea72/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+
+ $ ip addr show vrrp6-2-1
+ 8: vrrp6-2-1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 00:00:5e:00:02:05 brd ff:ff:ff:ff:ff:ff
+ inet6 2001:db8::370:7334/64 scope global
+ valid_lft forever preferred_lft forever
+ inet6 fe80::f8b7:c9dd:a1e8:9844/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+
+Using ``vrrp4-2-1`` as an example, a few things to note about this interface:
+
+- It is slaved to ``eth0``; any packets transmitted on this interface will
+ egress via ``eth0``
+- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for
+ :abbr:`VRID (Virtual Router ID)` ``5``
+- The link local address on the interface is not derived from the interface
+ MAC
+
+First to note is that packets transmitted on this interface will egress via
+``eth0``, but with their Ethernet source MAC set to the VRRP virtual MAC. This
+is how FRR's VRRP implementation accomplishes the virtual MAC requirement on
+real hardware.
+
+Ingress traffic is a more complicated matter. Macvlan devices have multiple
+operating modes that change how ingress traffic is handled. Of relevance to
+FRR's implementation are the ``bridge`` and ``private`` modes. In ``private``
+mode, any ingress traffic on ``eth0`` (in our example) with a source MAC
+address equal to the MAC address on any of ``eth0``'s macvlan devices will be
+placed *only* on that macvlan device. This curious behavior is undesirable,
+since FRR's implementation of VRRP needs to be able to receive advertisements
+from neighbors while in Backup mode - i.e., while its macvlan devices are in
+``protodown on``. If the macvlan devices are instead set to ``bridge`` mode,
+all ingress traffic shows up on all interfaces - including ``eth0`` -
+regardless of source MAC or any other factor. Consequently, macvlans used by
+FRR for VRRP must be set to ``bridge`` mode or the protocol will not function
+correctly.
+
+As for the MAC address assigned to this interface, the last byte of the address
+holds the :abbr:`VRID (Virtual Router Identifier)`, in this case ``0x05``. The
+second to last byte is ``0x01``, as specified by the RFC for IPv4 operation.
+The IPv6 MAC address is be identical except that the second to last byte is
+defined to be ``0x02``. Two things to note from this arrangement:
+
+1. There can only be up to 255 unique Virtual Routers on an interface (only 1
+ byte is available for the VRID)
+2. IPv4 and IPv6 addresses must be assigned to different macvlan devices,
+ because they have different MAC addresses
+
+Finally, take note of the generated IPv6 link local address on the interface.
+For interfaces on which VRRP will operate in IPv6 mode, this link local
+*cannot* be derived using the usual EUI-64 method. This is because VRRP
+advertisements are sent from the link local address of this interface, and VRRP
+uses the source address of received advertisements as part of its election
+algorithm. If the IPv6 link local of a router is equivalent to the IPv6 link
+local in a received advertisement, this can cause both routers to assume the
+Master role (very bad). ``ifupdown`` knows to set the ``addrgenmode`` of the
+interface properly, but when using ``iproute2`` to create the macvlan devices,
+you must be careful to manually specify ``addrgenmode random``.
+
+A brief note on the Backup state
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It is worth noting here that an alternate choice for the implementation of the
+Backup state, such as removing all the IP addresses assigned to the macvlan
+device or deleting their local routes instead of setting the device into
+``protodown on``, would allow the protocol to function regardless of whether
+the macvlan device(s) are set to ``private`` or ``bridge`` mode. Indeed, the
+strange behavior of the kernel macvlan driver in ``private`` mode, whereby it
+performs what may be thought of as a sort of interface-level layer 2 "NAT"
+based on source MAC, can be traced back to a patch clearly designed to
+accommodate a VRRP implementation from a different vendor. However, the
+``protodown`` based implementation allows for a configuration model in which
+FRR does not dynamically manage the addresses assigned on a system, but instead
+just manages interface state. Such a scenario was in mind when this protocol
+implementation was initially built, which is why the other choices are not
+currently present. Since support for placing macvlan devices into ``protodown``
+was not added to Linux until version 5.1, this also explains the relatively
+restrictive kernel versioning requirement.
+
+In the future other methods of implementing Backup state may be added along
+with a configuration knob to choose between them.
+
+.. _vrrp-interface-configuration:
+
+Interface Configuration
+-----------------------
+
+Continuing with the example from the previous section, we assume the macvlan
+interfaces have been properly configured with the proper MAC addresses and the
+IPvX addresses assigned.
+
+In FRR, a possible VRRPv3 configuration for this interface is:
+
+.. code-block:: frr
+
+ interface eth0
+ vrrp 5 version 3
+ vrrp 5 priority 200
+ vrrp 5 advertisement-interval 1500
+ vrrp 5 ip 10.0.2.16
+ vrrp 5 ipv6 2001:0db8::0370:7334
+
+VRRP will activate as soon as the first IPvX address configuration line is
+encountered. If you do not want this behavior, use the :clicmd:`vrrp (1-255)
+shutdown` command, and apply the ``no`` form when you are ready to activate
+VRRP.
+
+At this point executing ``show vrrp`` will display the following:
+
+.. code-block:: console
+
+ ubuntu-bionic# show vrrp
+
+ Virtual Router ID 5
+ Protocol Version 3
+ Autoconfigured Yes
+ Shutdown No
+ Interface eth0
+ VRRP interface (v4) vrrp4-2-5
+ VRRP interface (v6) vrrp6-2-5
+ Primary IP (v4) 10.0.2.15
+ Primary IP (v6) fe80::9b91:7155:bf6a:d386
+ Virtual MAC (v4) 00:00:5e:00:01:05
+ Virtual MAC (v6) 00:00:5e:00:02:05
+ Status (v4) Master
+ Status (v6) Master
+ Priority 200
+ Effective Priority (v4) 200
+ Effective Priority (v6) 200
+ Preempt Mode Yes
+ Accept Mode Yes
+ Advertisement Interval 1500 ms
+ Master Advertisement Interval (v4) 1000 ms
+ Master Advertisement Interval (v6) 1000 ms
+ Advertisements Tx (v4) 14
+ Advertisements Tx (v6) 14
+ Advertisements Rx (v4) 0
+ Advertisements Rx (v6) 0
+ Gratuitous ARP Tx (v4) 1
+ Neigh. Adverts Tx (v6) 1
+ State transitions (v4) 2
+ State transitions (v6) 2
+ Skew Time (v4) 210 ms
+ Skew Time (v6) 210 ms
+ Master Down Interval (v4) 3210 ms
+ Master Down Interval (v6) 3210 ms
+ IPv4 Addresses 1
+ .................................. 10.0.2.16
+ IPv6 Addresses 1
+ .................................. 2001:db8::370:7334
+
+At this point, VRRP has sent gratuitous ARP requests for the IPv4 address,
+Unsolicited Neighbor Advertisements for the IPv6 address, and has asked Zebra
+to send Router Advertisements on its behalf. It is also transmitting VRRPv3
+advertisements on the macvlan interfaces.
+
+The Primary IP fields are of some interest, as the behavior may be
+counterintuitive. These fields show the source address used for VRRP
+advertisements. Although VRRPv3 advertisements are always transmitted on the
+macvlan interfaces, in the IPv4 case the source address is set to the primary
+IPv4 address on the base interface, ``eth0`` in this case. This is a protocol
+requirement, and IPv4 VRRP will not function unless the base interface has an
+IPv4 address assigned. In the IPv6 case the link local of the macvlan interface
+is used.
+
+If any misconfiguration errors are detected, VRRP for the misconfigured address
+family will not come up and the configuration issue will be logged to FRR's
+configured logging destination.
+
+Per the RFC, IPv4 and IPv6 virtual routers are independent of each other. For
+instance, it is possible for the IPv4 router to be in Backup state while the
+IPv6 router is in Master state; or for either to be completely inoperative
+while the other is operative, etc. Instances sharing the same base interface
+and VRID are shown together in the show output for conceptual convenience.
+
+To complete your VRRP deployment, configure other routers on the segment with
+the exact same system and FRR configuration as shown above. Provided each
+router receives the others' VRRP advertisements, the Master election protocol
+will run, one Master will be elected, and the other routers will place their
+macvlan interfaces into ``protodown on`` until Master fails or priority values
+are changed to favor another router.
+
+Switching the protocol version to VRRPv2 is accomplished simply by changing
+``version 3`` to ``version 2`` in the VRID configuration line. Note that VRRPv2
+does not support IPv6, so any IPv6 configuration will be rejected by FRR when
+using VRRPv2.
+
+.. note::
+
+ All VRRP routers initially start in Backup state, and wait for the
+ calculated Master Down Interval to pass before they assume Master status.
+ This prevents downstream neighbor table churn if another router is already
+ Master with higher priority, meaning this box will ultimately assume Backup
+ status once the first advertisement is received. However, if the calculated
+ Master Down Interval is high and this router is configured such that it will
+ ultimately assume Master status, then it will take a while for this to
+ happen. This is a known issue.
+
+
+All interface configuration commands are documented below.
+
+.. index:: [no] vrrp (1-255) [version (2-3)]
+.. clicmd:: [no] vrrp (1-255) [version (2-3)]
+
+ Create a VRRP router with the specified VRID on the interface. Optionally
+ specify the protocol version. If the protocol version is not specified, the
+ default is VRRPv3.
+
+.. index:: [no] vrrp (1-255) advertisement-interval (10-40950)
+.. clicmd:: [no] vrrp (1-255) advertisement-interval (10-40950)
+
+ Set the advertisement interval. This is the interval at which VRRP
+ advertisements will be sent. Values are given in milliseconds, but must be
+ multiples of 10, as VRRP itself uses centiseconds.
+
+.. index:: [no] vrrp (1-255) ip A.B.C.D
+.. clicmd:: [no] vrrp (1-255) ip A.B.C.D
+
+ Add an IPv4 address to the router. This address must already be configured
+ on the appropriate macvlan device. Adding an IP address to the router will
+ implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to
+ override this behavior.
+
+.. index:: [no] vrrp (1-255) ipv6 X:X::X:X
+.. clicmd:: [no] vrrp (1-255) ipv6 X:X::X:X
+
+ Add an IPv6 address to the router. This address must already be configured
+ on the appropriate macvlan device. Adding an IP address to the router will
+ implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to
+ override this behavior.
+
+ This command will fail if the protocol version is set to VRRPv2, as VRRPv2
+ does not support IPv6.
+
+.. index:: [no] vrrp (1-255) preempt
+.. clicmd:: [no] vrrp (1-255) preempt
+
+ Toggle preempt mode. When enabled, preemption allows Backup routers with
+ higher priority to take over Master status from the existing Master. Enabled
+ by default.
+
+.. index:: [no] vrrp (1-255) priority (1-254)
+.. clicmd:: [no] vrrp (1-255) priority (1-254)
+
+ Set the router priority. The router with the highest priority is elected as
+ the Master. If all routers in the VRRP virtual router are configured with
+ the same priority, the router with the highest primary IP address is elected
+ as the Master. Priority value 255 is reserved for the acting Master router.
+
+.. index:: [no] vrrp (1-255) shutdown
+.. clicmd:: [no] vrrp (1-255) shutdown
+
+ Place the router into administrative shutdown. VRRP will not activate for
+ this router until this command is removed with the ``no`` form.
+
+.. _vrrp-global-configuration:
+
+Global Configuration
+--------------------
+
+Show commands, global defaults and debugging configuration commands.
+
+.. index:: show vrrp [interface INTERFACE] [(1-255)] [json]
+.. clicmd:: show vrrp [interface INTERFACE] [(1-255)] [json]
+
+ Shows VRRP status for some or all configured VRRP routers. Specifying an
+ interface will only show routers configured on that interface. Specifying a
+ VRID will only show routers with that VRID. Specifying ``json`` will dump
+ each router state in a JSON array.
+
+.. index:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}]
+.. clicmd:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}]
+
+ Toggle debugging logs for some or all components of VRRP.
+
+ protocol
+ Logs state changes, election protocol decisions, and interface status
+ changes.
+
+ autoconfigure
+ Logs actions taken by the autoconfiguration procedures. See
+ :ref:`vrrp-autoconfiguration`.
+
+ packets
+ Logs details of ingress and egress packets. Includes packet decodes and
+ hex dumps.
+
+ sockets
+ Logs details of socket configuration and initialization.
+
+ ndisc
+ Logs actions taken by the Neighbor Discovery component of VRRP.
+
+ arp
+ Logs actions taken by the ARP component of VRRP.
+
+ zebra
+ Logs communications with Zebra.
+
+.. index:: [no] vrrp default <advertisement-interval (1-4096)|preempt|priority (1-254)|shutdown>
+.. clicmd:: [no] vrrp default <advertisement-interval (1-4096)|preempt|priority (1-254)|shutdown>
+
+ Configure defaults for new VRRP routers. These values will not affect
+ already configured VRRP routers, but will be applied to newly configured
+ ones.
+
+.. _vrrp-autoconfiguration:
+
+Autoconfiguration
+-----------------
+
+In light of the complicated configuration required on the base system before
+VRRP can be enabled, FRR has the ability to automatically configure VRRP
+sessions by inspecting the interfaces present on the system. Since it is quite
+unlikely that macvlan devices with VRRP virtual MACs will exist on systems not
+using VRRP, this can be a convenient shortcut to automatically generate FRR
+configuration.
+
+After configuring the interfaces as described in
+:ref:`vrrp-system-configuration`, and configuring any defaults you may want,
+execute the following command:
+
+.. index:: [no] vrrp autoconfigure [version (2-3)]
+.. clicmd:: [no] vrrp autoconfigure [version (2-3)]
+
+ Generates VRRP configuration based on the interface configuration on the
+ base system. Any existing interfaces that are configured properly for VRRP -
+ i.e. have the correct MAC address, link local address (when required), IPv4
+ and IPv6 addresses - are used to create a VRRP router on their parent
+ interfaces, with VRRP IPvX addresses taken from the addresses assigned to
+ the macvlan devices. The generated configuration appears in the output of
+ ``show run``, which can then be modified as needed and written to the config
+ file. The ``version`` parameter controls the protocol version; if using
+ VRRPv2, keep in mind that IPv6 is not supported and will not be configured.
+
+The following configuration is then generated for you:
+
+.. code-block:: frr
+
+ interface eth0
+ vrrp 5
+ vrrp 5 ip 10.0.2.16
+ vrrp 5 ipv6 2001:db8::370:7334
+
+VRRP is automatically activated. Global defaults, if set, are applied.
+
+You can then edit this configuration with **vtysh** as needed, and commit it by
+writing to the configuration file.
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index a7bf0c74da..40d8949297 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -23,9 +23,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the
Runs in batch mode. *zebra* parses configuration file and terminates
immediately.
-.. option:: -k, --keep_kernel
+.. option:: -K TIME, --graceful_restart TIME
- When zebra starts up, don't delete old self inserted routes.
+ If this option is specified, the graceful restart time is TIME seconds.
+ Zebra, when started, will read in routes. Those routes that Zebra
+ identifies that it was the originator of will be swept in TIME seconds.
+ If no time is specified then we will sweep those routes immediately.
.. option:: -r, --retain
@@ -268,14 +271,6 @@ Link Parameters Commands
for InterASv2 link in OSPF (RFC5392). Note that this option is not yet
supported for ISIS (RFC5316).
-.. index:: table TABLENO
-.. clicmd:: table TABLENO
-
- Select the primary kernel routing table to be used. This only works for
- kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later).
- After setting TABLENO with this command, static routes defined after this
- are added to the specified table.
-
.. index:: ip nht resolve-via-default
.. clicmd:: ip nht resolve-via-default
@@ -693,6 +688,40 @@ replaces the information sent in the first message.
If the connection to the FPM goes down for some reason, zebra sends
the FPM a complete copy of the forwarding table(s) when it reconnects.
+.. _zebra-dplane:
+
+Dataplane Commands
+==================
+
+The zebra dataplane subsystem provides a framework for FIB
+programming. Zebra uses the dataplane to program the local kernel as
+it makes changes to objects such as IP routes, MPLS LSPs, and
+interface IP addresses. The dataplane runs in its own pthread, in
+order to off-load work from the main zebra pthread.
+
+
+.. index:: show zebra dplane [detailed]
+.. clicmd:: show zebra dplane [detailed]
+
+ Display statistics about the updates and events passing through the
+ dataplane subsystem.
+
+
+.. index:: show zebra dplane providers
+.. clicmd:: show zebra dplane providers
+
+ Display information about the running dataplane plugins that are
+ providing updates to a FIB. By default, the local kernel plugin is
+ present.
+
+
+.. index:: zebra dplane limit [NUMBER]
+.. clicmd:: zebra dplane limit [NUMBER]
+
+ Configure the limit on the number of pending updates that are
+ waiting to be processed by the dataplane pthread.
+
+
zebra Terminal Mode Commands
============================
@@ -746,6 +775,22 @@ zebra Terminal Mode Commands
Display various statistics related to the installation and deletion
of routes, neighbor updates, and LSP's into the kernel.
+.. index:: show zebra client [summary]
+.. clicmd:: show zebra client [summary]
+
+ Display statistics about clients that are connected to zebra. This is
+ useful for debugging and seeing how much data is being passed between
+ zebra and it's clients. If the summary form of the command is choosen
+ a table is displayed with shortened information.
+
+.. index:: show zebra router table summary
+.. clicmd:: show zebra router table summary
+
+ Display summarized data about tables created, their afi/safi/tableid
+ and how many routes each table contains. Please note this is the
+ total number of route nodes in the table. Which will be higher than
+ the actual number of routes that are held.
+
.. index:: show zebra fpm stats
.. clicmd:: show zebra fpm stats
diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c
index dc1ae675b0..0a74e86263 100644
--- a/eigrpd/eigrp_zebra.c
+++ b/eigrpd/eigrp_zebra.c
@@ -53,21 +53,15 @@
#include "eigrpd/eigrp_topology.h"
#include "eigrpd/eigrp_fsm.h"
-static int eigrp_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t);
-static int eigrp_interface_delete(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int eigrp_interface_address_add(int, struct zclient *, zebra_size_t,
- vrf_id_t vrf_id);
-static int eigrp_interface_address_delete(int, struct zclient *, zebra_size_t,
- vrf_id_t vrf_id);
-static int eigrp_interface_state_up(int, struct zclient *, zebra_size_t,
- vrf_id_t vrf_id);
-static int eigrp_interface_state_down(int, struct zclient *, zebra_size_t,
- vrf_id_t vrf_id);
+static int eigrp_interface_add(ZAPI_CALLBACK_ARGS);
+static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS);
+static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS);
+static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS);
+static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS);
+static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS);
static struct interface *zebra_interface_if_lookup(struct stream *);
-static int eigrp_zebra_read_route(int, struct zclient *, zebra_size_t,
- vrf_id_t vrf_id);
+static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS);
/* Zebra structure to hold current status. */
struct zclient *zclient = NULL;
@@ -77,8 +71,7 @@ extern struct thread_master *master;
struct in_addr router_id_zebra;
/* Router-id update message from zebra. */
-static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct eigrp *eigrp;
struct prefix router_id;
@@ -94,8 +87,7 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_zebra_route_notify_owner(ZAPI_CALLBACK_ARGS)
{
struct prefix p;
enum zapi_route_notify_owner note;
@@ -134,8 +126,7 @@ void eigrp_zebra_init(void)
/* Zebra route add and delete treatment. */
-static int eigrp_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
struct eigrp *eigrp;
@@ -150,9 +141,9 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient,
if (eigrp == NULL)
return 0;
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
- } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */
+ } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */
{
}
@@ -160,8 +151,7 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient,
}
/* Inteface addition message from zebra. */
-static int eigrp_interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct eigrp_interface *ei;
@@ -180,8 +170,7 @@ static int eigrp_interface_add(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -211,12 +200,11 @@ static int eigrp_interface_delete(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (c == NULL)
return 0;
@@ -233,14 +221,13 @@ static int eigrp_interface_address_add(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct interface *ifp;
struct eigrp_interface *ei;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (c == NULL)
return 0;
@@ -266,8 +253,7 @@ static int eigrp_interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_interface_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -323,8 +309,7 @@ static int eigrp_interface_state_up(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_interface_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
diff --git a/grpc/Makefile b/grpc/Makefile
new file mode 100644
index 0000000000..8748286629
--- /dev/null
+++ b/grpc/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. grpc/libfrrgrpc_pb.la
+%: ALWAYS
+ @$(MAKE) -s -C .. grpc/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/grpc/frr-northbound.proto b/grpc/frr-northbound.proto
new file mode 100644
index 0000000000..d070d715e8
--- /dev/null
+++ b/grpc/frr-northbound.proto
@@ -0,0 +1,412 @@
+//
+// Copyright (C) 2019 NetDEF, Inc.
+// Renato Westphal
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; see the file COPYING; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+syntax = "proto3";
+
+package frr;
+
+// Service specification for the FRR northbound interface.
+service Northbound {
+ // Retrieve the capabilities supported by the target.
+ rpc GetCapabilities(GetCapabilitiesRequest) returns (GetCapabilitiesResponse) {}
+
+ // Retrieve configuration data, state data or both from the target.
+ rpc Get(GetRequest) returns (stream GetResponse) {}
+
+ // Create a new candidate configuration and return a reference to it. The
+ // created candidate is a copy of the running configuration.
+ rpc CreateCandidate(CreateCandidateRequest) returns (CreateCandidateResponse) {}
+
+ // Delete a candidate configuration.
+ rpc DeleteCandidate(DeleteCandidateRequest) returns (DeleteCandidateResponse) {}
+
+ // Update a candidate configuration by rebasing the changes on top of the
+ // latest running configuration. Resolve conflicts automatically by giving
+ // preference to the changes done in the candidate configuration.
+ rpc UpdateCandidate(UpdateCandidateRequest) returns (UpdateCandidateResponse) {}
+
+ // Edit a candidate configuration. All changes are discarded if any error
+ // happens.
+ rpc EditCandidate(EditCandidateRequest) returns (EditCandidateResponse) {}
+
+ // Load configuration data into a candidate configuration. Both merge and
+ // replace semantics are supported.
+ rpc LoadToCandidate(LoadToCandidateRequest) returns (LoadToCandidateResponse) {}
+
+ // Create a new configuration transaction using a two-phase commit protocol.
+ rpc Commit(CommitRequest) returns (CommitResponse) {}
+
+ // List the metadata of all configuration transactions recorded in the
+ // transactions database.
+ rpc ListTransactions(ListTransactionsRequest) returns (stream ListTransactionsResponse) {}
+
+ // Fetch a configuration (identified by its transaction ID) from the
+ // transactions database.
+ rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse) {}
+
+ // Lock the running configuration, preventing other users from changing it.
+ rpc LockConfig(LockConfigRequest) returns (LockConfigResponse) {}
+
+ // Unlock the running configuration.
+ rpc UnlockConfig(UnlockConfigRequest) returns (UnlockConfigResponse) {}
+
+ // Execute a YANG RPC.
+ rpc Execute(ExecuteRequest) returns (ExecuteResponse) {}
+}
+
+// ----------------------- Parameters and return types -------------------------
+
+//
+// RPC: GetCapabilities()
+//
+message GetCapabilitiesRequest {
+ // Empty.
+}
+
+message GetCapabilitiesResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+
+ // FRR version.
+ string frr_version = 1;
+
+ // Indicates whether FRR was compiled with support for configuration
+ // rollbacks or not (--enable-config-rollbacks).
+ bool rollback_support = 2;
+
+ // Supported schema modules.
+ repeated ModuleData supported_modules = 3;
+
+ // Supported encodings.
+ repeated Encoding supported_encodings = 4;
+}
+
+//
+// RPC: Get()
+//
+message GetRequest {
+ // Type of elements within the data tree.
+ enum DataType {
+ // All data elements.
+ ALL = 0;
+
+ // Config elements.
+ CONFIG = 1;
+
+ // State elements.
+ STATE = 2;
+ }
+
+ // The type of data being requested.
+ DataType type = 1;
+
+ // Encoding to be used.
+ Encoding encoding = 2;
+
+ // Include implicit default nodes.
+ bool with_defaults = 3;
+
+ // Paths requested by the client.
+ repeated string path = 4;
+}
+
+message GetResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::INVALID_ARGUMENT: Invalid YANG data path.
+
+ // Timestamp in nanoseconds since Epoch.
+ int64 timestamp = 1;
+
+ // The requested data.
+ DataTree data = 2;
+}
+
+//
+// RPC: CreateCandidate()
+//
+message CreateCandidateRequest {
+ // Empty.
+}
+
+message CreateCandidateResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::RESOURCE_EXHAUSTED: can't create candidate
+ // configuration.
+
+ // Handle to the new created candidate configuration.
+ uint32 candidate_id = 1;
+}
+
+//
+// RPC: DeleteCandidate()
+//
+message DeleteCandidateRequest {
+ // Candidate configuration to delete.
+ uint32 candidate_id = 1;
+}
+
+message DeleteCandidateResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found.
+}
+
+//
+// RPC: UpdateCandidate()
+//
+message UpdateCandidateRequest {
+ // Candidate configuration to update.
+ uint32 candidate_id = 1;
+}
+
+message UpdateCandidateResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found.
+}
+
+//
+// RPC: EditCandidate()
+//
+message EditCandidateRequest {
+ // Candidate configuration that is going to be edited.
+ uint32 candidate_id = 1;
+
+ // Data elements to be created or updated.
+ repeated PathValue update = 2;
+
+ // Paths to be deleted from the data tree.
+ repeated PathValue delete = 3;
+}
+
+message EditCandidateResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found.
+ // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while editing the
+ // candidate configuration.
+}
+
+//
+// RPC: LoadToCandidate()
+//
+message LoadToCandidateRequest {
+ enum LoadType {
+ // Merge the data tree into the candidate configuration.
+ MERGE = 0;
+
+ // Replace the candidate configuration by the provided data tree.
+ REPLACE = 1;
+ }
+
+ // Candidate configuration that is going to be edited.
+ uint32 candidate_id = 1;
+
+ // Load operation to apply.
+ LoadType type = 2;
+
+ // Configuration data.
+ DataTree config = 3;
+}
+
+message LoadToCandidateResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while performing
+ // the load operation.
+}
+
+//
+// RPC: Commit()
+//
+message CommitRequest {
+ enum Phase {
+ // Validate if the configuration changes are valid (phase 0).
+ VALIDATE = 0;
+
+ // Prepare resources to apply the configuration changes (phase 1).
+ PREPARE = 1;
+
+ // Release previously allocated resources (phase 2).
+ ABORT = 2;
+
+ // Apply the configuration changes (phase 2).
+ APPLY = 3;
+
+ // All of the above (VALIDATE + PREPARE + ABORT/APPLY).
+ //
+ // This option can't be used to implement network-wide transactions,
+ // since they require the manager entity to take into account the results
+ // of the preparation phase of multiple managed devices.
+ ALL = 4;
+ }
+
+ // Candidate configuration that is going to be committed.
+ uint32 candidate_id = 1;
+
+ // Transaction phase.
+ Phase phase = 2;
+
+ // Assign a comment to this commit.
+ string comment = 3;
+}
+
+message CommitResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::FAILED_PRECONDITION: misuse of the two-phase commit
+ // protocol.
+ // - grpc::StatusCode::INVALID_ARGUMENT: Validation error.
+ // - grpc::StatusCode::RESOURCE_EXHAUSTED: Failure to allocate resource.
+
+ // ID of the created configuration transaction (when the phase is APPLY
+ // or ALL).
+ uint32 transaction_id = 1;
+}
+
+//
+// RPC: ListTransactions()
+//
+message ListTransactionsRequest {
+ // Empty.
+}
+
+message ListTransactionsResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+
+ // Transaction ID.
+ uint32 id = 1;
+
+ // Client that committed the transaction.
+ string client = 2;
+
+ // Date and time the transaction was committed.
+ string date = 3;
+
+ // Comment assigned to the transaction.
+ string comment = 4;
+}
+
+//
+// RPC: GetTransaction()
+//
+message GetTransactionRequest {
+ // Transaction to retrieve.
+ uint32 transaction_id = 1;
+
+ // Encoding to be used.
+ Encoding encoding = 2;
+
+ // Include implicit default nodes.
+ bool with_defaults = 3;
+}
+
+message GetTransactionResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::NOT_FOUND: Transaction wasn't found in the transactions
+ // database.
+
+ DataTree config = 1;
+}
+
+//
+// RPC: LockConfig()
+//
+message LockConfigRequest {
+ // Empty.
+}
+
+message LockConfigResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration is
+ // locked already.
+}
+
+//
+// RPC: UnlockConfig()
+//
+message UnlockConfigRequest {
+ // Empty.
+}
+
+message UnlockConfigResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+ // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration isn't
+ // locked.
+}
+
+//
+// RPC: Execute()
+//
+message ExecuteRequest {
+ // Path of the YANG RPC or YANG Action.
+ string path = 1;
+
+ // Input parameters.
+ repeated PathValue input = 2;
+}
+
+message ExecuteResponse {
+ // Return values:
+ // - grpc::StatusCode::OK: Success.
+
+ // Output parameters.
+ repeated PathValue output = 1;
+}
+
+// -------------------------------- Definitions --------------------------------
+
+// YANG module.
+message ModuleData {
+ // Name of the YANG module;
+ string name = 1;
+
+ // Organization publishing the module.
+ string organization = 2;
+
+ // Latest revision of the module;
+ string revision = 3;
+}
+
+// Supported encodings for YANG instance data.
+enum Encoding {
+ JSON = 0;
+ XML = 1;
+}
+
+// Path-value pair representing a data element.
+message PathValue {
+ // YANG data path.
+ string path = 1;
+
+ // Data value.
+ string value = 2;
+}
+
+// YANG instance data.
+message DataTree {
+ Encoding encoding = 1;
+ string data = 2;
+}
diff --git a/grpc/subdir.am b/grpc/subdir.am
new file mode 100644
index 0000000000..3fb163fccf
--- /dev/null
+++ b/grpc/subdir.am
@@ -0,0 +1,30 @@
+if GRPC
+lib_LTLIBRARIES += grpc/libfrrgrpc_pb.la
+endif
+
+grpc_libfrrgrpc_pb_la_LDFLAGS = -version-info 0:0:0
+grpc_libfrrgrpc_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(GRPC_CXXFLAGS)
+
+nodist_grpc_libfrrgrpc_pb_la_SOURCES = \
+ grpc/frr-northbound.pb.cc \
+ grpc/frr-northbound.grpc.pb.cc \
+ # end
+
+CLEANFILES += \
+ grpc/frr-northbound.pb.cc \
+ grpc/frr-northbound.pb.h \
+ grpc/frr-northbound.grpc.pb.cc \
+ grpc/frr-northbound.grpc.pb.h \
+ # end
+
+EXTRA_DIST += grpc/frr-northbound.proto
+
+AM_V_PROTOC = $(am__v_PROTOC_$(V))
+am__v_PROTOC_ = $(am__v_PROTOC_$(AM_DEFAULT_VERBOSITY))
+am__v_PROTOC_0 = @echo " PROTOC" $@;
+am__v_PROTOC_1 =
+
+.proto.pb.cc:
+ $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^
+.proto.grpc.pb.cc:
+ $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --grpc_out=$(top_srcdir) --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` $(top_srcdir)/$^
diff --git a/isisd/dict.c b/isisd/dict.c
deleted file mode 100644
index d91f05d254..0000000000
--- a/isisd/dict.c
+++ /dev/null
@@ -1,1510 +0,0 @@
-/*
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- */
-
-#include "zebra.h"
-#include "zassert.h"
-#include "memory.h"
-#include "isis_memory.h"
-#include "dict.h"
-
-/*
- * These macros provide short convenient names for structure members,
- * which are embellished with dict_ prefixes so that they are
- * properly confined to the documented namespace. It's legal for a
- * program which uses dict to define, for instance, a macro called ``parent''.
- * Such a macro would interfere with the dnode_t struct definition.
- * In general, highly portable and reusable C modules which expose their
- * structures need to confine structure member names to well-defined spaces.
- * The resulting identifiers aren't necessarily convenient to use, nor
- * readable, in the implementation, however!
- */
-
-#define left dict_left
-#define right dict_right
-#define parent dict_parent
-#define color dict_color
-#define key dict_key
-#define data dict_data
-
-#define nilnode dict_nilnode
-#define nodecount dict_nodecount
-#define maxcount dict_maxcount
-#define compare dict_compare
-#define allocnode dict_allocnode
-#define freenode dict_freenode
-#define context dict_context
-#define dupes dict_dupes
-
-#define dictptr dict_dictptr
-
-#define dict_root(D) ((D)->nilnode.left)
-#define dict_nil(D) (&(D)->nilnode)
-#define DICT_DEPTH_MAX 64
-
-static dnode_t *dnode_alloc(void *context);
-static void dnode_free(dnode_t *node, void *context);
-
-/*
- * Perform a ``left rotation'' adjustment on the tree. The given node P and
- * its right child C are rearranged so that the P instead becomes the left
- * child of C. The left subtree of C is inherited as the new right subtree
- * for P. The ordering of the keys within the tree is thus preserved.
- */
-
-static void rotate_left(dnode_t *upper)
-{
- dnode_t *lower, *lowleft, *upparent;
-
- lower = upper->right;
- upper->right = lowleft = lower->left;
- lowleft->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- /* don't need to check for root node here because root->parent is
- the sentinel nil node, and root->parent->left points back to root */
-
- if (upper == upparent->left) {
- upparent->left = lower;
- } else {
- assert(upper == upparent->right);
- upparent->right = lower;
- }
-
- lower->left = upper;
- upper->parent = lower;
-}
-
-/*
- * This operation is the ``mirror'' image of rotate_left. It is
- * the same procedure, but with left and right interchanged.
- */
-
-static void rotate_right(dnode_t *upper)
-{
- dnode_t *lower, *lowright, *upparent;
-
- lower = upper->left;
- upper->left = lowright = lower->right;
- lowright->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- if (upper == upparent->right) {
- upparent->right = lower;
- } else {
- assert(upper == upparent->left);
- upparent->left = lower;
- }
-
- lower->right = upper;
- upper->parent = lower;
-}
-
-/*
- * Do a postorder traversal of the tree rooted at the specified
- * node and free everything under it. Used by dict_free().
- */
-
-static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
-{
- if (node == nil)
- return;
- free_nodes(dict, node->left, nil);
- free_nodes(dict, node->right, nil);
- dict->freenode(node, dict->context);
-}
-
-/*
- * This procedure performs a verification that the given subtree is a binary
- * search tree. It performs an inorder traversal of the tree using the
- * dict_next() successor function, verifying that the key of each node is
- * strictly lower than that of its successor, if duplicates are not allowed,
- * or lower or equal if duplicates are allowed. This function is used for
- * debugging purposes.
- */
-
-static int verify_bintree(dict_t *dict)
-{
- dnode_t *first, *next;
-
- first = dict_first(dict);
-
- if (dict->dupes) {
- while (first && (next = dict_next(dict, first))) {
- if (dict->compare(first->key, next->key) > 0)
- return 0;
- first = next;
- }
- } else {
- while (first && (next = dict_next(dict, first))) {
- if (dict->compare(first->key, next->key) >= 0)
- return 0;
- first = next;
- }
- }
- return 1;
-}
-
-
-/*
- * This function recursively verifies that the given binary subtree satisfies
- * three of the red black properties. It checks that every red node has only
- * black children. It makes sure that each node is either red or black. And it
- * checks that every path has the same count of black nodes from root to leaf.
- * It returns the blackheight of the given subtree; this allows blackheights to
- * be computed recursively and compared for left and right siblings for
- * mismatches. It does not check for every nil node being black, because there
- * is only one sentinel nil node. The return value of this function is the
- * black height of the subtree rooted at the node ``root'', or zero if the
- * subtree is not red-black.
- */
-
-#ifdef EXTREME_DICT_DEBUG
-static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
-{
- unsigned height_left, height_right;
-
- if (root != nil) {
- height_left = verify_redblack(nil, root->left);
- height_right = verify_redblack(nil, root->right);
- if (height_left == 0 || height_right == 0)
- return 0;
- if (height_left != height_right)
- return 0;
- if (root->color == dnode_red) {
- if (root->left->color != dnode_black)
- return 0;
- if (root->right->color != dnode_black)
- return 0;
- return height_left;
- }
- if (root->color != dnode_black)
- return 0;
- return height_left + 1;
- }
- return 1;
-}
-#endif
-
-/*
- * Compute the actual count of nodes by traversing the tree and
- * return it. This could be compared against the stored count to
- * detect a mismatch.
- */
-
-#ifdef EXTREME_DICT_DEBUG
-static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
-{
- if (root == nil)
- return 0;
- else
- return 1 + verify_node_count(nil, root->left)
- + verify_node_count(nil, root->right);
-}
-#endif
-
-/*
- * Verify that the tree contains the given node. This is done by
- * traversing all of the nodes and comparing their pointers to the
- * given pointer. Returns 1 if the node is found, otherwise
- * returns zero. It is intended for debugging purposes.
- */
-
-static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
-{
- if (root != nil) {
- return root == node
- || verify_dict_has_node(nil, root->left, node)
- || verify_dict_has_node(nil, root->right, node);
- }
- return 0;
-}
-
-
-/*
- * Dynamically allocate and initialize a dictionary object.
- */
-
-dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
-{
- dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t));
-
- new->compare = comp;
- new->allocnode = dnode_alloc;
- new->freenode = dnode_free;
- new->context = NULL;
- new->nodecount = 0;
- new->maxcount = maxcount;
- new->nilnode.left = &new->nilnode;
- new->nilnode.right = &new->nilnode;
- new->nilnode.parent = &new->nilnode;
- new->nilnode.color = dnode_black;
- new->dupes = 0;
-
- return new;
-}
-
-/*
- * Select a different set of node allocator routines.
- */
-
-void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr,
- void *context)
-{
- assert(dict_count(dict) == 0);
- assert((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
-
- dict->allocnode = al ? al : dnode_alloc;
- dict->freenode = fr ? fr : dnode_free;
- dict->context = context;
-}
-
-/*
- * Free a dynamically allocated dictionary object. Removing the nodes
- * from the tree before deleting it is required.
- */
-
-void dict_destroy(dict_t *dict)
-{
- assert(dict_isempty(dict));
- XFREE(MTYPE_ISIS_DICT, dict);
-}
-
-/*
- * Free all the nodes in the dictionary by using the dictionary's
- * installed free routine. The dictionary is emptied.
- */
-
-void dict_free_nodes(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
- free_nodes(dict, root, nil);
- dict->nodecount = 0;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
-}
-
-/*
- * Obsolescent function, equivalent to dict_free_nodes
- */
-
-void dict_free(dict_t *dict)
-{
- dict_free_nodes(dict);
-}
-
-/*
- * Initialize a user-supplied dictionary object.
- */
-
-dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
-{
- dict->compare = comp;
- dict->allocnode = dnode_alloc;
- dict->freenode = dnode_free;
- dict->context = NULL;
- dict->nodecount = 0;
- dict->maxcount = maxcount;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- dict->nilnode.color = dnode_black;
- dict->dupes = 0;
- return dict;
-}
-
-/*
- * Initialize a dictionary in the likeness of another dictionary
- */
-
-void dict_init_like(dict_t *dict, const dict_t *template)
-{
- dict->compare = template->compare;
- dict->allocnode = template->allocnode;
- dict->freenode = template->freenode;
- dict->context = template->context;
- dict->nodecount = 0;
- dict->maxcount = template->maxcount;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- dict->nilnode.color = dnode_black;
- dict->dupes = template->dupes;
-
- assert(dict_similar(dict, template));
-}
-
-/*
- * Remove all nodes from the dictionary (without freeing them in any way).
- */
-
-static void dict_clear(dict_t *dict)
-{
- dict->nodecount = 0;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- assert(dict->nilnode.color == dnode_black);
-}
-
-
-/*
- * Verify the integrity of the dictionary structure. This is provided for
- * debugging purposes, and should be placed in assert statements. Just because
- * this function succeeds doesn't mean that the tree is not corrupt. Certain
- * corruptions in the tree may simply cause undefined behavior.
- */
-
-int dict_verify(dict_t *dict)
-{
-#ifdef EXTREME_DICT_DEBUG
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
-
- /* check that the sentinel node and root node are black */
- if (root->color != dnode_black)
- return 0;
- if (nil->color != dnode_black)
- return 0;
- if (nil->right != nil)
- return 0;
- /* nil->left is the root node; check that its parent pointer is nil */
- if (nil->left->parent != nil)
- return 0;
- /* perform a weak test that the tree is a binary search tree */
- if (!verify_bintree(dict))
- return 0;
- /* verify that the tree is a red-black tree */
- if (!verify_redblack(nil, root))
- return 0;
- if (verify_node_count(nil, root) != dict_count(dict))
- return 0;
-#endif
- return 1;
-}
-
-/*
- * Determine whether two dictionaries are similar: have the same comparison and
- * allocator functions, and same status as to whether duplicates are allowed.
- */
-
-int dict_similar(const dict_t *left, const dict_t *right)
-{
- if (left->compare != right->compare)
- return 0;
-
- if (left->allocnode != right->allocnode)
- return 0;
-
- if (left->freenode != right->freenode)
- return 0;
-
- if (left->context != right->context)
- return 0;
-
- if (left->dupes != right->dupes)
- return 0;
-
- return 1;
-}
-
-/*
- * Locate a node in the dictionary having the given key.
- * If the node is not found, a null a pointer is returned (rather than
- * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
- * located node is returned.
- */
-
-dnode_t *dict_lookup(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *saved;
- int result;
-
- /* simple binary search adapted for trees that contain duplicate keys */
-
- while (root != nil) {
- result = dict->compare(key, root->key);
- if (result < 0)
- root = root->left;
- else if (result > 0)
- root = root->right;
- else {
- if (!dict->dupes) { /* no duplicates, return match
- */
- return root;
- } else { /* could be dupes, find leftmost one */
- do {
- saved = root;
- root = root->left;
- while (root != nil
- && dict->compare(key, root->key))
- root = root->right;
- } while (root != nil);
- return saved;
- }
- }
- }
-
- return NULL;
-}
-
-/*
- * Look for the node corresponding to the lowest key that is equal to or
- * greater than the given key. If there is no such node, return null.
- */
-
-dnode_t *dict_lower_bound(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *tentative = 0;
-
- while (root != nil) {
- int result = dict->compare(key, root->key);
-
- if (result > 0) {
- root = root->right;
- } else if (result < 0) {
- tentative = root;
- root = root->left;
- } else {
- if (!dict->dupes) {
- return root;
- } else {
- tentative = root;
- root = root->left;
- }
- }
- }
-
- return tentative;
-}
-
-/*
- * Look for the node corresponding to the greatest key that is equal to or
- * lower than the given key. If there is no such node, return null.
- */
-
-dnode_t *dict_upper_bound(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *tentative = 0;
-
- while (root != nil) {
- int result = dict->compare(key, root->key);
-
- if (result < 0) {
- root = root->left;
- } else if (result > 0) {
- tentative = root;
- root = root->right;
- } else {
- if (!dict->dupes) {
- return root;
- } else {
- tentative = root;
- root = root->right;
- }
- }
- }
-
- return tentative;
-}
-
-/*
- * Insert a node into the dictionary. The node should have been
- * initialized with a data field. All other fields are ignored.
- * The behavior is undefined if the user attempts to insert into
- * a dictionary that is already full (for which the dict_isfull()
- * function returns true).
- */
-
-void dict_insert(dict_t *dict, dnode_t *node, const void *key)
-{
- dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
- dnode_t *parent = nil, *uncle, *grandpa;
- int result = -1;
-
- node->key = key;
-
- assert(!dict_isfull(dict));
- assert(!dict_contains(dict, node));
- assert(!dnode_is_in_a_dict(node));
-
- /* basic binary tree insert */
-
- while (where != nil) {
- parent = where;
- result = dict->compare(key, where->key);
- /* trap attempts at duplicate key insertion unless it's
- * explicitly allowed */
- assert(dict->dupes || result != 0);
- if (result < 0)
- where = where->left;
- else
- where = where->right;
- }
-
- assert(where == nil);
-
- if (result < 0)
- parent->left = node;
- else
- parent->right = node;
-
- node->parent = parent;
- node->left = nil;
- node->right = nil;
-
- dict->nodecount++;
-
- /* red black adjustments */
-
- node->color = dnode_red;
-
- while (parent->color == dnode_red) {
- grandpa = parent->parent;
- if (parent == grandpa->left) {
- uncle = grandpa->right;
- if (uncle->color
- == dnode_red) { /* red parent, red uncle */
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else { /* red parent, black uncle */
- if (node == parent->right) {
- rotate_left(parent);
- parent = node;
- assert(grandpa == parent->parent);
- /* rotation between parent and child
- * preserves grandpa */
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_right(grandpa);
- break;
- }
- } else { /* symmetric cases: parent == parent->parent->right */
- uncle = grandpa->left;
- if (uncle->color == dnode_red) {
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else {
- if (node == parent->left) {
- rotate_right(parent);
- parent = node;
- assert(grandpa == parent->parent);
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_left(grandpa);
- break;
- }
- }
- }
-
- dict_root(dict)->color = dnode_black;
-
- assert(dict_verify(dict));
-}
-
-/*
- * Delete the given node from the dictionary. If the given node does not belong
- * to the given dictionary, undefined behavior results. A pointer to the
- * deleted node is returned.
- */
-
-dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
-{
- dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
-
- /* basic deletion */
-
- assert(!dict_isempty(dict));
- assert(dict_contains(dict, delete));
-
- /*
- * If the node being deleted has two children, then we replace it with
- * its
- * successor (i.e. the leftmost node in the right subtree.) By doing
- * this,
- * we avoid the traditional algorithm under which the successor's key
- * and
- * value *only* move to the deleted node and the successor is spliced
- * out
- * from the tree. We cannot use this approach because the user may hold
- * pointers to the successor, or nodes may be inextricably tied to some
- * other structures by way of embedding, etc. So we must splice out the
- * node we are given, not some other node, and must not move contents
- * from
- * one node to another behind the user's back.
- */
-
- if (delete->left != nil && delete->right != nil) {
- dnode_t *next = dict_next(dict, delete);
- assert(next);
- dnode_t *nextparent = next->parent;
- dnode_color_t nextcolor = next->color;
-
- assert(next != nil);
- assert(next->parent != nil);
- assert(next->left == nil);
-
- /*
- * First, splice out the successor from the tree completely, by
- * moving up its right child into its place.
- */
-
- child = next->right;
- child->parent = nextparent;
-
- if (nextparent->left == next) {
- nextparent->left = child;
- } else {
- assert(nextparent->right == next);
- nextparent->right = child;
- }
-
- /*
- * Now that the successor has been extricated from the tree,
- * install it
- * in place of the node that we want deleted.
- */
-
- next->parent = delparent;
- next->left = delete->left;
- next->right = delete->right;
- next->left->parent = next;
- next->right->parent = next;
- next->color = delete->color;
- delete->color = nextcolor;
-
- if (delparent->left == delete) {
- delparent->left = next;
- } else {
- assert(delparent->right == delete);
- delparent->right = next;
- }
-
- } else {
- assert(delete != nil);
- assert(delete->left == nil || delete->right == nil);
-
- child = (delete->left != nil) ? delete->left : delete->right;
-
- child->parent = delparent = delete->parent;
-
- if (delete == delparent->left) {
- delparent->left = child;
- } else {
- assert(delete == delparent->right);
- delparent->right = child;
- }
- }
-
- delete->parent = NULL;
- delete->right = NULL;
- delete->left = NULL;
-
- dict->nodecount--;
-
- assert(verify_bintree(dict));
-
- /* red-black adjustments */
-
- if (delete->color == dnode_black) {
- dnode_t *parent, *sister;
-
- dict_root(dict)->color = dnode_red;
-
- while (child->color == dnode_black) {
- parent = child->parent;
- if (child == parent->left) {
- sister = parent->right;
- assert(sister != nil);
- if (sister->color == dnode_red) {
- sister->color = dnode_black;
- parent->color = dnode_red;
- rotate_left(parent);
- sister = parent->right;
- assert(sister != nil);
- }
- if (sister->left->color == dnode_black
- && sister->right->color == dnode_black) {
- sister->color = dnode_red;
- child = parent;
- } else {
- if (sister->right->color
- == dnode_black) {
- assert(sister->left->color
- == dnode_red);
- sister->left->color =
- dnode_black;
- sister->color = dnode_red;
- rotate_right(sister);
- sister = parent->right;
- assert(sister != nil);
- }
- sister->color = parent->color;
- sister->right->color = dnode_black;
- parent->color = dnode_black;
- rotate_left(parent);
- break;
- }
- } else { /* symmetric case: child ==
- child->parent->right */
- assert(child == parent->right);
- sister = parent->left;
- assert(sister != nil);
- if (sister->color == dnode_red) {
- sister->color = dnode_black;
- parent->color = dnode_red;
- rotate_right(parent);
- sister = parent->left;
- assert(sister != nil);
- }
- if (sister->right->color == dnode_black
- && sister->left->color == dnode_black) {
- sister->color = dnode_red;
- child = parent;
- } else {
- if (sister->left->color
- == dnode_black) {
- assert(sister->right->color
- == dnode_red);
- sister->right->color =
- dnode_black;
- sister->color = dnode_red;
- rotate_left(sister);
- sister = parent->left;
- assert(sister != nil);
- }
- sister->color = parent->color;
- sister->left->color = dnode_black;
- parent->color = dnode_black;
- rotate_right(parent);
- break;
- }
- }
- }
-
- child->color = dnode_black;
- dict_root(dict)->color = dnode_black;
- }
-
- assert(dict_verify(dict));
-
- return delete;
-}
-
-/*
- * Allocate a node using the dictionary's allocator routine, give it
- * the data item.
- */
-
-int dict_alloc_insert(dict_t *dict, const void *key, void *data)
-{
- dnode_t *node = dict->allocnode(dict->context);
-
- if (node) {
- dnode_init(node, data);
- dict_insert(dict, node, key);
- return 1;
- }
- return 0;
-}
-
-void dict_delete_free(dict_t *dict, dnode_t *node)
-{
- dict_delete(dict, node);
- dict->freenode(node, dict->context);
-}
-
-/*
- * Return the node with the lowest (leftmost) key. If the dictionary is empty
- * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
- */
-
-dnode_t *dict_first(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
-
- if (root != nil)
- while ((left = root->left) != nil)
- root = left;
-
- return (root == nil) ? NULL : root;
-}
-
-/*
- * Return the node with the highest (rightmost) key. If the dictionary is empty
- * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
- */
-
-dnode_t *dict_last(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
-
- if (root != nil)
- while ((right = root->right) != nil)
- root = right;
-
- return (root == nil) ? NULL : root;
-}
-
-/*
- * Return the given node's successor node---the node which has the
- * next key in the the left to right ordering. If the node has
- * no successor, a null pointer is returned rather than a pointer to
- * the nil node.
- */
-
-dnode_t *dict_next(dict_t *dict, dnode_t *curr)
-{
- dnode_t *nil = dict_nil(dict), *parent, *left;
-
- if (curr->right != nil) {
- curr = curr->right;
- while ((left = curr->left) != nil)
- curr = left;
- return curr;
- }
-
- parent = curr->parent;
-
- while (parent != nil && curr == parent->right) {
- curr = parent;
- parent = curr->parent;
- }
-
- return (parent == nil) ? NULL : parent;
-}
-
-/*
- * Return the given node's predecessor, in the key order.
- * The nil sentinel node is returned if there is no predecessor.
- */
-
-dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
-{
- dnode_t *nil = dict_nil(dict), *parent, *right;
-
- if (curr->left != nil) {
- curr = curr->left;
- while ((right = curr->right) != nil)
- curr = right;
- return curr;
- }
-
- parent = curr->parent;
-
- while (parent != nil && curr == parent->left) {
- curr = parent;
- parent = curr->parent;
- }
-
- return (parent == nil) ? NULL : parent;
-}
-
-void dict_allow_dupes(dict_t *dict)
-{
- dict->dupes = 1;
-}
-
-#undef dict_count
-#undef dict_isempty
-#undef dict_isfull
-#undef dnode_get
-#undef dnode_put
-#undef dnode_getkey
-
-dictcount_t dict_count(dict_t *dict)
-{
- return dict->nodecount;
-}
-
-int dict_isempty(dict_t *dict)
-{
- return dict->nodecount == 0;
-}
-
-int dict_isfull(dict_t *dict)
-{
- return dict->nodecount == dict->maxcount;
-}
-
-int dict_contains(dict_t *dict, dnode_t *node)
-{
- return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
-}
-
-static dnode_t *dnode_alloc(void *context)
-{
- return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t));
-}
-
-static void dnode_free(dnode_t *node, void *context)
-{
- XFREE(MTYPE_ISIS_DICT_NODE, node);
-}
-
-dnode_t *dnode_create(void *data)
-{
- dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t));
-
- new->data = data;
- new->parent = NULL;
- new->left = NULL;
- new->right = NULL;
-
- return new;
-}
-
-dnode_t *dnode_init(dnode_t *dnode, void *data)
-{
- dnode->data = data;
- dnode->parent = NULL;
- dnode->left = NULL;
- dnode->right = NULL;
- return dnode;
-}
-
-void dnode_destroy(dnode_t *dnode)
-{
- assert(!dnode_is_in_a_dict(dnode));
- XFREE(MTYPE_ISIS_DICT_NODE, dnode);
-}
-
-void *dnode_get(dnode_t *dnode)
-{
- return dnode->data;
-}
-
-const void *dnode_getkey(dnode_t *dnode)
-{
- return dnode->key;
-}
-
-void dnode_put(dnode_t *dnode, void *data)
-{
- dnode->data = data;
-}
-
-int dnode_is_in_a_dict(dnode_t *dnode)
-{
- return (dnode->parent && dnode->left && dnode->right);
-}
-
-void dict_process(dict_t *dict, void *context, dnode_process_t function)
-{
- dnode_t *node = dict_first(dict), *next;
-
- while (node != NULL) {
- /* check for callback function deleting */
- /* the next node from under us */
- assert(dict_contains(dict, node));
- next = dict_next(dict, node);
- function(dict, node, context);
- node = next;
- }
-}
-
-static void load_begin_internal(dict_load_t *load, dict_t *dict)
-{
- load->dictptr = dict;
- load->nilnode.left = &load->nilnode;
- load->nilnode.right = &load->nilnode;
-}
-
-void dict_load_begin(dict_load_t *load, dict_t *dict)
-{
- assert(dict_isempty(dict));
- load_begin_internal(load, dict);
-}
-
-void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
-{
- dict_t *dict = load->dictptr;
- dnode_t *nil = &load->nilnode;
-
- assert(!dnode_is_in_a_dict(newnode));
- assert(dict->nodecount < dict->maxcount);
-
-#ifndef NDEBUG
- if (dict->nodecount > 0) {
- if (dict->dupes)
- assert(dict->compare(nil->left->key, key) <= 0);
- else
- assert(dict->compare(nil->left->key, key) < 0);
- }
-#endif
-
- newnode->key = key;
- nil->right->left = newnode;
- nil->right = newnode;
- newnode->left = nil;
- dict->nodecount++;
-}
-
-void dict_load_end(dict_load_t *load)
-{
- dict_t *dict = load->dictptr;
- dnode_t *tree[DICT_DEPTH_MAX] = {0};
- dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode,
- *next;
- dnode_t *complete = 0;
- dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
- dictcount_t botrowcount;
- unsigned baselevel = 0, level = 0, i;
-
- assert(dnode_red == 0 && dnode_black == 1);
-
- while (fullcount >= nodecount && fullcount)
- fullcount >>= 1;
-
- botrowcount = nodecount - fullcount;
-
- for (curr = loadnil->left; curr != loadnil; curr = next) {
- next = curr->left;
-
- if (complete == NULL && botrowcount-- == 0) {
- assert(baselevel == 0);
- assert(level == 0);
- baselevel = level = 1;
- complete = tree[0];
-
- if (complete != NULL) {
- tree[0] = 0;
- complete->right = dictnil;
- while (tree[level] != NULL) {
- tree[level]->right = complete;
- complete->parent = tree[level];
- complete = tree[level];
- tree[level++] = 0;
- }
- }
- }
-
- if (complete == NULL) {
- curr->left = dictnil;
- curr->right = dictnil;
- curr->color = level % 2;
- complete = curr;
-
- assert(level == baselevel);
- while (tree[level] != NULL) {
- tree[level]->right = complete;
- complete->parent = tree[level];
- complete = tree[level];
- tree[level++] = 0;
- }
- } else {
- curr->left = complete;
- curr->color = (level + 1) % 2;
- complete->parent = curr;
- tree[level] = curr;
- complete = 0;
- level = baselevel;
- }
- }
-
- if (complete == NULL)
- complete = dictnil;
-
- for (i = 0; i < DICT_DEPTH_MAX; i++) {
- if (tree[i] != NULL) {
- tree[i]->right = complete;
- complete->parent = tree[i];
- complete = tree[i];
- }
- }
-
- dictnil->color = dnode_black;
- dictnil->right = dictnil;
- complete->parent = dictnil;
- complete->color = dnode_black;
- dict_root(dict) = complete;
-
- assert(dict_verify(dict));
-}
-
-void dict_merge(dict_t *dest, dict_t *source)
-{
- dict_load_t load;
- dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
-
- assert(dict_similar(dest, source));
-
- if (source == dest)
- return;
-
- dest->nodecount = 0;
- load_begin_internal(&load, dest);
-
- for (;;) {
- if (leftnode != NULL && rightnode != NULL) {
- if (dest->compare(leftnode->key, rightnode->key) < 0)
- goto copyleft;
- else
- goto copyright;
- } else if (leftnode != NULL) {
- goto copyleft;
- } else if (rightnode != NULL) {
- goto copyright;
- } else {
- assert(leftnode == NULL && rightnode == NULL);
- break;
- }
-
- copyleft : {
- dnode_t *next = dict_next(dest, leftnode);
-#ifndef NDEBUG
- leftnode->left =
- NULL; /* suppress assertion in dict_load_next */
-#endif
- dict_load_next(&load, leftnode, leftnode->key);
- leftnode = next;
- continue;
- }
-
- copyright : {
- dnode_t *next = dict_next(source, rightnode);
-#ifndef NDEBUG
- rightnode->left = NULL;
-#endif
- dict_load_next(&load, rightnode, rightnode->key);
- rightnode = next;
- continue;
- }
- }
-
- dict_clear(source);
- dict_load_end(&load);
-}
-
-#ifdef KAZLIB_TEST_MAIN
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-typedef char input_t[256];
-
-static int tokenize(char *string, ...)
-{
- char **tokptr;
- va_list arglist;
- int tokcount = 0;
-
- va_start(arglist, string);
- tokptr = va_arg(arglist, char **);
- while (tokptr) {
- while (*string && isspace((unsigned char)*string))
- string++;
- if (!*string)
- break;
- *tokptr = string;
- while (*string && !isspace((unsigned char)*string))
- string++;
- tokptr = va_arg(arglist, char **);
- tokcount++;
- if (!*string)
- break;
- *string++ = 0;
- }
- va_end(arglist);
-
- return tokcount;
-}
-
-static int comparef(const void *key1, const void *key2)
-{
- return strcmp(key1, key2);
-}
-
-static char *dupstring(char *str)
-{
- int sz = strlen(str) + 1;
- char *new = XCALLOC(MTYPE_ISIS_TMP, sz);
-
- memcpy(new, str, sz);
- return new;
-}
-
-static dnode_t *new_node(void *c)
-{
- static dnode_t few[5];
- static int count;
-
- if (count < 5)
- return few + count++;
-
- return NULL;
-}
-
-static void del_node(dnode_t *n, void *c)
-{
-}
-
-static int prompt = 0;
-
-static void construct(dict_t *d)
-{
- input_t in;
- int done = 0;
- dict_load_t dl;
- dnode_t *dn;
- char *tok1, *tok2, *val;
- const char *key;
- char *help =
- "p turn prompt on\n"
- "q finish construction\n"
- "a <key> <val> add new entry\n";
-
- if (!dict_isempty(d))
- puts("warning: dictionary not empty!");
-
- dict_load_begin(&dl, d);
-
- while (!done) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch (in[0]) {
- case '?':
- puts(help);
- break;
- case 'p':
- prompt = 1;
- break;
- case 'q':
- done = 1;
- break;
- case 'a':
- if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) {
- puts("what?");
- break;
- }
- key = dupstring(tok1);
- val = dupstring(tok2);
- dn = dnode_create(val);
-
- if (!key || !val || !dn) {
- puts("out of memory");
- free((void *)key);
- free(val);
- if (dn)
- dnode_destroy(dn);
- } else
- dict_load_next(&dl, dn, key);
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- dict_load_end(&dl);
-}
-
-int main(void)
-{
- input_t in;
- dict_t darray[10];
- dict_t *d = &darray[0];
- dnode_t *dn;
- int i;
- char *tok1, *tok2, *val;
- const char *key;
-
- char *help =
- "a <key> <val> add value to dictionary\n"
- "d <key> delete value from dictionary\n"
- "l <key> lookup value in dictionary\n"
- "( <key> lookup lower bound\n"
- ") <key> lookup upper bound\n"
- "# <num> switch to alternate dictionary (0-9)\n"
- "j <num> <num> merge two dictionaries\n"
- "f free the whole dictionary\n"
- "k allow duplicate keys\n"
- "c show number of entries\n"
- "t dump whole dictionary in sort order\n"
- "m make dictionary out of sorted items\n"
- "p turn prompt on\n"
- "s switch to non-functioning allocator\n"
- "q quit";
-
- for (i = 0; i < 10; i++)
- dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
-
- for (;;) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch (in[0]) {
- case '?':
- puts(help);
- break;
- case 'a':
- if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) {
- puts("what?");
- break;
- }
- key = dupstring(tok1);
- val = dupstring(tok2);
-
- if (!key || !val) {
- puts("out of memory");
- free((void *)key);
- free(val);
- }
-
- if (!dict_alloc_insert(d, key, val)) {
- puts("dict_alloc_insert failed");
- free((void *)key);
- free(val);
- break;
- }
- break;
- case 'd':
- if (tokenize(in + 1, &tok1, (char **)0) != 1) {
- puts("what?");
- break;
- }
- dn = dict_lookup(d, tok1);
- if (!dn) {
- puts("dict_lookup failed");
- break;
- }
- val = dnode_get(dn);
- key = dnode_getkey(dn);
- dict_delete_free(d, dn);
-
- free(val);
- free((void *)key);
- break;
- case 'f':
- dict_free(d);
- break;
- case 'l':
- case '(':
- case ')':
- if (tokenize(in + 1, &tok1, (char **)0) != 1) {
- puts("what?");
- break;
- }
- dn = 0;
- switch (in[0]) {
- case 'l':
- dn = dict_lookup(d, tok1);
- break;
- case '(':
- dn = dict_lower_bound(d, tok1);
- break;
- case ')':
- dn = dict_upper_bound(d, tok1);
- break;
- }
- if (!dn) {
- puts("lookup failed");
- break;
- }
- val = dnode_get(dn);
- puts(val);
- break;
- case 'm':
- construct(d);
- break;
- case 'k':
- dict_allow_dupes(d);
- break;
- case 'c':
- printf("%lu\n", (unsigned long)dict_count(d));
- break;
- case 't':
- for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
- printf("%s\t%s\n", (char *)dnode_getkey(dn),
- (char *)dnode_get(dn));
- }
- break;
- case 'q':
- exit(0);
- break;
- case '\0':
- break;
- case 'p':
- prompt = 1;
- break;
- case 's':
- dict_set_allocator(d, new_node, del_node, NULL);
- break;
- case '#':
- if (tokenize(in + 1, &tok1, (char **)0) != 1) {
- puts("what?");
- break;
- } else {
- int dictnum = atoi(tok1);
- if (dictnum < 0 || dictnum > 9) {
- puts("invalid number");
- break;
- }
- d = &darray[dictnum];
- }
- break;
- case 'j':
- if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) {
- puts("what?");
- break;
- } else {
- int dict1 = atoi(tok1), dict2 = atoi(tok2);
- if (dict1 < 0 || dict1 > 9 || dict2 < 0
- || dict2 > 9) {
- puts("invalid number");
- break;
- }
- dict_merge(&darray[dict1], &darray[dict2]);
- }
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- return 0;
-}
-
-#endif
diff --git a/isisd/dict.h b/isisd/dict.h
deleted file mode 100644
index 32683c57d5..0000000000
--- a/isisd/dict.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef DICT_H
-#define DICT_H
-
-#include <limits.h>
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long dictcount_t;
-#define DICTCOUNT_T_MAX ULONG_MAX
-
-/*
- * The dictionary is implemented as a red-black tree
- */
-
-typedef enum { dnode_red, dnode_black } dnode_color_t;
-
-typedef struct dnode_t {
- struct dnode_t *dict_left;
- struct dnode_t *dict_right;
- struct dnode_t *dict_parent;
- dnode_color_t dict_color;
- const void *dict_key;
- void *dict_data;
-} dnode_t;
-
-typedef int (*dict_comp_t)(const void *, const void *);
-typedef dnode_t *(*dnode_alloc_t)(void *);
-typedef void (*dnode_free_t)(dnode_t *, void *);
-
-typedef struct dict_t {
- dnode_t dict_nilnode;
- dictcount_t dict_nodecount;
- dictcount_t dict_maxcount;
- dict_comp_t dict_compare;
- dnode_alloc_t dict_allocnode;
- dnode_free_t dict_freenode;
- void *dict_context;
- int dict_dupes;
-} dict_t;
-
-typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
-
-typedef struct dict_load_t {
- dict_t *dict_dictptr;
- dnode_t dict_nilnode;
-} dict_load_t;
-
-extern dict_t *dict_create(dictcount_t, dict_comp_t);
-extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
-extern void dict_destroy(dict_t *);
-extern void dict_free_nodes(dict_t *);
-extern void dict_free(dict_t *);
-extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
-extern void dict_init_like(dict_t *, const dict_t *);
-extern int dict_verify(dict_t *);
-extern int dict_similar(const dict_t *, const dict_t *);
-extern dnode_t *dict_lookup(dict_t *, const void *);
-extern dnode_t *dict_lower_bound(dict_t *, const void *);
-extern dnode_t *dict_upper_bound(dict_t *, const void *);
-extern void dict_insert(dict_t *, dnode_t *, const void *);
-extern dnode_t *dict_delete(dict_t *, dnode_t *);
-extern int dict_alloc_insert(dict_t *, const void *, void *);
-extern void dict_delete_free(dict_t *, dnode_t *);
-extern dnode_t *dict_first(dict_t *);
-extern dnode_t *dict_last(dict_t *);
-extern dnode_t *dict_next(dict_t *, dnode_t *);
-extern dnode_t *dict_prev(dict_t *, dnode_t *);
-extern dictcount_t dict_count(dict_t *);
-extern int dict_isempty(dict_t *);
-extern int dict_isfull(dict_t *);
-extern int dict_contains(dict_t *, dnode_t *);
-extern void dict_allow_dupes(dict_t *);
-extern int dnode_is_in_a_dict(dnode_t *);
-extern dnode_t *dnode_create(void *);
-extern dnode_t *dnode_init(dnode_t *, void *);
-extern void dnode_destroy(dnode_t *);
-extern void *dnode_get(dnode_t *);
-extern const void *dnode_getkey(dnode_t *);
-extern void dnode_put(dnode_t *, void *);
-extern void dict_process(dict_t *, void *, dnode_process_t);
-extern void dict_load_begin(dict_load_t *, dict_t *);
-extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
-extern void dict_load_end(dict_load_t *);
-extern void dict_merge(dict_t *, dict_t *);
-
-#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
-#define dict_count(D) ((D)->dict_nodecount)
-#define dict_isempty(D) ((D)->dict_nodecount == 0)
-#define dnode_get(N) ((N)->dict_data)
-#define dnode_getkey(N) ((N)->dict_key)
-#define dnode_put(N, X) ((N)->dict_data = (X))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/isisd/fabricd.c b/isisd/fabricd.c
index 96af28f0a1..b9c27d51bd 100644
--- a/isisd/fabricd.c
+++ b/isisd/fabricd.c
@@ -108,9 +108,9 @@ static void neighbor_lists_clear(struct fabricd *f)
hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
}
-static unsigned neighbor_entry_hash_key(void *np)
+static unsigned neighbor_entry_hash_key(const void *np)
{
- struct neighbor_entry *n = np;
+ const struct neighbor_entry *n = np;
return jhash(n->id, sizeof(n->id), 0x55aa5a5a);
}
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 62814329ea..9b368cc404 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -32,7 +32,6 @@
#include "if.h"
#include "stream.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c
index 81976f8dd2..fa89c80049 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -92,14 +92,14 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down");
}
-static int isis_bfd_interface_dest_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct prefix dst_ip;
int status;
- ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, vrf_id);
+ ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status,
+ NULL, vrf_id);
if (!ifp || dst_ip.family != AF_INET)
return 0;
@@ -138,10 +138,9 @@ static int isis_bfd_interface_dest_update(int command, struct zclient *zclient,
return 0;
}
-static int isis_bfd_nbr_replay(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
{
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
struct listnode *anode;
struct isis_area *area;
@@ -169,7 +168,7 @@ static void isis_bfd_zebra_connected(struct zclient *zclient)
if (orig_zebra_connected)
orig_zebra_connected(zclient);
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
}
static void bfd_debug(struct in_addr *dst, struct in_addr *src,
@@ -219,6 +218,7 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj)
adj->circuit->interface->name,
0, /* ttl */
0, /* multihop */
+ 1, /* control plane independent bit is on */
ZEBRA_BFD_DEST_DEREGISTER,
0, /* set_flag */
VRF_DEFAULT);
@@ -260,6 +260,7 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
circuit->interface->name,
0, /* ttl */
0, /* multihop */
+ 1, /* control plane independent bit is on */
command,
0, /* set flag */
VRF_DEFAULT);
diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
index 28750278b0..4e9aef47ad 100644
--- a/isisd/isis_bpf.c
+++ b/isisd/isis_bpf.c
@@ -34,7 +34,6 @@
#include "if.h"
#include "lib_errors.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_circuit.h"
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 8377638b92..8d008d78bd 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -40,7 +40,6 @@
#include "qobj.h"
#include "lib/northbound_cli.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -540,7 +539,6 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit,
{
struct isis_area *area;
struct isis_lsp *lsp;
- dnode_t *dnode;
int level;
assert(circuit);
@@ -550,14 +548,10 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit,
if (!(level & circuit->is_type))
continue;
- if (!area->lspdb[level - 1]
- || !dict_count(area->lspdb[level - 1]))
+ if (!lspdb_count(&area->lspdb[level - 1]))
continue;
- for (dnode = dict_first(area->lspdb[level - 1]);
- dnode != NULL;
- dnode = dict_next(area->lspdb[level - 1], dnode)) {
- lsp = dnode_get(dnode);
+ frr_each (lspdb, &area->lspdb[level - 1], lsp) {
if (is_set) {
isis_tx_queue_add(circuit->tx_queue, lsp,
TX_LSP_NORMAL);
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index e0ea4f78b4..2371c0b73a 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -121,8 +121,7 @@ struct isis_circuit {
uint16_t psnp_interval[2]; /* psnp-interval in seconds */
uint8_t metric[2];
uint32_t te_metric[2];
- struct mpls_te_circuit
- *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */
+ struct mpls_te_circuit *mtc; /* MPLS-TE parameters */
int ip_router; /* Route IP ? */
int is_passive; /* Is Passive ? */
struct list *mt_settings; /* IS-IS MT Settings */
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 5f7be16034..0334b98a12 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -188,10 +188,14 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
}
/* check if the interface is a loopback and if so set it as passive */
- ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
- if (ifp && if_is_loopback(ifp))
- nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
- NB_OP_MODIFY, "true");
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ if (ifp && if_is_loopback(ifp))
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
+ NB_OP_MODIFY, "true");
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return nb_cli_apply_changes(vty, NULL);
}
@@ -258,10 +262,14 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
}
/* check if the interface is a loopback and if so set it as passive */
- ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
- if (ifp && if_is_loopback(ifp))
- nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
- NB_OP_MODIFY, "true");
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ if (ifp && if_is_loopback(ifp))
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
+ NB_OP_MODIFY, "true");
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return nb_cli_apply_changes(vty, NULL);
}
@@ -368,20 +376,26 @@ DEFPY(no_is_type, no_is_type_cmd,
"Act as both a station router and an area router\n"
"Act as an area router only\n")
{
- const char *value = NULL;
- struct isis_area *area;
+ const char *value;
- area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ struct isis_area *area;
+
+ area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+
+ /*
+ * Put the is-type back to defaults:
+ * - level-1-2 on first area
+ * - level-1 for the rest
+ */
+ if (area && listgetdata(listhead(isis->area_list)) == area)
+ value = "level-1-2";
+ else
+ value = NULL;
+ }
+ pthread_rwlock_unlock(&running_config->lock);
- /*
- * Put the is-type back to defaults:
- * - level-1-2 on first area
- * - level-1 for the rest
- */
- if (area && listgetdata(listhead(isis->area_list)) == area)
- value = "level-1-2";
- else
- value = NULL;
nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, value);
return nb_cli_apply_changes(vty, NULL);
@@ -928,12 +942,12 @@ void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode,
}
/*
- * XPath: /frr-isisd:isis/mpls-te
+ * XPath: /frr-isisd:isis/instance/mpls-te
*/
DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on",
MPLS_TE_STR "Enable the MPLS-TE functionality\n")
{
- nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_CREATE,
+ nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_CREATE,
NULL);
return nb_cli_apply_changes(vty, NULL);
@@ -942,9 +956,9 @@ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on",
DEFPY(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]",
NO_STR
"Disable the MPLS-TE functionality\n"
- "Enable the MPLS-TE functionality\n")
+ "Disable the MPLS-TE functionality\n")
{
- nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_DESTROY,
+ nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_DESTROY,
NULL);
return nb_cli_apply_changes(vty, NULL);
@@ -957,7 +971,7 @@ void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode,
}
/*
- * XPath: /frr-isisd:isis/mpls-te/router-address
+ * XPath: /frr-isisd:isis/instance/mpls-te/router-address
*/
DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd,
"mpls-te router-address A.B.C.D",
@@ -965,12 +979,24 @@ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd,
"Stable IP address of the advertising router\n"
"MPLS-TE router address in IPv4 address format\n")
{
- nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te/router-address",
+ nb_cli_enqueue_change(vty, "./mpls-te/router-address",
NB_OP_MODIFY, router_address_str);
return nb_cli_apply_changes(vty, NULL);
}
+DEFPY(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd,
+ "no mpls-te router-address [A.B.C.D]",
+ NO_STR MPLS_TE_STR
+ "Delete IP address of the advertising router\n"
+ "MPLS-TE router address in IPv4 address format\n")
+{
+ nb_cli_enqueue_change(vty, "./mpls-te/router-address",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
@@ -986,7 +1012,7 @@ DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd,
"AREA native mode self originate INTER-AS LSP with L1 and L2 flooding scope\n"
"AS native mode self originate INTER-AS LSP with L2 only flooding scope\n")
{
- vty_out(vty, "MPLS-TE Inter-AS is not yet supported.");
+ vty_out(vty, "MPLS-TE Inter-AS is not yet supported\n");
return CMD_SUCCESS;
}
@@ -1757,50 +1783,43 @@ DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd,
"Level-1-2 adjacencies are formed\n"
"Level-2 only adjacencies are formed\n")
{
- struct interface *ifp;
- struct isis_circuit *circuit;
- int is_type;
- const char *circ_type;
+ const char *circ_type = NULL;
/*
* Default value depends on whether the circuit is part of an area,
* and the is-type of the area if there is one. So we need to do this
* here.
*/
- ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
- if (!ifp)
- goto def_val;
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ struct interface *ifp;
+ struct isis_circuit *circuit;
- circuit = circuit_scan_by_ifp(ifp);
- if (!circuit)
- goto def_val;
+ ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ if (!ifp)
+ goto unlock;
- if (circuit->state == C_STATE_UP)
- is_type = circuit->area->is_type;
- else
- goto def_val;
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit || circuit->state != C_STATE_UP)
+ goto unlock;
- switch (is_type) {
- case IS_LEVEL_1:
- circ_type = "level-1";
- break;
- case IS_LEVEL_2:
- circ_type = "level-2";
- break;
- case IS_LEVEL_1_AND_2:
- circ_type = "level-1-2";
- break;
- default:
- return CMD_ERR_NO_MATCH;
+ switch (circuit->area->is_type) {
+ case IS_LEVEL_1:
+ circ_type = "level-1";
+ break;
+ case IS_LEVEL_2:
+ circ_type = "level-2";
+ break;
+ case IS_LEVEL_1_AND_2:
+ circ_type = "level-1-2";
+ break;
+ }
}
- nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type",
- NB_OP_MODIFY, circ_type);
+unlock:
+ pthread_rwlock_unlock(&running_config->lock);
- return nb_cli_apply_changes(vty, NULL);
-
-def_val:
nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type",
- NB_OP_MODIFY, NULL);
+ NB_OP_MODIFY, circ_type);
return nb_cli_apply_changes(vty, NULL);
}
@@ -1967,6 +1986,7 @@ void isis_cli_init(void)
install_element(ISIS_NODE, &isis_mpls_te_on_cmd);
install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd);
install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd);
+ install_element(ISIS_NODE, &no_isis_mpls_te_router_addr_cmd);
install_element(ISIS_NODE, &isis_mpls_te_inter_as_cmd);
install_element(ISIS_NODE, &isis_default_originate_cmd);
diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c
index e50f57ae1d..88676dd990 100644
--- a/isisd/isis_csm.c
+++ b/isisd/isis_csm.c
@@ -32,7 +32,6 @@
#include "prefix.h"
#include "stream.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
index 148b438661..a96dd93804 100644
--- a/isisd/isis_dlpi.c
+++ b/isisd/isis_dlpi.c
@@ -38,7 +38,6 @@
#include "if.h"
#include "lib_errors.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_circuit.h"
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c
index 449648656a..7be5307500 100644
--- a/isisd/isis_dr.c
+++ b/isisd/isis_dr.c
@@ -32,7 +32,6 @@
#include "stream.h"
#include "if.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_misc.h"
diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c
index 1d29d1086d..921e23d33a 100644
--- a/isisd/isis_dynhn.c
+++ b/isisd/isis_dynhn.c
@@ -31,7 +31,6 @@
#include "if.h"
#include "thread.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_events.c b/isisd/isis_events.c
index 4da23c5912..6a5dcfb075 100644
--- a/isisd/isis_events.c
+++ b/isisd/isis_events.c
@@ -32,7 +32,6 @@
#include "stream.h"
#include "table.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index b56a56fa3f..4b29e6dc7e 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -40,7 +40,6 @@
#include "srcdest_table.h"
#include "lib_errors.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -63,41 +62,38 @@ static int lsp_refresh(struct thread *thread);
static int lsp_l1_refresh_pseudo(struct thread *thread);
static int lsp_l2_refresh_pseudo(struct thread *thread);
+static void lsp_destroy(struct isis_lsp *lsp);
+
int lsp_id_cmp(uint8_t *id1, uint8_t *id2)
{
return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2);
}
-dict_t *lsp_db_init(void)
+int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b)
{
- dict_t *dict;
-
- dict = dict_create(DICTCOUNT_T_MAX, (dict_comp_t)lsp_id_cmp);
-
- return dict;
+ return memcmp(a->hdr.lsp_id, b->hdr.lsp_id, sizeof(a->hdr.lsp_id));
}
-struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb)
+void lsp_db_init(struct lspdb_head *head)
{
- dnode_t *node;
-
-#ifdef EXTREME_DEBUG
- dnode_t *dn;
+ lspdb_init(head);
+}
- zlog_debug("searching db");
- for (dn = dict_first(lspdb); dn; dn = dict_next(lspdb, dn)) {
- zlog_debug("%s\t%pX",
- rawlspid_print((uint8_t *)dnode_getkey(dn)),
- dnode_get(dn));
- }
-#endif /* EXTREME DEBUG */
+void lsp_db_fini(struct lspdb_head *head)
+{
+ struct isis_lsp *lsp;
- node = dict_lookup(lspdb, id);
+ while ((lsp = lspdb_pop(head)))
+ lsp_destroy(lsp);
+ lspdb_fini(head);
+}
- if (node)
- return (struct isis_lsp *)dnode_get(node);
+struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id)
+{
+ struct isis_lsp searchfor;
+ memcpy(searchfor.hdr.lsp_id, id, sizeof(searchfor.hdr.lsp_id));
- return NULL;
+ return lspdb_find(head, &searchfor);
}
static void lsp_clear_data(struct isis_lsp *lsp)
@@ -109,7 +105,7 @@ static void lsp_clear_data(struct isis_lsp *lsp)
lsp->tlvs = NULL;
}
-static void lsp_remove_frags(struct list *frags, dict_t *lspdb);
+static void lsp_remove_frags(struct lspdb_head *head, struct list *frags);
static void lsp_destroy(struct isis_lsp *lsp)
{
@@ -128,8 +124,8 @@ static void lsp_destroy(struct isis_lsp *lsp)
if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
if (lsp->lspu.frags) {
- lsp_remove_frags(lsp->lspu.frags,
- lsp->area->lspdb[lsp->level - 1]);
+ lsp_remove_frags(&lsp->area->lspdb[lsp->level - 1],
+ lsp->lspu.frags);
list_delete(&lsp->lspu.frags);
}
} else {
@@ -148,56 +144,34 @@ static void lsp_destroy(struct isis_lsp *lsp)
XFREE(MTYPE_ISIS_LSP, lsp);
}
-void lsp_db_destroy(dict_t *lspdb)
-{
- dnode_t *dnode, *next;
- struct isis_lsp *lsp;
-
- dnode = dict_first(lspdb);
- while (dnode) {
- next = dict_next(lspdb, dnode);
- lsp = dnode_get(dnode);
- lsp_destroy(lsp);
- dict_delete_free(lspdb, dnode);
- dnode = next;
- }
-
- dict_free(lspdb);
-
- return;
-}
-
/*
* Remove all the frags belonging to the given lsp
*/
-static void lsp_remove_frags(struct list *frags, dict_t *lspdb)
+static void lsp_remove_frags(struct lspdb_head *head, struct list *frags)
{
- dnode_t *dnode;
struct listnode *lnode, *lnnode;
struct isis_lsp *lsp;
for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) {
- dnode = dict_lookup(lspdb, lsp->hdr.lsp_id);
+ lsp = lsp_search(head, lsp->hdr.lsp_id);
+ lspdb_del(head, lsp);
lsp_destroy(lsp);
- dnode_destroy(dict_delete(lspdb, dnode));
}
}
-void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb)
+void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id)
{
- dnode_t *node;
struct isis_lsp *lsp;
- node = dict_lookup(lspdb, id);
- if (node) {
- node = dict_delete(lspdb, node);
- lsp = dnode_get(node);
+ lsp = lsp_search(head, id);
+ if (lsp) {
+ lspdb_del(head, lsp);
/*
* If this is a zero lsp, remove all the frags now
*/
if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) {
if (lsp->lspu.frags)
- lsp_remove_frags(lsp->lspu.frags, lspdb);
+ lsp_remove_frags(head, lsp->lspu.frags);
} else {
/*
* else just remove this frag, from the zero lsps' frag
@@ -209,7 +183,6 @@ void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb)
lsp);
}
lsp_destroy(lsp);
- dnode_destroy(node);
}
}
@@ -514,7 +487,7 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
memcpy(lspid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT(lspid) = 0;
- lsp0 = lsp_search(lspid, area->lspdb[level - 1]);
+ lsp0 = lsp_search(&area->lspdb[level - 1], lspid);
if (lsp0)
lsp_link_fragment(lsp, lsp0);
}
@@ -582,9 +555,9 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
return lsp;
}
-void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb)
+void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp)
{
- dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp);
+ lspdb_add(head, lsp);
if (lsp->hdr.seqno)
isis_spf_schedule(lsp->area, lsp->level);
}
@@ -592,13 +565,16 @@ void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb)
/*
* Build a list of LSPs with non-zero ht bounded by start and stop ids
*/
-void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id,
- struct list *list, dict_t *lspdb)
+void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id,
+ const uint8_t *stop_id, struct list *list)
{
- for (dnode_t *curr = dict_lower_bound(lspdb, start_id);
- curr; curr = dict_next(lspdb, curr)) {
- struct isis_lsp *lsp = curr->dict_data;
+ struct isis_lsp searchfor;
+ struct isis_lsp *lsp, *start;
+
+ memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
+ start = lspdb_find_gteq(head, &searchfor);
+ frr_each_from (lspdb, head, lsp, start) {
if (memcmp(lsp->hdr.lsp_id, stop_id,
ISIS_SYS_ID_LEN + 2) > 0)
break;
@@ -699,26 +675,20 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost)
}
/* print all the lsps info in the local lspdb */
-int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost)
+int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail,
+ char dynhost)
{
-
- dnode_t *node = dict_first(lspdb), *next;
+ struct isis_lsp *lsp;
int lsp_count = 0;
if (detail == ISIS_UI_LEVEL_BRIEF) {
- while (node != NULL) {
- /* I think it is unnecessary, so I comment it out */
- /* dict_contains (lspdb, node); */
- next = dict_next(lspdb, node);
- lsp_print(dnode_get(node), vty, dynhost);
- node = next;
+ frr_each (lspdb, head, lsp) {
+ lsp_print(lsp, vty, dynhost);
lsp_count++;
}
} else if (detail == ISIS_UI_LEVEL_DETAIL) {
- while (node != NULL) {
- next = dict_next(lspdb, node);
- lsp_print_detail(dnode_get(node), vty, dynhost);
- node = next;
+ frr_each (lspdb, head, lsp) {
+ lsp_print_detail(lsp, vty, dynhost);
lsp_count++;
}
}
@@ -847,7 +817,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0,
memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT(frag_id) = frag_num;
- lsp = lsp_search(frag_id, area->lspdb[level - 1]);
+ lsp = lsp_search(&area->lspdb[level - 1], frag_id);
if (lsp) {
lsp_clear_data(lsp);
if (!lsp->lspu.zero_lsp)
@@ -860,7 +830,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0,
area->attached_bit),
0, lsp0, level);
lsp->own_lsp = 1;
- lsp_insert(lsp, area->lspdb[level - 1]);
+ lsp_insert(&area->lspdb[level - 1], lsp);
return lsp;
}
@@ -1070,7 +1040,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
uint8_t subtlvs[256];
uint8_t subtlv_len;
- if (IS_MPLS_TE(isisMplsTE)
+ if (IS_MPLS_TE(area->mta)
&& circuit->interface
&& HAS_LINK_PARAMS(
circuit->interface))
@@ -1112,7 +1082,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
uint8_t subtlvs[256];
uint8_t subtlv_len;
- if (IS_MPLS_TE(isisMplsTE)
+ if (IS_MPLS_TE(area->mta)
&& circuit->interface != NULL
&& HAS_LINK_PARAMS(
circuit->interface))
@@ -1228,12 +1198,12 @@ int lsp_generate(struct isis_area *area, int level)
memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN);
/* only builds the lsp if the area shares the level */
- oldlsp = lsp_search(lspid, area->lspdb[level - 1]);
+ oldlsp = lsp_search(&area->lspdb[level - 1], lspid);
if (oldlsp) {
/* FIXME: we should actually initiate a purge */
seq_num = oldlsp->hdr.seqno;
- lsp_search_and_destroy(oldlsp->hdr.lsp_id,
- area->lspdb[level - 1]);
+ lsp_search_and_destroy(&area->lspdb[level - 1],
+ oldlsp->hdr.lsp_id);
}
rem_lifetime = lsp_rem_lifetime(area, level);
newlsp =
@@ -1243,7 +1213,7 @@ int lsp_generate(struct isis_area *area, int level)
newlsp->area = area;
newlsp->own_lsp = 1;
- lsp_insert(newlsp, area->lspdb[level - 1]);
+ lsp_insert(&area->lspdb[level - 1], newlsp);
/* build_lsp_data (newlsp, area); */
lsp_build(newlsp, area);
/* time to calculate our checksum */
@@ -1288,7 +1258,7 @@ int lsp_generate(struct isis_area *area, int level)
*/
static int lsp_regenerate(struct isis_area *area, int level)
{
- dict_t *lspdb;
+ struct lspdb_head *head;
struct isis_lsp *lsp, *frag;
struct listnode *node;
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
@@ -1297,12 +1267,12 @@ static int lsp_regenerate(struct isis_area *area, int level)
if ((area == NULL) || (area->is_type & level) != level)
return ISIS_ERROR;
- lspdb = area->lspdb[level - 1];
+ head = &area->lspdb[level - 1];
memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
- lsp = lsp_search(lspid, lspdb);
+ lsp = lsp_search(head, lspid);
if (!lsp) {
flog_err(EC_LIB_DEVELOPMENT,
@@ -1445,7 +1415,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level,
continue;
}
- lsp = lsp_search(id, area->lspdb[lvl - 1]);
+ lsp = lsp_search(&area->lspdb[lvl - 1], id);
if (!lsp) {
sched_debug(
"ISIS (%s): We do not have any LSPs to regenerate, nothing todo.",
@@ -1597,7 +1567,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
{
- dict_t *lspdb = circuit->area->lspdb[level - 1];
+ struct lspdb_head *head = &circuit->area->lspdb[level - 1];
struct isis_lsp *lsp;
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
uint16_t rem_lifetime, refresh_time;
@@ -1615,7 +1585,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
/*
* If for some reason have a pseudo LSP in the db already -> regenerate
*/
- if (lsp_search(lsp_id, lspdb))
+ if (lsp_search(head, lsp_id))
return lsp_regenerate_schedule_pseudo(circuit, level);
rem_lifetime = lsp_rem_lifetime(circuit->area, level);
@@ -1628,7 +1598,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
lsp_build_pseudo(lsp, circuit, level);
lsp_pack_pdu(lsp);
lsp->own_lsp = 1;
- lsp_insert(lsp, lspdb);
+ lsp_insert(head, lsp);
lsp_flood(lsp, NULL);
refresh_time = lsp_refresh_time(lsp, rem_lifetime);
@@ -1659,7 +1629,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
{
- dict_t *lspdb = circuit->area->lspdb[level - 1];
+ struct lspdb_head *head = &circuit->area->lspdb[level - 1];
struct isis_lsp *lsp;
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
uint16_t rem_lifetime, refresh_time;
@@ -1674,7 +1644,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
LSP_FRAGMENT(lsp_id) = 0;
- lsp = lsp_search(lsp_id, lspdb);
+ lsp = lsp_search(head, lsp_id);
if (!lsp) {
flog_err(EC_LIB_DEVELOPMENT,
@@ -1813,7 +1783,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
continue;
}
- lsp = lsp_search(lsp_id, circuit->area->lspdb[lvl - 1]);
+ lsp = lsp_search(&circuit->area->lspdb[lvl - 1], lsp_id);
if (!lsp) {
sched_debug(
"ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.",
@@ -1869,7 +1839,6 @@ int lsp_tick(struct thread *thread)
{
struct isis_area *area;
struct isis_lsp *lsp;
- dnode_t *dnode, *dnode_next;
int level;
uint16_t rem_lifetime;
bool fabricd_sync_incomplete = false;
@@ -1885,83 +1854,69 @@ int lsp_tick(struct thread *thread)
* Remove LSPs which have aged out
*/
for (level = 0; level < ISIS_LEVELS; level++) {
- if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) {
- for (dnode = dict_first(area->lspdb[level]);
- dnode != NULL; dnode = dnode_next) {
- dnode_next =
- dict_next(area->lspdb[level], dnode);
- lsp = dnode_get(dnode);
-
- /*
- * The lsp rem_lifetime is kept at 0 for MaxAge
- * or
- * ZeroAgeLifetime depending on explicit purge
- * or
- * natural age out. So schedule spf only once
- * when
- * the first time rem_lifetime becomes 0.
- */
- rem_lifetime = lsp->hdr.rem_lifetime;
- lsp_set_time(lsp);
-
- /*
- * Schedule may run spf which should be done
- * only after
- * the lsp rem_lifetime becomes 0 for the first
- * time.
- * ISO 10589 - 7.3.16.4 first paragraph.
- */
- if (rem_lifetime == 1 && lsp->hdr.seqno != 0) {
- /* 7.3.16.4 a) set SRM flags on all */
- /* 7.3.16.4 b) retain only the header */
- if (lsp->area->purge_originator)
- lsp_purge(lsp, lsp->level, NULL);
- else
- lsp_flood(lsp, NULL);
- /* 7.3.16.4 c) record the time to purge
- * FIXME */
- isis_spf_schedule(lsp->area, lsp->level);
- }
+ struct isis_lsp *next = lspdb_first(&area->lspdb[level]);
+ frr_each_from (lspdb, &area->lspdb[level], lsp, next) {
+ /*
+ * The lsp rem_lifetime is kept at 0 for MaxAge
+ * or
+ * ZeroAgeLifetime depending on explicit purge
+ * or
+ * natural age out. So schedule spf only once
+ * when
+ * the first time rem_lifetime becomes 0.
+ */
+ rem_lifetime = lsp->hdr.rem_lifetime;
+ lsp_set_time(lsp);
- if (lsp->age_out == 0) {
- zlog_debug(
- "ISIS-Upd (%s): L%u LSP %s seq "
- "0x%08" PRIx32 " aged out",
- area->area_tag, lsp->level,
- rawlspid_print(lsp->hdr.lsp_id),
- lsp->hdr.seqno);
-
- /* if we're aging out fragment 0,
- * lsp_destroy() below will delete all
- * other fragments too, so we need to
- * skip over those
- */
- while (!LSP_FRAGMENT(lsp->hdr.lsp_id)
- && dnode_next) {
- struct isis_lsp *nextlsp;
-
- nextlsp = dnode_get(dnode_next);
- if (memcmp(nextlsp->hdr.lsp_id,
- lsp->hdr.lsp_id,
- ISIS_SYS_ID_LEN + 1))
- break;
-
- dnode_next = dict_next(
- area->lspdb[level],
- dnode_next);
- }
+ /*
+ * Schedule may run spf which should be done
+ * only after
+ * the lsp rem_lifetime becomes 0 for the first
+ * time.
+ * ISO 10589 - 7.3.16.4 first paragraph.
+ */
+ if (rem_lifetime == 1 && lsp->hdr.seqno != 0) {
+ /* 7.3.16.4 a) set SRM flags on all */
+ /* 7.3.16.4 b) retain only the header */
+ if (lsp->area->purge_originator)
+ lsp_purge(lsp, lsp->level, NULL);
+ else
+ lsp_flood(lsp, NULL);
+ /* 7.3.16.4 c) record the time to purge
+ * FIXME */
+ isis_spf_schedule(lsp->area, lsp->level);
+ }
- lsp_destroy(lsp);
- lsp = NULL;
- dict_delete_free(area->lspdb[level],
- dnode);
- }
+ if (lsp->age_out == 0) {
+ zlog_debug(
+ "ISIS-Upd (%s): L%u LSP %s seq "
+ "0x%08" PRIx32 " aged out",
+ area->area_tag, lsp->level,
+ rawlspid_print(lsp->hdr.lsp_id),
+ lsp->hdr.seqno);
+
+ /* if we're aging out fragment 0, lsp_destroy()
+ * below will delete all other fragments too,
+ * so we need to skip over those
+ */
+ if (!LSP_FRAGMENT(lsp->hdr.lsp_id))
+ while (next &&
+ !memcmp(next->hdr.lsp_id,
+ lsp->hdr.lsp_id,
+ ISIS_SYS_ID_LEN + 1))
+ next = lspdb_next(
+ &area->lspdb[level],
+ next);
+
+ lspdb_del(&area->lspdb[level], lsp);
+ lsp_destroy(lsp);
+ lsp = NULL;
+ }
- if (fabricd_init_c && lsp) {
- fabricd_sync_incomplete |=
- ISIS_CHECK_FLAG(lsp->SSNflags,
- fabricd_init_c);
- }
+ if (fabricd_init_c && lsp) {
+ fabricd_sync_incomplete |=
+ ISIS_CHECK_FLAG(lsp->SSNflags,
+ fabricd_init_c);
}
}
}
@@ -1979,7 +1934,7 @@ void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level)
{
struct isis_lsp *lsp;
- lsp = lsp_search(id, circuit->area->lspdb[level - 1]);
+ lsp = lsp_search(&circuit->area->lspdb[level - 1], id);
if (!lsp)
return;
@@ -2012,7 +1967,7 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr,
lsp_pack_pdu(lsp);
- lsp_insert(lsp, area->lspdb[lsp->level - 1]);
+ lsp_insert(&area->lspdb[lsp->level - 1], lsp);
lsp_flood(lsp, NULL);
return;
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
index e6ea0b4eda..4cbca5d517 100644
--- a/isisd/isis_lsp.h
+++ b/isisd/isis_lsp.h
@@ -24,13 +24,18 @@
#ifndef _ZEBRA_ISIS_LSP_H
#define _ZEBRA_ISIS_LSP_H
+#include "lib/typesafe.h"
#include "isisd/isis_pdu.h"
+PREDECL_RBTREE_UNIQ(lspdb)
+
/* Structure for isis_lsp, this structure will only support the fixed
* System ID (Currently 6) (atleast for now). In order to support more
* We will have to split the header into two parts, and for readability
* sake it should better be avoided */
struct isis_lsp {
+ struct lspdb_item dbe;
+
struct isis_lsp_hdr hdr;
struct stream *pdu; /* full pdu lsp */
union {
@@ -54,8 +59,11 @@ struct isis_lsp {
bool flooding_circuit_scoped;
};
-dict_t *lsp_db_init(void);
-void lsp_db_destroy(dict_t *lspdb);
+extern int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b);
+DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare)
+
+void lsp_db_init(struct lspdb_head *head);
+void lsp_db_fini(struct lspdb_head *head);
int lsp_tick(struct thread *thread);
int lsp_generate(struct isis_area *area, int level);
@@ -76,14 +84,16 @@ struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr,
struct isis_tlvs *tlvs,
struct stream *stream, struct isis_lsp *lsp0,
struct isis_area *area, int level);
-void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb);
-struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb);
+void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp);
+struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id);
-void lsp_build_list(uint8_t *start_id, uint8_t *stop_id, uint8_t num_lsps,
- struct list *list, dict_t *lspdb);
-void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id,
- struct list *list, dict_t *lspdb);
-void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb);
+void lsp_build_list(struct lspdb_head *head, const uint8_t *start_id,
+ const uint8_t *stop_id, uint8_t num_lsps,
+ struct list *list);
+void lsp_build_list_nonzero_ht(struct lspdb_head *head,
+ const uint8_t *start_id,
+ const uint8_t *stop_id, struct list *list);
+void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id);
void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level);
void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr,
struct isis_area *area);
@@ -108,7 +118,8 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno);
void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag);
void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost);
void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost);
-int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost);
+int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail,
+ char dynhost);
/* sets SRMflags for all active circuits of an lsp */
void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set);
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index e74a9baadd..48ae760173 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -41,7 +41,6 @@
#include "qobj.h"
#include "libfrr.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c
index 2ce68262eb..0a42adea37 100644
--- a/isisd/isis_misc.c
+++ b/isisd/isis_misc.c
@@ -29,7 +29,6 @@
#include "if.h"
#include "command.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c
index 95595e37b9..d5cdec154b 100644
--- a/isisd/isis_northbound.c
+++ b/isisd/isis_northbound.c
@@ -25,7 +25,6 @@
#include "libfrr.h"
#include "linklist.h"
#include "log.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -1365,19 +1364,40 @@ isis_instance_log_adjacency_changes_modify(enum nb_event event,
}
/*
- * XPath: /frr-isisd:isis/mpls-te
+ * XPath: /frr-isisd:isis/instance/mpls-te
*/
-static int isis_mpls_te_create(enum nb_event event,
+static int isis_instance_mpls_te_create(enum nb_event event,
const struct lyd_node *dnode,
union nb_resource *resource)
{
struct listnode *node;
+ struct isis_area *area;
struct isis_circuit *circuit;
if (event != NB_EV_APPLY)
return NB_OK;
- isisMplsTE.status = enable;
+ area = nb_running_get_entry(dnode, NULL, true);
+ if (area->mta == NULL) {
+
+ struct mpls_te_area *new;
+
+ zlog_debug("ISIS MPLS-TE: Initialize area %s",
+ area->area_tag);
+
+ new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area));
+
+ /* Initialize MPLS_TE structure */
+ new->status = enable;
+ new->level = 0;
+ new->inter_as = off;
+ new->interas_areaid.s_addr = 0;
+ new->router_id.s_addr = 0;
+
+ area->mta = new;
+ } else {
+ area->mta->status = enable;
+ }
/*
* Following code is intended to handle two cases;
@@ -1387,11 +1407,11 @@ static int isis_mpls_te_create(enum nb_event event,
* MPLS_TE flag
* 2) MPLS-TE was once enabled then disabled, and now enabled again.
*/
- for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) {
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
if (circuit->mtc == NULL || IS_FLOOD_AS(circuit->mtc->type))
continue;
- if ((circuit->mtc->status == disable)
+ if (!IS_MPLS_TE(circuit->mtc)
&& HAS_LINK_PARAMS(circuit->interface))
circuit->mtc->status = enable;
else
@@ -1406,19 +1426,24 @@ static int isis_mpls_te_create(enum nb_event event,
return NB_OK;
}
-static int isis_mpls_te_destroy(enum nb_event event,
+static int isis_instance_mpls_te_destroy(enum nb_event event,
const struct lyd_node *dnode)
{
struct listnode *node;
+ struct isis_area *area;
struct isis_circuit *circuit;
if (event != NB_EV_APPLY)
return NB_OK;
- isisMplsTE.status = disable;
+ area = nb_running_get_entry(dnode, NULL, true);
+ if (IS_MPLS_TE(area->mta))
+ area->mta->status = disable;
+ else
+ return NB_OK;
/* Flush LSP if circuit engage */
- for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) {
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
if (circuit->mtc == NULL || (circuit->mtc->status == disable))
continue;
@@ -1435,55 +1460,53 @@ static int isis_mpls_te_destroy(enum nb_event event,
}
/*
- * XPath: /frr-isisd:isis/mpls-te/router-address
+ * XPath: /frr-isisd:isis/instance/mpls-te/router-address
*/
-static int isis_mpls_te_router_address_modify(enum nb_event event,
+static int isis_instance_mpls_te_router_address_modify(enum nb_event event,
const struct lyd_node *dnode,
union nb_resource *resource)
{
struct in_addr value;
- struct listnode *node;
struct isis_area *area;
if (event != NB_EV_APPLY)
return NB_OK;
- yang_dnode_get_ipv4(&value, dnode, NULL);
- isisMplsTE.router_id.s_addr = value.s_addr;
+ area = nb_running_get_entry(dnode, NULL, true);
/* only proceed if MPLS-TE is enabled */
- if (isisMplsTE.status == disable)
+ if (!IS_MPLS_TE(area->mta))
return NB_OK;
- /* Update main Router ID in isis global structure */
- isis->router_id = value.s_addr;
+ /* Update Area Router ID */
+ yang_dnode_get_ipv4(&value, dnode, NULL);
+ area->mta->router_id.s_addr = value.s_addr;
+
/* And re-schedule LSP update */
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
- if (listcount(area->area_addrs) > 0)
- lsp_regenerate_schedule(area, area->is_type, 0);
+ if (listcount(area->area_addrs) > 0)
+ lsp_regenerate_schedule(area, area->is_type, 0);
return NB_OK;
}
-static int isis_mpls_te_router_address_destroy(enum nb_event event,
+static int isis_instance_mpls_te_router_address_destroy(enum nb_event event,
const struct lyd_node *dnode)
{
- struct listnode *node;
struct isis_area *area;
if (event != NB_EV_APPLY)
return NB_OK;
- isisMplsTE.router_id.s_addr = INADDR_ANY;
+ area = nb_running_get_entry(dnode, NULL, true);
/* only proceed if MPLS-TE is enabled */
- if (isisMplsTE.status == disable)
+ if (!IS_MPLS_TE(area->mta))
return NB_OK;
- /* Update main Router ID in isis global structure */
- isis->router_id = 0;
+ /* Reset Area Router ID */
+ area->mta->router_id.s_addr = INADDR_ANY;
+
/* And re-schedule LSP update */
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
- if (listcount(area->area_addrs) > 0)
- lsp_regenerate_schedule(area, area->is_type, 0);
+ if (listcount(area->area_addrs) > 0)
+ lsp_regenerate_schedule(area, area->is_type, 0);
return NB_OK;
}
@@ -3015,15 +3038,15 @@ const struct frr_yang_module_info frr_isisd_info = {
.cbs.cli_show = cli_show_isis_log_adjacency,
},
{
- .xpath = "/frr-isisd:isis/mpls-te",
- .cbs.create = isis_mpls_te_create,
- .cbs.destroy = isis_mpls_te_destroy,
+ .xpath = "/frr-isisd:isis/instance/mpls-te",
+ .cbs.create = isis_instance_mpls_te_create,
+ .cbs.destroy = isis_instance_mpls_te_destroy,
.cbs.cli_show = cli_show_isis_mpls_te,
},
{
- .xpath = "/frr-isisd:isis/mpls-te/router-address",
- .cbs.modify = isis_mpls_te_router_address_modify,
- .cbs.destroy = isis_mpls_te_router_address_destroy,
+ .xpath = "/frr-isisd:isis/instance/mpls-te/router-address",
+ .cbs.modify = isis_instance_mpls_te_router_address_modify,
+ .cbs.destroy = isis_instance_mpls_te_router_address_destroy,
.cbs.cli_show = cli_show_isis_mpls_te_router_addr,
},
{
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index 8e9302963d..3d16d56016 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -36,7 +36,6 @@
#include "md5.h"
#include "lib_errors.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -201,8 +200,9 @@ static int process_p2p_hello(struct iih_info *iih)
adj);
/* Update MPLS TE Remote IP address parameter if possible */
- if (IS_MPLS_TE(isisMplsTE) && iih->circuit->mtc
- && IS_CIRCUIT_TE(iih->circuit->mtc) && adj->ipv4_address_count)
+ if (IS_MPLS_TE(iih->circuit->area->mta)
+ && IS_MPLS_TE(iih->circuit->mtc)
+ && adj->ipv4_address_count)
set_circuitparams_rmt_ipaddr(iih->circuit->mtc,
adj->ipv4_addresses[0]);
@@ -959,7 +959,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
/* Find the LSP in our database and compare it to this Link State header
*/
struct isis_lsp *lsp =
- lsp_search(hdr.lsp_id, circuit->area->lspdb[level - 1]);
+ lsp_search(&circuit->area->lspdb[level - 1], hdr.lsp_id);
int comp = 0;
if (lsp)
comp = lsp_compare(circuit->area->area_tag, lsp, hdr.seqno,
@@ -1186,7 +1186,7 @@ dontcheckadj:
memcpy(lspid, hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT(lspid) = 0;
lsp0 = lsp_search(
- lspid, circuit->area->lspdb[level - 1]);
+ &circuit->area->lspdb[level - 1], lspid);
if (!lsp0) {
zlog_debug(
"Got lsp frag, while zero lsp not in database");
@@ -1199,8 +1199,8 @@ dontcheckadj:
&hdr, tlvs, circuit->rcv_stream, lsp0,
circuit->area, level);
tlvs = NULL;
- lsp_insert(lsp,
- circuit->area->lspdb[level - 1]);
+ lsp_insert(&circuit->area->lspdb[level - 1],
+ lsp);
} else /* exists, so we overwrite */
{
lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream,
@@ -1416,7 +1416,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
for (struct isis_lsp_entry *entry = entry_head; entry;
entry = entry->next) {
struct isis_lsp *lsp =
- lsp_search(entry->id, circuit->area->lspdb[level - 1]);
+ lsp_search(&circuit->area->lspdb[level - 1], entry->id);
bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN);
if (lsp) {
/* 7.3.15.2 b) 1) is this LSP newer */
@@ -1467,8 +1467,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT(lspid) = 0;
lsp0 = lsp_search(
- lspid,
- circuit->area->lspdb[level - 1]);
+ &circuit->area->lspdb[level - 1],
+ lspid);
if (!lsp0) {
zlog_debug("Got lsp frag in snp, while zero not in database");
continue;
@@ -1477,8 +1477,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
lsp = lsp_new(circuit->area, entry->id,
entry->rem_lifetime, 0, 0,
entry->checksum, lsp0, level);
- lsp_insert(lsp,
- circuit->area->lspdb[level - 1]);
+ lsp_insert(&circuit->area->lspdb[level - 1],
+ lsp);
lsp_set_all_srmflags(lsp, false);
ISIS_SET_FLAG(lsp->SSNflags, circuit);
@@ -1495,8 +1495,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
* start_lsp_id and stop_lsp_id
*/
struct list *lsp_list = list_new();
- lsp_build_list_nonzero_ht(start_lsp_id, stop_lsp_id, lsp_list,
- circuit->area->lspdb[level - 1]);
+ lsp_build_list_nonzero_ht(&circuit->area->lspdb[level - 1],
+ start_lsp_id, stop_lsp_id, lsp_list);
/* Fixme: Find a better solution */
struct listnode *node, *nnode;
@@ -2040,8 +2040,7 @@ static uint16_t get_max_lsp_count(uint16_t size)
int send_csnp(struct isis_circuit *circuit, int level)
{
- if (circuit->area->lspdb[level - 1] == NULL
- || dict_count(circuit->area->lspdb[level - 1]) == 0)
+ if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0)
return ISIS_OK;
uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM
@@ -2094,7 +2093,7 @@ int send_csnp(struct isis_circuit *circuit, int level)
struct isis_lsp *last_lsp;
isis_tlvs_add_csnp_entries(tlvs, start, stop, num_lsps,
- circuit->area->lspdb[level - 1],
+ &circuit->area->lspdb[level - 1],
&last_lsp);
/*
* Update the stop lsp_id before encoding this CSNP.
@@ -2215,8 +2214,7 @@ static int send_psnp(int level, struct isis_circuit *circuit)
&& circuit->u.bc.is_dr[level - 1])
return ISIS_OK;
- if (circuit->area->lspdb[level - 1] == NULL
- || dict_count(circuit->area->lspdb[level - 1]) == 0)
+ if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0)
return ISIS_OK;
if (!circuit->snd_stream)
@@ -2254,16 +2252,13 @@ static int send_psnp(int level, struct isis_circuit *circuit)
get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream));
while (1) {
+ struct isis_lsp *lsp;
+
tlvs = isis_alloc_tlvs();
if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
isis_tlvs_add_auth(tlvs, passwd);
- for (dnode_t *dnode =
- dict_first(circuit->area->lspdb[level - 1]);
- dnode; dnode = dict_next(circuit->area->lspdb[level - 1],
- dnode)) {
- struct isis_lsp *lsp = dnode_get(dnode);
-
+ frr_each (lspdb, &circuit->area->lspdb[level - 1], lsp) {
if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit))
isis_tlvs_add_lsp_entry(tlvs, lsp);
diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
index 2f6526bc5e..824acd0ff8 100644
--- a/isisd/isis_pfpacket.c
+++ b/isisd/isis_pfpacket.c
@@ -33,7 +33,6 @@
#include "if.h"
#include "lib_errors.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_circuit.h"
diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c
index 9047707bf0..dc23e8ea49 100644
--- a/isisd/isis_redist.c
+++ b/isisd/isis_redist.c
@@ -32,7 +32,6 @@
#include "vty.h"
#include "srcdest_table.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
index 1439a4229a..82005c911e 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -38,7 +38,6 @@
#include "isis_constants.h"
#include "isis_common.h"
#include "isis_flags.h"
-#include "dict.h"
#include "isisd.h"
#include "isis_misc.h"
#include "isis_adjacency.h"
diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c
index 3c2cf7b3fc..d63676256b 100644
--- a/isisd/isis_routemap.c
+++ b/isisd/isis_routemap.c
@@ -37,7 +37,6 @@
#include "isis_constants.h"
#include "isis_common.h"
#include "isis_flags.h"
-#include "dict.h"
#include "isisd.h"
#include "isis_misc.h"
#include "isis_adjacency.h"
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 18eb857ec9..a28220eb26 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -39,7 +39,6 @@
#include "isis_constants.h"
#include "isis_common.h"
#include "isis_flags.h"
-#include "dict.h"
#include "isisd.h"
#include "isis_misc.h"
#include "isis_adjacency.h"
@@ -313,7 +312,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level,
memcpy(lspid, sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(lspid) = 0;
LSP_FRAGMENT(lspid) = 0;
- lsp = lsp_search(lspid, area->lspdb[level - 1]);
+ lsp = lsp_search(&area->lspdb[level - 1], lspid);
if (lsp && lsp->hdr.rem_lifetime != 0)
return lsp;
return NULL;
@@ -870,10 +869,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
[spftree->level - 1],
parent);
lsp = lsp_search(
- lsp_id,
- spftree->area
- ->lspdb[spftree->level
- - 1]);
+ &spftree->area->lspdb[spftree->level- 1],
+ lsp_id);
if (lsp == NULL
|| lsp->hdr.rem_lifetime == 0)
zlog_warn(
@@ -923,8 +920,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
continue;
}
lsp = lsp_search(
- lsp_id,
- spftree->area->lspdb[spftree->level - 1]);
+ &spftree->area->lspdb[spftree->level - 1],
+ lsp_id);
if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
zlog_warn(
"ISIS-Spf: No lsp (%p) found from root "
diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h
index 3a05df3f14..a8185a8be0 100644
--- a/isisd/isis_spf_private.h
+++ b/isisd/isis_spf_private.h
@@ -79,9 +79,9 @@ struct isis_vertex_queue {
};
__attribute__((__unused__))
-static unsigned isis_vertex_queue_hash_key(void *vp)
+static unsigned isis_vertex_queue_hash_key(const void *vp)
{
- struct isis_vertex *vertex = vp;
+ const struct isis_vertex *vertex = vp;
if (VTYPE_IP(vertex->type)) {
uint32_t key;
@@ -347,8 +347,8 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree,
memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
LSP_FRAGMENT(lsp_id) = 0;
- dict_t *lspdb = spftree->area->lspdb[spftree->level - 1];
- struct isis_lsp *lsp = lsp_search(lsp_id, lspdb);
+ struct lspdb_head *lspdb = &spftree->area->lspdb[spftree->level - 1];
+ struct isis_lsp *lsp = lsp_search(lspdb, lsp_id);
if (lsp && lsp->hdr.rem_lifetime != 0)
return lsp;
diff --git a/isisd/isis_te.c b/isisd/isis_te.c
index 23a1f10a18..4ea6c2c60d 100644
--- a/isisd/isis_te.c
+++ b/isisd/isis_te.c
@@ -43,7 +43,6 @@
#include "network.h"
#include "sbuf.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -58,9 +57,6 @@
#include "isisd/isis_spf.h"
#include "isisd/isis_te.h"
-/* Global varial for MPLS TE management */
-struct isis_mpls_te isisMplsTE;
-
const char *mode2text[] = {"Disable", "Area", "AS", "Emulate"};
/*------------------------------------------------------------------------*
@@ -624,7 +620,7 @@ void isis_link_params_update(struct isis_circuit *circuit,
/* Finally Update LSP */
#if 0
- if (IS_MPLS_TE(isisMplsTE) && circuit->area)
+ if (circuit->area && IS_MPLS_TE(circuit->area->mta))
lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
#endif
return;
@@ -646,7 +642,7 @@ void isis_mpls_te_update(struct interface *ifp)
isis_link_params_update(circuit, ifp);
/* ... and LSP */
- if (IS_MPLS_TE(isisMplsTE) && circuit->area)
+ if (circuit->area && IS_MPLS_TE(circuit->area->mta))
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
return;
@@ -1058,35 +1054,11 @@ void mpls_te_print_detail(struct sbuf *buf, int indent,
return;
}
-/* Specific MPLS TE router parameters write function */
-void isis_mpls_te_config_write_router(struct vty *vty)
-{
- if (IS_MPLS_TE(isisMplsTE)) {
- vty_out(vty, " mpls-te on\n");
- vty_out(vty, " mpls-te router-address %s\n",
- inet_ntoa(isisMplsTE.router_id));
- }
-
- return;
-}
-
-
/*------------------------------------------------------------------------*
* Followings are vty command functions.
*------------------------------------------------------------------------*/
#ifndef FABRICD
-/* Search MPLS TE Circuit context from Interface */
-static struct mpls_te_circuit *lookup_mpls_params_by_ifp(struct interface *ifp)
-{
- struct isis_circuit *circuit;
-
- if ((circuit = circuit_scan_by_ifp(ifp)) == NULL)
- return NULL;
-
- return circuit->mtc;
-}
-
DEFUN (show_isis_mpls_te_router,
show_isis_mpls_te_router_cmd,
"show " PROTO_NAME " mpls-te router",
@@ -1095,84 +1067,73 @@ DEFUN (show_isis_mpls_te_router,
MPLS_TE_STR
"Router information\n")
{
- if (IS_MPLS_TE(isisMplsTE)) {
- vty_out(vty, "--- MPLS-TE router parameters ---\n");
- if (ntohs(isisMplsTE.router_id.s_addr) != 0)
- vty_out(vty, " Router-Address: %s\n",
- inet_ntoa(isisMplsTE.router_id));
+ struct listnode *anode;
+ struct isis_area *area;
+
+ if (!isis) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+
+ if (!IS_MPLS_TE(area->mta))
+ continue;
+
+ vty_out(vty, "Area %s:\n", area->area_tag);
+ if (ntohs(area->mta->router_id.s_addr) != 0)
+ vty_out(vty, " MPLS-TE Router-Address: %s\n",
+ inet_ntoa(area->mta->router_id));
else
vty_out(vty, " N/A\n");
- } else
- vty_out(vty, " MPLS-TE is disable on this router\n");
+ }
return CMD_SUCCESS;
}
-static void show_mpls_te_sub(struct vty *vty, struct interface *ifp)
+static void show_mpls_te_sub(struct vty *vty, char *name,
+ struct mpls_te_circuit *mtc)
{
- struct mpls_te_circuit *mtc;
struct sbuf buf;
sbuf_init(&buf, NULL, 0);
- if ((IS_MPLS_TE(isisMplsTE))
- && ((mtc = lookup_mpls_params_by_ifp(ifp)) != NULL)) {
- /* Continue only if interface is not passive or support Inter-AS
- * TEv2 */
- if (mtc->status != enable) {
- if (IS_INTER_AS(mtc->type)) {
- vty_out(vty,
- "-- Inter-AS TEv2 link parameters for %s --\n",
- ifp->name);
- } else {
- /* MPLS-TE is not activate on this interface */
- /* or this interface is passive and Inter-AS
- * TEv2 is not activate */
- vty_out(vty,
- " %s: MPLS-TE is disabled on this interface\n",
- ifp->name);
- return;
- }
- } else {
- vty_out(vty, "-- MPLS-TE link parameters for %s --\n",
- ifp->name);
- }
-
- sbuf_reset(&buf);
- print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp);
+ if (mtc->status != enable)
+ return;
- if (SUBTLV_TYPE(mtc->local_ipaddr) != 0)
- print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr);
- if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0)
- print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr);
+ vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name);
- print_subtlv_max_bw(&buf, 4, &mtc->max_bw);
- print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw);
- print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw);
- print_subtlv_te_metric(&buf, 4, &mtc->te_metric);
+ sbuf_reset(&buf);
+ print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp);
- if (IS_INTER_AS(mtc->type)) {
- if (SUBTLV_TYPE(mtc->ras) != 0)
- print_subtlv_ras(&buf, 4, &mtc->ras);
- if (SUBTLV_TYPE(mtc->rip) != 0)
- print_subtlv_rip(&buf, 4, &mtc->rip);
- }
+ if (SUBTLV_TYPE(mtc->local_ipaddr) != 0)
+ print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr);
+ if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0)
+ print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr);
+
+ print_subtlv_max_bw(&buf, 4, &mtc->max_bw);
+ print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw);
+ print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw);
+ print_subtlv_te_metric(&buf, 4, &mtc->te_metric);
+
+ if (IS_INTER_AS(mtc->type)) {
+ if (SUBTLV_TYPE(mtc->ras) != 0)
+ print_subtlv_ras(&buf, 4, &mtc->ras);
+ if (SUBTLV_TYPE(mtc->rip) != 0)
+ print_subtlv_rip(&buf, 4, &mtc->rip);
+ }
- print_subtlv_av_delay(&buf, 4, &mtc->av_delay);
- print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay);
- print_subtlv_delay_var(&buf, 4, &mtc->delay_var);
- print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss);
- print_subtlv_res_bw(&buf, 4, &mtc->res_bw);
- print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw);
- print_subtlv_use_bw(&buf, 4, &mtc->use_bw);
+ print_subtlv_av_delay(&buf, 4, &mtc->av_delay);
+ print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay);
+ print_subtlv_delay_var(&buf, 4, &mtc->delay_var);
+ print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss);
+ print_subtlv_res_bw(&buf, 4, &mtc->res_bw);
+ print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw);
+ print_subtlv_use_bw(&buf, 4, &mtc->use_bw);
- vty_multiline(vty, "", "%s", sbuf_buf(&buf));
- vty_out(vty, "---------------\n\n");
- } else {
- vty_out(vty, " %s: MPLS-TE is disabled on this interface\n",
- ifp->name);
- }
+ vty_multiline(vty, "", "%s", sbuf_buf(&buf));
+ vty_out(vty, "---------------\n\n");
sbuf_free(&buf);
return;
@@ -1187,23 +1148,45 @@ DEFUN (show_isis_mpls_te_interface,
"Interface information\n"
"Interface name\n")
{
- struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
- int idx_interface = 4;
+ struct listnode *anode, *cnode;
+ struct isis_area *area;
+ struct isis_circuit *circuit;
struct interface *ifp;
+ int idx_interface = 4;
- /* Show All Interfaces. */
- if (argc == 4) {
- FOR_ALL_INTERFACES (vrf, ifp)
- show_mpls_te_sub(vty, ifp);
+ if (!isis) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
}
- /* Interface name is specified. */
- else {
- if ((ifp = if_lookup_by_name(argv[idx_interface]->arg,
- VRF_DEFAULT))
- == NULL)
+
+ if (argc == idx_interface) {
+ /* Show All Interfaces. */
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+
+ if (!IS_MPLS_TE(area->mta))
+ continue;
+
+ vty_out(vty, "Area %s:\n", area->area_tag);
+
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode,
+ circuit))
+ show_mpls_te_sub(vty, circuit->interface->name,
+ circuit->mtc);
+ }
+ } else {
+ /* Interface name is specified. */
+ ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT);
+ if (ifp == NULL)
vty_out(vty, "No such interface name\n");
- else
- show_mpls_te_sub(vty, ifp);
+ else {
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ vty_out(vty,
+ "ISIS is not enabled on circuit %s\n",
+ ifp->name);
+ else
+ show_mpls_te_sub(vty, ifp->name, circuit->mtc);
+ }
}
return CMD_SUCCESS;
@@ -1214,16 +1197,6 @@ DEFUN (show_isis_mpls_te_interface,
void isis_mpls_te_init(void)
{
- zlog_debug("ISIS MPLS-TE: Initialize");
-
- /* Initialize MPLS_TE structure */
- isisMplsTE.status = disable;
- isisMplsTE.level = 0;
- isisMplsTE.inter_as = off;
- isisMplsTE.interas_areaid.s_addr = 0;
- isisMplsTE.cir_list = list_new();
- isisMplsTE.router_id.s_addr = 0;
-
#ifndef FABRICD
/* Register new VTY commands */
install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd);
diff --git a/isisd/isis_te.h b/isisd/isis_te.h
index e9eff08cd1..beb0c1836f 100644
--- a/isisd/isis_te.h
+++ b/isisd/isis_te.h
@@ -244,11 +244,10 @@ typedef enum _status_t { disable, enable, learn } status_t;
/* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */
typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t;
-#define IS_MPLS_TE(m) (m.status == enable)
-#define IS_CIRCUIT_TE(c) (c->status == enable)
+#define IS_MPLS_TE(m) (m && m->status == enable)
-/* Following structure are internal use only. */
-struct isis_mpls_te {
+/* Per area MPLS-TE parameters */
+struct mpls_te_area {
/* Status of MPLS-TE: enable or disable */
status_t status;
@@ -259,15 +258,11 @@ struct isis_mpls_te {
interas_mode_t inter_as;
struct in_addr interas_areaid;
- /* Circuit list on which TE are enable */
- struct list *cir_list;
-
/* MPLS_TE router ID */
struct in_addr router_id;
};
-extern struct isis_mpls_te isisMplsTE;
-
+/* Per Circuit MPLS-TE parameters */
struct mpls_te_circuit {
/* Status of MPLS-TE on this interface */
@@ -318,6 +313,5 @@ uint8_t add_te_subtlvs(uint8_t *, struct mpls_te_circuit *);
uint8_t build_te_subtlvs(uint8_t *, struct isis_circuit *);
void isis_link_params_update(struct isis_circuit *, struct interface *);
void isis_mpls_te_update(struct interface *);
-void isis_mpls_te_config_write_router(struct vty *);
#endif /* _ZEBRA_ISIS_MPLS_TE_H */
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index fbb1e5714c..6b8e74f07b 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -405,7 +405,7 @@ static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s)
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent);
+ int indent, bool *unpacked_known_tlvs);
/* Functions related to TLVs 1 Area Addresses */
@@ -796,7 +796,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
size_t subtlv_start = stream_get_getp(s);
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s,
- log, NULL, indent + 4)) {
+ log, NULL, indent + 4, NULL)) {
goto out;
}
@@ -1272,6 +1272,11 @@ static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i,
if (mtid != ISIS_MT_IPV4_UNICAST)
sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
sbuf_push(buf, 0, "\n");
+
+ if (r->subtlvs) {
+ sbuf_push(buf, indent, " Subtlvs:\n");
+ format_subtlvs(r->subtlvs, buf, indent + 4);
+ }
}
static void free_item_extended_ip_reach(struct isis_item *i)
@@ -1386,10 +1391,16 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
}
rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
+ bool unpacked_known_tlvs = false;
+
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s,
- log, rv->subtlvs, indent + 4)) {
+ log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
goto out;
}
+ if (!unpacked_known_tlvs) {
+ isis_free_subtlvs(rv->subtlvs);
+ rv->subtlvs = NULL;
+ }
}
append_item(items, (struct isis_item *)rv);
@@ -1865,10 +1876,16 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
}
rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
+ bool unpacked_known_tlvs = false;
+
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
- log, rv->subtlvs, indent + 4)) {
+ log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
goto out;
}
+ if (!unpacked_known_tlvs) {
+ isis_free_subtlvs(rv->subtlvs);
+ rv->subtlvs = NULL;
+ }
}
append_item(items, (struct isis_item *)rv);
@@ -2966,7 +2983,7 @@ static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent)
+ int indent, bool *unpacked_known_tlvs)
{
uint8_t tlv_type, tlv_len;
const struct tlv_ops *ops;
@@ -2997,6 +3014,8 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
ops = tlv_table[context][tlv_type];
if (ops && ops->unpack) {
+ if (unpacked_known_tlvs)
+ *unpacked_known_tlvs = true;
return ops->unpack(context, tlv_type, tlv_len, stream, log,
dest, indent + 2);
}
@@ -3007,7 +3026,7 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent)
+ int indent, bool *unpacked_known_tlvs)
{
int rv;
size_t tlv_start, tlv_pos;
@@ -3020,7 +3039,7 @@ static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
while (tlv_pos < avail_len) {
rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
- indent + 2);
+ indent + 2, unpacked_known_tlvs);
if (rv)
return rv;
@@ -3052,7 +3071,7 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
result = isis_alloc_tlvs();
rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
- indent);
+ indent, NULL);
*log = sbuf_buf(&logbuf);
*dest = result;
@@ -3522,26 +3541,24 @@ void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
uint8_t *stop_id, uint16_t num_lsps,
- dict_t *lspdb, struct isis_lsp **last_lsp)
+ struct lspdb_head *head,
+ struct isis_lsp **last_lsp)
{
- dnode_t *first = dict_lower_bound(lspdb, start_id);
+ struct isis_lsp searchfor;
+ struct isis_lsp *first, *lsp;
+
+ memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
+ first = lspdb_find_gteq(head, &searchfor);
if (!first)
return;
- dnode_t *last = dict_upper_bound(lspdb, stop_id);
- dnode_t *curr = first;
-
- isis_tlvs_add_lsp_entry(tlvs, first->dict_data);
- *last_lsp = first->dict_data;
-
- while (curr) {
- curr = dict_next(lspdb, curr);
- if (curr) {
- isis_tlvs_add_lsp_entry(tlvs, curr->dict_data);
- *last_lsp = curr->dict_data;
- }
- if (curr == last || tlvs->lsp_entries.count == num_lsps)
+ frr_each_from (lspdb, head, lsp, first) {
+ if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id))
+ > 0 || tlvs->lsp_entries.count == num_lsps)
break;
+
+ isis_tlvs_add_lsp_entry(tlvs, lsp);
+ *last_lsp = lsp;
}
}
diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h
index fce30d4ee7..4954d791d8 100644
--- a/isisd/isis_tlvs.h
+++ b/isisd/isis_tlvs.h
@@ -25,8 +25,8 @@
#include "openbsd-tree.h"
#include "prefix.h"
-#include "isisd/dict.h"
+struct lspdb_head;
struct isis_subtlvs;
struct isis_area_address;
@@ -355,7 +355,8 @@ bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa);
void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp);
void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
uint8_t *stop_id, uint16_t num_lsps,
- dict_t *lspdb, struct isis_lsp **last_lsp);
+ struct lspdb_head *lspdb,
+ struct isis_lsp **last_lsp);
void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
const char *hostname);
void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c
index 270dcae7d0..507fd489bc 100644
--- a/isisd/isis_tx_queue.c
+++ b/isisd/isis_tx_queue.c
@@ -27,7 +27,6 @@
#include "isisd/isisd.h"
#include "isisd/isis_memory.h"
#include "isisd/isis_flags.h"
-#include "dict.h"
#include "isisd/isis_circuit.h"
#include "isisd/isis_lsp.h"
#include "isisd/isis_misc.h"
@@ -51,9 +50,9 @@ struct isis_tx_queue_entry {
struct isis_tx_queue *queue;
};
-static unsigned tx_queue_hash_key(void *p)
+static unsigned tx_queue_hash_key(const void *p)
{
- struct isis_tx_queue_entry *e = p;
+ const struct isis_tx_queue_entry *e = p;
uint32_t id_key = jhash(e->lsp->hdr.lsp_id,
ISIS_SYS_ID_LEN + 2, 0x55aa5a5a);
diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c
index b2c0440de6..2476bd2552 100644
--- a/isisd/isis_vty_fabricd.c
+++ b/isisd/isis_vty_fabricd.c
@@ -161,13 +161,14 @@ DEFUN (show_lsp_flooding,
struct isis_area *area;
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- dict_t *lspdb = area->lspdb[ISIS_LEVEL2 - 1];
+ struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1];
+ struct isis_lsp *lsp;
vty_out(vty, "Area %s:\n", area->area_tag ?
area->area_tag : "null");
if (lspid) {
- struct isis_lsp *lsp = lsp_for_arg(lspid, lspdb);
+ struct isis_lsp *lsp = lsp_for_arg(head, lspid);
if (lsp)
lsp_print_flooding(vty, lsp);
@@ -175,9 +176,8 @@ DEFUN (show_lsp_flooding,
continue;
}
- for (dnode_t *dnode = dict_first(lspdb); dnode;
- dnode = dict_next(lspdb, dnode)) {
- lsp_print_flooding(vty, dnode_get(dnode));
+ frr_each (lspdb, head, lsp) {
+ lsp_print_flooding(vty, lsp);
vty_out(vty, "\n");
}
}
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index 79d79f8911..e2ef934696 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -37,7 +37,6 @@
#include "vrf.h"
#include "libfrr.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -54,20 +53,12 @@
struct zclient *zclient = NULL;
/* Router-id update message from zebra. */
-static int isis_router_id_update_zebra(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct isis_area *area;
struct listnode *node;
struct prefix router_id;
- /*
- * If ISIS TE is enable, TE Router ID is set through specific command.
- * See mpls_te_router_addr() command in isis_te.c
- */
- if (IS_MPLS_TE(isisMplsTE))
- return 0;
-
zebra_router_id_update_read(zclient->ibuf, &router_id);
if (isis->router_id == router_id.u.prefix4.s_addr)
return 0;
@@ -80,8 +71,7 @@ static int isis_router_id_update_zebra(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -94,8 +84,7 @@ static int isis_zebra_if_add(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_del(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_del(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -121,8 +110,7 @@ static int isis_zebra_if_del(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_state_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -136,8 +124,7 @@ static int isis_zebra_if_state_up(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_state_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct isis_circuit *circuit;
@@ -155,8 +142,7 @@ static int isis_zebra_if_state_down(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
@@ -183,8 +169,7 @@ static int isis_zebra_if_address_add(int command, struct zclient *zclient,
return 0;
}
-static int isis_zebra_if_address_del(int command, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct interface *ifp;
@@ -218,8 +203,7 @@ static int isis_zebra_if_address_del(int command, struct zclient *client,
return 0;
}
-static int isis_zebra_link_params(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_link_params(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -354,8 +338,7 @@ void isis_zebra_route_update(struct prefix *prefix,
isis_zebra_route_del_route(prefix, src_p, route_info);
}
-static int isis_zebra_read(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int isis_zebra_read(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
@@ -375,10 +358,10 @@ static int isis_zebra_read(int command, struct zclient *zclient,
if (api.prefix.prefixlen == 0
&& api.src_prefix.prefixlen == 0
&& api.type == PROTO_TYPE) {
- command = ZEBRA_REDISTRIBUTE_ROUTE_DEL;
+ cmd = ZEBRA_REDISTRIBUTE_ROUTE_DEL;
}
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
isis_redist_add(api.type, &api.prefix, &api.src_prefix,
api.distance, api.metric);
else
diff --git a/isisd/isisd.c b/isisd/isisd.c
index ad02220438..07be68d9ae 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -38,7 +38,6 @@
#include "spf_backoff.h"
#include "lib/northbound_cli.h"
-#include "isisd/dict.h"
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
#include "isisd/isis_flags.h"
@@ -95,7 +94,6 @@ void isis_new(unsigned long process_id)
* uncomment the next line for full debugs
*/
/* isis->debugs = 0xFFFF; */
- isisMplsTE.status = disable; /* Only support TE metric */
QOBJ_REG(isis, isis);
}
@@ -122,12 +120,10 @@ struct isis_area *isis_area_create(const char *area_tag)
/*
* intialize the databases
*/
- if (area->is_type & IS_LEVEL_1) {
- area->lspdb[0] = lsp_db_init();
- }
- if (area->is_type & IS_LEVEL_2) {
- area->lspdb[1] = lsp_db_init();
- }
+ if (area->is_type & IS_LEVEL_1)
+ lsp_db_init(&area->lspdb[0]);
+ if (area->is_type & IS_LEVEL_2)
+ lsp_db_init(&area->lspdb[1]);
spftree_area_init(area);
@@ -258,6 +254,10 @@ int isis_area_destroy(const char *area_tag)
if (fabricd)
fabricd_finish(area->fabricd);
+ /* Disable MPLS if necessary before flooding LSP */
+ if (IS_MPLS_TE(area->mta))
+ area->mta->status = disable;
+
if (area->circuit_list) {
for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode,
circuit)) {
@@ -268,14 +268,8 @@ int isis_area_destroy(const char *area_tag)
list_delete(&area->circuit_list);
}
- if (area->lspdb[0] != NULL) {
- lsp_db_destroy(area->lspdb[0]);
- area->lspdb[0] = NULL;
- }
- if (area->lspdb[1] != NULL) {
- lsp_db_destroy(area->lspdb[1]);
- area->lspdb[1] = NULL;
- }
+ lsp_db_fini(&area->lspdb[0]);
+ lsp_db_fini(&area->lspdb[1]);
/* invalidate and verify to delete all routes from zebra */
isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2);
@@ -1341,7 +1335,7 @@ DEFUN (show_isis_summary,
return CMD_SUCCESS;
}
-struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb)
+struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv)
{
char sysid[255] = {0};
uint8_t number[3];
@@ -1389,13 +1383,13 @@ struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb)
* hostname.<pseudo-id>-<fragment>
*/
if (sysid2buff(lspid, sysid)) {
- lsp = lsp_search(lspid, lspdb);
+ lsp = lsp_search(head, lspid);
} else if ((dynhn = dynhn_find_by_name(sysid))) {
memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN);
- lsp = lsp_search(lspid, lspdb);
+ lsp = lsp_search(head, lspid);
} else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) {
memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
- lsp = lsp_search(lspid, lspdb);
+ lsp = lsp_search(head, lspid);
}
return lsp;
@@ -1432,9 +1426,8 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
area->area_tag ? area->area_tag : "null");
for (level = 0; level < ISIS_LEVELS; level++) {
- if (area->lspdb[level]
- && dict_count(area->lspdb[level]) > 0) {
- lsp = lsp_for_arg(argv, area->lspdb[level]);
+ if (lspdb_count(&area->lspdb[level]) > 0) {
+ lsp = lsp_for_arg(&area->lspdb[level], argv);
if (lsp != NULL || argv == NULL) {
vty_out(vty,
@@ -1456,7 +1449,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
area->dynhostname);
} else if (argv == NULL) {
lsp_count = lsp_print_all(
- vty, area->lspdb[level],
+ vty, &area->lspdb[level],
ui_level, area->dynhostname);
vty_out(vty, " %u LSPs\n\n",
@@ -1696,10 +1689,7 @@ static void area_resign_level(struct isis_area *area, int level)
isis_area_invalidate_routes(area, level);
isis_area_verify_routes(area);
- if (area->lspdb[level - 1]) {
- lsp_db_destroy(area->lspdb[level - 1]);
- area->lspdb[level - 1] = NULL;
- }
+ lsp_db_fini(&area->lspdb[level - 1]);
for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
if (area->spftree[tree][level - 1]) {
@@ -1735,8 +1725,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type)
if (is_type == IS_LEVEL_2)
area_resign_level(area, IS_LEVEL_1);
- if (area->lspdb[1] == NULL)
- area->lspdb[1] = lsp_db_init();
+ lsp_db_init(&area->lspdb[1]);
break;
case IS_LEVEL_1_AND_2:
@@ -1750,8 +1739,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type)
if (is_type == IS_LEVEL_1)
area_resign_level(area, IS_LEVEL_2);
- if (area->lspdb[0] == NULL)
- area->lspdb[0] = lsp_db_init();
+ lsp_db_init(&area->lspdb[0]);
break;
default:
@@ -2137,7 +2125,6 @@ int isis_config_write(struct vty *vty)
write += area_write_mt_settings(area, vty);
write += fabricd_write_settings(area, vty);
}
- isis_mpls_te_config_write_router(vty);
}
return write;
diff --git a/isisd/isisd.h b/isisd/isisd.h
index fb879395c1..f8486ae0d6 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -31,7 +31,7 @@
#include "isisd/isis_pdu_counter.h"
#include "isisd/isis_circuit.h"
#include "isis_flags.h"
-#include "dict.h"
+#include "isis_lsp.h"
#include "isis_memory.h"
#include "qobj.h"
@@ -107,7 +107,7 @@ enum isis_metric_style {
struct isis_area {
struct isis *isis; /* back pointer */
- dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */
+ struct lspdb_head lspdb[ISIS_LEVELS]; /* link-state dbs */
struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS];
#define DEFAULT_LSP_MTU 1497
unsigned int lsp_mtu; /* Size of LSPs to generate */
@@ -165,6 +165,8 @@ struct isis_area {
uint8_t log_adj_changes;
/* multi topology settings */
struct list *mt_settings;
+ /* MPLS-TE settings */
+ struct mpls_te_area *mta;
int ipv6_circuits;
bool purge_originator;
/* Counters */
@@ -195,7 +197,7 @@ struct isis_area *isis_area_lookup(const char *);
int isis_area_get(struct vty *vty, const char *area_tag);
int isis_area_destroy(const char *area_tag);
void print_debug(struct vty *, int, int);
-struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb);
+struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv);
void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
diff --git a/isisd/subdir.am b/isisd/subdir.am
index 4371d5993a..bae56309cf 100644
--- a/isisd/subdir.am
+++ b/isisd/subdir.am
@@ -25,7 +25,6 @@ dist_examples_DATA += isisd/fabricd.conf.sample
endif
noinst_HEADERS += \
- isisd/dict.h \
isisd/isis_adjacency.h \
isisd/isis_bfd.h \
isisd/isis_circuit.h \
@@ -61,7 +60,6 @@ noinst_HEADERS += \
# end
LIBISIS_SOURCES = \
- isisd/dict.c \
isisd/isis_adjacency.c \
isisd/isis_bfd.c \
isisd/isis_circuit.c \
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
index 9dc5677358..35a7d944d3 100644
--- a/ldpd/ldp_zebra.c
+++ b/ldpd/ldp_zebra.c
@@ -38,22 +38,14 @@ static void ifp2kif(struct interface *, struct kif *);
static void ifc2kaddr(struct interface *, struct connected *,
struct kaddr *);
static int zebra_send_mpls_labels(int, struct kroute *);
-static int ldp_router_id_update(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int ldp_interface_add(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int ldp_interface_delete(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int ldp_interface_status_change(int command, struct zclient *,
- zebra_size_t, vrf_id_t);
-static int ldp_interface_address_add(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int ldp_interface_address_delete(int, struct zclient *,
- zebra_size_t, vrf_id_t);
-static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t,
- vrf_id_t);
-static int ldp_zebra_read_pw_status_update(int, struct zclient *,
- zebra_size_t, vrf_id_t);
+static int ldp_router_id_update(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_add(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_delete(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_status_change(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS);
+static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS);
+static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS);
+static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS);
static void ldp_zebra_connected(struct zclient *);
static struct zclient *zclient;
@@ -235,8 +227,7 @@ kif_redistribute(const char *ifname)
}
static int
-ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+ldp_router_id_update(ZAPI_CALLBACK_ARGS)
{
struct prefix router_id;
@@ -255,8 +246,7 @@ ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length,
}
static int
-ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+ldp_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct kif kif;
@@ -272,8 +262,7 @@ ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length,
}
static int
-ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+ldp_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct kif kif;
@@ -297,8 +286,7 @@ ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length,
}
static int
-ldp_interface_status_change(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+ldp_interface_status_change(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct listnode *node;
@@ -337,14 +325,13 @@ ldp_interface_status_change(int command, struct zclient *zclient,
}
static int
-ldp_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+ldp_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct interface *ifp;
struct kaddr ka;
- ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return (0);
@@ -365,14 +352,13 @@ ldp_interface_address_add(int command, struct zclient *zclient,
}
static int
-ldp_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+ldp_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct interface *ifp;
struct kaddr ka;
- ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return (0);
@@ -394,8 +380,7 @@ ldp_interface_address_delete(int command, struct zclient *zclient,
}
static int
-ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+ldp_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
struct zapi_nexthop *api_nh;
@@ -439,7 +424,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
(kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6)))
return (0);
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
add = 1;
if (api.nexthop_num == 0)
@@ -502,12 +487,11 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
* Receive PW status update from Zebra and send it to LDE process.
*/
static int
-ldp_zebra_read_pw_status_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS)
{
struct zapi_pw_status zpw;
- zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw);
+ zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw);
debug_zebra_in("pseudowire %s status %s", zpw.ifname,
(zpw.status == PW_STATUS_UP) ? "up" : "down");
diff --git a/lib/atomlist.c b/lib/atomlist.c
new file mode 100644
index 0000000000..8169ba9eb4
--- /dev/null
+++ b/lib/atomlist.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "atomlist.h"
+
+void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item)
+{
+ atomptr_t prevval;
+ atomptr_t i = atomptr_i(item);
+
+ atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
+
+ /* updating ->last is possible here, but makes the code considerably
+ * more complicated... let's not.
+ */
+ prevval = ATOMPTR_NULL;
+ item->next = ATOMPTR_NULL;
+
+ /* head-insert atomically
+ * release barrier: item + item->next writes must be completed
+ */
+ while (!atomic_compare_exchange_weak_explicit(&h->first, &prevval, i,
+ memory_order_release, memory_order_relaxed))
+ atomic_store_explicit(&item->next, prevval,
+ memory_order_relaxed);
+}
+
+void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item)
+{
+ atomptr_t prevval = ATOMPTR_NULL;
+ atomptr_t i = atomptr_i(item);
+ atomptr_t hint;
+ struct atomlist_item *prevptr;
+ _Atomic atomptr_t *prev;
+
+ item->next = ATOMPTR_NULL;
+
+ atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
+
+ /* place new item into ->last
+ * release: item writes completed; acquire: DD barrier on hint
+ */
+ hint = atomic_exchange_explicit(&h->last, i, memory_order_acq_rel);
+
+ while (1) {
+ if (atomptr_p(hint) == NULL)
+ prev = &h->first;
+ else
+ prev = &atomlist_itemp(hint)->next;
+
+ do {
+ prevval = atomic_load_explicit(prev,
+ memory_order_consume);
+ prevptr = atomlist_itemp(prevval);
+ if (prevptr == NULL)
+ break;
+
+ prev = &prevptr->next;
+ } while (prevptr);
+
+ /* last item is being deleted - start over */
+ if (atomptr_l(prevval)) {
+ hint = ATOMPTR_NULL;
+ continue;
+ }
+
+ /* no barrier - item->next is NULL and was so in xchg above */
+ if (!atomic_compare_exchange_strong_explicit(prev, &prevval, i,
+ memory_order_consume,
+ memory_order_consume)) {
+ hint = prevval;
+ continue;
+ }
+ break;
+ }
+}
+
+static void atomlist_del_core(struct atomlist_head *h,
+ struct atomlist_item *item,
+ _Atomic atomptr_t *hint,
+ atomptr_t next)
+{
+ _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd;
+ atomptr_t prevval, updval;
+ struct atomlist_item *prevptr;
+
+ /* drop us off "last" if needed. no r/w to barrier. */
+ prevval = atomptr_i(item);
+ atomic_compare_exchange_strong_explicit(&h->last, &prevval,
+ ATOMPTR_NULL,
+ memory_order_relaxed, memory_order_relaxed);
+
+ atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed);
+
+ /* the following code should be identical (except sort<>list) to
+ * atomsort_del_hint()
+ */
+ while (1) {
+ upd = NULL;
+ updval = ATOMPTR_LOCK;
+
+ do {
+ prevval = atomic_load_explicit(prev,
+ memory_order_consume);
+
+ /* track the beginning of a chain of deleted items
+ * this is neccessary to make this lock-free; we can
+ * complete deletions started by other threads.
+ */
+ if (!atomptr_l(prevval)) {
+ updval = prevval;
+ upd = prev;
+ }
+
+ prevptr = atomlist_itemp(prevval);
+ if (prevptr == item)
+ break;
+
+ prev = &prevptr->next;
+ } while (prevptr);
+
+ if (prevptr != item)
+ /* another thread completed our deletion */
+ return;
+
+ if (!upd || atomptr_l(updval)) {
+ /* failed to find non-deleted predecessor...
+ * have to try again
+ */
+ prev = &h->first;
+ continue;
+ }
+
+ if (!atomic_compare_exchange_strong_explicit(upd, &updval,
+ next, memory_order_consume,
+ memory_order_consume)) {
+ /* prev doesn't point to item anymore, something
+ * was inserted. continue at same position forward.
+ */
+ continue;
+ }
+ break;
+ }
+}
+
+void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item,
+ _Atomic atomptr_t *hint)
+{
+ atomptr_t next;
+
+ /* mark ourselves in-delete - full barrier */
+ next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
+ memory_order_acquire);
+ assert(!atomptr_l(next)); /* delete race on same item */
+
+ atomlist_del_core(h, item, hint, next);
+}
+
+struct atomlist_item *atomlist_pop(struct atomlist_head *h)
+{
+ struct atomlist_item *item;
+ atomptr_t next;
+
+ /* grab head of the list - and remember it in replval for the
+ * actual delete below. No matter what, the head of the list is
+ * where we start deleting because either it's our item, or it's
+ * some delete-marked items and then our item.
+ */
+ next = atomic_load_explicit(&h->first, memory_order_consume);
+
+ do {
+ item = atomlist_itemp(next);
+ if (!item)
+ return NULL;
+
+ /* try to mark deletion */
+ next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
+ memory_order_acquire);
+
+ } while (atomptr_l(next));
+ /* if loop is taken: delete race on same item (another pop or del)
+ * => proceed to next item
+ * if loop exited here: we have our item selected and marked
+ */
+ atomlist_del_core(h, item, &h->first, next);
+ return item;
+}
+
+struct atomsort_item *atomsort_add(struct atomsort_head *h,
+ struct atomsort_item *item, int (*cmpfn)(
+ const struct atomsort_item *,
+ const struct atomsort_item *))
+{
+ _Atomic atomptr_t *prev;
+ atomptr_t prevval;
+ atomptr_t i = atomptr_i(item);
+ struct atomsort_item *previtem;
+ int cmpval;
+
+ do {
+ prev = &h->first;
+
+ do {
+ prevval = atomic_load_explicit(prev,
+ memory_order_acquire);
+ previtem = atomptr_p(prevval);
+
+ if (!previtem || (cmpval = cmpfn(previtem, item)) > 0)
+ break;
+ if (cmpval == 0)
+ return previtem;
+
+ prev = &previtem->next;
+ } while (1);
+
+ if (atomptr_l(prevval))
+ continue;
+
+ item->next = prevval;
+ if (atomic_compare_exchange_strong_explicit(prev, &prevval, i,
+ memory_order_release, memory_order_relaxed))
+ break;
+ } while (1);
+
+ atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
+ return NULL;
+}
+
+static void atomsort_del_core(struct atomsort_head *h,
+ struct atomsort_item *item, _Atomic atomptr_t *hint,
+ atomptr_t next)
+{
+ _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd;
+ atomptr_t prevval, updval;
+ struct atomsort_item *prevptr;
+
+ atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed);
+
+ /* the following code should be identical (except sort<>list) to
+ * atomlist_del_core()
+ */
+ while (1) {
+ upd = NULL;
+ updval = ATOMPTR_LOCK;
+
+ do {
+ prevval = atomic_load_explicit(prev,
+ memory_order_consume);
+
+ /* track the beginning of a chain of deleted items
+ * this is neccessary to make this lock-free; we can
+ * complete deletions started by other threads.
+ */
+ if (!atomptr_l(prevval)) {
+ updval = prevval;
+ upd = prev;
+ }
+
+ prevptr = atomsort_itemp(prevval);
+ if (prevptr == item)
+ break;
+
+ prev = &prevptr->next;
+ } while (prevptr);
+
+ if (prevptr != item)
+ /* another thread completed our deletion */
+ return;
+
+ if (!upd || atomptr_l(updval)) {
+ /* failed to find non-deleted predecessor...
+ * have to try again
+ */
+ prev = &h->first;
+ continue;
+ }
+
+ if (!atomic_compare_exchange_strong_explicit(upd, &updval,
+ next, memory_order_relaxed,
+ memory_order_relaxed)) {
+ /* prev doesn't point to item anymore, something
+ * was inserted. continue at same position forward.
+ */
+ continue;
+ }
+ break;
+ }
+}
+
+void atomsort_del_hint(struct atomsort_head *h, struct atomsort_item *item,
+ _Atomic atomptr_t *hint)
+{
+ atomptr_t next;
+
+ /* mark ourselves in-delete - full barrier */
+ next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
+ memory_order_seq_cst);
+ assert(!atomptr_l(next)); /* delete race on same item */
+
+ atomsort_del_core(h, item, hint, next);
+}
+
+struct atomsort_item *atomsort_pop(struct atomsort_head *h)
+{
+ struct atomsort_item *item;
+ atomptr_t next;
+
+ /* grab head of the list - and remember it in replval for the
+ * actual delete below. No matter what, the head of the list is
+ * where we start deleting because either it's our item, or it's
+ * some delete-marked items and then our item.
+ */
+ next = atomic_load_explicit(&h->first, memory_order_consume);
+
+ do {
+ item = atomsort_itemp(next);
+ if (!item)
+ return NULL;
+
+ /* try to mark deletion */
+ next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
+ memory_order_acquire);
+
+ } while (atomptr_l(next));
+ /* if loop is taken: delete race on same item (another pop or del)
+ * => proceed to next item
+ * if loop exited here: we have our item selected and marked
+ */
+ atomsort_del_core(h, item, &h->first, next);
+ return item;
+}
diff --git a/lib/atomlist.h b/lib/atomlist.h
new file mode 100644
index 0000000000..e4098ccb54
--- /dev/null
+++ b/lib/atomlist.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_ATOMLIST_H
+#define _FRR_ATOMLIST_H
+
+#include "typesafe.h"
+#include "frratomic.h"
+
+/* pointer with lock/deleted/invalid bit in lowest bit
+ *
+ * for atomlist/atomsort, "locked" means "this pointer can't be updated, the
+ * item is being deleted". it is permissible to assume the item will indeed
+ * be deleted (as there are no replace/etc. ops in this).
+ *
+ * in general, lowest 2/3 bits on 32/64bit architectures are available for
+ * uses like this; the only thing that will really break this is putting an
+ * atomlist_item in a struct with "packed" attribute. (it'll break
+ * immediately and consistently.) -- don't do that.
+ *
+ * ATOMPTR_USER is currently unused (and available for atomic hash or skiplist
+ * implementations.)
+ */
+typedef uintptr_t atomptr_t;
+#define ATOMPTR_MASK (UINTPTR_MAX - 3)
+#define ATOMPTR_LOCK (1)
+#define ATOMPTR_USER (2)
+#define ATOMPTR_NULL (0)
+
+static inline atomptr_t atomptr_i(void *val)
+{
+ atomptr_t atomval = (atomptr_t)val;
+
+ assert(!(atomval & ATOMPTR_LOCK));
+ return atomval;
+}
+static inline void *atomptr_p(atomptr_t val)
+{
+ return (void *)(val & ATOMPTR_MASK);
+}
+static inline bool atomptr_l(atomptr_t val)
+{
+ return (bool)(val & ATOMPTR_LOCK);
+}
+static inline bool atomptr_u(atomptr_t val)
+{
+ return (bool)(val & ATOMPTR_USER);
+}
+
+
+/* the problem with, find(), find_gteq() and find_lt() on atomic lists is that
+ * they're neither an "acquire" nor a "release" operation; the element that
+ * was found is still on the list and doesn't change ownership. Therefore,
+ * an atomic transition in ownership state can't be implemented.
+ *
+ * Contrast this with add() or pop(): both function calls atomically transfer
+ * ownership of an item to or from the list, which makes them "acquire" /
+ * "release" operations.
+ *
+ * What can be implemented atomically is a "find_pop()", i.e. try to locate an
+ * item and atomically try to remove it if found. It's not currently
+ * implemented but can be added when needed.
+ *
+ * Either way - for find(), generally speaking, if you need to use find() on
+ * a list then the whole thing probably isn't well-suited to atomic
+ * implementation and you'll need to have extra locks around to make it work
+ * correctly.
+ */
+#ifdef WNO_ATOMLIST_UNSAFE_FIND
+# define atomic_find_warn
+#else
+# define atomic_find_warn __attribute__((_DEPRECATED( \
+ "WARNING: find() on atomic lists cannot be atomic by principle; " \
+ "check code to make sure usage pattern is OK and if it is, use " \
+ "#define WNO_ATOMLIST_UNSAFE_FIND")))
+#endif
+
+
+/* single-linked list, unsorted/arbitrary.
+ * can be used as queue with add_tail / pop
+ *
+ * all operations are lock-free, but not neccessarily wait-free. this means
+ * that there is no state where the system as a whole stops making process,
+ * but it *is* possible that a *particular* thread is delayed by some time.
+ *
+ * the only way for this to happen is for other threads to continuously make
+ * updates. an inactive / blocked / deadlocked other thread cannot cause such
+ * delays, and to cause such delays a thread must be heavily hitting the list -
+ * it's a rather theoretical concern.
+ */
+
+/* don't use these structs directly */
+struct atomlist_item {
+ _Atomic atomptr_t next;
+};
+#define atomlist_itemp(val) ((struct atomlist_item *)atomptr_p(val))
+
+struct atomlist_head {
+ _Atomic atomptr_t first, last;
+ _Atomic size_t count;
+};
+
+/* use as:
+ *
+ * PREDECL_ATOMLIST(namelist)
+ * struct name {
+ * struct namelist_item nlitem;
+ * }
+ * DECLARE_ATOMLIST(namelist, struct name, nlitem)
+ */
+#define PREDECL_ATOMLIST(prefix) \
+struct prefix ## _head { struct atomlist_head ah; }; \
+struct prefix ## _item { struct atomlist_item ai; };
+
+#define INIT_ATOMLIST(var) { }
+
+#define DECLARE_ATOMLIST(prefix, type, field) \
+macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \
+{ atomlist_add_head(&h->ah, &item->field.ai); } \
+macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \
+{ atomlist_add_tail(&h->ah, &item->field.ai); } \
+macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \
+ _Atomic atomptr_t *hint) \
+{ atomlist_del_hint(&h->ah, &item->field.ai, hint); } \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); } \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ char *p = (char *)atomlist_pop(&h->ah); \
+ return p ? (type *)(p - offsetof(type, field)) : NULL; } \
+macro_inline type *prefix ## _first(struct prefix##_head *h) \
+{ char *p = atomptr_p(atomic_load_explicit(&h->ah.first, \
+ memory_order_acquire)); \
+ return p ? (type *)(p - offsetof(type, field)) : NULL; } \
+macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ char *p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \
+ memory_order_acquire)); \
+ return p ? (type *)(p - offsetof(type, field)) : NULL; } \
+macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ return item ? prefix##_next(h, item) : NULL; } \
+macro_inline size_t prefix ## _count(struct prefix##_head *h) \
+{ return atomic_load_explicit(&h->ah.count, memory_order_relaxed); } \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ assert(prefix ## _count(h) == 0); \
+ memset(h, 0, sizeof(*h)); \
+} \
+/* ... */
+
+/* add_head:
+ * - contention on ->first pointer
+ * - return implies completion
+ */
+void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item);
+
+/* add_tail:
+ * - concurrent add_tail can cause wait but has progress guarantee
+ * - return does NOT imply completion. completion is only guaranteed after
+ * all other add_tail operations that started before this add_tail have
+ * completed as well.
+ */
+void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item);
+
+/* del/del_hint:
+ *
+ * OWNER MUST HOLD REFERENCE ON ITEM TO BE DELETED, ENSURING NO OTHER THREAD
+ * WILL TRY TO DELETE THE SAME ITEM. DELETING INCLUDES pop().
+ *
+ * as with all deletions, threads that started reading earlier may still hold
+ * pointers to the deleted item. completion is however guaranteed for all
+ * reads starting later.
+ */
+void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item,
+ _Atomic atomptr_t *hint);
+
+/* pop:
+ *
+ * as with all deletions, threads that started reading earlier may still hold
+ * pointers to the deleted item. completion is however guaranteed for all
+ * reads starting later.
+ */
+struct atomlist_item *atomlist_pop(struct atomlist_head *h);
+
+
+
+struct atomsort_item {
+ _Atomic atomptr_t next;
+};
+#define atomsort_itemp(val) ((struct atomsort_item *)atomptr_p(val))
+
+struct atomsort_head {
+ _Atomic atomptr_t first;
+ _Atomic size_t count;
+};
+
+#define _PREDECL_ATOMSORT(prefix) \
+struct prefix ## _head { struct atomsort_head ah; }; \
+struct prefix ## _item { struct atomsort_item ai; };
+
+#define INIT_ATOMSORT_UNIQ(var) { }
+#define INIT_ATOMSORT_NONUNIQ(var) { }
+
+#define _DECLARE_ATOMSORT(prefix, type, field, cmpfn_nuq, cmpfn_uq) \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ assert(h->ah.count == 0); \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ struct atomsort_item *p; \
+ p = atomsort_add(&h->ah, &item->field.ai, cmpfn_uq); \
+ return container_of_null(p, type, field.ai); \
+} \
+macro_inline type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ struct atomsort_item *p; \
+ p = atomptr_p(atomic_load_explicit(&h->ah.first, \
+ memory_order_acquire)); \
+ return container_of_null(p, type, field.ai); \
+} \
+macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ struct atomsort_item *p; \
+ p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \
+ memory_order_acquire)); \
+ return container_of_null(p, type, field.ai); \
+} \
+macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ return item ? prefix##_next(h, item) : NULL; \
+} \
+atomic_find_warn \
+macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ type *p = prefix ## _first(h); \
+ while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \
+ p = prefix ## _next(h, p); \
+ return p; \
+} \
+atomic_find_warn \
+macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ type *p = prefix ## _first(h), *prev = NULL; \
+ while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \
+ p = prefix ## _next(h, (prev = p)); \
+ return prev; \
+} \
+macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \
+ _Atomic atomptr_t *hint) \
+{ \
+ atomsort_del_hint(&h->ah, &item->field.ai, hint); \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ atomsort_del_hint(&h->ah, &item->field.ai, NULL); \
+} \
+macro_inline size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return atomic_load_explicit(&h->ah.count, memory_order_relaxed); \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct atomsort_item *p = atomsort_pop(&h->ah); \
+ return p ? container_of(p, type, field.ai) : NULL; \
+} \
+/* ... */
+
+#define PREDECL_ATOMSORT_UNIQ(prefix) \
+ _PREDECL_ATOMSORT(prefix)
+#define DECLARE_ATOMSORT_UNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct atomsort_item *a, \
+ const struct atomsort_item *b) \
+{ \
+ return cmpfn(container_of(a, type, field.ai), \
+ container_of(b, type, field.ai)); \
+} \
+ \
+_DECLARE_ATOMSORT(prefix, type, field, \
+ prefix ## __cmp, prefix ## __cmp) \
+ \
+atomic_find_warn \
+macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
+{ \
+ type *p = prefix ## _first(h); \
+ int cmpval = 0; \
+ while (p && (cmpval = cmpfn(p, item)) < 0) \
+ p = prefix ## _next(h, p); \
+ if (!p || cmpval > 0) \
+ return NULL; \
+ return p; \
+} \
+/* ... */
+
+#define PREDECL_ATOMSORT_NONUNIQ(prefix) \
+ _PREDECL_ATOMSORT(prefix)
+#define DECLARE_ATOMSORT_NONUNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct atomsort_item *a, \
+ const struct atomsort_item *b) \
+{ \
+ return cmpfn(container_of(a, type, field.ai), \
+ container_of(b, type, field.ai)); \
+} \
+macro_inline int prefix ## __cmp_uq(const struct atomsort_item *a, \
+ const struct atomsort_item *b) \
+{ \
+ int cmpval = cmpfn(container_of(a, type, field.ai), \
+ container_of(b, type, field.ai)); \
+ if (cmpval) \
+ return cmpval; \
+ if (a < b) \
+ return -1; \
+ if (a > b) \
+ return 1; \
+ return 0; \
+} \
+ \
+_DECLARE_ATOMSORT(prefix, type, field, \
+ prefix ## __cmp, prefix ## __cmp_uq) \
+/* ... */
+
+struct atomsort_item *atomsort_add(struct atomsort_head *h,
+ struct atomsort_item *item, int (*cmpfn)(
+ const struct atomsort_item *,
+ const struct atomsort_item *));
+
+void atomsort_del_hint(struct atomsort_head *h,
+ struct atomsort_item *item, _Atomic atomptr_t *hint);
+
+struct atomsort_item *atomsort_pop(struct atomsort_head *h);
+
+#endif /* _FRR_ATOMLIST_H */
diff --git a/lib/bfd.c b/lib/bfd.c
index 7e27a64de7..00dbd1b3d1 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -127,8 +127,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
*/
void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip, char *if_name,
- int ttl, int multihop, int command, int set_flag,
- vrf_id_t vrf_id)
+ int ttl, int multihop, int cbit, int command,
+ int set_flag, vrf_id_t vrf_id)
{
struct stream *s;
int ret;
@@ -208,6 +208,11 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
stream_putc(s, 0);
}
}
+ /* cbit */
+ if (cbit)
+ stream_putc(s, 1);
+ else
+ stream_putc(s, 0);
stream_putw_at(s, 0, stream_get_endp(s));
@@ -253,11 +258,13 @@ const char *bfd_get_command_dbg_str(int command)
*/
struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
struct prefix *sp, int *status,
+ int *remote_cbit,
vrf_id_t vrf_id)
{
unsigned int ifindex;
struct interface *ifp = NULL;
int plen;
+ int local_remote_cbit;
/* Get interface index. */
ifindex = stream_getl(s);
@@ -292,6 +299,9 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
stream_get(&sp->u.prefix, s, plen);
sp->prefixlen = stream_getc(s);
}
+ local_remote_cbit = stream_getc(s);
+ if (remote_cbit)
+ *remote_cbit = local_remote_cbit;
return ifp;
}
@@ -433,7 +443,8 @@ void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop,
* bfd_client_sendmsg - Format and send a client register
* command to Zebra to be forwarded to BFD
*/
-void bfd_client_sendmsg(struct zclient *zclient, int command)
+void bfd_client_sendmsg(struct zclient *zclient, int command,
+ vrf_id_t vrf_id)
{
struct stream *s;
int ret;
@@ -450,7 +461,7 @@ void bfd_client_sendmsg(struct zclient *zclient, int command)
s = zclient->obuf;
stream_reset(s);
- zclient_create_header(s, command, VRF_DEFAULT);
+ zclient_create_header(s, command, vrf_id);
stream_putl(s, getpid());
diff --git a/lib/bfd.h b/lib/bfd.h
index a93875c4cf..e4781f4eaf 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -48,6 +48,8 @@ struct bfd_gbl {
#define BFD_FLAG_PARAM_CFG (1 << 0) /* parameters have been configured */
#define BFD_FLAG_BFD_REG (1 << 1) /* Peer registered with BFD */
#define BFD_FLAG_BFD_TYPE_MULTIHOP (1 << 2) /* Peer registered with BFD as multihop */
+#define BFD_FLAG_BFD_CBIT_ON (1 << 3) /* Peer registered with CBIT set to on */
+#define BFD_FLAG_BFD_CHECK_CONTROLPLANE (1 << 4) /* BFD and controlplane daemon are linked */
#define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */
#define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */
@@ -83,13 +85,14 @@ extern void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx,
int *command);
extern void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip,
- char *if_name, int ttl, int multihop, int command,
- int set_flag, vrf_id_t vrf_id);
+ char *if_name, int ttl, int multihop, int cbit,
+ int command, int set_flag, vrf_id_t vrf_id);
extern const char *bfd_get_command_dbg_str(int command);
extern struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
struct prefix *sp, int *status,
+ int *remote_cbit,
vrf_id_t vrf_id);
const char *bfd_get_status_str(int status);
@@ -102,7 +105,8 @@ extern void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info,
int multihop, int extra_space, bool use_json,
json_object *json_obj);
-extern void bfd_client_sendmsg(struct zclient *zclient, int command);
+extern void bfd_client_sendmsg(struct zclient *zclient, int command,
+ vrf_id_t vrf_id);
extern void bfd_gbl_init(void);
diff --git a/lib/checksum.c b/lib/checksum.c
index 18e3850474..3473370041 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -46,6 +46,24 @@ int /* return checksum in low-order 16 bits */
return (answer);
}
+int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes)
+{
+ uint8_t dat[sizeof(struct ipv4_ph) + nbytes];
+
+ memcpy(dat, ph, sizeof(struct ipv4_ph));
+ memcpy(dat + sizeof(struct ipv4_ph), data, nbytes);
+ return in_cksum(dat, sizeof(dat));
+}
+
+int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes)
+{
+ uint8_t dat[sizeof(struct ipv6_ph) + nbytes];
+
+ memcpy(dat, ph, sizeof(struct ipv6_ph));
+ memcpy(dat + sizeof(struct ipv6_ph), data, nbytes);
+ return in_cksum(dat, sizeof(dat));
+}
+
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102U /* 5802 should be fine */
diff --git a/lib/checksum.h b/lib/checksum.h
index 7d50371439..56771d4f24 100644
--- a/lib/checksum.h
+++ b/lib/checksum.h
@@ -1,8 +1,33 @@
+#include <stdint.h>
+#include <netinet/in.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-extern int in_cksum(void *, int);
+
+/* IPv4 pseudoheader */
+struct ipv4_ph {
+ struct in_addr src;
+ struct in_addr dst;
+ uint8_t rsvd;
+ uint8_t proto;
+ uint16_t len;
+} __attribute__((packed));
+
+/* IPv6 pseudoheader */
+struct ipv6_ph {
+ struct in6_addr src;
+ struct in6_addr dst;
+ uint32_t ulpl;
+ uint8_t zero[3];
+ uint8_t next_hdr;
+} __attribute__((packed));
+
+extern int in_cksum(void *data, int nbytes);
+extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes);
+extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes);
+
#define FLETCHER_CHECKSUM_VALIDATE 0xffff
extern uint16_t fletcher_checksum(uint8_t *, const size_t len,
const uint16_t offset);
diff --git a/lib/command.c b/lib/command.c
index 559457c119..18426e0c51 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -149,6 +149,7 @@ const char *node_names[] = {
"bfd", /* BFD_NODE */
"bfd peer", /* BFD_PEER_NODE */
"openfabric", // OPENFABRIC_NODE
+ "vrrp", /* VRRP_NODE */
};
/* clang-format on */
@@ -332,7 +333,7 @@ int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
return found;
}
-static unsigned int cmd_hash_key(void *p)
+static unsigned int cmd_hash_key(const void *p)
{
int size = sizeof(p);
@@ -1386,7 +1387,7 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
/* Configuration from terminal */
DEFUN (config_terminal,
config_terminal_cmd,
- "configure terminal",
+ "configure [terminal]",
"Configuration from vty interface\n"
"Configuration terminal\n")
{
@@ -1705,12 +1706,16 @@ static int vty_write_config(struct vty *vty)
vty_out(vty, "frr defaults %s\n", DFLT_NAME);
vty_out(vty, "!\n");
- for (i = 0; i < vector_active(cmdvec); i++)
- if ((node = vector_slot(cmdvec, i)) && node->func
- && (node->vtysh || vty->type != VTY_SHELL)) {
- if ((*node->func)(vty))
- vty_out(vty, "!\n");
- }
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func
+ && (node->vtysh || vty->type != VTY_SHELL)) {
+ if ((*node->func)(vty))
+ vty_out(vty, "!\n");
+ }
+ }
+ pthread_rwlock_unlock(&running_config->lock);
if (vty->type == VTY_TERM) {
vty_out(vty, "end\n");
diff --git a/lib/command.h b/lib/command.h
index a5f9616dbf..d96ec97e67 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -147,6 +147,7 @@ enum node_type {
BFD_NODE, /* BFD protocol mode. */
BFD_PEER_NODE, /* BFD peer configuration mode. */
OPENFABRIC_NODE, /* OpenFabric router configuration node */
+ VRRP_NODE, /* VRRP node */
NODE_TYPE_MAX, /* maximum */
};
diff --git a/lib/compiler.h b/lib/compiler.h
index cb4f7531ec..c2e57db7f8 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -32,6 +32,7 @@ extern "C" {
# define _FALLTHROUGH __attribute__((fallthrough));
#endif
# define _CONSTRUCTOR(x) constructor(x)
+# define _DEPRECATED(x) deprecated(x)
#elif defined(__GNUC__)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
# define _RET_NONNULL , returns_nonnull
@@ -41,6 +42,9 @@ extern "C" {
# define _DESTRUCTOR(x) destructor(x)
# define _ALLOC_SIZE(x) alloc_size(x)
#endif
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define _DEPRECATED(x) deprecated(x)
+#endif
#if __GNUC__ >= 7
# define _FALLTHROUGH __attribute__((fallthrough));
#endif
@@ -68,6 +72,22 @@ extern "C" {
#ifndef _FALLTHROUGH
#define _FALLTHROUGH
#endif
+#ifndef _DEPRECATED
+#define _DEPRECATED(x) deprecated
+#endif
+
+/* pure = function does not modify memory & return value is the same if
+ * memory hasn't changed (=> allows compiler to optimize)
+ *
+ * Mostly autodetected by the compiler if function body is available (i.e.
+ * static inline functions in headers). Since that implies it should only be
+ * used in headers for non-inline functions, the "extern" is included here.
+ */
+#define ext_pure extern __attribute__((pure))
+
+/* for helper functions defined inside macros */
+#define macro_inline static inline __attribute__((unused))
+#define macro_pure static inline __attribute__((unused, pure))
/*
* for warnings on macros, put in the macro content like this:
@@ -92,6 +112,80 @@ extern "C" {
#define CPP_NOTICE(text)
#endif
+/* MAX / MIN are not commonly defined, but useful */
+/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a, b) \
+ ({ \
+ typeof(a) _max_a = (a); \
+ typeof(b) _max_b = (b); \
+ _max_a > _max_b ? _max_a : _max_b; \
+ })
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(a, b) \
+ ({ \
+ typeof(a) _min_a = (a); \
+ typeof(b) _min_b = (b); \
+ _min_a < _min_b ? _min_a : _min_b; \
+ })
+
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+/* this variant of container_of() retains 'const' on pointers without needing
+ * to be told to do so. The following will all work without warning:
+ *
+ * struct member *p;
+ * const struct member *cp;
+ *
+ * const struct cont *x = container_of(cp, struct cont, member);
+ * const struct cont *x = container_of(cp, const struct cont, member);
+ * const struct cont *x = container_of(p, struct cont, member);
+ * const struct cont *x = container_of(p, const struct cont, member);
+ * struct cont *x = container_of(p, struct cont, member);
+ *
+ * but the following will generate warnings about stripping const:
+ *
+ * struct cont *x = container_of(cp, struct cont, member);
+ * struct cont *x = container_of(cp, const struct cont, member);
+ * struct cont *x = container_of(p, const struct cont, member);
+ */
+#ifdef container_of
+#undef container_of
+#endif
+#define container_of(ptr, type, member) \
+ (__builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(&((type *)0)->member), \
+ typeof(ptr)) \
+ || __builtin_types_compatible_p(void *, typeof(ptr)), \
+ ({ \
+ typeof(((type *)0)->member) *__mptr = (void *)(ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); \
+ }), \
+ ({ \
+ typeof(((const type *)0)->member) *__mptr = (ptr); \
+ (const type *)((const char *)__mptr - \
+ offsetof(type, member)); \
+ }) \
+ ))
+
+#define container_of_null(ptr, type, member) \
+ ({ \
+ typeof(ptr) _tmp = (ptr); \
+ _tmp ? container_of(_tmp, type, member) : NULL; \
+ })
+
+#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/distribute.c b/lib/distribute.c
index be40bd2603..2aa6b927fb 100644
--- a/lib/distribute.c
+++ b/lib/distribute.c
@@ -131,7 +131,7 @@ static struct distribute *distribute_get(struct distribute_ctx *ctx,
return ret;
}
-static unsigned int distribute_hash_make(void *arg)
+static unsigned int distribute_hash_make(const void *arg)
{
const struct distribute *dist = arg;
diff --git a/lib/ferr.c b/lib/ferr.c
index d7fa1a84f8..65c0cf886d 100644
--- a/lib/ferr.c
+++ b/lib/ferr.c
@@ -72,9 +72,9 @@ static bool ferr_hash_cmp(const void *a, const void *b)
return f_a->code == f_b->code;
}
-static inline unsigned int ferr_hash_key(void *a)
+static inline unsigned int ferr_hash_key(const void *a)
{
- struct log_ref *f = a;
+ const struct log_ref *f = a;
return f->code;
}
diff --git a/lib/fifo.h b/lib/fifo.h
deleted file mode 100644
index 6f9c59b5c1..0000000000
--- a/lib/fifo.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* FIFO common header.
- * Copyright (C) 2015 Kunihiro Ishiguro
- *
- * This file is part of Quagga.
- *
- * Quagga is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * Quagga is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __LIB_FIFO_H__
-#define __LIB_FIFO_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* FIFO -- first in first out structure and macros. */
-struct fifo {
- struct fifo *next;
- struct fifo *prev;
-};
-
-#define FIFO_INIT(F) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- Xfifo->next = Xfifo->prev = Xfifo; \
- } while (0)
-
-#define FIFO_ADD(F, N) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- struct fifo *Xnode = (struct fifo *)(N); \
- Xnode->next = Xfifo; \
- Xnode->prev = Xfifo->prev; \
- Xfifo->prev = Xfifo->prev->next = Xnode; \
- } while (0)
-
-#define FIFO_DEL(N) \
- do { \
- struct fifo *Xnode = (struct fifo *)(N); \
- Xnode->prev->next = Xnode->next; \
- Xnode->next->prev = Xnode->prev; \
- } while (0)
-
-#define FIFO_HEAD(F) \
- ((((struct fifo *)(F))->next == (struct fifo *)(F)) ? NULL : (F)->next)
-
-#define FIFO_EMPTY(F) (((struct fifo *)(F))->next == (struct fifo *)(F))
-
-#define FIFO_TOP(F) (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LIB_FIFO_H__ */
diff --git a/lib/frratomic.h b/lib/frratomic.h
index e86030f83c..1e28253f2b 100644
--- a/lib/frratomic.h
+++ b/lib/frratomic.h
@@ -80,6 +80,9 @@ typedef std::atomic<uint_fast32_t> atomic_uint_fast32_t;
#define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \
mem2) \
__atomic_compare_exchange_n(atom, expect, desire, 1, mem1, mem2)
+#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \
+ mem2) \
+ __atomic_compare_exchange_n(atom, expect, desire, 0, mem1, mem2)
/* gcc 4.1 and newer,
* clang 3.3 (possibly older)
@@ -152,7 +155,7 @@ typedef std::atomic<uint_fast32_t> atomic_uint_fast32_t;
rval; \
})
-#define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \
+#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \
mem2) \
({ \
typeof(atom) _atom = (atom); \
@@ -166,6 +169,8 @@ typedef std::atomic<uint_fast32_t> atomic_uint_fast32_t;
*_expect = rval; \
ret; \
})
+#define atomic_compare_exchange_weak_explicit \
+ atomic_compare_exchange_strong_explicit
#define atomic_fetch_and_explicit(ptr, val, mem) \
({ \
diff --git a/lib/lua.c b/lib/frrlua.c
index 3d701a9364..b7d8eea6e8 100644
--- a/lib/lua.c
+++ b/lib/frrlua.c
@@ -26,7 +26,7 @@
#if defined(HAVE_LUA)
#include "prefix.h"
-#include "lua.h"
+#include "frrlua.h"
#include "log.h"
static int lua_zlog_debug(lua_State *L)
diff --git a/lib/lua.h b/lib/frrlua.h
index a864ab30e0..374eb70311 100644
--- a/lib/lua.h
+++ b/lib/frrlua.h
@@ -25,9 +25,9 @@
#if defined(HAVE_LUA)
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
#ifdef __cplusplus
extern "C" {
diff --git a/lib/frrstr.c b/lib/frrstr.c
index fd337073f8..c575c0b568 100644
--- a/lib/frrstr.c
+++ b/lib/frrstr.c
@@ -152,7 +152,33 @@ void frrstr_strvec_free(vector v)
vector_free(v);
}
-bool begins_with(const char *str, const char *prefix)
+char *frrstr_replace(const char *str, const char *find, const char *replace)
+{
+ char *ch;
+ char *nustr = XSTRDUP(MTYPE_TMP, str);
+
+ size_t findlen = strlen(find);
+ size_t repllen = strlen(replace);
+
+ while ((ch = strstr(nustr, find))) {
+ if (repllen > findlen) {
+ size_t nusz = strlen(nustr) + repllen - findlen + 1;
+ nustr = XREALLOC(MTYPE_TMP, nustr, nusz);
+ ch = strstr(nustr, find);
+ }
+
+ size_t nustrlen = strlen(nustr);
+ size_t taillen = (nustr + nustrlen) - (ch + findlen);
+
+ memmove(ch + findlen + (repllen - findlen), ch + findlen,
+ taillen + 1);
+ memcpy(ch, replace, repllen);
+ }
+
+ return nustr;
+}
+
+bool frrstr_startswith(const char *str, const char *prefix)
{
if (!str || !prefix)
return false;
@@ -166,6 +192,20 @@ bool begins_with(const char *str, const char *prefix)
return strncmp(str, prefix, lenprefix) == 0;
}
+bool frrstr_endswith(const char *str, const char *suffix)
+{
+ if (!str || !suffix)
+ return false;
+
+ size_t lenstr = strlen(str);
+ size_t lensuffix = strlen(suffix);
+
+ if (lensuffix > lenstr)
+ return false;
+
+ return strncmp(&str[lenstr - lensuffix], suffix, lensuffix) == 0;
+}
+
int all_digit(const char *str)
{
for (; *str != '\0'; str++)
diff --git a/lib/frrstr.h b/lib/frrstr.h
index 8b591849a3..3a935c90cb 100644
--- a/lib/frrstr.h
+++ b/lib/frrstr.h
@@ -88,6 +88,29 @@ void frrstr_filter_vec(vector v, regex_t *filter);
void frrstr_strvec_free(vector v);
/*
+ * Given a string, replaces all occurrences of a substring with a different
+ * string. The result is a new string. The original string is not modified.
+ *
+ * If 'replace' is longer than 'find', this function performs N+1 allocations,
+ * where N is the number of times 'find' occurs in 'str'. If 'replace' is equal
+ * in length or shorter than 'find', only 1 allocation is performed.
+ *
+ * str
+ * String to perform replacement on.
+ *
+ * find
+ * Substring to replace.
+ *
+ * replace
+ * String to replace 'find' with.
+ *
+ * Returns:
+ * A new string, allocated with MTYPE_TMP, that is the result of performing
+ * the replacement on 'str'. This must be freed by the caller.
+ */
+char *frrstr_replace(const char *str, const char *find, const char *replace);
+
+/*
* Prefix match for string.
*
* str
@@ -97,9 +120,23 @@ void frrstr_strvec_free(vector v);
* prefix to look for
*
* Returns:
- * true str starts with prefix, false otherwise
+ * true if str starts with prefix, false otherwise
+ */
+bool frrstr_startswith(const char *str, const char *prefix);
+
+/*
+ * Suffix match for string.
+ *
+ * str
+ * string to check for suffix match
+ *
+ * suffix
+ * suffix to look for
+ *
+ * Returns:
+ * true if str ends with suffix, false otherwise
*/
-bool begins_with(const char *str, const char *prefix);
+bool frrstr_endswith(const char *str, const char *suffix);
/*
* Check the string only contains digit characters.
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
index c9c942f9bf..38494fb007 100644
--- a/lib/grammar_sandbox_main.c
+++ b/lib/grammar_sandbox_main.c
@@ -58,6 +58,8 @@ int main(int argc, char **argv)
vty_init(master);
memory_init();
+ yang_init();
+ nb_init(master, NULL, 0);
vty_stdio(vty_do_exit);
diff --git a/lib/hash.c b/lib/hash.c
index 884c8f22af..fad7de5138 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -37,7 +37,7 @@ static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER;
static struct list *_hashes;
struct hash *hash_create_size(unsigned int size,
- unsigned int (*hash_key)(void *),
+ unsigned int (*hash_key)(const void *),
bool (*hash_cmp)(const void *, const void *),
const char *name)
{
@@ -66,7 +66,7 @@ struct hash *hash_create_size(unsigned int size,
return hash;
}
-struct hash *hash_create(unsigned int (*hash_key)(void *),
+struct hash *hash_create(unsigned int (*hash_key)(const void *),
bool (*hash_cmp)(const void *, const void *),
const char *name)
{
diff --git a/lib/hash.h b/lib/hash.h
index 60c412b8e0..c56a98d50c 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -79,7 +79,7 @@ struct hash {
unsigned int max_size;
/* Key make function. */
- unsigned int (*hash_key)(void *);
+ unsigned int (*hash_key)(const void *);
/* Data compare function. */
bool (*hash_cmp)(const void *, const void *);
@@ -123,7 +123,7 @@ struct hash {
* Returns:
* a new hash table
*/
-extern struct hash *hash_create(unsigned int (*hash_key)(void *),
+extern struct hash *hash_create(unsigned int (*hash_key)(const void *),
bool (*hash_cmp)(const void *, const void *),
const char *name);
@@ -158,7 +158,7 @@ extern struct hash *hash_create(unsigned int (*hash_key)(void *),
* a new hash table
*/
extern struct hash *
-hash_create_size(unsigned int size, unsigned int (*hash_key)(void *),
+hash_create_size(unsigned int size, unsigned int (*hash_key)(const void *),
bool (*hash_cmp)(const void *, const void *),
const char *name);
diff --git a/lib/if.c b/lib/if.c
index 86b850c059..3f489e0c3e 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -187,18 +187,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
if (yang_module_find("frr-interface")) {
struct lyd_node *if_dnode;
- if_dnode = yang_dnode_get(
- running_config->dnode,
- "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
- ifp->name, old_vrf->name);
- if (if_dnode) {
- yang_dnode_change_leaf(if_dnode, vrf->name);
- running_config->version++;
+ pthread_rwlock_wrlock(&running_config->lock);
+ {
+ if_dnode = yang_dnode_get(
+ running_config->dnode,
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
+ ifp->name, old_vrf->name);
+ if (if_dnode) {
+ yang_dnode_change_leaf(if_dnode, vrf->name);
+ running_config->version++;
+ }
}
+ pthread_rwlock_unlock(&running_config->lock);
}
}
-
/* Delete interface structure. */
void if_delete_retain(struct interface *ifp)
{
@@ -386,6 +389,34 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id)
return NULL;
}
+size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
+ struct interface ***result, vrf_id_t vrf_id)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ struct list *rs = list_new();
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (ifp->hw_addr_len == (int)addrsz
+ && !memcmp(hw_addr, ifp->hw_addr, addrsz))
+ listnode_add(rs, ifp);
+ }
+
+ if (rs->count) {
+ *result = XCALLOC(MTYPE_TMP,
+ sizeof(struct interface *) * rs->count);
+ list_to_array(rs, (void **)*result, rs->count);
+ }
+
+ int count = rs->count;
+
+ list_delete(&rs);
+
+ return count;
+}
+
+
/* Get interface by name if given name interface doesn't exist create
one. */
struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id)
@@ -873,6 +904,19 @@ struct connected *connected_add_by_prefix(struct interface *ifp,
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)) {
+ if (c->address->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
+ break;
+ }
+ return c;
+}
+
#if 0 /* this route_table of struct connected's is unused \
* however, it would be good to use a route_table rather than \
* a list.. \
diff --git a/lib/if.h b/lib/if.h
index d26d4dd68b..2dc1a7b2de 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -225,6 +225,10 @@ struct interface {
not work as expected.
*/
ifindex_t ifindex;
+ /*
+ * ifindex of parent interface, if any
+ */
+ ifindex_t link_ifindex;
#define IFINDEX_INTERNAL 0
/* Zebra internal interface status */
@@ -482,6 +486,8 @@ extern struct connected *if_lookup_address(void *matchaddr, int family,
vrf_id_t vrf_id);
extern struct interface *if_lookup_prefix(struct prefix *prefix,
vrf_id_t vrf_id);
+size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
+ struct interface ***result, vrf_id_t vrf_id);
/* These 3 functions are to be used when the ifname argument is terminated
by a '\0' character: */
@@ -540,6 +546,7 @@ extern struct connected *connected_lookup_prefix_exact(struct interface *,
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 *);
+struct connected *connected_get_linklocal(struct interface *ifp);
/* link parameters */
struct if_link_params *if_link_params_get(struct interface *);
diff --git a/lib/if_rmap.c b/lib/if_rmap.c
index b0802da961..ca6f512ec6 100644
--- a/lib/if_rmap.c
+++ b/lib/if_rmap.c
@@ -107,7 +107,7 @@ static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname)
return ret;
}
-static unsigned int if_rmap_hash_make(void *data)
+static unsigned int if_rmap_hash_make(const void *data)
{
const struct if_rmap *if_rmap = data;
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index f4ddadc66e..1c2399fdd3 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -56,6 +56,9 @@ struct ipaddr {
#define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4
#define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6
+#define IPADDRSZ(p) \
+ (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr))
+
static inline int str2ipaddr(const char *str, struct ipaddr *ip)
{
int ret;
diff --git a/lib/json.c b/lib/json.c
index 4ea20ba178..efc3794040 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -64,6 +64,11 @@ void json_object_boolean_true_add(struct json_object *obj, const char *key)
json_object_object_add(obj, key, json_object_new_boolean(1));
}
+void json_object_boolean_add(struct json_object *obj, const char *key, bool val)
+{
+ json_object_object_add(obj, key, json_object_new_boolean(val));
+}
+
struct json_object *json_object_lock(struct json_object *obj)
{
return json_object_get(obj);
diff --git a/lib/json.h b/lib/json.h
index a5251662be..c4d566b318 100644
--- a/lib/json.h
+++ b/lib/json.h
@@ -61,6 +61,8 @@ extern void json_object_string_add(struct json_object *obj, const char *key,
const char *s);
extern void json_object_int_add(struct json_object *obj, const char *key,
int64_t i);
+void json_object_boolean_add(struct json_object *obj, const char *key,
+ bool val);
extern void json_object_boolean_false_add(struct json_object *obj,
const char *key);
extern void json_object_boolean_true_add(struct json_object *obj,
diff --git a/lib/lib_errors.c b/lib/lib_errors.c
index 5f6c25b770..b6c764d873 100644
--- a/lib/lib_errors.c
+++ b/lib/lib_errors.c
@@ -333,6 +333,12 @@ static struct log_ref ferr_lib_err[] = {
.suggestion = "Open an Issue with all relevant log files and restart FRR"
},
{
+ .code = EC_LIB_GRPC_INIT,
+ .title = "gRPC initialization error",
+ .description = "Upon startup FRR failed to properly initialize and startup the gRPC northbound plugin",
+ .suggestion = "Check if the gRPC libraries are installed correctly in the system.",
+ },
+ {
.code = EC_LIB_NB_CB_CONFIG_ABORT,
.title = "A northbound configuration callback has failed in the ABORT phase",
.description = "A callback used to process a configuration change has returned an error while trying to abort a change",
diff --git a/lib/lib_errors.h b/lib/lib_errors.h
index fc405c2098..39b39fb065 100644
--- a/lib/lib_errors.h
+++ b/lib/lib_errors.h
@@ -80,6 +80,7 @@ enum lib_log_refs {
EC_LIB_SYSREPO_INIT,
EC_LIB_SYSREPO_DATA_CONVERT,
EC_LIB_LIBSYSREPO,
+ EC_LIB_GRPC_INIT,
EC_LIB_ID_CONSISTENCY,
EC_LIB_ID_EXHAUST,
};
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 0d4c8d6c0f..5970e70a6b 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -830,7 +830,12 @@ static int frr_config_read_in(struct thread *t)
/*
* Update the shared candidate after reading the startup configuration.
*/
- nb_config_replace(vty_shared_candidate_config, running_config, true);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_replace(vty_shared_candidate_config, running_config,
+ true);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return 0;
}
diff --git a/lib/linklist.c b/lib/linklist.c
index 6cb639b27c..e8ba9edc12 100644
--- a/lib/linklist.c
+++ b/lib/linklist.c
@@ -391,3 +391,18 @@ struct listnode *listnode_add_force(struct list **list, void *val)
*list = list_new();
return listnode_add(*list, val);
}
+
+void **list_to_array(struct list *list, void **arr, size_t arrlen)
+{
+ struct listnode *ln;
+ void *vp;
+ size_t idx = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(list, ln, vp)) {
+ arr[idx++] = vp;
+ if (idx == arrlen)
+ break;
+ }
+
+ return arr;
+}
diff --git a/lib/linklist.h b/lib/linklist.h
index 8761bc3d16..da42aa2688 100644
--- a/lib/linklist.h
+++ b/lib/linklist.h
@@ -240,6 +240,26 @@ extern void list_sort(struct list *list,
int (*cmp)(const void **, const void **));
/*
+ * Convert a list to an array of void pointers.
+ *
+ * Starts from the list head and ends either on the last node of the list or
+ * when the provided array cannot store any more elements.
+ *
+ * list
+ * list to convert
+ *
+ * arr
+ * Pre-allocated array of void *
+ *
+ * arrlen
+ * Number of elements in arr
+ *
+ * Returns:
+ * arr
+ */
+void **list_to_array(struct list *list, void **arr, size_t arrlen);
+
+/*
* Delete a list and NULL its pointer.
*
* If non-null, list->del is called with each data element.
diff --git a/lib/memory.h b/lib/memory.h
index 91a02b7966..0002ea3349 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -26,8 +26,6 @@
extern "C" {
#endif
-#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
-
#if defined(HAVE_MALLOC_SIZE) && !defined(HAVE_MALLOC_USABLE_SIZE)
#define malloc_usable_size(x) malloc_size(x)
#define HAVE_MALLOC_USABLE_SIZE
diff --git a/lib/nexthop.c b/lib/nexthop.c
index 8e16e70590..57a2f1daaa 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -36,40 +36,132 @@
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
-/* check if nexthops are same, non-recursive */
-int nexthop_same_no_recurse(const struct nexthop *next1,
- const struct nexthop *next2)
+static int _nexthop_labels_cmp(const struct nexthop *nh1,
+ const struct nexthop *nh2)
{
- if (next1->type != next2->type)
+ const struct mpls_label_stack *nhl1 = NULL;
+ const struct mpls_label_stack *nhl2 = NULL;
+
+ nhl1 = nh1->nh_label;
+ nhl2 = nh2->nh_label;
+
+ /* No labels is a match */
+ if (!nhl1 && !nhl2)
return 0;
- switch (next1->type) {
+ if (nhl1 && !nhl2)
+ return 1;
+
+ if (nhl2 && !nhl1)
+ return -1;
+
+ if (nhl1->num_labels > nhl2->num_labels)
+ return 1;
+
+ if (nhl1->num_labels < nhl2->num_labels)
+ return -1;
+
+ return memcmp(nhl1->label, nhl2->label, nhl1->num_labels);
+}
+
+static int _nexthop_g_addr_cmp(enum nexthop_types_t type,
+ const union g_addr *addr1,
+ const union g_addr *addr2)
+{
+ int ret = 0;
+
+ switch (type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4))
- return 0;
- if (next1->ifindex && (next1->ifindex != next2->ifindex))
- return 0;
+ ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6);
break;
case NEXTHOP_TYPE_IFINDEX:
- if (next1->ifindex != next2->ifindex)
- return 0;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ /* No addr here */
break;
+ }
+
+ return ret;
+}
+
+static int _nexthop_gateway_cmp(const struct nexthop *nh1,
+ const struct nexthop *nh2)
+{
+ return _nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
+}
+
+static int _nexthop_source_cmp(const struct nexthop *nh1,
+ const struct nexthop *nh2)
+{
+ return _nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
+}
+
+static int _nexthop_cmp_no_labels(const struct nexthop *next1,
+ const struct nexthop *next2)
+{
+ int ret = 0;
+
+ if (next1->vrf_id < next2->vrf_id)
+ return -1;
+
+ if (next1->vrf_id > next2->vrf_id)
+ return 1;
+
+ if (next1->type < next2->type)
+ return -1;
+
+ if (next1->type > next2->type)
+ return 1;
+
+ switch (next1->type) {
+ case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV6:
- if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6))
- return 0;
+ ret = _nexthop_gateway_cmp(next1, next2);
+ if (ret != 0)
+ return ret;
break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV6_IFINDEX:
- if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6))
- return 0;
- if (next1->ifindex != next2->ifindex)
- return 0;
+ ret = _nexthop_gateway_cmp(next1, next2);
+ if (ret != 0)
+ return ret;
+ /* Intentional Fall-Through */
+ case NEXTHOP_TYPE_IFINDEX:
+ if (next1->ifindex < next2->ifindex)
+ return -1;
+
+ if (next1->ifindex > next2->ifindex)
+ return 1;
break;
- default:
- /* do nothing */
+ case NEXTHOP_TYPE_BLACKHOLE:
+ if (next1->bh_type < next2->bh_type)
+ return -1;
+
+ if (next1->bh_type > next2->bh_type)
+ return 1;
break;
}
- return 1;
+
+ ret = _nexthop_source_cmp(next1, next2);
+
+ return ret;
+}
+
+int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
+{
+ int ret = 0;
+
+ ret = _nexthop_cmp_no_labels(next1, next2);
+ if (ret != 0)
+ return ret;
+
+ ret = _nexthop_labels_cmp(next1, next2);
+
+ return ret;
}
int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2)
@@ -121,27 +213,12 @@ const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
/*
* Check if the labels match for the 2 nexthops specified.
*/
-int nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
+bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
{
- const struct mpls_label_stack *nhl1, *nhl2;
-
- nhl1 = nh1->nh_label;
- nhl2 = nh2->nh_label;
-
- /* No labels is a match */
- if (!nhl1 && !nhl2)
- return 1;
-
- if (!nhl1 || !nhl2)
- return 0;
-
- if (nhl1->num_labels != nhl2->num_labels)
- return 0;
-
- if (memcmp(nhl1->label, nhl2->label, nhl1->num_labels))
- return 0;
+ if (_nexthop_labels_cmp(nh1, nh2) != 0)
+ return false;
- return 1;
+ return true;
}
struct nexthop *nexthop_new(void)
@@ -180,45 +257,28 @@ bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
if (nh1 == nh2)
return true;
- if (nh1->vrf_id != nh2->vrf_id)
+ if (nexthop_cmp(nh1, nh2) != 0)
return false;
- if (nh1->type != nh2->type)
+ return true;
+}
+
+bool nexthop_same_no_labels(const struct nexthop *nh1,
+ const struct nexthop *nh2)
+{
+ if (nh1 && !nh2)
return false;
- switch (nh1->type) {
- case NEXTHOP_TYPE_IFINDEX:
- if (nh1->ifindex != nh2->ifindex)
- return false;
- break;
- case NEXTHOP_TYPE_IPV4:
- if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr)
- return false;
- break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr)
- return false;
- if (nh1->ifindex != nh2->ifindex)
- return false;
- break;
- case NEXTHOP_TYPE_IPV6:
- if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16))
- return false;
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16))
- return false;
- if (nh1->ifindex != nh2->ifindex)
- return false;
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- if (nh1->bh_type != nh2->bh_type)
- return false;
- break;
- }
+ if (!nh1 && nh2)
+ return false;
+
+ if (nh1 == nh2)
+ return true;
+
+ if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
+ return false;
- /* Compare labels too (if present) */
- return (!!nexthop_labels_match(nh1, nh2));
+ return true;
}
/* Update nexthop with label information. */
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 663acaeb69..5b6c12d4ef 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -139,12 +139,13 @@ void nexthop_del_labels(struct nexthop *);
uint32_t nexthop_hash(const struct nexthop *nexthop);
extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);
+extern bool nexthop_same_no_labels(const struct nexthop *nh1,
+ const struct nexthop *nh2);
+extern int nexthop_cmp(const struct nexthop *nh1, const struct nexthop *nh2);
extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type);
-extern int nexthop_same_no_recurse(const struct nexthop *next1,
- const struct nexthop *next2);
-extern int nexthop_labels_match(const struct nexthop *nh1,
- const struct nexthop *nh2);
+extern bool nexthop_labels_match(const struct nexthop *nh1,
+ const struct nexthop *nh2);
extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2);
extern const char *nexthop2str(const struct nexthop *nexthop,
diff --git a/lib/northbound.c b/lib/northbound.c
index 5e031ac2ce..dbf90c58d4 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -40,6 +40,21 @@ struct nb_config *running_config;
/* Hash table of user pointers associated with configuration entries. */
static struct hash *running_config_entries;
+/* Management lock for the running configuration. */
+static struct {
+ /* Mutex protecting this structure. */
+ pthread_mutex_t mtx;
+
+ /* Actual lock. */
+ bool locked;
+
+ /* Northbound client who owns this lock. */
+ enum nb_client owner_client;
+
+ /* Northbound user who owns this lock. */
+ const void *owner_user;
+} running_config_mgmt_lock;
+
/*
* Global lock used to prevent multiple configuration transactions from
* happening concurrently.
@@ -51,6 +66,7 @@ static int nb_callback_configuration(const enum nb_event event,
static struct nb_transaction *nb_transaction_new(struct nb_config *config,
struct nb_config_cbs *changes,
enum nb_client client,
+ const void *user,
const char *comment);
static void nb_transaction_free(struct nb_transaction *transaction);
static int nb_transaction_process(enum nb_event event,
@@ -248,6 +264,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
else
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
+ pthread_rwlock_init(&config->lock, NULL);
return config;
}
@@ -256,6 +273,7 @@ void nb_config_free(struct nb_config *config)
{
if (config->dnode)
yang_dnode_free(config->dnode);
+ pthread_rwlock_destroy(&config->lock);
XFREE(MTYPE_NB_CONFIG, config);
}
@@ -266,6 +284,7 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
dup->dnode = yang_dnode_dup(config->dnode);
dup->version = config->version;
+ pthread_rwlock_init(&dup->lock, NULL);
return dup;
}
@@ -513,17 +532,28 @@ int nb_candidate_edit(struct nb_config *candidate,
bool nb_candidate_needs_update(const struct nb_config *candidate)
{
- if (candidate->version < running_config->version)
- return true;
+ bool ret = false;
- return false;
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ if (candidate->version < running_config->version)
+ ret = true;
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+
+ return ret;
}
int nb_candidate_update(struct nb_config *candidate)
{
struct nb_config *updated_config;
- updated_config = nb_config_dup(running_config);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ updated_config = nb_config_dup(running_config);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+
if (nb_config_merge(updated_config, candidate, true) != NB_OK)
return NB_ERR;
@@ -575,15 +605,20 @@ int nb_candidate_validate(struct nb_config *candidate)
return NB_ERR_VALIDATION;
RB_INIT(nb_config_cbs, &changes);
- nb_config_diff(running_config, candidate, &changes);
- ret = nb_candidate_validate_changes(candidate, &changes);
- nb_config_diff_del_changes(&changes);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_diff(running_config, candidate, &changes);
+ ret = nb_candidate_validate_changes(candidate, &changes);
+ nb_config_diff_del_changes(&changes);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return ret;
}
int nb_candidate_commit_prepare(struct nb_config *candidate,
- enum nb_client client, const char *comment,
+ enum nb_client client, const void *user,
+ const char *comment,
struct nb_transaction **transaction)
{
struct nb_config_cbs changes;
@@ -596,25 +631,36 @@ int nb_candidate_commit_prepare(struct nb_config *candidate,
}
RB_INIT(nb_config_cbs, &changes);
- nb_config_diff(running_config, candidate, &changes);
- if (RB_EMPTY(nb_config_cbs, &changes))
- return NB_ERR_NO_CHANGES;
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_diff(running_config, candidate, &changes);
+ if (RB_EMPTY(nb_config_cbs, &changes)) {
+ pthread_rwlock_unlock(&running_config->lock);
+ return NB_ERR_NO_CHANGES;
+ }
- if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) {
- flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
- "%s: failed to validate candidate configuration",
- __func__);
- nb_config_diff_del_changes(&changes);
- return NB_ERR_VALIDATION;
- }
+ if (nb_candidate_validate_changes(candidate, &changes)
+ != NB_OK) {
+ flog_warn(
+ EC_LIB_NB_CANDIDATE_INVALID,
+ "%s: failed to validate candidate configuration",
+ __func__);
+ nb_config_diff_del_changes(&changes);
+ pthread_rwlock_unlock(&running_config->lock);
+ return NB_ERR_VALIDATION;
+ }
- *transaction = nb_transaction_new(candidate, &changes, client, comment);
- if (*transaction == NULL) {
- flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
- "%s: failed to create transaction", __func__);
- nb_config_diff_del_changes(&changes);
- return NB_ERR_LOCKED;
+ *transaction = nb_transaction_new(candidate, &changes, client,
+ user, comment);
+ if (*transaction == NULL) {
+ flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
+ "%s: failed to create transaction", __func__);
+ nb_config_diff_del_changes(&changes);
+ pthread_rwlock_unlock(&running_config->lock);
+ return NB_ERR_LOCKED;
+ }
}
+ pthread_rwlock_unlock(&running_config->lock);
return nb_transaction_process(NB_EV_PREPARE, *transaction);
}
@@ -633,7 +679,11 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction,
/* Replace running by candidate. */
transaction->config->version++;
- nb_config_replace(running_config, transaction->config, true);
+ pthread_rwlock_wrlock(&running_config->lock);
+ {
+ nb_config_replace(running_config, transaction->config, true);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
/* Record transaction. */
if (save_transaction
@@ -645,13 +695,13 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction,
}
int nb_candidate_commit(struct nb_config *candidate, enum nb_client client,
- bool save_transaction, const char *comment,
- uint32_t *transaction_id)
+ const void *user, bool save_transaction,
+ const char *comment, uint32_t *transaction_id)
{
struct nb_transaction *transaction = NULL;
int ret;
- ret = nb_candidate_commit_prepare(candidate, client, comment,
+ ret = nb_candidate_commit_prepare(candidate, client, user, comment,
&transaction);
/*
* Apply the changes if the preparation phase succeeded. Otherwise abort
@@ -666,6 +716,60 @@ int nb_candidate_commit(struct nb_config *candidate, enum nb_client client,
return ret;
}
+int nb_running_lock(enum nb_client client, const void *user)
+{
+ int ret = -1;
+
+ pthread_mutex_lock(&running_config_mgmt_lock.mtx);
+ {
+ if (!running_config_mgmt_lock.locked) {
+ running_config_mgmt_lock.locked = true;
+ running_config_mgmt_lock.owner_client = client;
+ running_config_mgmt_lock.owner_user = user;
+ ret = 0;
+ }
+ }
+ pthread_mutex_unlock(&running_config_mgmt_lock.mtx);
+
+ return ret;
+}
+
+int nb_running_unlock(enum nb_client client, const void *user)
+{
+ int ret = -1;
+
+ pthread_mutex_lock(&running_config_mgmt_lock.mtx);
+ {
+ if (running_config_mgmt_lock.locked
+ && running_config_mgmt_lock.owner_client == client
+ && running_config_mgmt_lock.owner_user == user) {
+ running_config_mgmt_lock.locked = false;
+ running_config_mgmt_lock.owner_client = NB_CLIENT_NONE;
+ running_config_mgmt_lock.owner_user = NULL;
+ ret = 0;
+ }
+ }
+ pthread_mutex_unlock(&running_config_mgmt_lock.mtx);
+
+ return ret;
+}
+
+int nb_running_lock_check(enum nb_client client, const void *user)
+{
+ int ret = -1;
+
+ pthread_mutex_lock(&running_config_mgmt_lock.mtx);
+ {
+ if (!running_config_mgmt_lock.locked
+ || (running_config_mgmt_lock.owner_client == client
+ && running_config_mgmt_lock.owner_user == user))
+ ret = 0;
+ }
+ pthread_mutex_unlock(&running_config_mgmt_lock.mtx);
+
+ return ret;
+}
+
static void nb_log_callback(const enum nb_event event,
enum nb_operation operation, const char *xpath,
const char *value)
@@ -812,13 +916,20 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
return nb_node->cbs.rpc(xpath, input, output);
}
-static struct nb_transaction *nb_transaction_new(struct nb_config *config,
- struct nb_config_cbs *changes,
- enum nb_client client,
- const char *comment)
+static struct nb_transaction *
+nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes,
+ enum nb_client client, const void *user, const char *comment)
{
struct nb_transaction *transaction;
+ if (nb_running_lock_check(client, user)) {
+ flog_warn(
+ EC_LIB_NB_TRANSACTION_CREATION_FAILED,
+ "%s: running configuration is locked by another client",
+ __func__);
+ return NULL;
+ }
+
if (transaction_in_progress) {
flog_warn(
EC_LIB_NB_TRANSACTION_CREATION_FAILED,
@@ -852,40 +963,52 @@ static int nb_transaction_process(enum nb_event event,
{
struct nb_config_cb *cb;
- RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
- struct nb_config_change *change = (struct nb_config_change *)cb;
- int ret;
-
- /*
- * Only try to release resources that were allocated
- * successfully.
- */
- if (event == NB_EV_ABORT && change->prepare_ok == false)
- break;
+ /*
+ * Need to lock the running configuration since transaction->changes
+ * can contain pointers to data nodes from the running configuration.
+ */
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
+ struct nb_config_change *change =
+ (struct nb_config_change *)cb;
+ int ret;
- /* Call the appropriate callback. */
- ret = nb_callback_configuration(event, change);
- switch (event) {
- case NB_EV_PREPARE:
- if (ret != NB_OK)
- return ret;
- change->prepare_ok = true;
- break;
- case NB_EV_ABORT:
- case NB_EV_APPLY:
/*
- * At this point it's not possible to reject the
- * transaction anymore, so any failure here can lead to
- * inconsistencies and should be treated as a bug.
- * Operations prone to errors, like validations and
- * resource allocations, should be performed during the
- * 'prepare' phase.
+ * Only try to release resources that were allocated
+ * successfully.
*/
- break;
- default:
- break;
+ if (event == NB_EV_ABORT && change->prepare_ok == false)
+ break;
+
+ /* Call the appropriate callback. */
+ ret = nb_callback_configuration(event, change);
+ switch (event) {
+ case NB_EV_PREPARE:
+ if (ret != NB_OK) {
+ pthread_rwlock_unlock(
+ &running_config->lock);
+ return ret;
+ }
+ change->prepare_ok = true;
+ break;
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /*
+ * At this point it's not possible to reject the
+ * transaction anymore, so any failure here can
+ * lead to inconsistencies and should be treated
+ * as a bug. Operations prone to errors, like
+ * validations and resource allocations, should
+ * be performed during the 'prepare' phase.
+ */
+ break;
+ default:
+ break;
+ }
}
}
+ pthread_rwlock_unlock(&running_config->lock);
return NB_OK;
}
@@ -1531,7 +1654,7 @@ static bool running_config_entry_cmp(const void *value1, const void *value2)
return strmatch(c1->xpath, c2->xpath);
}
-static unsigned int running_config_entry_key_make(void *value)
+static unsigned int running_config_entry_key_make(const void *value)
{
return string_hash_make(value);
}
@@ -1704,6 +1827,8 @@ const char *nb_client_name(enum nb_client client)
return "ConfD";
case NB_CLIENT_SYSREPO:
return "Sysrepo";
+ case NB_CLIENT_GRPC:
+ return "gRPC";
default:
return "unknown";
}
@@ -1761,6 +1886,7 @@ void nb_init(struct thread_master *tm,
running_config_entries = hash_create(running_config_entry_key_make,
running_config_entry_cmp,
"Running Configuration Entries");
+ pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL);
/* Initialize the northbound CLI. */
nb_cli_init(tm);
@@ -1778,4 +1904,5 @@ void nb_terminate(void)
hash_clean(running_config_entries, running_config_entry_free);
hash_free(running_config_entries);
nb_config_free(running_config);
+ pthread_mutex_destroy(&running_config_mgmt_lock.mtx);
}
diff --git a/lib/northbound.h b/lib/northbound.h
index 14f27c1d41..8f6753506b 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -414,15 +414,28 @@ enum nb_error {
/* Northbound clients. */
enum nb_client {
- NB_CLIENT_CLI = 0,
+ NB_CLIENT_NONE = 0,
+ NB_CLIENT_CLI,
NB_CLIENT_CONFD,
NB_CLIENT_SYSREPO,
+ NB_CLIENT_GRPC,
};
/* Northbound configuration. */
struct nb_config {
+ /* Configuration data. */
struct lyd_node *dnode;
+
+ /* Configuration version. */
uint32_t version;
+
+ /*
+ * Lock protecting this structure. The use of this lock is always
+ * necessary when reading or modifying the global running configuration.
+ * For candidate configurations, use of this lock is optional depending
+ * on the threading scheme of the northbound plugin.
+ */
+ pthread_rwlock_t lock;
};
/* Northbound configuration callback. */
@@ -662,6 +675,9 @@ extern int nb_candidate_validate(struct nb_config *candidate);
* client
* Northbound client performing the commit.
*
+ * user
+ * Northbound user performing the commit (can be NULL).
+ *
* comment
* Optional comment describing the commit.
*
@@ -682,7 +698,7 @@ extern int nb_candidate_validate(struct nb_config *candidate);
* - NB_ERR for other errors.
*/
extern int nb_candidate_commit_prepare(struct nb_config *candidate,
- enum nb_client client,
+ enum nb_client client, const void *user,
const char *comment,
struct nb_transaction **transaction);
@@ -727,6 +743,9 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
* client
* Northbound client performing the commit.
*
+ * user
+ * Northbound user performing the commit (can be NULL).
+ *
* save_transaction
* Specify whether the transaction should be recorded in the transactions log
* or not.
@@ -748,11 +767,57 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
* - NB_ERR for other errors.
*/
extern int nb_candidate_commit(struct nb_config *candidate,
- enum nb_client client, bool save_transaction,
- const char *comment, uint32_t *transaction_id);
+ enum nb_client client, const void *user,
+ bool save_transaction, const char *comment,
+ uint32_t *transaction_id);
+
+/*
+ * Lock the running configuration.
+ *
+ * client
+ * Northbound client.
+ *
+ * user
+ * Northbound user (can be NULL).
+ *
+ * Returns:
+ * 0 on success, -1 when the running configuration is already locked.
+ */
+extern int nb_running_lock(enum nb_client client, const void *user);
+
+/*
+ * Unlock the running configuration.
+ *
+ * client
+ * Northbound client.
+ *
+ * user
+ * Northbound user (can be NULL).
+ *
+ * Returns:
+ * 0 on success, -1 when the running configuration is already unlocked or
+ * locked by another client/user.
+ */
+extern int nb_running_unlock(enum nb_client client, const void *user);
+
+/*
+ * Check if the running configuration is locked or not for the given
+ * client/user.
+ *
+ * client
+ * Northbound client.
+ *
+ * user
+ * Northbound user (can be NULL).
+ *
+ * Returns:
+ * 0 if the running configuration is unlocked or if the client/user owns the
+ * lock, -1 otherwise.
+ */
+extern int nb_running_lock_check(enum nb_client client, const void *user);
/*
- * Iterate over operetional data.
+ * Iterate over operational data.
*
* xpath
* Data path of the YANG data we want to iterate over.
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 48faa7595a..ae1b0578a0 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -185,7 +185,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
/* Do an implicit "commit" when using the classic CLI mode. */
if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
- false, NULL, NULL);
+ vty, false, NULL, NULL);
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
vty_out(vty, "%% Configuration failed: %s.\n\n",
nb_err_name(ret));
@@ -193,8 +193,13 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
"Please check the logs for more details.\n");
/* Regenerate candidate for consistency. */
- nb_config_replace(vty->candidate_config, running_config,
- true);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_replace(vty->candidate_config,
+ running_config, true);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+
return CMD_WARNING_CONFIG_FAILED;
}
}
@@ -237,7 +242,7 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty)
/* Perform the rollback. */
ret = nb_candidate_commit(
- vty->confirmed_commit_rollback, NB_CLIENT_CLI, true,
+ vty->confirmed_commit_rollback, NB_CLIENT_CLI, vty, true,
"Rollback to previous configuration - confirmed commit has timed out",
&transaction_id);
if (ret == NB_OK)
@@ -291,11 +296,6 @@ static int nb_cli_commit(struct vty *vty, bool force,
return CMD_SUCCESS;
}
- if (vty_exclusive_lock != NULL && vty_exclusive_lock != vty) {
- vty_out(vty, "%% Configuration is locked by another VTY.\n\n");
- return CMD_WARNING;
- }
-
/* "force" parameter. */
if (!force && nb_candidate_needs_update(vty->candidate_config)) {
vty_out(vty,
@@ -307,7 +307,12 @@ static int nb_cli_commit(struct vty *vty, bool force,
/* "confirm" parameter. */
if (confirmed_timeout) {
- vty->confirmed_commit_rollback = nb_config_dup(running_config);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ vty->confirmed_commit_rollback =
+ nb_config_dup(running_config);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
vty->t_confirmed_commit_timeout = NULL;
thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
@@ -315,14 +320,19 @@ static int nb_cli_commit(struct vty *vty, bool force,
&vty->t_confirmed_commit_timeout);
}
- ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true,
- comment, &transaction_id);
+ ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, vty,
+ true, comment, &transaction_id);
/* Map northbound return code to CLI return code. */
switch (ret) {
case NB_OK:
- nb_config_replace(vty->candidate_config_base, running_config,
- true);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_replace(vty->candidate_config_base,
+ running_config, true);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+
vty_out(vty,
"%% Configuration committed successfully (Transaction ID #%u).\n\n",
transaction_id);
@@ -687,7 +697,12 @@ DEFPY (config_update,
return CMD_WARNING;
}
- nb_config_replace(vty->candidate_config_base, running_config, true);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_config_replace(vty->candidate_config_base, running_config,
+ true);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
@@ -787,8 +802,12 @@ DEFPY (show_config_running,
}
}
- nb_cli_show_config(vty, running_config, format, translator,
- !!with_defaults);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ nb_cli_show_config(vty, running_config, format, translator,
+ !!with_defaults);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return CMD_SUCCESS;
}
@@ -902,57 +921,68 @@ DEFPY (show_config_compare,
struct nb_config *config2, *config_transaction2 = NULL;
int ret = CMD_WARNING;
- if (c1_candidate)
- config1 = vty->candidate_config;
- else if (c1_running)
- config1 = running_config;
- else {
- config_transaction1 = nb_db_transaction_load(c1_tid);
- if (!config_transaction1) {
- vty_out(vty, "%% Transaction %u does not exist\n\n",
- (unsigned int)c1_tid);
- goto exit;
+ /*
+ * For simplicity, lock the running configuration regardless if it's
+ * going to be used or not.
+ */
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ if (c1_candidate)
+ config1 = vty->candidate_config;
+ else if (c1_running)
+ config1 = running_config;
+ else {
+ config_transaction1 = nb_db_transaction_load(c1_tid);
+ if (!config_transaction1) {
+ vty_out(vty,
+ "%% Transaction %u does not exist\n\n",
+ (unsigned int)c1_tid);
+ goto exit;
+ }
+ config1 = config_transaction1;
}
- config1 = config_transaction1;
- }
- if (c2_candidate)
- config2 = vty->candidate_config;
- else if (c2_running)
- config2 = running_config;
- else {
- config_transaction2 = nb_db_transaction_load(c2_tid);
- if (!config_transaction2) {
- vty_out(vty, "%% Transaction %u does not exist\n\n",
- (unsigned int)c2_tid);
- goto exit;
+ if (c2_candidate)
+ config2 = vty->candidate_config;
+ else if (c2_running)
+ config2 = running_config;
+ else {
+ config_transaction2 = nb_db_transaction_load(c2_tid);
+ if (!config_transaction2) {
+ vty_out(vty,
+ "%% Transaction %u does not exist\n\n",
+ (unsigned int)c2_tid);
+ goto exit;
+ }
+ config2 = config_transaction2;
}
- config2 = config_transaction2;
- }
- if (json)
- format = NB_CFG_FMT_JSON;
- else if (xml)
- format = NB_CFG_FMT_XML;
- else
- format = NB_CFG_FMT_CMDS;
+ if (json)
+ format = NB_CFG_FMT_JSON;
+ else if (xml)
+ format = NB_CFG_FMT_XML;
+ else
+ format = NB_CFG_FMT_CMDS;
- if (translator_family) {
- translator = yang_translator_find(translator_family);
- if (!translator) {
- vty_out(vty, "%% Module translator \"%s\" not found\n",
- translator_family);
- goto exit;
+ if (translator_family) {
+ translator = yang_translator_find(translator_family);
+ if (!translator) {
+ vty_out(vty,
+ "%% Module translator \"%s\" not found\n",
+ translator_family);
+ goto exit;
+ }
}
- }
- ret = nb_cli_show_config_compare(vty, config1, config2, format,
- translator);
-exit:
- if (config_transaction1)
- nb_config_free(config_transaction1);
- if (config_transaction2)
- nb_config_free(config_transaction2);
+ ret = nb_cli_show_config_compare(vty, config1, config2, format,
+ translator);
+ exit:
+ if (config_transaction1)
+ nb_config_free(config_transaction1);
+ if (config_transaction2)
+ nb_config_free(config_transaction2);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
return ret;
}
@@ -1509,7 +1539,7 @@ static int nb_cli_rollback_configuration(struct vty *vty,
snprintf(comment, sizeof(comment), "Rollback to transaction %u",
transaction_id);
- ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, true, comment,
+ ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, vty, true, comment,
NULL);
nb_config_free(candidate);
switch (ret) {
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
index 0df286511e..e9669fc7e1 100644
--- a/lib/northbound_confd.c
+++ b/lib/northbound_confd.c
@@ -289,7 +289,11 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
struct cdb_iter_args iter_args;
int ret;
- candidate = nb_config_dup(running_config);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ candidate = nb_config_dup(running_config);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
/* Iterate over all configuration changes. */
iter_args.candidate = candidate;
@@ -322,7 +326,7 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
*/
transaction = NULL;
ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL,
- &transaction);
+ NULL, &transaction);
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
enum confd_errcode errcode;
const char *errmsg;
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
new file mode 100644
index 0000000000..a55da23dd1
--- /dev/null
+++ b/lib/northbound_grpc.cpp
@@ -0,0 +1,936 @@
+//
+// Copyright (C) 2019 NetDEF, Inc.
+// Renato Westphal
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; see the file COPYING; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+#include <zebra.h>
+
+#include "log.h"
+#include "libfrr.h"
+#include "version.h"
+#include "command.h"
+#include "lib_errors.h"
+#include "northbound.h"
+#include "northbound_db.h"
+
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <string>
+
+#include <grpcpp/grpcpp.h>
+#include "grpc/frr-northbound.grpc.pb.h"
+
+#define GRPC_DEFAULT_PORT 50051
+
+/*
+ * NOTE: we can't use the FRR debugging infrastructure here since it uses
+ * atomics and C++ has a different atomics API. Enable gRPC debugging
+ * unconditionally until we figure out a way to solve this problem.
+ */
+static bool nb_dbg_client_grpc = 1;
+
+static pthread_t grpc_pthread;
+
+class NorthboundImpl final : public frr::Northbound::Service
+{
+ public:
+ NorthboundImpl(void)
+ {
+ _nextCandidateId = 0;
+ }
+
+ ~NorthboundImpl(void)
+ {
+ // Delete candidates.
+ for (auto it = _candidates.begin(); it != _candidates.end();
+ it++)
+ delete_candidate(&it->second);
+ }
+
+ grpc::Status
+ GetCapabilities(grpc::ServerContext *context,
+ frr::GetCapabilitiesRequest const *request,
+ frr::GetCapabilitiesResponse *response) override
+ {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC GetCapabilities()");
+
+ // Response: string frr_version = 1;
+ response->set_frr_version(FRR_VERSION);
+
+ // Response: bool rollback_support = 2;
+#ifdef HAVE_CONFIG_ROLLBACKS
+ response->set_rollback_support(true);
+#else
+ response->set_rollback_support(false);
+#endif
+
+ // Response: repeated ModuleData supported_modules = 3;
+ struct yang_module *module;
+ RB_FOREACH (module, yang_modules, &yang_modules) {
+ auto m = response->add_supported_modules();
+
+ m->set_name(module->name);
+ if (module->info->rev_size)
+ m->set_revision(module->info->rev[0].date);
+ m->set_organization(module->info->org);
+ }
+
+ // Response: repeated Encoding supported_encodings = 4;
+ response->add_supported_encodings(frr::JSON);
+ response->add_supported_encodings(frr::XML);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status Get(grpc::ServerContext *context,
+ frr::GetRequest const *request,
+ grpc::ServerWriter<frr::GetResponse> *writer) override
+ {
+ // Request: DataType type = 1;
+ int type = request->type();
+ // Request: Encoding encoding = 2;
+ frr::Encoding encoding = request->encoding();
+ // Request: bool with_defaults = 3;
+ bool with_defaults = request->with_defaults();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC Get(type: %u, encoding: %u, with_defaults: %u)",
+ type, encoding, with_defaults);
+
+ // Request: repeated string path = 4;
+ auto paths = request->path();
+ for (const std::string &path : paths) {
+ frr::GetResponse response;
+ grpc::Status status;
+
+ // Response: int64 timestamp = 1;
+ response.set_timestamp(time(NULL));
+
+ // Response: DataTree data = 2;
+ auto *data = response.mutable_data();
+ data->set_encoding(request->encoding());
+ status = get_path(data, path, type,
+ encoding2lyd_format(encoding),
+ with_defaults);
+
+ // Something went wrong...
+ if (!status.ok())
+ return status;
+
+ writer->Write(response);
+ }
+
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC Get() end");
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ CreateCandidate(grpc::ServerContext *context,
+ frr::CreateCandidateRequest const *request,
+ frr::CreateCandidateResponse *response) override
+ {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC CreateCandidate()");
+
+ struct candidate *candidate = create_candidate();
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::RESOURCE_EXHAUSTED,
+ "Can't create candidate configuration");
+
+ // Response: uint32 candidate_id = 1;
+ response->set_candidate_id(candidate->id);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ DeleteCandidate(grpc::ServerContext *context,
+ frr::DeleteCandidateRequest const *request,
+ frr::DeleteCandidateResponse *response) override
+ {
+ // Request: uint32 candidate_id = 1;
+ uint32_t candidate_id = request->candidate_id();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC DeleteCandidate(candidate_id: %u)",
+ candidate_id);
+
+ struct candidate *candidate = get_candidate(candidate_id);
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+
+ delete_candidate(candidate);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ UpdateCandidate(grpc::ServerContext *context,
+ frr::UpdateCandidateRequest const *request,
+ frr::UpdateCandidateResponse *response) override
+ {
+ // Request: uint32 candidate_id = 1;
+ uint32_t candidate_id = request->candidate_id();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC UpdateCandidate(candidate_id: %u)",
+ candidate_id);
+
+ struct candidate *candidate = get_candidate(candidate_id);
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+
+ if (candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "candidate is in the middle of a transaction");
+
+ if (nb_candidate_update(candidate->config) != NB_OK)
+ return grpc::Status(
+ grpc::StatusCode::INTERNAL,
+ "failed to update candidate configuration");
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ EditCandidate(grpc::ServerContext *context,
+ frr::EditCandidateRequest const *request,
+ frr::EditCandidateResponse *response) override
+ {
+ // Request: uint32 candidate_id = 1;
+ uint32_t candidate_id = request->candidate_id();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC EditCandidate(candidate_id: %u)",
+ candidate_id);
+
+ struct candidate *candidate = get_candidate(candidate_id);
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+
+ // Create a copy of the candidate. For consistency, we need to
+ // ensure that either all changes are accepted or none are (in
+ // the event of an error).
+ struct nb_config *candidate_tmp =
+ nb_config_dup(candidate->config);
+
+ auto pvs = request->update();
+ for (const frr::PathValue &pv : pvs) {
+ if (yang_dnode_edit(candidate_tmp->dnode, pv.path(),
+ pv.value())
+ != 0) {
+ nb_config_free(candidate_tmp);
+ return grpc::Status(
+ grpc::StatusCode::INVALID_ARGUMENT,
+ "Failed to update \"" + pv.path()
+ + "\"");
+ }
+ }
+
+ pvs = request->delete_();
+ for (const frr::PathValue &pv : pvs) {
+ if (yang_dnode_delete(candidate_tmp->dnode, pv.path())
+ != 0) {
+ nb_config_free(candidate_tmp);
+ return grpc::Status(
+ grpc::StatusCode::INVALID_ARGUMENT,
+ "Failed to remove \"" + pv.path()
+ + "\"");
+ }
+ }
+
+ // No errors, accept all changes.
+ nb_config_replace(candidate->config, candidate_tmp, false);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ LoadToCandidate(grpc::ServerContext *context,
+ frr::LoadToCandidateRequest const *request,
+ frr::LoadToCandidateResponse *response) override
+ {
+ // Request: uint32 candidate_id = 1;
+ uint32_t candidate_id = request->candidate_id();
+ // Request: LoadType type = 2;
+ int load_type = request->type();
+ // Request: DataTree config = 3;
+ auto config = request->config();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC LoadToCandidate(candidate_id: %u)",
+ candidate_id);
+
+ struct candidate *candidate = get_candidate(candidate_id);
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+
+ struct lyd_node *dnode = dnode_from_data_tree(&config, true);
+ if (!dnode)
+ return grpc::Status(
+ grpc::StatusCode::INTERNAL,
+ "Failed to parse the configuration");
+
+ struct nb_config *loaded_config = nb_config_new(dnode);
+
+ if (load_type == frr::LoadToCandidateRequest::REPLACE)
+ nb_config_replace(candidate->config, loaded_config,
+ false);
+ else if (nb_config_merge(candidate->config, loaded_config,
+ false)
+ != NB_OK)
+ return grpc::Status(
+ grpc::StatusCode::INTERNAL,
+ "Failed to merge the loaded configuration");
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status Commit(grpc::ServerContext *context,
+ frr::CommitRequest const *request,
+ frr::CommitResponse *response) override
+ {
+ // Request: uint32 candidate_id = 1;
+ uint32_t candidate_id = request->candidate_id();
+ // Request: Phase phase = 2;
+ int phase = request->phase();
+ // Request: string comment = 3;
+ const std::string comment = request->comment();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC Commit(candidate_id: %u)",
+ candidate_id);
+
+ // Find candidate configuration.
+ struct candidate *candidate = get_candidate(candidate_id);
+ if (!candidate)
+ return grpc::Status(
+ grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+
+ int ret = NB_OK;
+ uint32_t transaction_id = 0;
+
+ // Check for misuse of the two-phase commit protocol.
+ switch (phase) {
+ case frr::CommitRequest::PREPARE:
+ case frr::CommitRequest::ALL:
+ if (candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "pending transaction in progress");
+ break;
+ case frr::CommitRequest::ABORT:
+ case frr::CommitRequest::APPLY:
+ if (!candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "no transaction in progress");
+ break;
+ default:
+ break;
+ }
+
+ // Execute the user request.
+ switch (phase) {
+ case frr::CommitRequest::VALIDATE:
+ ret = nb_candidate_validate(candidate->config);
+ break;
+ case frr::CommitRequest::PREPARE:
+ ret = nb_candidate_commit_prepare(
+ candidate->config, NB_CLIENT_GRPC, NULL,
+ comment.c_str(), &candidate->transaction);
+ break;
+ case frr::CommitRequest::ABORT:
+ nb_candidate_commit_abort(candidate->transaction);
+ break;
+ case frr::CommitRequest::APPLY:
+ nb_candidate_commit_apply(candidate->transaction, true,
+ &transaction_id);
+ break;
+ case frr::CommitRequest::ALL:
+ ret = nb_candidate_commit(
+ candidate->config, NB_CLIENT_GRPC, NULL, true,
+ comment.c_str(), &transaction_id);
+ break;
+ }
+
+ // Map northbound error codes to gRPC error codes.
+ switch (ret) {
+ case NB_ERR_NO_CHANGES:
+ return grpc::Status(
+ grpc::StatusCode::ABORTED,
+ "No configuration changes detected");
+ case NB_ERR_LOCKED:
+ return grpc::Status(
+ grpc::StatusCode::UNAVAILABLE,
+ "There's already a transaction in progress");
+ case NB_ERR_VALIDATION:
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Validation error");
+ case NB_ERR_RESOURCE:
+ return grpc::Status(
+ grpc::StatusCode::RESOURCE_EXHAUSTED,
+ "Failed do allocate resources");
+ case NB_ERR:
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Internal error");
+ default:
+ break;
+ }
+
+ // Response: uint32 transaction_id = 1;
+ if (transaction_id)
+ response->set_transaction_id(transaction_id);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ ListTransactions(grpc::ServerContext *context,
+ frr::ListTransactionsRequest const *request,
+ grpc::ServerWriter<frr::ListTransactionsResponse>
+ *writer) override
+ {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC ListTransactions()");
+
+ nb_db_transactions_iterate(list_transactions_cb, writer);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status
+ GetTransaction(grpc::ServerContext *context,
+ frr::GetTransactionRequest const *request,
+ frr::GetTransactionResponse *response) override
+ {
+ struct nb_config *nb_config;
+
+ // Request: uint32 transaction_id = 1;
+ uint32_t transaction_id = request->transaction_id();
+ // Request: Encoding encoding = 2;
+ frr::Encoding encoding = request->encoding();
+ // Request: bool with_defaults = 3;
+ bool with_defaults = request->with_defaults();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug(
+ "received RPC GetTransaction(transaction_id: %u, encoding: %u)",
+ transaction_id, encoding);
+
+ // Load configuration from the transactions database.
+ nb_config = nb_db_transaction_load(transaction_id);
+ if (!nb_config)
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Transaction not found");
+
+ // Response: DataTree config = 1;
+ auto config = response->mutable_config();
+ config->set_encoding(encoding);
+
+ // Dump data using the requested format.
+ if (data_tree_from_dnode(config, nb_config->dnode,
+ encoding2lyd_format(encoding),
+ with_defaults)
+ != 0) {
+ nb_config_free(nb_config);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Failed to dump data");
+ }
+
+ nb_config_free(nb_config);
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status LockConfig(grpc::ServerContext *context,
+ frr::LockConfigRequest const *request,
+ frr::LockConfigResponse *response) override
+ {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC LockConfig()");
+
+ if (nb_running_lock(NB_CLIENT_GRPC, NULL))
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "running configuration is locked already");
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status UnlockConfig(grpc::ServerContext *context,
+ frr::UnlockConfigRequest const *request,
+ frr::UnlockConfigResponse *response) override
+ {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC UnlockConfig()");
+
+ if (nb_running_unlock(NB_CLIENT_GRPC, NULL))
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "failed to unlock the running configuration");
+
+ return grpc::Status::OK;
+ }
+
+ grpc::Status Execute(grpc::ServerContext *context,
+ frr::ExecuteRequest const *request,
+ frr::ExecuteResponse *response) override
+ {
+ struct nb_node *nb_node;
+ struct list *input_list;
+ struct list *output_list;
+ struct listnode *node;
+ struct yang_data *data;
+ const char *xpath;
+
+ // Request: string path = 1;
+ xpath = request->path().c_str();
+
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC Execute(path: \"%s\")", xpath);
+
+ if (request->path().empty())
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Data path is empty");
+
+ nb_node = nb_node_find(xpath);
+ if (!nb_node)
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Unknown data path");
+
+ input_list = yang_data_list_new();
+ output_list = yang_data_list_new();
+
+ // Read input parameters.
+ auto input = request->input();
+ for (const frr::PathValue &pv : input) {
+ // Request: repeated PathValue input = 2;
+ data = yang_data_new(pv.path().c_str(),
+ pv.value().c_str());
+ listnode_add(input_list, data);
+ }
+
+ // Execute callback registered for this XPath.
+ if (nb_node->cbs.rpc(xpath, input_list, output_list) != NB_OK) {
+ flog_warn(EC_LIB_NB_CB_RPC,
+ "%s: rpc callback failed: %s", __func__,
+ xpath);
+ list_delete(&input_list);
+ list_delete(&output_list);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "RPC failed");
+ }
+
+ // Process output parameters.
+ for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) {
+ // Response: repeated PathValue output = 1;
+ frr::PathValue *pv = response->add_output();
+ pv->set_path(data->xpath);
+ pv->set_value(data->value);
+ }
+
+ // Release memory.
+ list_delete(&input_list);
+ list_delete(&output_list);
+
+ return grpc::Status::OK;
+ }
+
+ private:
+ struct candidate {
+ uint32_t id;
+ struct nb_config *config;
+ struct nb_transaction *transaction;
+ };
+ std::map<uint32_t, struct candidate> _candidates;
+ uint32_t _nextCandidateId;
+
+ static int yang_dnode_edit(struct lyd_node *dnode,
+ const std::string &path,
+ const std::string &value)
+ {
+ ly_errno = LY_SUCCESS;
+ dnode = lyd_new_path(dnode, ly_native_ctx, path.c_str(),
+ (void *)value.c_str(),
+ (LYD_ANYDATA_VALUETYPE)0,
+ LYD_PATH_OPT_UPDATE);
+ if (!dnode && ly_errno != LY_SUCCESS) {
+ flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
+ __func__);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static int yang_dnode_delete(struct lyd_node *dnode,
+ const std::string &path)
+ {
+ dnode = yang_dnode_get(dnode, path.c_str());
+ if (!dnode)
+ return -1;
+
+ lyd_free(dnode);
+
+ return 0;
+ }
+
+ static LYD_FORMAT encoding2lyd_format(enum frr::Encoding encoding)
+ {
+ switch (encoding) {
+ case frr::JSON:
+ return LYD_JSON;
+ case frr::XML:
+ return LYD_XML;
+ }
+ }
+
+ static int get_oper_data_cb(const struct lys_node *snode,
+ struct yang_translator *translator,
+ struct yang_data *data, void *arg)
+ {
+ struct lyd_node *dnode = static_cast<struct lyd_node *>(arg);
+ int ret = yang_dnode_edit(dnode, data->xpath, data->value);
+ yang_data_free(data);
+
+ return (ret == 0) ? NB_OK : NB_ERR;
+ }
+
+ static void list_transactions_cb(void *arg, int transaction_id,
+ const char *client_name,
+ const char *date, const char *comment)
+ {
+ grpc::ServerWriter<frr::ListTransactionsResponse> *writer =
+ static_cast<grpc::ServerWriter<
+ frr::ListTransactionsResponse> *>(arg);
+ frr::ListTransactionsResponse response;
+
+ // Response: uint32 id = 1;
+ response.set_id(transaction_id);
+
+ // Response: string client = 2;
+ response.set_client(client_name);
+
+ // Response: string date = 3;
+ response.set_date(date);
+
+ // Response: string comment = 4;
+ response.set_comment(comment);
+
+ writer->Write(response);
+ }
+
+ static int data_tree_from_dnode(frr::DataTree *dt,
+ const struct lyd_node *dnode,
+ LYD_FORMAT lyd_format,
+ bool with_defaults)
+ {
+ char *strp;
+ int options = 0;
+
+ SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS);
+ if (with_defaults)
+ SET_FLAG(options, LYP_WD_ALL);
+ else
+ SET_FLAG(options, LYP_WD_TRIM);
+
+ if (lyd_print_mem(&strp, dnode, lyd_format, options) == 0) {
+ if (strp) {
+ dt->set_data(strp);
+ free(strp);
+ }
+ return 0;
+ }
+
+ return -1;
+ }
+
+ static struct lyd_node *dnode_from_data_tree(const frr::DataTree *dt,
+ bool config_only)
+ {
+ struct lyd_node *dnode;
+ int options;
+
+ if (config_only)
+ options = LYD_OPT_CONFIG;
+ else
+ options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
+
+ dnode = lyd_parse_mem(ly_native_ctx, dt->data().c_str(),
+ encoding2lyd_format(dt->encoding()),
+ options);
+
+ return dnode;
+ }
+
+ static struct lyd_node *get_dnode_config(const std::string &path)
+ {
+ struct lyd_node *dnode;
+
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ dnode = yang_dnode_get(running_config->dnode,
+ path.empty() ? NULL
+ : path.c_str());
+ if (dnode)
+ dnode = yang_dnode_dup(dnode);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+
+ return dnode;
+ }
+
+ static struct lyd_node *get_dnode_state(const std::string &path)
+ {
+ struct lyd_node *dnode;
+
+ 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;
+ }
+
+ return dnode;
+ }
+
+ static grpc::Status get_path(frr::DataTree *dt, const std::string &path,
+ int type, LYD_FORMAT lyd_format,
+ bool with_defaults)
+ {
+ struct lyd_node *dnode_config = NULL;
+ struct lyd_node *dnode_state = NULL;
+ struct lyd_node *dnode_final;
+
+ // Configuration data.
+ if (type == frr::GetRequest_DataType_ALL
+ || type == frr::GetRequest_DataType_CONFIG) {
+ dnode_config = get_dnode_config(path);
+ if (!dnode_config)
+ return grpc::Status(
+ grpc::StatusCode::INVALID_ARGUMENT,
+ "Data path not found");
+ }
+
+ // Operational data.
+ if (type == frr::GetRequest_DataType_ALL
+ || type == frr::GetRequest_DataType_STATE) {
+ dnode_state = get_dnode_state(path);
+ if (!dnode_state) {
+ if (dnode_config)
+ yang_dnode_free(dnode_config);
+ return grpc::Status(
+ grpc::StatusCode::INVALID_ARGUMENT,
+ "Failed to fetch operational data");
+ }
+ }
+
+ switch (type) {
+ case frr::GetRequest_DataType_ALL:
+ //
+ // Combine configuration and state data into a single
+ // dnode.
+ //
+ if (lyd_merge(dnode_state, dnode_config,
+ LYD_OPT_EXPLICIT)
+ != 0) {
+ yang_dnode_free(dnode_state);
+ yang_dnode_free(dnode_config);
+ return grpc::Status(
+ grpc::StatusCode::INTERNAL,
+ "Failed to merge configuration and state data");
+ }
+
+ dnode_final = dnode_state;
+ break;
+ case frr::GetRequest_DataType_CONFIG:
+ dnode_final = dnode_config;
+ break;
+ case frr::GetRequest_DataType_STATE:
+ dnode_final = dnode_state;
+ break;
+ }
+
+ // Validate data to create implicit default nodes if necessary.
+ int validate_opts = 0;
+ if (type == frr::GetRequest_DataType_CONFIG)
+ validate_opts = LYD_OPT_CONFIG;
+ else
+ validate_opts = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
+ lyd_validate(&dnode_final, validate_opts, ly_native_ctx);
+
+ // Dump data using the requested format.
+ int ret = data_tree_from_dnode(dt, dnode_final, lyd_format,
+ with_defaults);
+ yang_dnode_free(dnode_final);
+ if (ret != 0)
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Failed to dump data");
+
+ return grpc::Status::OK;
+ }
+
+ struct candidate *create_candidate(void)
+ {
+ uint32_t candidate_id = ++_nextCandidateId;
+
+ // Check for overflow.
+ // TODO: implement an algorithm for unique reusable IDs.
+ if (candidate_id == 0)
+ return NULL;
+
+ struct candidate *candidate = &_candidates[candidate_id];
+ candidate->id = candidate_id;
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ candidate->config = nb_config_dup(running_config);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
+ candidate->transaction = NULL;
+
+ return candidate;
+ }
+
+ void delete_candidate(struct candidate *candidate)
+ {
+ _candidates.erase(candidate->id);
+ nb_config_free(candidate->config);
+ if (candidate->transaction)
+ nb_candidate_commit_abort(candidate->transaction);
+ }
+
+ struct candidate *get_candidate(uint32_t candidate_id)
+ {
+ struct candidate *candidate;
+
+ if (_candidates.count(candidate_id) == 0)
+ return NULL;
+
+ return &_candidates[candidate_id];
+ }
+};
+
+static void *grpc_pthread_start(void *arg)
+{
+ unsigned long *port = static_cast<unsigned long *>(arg);
+ NorthboundImpl service;
+ std::stringstream server_address;
+
+ server_address << "0.0.0.0:" << *port;
+
+ grpc::ServerBuilder builder;
+ builder.AddListeningPort(server_address.str(),
+ grpc::InsecureServerCredentials());
+ builder.RegisterService(&service);
+
+ std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
+
+ zlog_notice("gRPC server listening on %s",
+ server_address.str().c_str());
+
+ server->Wait();
+
+ return NULL;
+}
+
+static int frr_grpc_init(unsigned long *port)
+{
+ /* Create a pthread for gRPC since it runs its own event loop. */
+ if (pthread_create(&grpc_pthread, NULL, grpc_pthread_start, port)) {
+ flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s",
+ __func__, safe_strerror(errno));
+ return -1;
+ }
+ pthread_detach(grpc_pthread);
+
+ return 0;
+}
+
+static int frr_grpc_finish(void)
+{
+ // TODO: cancel the gRPC pthreads gracefully.
+
+ return 0;
+}
+
+static int frr_grpc_module_late_init(struct thread_master *tm)
+{
+ static unsigned long port = GRPC_DEFAULT_PORT;
+ const char *args = THIS_MODULE->load_args;
+
+ // Parse port number.
+ if (args) {
+ try {
+ port = std::stoul(args);
+ if (port < 1024)
+ throw std::invalid_argument(
+ "can't use privileged port");
+ if (port > UINT16_MAX)
+ throw std::invalid_argument(
+ "port number is too big");
+ } catch (std::exception &e) {
+ flog_err(EC_LIB_GRPC_INIT,
+ "%s: failed to parse port number: %s",
+ __func__, e.what());
+ goto error;
+ }
+ }
+
+ if (frr_grpc_init(&port) < 0)
+ goto error;
+
+ hook_register(frr_fini, frr_grpc_finish);
+
+ return 0;
+
+error:
+ flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module");
+ return -1;
+}
+
+static int frr_grpc_module_init(void)
+{
+ hook_register(frr_late_init, frr_grpc_module_late_init);
+
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "frr_grpc", .version = FRR_VERSION,
+ .description = "FRR gRPC northbound module",
+ .init = frr_grpc_module_init, )
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 33b6c24782..44a55137f8 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -256,7 +256,11 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
return ret;
}
- candidate = nb_config_dup(running_config);
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ candidate = nb_config_dup(running_config);
+ }
+ pthread_rwlock_unlock(&running_config->lock);
while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
&sr_new_val))
@@ -282,15 +286,15 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
* single event (SR_EV_ENABLED). This means we need to perform
* the full two-phase commit protocol in one go here.
*/
- ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true,
- NULL, NULL);
+ ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, NULL,
+ true, NULL, NULL);
} else {
/*
* Validate the configuration changes and allocate all resources
* required to apply them.
*/
ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO,
- NULL, &transaction);
+ NULL, NULL, &transaction);
}
/* Map northbound return code to sysrepo return code. */
diff --git a/lib/openbsd-tree.c b/lib/openbsd-tree.c
index eadef9902b..ddcc59fa8f 100644
--- a/lib/openbsd-tree.c
+++ b/lib/openbsd-tree.c
@@ -435,7 +435,8 @@ void *_rb_insert(const struct rb_type *t, struct rbt_tree *rbt, void *elm)
}
/* Finds the node with the same key as elm */
-void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key)
+void *_rb_find(const struct rb_type *t, const struct rbt_tree *rbt,
+ const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
@@ -456,7 +457,8 @@ void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key)
}
/* Finds the first node greater than or equal to the search key */
-void *_rb_nfind(const struct rb_type *t, struct rbt_tree *rbt, const void *key)
+void *_rb_nfind(const struct rb_type *t, const struct rbt_tree *rbt,
+ const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
@@ -522,14 +524,14 @@ void *_rb_prev(const struct rb_type *t, void *elm)
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
-void *_rb_root(const struct rb_type *t, struct rbt_tree *rbt)
+void *_rb_root(const struct rb_type *t, const struct rbt_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
return (rbe == NULL ? rbe : rb_e2n(t, rbe));
}
-void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt)
+void *_rb_min(const struct rb_type *t, const struct rbt_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
@@ -542,7 +544,7 @@ void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt)
return (parent == NULL ? NULL : rb_e2n(t, parent));
}
-void *_rb_max(const struct rb_type *t, struct rbt_tree *rbt)
+void *_rb_max(const struct rb_type *t, const struct rbt_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h
index d2f0781333..832a10141e 100644
--- a/lib/openbsd-tree.h
+++ b/lib/openbsd-tree.h
@@ -364,18 +364,18 @@ static inline void _rb_init(struct rbt_tree *rbt)
rbt->rbt_root = NULL;
}
-static inline int _rb_empty(struct rbt_tree *rbt)
+static inline int _rb_empty(const struct rbt_tree *rbt)
{
return (rbt->rbt_root == NULL);
}
void *_rb_insert(const struct rb_type *, struct rbt_tree *, void *);
void *_rb_remove(const struct rb_type *, struct rbt_tree *, void *);
-void *_rb_find(const struct rb_type *, struct rbt_tree *, const void *);
-void *_rb_nfind(const struct rb_type *, struct rbt_tree *, const void *);
-void *_rb_root(const struct rb_type *, struct rbt_tree *);
-void *_rb_min(const struct rb_type *, struct rbt_tree *);
-void *_rb_max(const struct rb_type *, struct rbt_tree *);
+void *_rb_find(const struct rb_type *, const struct rbt_tree *, const void *);
+void *_rb_nfind(const struct rb_type *, const struct rbt_tree *, const void *);
+void *_rb_root(const struct rb_type *, const struct rbt_tree *);
+void *_rb_min(const struct rb_type *, const struct rbt_tree *);
+void *_rb_max(const struct rb_type *, const struct rbt_tree *);
void *_rb_next(const struct rb_type *, void *);
void *_rb_prev(const struct rb_type *, void *);
void *_rb_left(const struct rb_type *, void *);
@@ -401,56 +401,58 @@ int _rb_check(const struct rb_type *, void *, unsigned long);
__attribute__((__unused__)) static inline struct _type \
*_name##_RB_INSERT(struct _name *head, struct _type *elm) \
{ \
- return (struct _type *)_rb_insert( \
- _name##_RB_TYPE, &head->rbh_root, elm); \
+ return (struct _type *)_rb_insert(_name##_RB_TYPE, \
+ &head->rbh_root, elm); \
} \
\
__attribute__((__unused__)) static inline struct _type \
*_name##_RB_REMOVE(struct _name *head, struct _type *elm) \
{ \
- return (struct _type *)_rb_remove( \
- _name##_RB_TYPE, &head->rbh_root, elm); \
+ return (struct _type *)_rb_remove(_name##_RB_TYPE, \
+ &head->rbh_root, elm); \
} \
\
__attribute__((__unused__)) static inline struct _type \
- *_name##_RB_FIND(struct _name *head, const struct _type *key) \
+ *_name##_RB_FIND(const struct _name *head, \
+ const struct _type *key) \
{ \
- return (struct _type *)_rb_find( \
- _name##_RB_TYPE, &head->rbh_root, key); \
+ return (struct _type *)_rb_find(_name##_RB_TYPE, \
+ &head->rbh_root, key); \
} \
\
__attribute__((__unused__)) static inline struct _type \
- *_name##_RB_NFIND(struct _name *head, const struct _type *key) \
+ *_name##_RB_NFIND(const struct _name *head, \
+ const struct _type *key) \
{ \
- return (struct _type *)_rb_nfind( \
- _name##_RB_TYPE, &head->rbh_root, key); \
+ return (struct _type *)_rb_nfind(_name##_RB_TYPE, \
+ &head->rbh_root, key); \
} \
\
__attribute__((__unused__)) static inline struct _type \
- *_name##_RB_ROOT(struct _name *head) \
+ *_name##_RB_ROOT(const struct _name *head) \
{ \
- return (struct _type *)_rb_root( \
- _name##_RB_TYPE, &head->rbh_root); \
+ return (struct _type *)_rb_root(_name##_RB_TYPE, \
+ &head->rbh_root); \
} \
\
__attribute__((__unused__)) static inline int _name##_RB_EMPTY( \
- struct _name *head) \
+ const struct _name *head) \
{ \
return _rb_empty(&head->rbh_root); \
} \
\
__attribute__((__unused__)) static inline struct _type \
- *_name##_RB_MIN(struct _name *head) \
+ *_name##_RB_MIN(const struct _name *head) \
{ \
- return (struct _type *)_rb_min( \
- _name##_RB_TYPE, &head->rbh_root); \
+ return (struct _type *)_rb_min(_name##_RB_TYPE, \
+ &head->rbh_root); \
} \
\
__attribute__((__unused__)) static inline struct _type \
- *_name##_RB_MAX(struct _name *head) \
+ *_name##_RB_MAX(const struct _name *head) \
{ \
- return (struct _type *)_rb_max( \
- _name##_RB_TYPE, &head->rbh_root); \
+ return (struct _type *)_rb_max(_name##_RB_TYPE, \
+ &head->rbh_root); \
} \
\
__attribute__((__unused__)) static inline struct _type \
diff --git a/lib/prefix.c b/lib/prefix.c
index 6b91969218..d2a4c3a432 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1543,7 +1543,7 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size)
return ptr;
}
-unsigned prefix_hash_key(void *pp)
+unsigned prefix_hash_key(const void *pp)
{
struct prefix copy;
diff --git a/lib/prefix.h b/lib/prefix.h
index d3c387e102..d57b43dac6 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -466,7 +466,7 @@ extern int is_zero_mac(struct ethaddr *mac);
extern int prefix_str2mac(const char *str, struct ethaddr *mac);
extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size);
-extern unsigned prefix_hash_key(void *pp);
+extern unsigned prefix_hash_key(const void *pp);
extern int str_to_esi(const char *str, esi_t *esi);
extern char *esi_to_str(const esi_t *esi, char *buf, int size);
diff --git a/lib/privs.c b/lib/privs.c
index a19707b1c9..a3314c6c3c 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -917,7 +917,7 @@ void zprivs_init(struct zebra_privs_t *zprivs)
zprivs->user, zprivs->vty_group);
exit(1);
}
- if (i >= ngroups && ngroups < (int)ZEBRA_NUM_OF(groups)) {
+ if (i >= ngroups && ngroups < (int)array_size(groups)) {
groups[i] = zprivs_state.vtygrp;
}
}
diff --git a/lib/qobj.c b/lib/qobj.c
index 811645f3c3..3e3860a96a 100644
--- a/lib/qobj.c
+++ b/lib/qobj.c
@@ -27,21 +27,27 @@
#include "qobj.h"
#include "jhash.h"
-static pthread_rwlock_t nodes_lock;
-static struct hash *nodes = NULL;
-
-static unsigned int qobj_key(void *data)
+static uint32_t qobj_hash(const struct qobj_node *node)
{
- struct qobj_node *node = data;
- return (unsigned int)node->nid;
+ return (uint32_t)node->nid;
}
-static bool qobj_cmp(const void *a, const void *b)
+static int qobj_cmp(const struct qobj_node *na, const struct qobj_node *nb)
{
- const struct qobj_node *na = a, *nb = b;
- return na->nid == nb->nid;
+ if (na->nid < nb->nid)
+ return -1;
+ if (na->nid > nb->nid)
+ return 1;
+ return 0;
}
+DECLARE_HASH(qobj_nodes, struct qobj_node, nodehash,
+ qobj_cmp, qobj_hash)
+
+static pthread_rwlock_t nodes_lock;
+static struct qobj_nodes_head nodes = { };
+
+
void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type)
{
node->type = type;
@@ -49,15 +55,15 @@ void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type)
do {
node->nid = (uint64_t)random();
node->nid ^= (uint64_t)random() << 32;
- } while (!node->nid
- || hash_get(nodes, node, hash_alloc_intern) != node);
+ } while (!node->nid || qobj_nodes_find(&nodes, node));
+ qobj_nodes_add(&nodes, node);
pthread_rwlock_unlock(&nodes_lock);
}
void qobj_unreg(struct qobj_node *node)
{
pthread_rwlock_wrlock(&nodes_lock);
- hash_release(nodes, node);
+ qobj_nodes_del(&nodes, node);
pthread_rwlock_unlock(&nodes_lock);
}
@@ -65,7 +71,7 @@ struct qobj_node *qobj_get(uint64_t id)
{
struct qobj_node dummy = {.nid = id}, *rv;
pthread_rwlock_rdlock(&nodes_lock);
- rv = hash_lookup(nodes, &dummy);
+ rv = qobj_nodes_find(&nodes, &dummy);
pthread_rwlock_unlock(&nodes_lock);
return rv;
}
@@ -77,7 +83,7 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type)
void *rv;
pthread_rwlock_rdlock(&nodes_lock);
- node = hash_lookup(nodes, &dummy);
+ node = qobj_nodes_find(&nodes, &dummy);
/* note: we explicitly hold the lock until after we have checked the
* type.
@@ -96,16 +102,14 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type)
void qobj_init(void)
{
- if (!nodes) {
- pthread_rwlock_init(&nodes_lock, NULL);
- nodes = hash_create_size(16, qobj_key, qobj_cmp, "QOBJ Hash");
- }
+ pthread_rwlock_init(&nodes_lock, NULL);
+ qobj_nodes_init(&nodes);
}
void qobj_finish(void)
{
- hash_clean(nodes, NULL);
- hash_free(nodes);
- nodes = NULL;
+ struct qobj_node *node;
+ while ((node = qobj_nodes_pop(&nodes)))
+ qobj_nodes_del(&nodes, node);
pthread_rwlock_destroy(&nodes_lock);
}
diff --git a/lib/qobj.h b/lib/qobj.h
index d63988cbab..415eae02ef 100644
--- a/lib/qobj.h
+++ b/lib/qobj.h
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <stddef.h>
+#include "typesafe.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -69,6 +71,8 @@ struct qobj_nodetype_capnp {
};
#endif
+#include "typesafe.h"
+
/* each different kind of object will have a global variable of this type,
* which can be used by various other pieces to store type-related bits.
* type equality can be tested as pointer equality. (cf. QOBJ_GET_TYPESAFE)
@@ -79,9 +83,12 @@ struct qobj_nodetype {
RESERVED_SPACE_STRUCT(qobj_nodetype_capnp, capnp, 256)
};
+PREDECL_HASH(qobj_nodes)
+
/* anchor to be embedded somewhere in the object's struct */
struct qobj_node {
uint64_t nid;
+ struct qobj_nodes_item nodehash;
struct qobj_nodetype *type;
};
diff --git a/lib/route_types.txt b/lib/route_types.txt
index c5eff44ca7..59f3a91cf0 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -83,6 +83,7 @@ ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP"
ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric"
+ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP"
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
@@ -110,4 +111,5 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"
ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)"
+ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)"
ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol"
diff --git a/lib/routemap.c b/lib/routemap.c
index 4898a8d0fa..3542994e65 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -616,14 +616,14 @@ struct route_map_list {
void (*add_hook)(const char *);
void (*delete_hook)(const char *);
- void (*event_hook)(route_map_event_t, const char *);
+ void (*event_hook)(const char *);
};
/* Master list of route map. */
static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
struct hash *route_map_master_hash = NULL;
-static unsigned int route_map_hash_key_make(void *p)
+static unsigned int route_map_hash_key_make(const void *p)
{
const struct route_map *map = p;
return string_hash_make(map->name);
@@ -673,7 +673,7 @@ struct route_map_dep {
/* Hashes maintaining dependency between various sublists used by route maps */
struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX];
-static unsigned int route_map_dep_hash_make_key(void *p);
+static unsigned int route_map_dep_hash_make_key(const void *p);
static void route_map_clear_all_references(char *rmap_name);
static void route_map_rule_delete(struct route_map_rule_list *,
struct route_map_rule *);
@@ -902,10 +902,12 @@ static const char *route_map_type_str(enum route_map_type type)
case RMAP_DENY:
return "deny";
break;
- default:
+ case RMAP_ANY:
return "";
break;
}
+
+ return "";
}
static int route_map_empty(struct route_map *map)
@@ -1077,8 +1079,7 @@ static void route_map_index_delete(struct route_map_index *index, int notify)
/* Execute event hook. */
if (route_map_master.event_hook && notify) {
- (*route_map_master.event_hook)(RMAP_EVENT_INDEX_DELETED,
- index->map->name);
+ (*route_map_master.event_hook)(index->map->name);
route_map_notify_dependencies(index->map->name,
RMAP_EVENT_CALL_ADDED);
}
@@ -1137,8 +1138,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
/* Execute event hook. */
if (route_map_master.event_hook) {
- (*route_map_master.event_hook)(RMAP_EVENT_INDEX_ADDED,
- map->name);
+ (*route_map_master.event_hook)(map->name);
route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED);
}
return index;
@@ -1289,7 +1289,6 @@ int route_map_add_match(struct route_map_index *index, const char *match_name,
struct route_map_rule *next;
struct route_map_rule_cmd *cmd;
void *compile;
- int replaced = 0;
/* First lookup rule for add match statement. */
cmd = route_map_lookup_match(match_name);
@@ -1308,8 +1307,17 @@ int route_map_add_match(struct route_map_index *index, const char *match_name,
for (rule = index->match_list.head; rule; rule = next) {
next = rule->next;
if (rule->cmd == cmd) {
+ /* If the configured route-map match rule is exactly
+ * the same as the existing configuration then,
+ * ignore the duplicate configuration.
+ */
+ if (strcmp(match_arg, rule->rule_str) == 0) {
+ if (cmd->func_free)
+ (*cmd->func_free)(compile);
+ return RMAP_COMPILE_SUCCESS;
+ }
+
route_map_rule_delete(&index->match_list, rule);
- replaced = 1;
}
}
@@ -1327,10 +1335,7 @@ int route_map_add_match(struct route_map_index *index, const char *match_name,
/* Execute event hook. */
if (route_map_master.event_hook) {
- (*route_map_master.event_hook)(
- replaced ? RMAP_EVENT_MATCH_REPLACED
- : RMAP_EVENT_MATCH_ADDED,
- index->map->name);
+ (*route_map_master.event_hook)(index->map->name);
route_map_notify_dependencies(index->map->name,
RMAP_EVENT_CALL_ADDED);
}
@@ -1355,9 +1360,7 @@ int route_map_delete_match(struct route_map_index *index,
route_map_rule_delete(&index->match_list, rule);
/* Execute event hook. */
if (route_map_master.event_hook) {
- (*route_map_master.event_hook)(
- RMAP_EVENT_MATCH_DELETED,
- index->map->name);
+ (*route_map_master.event_hook)(index->map->name);
route_map_notify_dependencies(
index->map->name,
RMAP_EVENT_CALL_ADDED);
@@ -1376,7 +1379,6 @@ int route_map_add_set(struct route_map_index *index, const char *set_name,
struct route_map_rule *next;
struct route_map_rule_cmd *cmd;
void *compile;
- int replaced = 0;
cmd = route_map_lookup_set(set_name);
if (cmd == NULL)
@@ -1395,10 +1397,8 @@ int route_map_add_set(struct route_map_index *index, const char *set_name,
route_map_index. */
for (rule = index->set_list.head; rule; rule = next) {
next = rule->next;
- if (rule->cmd == cmd) {
+ if (rule->cmd == cmd)
route_map_rule_delete(&index->set_list, rule);
- replaced = 1;
- }
}
/* Add new route map match rule. */
@@ -1415,10 +1415,7 @@ int route_map_add_set(struct route_map_index *index, const char *set_name,
/* Execute event hook. */
if (route_map_master.event_hook) {
- (*route_map_master.event_hook)(replaced
- ? RMAP_EVENT_SET_REPLACED
- : RMAP_EVENT_SET_ADDED,
- index->map->name);
+ (*route_map_master.event_hook)(index->map->name);
route_map_notify_dependencies(index->map->name,
RMAP_EVENT_CALL_ADDED);
}
@@ -1442,9 +1439,7 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name,
route_map_rule_delete(&index->set_list, rule);
/* Execute event hook. */
if (route_map_master.event_hook) {
- (*route_map_master.event_hook)(
- RMAP_EVENT_SET_DELETED,
- index->map->name);
+ (*route_map_master.event_hook)(index->map->name);
route_map_notify_dependencies(
index->map->name,
RMAP_EVENT_CALL_ADDED);
@@ -1641,7 +1636,7 @@ void route_map_delete_hook(void (*func)(const char *))
route_map_master.delete_hook = func;
}
-void route_map_event_hook(void (*func)(route_map_event_t, const char *))
+void route_map_event_hook(void (*func)(const char *name))
{
route_map_master.event_hook = func;
}
@@ -1711,7 +1706,7 @@ static void *route_map_name_hash_alloc(void *p)
return ((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (const char *)p));
}
-static unsigned int route_map_dep_hash_make_key(void *p)
+static unsigned int route_map_dep_hash_make_key(const void *p)
{
return (string_hash_make((char *)p));
}
@@ -1785,7 +1780,14 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
dep = NULL;
}
break;
- default:
+ case RMAP_EVENT_SET_ADDED:
+ case RMAP_EVENT_SET_DELETED:
+ case RMAP_EVENT_SET_REPLACED:
+ case RMAP_EVENT_MATCH_ADDED:
+ case RMAP_EVENT_MATCH_DELETED:
+ case RMAP_EVENT_MATCH_REPLACED:
+ case RMAP_EVENT_INDEX_ADDED:
+ case RMAP_EVENT_INDEX_DELETED:
break;
}
@@ -1828,13 +1830,26 @@ static struct hash *route_map_get_dep_hash(route_map_event_t event)
break;
case RMAP_EVENT_CALL_ADDED:
case RMAP_EVENT_CALL_DELETED:
+ case RMAP_EVENT_MATCH_ADDED:
+ case RMAP_EVENT_MATCH_DELETED:
upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP];
break;
case RMAP_EVENT_FILTER_ADDED:
case RMAP_EVENT_FILTER_DELETED:
upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER];
break;
- default:
+ /*
+ * Should we actually be ignoring these?
+ * I am not sure but at this point in time, let
+ * us get them into this switch and we can peel
+ * them into the appropriate place in the future
+ */
+ case RMAP_EVENT_SET_ADDED:
+ case RMAP_EVENT_SET_DELETED:
+ case RMAP_EVENT_SET_REPLACED:
+ case RMAP_EVENT_MATCH_REPLACED:
+ case RMAP_EVENT_INDEX_ADDED:
+ case RMAP_EVENT_INDEX_DELETED:
upd8_hash = NULL;
break;
}
@@ -1844,13 +1859,12 @@ static struct hash *route_map_get_dep_hash(route_map_event_t event)
static void route_map_process_dependency(struct hash_bucket *bucket, void *data)
{
char *rmap_name = (char *)bucket->data;
- route_map_event_t type = (route_map_event_t)(ptrdiff_t)data;
if (rmap_debug)
zlog_debug("%s: Notifying %s of dependency",
__FUNCTION__, rmap_name);
if (route_map_master.event_hook)
- (*route_map_master.event_hook)(type, rmap_name);
+ (*route_map_master.event_hook)(rmap_name);
}
void route_map_upd8_dependency(route_map_event_t type, const char *arg,
@@ -2803,6 +2817,13 @@ DEFUN (rmap_call,
assert(index);
+ /* If "call" is invoked with the same route-map name as
+ * the one previously configured then, ignore the duplicate
+ * configuration.
+ */
+ if (index->nextrm && (strcmp(index->nextrm, rmap) == 0))
+ return CMD_SUCCESS;
+
if (index->nextrm) {
route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED,
index->nextrm, index->map->name);
diff --git a/lib/routemap.h b/lib/routemap.h
index e43e74a633..9969936a6b 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -238,7 +238,15 @@ extern route_map_result_t route_map_apply(struct route_map *map,
extern void route_map_add_hook(void (*func)(const char *));
extern void route_map_delete_hook(void (*func)(const char *));
-extern void route_map_event_hook(void (*func)(route_map_event_t, const char *));
+
+/*
+ * This is the callback for when something has changed about a
+ * route-map. The interested parties can register to receive
+ * this data.
+ *
+ * name - Is the name of the changed route-map
+ */
+extern void route_map_event_hook(void (*func)(const char *name));
extern int route_map_mark_updated(const char *name);
extern void route_map_walk_update_list(void (*update_fn)(char *name));
extern void route_map_upd8_dependency(route_map_event_t type, const char *arg,
diff --git a/lib/seqlock.c b/lib/seqlock.c
new file mode 100644
index 0000000000..223d14952c
--- /dev/null
+++ b/lib/seqlock.c
@@ -0,0 +1,167 @@
+/*
+ * "Sequence" lock primitive
+ *
+ * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "seqlock.h"
+
+#ifdef HAVE_SYNC_LINUX_FUTEX
+/* Linux-specific - sys_futex() */
+#include <sys/syscall.h>
+#include <linux/futex.h>
+
+static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
+ void *addr2, int val3)
+{
+ return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
+}
+
+#define wait_once(sqlo, val) \
+ sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_poke(sqlo) \
+ sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+
+#elif defined(HAVE_SYNC_OPENBSD_FUTEX)
+/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */
+#include <sys/syscall.h>
+#include <sys/futex.h>
+
+#define wait_once(sqlo, val) \
+ futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_poke(sqlo) \
+ futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+
+#elif defined(HAVE_SYNC_UMTX_OP)
+/* FreeBSD-specific: umtx_op() */
+#include <sys/umtx.h>
+
+#define wait_once(sqlo, val) \
+ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
+#define wait_poke(sqlo) \
+ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
+
+#else
+/* generic version. used on *BSD, Solaris and OSX.
+ */
+
+#define wait_init(sqlo) do { \
+ pthread_mutex_init(&sqlo->lock, NULL); \
+ pthread_cond_init(&sqlo->wake, NULL); \
+ } while (0)
+#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
+#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
+#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
+#define wait_poke(sqlo) do { \
+ pthread_mutex_lock(&sqlo->lock); \
+ pthread_cond_broadcast(&sqlo->wake); \
+ pthread_mutex_unlock(&sqlo->lock); \
+ } while (0)
+
+#endif
+
+#ifndef wait_init
+#define wait_init(sqlo) /**/
+#define wait_prep(sqlo) /**/
+#define wait_done(sqlo) /**/
+#endif /* wait_init */
+
+
+void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
+{
+ seqlock_val_t cur, cal;
+
+ seqlock_assert_valid(val);
+
+ wait_prep(sqlo);
+ while (1) {
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
+ if (!(cur & 1))
+ break;
+ cal = cur - val - 1;
+ assert(cal < 0x40000000 || cal > 0xc0000000);
+ if (cal < 0x80000000)
+ break;
+
+ wait_once(sqlo, cur);
+ }
+ wait_done(sqlo);
+}
+
+bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
+{
+ seqlock_val_t cur;
+
+ seqlock_assert_valid(val);
+
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
+ if (!(cur & 1))
+ return 1;
+ cur -= val;
+ assert(cur < 0x40000000 || cur > 0xc0000000);
+ return cur < 0x80000000;
+}
+
+void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
+{
+ seqlock_assert_valid(val);
+
+ atomic_store_explicit(&sqlo->pos, val, memory_order_release);
+ wait_poke(sqlo);
+}
+
+void seqlock_release(struct seqlock *sqlo)
+{
+ atomic_store_explicit(&sqlo->pos, 0, memory_order_release);
+ wait_poke(sqlo);
+}
+
+void seqlock_init(struct seqlock *sqlo)
+{
+ sqlo->pos = 0;
+ wait_init(sqlo);
+}
+
+
+seqlock_val_t seqlock_cur(struct seqlock *sqlo)
+{
+ return atomic_load_explicit(&sqlo->pos, memory_order_acquire);
+}
+
+seqlock_val_t seqlock_bump(struct seqlock *sqlo)
+{
+ seqlock_val_t val;
+
+ val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release);
+ wait_poke(sqlo);
+ return val;
+}
diff --git a/lib/seqlock.h b/lib/seqlock.h
new file mode 100644
index 0000000000..eef05a4307
--- /dev/null
+++ b/lib/seqlock.h
@@ -0,0 +1,106 @@
+/*
+ * "Sequence" lock primitive
+ *
+ * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#ifndef _SEQLOCK_H
+#define _SEQLOCK_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <pthread.h>
+#include "frratomic.h"
+
+/*
+ * this locking primitive is intended to use in a 1:N setup.
+ *
+ * - one "counter" seqlock issuing increasing numbers
+ * - multiple seqlock users hold references on these numbers
+ *
+ * this is intended for implementing RCU reference-holding. There is one
+ * global counter, with threads locking a seqlock whenever they take a
+ * reference. A seqlock can also be idle/unlocked.
+ *
+ * The "counter" seqlock will always stay locked; the RCU cleanup thread
+ * continuously counts it up, waiting for threads to release or progress to a
+ * sequence number further ahead. If all threads are > N, references dropped
+ * in N can be free'd.
+ *
+ * generally, the lock function is:
+ *
+ * Thread-A Thread-B
+ *
+ * seqlock_acquire(a)
+ * | running seqlock_wait(b) -- a <= b
+ * seqlock_release() | blocked
+ * OR: seqlock_acquire(a') | -- a' > b
+ * (resumes)
+ */
+
+/* use sequentially increasing "ticket numbers". lowest bit will always
+ * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. )
+ */
+typedef _Atomic uint32_t seqlock_ctr_t;
+typedef uint32_t seqlock_val_t;
+#define seqlock_assert_valid(val) assert(val & 1)
+
+
+struct seqlock {
+/* always used */
+ seqlock_ctr_t pos;
+/* used when futexes not available: (i.e. non-linux) */
+ pthread_mutex_t lock;
+ pthread_cond_t wake;
+};
+
+
+/* sqlo = 0 - init state: not held */
+extern void seqlock_init(struct seqlock *sqlo);
+
+
+/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */
+extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val);
+extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val);
+
+static inline bool seqlock_held(struct seqlock *sqlo)
+{
+ return !!atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+}
+
+/* sqlo - get seqlock position -- for the "counter" seqlock */
+extern seqlock_val_t seqlock_cur(struct seqlock *sqlo);
+/* sqlo++ - note: like x++, returns previous value, before bumping */
+extern seqlock_val_t seqlock_bump(struct seqlock *sqlo);
+
+
+/* sqlo = val - can be used on held seqlock. */
+extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val);
+/* sqlo = ref - standard pattern: acquire relative to other seqlock */
+static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref)
+{
+ seqlock_acquire_val(sqlo, seqlock_cur(ref));
+}
+
+/* sqlo = 0 - set seqlock position to 0, marking as non-held */
+extern void seqlock_release(struct seqlock *sqlo);
+/* release should normally be followed by a bump on the "counter", if
+ * anything other than reading RCU items was done
+ */
+
+#endif /* _SEQLOCK_H */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index af4f41f37c..8fa9a3fad9 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -163,7 +163,7 @@ int sockunion_accept(int sock, union sockunion *su)
}
/* Return sizeof union sockunion. */
-static int sockunion_sizeof(const union sockunion *su)
+int sockunion_sizeof(const union sockunion *su)
{
int ret;
@@ -366,21 +366,6 @@ int sockopt_cork(int sock, int onoff)
return 0;
}
-int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *cap)
-{
-#ifdef SO_MARK
- int ret;
-
- frr_elevate_privs(cap) {
- ret = setsockopt(sock, SOL_SOCKET, SO_MARK, &mark,
- sizeof(mark));
- }
- return ret;
-#else
- return 0;
-#endif
-}
-
int sockopt_minttl(int family, int sock, int minttl)
{
#ifdef IP_MINTTL
@@ -472,7 +457,7 @@ unsigned int sockunion_hash(const union sockunion *su)
return jhash_1word(su->sin.sin_addr.s_addr, 0);
case AF_INET6:
return jhash2(su->sin6.sin6_addr.s6_addr32,
- ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0);
+ array_size(su->sin6.sin6_addr.s6_addr32), 0);
}
return 0;
}
diff --git a/lib/sockunion.h b/lib/sockunion.h
index d7d26ba85a..7091c1b5e7 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -83,6 +83,7 @@ extern void sockunion_set(union sockunion *, int family, const uint8_t *addr,
extern union sockunion *sockunion_str2su(const char *str);
extern int sockunion_accept(int sock, union sockunion *);
+extern int sockunion_sizeof(const union sockunion *su);
extern int sockunion_stream_socket(union sockunion *);
extern int sockopt_reuseaddr(int);
extern int sockopt_reuseport(int);
@@ -92,7 +93,6 @@ extern int sockunion_bind(int sock, union sockunion *, unsigned short,
extern int sockopt_ttl(int family, int sock, int ttl);
extern int sockopt_minttl(int family, int sock, int minttl);
extern int sockopt_cork(int sock, int onoff);
-extern int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *);
extern int sockunion_socket(const union sockunion *su);
extern const char *inet_sutop(const union sockunion *su, char *str);
extern enum connect_result sockunion_connect(int fd, const union sockunion *su,
diff --git a/lib/subdir.am b/lib/subdir.am
index 3b14be4676..4897f5e8e5 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -3,10 +3,11 @@
#
lib_LTLIBRARIES += lib/libfrr.la
lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
-lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS)
+lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB)
lib_libfrr_la_SOURCES = \
lib/agg_table.c \
+ lib/atomlist.c \
lib/bfd.c \
lib/buffer.c \
lib/checksum.c \
@@ -20,6 +21,7 @@ lib_libfrr_la_SOURCES = \
lib/distribute.c \
lib/ferr.c \
lib/filter.c \
+ lib/frrlua.c \
lib/frr_pthread.c \
lib/frrstr.c \
lib/getopt.c \
@@ -65,6 +67,7 @@ lib_libfrr_la_SOURCES = \
lib/ringbuf.c \
lib/routemap.c \
lib/sbuf.c \
+ lib/seqlock.c \
lib/sha256.c \
lib/sigevent.c \
lib/skiplist.c \
@@ -79,6 +82,8 @@ lib_libfrr_la_SOURCES = \
lib/table.c \
lib/termtable.c \
lib/thread.c \
+ lib/typerb.c \
+ lib/typesafe.c \
lib/vector.c \
lib/vrf.c \
lib/vty.c \
@@ -89,7 +94,6 @@ lib_libfrr_la_SOURCES = \
lib/yang_wrappers.c \
lib/zclient.c \
lib/logicalrouter.c \
- lib/lua.c \
# end
nodist_lib_libfrr_la_SOURCES = \
@@ -130,6 +134,7 @@ lib/northbound_cli.lo: lib/northbound_cli_clippy.c
pkginclude_HEADERS += \
lib/agg_table.h \
+ lib/atomlist.h \
lib/bfd.h \
lib/bitfield.h \
lib/buffer.h \
@@ -144,9 +149,9 @@ pkginclude_HEADERS += \
lib/debug.h \
lib/distribute.h \
lib/ferr.h \
- lib/fifo.h \
lib/filter.h \
lib/freebsd-queue.h \
+ lib/frrlua.h \
lib/frr_pthread.h \
lib/frratomic.h \
lib/frrstr.h \
@@ -193,6 +198,7 @@ pkginclude_HEADERS += \
lib/ringbuf.h \
lib/routemap.h \
lib/sbuf.h \
+ lib/seqlock.h \
lib/sha256.h \
lib/sigevent.h \
lib/skiplist.h \
@@ -206,6 +212,8 @@ pkginclude_HEADERS += \
lib/table.h \
lib/termtable.h \
lib/thread.h \
+ lib/typerb.h \
+ lib/typesafe.h \
lib/vector.h \
lib/vlan.h \
lib/vrf.h \
@@ -221,7 +229,6 @@ pkginclude_HEADERS += \
lib/zclient.h \
lib/zebra.h \
lib/logicalrouter.h \
- lib/lua.h \
lib/pbr.h \
# end
@@ -303,6 +310,18 @@ lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS)
lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c
#
+# gRPC northbound plugin
+#
+if GRPC
+module_LTLIBRARIES += lib/grpc.la
+endif
+
+lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS)
+lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS)
+lib_grpc_la_SOURCES = lib/northbound_grpc.cpp
+
+#
# CLI utilities
#
noinst_PROGRAMS += \
@@ -346,7 +365,7 @@ am__v_CLIPPY_1 =
CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py
-SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h
+SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h .pb.cc .grpc.pb.cc
.c_clippy.c:
@{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || \
$(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; }
diff --git a/lib/table.c b/lib/table.c
index edba7f1932..728615c776 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -33,12 +33,14 @@ DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node")
static void route_table_free(struct route_table *);
-static bool route_table_hash_cmp(const void *a, const void *b)
+static int route_table_hash_cmp(const struct route_node *a,
+ const struct route_node *b)
{
- const struct prefix *pa = a, *pb = b;
- return prefix_cmp(pa, pb) == 0;
+ return prefix_cmp(&a->p, &b->p);
}
+DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp,
+ prefix_hash_key)
/*
* route_table_init_with_delegate
*/
@@ -49,8 +51,7 @@ route_table_init_with_delegate(route_table_delegate_t *delegate)
rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table));
rt->delegate = delegate;
- rt->hash = hash_create(prefix_hash_key, route_table_hash_cmp,
- "route table hash");
+ rn_hash_node_init(&rt->hash);
return rt;
}
@@ -69,15 +70,14 @@ static struct route_node *route_node_new(struct route_table *table)
static struct route_node *route_node_set(struct route_table *table,
const struct prefix *prefix)
{
- struct route_node *node, *inserted;
+ struct route_node *node;
node = route_node_new(table);
prefix_copy(&node->p, prefix);
node->table = table;
- inserted = hash_get(node->table->hash, node, hash_alloc_intern);
- assert(inserted == node);
+ rn_hash_node_add(&node->table->hash, node);
return node;
}
@@ -99,9 +99,6 @@ static void route_table_free(struct route_table *rt)
if (rt == NULL)
return;
- hash_clean(rt->hash, NULL);
- hash_free(rt->hash);
-
node = rt->top;
/* Bulk deletion of nodes remaining in this table. This function is not
@@ -123,6 +120,7 @@ static void route_table_free(struct route_table *rt)
tmp_node->table->count--;
tmp_node->lock = 0; /* to cause assert if unlocked after this */
+ rn_hash_node_del(&rt->hash, tmp_node);
route_node_free(rt, tmp_node);
if (node != NULL) {
@@ -137,6 +135,7 @@ static void route_table_free(struct route_table *rt)
assert(rt->count == 0);
+ rn_hash_node_fini(&rt->hash);
XFREE(MTYPE_ROUTE_TABLE, rt);
return;
}
@@ -192,7 +191,7 @@ static void set_link(struct route_node *node, struct route_node *new)
}
/* Find matched prefix. */
-struct route_node *route_node_match(const struct route_table *table,
+struct route_node *route_node_match(struct route_table *table,
union prefixconstptr pu)
{
const struct prefix *p = pu.p;
@@ -222,7 +221,7 @@ struct route_node *route_node_match(const struct route_table *table,
return NULL;
}
-struct route_node *route_node_match_ipv4(const struct route_table *table,
+struct route_node *route_node_match_ipv4(struct route_table *table,
const struct in_addr *addr)
{
struct prefix_ipv4 p;
@@ -235,7 +234,7 @@ struct route_node *route_node_match_ipv4(const struct route_table *table,
return route_node_match(table, (struct prefix *)&p);
}
-struct route_node *route_node_match_ipv6(const struct route_table *table,
+struct route_node *route_node_match_ipv6(struct route_table *table,
const struct in6_addr *addr)
{
struct prefix_ipv6 p;
@@ -245,49 +244,50 @@ struct route_node *route_node_match_ipv6(const struct route_table *table,
p.prefixlen = IPV6_MAX_PREFIXLEN;
p.prefix = *addr;
- return route_node_match(table, (struct prefix *)&p);
+ return route_node_match(table, &p);
}
/* Lookup same prefix node. Return NULL when we can't find route. */
-struct route_node *route_node_lookup(const struct route_table *table,
+struct route_node *route_node_lookup(struct route_table *table,
union prefixconstptr pu)
{
- struct prefix p;
- struct route_node *node;
- prefix_copy(&p, pu.p);
- apply_mask(&p);
+ struct route_node rn, *node;
+ prefix_copy(&rn.p, pu.p);
+ apply_mask(&rn.p);
- node = hash_get(table->hash, (void *)&p, NULL);
+ node = rn_hash_node_find(&table->hash, &rn);
return (node && node->info) ? route_lock_node(node) : NULL;
}
/* Lookup same prefix node. Return NULL when we can't find route. */
-struct route_node *route_node_lookup_maynull(const struct route_table *table,
+struct route_node *route_node_lookup_maynull(struct route_table *table,
union prefixconstptr pu)
{
- struct prefix p;
- struct route_node *node;
- prefix_copy(&p, pu.p);
- apply_mask(&p);
+ struct route_node rn, *node;
+ prefix_copy(&rn.p, pu.p);
+ apply_mask(&rn.p);
- node = hash_get(table->hash, (void *)&p, NULL);
+ node = rn_hash_node_find(&table->hash, &rn);
return node ? route_lock_node(node) : NULL;
}
/* Add node to routing table. */
-struct route_node *route_node_get(struct route_table *const table,
+struct route_node *route_node_get(struct route_table *table,
union prefixconstptr pu)
{
- const struct prefix *p = pu.p;
+ struct route_node search;
+ struct prefix *p = &search.p;
+
+ prefix_copy(p, pu.p);
+ apply_mask(p);
+
struct route_node *new;
struct route_node *node;
struct route_node *match;
- struct route_node *inserted;
uint16_t prefixlen = p->prefixlen;
const uint8_t *prefix = &p->u.prefix;
- apply_mask((struct prefix *)p);
- node = hash_get(table->hash, (void *)p, NULL);
+ node = rn_hash_node_find(&table->hash, &search);
if (node && node->info)
return route_lock_node(node);
@@ -314,8 +314,7 @@ struct route_node *route_node_get(struct route_table *const table,
new->p.family = p->family;
new->table = table;
set_link(new, node);
- inserted = hash_get(node->table->hash, new, hash_alloc_intern);
- assert(inserted == new);
+ rn_hash_node_add(&table->hash, new);
if (match)
set_link(match, new);
@@ -367,7 +366,7 @@ void route_node_delete(struct route_node *node)
node->table->count--;
- hash_release(node->table->hash, node);
+ rn_hash_node_del(&node->table->hash, node);
/* WARNING: FRAGILE CODE!
* route_node_free may have the side effect of free'ing the entire
@@ -472,7 +471,7 @@ struct route_node *route_next_until(struct route_node *node,
return NULL;
}
-unsigned long route_table_count(const struct route_table *table)
+unsigned long route_table_count(struct route_table *table)
{
return table->count;
}
@@ -607,7 +606,7 @@ static struct route_node *route_get_subtree_next(struct route_node *node)
* @see route_table_get_next
*/
static struct route_node *
-route_table_get_next_internal(const struct route_table *table,
+route_table_get_next_internal(struct route_table *table,
const struct prefix *p)
{
struct route_node *node, *tmp_node;
@@ -708,7 +707,7 @@ route_table_get_next_internal(const struct route_table *table,
* Find the node that occurs after the given prefix in order of
* iteration.
*/
-struct route_node *route_table_get_next(const struct route_table *table,
+struct route_node *route_table_get_next(struct route_table *table,
union prefixconstptr pu)
{
const struct prefix *p = pu.p;
diff --git a/lib/table.h b/lib/table.h
index ce578e795c..14be7ab656 100644
--- a/lib/table.h
+++ b/lib/table.h
@@ -25,6 +25,7 @@
#include "memory.h"
#include "hash.h"
#include "prefix.h"
+#include "typesafe.h"
#ifdef __cplusplus
extern "C" {
@@ -59,10 +60,12 @@ struct route_table_delegate_t_ {
route_table_destroy_node_func_t destroy_node;
};
+PREDECL_HASH(rn_hash_node)
+
/* Routing table top structure. */
struct route_table {
struct route_node *top;
- struct hash *hash;
+ struct rn_hash_node_head hash;
/*
* Delegate that performs certain functions for this table.
@@ -129,6 +132,7 @@ struct route_table {
/* Lock of this radix */ \
unsigned int table_rdonly(lock); \
\
+ struct rn_hash_node_item nodehash; \
/* Each node of route. */ \
void *info; \
@@ -194,26 +198,29 @@ static inline void route_table_set_info(struct route_table *table, void *d)
table->info = d;
}
+/* ext_pure => extern __attribute__((pure))
+ * does not modify memory (but depends on mem), allows compiler to optimize
+ */
+
extern void route_table_finish(struct route_table *table);
-extern struct route_node *route_top(struct route_table *table);
-extern struct route_node *route_next(struct route_node *node);
-extern struct route_node *route_next_until(struct route_node *node,
- const struct route_node *limit);
-extern struct route_node *route_node_get(struct route_table *const table,
+ext_pure struct route_node *route_top(struct route_table *table);
+ext_pure struct route_node *route_next(struct route_node *node);
+ext_pure struct route_node *route_next_until(struct route_node *node,
+ const struct route_node *limit);
+extern struct route_node *route_node_get(struct route_table *table,
union prefixconstptr pu);
-extern struct route_node *route_node_lookup(const struct route_table *table,
- union prefixconstptr pu);
-extern struct route_node *
-route_node_lookup_maynull(const struct route_table *table,
- union prefixconstptr pu);
-extern struct route_node *route_node_match(const struct route_table *table,
- union prefixconstptr pu);
-extern struct route_node *route_node_match_ipv4(const struct route_table *table,
- const struct in_addr *addr);
-extern struct route_node *route_node_match_ipv6(const struct route_table *table,
- const struct in6_addr *addr);
-
-extern unsigned long route_table_count(const struct route_table *table);
+ext_pure struct route_node *route_node_lookup(struct route_table *table,
+ union prefixconstptr pu);
+ext_pure struct route_node *route_node_lookup_maynull(struct route_table *table,
+ union prefixconstptr pu);
+ext_pure struct route_node *route_node_match(struct route_table *table,
+ union prefixconstptr pu);
+ext_pure struct route_node *route_node_match_ipv4(struct route_table *table,
+ const struct in_addr *addr);
+ext_pure struct route_node *route_node_match_ipv6(struct route_table *table,
+ const struct in6_addr *addr);
+
+ext_pure unsigned long route_table_count(struct route_table *table);
extern struct route_node *route_node_create(route_table_delegate_t *delegate,
struct route_table *table);
@@ -222,10 +229,10 @@ extern void route_node_destroy(route_table_delegate_t *delegate,
struct route_table *table,
struct route_node *node);
-extern struct route_node *route_table_get_next(const struct route_table *table,
- union prefixconstptr pu);
-extern int route_table_prefix_iter_cmp(const struct prefix *p1,
- const struct prefix *p2);
+ext_pure struct route_node *route_table_get_next(struct route_table *table,
+ union prefixconstptr pu);
+ext_pure int route_table_prefix_iter_cmp(const struct prefix *p1,
+ const struct prefix *p2);
/*
* Iterator functions.
diff --git a/lib/thread.c b/lib/thread.c
index 2760b83fb3..d3fb2cdf36 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -40,6 +40,8 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_MASTER, "Thread master")
DEFINE_MTYPE_STATIC(LIB, THREAD_POLL, "Thread Poll Info")
DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats")
+DECLARE_LIST(thread_list, struct thread, threaditem)
+
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
@@ -61,7 +63,7 @@ static struct list *masters;
static void thread_free(struct thread_master *master, struct thread *thread);
/* CLI start ---------------------------------------------------------------- */
-static unsigned int cpu_record_hash_key(struct cpu_thread_history *a)
+static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
{
int size = sizeof(a->func);
@@ -279,7 +281,7 @@ DEFUN (show_thread_cpu,
SHOW_STR
"Thread information\n"
"Thread CPU usage\n"
- "Display filter (rwtexb)\n")
+ "Display filter (rwtex)\n")
{
uint8_t filter = (uint8_t)-1U;
int idx = 0;
@@ -310,7 +312,8 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m)
vty_out(vty, "\nShowing poll FD's for %s\n", name);
vty_out(vty, "----------------------%s\n", underline);
- vty_out(vty, "Count: %u\n", (uint32_t)m->handler.pfdcount);
+ vty_out(vty, "Count: %u/%d\n", (uint32_t)m->handler.pfdcount,
+ m->fd_limit);
for (i = 0; i < m->handler.pfdcount; i++)
vty_out(vty, "\t%6d fd:%6d events:%2d revents:%2d\n", i,
m->handler.pfds[i].fd,
@@ -431,10 +434,13 @@ struct thread_master *thread_master_create(const char *name)
sizeof(struct thread *) * rv->fd_limit);
rv->cpu_record = hash_create_size(
- 8, (unsigned int (*)(void *))cpu_record_hash_key,
+ 8, (unsigned int (*)(const void *))cpu_record_hash_key,
(bool (*)(const void *, const void *))cpu_record_hash_cmp,
"Thread Hash");
+ thread_list_init(&rv->event);
+ thread_list_init(&rv->ready);
+ thread_list_init(&rv->unuse);
/* Initialize the timer queues */
rv->timer = pqueue_create();
@@ -487,50 +493,6 @@ void thread_master_set_name(struct thread_master *master, const char *name)
pthread_mutex_unlock(&master->mtx);
}
-/* Add a new thread to the list. */
-static void thread_list_add(struct thread_list *list, struct thread *thread)
-{
- thread->next = NULL;
- thread->prev = list->tail;
- if (list->tail)
- list->tail->next = thread;
- else
- list->head = thread;
- list->tail = thread;
- list->count++;
-}
-
-/* Delete a thread from the list. */
-static struct thread *thread_list_delete(struct thread_list *list,
- struct thread *thread)
-{
- if (thread->next)
- thread->next->prev = thread->prev;
- else
- list->tail = thread->prev;
- if (thread->prev)
- thread->prev->next = thread->next;
- else
- list->head = thread->next;
- thread->next = thread->prev = NULL;
- list->count--;
- return thread;
-}
-
-/* Thread list is empty or not. */
-static int thread_empty(struct thread_list *list)
-{
- return list->head ? 0 : 1;
-}
-
-/* Delete top of the list and return it. */
-static struct thread *thread_trim_head(struct thread_list *list)
-{
- if (!thread_empty(list))
- return thread_list_delete(list, list->head);
- return NULL;
-}
-
#define THREAD_UNUSED_DEPTH 10
/* Move thread to unuse list. */
@@ -539,8 +501,6 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread)
pthread_mutex_t mtxc = thread->mtx;
assert(m != NULL && thread != NULL);
- assert(thread->next == NULL);
- assert(thread->prev == NULL);
thread->hist->total_active--;
memset(thread, 0, sizeof(struct thread));
@@ -549,8 +509,8 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread)
/* Restore the thread mutex context. */
thread->mtx = mtxc;
- if (m->unuse.count < THREAD_UNUSED_DEPTH) {
- thread_list_add(&m->unuse, thread);
+ if (thread_list_count(&m->unuse) < THREAD_UNUSED_DEPTH) {
+ thread_list_add_tail(&m->unuse, thread);
return;
}
@@ -558,16 +518,13 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread)
}
/* Free all unused thread. */
-static void thread_list_free(struct thread_master *m, struct thread_list *list)
+static void thread_list_free(struct thread_master *m,
+ struct thread_list_head *list)
{
struct thread *t;
- struct thread *next;
- for (t = list->head; t; t = next) {
- next = t->next;
+ while ((t = thread_list_pop(list)))
thread_free(m, t);
- list->count--;
- }
}
static void thread_array_free(struct thread_master *m,
@@ -609,9 +566,8 @@ void thread_master_free_unused(struct thread_master *m)
pthread_mutex_lock(&m->mtx);
{
struct thread *t;
- while ((t = thread_trim_head(&m->unuse)) != NULL) {
+ while ((t = thread_list_pop(&m->unuse)))
thread_free(m, t);
- }
}
pthread_mutex_unlock(&m->mtx);
}
@@ -690,7 +646,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
int (*func)(struct thread *), void *arg,
debugargdef)
{
- struct thread *thread = thread_trim_head(&m->unuse);
+ struct thread *thread = thread_list_pop(&m->unuse);
struct cpu_thread_history tmp;
if (!thread) {
@@ -971,7 +927,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m,
pthread_mutex_lock(&thread->mtx);
{
thread->u.val = val;
- thread_list_add(&m->event, thread);
+ thread_list_add_tail(&m->event, thread);
}
pthread_mutex_unlock(&thread->mtx);
@@ -1063,7 +1019,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state)
*/
static void do_thread_cancel(struct thread_master *master)
{
- struct thread_list *list = NULL;
+ struct thread_list_head *list = NULL;
struct pqueue *queue = NULL;
struct thread **thread_array = NULL;
struct thread *thread;
@@ -1078,31 +1034,23 @@ static void do_thread_cancel(struct thread_master *master)
* need to check every thread in the ready queue. */
if (cr->eventobj) {
struct thread *t;
- thread = master->event.head;
-
- while (thread) {
- t = thread;
- thread = t->next;
-
- if (t->arg == cr->eventobj) {
- thread_list_delete(&master->event, t);
- if (t->ref)
- *t->ref = NULL;
- thread_add_unuse(master, t);
- }
+
+ frr_each_safe(thread_list, &master->event, t) {
+ if (t->arg != cr->eventobj)
+ continue;
+ thread_list_del(&master->event, t);
+ if (t->ref)
+ *t->ref = NULL;
+ thread_add_unuse(master, t);
}
- thread = master->ready.head;
- while (thread) {
- t = thread;
- thread = t->next;
-
- if (t->arg == cr->eventobj) {
- thread_list_delete(&master->ready, t);
- if (t->ref)
- *t->ref = NULL;
- thread_add_unuse(master, t);
- }
+ frr_each_safe(thread_list, &master->ready, t) {
+ if (t->arg != cr->eventobj)
+ continue;
+ thread_list_del(&master->ready, t);
+ if (t->ref)
+ *t->ref = NULL;
+ thread_add_unuse(master, t);
}
continue;
}
@@ -1146,7 +1094,7 @@ static void do_thread_cancel(struct thread_master *master)
assert(thread == queue->array[thread->index]);
pqueue_remove_at(thread->index, queue);
} else if (list) {
- thread_list_delete(list, thread);
+ thread_list_del(list, thread);
} else if (thread_array) {
thread_array[thread->u.fd] = NULL;
} else {
@@ -1301,7 +1249,7 @@ static int thread_process_io_helper(struct thread_master *m,
thread_array = m->write;
thread_array[thread->u.fd] = NULL;
- thread_list_add(&m->ready, thread);
+ thread_list_add_tail(&m->ready, thread);
thread->type = THREAD_READY;
/* if another pthread scheduled this file descriptor for the event we're
* responding to, no problem; we're getting to it now */
@@ -1380,24 +1328,21 @@ static unsigned int thread_process_timers(struct pqueue *queue,
return ready;
pqueue_dequeue(queue);
thread->type = THREAD_READY;
- thread_list_add(&thread->master->ready, thread);
+ thread_list_add_tail(&thread->master->ready, thread);
ready++;
}
return ready;
}
/* process a list en masse, e.g. for event thread lists */
-static unsigned int thread_process(struct thread_list *list)
+static unsigned int thread_process(struct thread_list_head *list)
{
struct thread *thread;
- struct thread *next;
unsigned int ready = 0;
- for (thread = list->head; thread; thread = next) {
- next = thread->next;
- thread_list_delete(list, thread);
+ while ((thread = thread_list_pop(list))) {
thread->type = THREAD_READY;
- thread_list_add(&thread->master->ready, thread);
+ thread_list_add_tail(&thread->master->ready, thread);
ready++;
}
return ready;
@@ -1429,7 +1374,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
* Attempt to flush ready queue before going into poll().
* This is performance-critical. Think twice before modifying.
*/
- if ((thread = thread_trim_head(&m->ready))) {
+ if ((thread = thread_list_pop(&m->ready))) {
fetch = thread_run(m, thread, fetch);
if (fetch->ref)
*fetch->ref = NULL;
@@ -1462,10 +1407,11 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
* In every case except the last, we need to hit poll() at least
* once per loop to avoid starvation by events
*/
- if (m->ready.count == 0)
+ if (!thread_list_count(&m->ready))
tw = thread_timer_wait(m->timer, &tv);
- if (m->ready.count != 0 || (tw && !timercmp(tw, &zerotime, >)))
+ if (thread_list_count(&m->ready) ||
+ (tw && !timercmp(tw, &zerotime, >)))
tw = &zerotime;
if (!tw && m->handler.pfdcount == 0) { /* die */
diff --git a/lib/thread.h b/lib/thread.h
index ec774a6543..7897265120 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -26,6 +26,7 @@
#include <poll.h>
#include "monotime.h"
#include "frratomic.h"
+#include "typesafe.h"
#ifdef __cplusplus
extern "C" {
@@ -39,12 +40,7 @@ struct rusage_t {
#define GETRUSAGE(X) thread_getrusage(X)
-/* Linked list of thread. */
-struct thread_list {
- struct thread *head;
- struct thread *tail;
- int count;
-};
+PREDECL_LIST(thread_list)
struct pqueue;
@@ -78,9 +74,7 @@ struct thread_master {
struct thread **read;
struct thread **write;
struct pqueue *timer;
- struct thread_list event;
- struct thread_list ready;
- struct thread_list unuse;
+ struct thread_list_head event, ready, unuse;
struct list *cancel_req;
bool canceled;
pthread_cond_t cancel_cond;
@@ -100,8 +94,7 @@ struct thread_master {
struct thread {
uint8_t type; /* thread type */
uint8_t add_type; /* thread type */
- struct thread *next; /* next pointer of the thread */
- struct thread *prev; /* previous pointer of the thread */
+ struct thread_list_item threaditem;
struct thread **ref; /* external reference (if given) */
struct thread_master *master; /* pointer to the struct thread_master */
int (*func)(struct thread *); /* event function */
diff --git a/lib/typerb.c b/lib/typerb.c
new file mode 100644
index 0000000000..d361e7651e
--- /dev/null
+++ b/lib/typerb.c
@@ -0,0 +1,472 @@
+/* RB-tree */
+
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "typerb.h"
+
+#define RB_BLACK 0
+#define RB_RED 1
+
+#define rb_entry typed_rb_entry
+#define rbt_tree typed_rb_root
+
+#define RBE_LEFT(_rbe) (_rbe)->rbt_left
+#define RBE_RIGHT(_rbe) (_rbe)->rbt_right
+#define RBE_PARENT(_rbe) (_rbe)->rbt_parent
+#define RBE_COLOR(_rbe) (_rbe)->rbt_color
+
+#define RBH_ROOT(_rbt) (_rbt)->rbt_root
+
+static inline void rbe_set(struct rb_entry *rbe, struct rb_entry *parent)
+{
+ RBE_PARENT(rbe) = parent;
+ RBE_LEFT(rbe) = RBE_RIGHT(rbe) = NULL;
+ RBE_COLOR(rbe) = RB_RED;
+}
+
+static inline void rbe_set_blackred(struct rb_entry *black,
+ struct rb_entry *red)
+{
+ RBE_COLOR(black) = RB_BLACK;
+ RBE_COLOR(red) = RB_RED;
+}
+
+static inline void rbe_rotate_left(struct rbt_tree *rbt, struct rb_entry *rbe)
+{
+ struct rb_entry *parent;
+ struct rb_entry *tmp;
+
+ tmp = RBE_RIGHT(rbe);
+ RBE_RIGHT(rbe) = RBE_LEFT(tmp);
+ if (RBE_RIGHT(rbe) != NULL)
+ RBE_PARENT(RBE_LEFT(tmp)) = rbe;
+
+ parent = RBE_PARENT(rbe);
+ RBE_PARENT(tmp) = parent;
+ if (parent != NULL) {
+ if (rbe == RBE_LEFT(parent))
+ RBE_LEFT(parent) = tmp;
+ else
+ RBE_RIGHT(parent) = tmp;
+ } else
+ RBH_ROOT(rbt) = tmp;
+
+ RBE_LEFT(tmp) = rbe;
+ RBE_PARENT(rbe) = tmp;
+}
+
+static inline void rbe_rotate_right(struct rbt_tree *rbt, struct rb_entry *rbe)
+{
+ struct rb_entry *parent;
+ struct rb_entry *tmp;
+
+ tmp = RBE_LEFT(rbe);
+ RBE_LEFT(rbe) = RBE_RIGHT(tmp);
+ if (RBE_LEFT(rbe) != NULL)
+ RBE_PARENT(RBE_RIGHT(tmp)) = rbe;
+
+ parent = RBE_PARENT(rbe);
+ RBE_PARENT(tmp) = parent;
+ if (parent != NULL) {
+ if (rbe == RBE_LEFT(parent))
+ RBE_LEFT(parent) = tmp;
+ else
+ RBE_RIGHT(parent) = tmp;
+ } else
+ RBH_ROOT(rbt) = tmp;
+
+ RBE_RIGHT(tmp) = rbe;
+ RBE_PARENT(rbe) = tmp;
+}
+
+static inline void rbe_insert_color(struct rbt_tree *rbt, struct rb_entry *rbe)
+{
+ struct rb_entry *parent, *gparent, *tmp;
+
+ rbt->count++;
+
+ while ((parent = RBE_PARENT(rbe)) != NULL
+ && RBE_COLOR(parent) == RB_RED) {
+ gparent = RBE_PARENT(parent);
+
+ if (parent == RBE_LEFT(gparent)) {
+ tmp = RBE_RIGHT(gparent);
+ if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
+ RBE_COLOR(tmp) = RB_BLACK;
+ rbe_set_blackred(parent, gparent);
+ rbe = gparent;
+ continue;
+ }
+
+ if (RBE_RIGHT(parent) == rbe) {
+ rbe_rotate_left(rbt, parent);
+ tmp = parent;
+ parent = rbe;
+ rbe = tmp;
+ }
+
+ rbe_set_blackred(parent, gparent);
+ rbe_rotate_right(rbt, gparent);
+ } else {
+ tmp = RBE_LEFT(gparent);
+ if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
+ RBE_COLOR(tmp) = RB_BLACK;
+ rbe_set_blackred(parent, gparent);
+ rbe = gparent;
+ continue;
+ }
+
+ if (RBE_LEFT(parent) == rbe) {
+ rbe_rotate_right(rbt, parent);
+ tmp = parent;
+ parent = rbe;
+ rbe = tmp;
+ }
+
+ rbe_set_blackred(parent, gparent);
+ rbe_rotate_left(rbt, gparent);
+ }
+ }
+
+ RBE_COLOR(RBH_ROOT(rbt)) = RB_BLACK;
+}
+
+static inline void rbe_remove_color(struct rbt_tree *rbt,
+ struct rb_entry *parent,
+ struct rb_entry *rbe)
+{
+ struct rb_entry *tmp;
+
+ while ((rbe == NULL || RBE_COLOR(rbe) == RB_BLACK)
+ && rbe != RBH_ROOT(rbt) && parent) {
+ if (RBE_LEFT(parent) == rbe) {
+ tmp = RBE_RIGHT(parent);
+ if (RBE_COLOR(tmp) == RB_RED) {
+ rbe_set_blackred(tmp, parent);
+ rbe_rotate_left(rbt, parent);
+ tmp = RBE_RIGHT(parent);
+ }
+ if ((RBE_LEFT(tmp) == NULL
+ || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK)
+ && (RBE_RIGHT(tmp) == NULL
+ || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
+ RBE_COLOR(tmp) = RB_RED;
+ rbe = parent;
+ parent = RBE_PARENT(rbe);
+ } else {
+ if (RBE_RIGHT(tmp) == NULL
+ || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK) {
+ struct rb_entry *oleft;
+
+ oleft = RBE_LEFT(tmp);
+ if (oleft != NULL)
+ RBE_COLOR(oleft) = RB_BLACK;
+
+ RBE_COLOR(tmp) = RB_RED;
+ rbe_rotate_right(rbt, tmp);
+ tmp = RBE_RIGHT(parent);
+ }
+
+ RBE_COLOR(tmp) = RBE_COLOR(parent);
+ RBE_COLOR(parent) = RB_BLACK;
+ if (RBE_RIGHT(tmp))
+ RBE_COLOR(RBE_RIGHT(tmp)) = RB_BLACK;
+
+ rbe_rotate_left(rbt, parent);
+ rbe = RBH_ROOT(rbt);
+ break;
+ }
+ } else {
+ tmp = RBE_LEFT(parent);
+ if (RBE_COLOR(tmp) == RB_RED) {
+ rbe_set_blackred(tmp, parent);
+ rbe_rotate_right(rbt, parent);
+ tmp = RBE_LEFT(parent);
+ }
+
+ if ((RBE_LEFT(tmp) == NULL
+ || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK)
+ && (RBE_RIGHT(tmp) == NULL
+ || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
+ RBE_COLOR(tmp) = RB_RED;
+ rbe = parent;
+ parent = RBE_PARENT(rbe);
+ } else {
+ if (RBE_LEFT(tmp) == NULL
+ || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) {
+ struct rb_entry *oright;
+
+ oright = RBE_RIGHT(tmp);
+ if (oright != NULL)
+ RBE_COLOR(oright) = RB_BLACK;
+
+ RBE_COLOR(tmp) = RB_RED;
+ rbe_rotate_left(rbt, tmp);
+ tmp = RBE_LEFT(parent);
+ }
+
+ RBE_COLOR(tmp) = RBE_COLOR(parent);
+ RBE_COLOR(parent) = RB_BLACK;
+ if (RBE_LEFT(tmp) != NULL)
+ RBE_COLOR(RBE_LEFT(tmp)) = RB_BLACK;
+
+ rbe_rotate_right(rbt, parent);
+ rbe = RBH_ROOT(rbt);
+ break;
+ }
+ }
+ }
+
+ if (rbe != NULL)
+ RBE_COLOR(rbe) = RB_BLACK;
+}
+
+static inline struct rb_entry *
+rbe_remove(struct rbt_tree *rbt, struct rb_entry *rbe)
+{
+ struct rb_entry *child, *parent, *old = rbe;
+ unsigned int color;
+
+ if (RBE_LEFT(rbe) == NULL)
+ child = RBE_RIGHT(rbe);
+ else if (RBE_RIGHT(rbe) == NULL)
+ child = RBE_LEFT(rbe);
+ else {
+ struct rb_entry *tmp;
+
+ rbe = RBE_RIGHT(rbe);
+ while ((tmp = RBE_LEFT(rbe)) != NULL)
+ rbe = tmp;
+
+ child = RBE_RIGHT(rbe);
+ parent = RBE_PARENT(rbe);
+ color = RBE_COLOR(rbe);
+ if (child != NULL)
+ RBE_PARENT(child) = parent;
+ if (parent != NULL) {
+ if (RBE_LEFT(parent) == rbe)
+ RBE_LEFT(parent) = child;
+ else
+ RBE_RIGHT(parent) = child;
+ } else
+ RBH_ROOT(rbt) = child;
+ if (RBE_PARENT(rbe) == old)
+ parent = rbe;
+ *rbe = *old;
+
+ tmp = RBE_PARENT(old);
+ if (tmp != NULL) {
+ if (RBE_LEFT(tmp) == old)
+ RBE_LEFT(tmp) = rbe;
+ else
+ RBE_RIGHT(tmp) = rbe;
+ } else
+ RBH_ROOT(rbt) = rbe;
+
+ RBE_PARENT(RBE_LEFT(old)) = rbe;
+ if (RBE_RIGHT(old))
+ RBE_PARENT(RBE_RIGHT(old)) = rbe;
+
+ goto color;
+ }
+
+ parent = RBE_PARENT(rbe);
+ color = RBE_COLOR(rbe);
+
+ if (child != NULL)
+ RBE_PARENT(child) = parent;
+ if (parent != NULL) {
+ if (RBE_LEFT(parent) == rbe)
+ RBE_LEFT(parent) = child;
+ else
+ RBE_RIGHT(parent) = child;
+ } else
+ RBH_ROOT(rbt) = child;
+color:
+ if (color == RB_BLACK)
+ rbe_remove_color(rbt, parent, child);
+
+ rbt->count--;
+ return (old);
+}
+
+void typed_rb_remove(struct rbt_tree *rbt, struct rb_entry *rbe)
+{
+ rbe_remove(rbt, rbe);
+}
+
+struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt,
+ struct rb_entry *rbe, int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b))
+{
+ struct rb_entry *tmp;
+ struct rb_entry *parent = NULL;
+ int comp = 0;
+
+ tmp = RBH_ROOT(rbt);
+ while (tmp != NULL) {
+ parent = tmp;
+
+ comp = cmpfn(rbe, tmp);
+ if (comp < 0)
+ tmp = RBE_LEFT(tmp);
+ else if (comp > 0)
+ tmp = RBE_RIGHT(tmp);
+ else
+ return tmp;
+ }
+
+ rbe_set(rbe, parent);
+
+ if (parent != NULL) {
+ if (comp < 0)
+ RBE_LEFT(parent) = rbe;
+ else
+ RBE_RIGHT(parent) = rbe;
+ } else
+ RBH_ROOT(rbt) = rbe;
+
+ rbe_insert_color(rbt, rbe);
+
+ return NULL;
+}
+
+/* Finds the node with the same key as elm */
+struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b))
+{
+ struct rb_entry *tmp = RBH_ROOT(rbt);
+ int comp;
+
+ while (tmp != NULL) {
+ comp = cmpfn(key, tmp);
+ if (comp < 0)
+ tmp = RBE_LEFT(tmp);
+ else if (comp > 0)
+ tmp = RBE_RIGHT(tmp);
+ else
+ return tmp;
+ }
+
+ return (NULL);
+}
+
+struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt,
+ const struct rb_entry *key,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b))
+{
+ struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
+ int comp;
+
+ while (tmp != NULL) {
+ comp = cmpfn(key, tmp);
+ if (comp < 0) {
+ best = tmp;
+ tmp = RBE_LEFT(tmp);
+ } else if (comp > 0)
+ tmp = RBE_RIGHT(tmp);
+ else
+ return tmp;
+ }
+
+ return best;
+}
+
+struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt,
+ const struct rb_entry *key,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b))
+{
+ struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
+ int comp;
+
+ while (tmp != NULL) {
+ comp = cmpfn(key, tmp);
+ if (comp <= 0)
+ tmp = RBE_LEFT(tmp);
+ else {
+ best = tmp;
+ tmp = RBE_RIGHT(tmp);
+ }
+ }
+
+ return best;
+}
+
+struct rb_entry *typed_rb_next(struct rb_entry *rbe)
+{
+ if (RBE_RIGHT(rbe) != NULL) {
+ rbe = RBE_RIGHT(rbe);
+ while (RBE_LEFT(rbe) != NULL)
+ rbe = RBE_LEFT(rbe);
+ } else {
+ if (RBE_PARENT(rbe) && (rbe == RBE_LEFT(RBE_PARENT(rbe))))
+ rbe = RBE_PARENT(rbe);
+ else {
+ while (RBE_PARENT(rbe)
+ && (rbe == RBE_RIGHT(RBE_PARENT(rbe))))
+ rbe = RBE_PARENT(rbe);
+ rbe = RBE_PARENT(rbe);
+ }
+ }
+
+ return rbe;
+}
+
+struct rb_entry *typed_rb_min(struct rbt_tree *rbt)
+{
+ struct rb_entry *rbe = RBH_ROOT(rbt);
+ struct rb_entry *parent = NULL;
+
+ while (rbe != NULL) {
+ parent = rbe;
+ rbe = RBE_LEFT(rbe);
+ }
+
+ return parent;
+}
diff --git a/lib/typerb.h b/lib/typerb.h
new file mode 100644
index 0000000000..ce8446f853
--- /dev/null
+++ b/lib/typerb.h
@@ -0,0 +1,190 @@
+/*
+ * The following Red-Black tree implementation is based off code with
+ * original copyright:
+ *
+ * Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_TYPERB_H
+#define _FRR_TYPERB_H
+
+#include "typesafe.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct typed_rb_entry {
+ struct typed_rb_entry *rbt_parent;
+ struct typed_rb_entry *rbt_left;
+ struct typed_rb_entry *rbt_right;
+ unsigned int rbt_color;
+};
+
+struct typed_rb_root {
+ struct typed_rb_entry *rbt_root;
+ size_t count;
+};
+
+struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *,
+ struct typed_rb_entry *rbe,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b));
+void typed_rb_remove(struct typed_rb_root *, struct typed_rb_entry *rbe);
+struct typed_rb_entry *typed_rb_find(struct typed_rb_root *,
+ const struct typed_rb_entry *rbe,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b));
+struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *,
+ const struct typed_rb_entry *rbe,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b));
+struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *,
+ const struct typed_rb_entry *rbe,
+ int (*cmpfn)(
+ const struct typed_rb_entry *a,
+ const struct typed_rb_entry *b));
+struct typed_rb_entry *typed_rb_min(struct typed_rb_root *);
+struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *);
+
+#define _PREDECL_RBTREE(prefix) \
+struct prefix ## _head { struct typed_rb_root rr; }; \
+struct prefix ## _item { struct typed_rb_entry re; };
+
+#define INIT_RBTREE_UNIQ(var) { }
+#define INIT_RBTREE_NONUNIQ(var) { }
+
+#define _DECLARE_RBTREE(prefix, type, field, cmpfn_nuq, cmpfn_uq) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \
+ return container_of_null(re, type, field.re); \
+} \
+macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \
+ return container_of_null(re, type, field.re); \
+} \
+macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \
+ return container_of_null(re, type, field.re); \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ typed_rb_remove(&h->rr, &item->field.re); \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_min(&h->rr); \
+ if (!re) \
+ return NULL; \
+ typed_rb_remove(&h->rr, re); \
+ return container_of(re, type, field.re); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_min(&h->rr); \
+ return container_of_null(re, type, field.re); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_next(&item->field.re); \
+ return container_of_null(re, type, field.re); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = item ? typed_rb_next(&item->field.re) : NULL; \
+ return container_of_null(re, type, field.re); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->rr.count; \
+} \
+/* ... */
+
+#define PREDECL_RBTREE_UNIQ(prefix) \
+ _PREDECL_RBTREE(prefix)
+#define DECLARE_RBTREE_UNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \
+ const struct typed_rb_entry *b) \
+{ \
+ return cmpfn(container_of(a, type, field.re), \
+ container_of(b, type, field.re)); \
+} \
+macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
+{ \
+ struct typed_rb_entry *re; \
+ re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \
+ return container_of_null(re, type, field.re); \
+} \
+ \
+_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \
+/* ... */
+
+#define PREDECL_RBTREE_NONUNIQ(prefix) \
+ _PREDECL_RBTREE(prefix)
+#define DECLARE_RBTREE_NONUNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \
+ const struct typed_rb_entry *b) \
+{ \
+ return cmpfn(container_of(a, type, field.re), \
+ container_of(b, type, field.re)); \
+} \
+macro_inline int prefix ## __cmp_uq(const struct typed_rb_entry *a, \
+ const struct typed_rb_entry *b) \
+{ \
+ int cmpval = cmpfn(container_of(a, type, field.re), \
+ container_of(b, type, field.re)); \
+ if (cmpval) \
+ return cmpval; \
+ if (a < b) \
+ return -1; \
+ if (a > b) \
+ return 1; \
+ return 0; \
+} \
+ \
+_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp_uq) \
+/* ... */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_TYPERB_H */
diff --git a/lib/typesafe.c b/lib/typesafe.c
new file mode 100644
index 0000000000..47a6d0af48
--- /dev/null
+++ b/lib/typesafe.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "typesafe.h"
+#include "memory.h"
+
+DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket")
+DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow")
+DEFINE_MTYPE_STATIC(LIB, HEAP_ARRAY, "Typed-heap array")
+
+#if 0
+static void hash_consistency_check(struct thash_head *head)
+{
+ uint32_t i;
+ struct thash_item *item, *prev;
+
+ for (i = 0; i < HASH_SIZE(*head); i++) {
+ item = head->entries[i];
+ prev = NULL;
+ while (item) {
+ assert(HASH_KEY(*head, item->hashval) == i);
+ assert(!prev || item->hashval >= prev->hashval);
+ prev = item;
+ item = item->next;
+ }
+ }
+}
+#else
+#define hash_consistency_check(x)
+#endif
+
+void typesafe_hash_grow(struct thash_head *head)
+{
+ uint32_t newsize = head->count, i, j;
+ uint8_t newshift, delta;
+
+ hash_consistency_check(head);
+
+ newsize |= newsize >> 1;
+ newsize |= newsize >> 2;
+ newsize |= newsize >> 4;
+ newsize |= newsize >> 8;
+ newsize |= newsize >> 16;
+ newsize++;
+ newshift = __builtin_ctz(newsize) + 1;
+
+ if (head->maxshift && newshift > head->maxshift)
+ newshift = head->maxshift;
+ if (newshift == head->tabshift)
+ return;
+ newsize = _HASH_SIZE(newshift);
+
+ head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries,
+ sizeof(head->entries[0]) * newsize);
+ memset(head->entries + HASH_SIZE(*head), 0,
+ sizeof(head->entries[0]) *
+ (newsize - HASH_SIZE(*head)));
+
+ delta = newshift - head->tabshift;
+
+ i = HASH_SIZE(*head);
+ if (i == 0)
+ goto out;
+ do {
+ struct thash_item **apos, *item;
+
+ i--;
+ apos = &head->entries[i];
+
+ for (j = 0; j < (1U << delta); j++) {
+ item = *apos;
+ *apos = NULL;
+
+ head->entries[(i << delta) + j] = item;
+ apos = &head->entries[(i << delta) + j];
+
+ while ((item = *apos)) {
+ uint32_t midbits;
+ midbits = _HASH_KEY(newshift, item->hashval);
+ midbits &= (1 << delta) - 1;
+ if (midbits > j)
+ break;
+ apos = &item->next;
+ }
+ }
+ } while (i > 0);
+
+out:
+ head->tabshift = newshift;
+ hash_consistency_check(head);
+}
+
+void typesafe_hash_shrink(struct thash_head *head)
+{
+ uint32_t newsize = head->count, i, j;
+ uint8_t newshift, delta;
+
+ hash_consistency_check(head);
+
+ if (!head->count) {
+ XFREE(MTYPE_TYPEDHASH_BUCKET, head->entries);
+ head->tabshift = 0;
+ return;
+ }
+
+ newsize |= newsize >> 1;
+ newsize |= newsize >> 2;
+ newsize |= newsize >> 4;
+ newsize |= newsize >> 8;
+ newsize |= newsize >> 16;
+ newsize++;
+ newshift = __builtin_ctz(newsize) + 1;
+
+ if (head->minshift && newshift < head->minshift)
+ newshift = head->minshift;
+ if (newshift == head->tabshift)
+ return;
+ newsize = _HASH_SIZE(newshift);
+
+ delta = head->tabshift - newshift;
+
+ for (i = 0; i < newsize; i++) {
+ struct thash_item **apos = &head->entries[i];
+
+ for (j = 0; j < (1U << delta); j++) {
+ *apos = head->entries[(i << delta) + j];
+ while (*apos)
+ apos = &(*apos)->next;
+ }
+ }
+ head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries,
+ sizeof(head->entries[0]) * newsize);
+ head->tabshift = newshift;
+
+ hash_consistency_check(head);
+}
+
+/* skiplist */
+
+static inline struct sskip_item *sl_level_get(struct sskip_item *item,
+ size_t level)
+{
+ if (level < SKIPLIST_OVERFLOW)
+ return item->next[level];
+ if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1))
+ return item->next[level];
+
+ uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW];
+ ptrval &= UINTPTR_MAX - 3;
+ struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval;
+ return oflow->next[level - SKIPLIST_OVERFLOW];
+}
+
+static inline void sl_level_set(struct sskip_item *item, size_t level,
+ struct sskip_item *value)
+{
+ if (level < SKIPLIST_OVERFLOW)
+ item->next[level] = value;
+ else if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1))
+ item->next[level] = value;
+ else {
+ uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW];
+ ptrval &= UINTPTR_MAX - 3;
+ struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval;
+ oflow->next[level - SKIPLIST_OVERFLOW] = value;
+ }
+}
+
+struct sskip_item *typesafe_skiplist_add(struct sskip_head *head,
+ struct sskip_item *item,
+ int (*cmpfn)(const struct sskip_item *a,
+ const struct sskip_item *b))
+{
+ size_t level = SKIPLIST_MAXDEPTH, newlevel, auxlevel;
+ struct sskip_item *prev = &head->hitem, *next, *auxprev, *auxnext;
+ int cmpval;
+
+ /* level / newlevel are 1-counted here */
+ newlevel = __builtin_ctz(random()) + 1;
+ if (newlevel > SKIPLIST_MAXDEPTH)
+ newlevel = SKIPLIST_MAXDEPTH;
+
+ next = NULL;
+ while (level >= newlevel) {
+ next = sl_level_get(prev, level - 1);
+ if (!next) {
+ level--;
+ continue;
+ }
+ cmpval = cmpfn(next, item);
+ if (cmpval < 0) {
+ prev = next;
+ continue;
+ } else if (cmpval == 0) {
+ return next;
+ }
+ level--;
+ }
+
+ /* check for duplicate item - could be removed if code doesn't rely
+ * on it, but not really work the complication. */
+ auxlevel = level;
+ auxprev = prev;
+ while (auxlevel) {
+ auxlevel--;
+ auxnext = sl_level_get(auxprev, auxlevel);
+ cmpval = 1;
+ while (auxnext && (cmpval = cmpfn(auxnext, item)) < 0) {
+ auxprev = auxnext;
+ auxnext = sl_level_get(auxprev, auxlevel);
+ }
+ if (cmpval == 0)
+ return auxnext;
+ };
+
+ head->count++;
+ memset(item, 0, sizeof(*item));
+ if (newlevel > SKIPLIST_EMBED) {
+ struct sskip_overflow *oflow;
+ oflow = XMALLOC(MTYPE_SKIPLIST_OFLOW, sizeof(void *)
+ * (newlevel - SKIPLIST_OVERFLOW));
+ item->next[SKIPLIST_OVERFLOW] = (struct sskip_item *)
+ ((uintptr_t)oflow | 1);
+ }
+
+ sl_level_set(item, level, next);
+ sl_level_set(prev, level, item);
+ /* level is now 0-counted and < newlevel*/
+ while (level) {
+ level--;
+ next = sl_level_get(prev, level);
+ while (next && cmpfn(next, item) < 0) {
+ prev = next;
+ next = sl_level_get(prev, level);
+ }
+
+ sl_level_set(item, level, next);
+ sl_level_set(prev, level, item);
+ };
+ return NULL;
+}
+
+/* NOTE: level counting below is 1-based since that makes the code simpler! */
+
+struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b))
+{
+ size_t level = SKIPLIST_MAXDEPTH;
+ struct sskip_item *prev = &head->hitem, *next;
+ int cmpval;
+
+ while (level) {
+ next = sl_level_get(prev, level - 1);
+ if (!next) {
+ level--;
+ continue;
+ }
+ cmpval = cmpfn(next, item);
+ if (cmpval < 0) {
+ prev = next;
+ continue;
+ }
+ if (cmpval == 0)
+ return next;
+ level--;
+ }
+ return NULL;
+}
+
+struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b))
+{
+ size_t level = SKIPLIST_MAXDEPTH;
+ struct sskip_item *prev = &head->hitem, *next;
+ int cmpval;
+
+ while (level) {
+ next = sl_level_get(prev, level - 1);
+ if (!next) {
+ level--;
+ continue;
+ }
+ cmpval = cmpfn(next, item);
+ if (cmpval < 0) {
+ prev = next;
+ continue;
+ }
+ if (cmpval == 0)
+ return next;
+ level--;
+ }
+ return next;
+}
+
+struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b))
+{
+ size_t level = SKIPLIST_MAXDEPTH;
+ struct sskip_item *prev = &head->hitem, *next, *best = NULL;
+ int cmpval;
+
+ while (level) {
+ next = sl_level_get(prev, level - 1);
+ if (!next) {
+ level--;
+ continue;
+ }
+ cmpval = cmpfn(next, item);
+ if (cmpval < 0) {
+ best = prev = next;
+ continue;
+ }
+ level--;
+ }
+ return best;
+}
+
+void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item,
+ int (*cmpfn)(const struct sskip_item *a,
+ const struct sskip_item *b))
+{
+ size_t level = SKIPLIST_MAXDEPTH;
+ struct sskip_item *prev = &head->hitem, *next;
+ int cmpval;
+
+ while (level) {
+ next = sl_level_get(prev, level - 1);
+ if (!next) {
+ level--;
+ continue;
+ }
+ if (next == item) {
+ sl_level_set(prev, level - 1,
+ sl_level_get(item, level - 1));
+ level--;
+ continue;
+ }
+ cmpval = cmpfn(next, item);
+ if (cmpval < 0) {
+ prev = next;
+ continue;
+ }
+ level--;
+ }
+
+ /* TBD: assert when trying to remove non-existing item? */
+ head->count--;
+
+ if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) {
+ uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW];
+ ptrval &= UINTPTR_MAX - 3;
+ struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval;
+ XFREE(MTYPE_SKIPLIST_OFLOW, oflow);
+ }
+ memset(item, 0, sizeof(*item));
+}
+
+struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head)
+{
+ size_t level = SKIPLIST_MAXDEPTH;
+ struct sskip_item *prev = &head->hitem, *next, *item;
+
+ item = sl_level_get(prev, 0);
+ if (!item)
+ return NULL;
+
+ do {
+ level--;
+
+ next = sl_level_get(prev, level);
+ if (next != item)
+ continue;
+
+ sl_level_set(prev, level, sl_level_get(item, level));
+ } while (level);
+
+ head->count--;
+
+ if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) {
+ uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW];
+ ptrval &= UINTPTR_MAX - 3;
+ struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval;
+ XFREE(MTYPE_SKIPLIST_OFLOW, oflow);
+ }
+ memset(item, 0, sizeof(*item));
+
+ return item;
+}
+
+/* heap */
+
+#if 0
+static void heap_consistency_check(struct heap_head *head,
+ int (*cmpfn)(const struct heap_item *a,
+ const struct heap_item *b),
+ uint32_t pos)
+{
+ uint32_t rghtpos = pos + 1;
+ uint32_t downpos = HEAP_NARY * (pos + 1);
+
+ if (pos + 1 > ~0U / HEAP_NARY)
+ downpos = ~0U;
+
+ if ((pos & (HEAP_NARY - 1)) != HEAP_NARY - 1 && rghtpos < head->count) {
+ assert(cmpfn(head->array[rghtpos], head->array[pos]) >= 0);
+ heap_consistency_check(head, cmpfn, rghtpos);
+ }
+ if (downpos < head->count) {
+ assert(cmpfn(head->array[downpos], head->array[pos]) >= 0);
+ heap_consistency_check(head, cmpfn, downpos);
+ }
+}
+#else
+#define heap_consistency_check(head, cmpfn, pos)
+#endif
+
+void typesafe_heap_resize(struct heap_head *head, bool grow)
+{
+ uint32_t newsize;
+
+ if (grow) {
+ newsize = head->arraysz;
+ if (newsize <= 36)
+ newsize = 72;
+ else if (newsize < 262144)
+ newsize += newsize / 2;
+ else if (newsize < 0xaaaa0000)
+ newsize += newsize / 3;
+ else
+ assert(!newsize);
+ } else if (head->count > 0) {
+ newsize = head->count;
+ } else {
+ XFREE(MTYPE_HEAP_ARRAY, head->array);
+ head->arraysz = 0;
+ return;
+ }
+
+ newsize += HEAP_NARY - 1;
+ newsize &= ~(HEAP_NARY - 1);
+ if (newsize == head->arraysz)
+ return;
+
+ head->array = XREALLOC(MTYPE_HEAP_ARRAY, head->array,
+ newsize * sizeof(struct heap_item *));
+ head->arraysz = newsize;
+}
+
+void typesafe_heap_pushdown(struct heap_head *head, uint32_t pos,
+ struct heap_item *item,
+ int (*cmpfn)(const struct heap_item *a,
+ const struct heap_item *b))
+{
+ uint32_t rghtpos, downpos, moveto;
+
+ while (1) {
+ /* rghtpos: neighbor to the "right", inside block of NARY.
+ * may be invalid if last in block, check nary_last()
+ * downpos: first neighbor in the "downwards" block further
+ * away from the root
+ */
+ rghtpos = pos + 1;
+
+ /* make sure we can use the full 4G items */
+ downpos = HEAP_NARY * (pos + 1);
+ if (pos + 1 > ~0U / HEAP_NARY)
+ /* multiplication overflowed. ~0U is guaranteed
+ * to be an invalid index; size limit is enforced in
+ * resize()
+ */
+ downpos = ~0U;
+
+ /* only used on break */
+ moveto = pos;
+
+#define nary_last(x) (((x) & (HEAP_NARY - 1)) == HEAP_NARY - 1)
+ if (downpos >= head->count
+ || cmpfn(head->array[downpos], item) >= 0) {
+ /* not moving down; either at end or down is >= item */
+ if (nary_last(pos) || rghtpos >= head->count
+ || cmpfn(head->array[rghtpos], item) >= 0)
+ /* not moving right either - got our spot */
+ break;
+
+ moveto = rghtpos;
+
+ /* else: downpos is valid and < item. choose between down
+ * or right (if the latter is an option) */
+ } else if (nary_last(pos) || cmpfn(head->array[rghtpos],
+ head->array[downpos]) >= 0)
+ moveto = downpos;
+ else
+ moveto = rghtpos;
+#undef nary_last
+
+ head->array[pos] = head->array[moveto];
+ head->array[pos]->index = pos;
+ pos = moveto;
+ }
+
+ head->array[moveto] = item;
+ item->index = moveto;
+
+ heap_consistency_check(head, cmpfn, 0);
+}
+
+void typesafe_heap_pullup(struct heap_head *head, uint32_t pos,
+ struct heap_item *item,
+ int (*cmpfn)(const struct heap_item *a,
+ const struct heap_item *b))
+{
+ uint32_t moveto;
+
+ while (pos != 0) {
+ if ((pos & (HEAP_NARY - 1)) == 0)
+ moveto = pos / HEAP_NARY - 1;
+ else
+ moveto = pos - 1;
+
+ if (cmpfn(head->array[moveto], item) <= 0)
+ break;
+
+ head->array[pos] = head->array[moveto];
+ head->array[pos]->index = pos;
+
+ pos = moveto;
+ }
+
+ head->array[pos] = item;
+ item->index = pos;
+
+ heap_consistency_check(head, cmpfn, 0);
+}
diff --git a/lib/typesafe.h b/lib/typesafe.h
new file mode 100644
index 0000000000..0a4ed69e4e
--- /dev/null
+++ b/lib/typesafe.h
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_TYPESAFE_H
+#define _FRR_TYPESAFE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include "compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* generic macros for all list-like types */
+
+#define frr_each(prefix, head, item) \
+ for (item = prefix##_first(head); item; \
+ item = prefix##_next(head, item))
+#define frr_each_safe(prefix, head, item) \
+ for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \
+ prefix##_next_safe(head, \
+ (item = prefix##_first(head))); \
+ item; \
+ item = prefix##_safe, \
+ prefix##_safe = prefix##_next_safe(head, prefix##_safe))
+#define frr_each_from(prefix, head, item, from) \
+ for (item = from, from = prefix##_next_safe(head, item); \
+ item; \
+ item = from, from = prefix##_next_safe(head, from))
+
+/* single-linked list, unsorted/arbitrary.
+ * can be used as queue with add_tail / pop
+ */
+
+/* don't use these structs directly */
+struct slist_item {
+ struct slist_item *next;
+};
+
+struct slist_head {
+ struct slist_item *first, **last_next;
+ size_t count;
+};
+
+static inline void typesafe_list_add(struct slist_head *head,
+ struct slist_item **pos, struct slist_item *item)
+{
+ item->next = *pos;
+ *pos = item;
+ if (pos == head->last_next)
+ head->last_next = &item->next;
+ head->count++;
+}
+
+/* use as:
+ *
+ * PREDECL_LIST(namelist)
+ * struct name {
+ * struct namelist_item nlitem;
+ * }
+ * DECLARE_LIST(namelist, struct name, nlitem)
+ */
+#define PREDECL_LIST(prefix) \
+struct prefix ## _head { struct slist_head sh; }; \
+struct prefix ## _item { struct slist_item si; };
+
+#define INIT_LIST(var) { .sh = { .last_next = &var.sh.first, }, }
+
+#define DECLARE_LIST(prefix, type, field) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+ h->sh.last_next = &h->sh.first; \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \
+{ \
+ typesafe_list_add(&h->sh, &h->sh.first, &item->field.si); \
+} \
+macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \
+{ \
+ typesafe_list_add(&h->sh, h->sh.last_next, &item->field.si); \
+} \
+macro_inline void prefix ## _add_after(struct prefix##_head *h, \
+ type *after, type *item) \
+{ \
+ struct slist_item **nextp; \
+ nextp = after ? &after->field.si.next : &h->sh.first; \
+ typesafe_list_add(&h->sh, nextp, &item->field.si); \
+} \
+/* TODO: del_hint */ \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ struct slist_item **iter = &h->sh.first; \
+ while (*iter && *iter != &item->field.si) \
+ iter = &(*iter)->next; \
+ if (!*iter) \
+ return; \
+ h->sh.count--; \
+ *iter = item->field.si.next; \
+ if (!item->field.si.next) \
+ h->sh.last_next = iter; \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct slist_item *sitem = h->sh.first; \
+ if (!sitem) \
+ return NULL; \
+ h->sh.count--; \
+ h->sh.first = sitem->next; \
+ if (h->sh.first == NULL) \
+ h->sh.last_next = &h->sh.first; \
+ return container_of(sitem, type, field.si); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ return container_of_null(h->sh.first, type, field.si); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \
+{ \
+ struct slist_item *sitem = &item->field.si; \
+ return container_of_null(sitem->next, type, field.si); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ struct slist_item *sitem; \
+ if (!item) \
+ return NULL; \
+ sitem = &item->field.si; \
+ return container_of_null(sitem->next, type, field.si); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->sh.count; \
+} \
+/* ... */
+
+/* don't use these structs directly */
+struct dlist_item {
+ struct dlist_item *next;
+ struct dlist_item *prev;
+};
+
+struct dlist_head {
+ struct dlist_item hitem;
+ size_t count;
+};
+
+static inline void typesafe_dlist_add(struct dlist_head *head,
+ struct dlist_item *prev, struct dlist_item *item)
+{
+ item->next = prev->next;
+ item->next->prev = item;
+ item->prev = prev;
+ prev->next = item;
+ head->count++;
+}
+
+/* double-linked list, for fast item deletion
+ */
+#define PREDECL_DLIST(prefix) \
+struct prefix ## _head { struct dlist_head dh; }; \
+struct prefix ## _item { struct dlist_item di; };
+
+#define INIT_DLIST(var) { .dh = { \
+ .hitem = { &var.dh.hitem, &var.dh.hitem }, }, }
+
+#define DECLARE_DLIST(prefix, type, field) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+ h->dh.hitem.prev = &h->dh.hitem; \
+ h->dh.hitem.next = &h->dh.hitem; \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \
+{ \
+ typesafe_dlist_add(&h->dh, &h->dh.hitem, &item->field.di); \
+} \
+macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \
+{ \
+ typesafe_dlist_add(&h->dh, h->dh.hitem.prev, &item->field.di); \
+} \
+macro_inline void prefix ## _add_after(struct prefix##_head *h, \
+ type *after, type *item) \
+{ \
+ struct dlist_item *prev; \
+ prev = after ? &after->field.di : &h->dh.hitem; \
+ typesafe_dlist_add(&h->dh, prev, &item->field.di); \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ struct dlist_item *ditem = &item->field.di; \
+ ditem->prev->next = ditem->next; \
+ ditem->next->prev = ditem->prev; \
+ h->dh.count--; \
+ ditem->prev = ditem->next = NULL; \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct dlist_item *ditem = h->dh.hitem.next; \
+ if (ditem == &h->dh.hitem) \
+ return NULL; \
+ ditem->prev->next = ditem->next; \
+ ditem->next->prev = ditem->prev; \
+ h->dh.count--; \
+ return container_of(ditem, type, field.di); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ struct dlist_item *ditem = h->dh.hitem.next; \
+ if (ditem == &h->dh.hitem) \
+ return NULL; \
+ return container_of(ditem, type, field.di); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \
+{ \
+ struct dlist_item *ditem = &item->field.di; \
+ if (ditem->next == &h->dh.hitem) \
+ return NULL; \
+ return container_of(ditem->next, type, field.di); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ if (!item) \
+ return NULL; \
+ return prefix ## _next(h, item); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->dh.count; \
+} \
+/* ... */
+
+/* note: heap currently caps out at 4G items */
+
+#define HEAP_NARY 8U
+typedef uint32_t heap_index_i;
+
+struct heap_item {
+ uint32_t index;
+};
+
+struct heap_head {
+ struct heap_item **array;
+ uint32_t arraysz, count;
+};
+
+#define HEAP_RESIZE_TRESH_UP(h) \
+ (h->hh.count + 1 >= h->hh.arraysz)
+#define HEAP_RESIZE_TRESH_DN(h) \
+ (h->hh.count == 0 || \
+ h->hh.arraysz - h->hh.count > (h->hh.count + 1024) / 2)
+
+#define PREDECL_HEAP(prefix) \
+struct prefix ## _head { struct heap_head hh; }; \
+struct prefix ## _item { struct heap_item hi; };
+
+#define INIT_HEAP(var) { }
+
+#define DECLARE_HEAP(prefix, type, field, cmpfn) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ assert(h->hh.count == 0); \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline int prefix ## __cmp(const struct heap_item *a, \
+ const struct heap_item *b) \
+{ \
+ return cmpfn(container_of(a, type, field.hi), \
+ container_of(b, type, field.hi)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ if (HEAP_RESIZE_TRESH_UP(h)) \
+ typesafe_heap_resize(&h->hh, true); \
+ typesafe_heap_pullup(&h->hh, h->hh.count, &item->field.hi, \
+ prefix ## __cmp); \
+ h->hh.count++; \
+ return NULL; \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ struct heap_item *other; \
+ uint32_t index = item->field.hi.index; \
+ assert(h->hh.array[index] == &item->field.hi); \
+ h->hh.count--; \
+ other = h->hh.array[h->hh.count]; \
+ if (cmpfn(container_of(other, type, field.hi), item) < 0) \
+ typesafe_heap_pullup(&h->hh, index, other, prefix ## __cmp); \
+ else \
+ typesafe_heap_pushdown(&h->hh, index, other, prefix ## __cmp); \
+ if (HEAP_RESIZE_TRESH_DN(h)) \
+ typesafe_heap_resize(&h->hh, false); \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct heap_item *hitem, *other; \
+ if (h->hh.count == 0) \
+ return NULL; \
+ hitem = h->hh.array[0]; \
+ h->hh.count--; \
+ other = h->hh.array[h->hh.count]; \
+ typesafe_heap_pushdown(&h->hh, 0, other, prefix ## __cmp); \
+ if (HEAP_RESIZE_TRESH_DN(h)) \
+ typesafe_heap_resize(&h->hh, false); \
+ return container_of(hitem, type, field.hi); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ if (h->hh.count == 0) \
+ return NULL; \
+ return container_of(h->hh.array[0], type, field.hi); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ uint32_t idx = item->field.hi.index + 1; \
+ if (idx >= h->hh.count) \
+ return NULL; \
+ return container_of(h->hh.array[idx], type, field.hi); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ if (!item) \
+ return NULL; \
+ return prefix ## _next(h, item); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->hh.count; \
+} \
+/* ... */
+
+extern void typesafe_heap_resize(struct heap_head *head, bool grow);
+extern void typesafe_heap_pushdown(struct heap_head *head, uint32_t index,
+ struct heap_item *item,
+ int (*cmpfn)(const struct heap_item *a,
+ const struct heap_item *b));
+extern void typesafe_heap_pullup(struct heap_head *head, uint32_t index,
+ struct heap_item *item,
+ int (*cmpfn)(const struct heap_item *a,
+ const struct heap_item *b));
+
+/* single-linked list, sorted.
+ * can be used as priority queue with add / pop
+ */
+
+/* don't use these structs directly */
+struct ssort_item {
+ struct ssort_item *next;
+};
+
+struct ssort_head {
+ struct ssort_item *first;
+ size_t count;
+};
+
+/* use as:
+ *
+ * PREDECL_SORTLIST(namelist)
+ * struct name {
+ * struct namelist_item nlitem;
+ * }
+ * DECLARE_SORTLIST(namelist, struct name, nlitem)
+ */
+#define _PREDECL_SORTLIST(prefix) \
+struct prefix ## _head { struct ssort_head sh; }; \
+struct prefix ## _item { struct ssort_item si; };
+
+#define INIT_SORTLIST_UNIQ(var) { }
+#define INIT_SORTLIST_NONUNIQ(var) { }
+
+#define PREDECL_SORTLIST_UNIQ(prefix) \
+ _PREDECL_SORTLIST(prefix)
+#define PREDECL_SORTLIST_NONUNIQ(prefix) \
+ _PREDECL_SORTLIST(prefix)
+
+#define _DECLARE_SORTLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ struct ssort_item **np = &h->sh.first; \
+ int c = 1; \
+ while (*np && (c = cmpfn_uq( \
+ container_of(*np, type, field.si), item)) < 0) \
+ np = &(*np)->next; \
+ if (c == 0) \
+ return container_of(*np, type, field.si); \
+ item->field.si.next = *np; \
+ *np = &item->field.si; \
+ h->sh.count++; \
+ return NULL; \
+} \
+macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct ssort_item *sitem = h->sh.first; \
+ int cmpval = 0; \
+ while (sitem && (cmpval = cmpfn_nuq( \
+ container_of(sitem, type, field.si), item) < 0)) \
+ sitem = sitem->next; \
+ return container_of_null(sitem, type, field.si); \
+} \
+macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct ssort_item *prev = NULL, *sitem = h->sh.first; \
+ int cmpval = 0; \
+ while (sitem && (cmpval = cmpfn_nuq( \
+ container_of(sitem, type, field.si), item) < 0)) \
+ sitem = (prev = sitem)->next; \
+ return container_of_null(prev, type, field.si); \
+} \
+/* TODO: del_hint */ \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ struct ssort_item **iter = &h->sh.first; \
+ while (*iter && *iter != &item->field.si) \
+ iter = &(*iter)->next; \
+ if (!*iter) \
+ return; \
+ h->sh.count--; \
+ *iter = item->field.si.next; \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct ssort_item *sitem = h->sh.first; \
+ if (!sitem) \
+ return NULL; \
+ h->sh.count--; \
+ h->sh.first = sitem->next; \
+ return container_of(sitem, type, field.si); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ return container_of_null(h->sh.first, type, field.si); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ struct ssort_item *sitem = &item->field.si; \
+ return container_of_null(sitem->next, type, field.si); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ struct ssort_item *sitem; \
+ if (!item) \
+ return NULL; \
+ sitem = &item->field.si; \
+ return container_of_null(sitem->next, type, field.si); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->sh.count; \
+} \
+/* ... */
+
+#define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \
+ _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \
+ \
+macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
+{ \
+ struct ssort_item *sitem = h->sh.first; \
+ int cmpval = 0; \
+ while (sitem && (cmpval = cmpfn( \
+ container_of(sitem, type, field.si), item) < 0)) \
+ sitem = sitem->next; \
+ if (!sitem || cmpval > 0) \
+ return NULL; \
+ return container_of(sitem, type, field.si); \
+} \
+/* ... */
+
+#define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \
+macro_inline int _ ## prefix ## _cmp(const type *a, const type *b) \
+{ \
+ int cmpval = cmpfn(a, b); \
+ if (cmpval) \
+ return cmpval; \
+ if (a < b) \
+ return -1; \
+ if (a > b) \
+ return 1; \
+ return 0; \
+} \
+ _DECLARE_SORTLIST(prefix, type, field, cmpfn, _ ## prefix ## _cmp) \
+/* ... */
+
+
+/* hash, "sorted" by hash value
+ */
+
+/* don't use these structs directly */
+struct thash_item {
+ struct thash_item *next;
+ uint32_t hashval;
+};
+
+struct thash_head {
+ struct thash_item **entries;
+ uint32_t count;
+
+ uint8_t tabshift;
+ uint8_t minshift, maxshift;
+};
+
+#define _HASH_SIZE(tabshift) \
+ ((1U << (tabshift)) >> 1)
+#define HASH_SIZE(head) \
+ _HASH_SIZE((head).tabshift)
+#define _HASH_KEY(tabshift, val) \
+ ((val) >> (33 - (tabshift)))
+#define HASH_KEY(head, val) \
+ _HASH_KEY((head).tabshift, val)
+#define HASH_GROW_THRESHOLD(head) \
+ ((head).count >= HASH_SIZE(head))
+#define HASH_SHRINK_THRESHOLD(head) \
+ ((head).count <= (HASH_SIZE(head) - 1) / 2)
+
+extern void typesafe_hash_grow(struct thash_head *head);
+extern void typesafe_hash_shrink(struct thash_head *head);
+
+/* use as:
+ *
+ * PREDECL_HASH(namelist)
+ * struct name {
+ * struct namelist_item nlitem;
+ * }
+ * DECLARE_HASH(namelist, struct name, nlitem, cmpfunc, hashfunc)
+ */
+#define PREDECL_HASH(prefix) \
+struct prefix ## _head { struct thash_head hh; }; \
+struct prefix ## _item { struct thash_item hi; };
+
+#define INIT_HASH(var) { }
+
+#define DECLARE_HASH(prefix, type, field, cmpfn, hashfn) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ assert(h->hh.count == 0); \
+ h->hh.minshift = 0; \
+ typesafe_hash_shrink(&h->hh); \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ h->hh.count++; \
+ if (!h->hh.tabshift || HASH_GROW_THRESHOLD(h->hh)) \
+ typesafe_hash_grow(&h->hh); \
+ \
+ uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \
+ item->field.hi.hashval = hval; \
+ struct thash_item **np = &h->hh.entries[hbits]; \
+ while (*np && (*np)->hashval < hval) \
+ np = &(*np)->next; \
+ if (*np && cmpfn(container_of(*np, type, field.hi), item) == 0) { \
+ h->hh.count--; \
+ return container_of(*np, type, field.hi); \
+ } \
+ item->field.hi.next = *np; \
+ *np = &item->field.hi; \
+ return NULL; \
+} \
+macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
+{ \
+ if (!h->hh.tabshift) \
+ return NULL; \
+ uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \
+ struct thash_item *hitem = h->hh.entries[hbits]; \
+ while (hitem && hitem->hashval < hval) \
+ hitem = hitem->next; \
+ while (hitem && hitem->hashval == hval) { \
+ if (!cmpfn(container_of(hitem, type, field.hi), item)) \
+ return container_of(hitem, type, field.hi); \
+ hitem = hitem->next; \
+ } \
+ return NULL; \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ if (!h->hh.tabshift) \
+ return; \
+ uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \
+ struct thash_item **np = &h->hh.entries[hbits]; \
+ while (*np && (*np)->hashval < hval) \
+ np = &(*np)->next; \
+ while (*np && *np != &item->field.hi && (*np)->hashval == hval) \
+ np = &(*np)->next; \
+ if (*np != &item->field.hi) \
+ return; \
+ *np = item->field.hi.next; \
+ item->field.hi.next = NULL; \
+ h->hh.count--; \
+ if (HASH_SHRINK_THRESHOLD(h->hh)) \
+ typesafe_hash_shrink(&h->hh); \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ uint32_t i; \
+ for (i = 0; i < HASH_SIZE(h->hh); i++) \
+ if (h->hh.entries[i]) { \
+ struct thash_item *hitem = h->hh.entries[i]; \
+ h->hh.entries[i] = hitem->next; \
+ h->hh.count--; \
+ hitem->next = NULL; \
+ if (HASH_SHRINK_THRESHOLD(h->hh)) \
+ typesafe_hash_shrink(&h->hh); \
+ return container_of(hitem, type, field.hi); \
+ } \
+ return NULL; \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ uint32_t i; \
+ for (i = 0; i < HASH_SIZE(h->hh); i++) \
+ if (h->hh.entries[i]) \
+ return container_of(h->hh.entries[i], type, field.hi); \
+ return NULL; \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ struct thash_item *hitem = &item->field.hi; \
+ if (hitem->next) \
+ return container_of(hitem->next, type, field.hi); \
+ uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \
+ for (; i < HASH_SIZE(h->hh); i++) \
+ if (h->hh.entries[i]) \
+ return container_of(h->hh.entries[i], type, field.hi); \
+ return NULL; \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ if (!item) \
+ return NULL; \
+ return prefix ## _next(h, item); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->hh.count; \
+} \
+/* ... */
+
+/* skiplist, sorted.
+ * can be used as priority queue with add / pop
+ */
+
+/* don't use these structs directly */
+#define SKIPLIST_MAXDEPTH 16
+#define SKIPLIST_EMBED 4
+#define SKIPLIST_OVERFLOW (SKIPLIST_EMBED - 1)
+
+struct sskip_item {
+ struct sskip_item *next[SKIPLIST_EMBED];
+};
+
+struct sskip_overflow {
+ struct sskip_item *next[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW];
+};
+
+struct sskip_head {
+ struct sskip_item hitem;
+ struct sskip_item *overflow[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW];
+ size_t count;
+};
+
+/* use as:
+ *
+ * PREDECL_SKIPLIST(namelist)
+ * struct name {
+ * struct namelist_item nlitem;
+ * }
+ * DECLARE_SKIPLIST(namelist, struct name, nlitem, cmpfunc)
+ */
+#define _PREDECL_SKIPLIST(prefix) \
+struct prefix ## _head { struct sskip_head sh; }; \
+struct prefix ## _item { struct sskip_item si; };
+
+#define INIT_SKIPLIST_UNIQ(var) { }
+#define INIT_SKIPLIST_NONUNIQ(var) { }
+
+#define _DECLARE_SKIPLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \
+ \
+macro_inline void prefix ## _init(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+ h->sh.hitem.next[SKIPLIST_OVERFLOW] = (struct sskip_item *) \
+ ((uintptr_t)h->sh.overflow | 1); \
+} \
+macro_inline void prefix ## _fini(struct prefix##_head *h) \
+{ \
+ memset(h, 0, sizeof(*h)); \
+} \
+macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
+{ \
+ struct sskip_item *si; \
+ si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \
+ return container_of_null(si, type, field.si); \
+} \
+macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \
+ &item->field.si, cmpfn_nuq); \
+ return container_of_null(sitem, type, field.si); \
+} \
+macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
+ const type *item) \
+{ \
+ struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \
+ &item->field.si, cmpfn_nuq); \
+ return container_of_null(sitem, type, field.si); \
+} \
+macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \
+{ \
+ typesafe_skiplist_del(&h->sh, &item->field.si, cmpfn_uq); \
+} \
+macro_inline type *prefix ## _pop(struct prefix##_head *h) \
+{ \
+ struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \
+ return container_of_null(sitem, type, field.si); \
+} \
+macro_pure type *prefix ## _first(struct prefix##_head *h) \
+{ \
+ struct sskip_item *first = h->sh.hitem.next[0]; \
+ return container_of_null(first, type, field.si); \
+} \
+macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
+{ \
+ struct sskip_item *next = item->field.si.next[0]; \
+ return container_of_null(next, type, field.si); \
+} \
+macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
+{ \
+ struct sskip_item *next; \
+ next = item ? item->field.si.next[0] : NULL; \
+ return container_of_null(next, type, field.si); \
+} \
+macro_pure size_t prefix ## _count(struct prefix##_head *h) \
+{ \
+ return h->sh.count; \
+} \
+/* ... */
+
+#define PREDECL_SKIPLIST_UNIQ(prefix) \
+ _PREDECL_SKIPLIST(prefix)
+#define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct sskip_item *a, \
+ const struct sskip_item *b) \
+{ \
+ return cmpfn(container_of(a, type, field.si), \
+ container_of(b, type, field.si)); \
+} \
+macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
+{ \
+ struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \
+ &item->field.si, &prefix ## __cmp); \
+ return container_of_null(sitem, type, field.si); \
+} \
+ \
+_DECLARE_SKIPLIST(prefix, type, field, \
+ prefix ## __cmp, prefix ## __cmp) \
+/* ... */
+
+#define PREDECL_SKIPLIST_NONUNIQ(prefix) \
+ _PREDECL_SKIPLIST(prefix)
+#define DECLARE_SKIPLIST_NONUNIQ(prefix, type, field, cmpfn) \
+ \
+macro_inline int prefix ## __cmp(const struct sskip_item *a, \
+ const struct sskip_item *b) \
+{ \
+ return cmpfn(container_of(a, type, field.si), \
+ container_of(b, type, field.si)); \
+} \
+macro_inline int prefix ## __cmp_uq(const struct sskip_item *a, \
+ const struct sskip_item *b) \
+{ \
+ int cmpval = cmpfn(container_of(a, type, field.si), \
+ container_of(b, type, field.si)); \
+ if (cmpval) \
+ return cmpval; \
+ if (a < b) \
+ return -1; \
+ if (a > b) \
+ return 1; \
+ return 0; \
+} \
+ \
+_DECLARE_SKIPLIST(prefix, type, field, \
+ prefix ## __cmp, prefix ## __cmp_uq) \
+/* ... */
+
+
+extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head,
+ struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b));
+extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b));
+extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b));
+extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head,
+ const struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b));
+extern void typesafe_skiplist_del(struct sskip_head *head,
+ struct sskip_item *item, int (*cmpfn)(
+ const struct sskip_item *a,
+ const struct sskip_item *b));
+extern struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* this needs to stay at the end because both files include each other.
+ * the resolved order is typesafe.h before typerb.h
+ */
+#include "typerb.h"
+
+#endif /* _FRR_TYPESAFE_H */
diff --git a/lib/vrf.c b/lib/vrf.c
index de50e6a517..229f19f29a 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -362,9 +362,9 @@ struct vrf_bit_set {
bool set;
};
-static unsigned int vrf_hash_bitmap_key(void *data)
+static unsigned int vrf_hash_bitmap_key(const void *data)
{
- struct vrf_bit_set *bit = data;
+ const struct vrf_bit_set *bit = data;
return bit->vrf_id;
}
@@ -860,7 +860,6 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty),
void vrf_set_default_name(const char *default_name, bool force)
{
struct vrf *def_vrf;
- struct vrf *vrf_with_default_name = NULL;
static bool def_vrf_forced;
def_vrf = vrf_lookup_by_id(VRF_DEFAULT);
@@ -871,13 +870,7 @@ void vrf_set_default_name(const char *default_name, bool force)
def_vrf->vrf_id);
return;
}
- if (vrf_with_default_name && vrf_with_default_name != def_vrf) {
- /* vrf name already used by an other VRF */
- zlog_debug("VRF: %s, avoid changing name to %s, same name exists (%u)",
- vrf_with_default_name->name, default_name,
- vrf_with_default_name->vrf_id);
- return;
- }
+
snprintf(vrf_default_name, VRF_NAMSIZ, "%s", default_name);
if (def_vrf) {
if (force)
@@ -911,10 +904,16 @@ vrf_id_t vrf_get_default_id(void)
int vrf_bind(vrf_id_t vrf_id, int fd, const char *name)
{
int ret = 0;
+ struct interface *ifp;
if (fd < 0 || name == NULL)
return fd;
- if (vrf_is_backend_netns())
+ /* the device should exist
+ * otherwise we should return
+ * case ifname = vrf in netns mode => return
+ */
+ ifp = if_lookup_by_name(name, vrf_id);
+ if (!ifp)
return fd;
#ifdef SO_BINDTODEVICE
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1);
diff --git a/lib/vty.c b/lib/vty.c
index dae8e82599..0ee9b78b91 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -86,9 +86,6 @@ static vector Vvty_serv_thread;
/* Current directory. */
char *vty_cwd = NULL;
-/* Exclusive configuration lock. */
-struct vty *vty_exclusive_lock;
-
/* Login password check. */
static int no_password_check = 0;
@@ -2369,7 +2366,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp)
if (config == NULL && vty->candidate_config
&& frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
- true, "Read configuration file",
+ vty, true, "Read configuration file",
NULL);
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
zlog_err("%s: failed to read configuration file.",
@@ -2601,8 +2598,8 @@ void vty_log_fixed(char *buf, size_t len)
int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
{
- if (exclusive && !vty_config_exclusive_lock(vty)) {
- vty_out(vty, "VTY configuration is locked by other VTY\n");
+ if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
+ vty_out(vty, "%% Configuration is locked by other client\n");
return CMD_WARNING;
}
@@ -2611,17 +2608,22 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
vty->private_config = private_config;
vty->xpath_index = 0;
- if (private_config) {
- vty->candidate_config = nb_config_dup(running_config);
- vty->candidate_config_base = nb_config_dup(running_config);
- vty_out(vty,
- "Warning: uncommitted changes will be discarded on exit.\n\n");
- } else {
- vty->candidate_config = vty_shared_candidate_config;
- if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
+ pthread_rwlock_rdlock(&running_config->lock);
+ {
+ if (private_config) {
+ vty->candidate_config = nb_config_dup(running_config);
vty->candidate_config_base =
nb_config_dup(running_config);
+ vty_out(vty,
+ "Warning: uncommitted changes will be discarded on exit.\n\n");
+ } else {
+ vty->candidate_config = vty_shared_candidate_config;
+ if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
+ vty->candidate_config_base =
+ nb_config_dup(running_config);
+ }
}
+ pthread_rwlock_unlock(&running_config->lock);
return CMD_SUCCESS;
}
@@ -2636,7 +2638,7 @@ void vty_config_exit(struct vty *vty)
nb_cli_confirmed_commit_clean(vty);
}
- vty_config_exclusive_unlock(vty);
+ (void)nb_running_unlock(NB_CLIENT_CLI, vty);
if (vty->candidate_config) {
if (vty->private_config)
@@ -2651,21 +2653,6 @@ void vty_config_exit(struct vty *vty)
vty->config = false;
}
-int vty_config_exclusive_lock(struct vty *vty)
-{
- if (vty_exclusive_lock == NULL) {
- vty_exclusive_lock = vty;
- return 1;
- }
- return 0;
-}
-
-void vty_config_exclusive_unlock(struct vty *vty)
-{
- if (vty_exclusive_lock == vty)
- vty_exclusive_lock = NULL;
-}
-
/* Master of the threads. */
static struct thread_master *vty_master;
diff --git a/lib/vty.h b/lib/vty.h
index 9c2653e1ac..98d75542fd 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -289,9 +289,6 @@ struct vty_arg {
#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP)
#endif
-/* Exported variables */
-extern struct vty *vty_exclusive_lock;
-
/* Prototypes. */
extern void vty_init(struct thread_master *);
extern void vty_init_vtysh(void);
@@ -321,8 +318,6 @@ extern void vty_log(const char *level, const char *proto, const char *fmt,
extern int vty_config_enter(struct vty *vty, bool private_config,
bool exclusive);
extern void vty_config_exit(struct vty *);
-extern int vty_config_exclusive_lock(struct vty *vty);
-extern void vty_config_exclusive_unlock(struct vty *vty);
extern int vty_shell(struct vty *);
extern int vty_shell_serv(struct vty *);
extern void vty_hello(struct vty *);
diff --git a/lib/wheel.c b/lib/wheel.c
index 69d2fa48dc..8e479c931b 100644
--- a/lib/wheel.c
+++ b/lib/wheel.c
@@ -80,7 +80,7 @@ static int wheel_timer_thread(struct thread *t)
}
struct timer_wheel *wheel_init(struct thread_master *master, int period,
- size_t slots, unsigned int (*slot_key)(void *),
+ size_t slots, unsigned int (*slot_key)(const void *),
void (*slot_run)(void *),
const char *run_name)
{
diff --git a/lib/wheel.h b/lib/wheel.h
index e66751c1d0..401789e1a0 100644
--- a/lib/wheel.h
+++ b/lib/wheel.h
@@ -38,7 +38,7 @@ struct timer_wheel {
/*
* Key to determine what slot the item belongs in
*/
- unsigned int (*slot_key)(void *);
+ unsigned int (*slot_key)(const void *);
void (*slot_run)(void *);
};
@@ -80,9 +80,9 @@ struct timer_wheel {
* of running your code.
*/
struct timer_wheel *wheel_init(struct thread_master *master, int period,
- size_t slots, unsigned int (*slot_key)(void *),
- void (*slot_run)(void *),
- const char *run_name);
+ size_t slots,
+ unsigned int (*slot_key)(const void *),
+ void (*slot_run)(void *), const char *run_name);
/*
* Delete the specified timer wheel created
diff --git a/lib/workqueue.c b/lib/workqueue.c
index 066d81f350..54090d0d0f 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -280,7 +280,7 @@ int work_queue_run(struct thread *thread)
if (item->ran > wq->spec.max_retries) {
/* run error handler, if any */
if (wq->spec.errorfunc)
- wq->spec.errorfunc(wq, item->data);
+ wq->spec.errorfunc(wq, item);
work_queue_item_remove(wq, item);
continue;
}
diff --git a/lib/yang.c b/lib/yang.c
index 2a2c155dee..2f9a9aa5a3 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -617,14 +617,6 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
zlog(priority, "libyang: %s", msg);
}
-#if CONFDATE > 20190401
-CPP_NOTICE("lib/yang: time to remove non-LIBYANG_EXT_BUILTIN support")
-#endif
-
-#ifdef LIBYANG_EXT_BUILTIN
-extern struct lytype_plugin_list frr_user_types[];
-#endif
-
struct ly_ctx *yang_ctx_new_setup(void)
{
struct ly_ctx *ctx;
@@ -650,31 +642,10 @@ struct ly_ctx *yang_ctx_new_setup(void)
void yang_init(void)
{
-#ifndef LIBYANG_EXT_BUILTIN
-CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!")
- static char ly_plugin_dir[PATH_MAX];
- const char *const *ly_loaded_plugins;
- const char *ly_plugin;
- bool found_ly_frr_types = false;
-
- /* Tell libyang where to find its plugins. */
- snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s",
- "LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH);
- putenv(ly_plugin_dir);
-#endif
-
/* Initialize libyang global parameters that affect all containers. */
ly_set_log_clb(ly_log_cb, 1);
ly_log_options(LY_LOLOG | LY_LOSTORE);
-#ifdef LIBYANG_EXT_BUILTIN
- if (ly_register_types(frr_user_types, "frr_user_types")) {
- flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD,
- "ly_register_types() failed");
- exit(1);
- }
-#endif
-
/* Initialize libyang container for native models. */
ly_native_ctx = yang_ctx_new_setup();
if (!ly_native_ctx) {
@@ -682,22 +653,6 @@ CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!")
exit(1);
}
-#ifndef LIBYANG_EXT_BUILTIN
- /* Detect if the required libyang plugin(s) were loaded successfully. */
- ly_loaded_plugins = ly_get_loaded_plugins();
- for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) {
- if (strmatch(ly_plugin, "frr_user_types")) {
- found_ly_frr_types = true;
- break;
- }
- }
- if (!found_ly_frr_types) {
- flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD,
- "%s: failed to load frr_user_types.so", __func__);
- exit(1);
- }
-#endif
-
yang_translator_init();
}
diff --git a/lib/yang_translator.c b/lib/yang_translator.c
index 76a6cc5fd1..341420eeda 100644
--- a/lib/yang_translator.c
+++ b/lib/yang_translator.c
@@ -24,6 +24,7 @@
#include "hash.h"
#include "yang.h"
#include "yang_translator.h"
+#include "frrstr.h"
DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator")
DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module")
@@ -45,8 +46,6 @@ static struct ly_ctx *ly_translator_ctx;
static unsigned int
yang_translator_validate(struct yang_translator *translator);
static unsigned int yang_module_nodes_count(const struct lys_module *module);
-static void str_replace(char *o_string, const char *s_string,
- const char *r_string);
struct yang_mapping_node {
char xpath_from_canonical[XPATH_MAXLEN];
@@ -62,7 +61,7 @@ static bool yang_mapping_hash_cmp(const void *value1, const void *value2)
return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical);
}
-static unsigned int yang_mapping_hash_key(void *value)
+static unsigned int yang_mapping_hash_key(const void *value)
{
return string_hash_make(value);
}
@@ -108,14 +107,24 @@ static void yang_mapping_add(struct yang_translator *translator, int dir,
sizeof(mapping->xpath_from_fmt));
strlcpy(mapping->xpath_to_fmt, xpath_to_fmt,
sizeof(mapping->xpath_to_fmt));
- str_replace(mapping->xpath_from_fmt, "KEY1", "%[^']");
- str_replace(mapping->xpath_from_fmt, "KEY2", "%[^']");
- str_replace(mapping->xpath_from_fmt, "KEY3", "%[^']");
- str_replace(mapping->xpath_from_fmt, "KEY4", "%[^']");
- str_replace(mapping->xpath_to_fmt, "KEY1", "%s");
- str_replace(mapping->xpath_to_fmt, "KEY2", "%s");
- str_replace(mapping->xpath_to_fmt, "KEY3", "%s");
- str_replace(mapping->xpath_to_fmt, "KEY4", "%s");
+
+ const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"};
+ char *xpfmt;
+
+ for (unsigned int i = 0; i < array_size(keys); i++) {
+ xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i],
+ "%[^']");
+ strlcpy(mapping->xpath_from_fmt, xpfmt,
+ sizeof(mapping->xpath_from_fmt));
+ XFREE(MTYPE_TMP, xpfmt);
+ }
+
+ for (unsigned int i = 0; i < array_size(keys); i++) {
+ xpfmt = frrstr_replace(mapping->xpath_to_fmt, keys[i], "%s");
+ strlcpy(mapping->xpath_to_fmt, xpfmt,
+ sizeof(mapping->xpath_to_fmt));
+ XFREE(MTYPE_TMP, xpfmt);
+ }
}
struct yang_translator *yang_translator_load(const char *path)
@@ -500,28 +509,6 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module)
return total;
}
-/* TODO: rewrite this function. */
-static void str_replace(char *o_string, const char *s_string,
- const char *r_string)
-{
- char buffer[BUFSIZ];
- char *ch;
-
- ch = strstr(o_string, s_string);
- if (!ch)
- return;
-
- memcpy(buffer, o_string, ch - o_string);
- buffer[ch - o_string] = 0;
-
- sprintf(buffer + (ch - o_string), "%s%s", r_string,
- ch + strlen(s_string));
-
- o_string[0] = 0;
- strcpy(o_string, buffer);
- return str_replace(o_string, s_string, r_string);
-}
-
void yang_translator_init(void)
{
ly_translator_ctx = yang_ctx_new_setup();
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index 7ecea5f445..0558383823 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -817,7 +817,7 @@ void yang_dnode_get_ipv4(struct in_addr *addr, const struct lyd_node *dnode,
dleaf = (const struct lyd_node_leaf_list *)dnode;
assert(dleaf->value_type == LY_TYPE_STRING);
- memcpy(addr, dleaf->value.ptr, sizeof(*addr));
+ (void)inet_pton(AF_INET, dleaf->value_str, addr);
}
void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, ...)
@@ -874,7 +874,7 @@ void yang_dnode_get_ipv4p(union prefixptr prefix, const struct lyd_node *dnode,
dleaf = (const struct lyd_node_leaf_list *)dnode;
assert(dleaf->value_type == LY_TYPE_STRING);
- memcpy(prefix4, dleaf->value.ptr, sizeof(*prefix4));
+ (void)str2prefix_ipv4(dleaf->value_str, prefix4);
}
void yang_get_default_ipv4p(union prefixptr var, const char *xpath_fmt, ...)
@@ -927,7 +927,7 @@ void yang_dnode_get_ipv6(struct in6_addr *addr, const struct lyd_node *dnode,
dleaf = (const struct lyd_node_leaf_list *)dnode;
assert(dleaf->value_type == LY_TYPE_STRING);
- memcpy(addr, dleaf->value.ptr, sizeof(*addr));
+ (void)inet_pton(AF_INET6, dleaf->value_str, addr);
}
void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt, ...)
@@ -984,7 +984,7 @@ void yang_dnode_get_ipv6p(union prefixptr prefix, const struct lyd_node *dnode,
dleaf = (const struct lyd_node_leaf_list *)dnode;
assert(dleaf->value_type == LY_TYPE_STRING);
- memcpy(prefix6, dleaf->value.ptr, sizeof(*prefix6));
+ (void)str2prefix_ipv6(dleaf->value_str, prefix6);
}
void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...)
diff --git a/lib/zclient.c b/lib/zclient.c
index 4901c92743..0972590ca6 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -555,6 +555,25 @@ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id,
zclient_send_message(zclient);
}
+int zclient_send_interface_protodown(struct zclient *zclient, vrf_id_t vrf_id,
+ struct interface *ifp, bool down)
+{
+ struct stream *s;
+
+ if (zclient->sock < 0)
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+ zclient_create_header(s, ZEBRA_INTERFACE_SET_PROTODOWN, vrf_id);
+ stream_putl(s, ifp->ifindex);
+ stream_putc(s, !!down);
+ stream_putw_at(s, 0, stream_get_endp(s));
+ zclient_send_message(zclient);
+
+ return 0;
+}
+
/* Make connection to zebra daemon. */
int zclient_start(struct zclient *zclient)
{
@@ -1381,6 +1400,8 @@ stream_failure:
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | bandwidth |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | parent ifindex |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Link Layer Type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Harware Address Length |
@@ -1561,6 +1582,7 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp)
ifp->mtu = stream_getl(s);
ifp->mtu6 = stream_getl(s);
ifp->bandwidth = stream_getl(s);
+ ifp->link_ifindex = stream_getl(s);
ifp->ll_type = stream_getl(s);
ifp->hw_addr_len = stream_getl(s);
if (ifp->hw_addr_len)
@@ -2371,9 +2393,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw)
/*
* Receive PW status update from Zebra and send it to LDE process.
*/
-void zebra_read_pw_status_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id,
- struct zapi_pw_status *pw)
+void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
{
struct stream *s;
@@ -2386,8 +2406,7 @@ void zebra_read_pw_status_update(int command, struct zclient *zclient,
pw->status = stream_getl(s);
}
-static void zclient_capability_decode(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
{
struct zclient_capabilities cap;
struct stream *s = zclient->ibuf;
diff --git a/lib/zclient.h b/lib/zclient.h
index 0926281f2e..09f0acad84 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -76,6 +76,7 @@ typedef enum {
ZEBRA_INTERFACE_UP,
ZEBRA_INTERFACE_DOWN,
ZEBRA_INTERFACE_SET_MASTER,
+ ZEBRA_INTERFACE_SET_PROTODOWN,
ZEBRA_ROUTE_ADD,
ZEBRA_ROUTE_DELETE,
ZEBRA_ROUTE_NOTIFY_OWNER,
@@ -221,66 +222,49 @@ struct zclient {
/* Redistribute defauilt. */
vrf_bitmap_t default_information[AFI_MAX];
+#define ZAPI_CALLBACK_ARGS \
+ int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id
+
/* Pointer to the callback functions. */
void (*zebra_connected)(struct zclient *);
void (*zebra_capabilities)(struct zclient_capabilities *cap);
- int (*router_id_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_add)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_delete)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_up)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_down)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_address_add)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_address_delete)(int, struct zclient *, uint16_t,
- vrf_id_t);
- int (*interface_link_params)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*interface_bfd_dest_update)(int, struct zclient *, uint16_t,
- vrf_id_t);
- int (*interface_nbr_address_add)(int, struct zclient *, uint16_t,
- vrf_id_t);
- int (*interface_nbr_address_delete)(int, struct zclient *, uint16_t,
- vrf_id_t);
- int (*interface_vrf_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*nexthop_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*import_check_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*bfd_dest_replay)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*redistribute_route_add)(int, struct zclient *, uint16_t,
- vrf_id_t);
- int (*redistribute_route_del)(int, struct zclient *, uint16_t,
- vrf_id_t);
+ int (*router_id_update)(ZAPI_CALLBACK_ARGS);
+ int (*interface_add)(ZAPI_CALLBACK_ARGS);
+ int (*interface_delete)(ZAPI_CALLBACK_ARGS);
+ int (*interface_up)(ZAPI_CALLBACK_ARGS);
+ int (*interface_down)(ZAPI_CALLBACK_ARGS);
+ int (*interface_address_add)(ZAPI_CALLBACK_ARGS);
+ int (*interface_address_delete)(ZAPI_CALLBACK_ARGS);
+ int (*interface_link_params)(ZAPI_CALLBACK_ARGS);
+ int (*interface_bfd_dest_update)(ZAPI_CALLBACK_ARGS);
+ int (*interface_nbr_address_add)(ZAPI_CALLBACK_ARGS);
+ int (*interface_nbr_address_delete)(ZAPI_CALLBACK_ARGS);
+ int (*interface_vrf_update)(ZAPI_CALLBACK_ARGS);
+ int (*nexthop_update)(ZAPI_CALLBACK_ARGS);
+ int (*import_check_update)(ZAPI_CALLBACK_ARGS);
+ int (*bfd_dest_replay)(ZAPI_CALLBACK_ARGS);
+ int (*redistribute_route_add)(ZAPI_CALLBACK_ARGS);
+ int (*redistribute_route_del)(ZAPI_CALLBACK_ARGS);
int (*fec_update)(int, struct zclient *, uint16_t);
- int (*local_es_add)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- int (*local_es_del)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
- void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t);
- void (*local_ip_prefix_del)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*route_notify_owner)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- int (*rule_notify_owner)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- void (*label_chunk)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- int (*ipset_notify_owner)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
- int (*ipset_entry_notify_owner)(int command,
- struct zclient *zclient,
- uint16_t length,
- vrf_id_t vrf_id);
- int (*iptable_notify_owner)(int command,
- struct zclient *zclient,
- uint16_t length,
- vrf_id_t vrf_id);
- int (*vxlan_sg_add)(int command, struct zclient *client,
- uint16_t length, vrf_id_t vrf_id);
- int (*vxlan_sg_del)(int command, struct zclient *client,
- uint16_t length, vrf_id_t vrf_id_t);
+ int (*local_es_add)(ZAPI_CALLBACK_ARGS);
+ int (*local_es_del)(ZAPI_CALLBACK_ARGS);
+ int (*local_vni_add)(ZAPI_CALLBACK_ARGS);
+ int (*local_vni_del)(ZAPI_CALLBACK_ARGS);
+ int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS);
+ int (*local_l3vni_del)(ZAPI_CALLBACK_ARGS);
+ void (*local_ip_prefix_add)(ZAPI_CALLBACK_ARGS);
+ void (*local_ip_prefix_del)(ZAPI_CALLBACK_ARGS);
+ int (*local_macip_add)(ZAPI_CALLBACK_ARGS);
+ int (*local_macip_del)(ZAPI_CALLBACK_ARGS);
+ int (*pw_status_update)(ZAPI_CALLBACK_ARGS);
+ int (*route_notify_owner)(ZAPI_CALLBACK_ARGS);
+ int (*rule_notify_owner)(ZAPI_CALLBACK_ARGS);
+ void (*label_chunk)(ZAPI_CALLBACK_ARGS);
+ int (*ipset_notify_owner)(ZAPI_CALLBACK_ARGS);
+ int (*ipset_entry_notify_owner)(ZAPI_CALLBACK_ARGS);
+ int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS);
+ int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS);
+ int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS);
};
/* Zebra API message flag. */
@@ -483,6 +467,9 @@ extern void zclient_send_interface_radv_req(struct zclient *zclient,
vrf_id_t vrf_id,
struct interface *ifp, int enable,
int ra_interval);
+extern int zclient_send_interface_protodown(struct zclient *zclient,
+ vrf_id_t vrf_id,
+ struct interface *ifp, bool down);
/* Send redistribute command to zebra daemon. Do not update zclient state. */
extern int zebra_redistribute_send(int command, struct zclient *, afi_t,
@@ -597,9 +584,7 @@ extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
extern int zebra_send_pw(struct zclient *zclient, int command,
struct zapi_pw *pw);
-extern void zebra_read_pw_status_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id,
- struct zapi_pw_status *pw);
+extern void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw);
extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *);
extern int zclient_send_rnh(struct zclient *zclient, int command,
diff --git a/lib/zebra.h b/lib/zebra.h
index b96fb5a206..3e1eefdb2e 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -335,45 +335,6 @@ struct in_pktinfo {
#endif /* ndef BYTE_ORDER */
-/* MAX / MIN are not commonly defined, but useful */
-/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */
-#ifdef MAX
-#undef MAX
-#endif
-#define MAX(a, b) \
- ({ \
- typeof(a) _max_a = (a); \
- typeof(b) _max_b = (b); \
- _max_a > _max_b ? _max_a : _max_b; \
- })
-#ifdef MIN
-#undef MIN
-#endif
-#define MIN(a, b) \
- ({ \
- typeof(a) _min_a = (a); \
- typeof(b) _min_b = (b); \
- _min_a < _min_b ? _min_a : _min_b; \
- })
-
-#ifndef offsetof
-#ifdef __compiler_offsetof
-#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
-#else
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-#endif
-
-#ifndef container_of
-#define container_of(ptr, type, member) \
- ({ \
- const typeof(((type *)0)->member) *__mptr = (ptr); \
- (type *)((char *)__mptr - offsetof(type, member)); \
- })
-#endif
-
-#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0]))
-
/* For old definition. */
#ifndef IN6_ARE_ADDR_EQUAL
#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL
@@ -461,7 +422,13 @@ extern const char *zserv_command_string(unsigned int command);
#endif
/* Address family numbers from RFC1700. */
-typedef enum { AFI_IP = 1, AFI_IP6 = 2, AFI_L2VPN = 3, AFI_MAX = 4 } afi_t;
+typedef enum {
+ AFI_UNSPEC = 0,
+ AFI_IP = 1,
+ AFI_IP6 = 2,
+ AFI_L2VPN = 3,
+ AFI_MAX = 4
+} afi_t;
/* Subsequent Address Family Identifier. */
typedef enum {
diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4
new file mode 100644
index 0000000000..9feb352255
--- /dev/null
+++ b/m4/ax_lua.m4
@@ -0,0 +1,664 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_lua.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
+# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
+# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
+# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
+#
+# DESCRIPTION
+#
+# Detect a Lua interpreter, optionally specifying a minimum and maximum
+# version number. Set up important Lua paths, such as the directories in
+# which to install scripts and modules (shared libraries).
+#
+# Also detect Lua headers and libraries. The Lua version contained in the
+# header is checked to match the Lua interpreter version exactly. When
+# searching for Lua libraries, the version number is used as a suffix.
+# This is done with the goal of supporting multiple Lua installs (5.1,
+# 5.2, and 5.3 side-by-side).
+#
+# A note on compatibility with previous versions: This file has been
+# mostly rewritten for serial 18. Most developers should be able to use
+# these macros without needing to modify configure.ac. Care has been taken
+# to preserve each macro's behavior, but there are some differences:
+#
+# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
+# AX_PROG_LUA with no arguments.
+#
+# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
+# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
+# unnecessary, so it is deprecated and does not expand to anything.
+#
+# 3) The configure flag --with-lua-suffix no longer exists; the user
+# should instead specify the LUA precious variable on the command line.
+# See the AX_PROG_LUA description for details.
+#
+# Please read the macro descriptions below for more information.
+#
+# This file was inspired by Andrew Dalke's and James Henstridge's
+# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
+# (serial 17). Basically, this file is a mash-up of those two files. I
+# like to think it combines the best of the two!
+#
+# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
+# paths. Adds precious variable LUA, which may contain the path of the Lua
+# interpreter. If LUA is blank, the user's path is searched for an
+# suitable interpreter.
+#
+# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
+# version number greater or equal to MINIMUM-VERSION will be accepted. If
+# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
+# version number greater or equal to MINIMUM-VERSION and less than
+# TOO-BIG-VERSION will be accepted.
+#
+# The Lua version number, LUA_VERSION, is found from the interpreter, and
+# substituted. LUA_PLATFORM is also found, but not currently supported (no
+# standard representation).
+#
+# Finally, the macro finds four paths:
+#
+# luadir Directory to install Lua scripts.
+# pkgluadir $luadir/$PACKAGE
+# luaexecdir Directory to install Lua modules.
+# pkgluaexecdir $luaexecdir/$PACKAGE
+#
+# These paths are found based on $prefix, $exec_prefix, Lua's
+# package.path, and package.cpath. The first path of package.path
+# beginning with $prefix is selected as luadir. The first path of
+# package.cpath beginning with $exec_prefix is used as luaexecdir. This
+# should work on all reasonable Lua installations. If a path cannot be
+# determined, a default path is used. Of course, the user can override
+# these later when invoking make.
+#
+# luadir Default: $prefix/share/lua/$LUA_VERSION
+# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
+#
+# These directories can be used by Automake as install destinations. The
+# variable name minus 'dir' needs to be used as a prefix to the
+# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
+#
+# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
+# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
+# FOUND is blank, then it will default to printing an error. To prevent
+# the default behavior, give ':' as an action.
+#
+# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
+# expanded before this macro. Adds precious variable LUA_INCLUDE, which
+# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
+# LUA_INCLUDE is blank, then this macro will attempt to find suitable
+# flags.
+#
+# LUA_INCLUDE can be used by Automake to compile Lua modules or
+# executables with embedded interpreters. The *_CPPFLAGS variables should
+# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
+#
+# This macro searches for the header lua.h (and others). The search is
+# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
+# If the search is unsuccessful, then some common directories are tried.
+# If the headers are then found, then LUA_INCLUDE is set accordingly.
+#
+# The paths automatically searched are:
+#
+# * /usr/include/luaX.Y
+# * /usr/include/lua/X.Y
+# * /usr/include/luaXY
+# * /usr/local/include/luaX.Y
+# * /usr/local/include/lua-X.Y
+# * /usr/local/include/lua/X.Y
+# * /usr/local/include/luaXY
+#
+# (Where X.Y is the Lua version number, e.g. 5.1.)
+#
+# The Lua version number found in the headers is always checked to match
+# the Lua interpreter's version number. Lua headers with mismatched
+# version numbers are not accepted.
+#
+# If headers are found, then ACTION-IF-FOUND is performed, otherwise
+# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
+# it will default to printing an error. To prevent the default behavior,
+# set the action to ':'.
+#
+# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
+# expanded before this macro. Adds precious variable LUA_LIB, which may
+# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
+# then this macro will attempt to find suitable flags.
+#
+# LUA_LIB can be used by Automake to link Lua modules or executables with
+# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
+# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
+#
+# This macro searches for the Lua library. More technically, it searches
+# for a library containing the function lua_load. The search is performed
+# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
+#
+# If the search determines that some linker flags are missing, then those
+# flags will be added to LUA_LIB.
+#
+# If libraries are found, then ACTION-IF-FOUND is performed, otherwise
+# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
+# it will default to printing an error. To prevent the default behavior,
+# set the action to ':'.
+#
+# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
+# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
+# Autoconf Archive.
+#
+# If a readline compatible library is found, then ACTION-IF-FOUND is
+# performed, otherwise ACTION-IF-NOT-FOUND is performed.
+#
+# LICENSE
+#
+# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
+# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 39
+
+dnl =========================================================================
+dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_PROG_LUA],
+[
+ dnl Check for required tools.
+ AC_REQUIRE([AC_PROG_GREP])
+ AC_REQUIRE([AC_PROG_SED])
+
+ dnl Make LUA a precious variable.
+ AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
+
+ dnl Find a Lua interpreter.
+ m4_define_default([_AX_LUA_INTERPRETER_LIST],
+ [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
+
+ m4_if([$1], [],
+ [ dnl No version check is needed. Find any Lua interpreter.
+ AS_IF([test "x$LUA" = 'x'],
+ [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
+ ax_display_LUA='lua'
+
+ AS_IF([test "x$LUA" != 'x:'],
+ [ dnl At least check if this is a Lua interpreter.
+ AC_MSG_CHECKING([if $LUA is a Lua interpreter])
+ _AX_LUA_CHK_IS_INTRP([$LUA],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([not a Lua interpreter])
+ ])
+ ])
+ ],
+ [ dnl A version check is needed.
+ AS_IF([test "x$LUA" != 'x'],
+ [ dnl Check if this is a Lua interpreter.
+ AC_MSG_CHECKING([if $LUA is a Lua interpreter])
+ _AX_LUA_CHK_IS_INTRP([$LUA],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([not a Lua interpreter])
+ ])
+ dnl Check the version.
+ m4_if([$2], [],
+ [_ax_check_text="whether $LUA version >= $1"],
+ [_ax_check_text="whether $LUA version >= $1, < $2"])
+ AC_MSG_CHECKING([$_ax_check_text])
+ _AX_LUA_CHK_VER([$LUA], [$1], [$2],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([version is out of range for specified LUA])])
+ ax_display_LUA=$LUA
+ ],
+ [ dnl Try each interpreter until we find one that satisfies VERSION.
+ m4_if([$2], [],
+ [_ax_check_text="for a Lua interpreter with version >= $1"],
+ [_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
+ AC_CACHE_CHECK([$_ax_check_text],
+ [ax_cv_pathless_LUA],
+ [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
+ test "x$ax_cv_pathless_LUA" = 'xnone' && break
+ _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
+ _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
+ done
+ ])
+ dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
+ AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
+ [LUA=':'],
+ [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
+ ax_display_LUA=$ax_cv_pathless_LUA
+ ])
+ ])
+
+ AS_IF([test "x$LUA" = 'x:'],
+ [ dnl Run any user-specified action, or abort.
+ m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
+ ],
+ [ dnl Query Lua for its version number.
+ AC_CACHE_CHECK([for $ax_display_LUA version],
+ [ax_cv_lua_version],
+ [ dnl Get the interpreter version in X.Y format. This should work for
+ dnl interpreters version 5.0 and beyond.
+ ax_cv_lua_version=[`$LUA -e '
+ -- return a version number in X.Y format
+ local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
+ print(ver)'`]
+ ])
+ AS_IF([test "x$ax_cv_lua_version" = 'x'],
+ [AC_MSG_ERROR([invalid Lua version number])])
+ AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
+ AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
+
+ dnl The following check is not supported:
+ dnl At times (like when building shared libraries) you may want to know
+ dnl which OS platform Lua thinks this is.
+ AC_CACHE_CHECK([for $ax_display_LUA platform],
+ [ax_cv_lua_platform],
+ [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]])
+ AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
+
+ dnl Use the values of $prefix and $exec_prefix for the corresponding
+ dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
+ dnl variables so they can be overridden if need be. However, the general
+ dnl consensus is that you shouldn't need this ability.
+ AC_SUBST([LUA_PREFIX], ['${prefix}'])
+ AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
+
+ dnl Lua provides no way to query the script directory, and instead
+ dnl provides LUA_PATH. However, we should be able to make a safe educated
+ dnl guess. If the built-in search path contains a directory which is
+ dnl prefixed by $prefix, then we can store scripts there. The first
+ dnl matching path will be used.
+ AC_CACHE_CHECK([for $ax_display_LUA script directory],
+ [ax_cv_lua_luadir],
+ [ AS_IF([test "x$prefix" = 'xNONE'],
+ [ax_lua_prefix=$ac_default_prefix],
+ [ax_lua_prefix=$prefix])
+
+ dnl Initialize to the default path.
+ ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
+
+ dnl Try to find a path with the prefix.
+ _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
+ AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
+ [ dnl Fix the prefix.
+ _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
+ ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
+ $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"`
+ ])
+ ])
+ AC_SUBST([luadir], [$ax_cv_lua_luadir])
+ AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
+
+ dnl Lua provides no way to query the module directory, and instead
+ dnl provides LUA_PATH. However, we should be able to make a safe educated
+ dnl guess. If the built-in search path contains a directory which is
+ dnl prefixed by $exec_prefix, then we can store modules there. The first
+ dnl matching path will be used.
+ AC_CACHE_CHECK([for $ax_display_LUA module directory],
+ [ax_cv_lua_luaexecdir],
+ [ AS_IF([test "x$exec_prefix" = 'xNONE'],
+ [ax_lua_exec_prefix=$ax_lua_prefix],
+ [ax_lua_exec_prefix=$exec_prefix])
+
+ dnl Initialize to the default path.
+ ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
+
+ dnl Try to find a path with the prefix.
+ _AX_LUA_FND_PRFX_PTH([$LUA],
+ [$ax_lua_exec_prefix], [module])
+ AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
+ [ dnl Fix the prefix.
+ _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
+ ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
+ $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"`
+ ])
+ ])
+ AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
+ AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
+
+ dnl Run any user specified action.
+ $3
+ ])
+])
+
+dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
+AC_DEFUN([AX_WITH_LUA],
+[
+ AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]])
+ AX_PROG_LUA
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
+[
+ dnl A minimal Lua factorial to prove this is an interpreter. This should work
+ dnl for Lua interpreters version 5.0 and beyond.
+ _ax_lua_factorial=[`$1 2>/dev/null -e '
+ -- a simple factorial
+ function fact (n)
+ if n == 0 then
+ return 1
+ else
+ return n * fact(n-1)
+ end
+ end
+ print("fact(5) is " .. fact(5))'`]
+ AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'],
+ [$2], [$3])
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
+dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_CHK_VER],
+[
+ dnl Check that the Lua version is within the bounds. Only the major and minor
+ dnl version numbers are considered. This should work for Lua interpreters
+ dnl version 5.0 and beyond.
+ _ax_lua_good_version=[`$1 -e '
+ -- a script to compare versions
+ function verstr2num(verstr)
+ local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)")
+ if majorver and minorver then
+ return tonumber(majorver) * 100 + tonumber(minorver)
+ end
+ end
+ local minver = verstr2num("$2")
+ local _, _, trimver = string.find(_VERSION, "^Lua (.*)")
+ local ver = verstr2num(trimver)
+ local maxver = verstr2num("$3") or 1e9
+ if minver <= ver and ver < maxver then
+ print("yes")
+ else
+ print("no")
+ end'`]
+ AS_IF([test "x$_ax_lua_good_version" = "xyes"],
+ [$4], [$5])
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR)
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
+[
+ dnl Get the script or module directory by querying the Lua interpreter,
+ dnl filtering on the given prefix, and selecting the shallowest path. If no
+ dnl path is found matching the prefix, the result will be an empty string.
+ dnl The third argument determines the type of search, it can be 'script' or
+ dnl 'module'. Supplying 'script' will perform the search with package.path
+ dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
+ dnl LUA_CPATH. This is done for compatibility with Lua 5.0.
+
+ ax_lua_prefixed_path=[`$1 -e '
+ -- get the path based on search type
+ local searchtype = "$3"
+ local paths = ""
+ if searchtype == "script" then
+ paths = (package and package.path) or LUA_PATH
+ elseif searchtype == "module" then
+ paths = (package and package.cpath) or LUA_CPATH
+ end
+ -- search for the prefix
+ local prefix = "'$2'"
+ local minpath = ""
+ local mindepth = 1e9
+ string.gsub(paths, "(@<:@^;@:>@+)",
+ function (path)
+ path = string.gsub(path, "%?.*$", "")
+ path = string.gsub(path, "/@<:@^/@:>@*$", "")
+ if string.find(path, prefix) then
+ local depth = string.len(string.gsub(path, "@<:@^/@:>@", ""))
+ if depth < mindepth then
+ minpath = path
+ mindepth = depth
+ end
+ end
+ end)
+ print(minpath)'`]
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_HEADERS],
+[
+ dnl Check for LUA_VERSION.
+ AC_MSG_CHECKING([if LUA_VERSION is defined])
+ AS_IF([test "x$LUA_VERSION" != 'x'],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
+ ])
+
+ dnl Make LUA_INCLUDE a precious variable.
+ AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
+
+ dnl Some default directories to search.
+ LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
+ m4_define_default([_AX_LUA_INCLUDE_LIST],
+ [ /usr/include/lua$LUA_VERSION \
+ /usr/include/lua-$LUA_VERSION \
+ /usr/include/lua/$LUA_VERSION \
+ /usr/include/lua$LUA_SHORT_VERSION \
+ /usr/local/include/lua$LUA_VERSION \
+ /usr/local/include/lua-$LUA_VERSION \
+ /usr/local/include/lua/$LUA_VERSION \
+ /usr/local/include/lua$LUA_SHORT_VERSION \
+ ])
+
+ dnl Try to find the headers.
+ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
+ AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+
+ dnl Try some other directories if LUA_INCLUDE was not set.
+ AS_IF([test "x$LUA_INCLUDE" = 'x' &&
+ test "x$ac_cv_header_lua_h" != 'xyes'],
+ [ dnl Try some common include paths.
+ for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
+ test ! -d "$_ax_include_path" && continue
+
+ AC_MSG_CHECKING([for Lua headers in])
+ AC_MSG_RESULT([$_ax_include_path])
+
+ AS_UNSET([ac_cv_header_lua_h])
+ AS_UNSET([ac_cv_header_lualib_h])
+ AS_UNSET([ac_cv_header_lauxlib_h])
+ AS_UNSET([ac_cv_header_luaconf_h])
+
+ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
+ AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+
+ AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
+ [ LUA_INCLUDE="-I$_ax_include_path"
+ break
+ ])
+ done
+ ])
+
+ AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
+ [ dnl Make a program to print LUA_VERSION defined in the header.
+ dnl TODO It would be really nice if we could do this without compiling a
+ dnl program, then it would work when cross compiling. But I'm not sure how
+ dnl to do this reliably. For now, assume versions match when cross compiling.
+
+ AS_IF([test "x$cross_compiling" != 'xyes'],
+ [ AC_CACHE_CHECK([for Lua header version],
+ [ax_cv_lua_header_version],
+ [ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
+ AC_RUN_IFELSE(
+ [ AC_LANG_SOURCE([[
+#include <lua.h>
+#include <stdlib.h>
+#include <stdio.h>
+int main(int argc, char ** argv)
+{
+ if(argc > 1) printf("%s", LUA_VERSION);
+ exit(EXIT_SUCCESS);
+}
+]])
+ ],
+ [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \
+ $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"`
+ ],
+ [ax_cv_lua_header_version='unknown'])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+ ])
+
+ dnl Compare this to the previously found LUA_VERSION.
+ AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
+ AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
+ [ AC_MSG_RESULT([yes])
+ ax_header_version_match='yes'
+ ],
+ [ AC_MSG_RESULT([no])
+ ax_header_version_match='no'
+ ])
+ ],
+ [ AC_MSG_WARN([cross compiling so assuming header version number matches])
+ ax_header_version_match='yes'
+ ])
+ ])
+
+ dnl Was LUA_INCLUDE specified?
+ AS_IF([test "x$ax_header_version_match" != 'xyes' &&
+ test "x$LUA_INCLUDE" != 'x'],
+ [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
+
+ dnl Test the final result and run user code.
+ AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
+ [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
+])
+
+dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
+AC_DEFUN([AX_LUA_HEADERS_VERSION],
+[
+ AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]])
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_LIBS],
+[
+ dnl TODO Should this macro also check various -L flags?
+
+ dnl Check for LUA_VERSION.
+ AC_MSG_CHECKING([if LUA_VERSION is defined])
+ AS_IF([test "x$LUA_VERSION" != 'x'],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
+ ])
+
+ dnl Make LUA_LIB a precious variable.
+ AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
+
+ AS_IF([test "x$LUA_LIB" != 'x'],
+ [ dnl Check that LUA_LIBS works.
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([lua_load], [],
+ [_ax_found_lua_libs='yes'],
+ [_ax_found_lua_libs='no'])
+ LIBS=$_ax_lua_saved_libs
+
+ dnl Check the result.
+ AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
+ [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
+ ],
+ [ dnl First search for extra libs.
+ _ax_lua_extra_libs=''
+
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([exp], [m])
+ AC_SEARCH_LIBS([dlopen], [dl])
+ LIBS=$_ax_lua_saved_libs
+
+ AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
+ test "x$ac_cv_search_exp" != 'xnone required'],
+ [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
+
+ AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
+ test "x$ac_cv_search_dlopen" != 'xnone required'],
+ [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
+
+ dnl Try to find the Lua libs.
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([lua_load],
+ [ lua$LUA_VERSION \
+ lua$LUA_SHORT_VERSION \
+ lua-$LUA_VERSION \
+ lua-$LUA_SHORT_VERSION \
+ lua \
+ ],
+ [_ax_found_lua_libs='yes'],
+ [_ax_found_lua_libs='no'],
+ [$_ax_lua_extra_libs])
+ LIBS=$_ax_lua_saved_libs
+
+ AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+ test "x$ac_cv_search_lua_load" != 'xnone required'],
+ [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
+ ])
+
+ dnl Test the result and run user code.
+ AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
+ [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_READLINE],
+[
+ AX_LIB_READLINE
+ AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
+ test "x$ac_cv_header_readline_history_h" != 'x'],
+ [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
+ $1
+ ],
+ [$2])
+])
diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c
index 5508778243..cc18b36f6a 100644
--- a/nhrpd/nhrp_cache.c
+++ b/nhrpd/nhrp_cache.c
@@ -30,9 +30,9 @@ const char *const nhrp_cache_type_str[] = {
[NHRP_CACHE_LOCAL] = "local",
};
-static unsigned int nhrp_cache_protocol_key(void *peer_data)
+static unsigned int nhrp_cache_protocol_key(const void *peer_data)
{
- struct nhrp_cache *p = peer_data;
+ const struct nhrp_cache *p = peer_data;
return sockunion_hash(&p->remote_addr);
}
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
index b33eaa0478..8f1ba14fe4 100644
--- a/nhrpd/nhrp_interface.c
+++ b/nhrpd/nhrp_interface.c
@@ -296,13 +296,12 @@ void nhrp_interface_update(struct interface *ifp)
}
}
-int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id)
+int nhrp_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
/* read and add the interface in the iflist. */
- ifp = zebra_interface_add_read(client->ibuf, vrf_id);
+ ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
if (ifp == NULL)
return 0;
@@ -315,13 +314,12 @@ int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length,
return 0;
}
-int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id)
+int nhrp_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
- s = client->ibuf;
+ s = zclient->ibuf;
ifp = zebra_interface_state_read(s, vrf_id);
if (ifp == NULL)
return 0;
@@ -335,12 +333,11 @@ int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length,
return 0;
}
-int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id)
+int nhrp_interface_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
- ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
if (ifp == NULL)
return 0;
@@ -350,12 +347,11 @@ int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length,
return 0;
}
-int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id)
+int nhrp_interface_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
- ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
if (ifp == NULL)
return 0;
@@ -364,13 +360,12 @@ int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length,
return 0;
}
-int nhrp_interface_address_add(int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id)
+int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
char buf[PREFIX_STRLEN];
- ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
@@ -383,13 +378,12 @@ int nhrp_interface_address_add(int cmd, struct zclient *client,
return 0;
}
-int nhrp_interface_address_delete(int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id)
+int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
char buf[PREFIX_STRLEN];
- ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
index 9b8599eded..d7c485f0a0 100644
--- a/nhrpd/nhrp_main.c
+++ b/nhrpd/nhrp_main.c
@@ -55,7 +55,7 @@ struct zebra_privs_t nhrpd_privs = {
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
- .cap_num_p = ZEBRA_NUM_OF(_caps_p),
+ .cap_num_p = array_size(_caps_p),
};
static void parse_arguments(int argc, char **argv)
diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
index db2f72ac22..ca309f2506 100644
--- a/nhrpd/nhrp_peer.c
+++ b/nhrpd/nhrp_peer.c
@@ -151,9 +151,9 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
nhrp_peer_unref(p);
}
-static unsigned int nhrp_peer_key(void *peer_data)
+static unsigned int nhrp_peer_key(const void *peer_data)
{
- struct nhrp_peer *p = peer_data;
+ const struct nhrp_peer *p = peer_data;
return sockunion_hash(&p->vc->remote.nbma);
}
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
index dae00bbcea..a788eb2efb 100644
--- a/nhrpd/nhrp_route.c
+++ b/nhrpd/nhrp_route.c
@@ -184,8 +184,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type,
&api);
}
-int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+int nhrp_route_read(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
struct zapi_nexthop *api_nh;
diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c
index f92ea4ac90..605aa34ff9 100644
--- a/nhrpd/nhrp_vc.c
+++ b/nhrpd/nhrp_vc.c
@@ -28,9 +28,9 @@ struct child_sa {
static struct hash *nhrp_vc_hash;
static struct list_head childlist_head[512];
-static unsigned int nhrp_vc_key(void *peer_data)
+static unsigned int nhrp_vc_key(const void *peer_data)
{
- struct nhrp_vc *vc = peer_data;
+ const struct nhrp_vc *vc = peer_data;
return jhash_2words(sockunion_hash(&vc->local.nbma),
sockunion_hash(&vc->remote.nbma), 0);
}
@@ -102,7 +102,7 @@ int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc)
{
char buf[2][SU_ADDRSTRLEN];
struct child_sa *sa = NULL, *lsa;
- uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head);
+ uint32_t child_hash = child_id % array_size(childlist_head);
int abort_migration = 0;
list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry)
@@ -202,7 +202,7 @@ void nhrp_vc_init(void)
size_t i;
nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp, "NHRP VC hash");
- for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++)
+ for (i = 0; i < array_size(childlist_head); i++)
list_init(&childlist_head[i]);
}
@@ -211,7 +211,7 @@ void nhrp_vc_reset(void)
struct child_sa *sa, *n;
size_t i;
- for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) {
+ for (i = 0; i < array_size(childlist_head); i++) {
list_for_each_entry_safe(sa, n, &childlist_head[i],
childlist_entry)
nhrp_vc_ipsec_updown(sa->id, 0);
diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
index 8f1c63457a..89de145e65 100644
--- a/nhrpd/nhrpd.h
+++ b/nhrpd/nhrpd.h
@@ -314,18 +314,12 @@ void nhrp_interface_init(void);
void nhrp_interface_update(struct interface *ifp);
void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi);
-int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id);
-int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id);
-int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id);
-int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length,
- vrf_id_t vrf_id);
-int nhrp_interface_address_add(int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id);
-int nhrp_interface_address_delete(int cmd, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_add(ZAPI_CALLBACK_ARGS);
+int nhrp_interface_delete(ZAPI_CALLBACK_ARGS);
+int nhrp_interface_up(ZAPI_CALLBACK_ARGS);
+int nhrp_interface_down(ZAPI_CALLBACK_ARGS);
+int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS);
+int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS);
void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n,
notifier_fn_t fn);
@@ -349,8 +343,7 @@ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
void nhrp_route_announce(int add, enum nhrp_cache_type type,
const struct prefix *p, struct interface *ifp,
const union sockunion *nexthop, uint32_t mtu);
-int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id);
+int nhrp_route_read(ZAPI_CALLBACK_ARGS);
int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p,
union sockunion *via, struct interface **ifp);
enum nhrp_route_type nhrp_route_address(struct interface *in_ifp,
diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c
index 08a007bdf9..e56bbe3bf7 100644
--- a/nhrpd/reqid.c
+++ b/nhrpd/reqid.c
@@ -2,9 +2,9 @@
#include "hash.h"
#include "nhrpd.h"
-static unsigned int nhrp_reqid_key(void *data)
+static unsigned int nhrp_reqid_key(const void *data)
{
- struct nhrp_reqid *r = data;
+ const struct nhrp_reqid *r = data;
return r->request_id;
}
diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c
index 830f0e1c84..64b16e7ee3 100644
--- a/nhrpd/resolver.c
+++ b/nhrpd/resolver.c
@@ -171,7 +171,7 @@ static void ares_address_cb(void *arg, int status, int timeouts,
return;
}
- for (i = 0; i < ZEBRA_NUM_OF(addr) && he->h_addr_list[i] != NULL; i++) {
+ for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) {
memset(&addr[i], 0, sizeof(addr[i]));
addr[i].sa.sa_family = he->h_addrtype;
switch (he->h_addrtype) {
diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c
index c662295083..7f1475cc69 100644
--- a/nhrpd/zbuf.c
+++ b/nhrpd/zbuf.c
@@ -196,7 +196,7 @@ int zbufq_write(struct zbuf_queue *zbq, int fd)
iov[iovcnt++] = (struct iovec){
.iov_base = zb->head, .iov_len = zbuf_used(zb),
};
- if (iovcnt >= ZEBRA_NUM_OF(iov))
+ if (iovcnt >= array_size(iov))
break;
}
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 2795bb9abd..946bbf8cc9 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -956,7 +956,7 @@ static void ospf6_asbr_routemap_update(const char *mapname)
}
}
-static void ospf6_asbr_routemap_event(route_map_event_t event, const char *name)
+static void ospf6_asbr_routemap_event(const char *name)
{
int type;
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
index e7284a6659..f0500601b0 100644
--- a/ospf6d/ospf6_bfd.c
+++ b/ospf6d/ospf6_bfd.c
@@ -74,6 +74,7 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command)
struct interface *ifp = oi->interface;
struct bfd_info *bfd_info;
char src[64];
+ int cbit;
if (!oi->bfd_info || !on->bfd_info)
return;
@@ -85,9 +86,11 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command)
bfd_get_command_dbg_str(command), src);
}
+ cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
+
bfd_peer_sendmsg(zclient, bfd_info, AF_INET6, &on->linklocal_addr,
- on->ospf6_if->linklocal_addr, ifp->name, 0, 0, command,
- 0, VRF_DEFAULT);
+ on->ospf6_if->linklocal_addr, ifp->name, 0, 0,
+ cbit, command, 0, VRF_DEFAULT);
if (command == ZEBRA_BFD_DEST_DEREGISTER)
bfd_info_free((struct bfd_info **)&on->bfd_info);
@@ -138,8 +141,7 @@ static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, int command)
* ospf6_bfd_nbr_replay - Replay all the neighbors that have BFD enabled
* to zebra
*/
-static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct listnode *node;
@@ -152,7 +154,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient,
zlog_debug("Zebra: BFD Dest replay request");
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
/* Replay the neighbor, if BFD is enabled on the interface*/
FOR_ALL_INTERFACES (vrf, ifp) {
@@ -182,8 +184,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient,
* has changed and bring down the neighbor
* connectivity if BFD down is received.
*/
-static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct ospf6_interface *oi;
@@ -197,7 +198,8 @@ static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient,
struct bfd_info *bfd_info;
struct timeval tv;
- ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id);
+ ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status,
+ NULL, vrf_id);
if ((ifp == NULL) || (dp.family != AF_INET6))
return 0;
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index f08426fb47..aa4a995173 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -27,7 +27,6 @@
#include "command.h"
#include "vty.h"
#include "prefix.h"
-#include "pqueue.h"
#include "linklist.h"
#include "thread.h"
#include "lib_errors.h"
@@ -76,16 +75,18 @@ static unsigned int ospf6_spf_get_ifindex_from_nh(struct ospf6_vertex *v)
return 0;
}
-static int ospf6_vertex_cmp(void *a, void *b)
+static int ospf6_vertex_cmp(const struct ospf6_vertex *va,
+ const struct ospf6_vertex *vb)
{
- struct ospf6_vertex *va = (struct ospf6_vertex *)a;
- struct ospf6_vertex *vb = (struct ospf6_vertex *)b;
-
/* ascending order */
if (va->cost != vb->cost)
return (va->cost - vb->cost);
- return (va->hops - vb->hops);
+ if (va->hops != vb->hops)
+ return (va->hops - vb->hops);
+ return 0;
}
+DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct ospf6_vertex, pqi,
+ ospf6_vertex_cmp)
static int ospf6_vertex_id_cmp(void *a, void *b)
{
@@ -461,7 +462,7 @@ void ospf6_spf_calculation(uint32_t router_id,
struct ospf6_route_table *result_table,
struct ospf6_area *oa)
{
- struct pqueue *candidate_list;
+ struct vertex_pqueue_head candidate_list;
struct ospf6_vertex *root, *v, *w;
int size;
caddr_t lsdesc;
@@ -481,8 +482,7 @@ void ospf6_spf_calculation(uint32_t router_id,
}
/* initialize */
- candidate_list = pqueue_create();
- candidate_list->cmp = ospf6_vertex_cmp;
+ vertex_pqueue_init(&candidate_list);
root = ospf6_vertex_create(lsa);
root->area = oa;
@@ -492,13 +492,10 @@ void ospf6_spf_calculation(uint32_t router_id,
inet_pton(AF_INET6, "::1", &address);
/* Actually insert root to the candidate-list as the only candidate */
- pqueue_enqueue(root, candidate_list);
+ vertex_pqueue_add(&candidate_list, root);
/* Iterate until candidate-list becomes empty */
- while (candidate_list->size) {
- /* get closest candidate from priority queue */
- v = pqueue_dequeue(candidate_list);
-
+ while ((v = vertex_pqueue_pop(&candidate_list))) {
/* installing may result in merging or rejecting of the vertex
*/
if (ospf6_spf_install(v, result_table) < 0)
@@ -557,12 +554,11 @@ void ospf6_spf_calculation(uint32_t router_id,
zlog_debug(
" New candidate: %s hops %d cost %d",
w->name, w->hops, w->cost);
- pqueue_enqueue(w, candidate_list);
+ vertex_pqueue_add(&candidate_list, w);
}
}
-
- pqueue_delete(candidate_list);
+ //vertex_pqueue_fini(&candidate_list);
ospf6_remove_temp_router_lsa(oa);
diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h
index da95ec80a3..a387d40a57 100644
--- a/ospf6d/ospf6_spf.h
+++ b/ospf6d/ospf6_spf.h
@@ -21,6 +21,7 @@
#ifndef OSPF6_SPF_H
#define OSPF6_SPF_H
+#include "typesafe.h"
#include "ospf6_top.h"
/* Debug option */
@@ -33,6 +34,7 @@ extern unsigned char conf_debug_ospf6_spf;
#define IS_OSPF6_DEBUG_SPF(level) \
(conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level)
+PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue)
/* Transit Vertex */
struct ospf6_vertex {
/* type of this vertex */
@@ -41,6 +43,8 @@ struct ospf6_vertex {
/* Vertex Identifier */
struct prefix vertex_id;
+ struct vertex_pqueue_item pqi;
+
/* Identifier String */
char name[128];
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index abdc82a738..af16c5aa7c 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -48,8 +48,7 @@ unsigned char conf_debug_ospf6_zebra = 0;
struct zclient *zclient = NULL;
/* Router-id update message from zebra. */
-static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct prefix router_id;
struct ospf6 *o = ospf6;
@@ -99,8 +98,7 @@ void ospf6_zebra_no_redistribute(int type)
}
/* Inteface addition message from zebra. */
-static int ospf6_zebra_if_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_zebra_if_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -112,8 +110,7 @@ static int ospf6_zebra_if_add(int command, struct zclient *zclient,
return 0;
}
-static int ospf6_zebra_if_del(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_zebra_if_del(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -132,8 +129,7 @@ static int ospf6_zebra_if_del(int command, struct zclient *zclient,
return 0;
}
-static int ospf6_zebra_if_state_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_zebra_if_state_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -152,10 +148,7 @@ static int ospf6_zebra_if_state_update(int command, struct zclient *zclient,
return 0;
}
-static int ospf6_zebra_if_address_update_add(int command,
- struct zclient *zclient,
- zebra_size_t length,
- vrf_id_t vrf_id)
+static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
char buf[128];
@@ -179,10 +172,7 @@ static int ospf6_zebra_if_address_update_add(int command,
return 0;
}
-static int ospf6_zebra_if_address_update_delete(int command,
- struct zclient *zclient,
- zebra_size_t length,
- vrf_id_t vrf_id)
+static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
char buf[128];
@@ -209,8 +199,7 @@ static int ospf6_zebra_if_address_update_delete(int command,
return 0;
}
-static int ospf6_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
unsigned long ifindex;
@@ -240,13 +229,13 @@ static int ospf6_zebra_read_route(int command, struct zclient *zclient,
zlog_debug(
"Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %" ROUTE_TAG_PRI,
- (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add"
- : "delete"),
+ (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add"
+ : "delete"),
zebra_route_string(api.type), prefixstr, nexthopstr,
ifindex, api.tag);
}
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix,
api.nexthop_num, nexthop, api.tag);
else
@@ -582,7 +571,7 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or)
static void ospf6_zebra_connected(struct zclient *zclient)
{
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c
index 594735a08f..a17975270a 100644
--- a/ospfd/ospf_bfd.c
+++ b/ospfd/ospf_bfd.c
@@ -65,6 +65,7 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command)
struct interface *ifp = oi->ifp;
struct ospf_if_params *params;
struct bfd_info *bfd_info;
+ int cbit;
/* Check if BFD is enabled */
params = IF_DEF_PARAMS(ifp);
@@ -80,8 +81,10 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command)
inet_ntoa(nbr->src),
ospf_vrf_id_to_name(oi->ospf->vrf_id));
+ cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
+
bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->src, NULL, ifp->name,
- 0, 0, command, 0, oi->ospf->vrf_id);
+ 0, 0, cbit, command, 0, oi->ospf->vrf_id);
}
/*
@@ -141,8 +144,7 @@ static int ospf_bfd_reg_dereg_all_nbr(struct interface *ifp, int command)
* ospf_bfd_nbr_replay - Replay all the neighbors that have BFD enabled
* to zebra
*/
-static int ospf_bfd_nbr_replay(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
{
struct listnode *inode, *node, *onode;
struct ospf *ospf;
@@ -157,7 +159,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient,
}
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
/* Replay the neighbor, if BFD is enabled in OSPF */
for (ALL_LIST_ELEMENTS(om->ospf, node, onode, ospf)) {
@@ -195,8 +197,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient,
* connectivity if the BFD status changed to
* down.
*/
-static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct ospf_interface *oi;
@@ -209,7 +210,8 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient,
struct bfd_info *bfd_info;
struct timeval tv;
- ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id);
+ ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status,
+ NULL, vrf_id);
if ((ifp == NULL) || (p.family != AF_INET))
return 0;
@@ -251,6 +253,13 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient,
OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer);
}
+ if ((status == BFD_STATUS_UP)
+ && (old_status == BFD_STATUS_DOWN)) {
+ if (IS_DEBUG_OSPF(nsm, NSM_EVENTS))
+ zlog_debug("NSM[%s:%s]: BFD Up",
+ IF_NAME(nbr->oi),
+ inet_ntoa(nbr->address.u.prefix4));
+ }
}
return 0;
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index a381cf7145..5e3dabc27a 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -69,6 +69,8 @@ struct lsa_header {
uint16_t length;
};
+struct vertex;
+
/* OSPF LSA. */
struct ospf_lsa {
/* LSA origination flag. */
@@ -95,10 +97,7 @@ struct ospf_lsa {
int lock;
/* Flags for the SPF calculation. */
- int stat;
-#define LSA_SPF_NOT_EXPLORED -1
-#define LSA_SPF_IN_SPFTREE -2
- /* If stat >= 0, stat is LSA position in candidates heap. */
+ struct vertex *stat;
/* References to this LSA in neighbor retransmission lists*/
int retransmit_counter;
diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c
index 2e850c4e26..86eb141312 100644
--- a/ospfd/ospf_lsdb.c
+++ b/ospfd/ospf_lsdb.c
@@ -169,21 +169,6 @@ void ospf_lsdb_delete_all(struct ospf_lsdb *lsdb)
}
}
-void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb)
-{
- struct route_table *table;
- struct route_node *rn;
- struct ospf_lsa *lsa;
- int i;
-
- for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) {
- table = lsdb->type[i].db;
- for (rn = route_top(table); rn; rn = route_next(rn))
- if ((lsa = (rn->info)) != NULL)
- lsa->stat = LSA_SPF_NOT_EXPLORED;
- }
-}
-
struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
{
struct route_table *table;
diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h
index 65c7e28fed..5cf5d05449 100644
--- a/ospfd/ospf_lsdb.h
+++ b/ospfd/ospf_lsdb.h
@@ -67,8 +67,6 @@ extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa);
extern void ospf_lsdb_add(struct ospf_lsdb *, struct ospf_lsa *);
extern void ospf_lsdb_delete(struct ospf_lsdb *, struct ospf_lsa *);
extern void ospf_lsdb_delete_all(struct ospf_lsdb *);
-/* Set all stats to -1 (LSA_SPF_NOT_EXPLORED). */
-extern void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb);
extern struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *, struct ospf_lsa *);
extern struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *, uint8_t,
struct in_addr, struct in_addr);
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 43c5e338b0..6bc8c25153 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -2107,7 +2107,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
dump_lsa_key(lsa));
DISCARD_LSA(lsa, 4);
- continue;
}
/* Actual flooding procedure. */
diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c
index 30b2a50bb3..ab2d5ae584 100644
--- a/ospfd/ospf_routemap.c
+++ b/ospfd/ospf_routemap.c
@@ -97,7 +97,7 @@ static void ospf_route_map_update(const char *name)
}
}
-static void ospf_route_map_event(route_map_event_t event, const char *name)
+static void ospf_route_map_event(const char *name)
{
struct ospf *ospf;
int type;
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 6e03fa9bde..296a05bdf1 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -30,7 +30,6 @@
#include "table.h"
#include "log.h"
#include "sockunion.h" /* for inet_ntop () */
-#include "pqueue.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
@@ -53,6 +52,11 @@
static unsigned int spf_reason_flags = 0;
+/* dummy vertex to flag "in spftree" */
+static const struct vertex vertex_in_spftree = {};
+#define LSA_SPF_IN_SPFTREE (struct vertex *)&vertex_in_spftree
+#define LSA_SPF_NOT_EXPLORED NULL
+
static void ospf_clear_spf_reason_flags(void)
{
spf_reason_flags = 0;
@@ -72,35 +76,36 @@ static struct list vertex_list = {.del = ospf_vertex_free};
/* Heap related functions, for the managment of the candidates, to
* be used with pqueue. */
-static int cmp(void *node1, void *node2)
+static int vertex_cmp(const struct vertex *v1, const struct vertex *v2)
{
- struct vertex *v1 = (struct vertex *)node1;
- struct vertex *v2 = (struct vertex *)node2;
- if (v1 != NULL && v2 != NULL) {
- /* network vertices must be chosen before router vertices of
- * same
- * cost in order to find all shortest paths
- */
- if (((v1->distance - v2->distance) == 0)
- && (v1->type != v2->type)) {
- switch (v1->type) {
- case OSPF_VERTEX_NETWORK:
- return -1;
- case OSPF_VERTEX_ROUTER:
- return 1;
- }
- } else
- return (v1->distance - v2->distance);
+ if (v1->distance != v2->distance)
+ return v1->distance - v2->distance;
+
+ if (v1->type != v2->type) {
+ switch (v1->type) {
+ case OSPF_VERTEX_NETWORK:
+ return -1;
+ case OSPF_VERTEX_ROUTER:
+ return 1;
+ }
}
return 0;
}
+DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct vertex, pqi, vertex_cmp)
-static void update_stat(void *node, int position)
+static void lsdb_clean_stat(struct ospf_lsdb *lsdb)
{
- struct vertex *v = node;
-
- /* Set the status of the vertex, when its position changes. */
- *(v->stat) = position;
+ struct route_table *table;
+ struct route_node *rn;
+ struct ospf_lsa *lsa;
+ int i;
+
+ for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) {
+ table = lsdb->type[i].db;
+ for (rn = route_top(table); rn; rn = route_next(rn))
+ if ((lsa = (rn->info)) != NULL)
+ lsa->stat = LSA_SPF_NOT_EXPLORED;
+ }
}
static struct vertex_nexthop *vertex_nexthop_new(void)
@@ -179,7 +184,6 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa)
new = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex));
new->flags = 0;
- new->stat = &(lsa->stat);
new->type = lsa->data->type;
new->id = lsa->data->id;
new->lsa = lsa->data;
@@ -187,6 +191,9 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa)
new->parents = list_new();
new->parents->del = vertex_parent_free;
new->parents->cmp = vertex_parent_cmp;
+ new->lsa_p = lsa;
+
+ lsa->stat = new;
listnode_add(&vertex_list, new);
@@ -786,7 +793,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
* path is found to a vertex already on the candidate list, store the new cost.
*/
static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
- struct ospf_area *area, struct pqueue *candidate)
+ struct ospf_area *area,
+ struct vertex_pqueue_head *candidate)
{
struct ospf_lsa *w_lsa = NULL;
uint8_t *p;
@@ -935,13 +943,11 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
/* Calculate nexthop to W. */
if (ospf_nexthop_calculation(area, v, w, l, distance,
lsa_pos))
- pqueue_enqueue(w, candidate);
+ vertex_pqueue_add(candidate, w);
else if (IS_DEBUG_OSPF_EVENT)
zlog_debug("Nexthop Calc failed");
- } else if (w_lsa->stat >= 0) {
- /* Get the vertex from candidates. */
- w = candidate->array[w_lsa->stat];
-
+ } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) {
+ w = w_lsa->stat;
/* if D is greater than. */
if (w->distance < distance) {
continue;
@@ -962,18 +968,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
* which
* will flush the old parents
*/
- if (ospf_nexthop_calculation(area, v, w, l,
- distance, lsa_pos))
- /* Decrease the key of the node in the
- * heap.
- * trickle-sort it up towards root, just
- * in case this
- * node should now be the new root due
- * the cost change.
- * (next pqueu_{de,en}queue will fully
- * re-heap the queue).
- */
- trickle_up(w_lsa->stat, candidate);
+ vertex_pqueue_del(candidate, w);
+ ospf_nexthop_calculation(area, v, w, l,
+ distance, lsa_pos);
+ vertex_pqueue_add(candidate, w);
}
} /* end W is already on the candidate list */
} /* end loop over the links in V's LSA */
@@ -1169,7 +1167,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table,
struct route_table *new_rtrs)
{
- struct pqueue *candidate;
+ struct vertex_pqueue_head candidate;
struct vertex *v;
if (IS_DEBUG_OSPF_EVENT) {
@@ -1194,11 +1192,9 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
/* This function scans all the LSA database and set the stat field to
* LSA_SPF_NOT_EXPLORED. */
- ospf_lsdb_clean_stat(area->lsdb);
+ lsdb_clean_stat(area->lsdb);
/* Create a new heap for the candidates. */
- candidate = pqueue_create();
- candidate->cmp = cmp;
- candidate->update = update_stat;
+ vertex_pqueue_init(&candidate);
/* Initialize the shortest-path tree to only the root (which is the
router doing the calculation). */
@@ -1207,7 +1203,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
/* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of
* the
* spanning tree. */
- *(v->stat) = LSA_SPF_IN_SPFTREE;
+ v->lsa_p->stat = LSA_SPF_IN_SPFTREE;
/* Set Area A's TransitCapability to FALSE. */
area->transit = OSPF_TRANSIT_FALSE;
@@ -1215,23 +1211,22 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
for (;;) {
/* RFC2328 16.1. (2). */
- ospf_spf_next(v, ospf, area, candidate);
+ ospf_spf_next(v, ospf, area, &candidate);
/* RFC2328 16.1. (3). */
/* If at this step the candidate list is empty, the shortest-
path tree (of transit vertices) has been completely built and
this stage of the procedure terminates. */
- if (candidate->size == 0)
- break;
-
/* Otherwise, choose the vertex belonging to the candidate list
that is closest to the root, and add it to the shortest-path
tree (removing it from the candidate list in the
process). */
/* Extract from the candidates the node with the lower key. */
- v = (struct vertex *)pqueue_dequeue(candidate);
+ v = vertex_pqueue_pop(&candidate);
+ if (!v)
+ break;
/* Update stat field in vertex. */
- *(v->stat) = LSA_SPF_IN_SPFTREE;
+ v->lsa_p->stat = LSA_SPF_IN_SPFTREE;
ospf_vertex_add_parent(v);
@@ -1255,7 +1250,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
ospf_spf_process_stubs(area, area->spf, new_table, 0);
/* Free candidate queue. */
- pqueue_delete(candidate);
+ //vertex_pqueue_fini(&candidate);
ospf_vertex_dump(__func__, area->spf, 0, 1);
/* Free nexthop information, canonical versions of which are attached
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 85f42bcd18..09a0b6f1b7 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -22,6 +22,8 @@
#ifndef _QUAGGA_OSPF_SPF_H
#define _QUAGGA_OSPF_SPF_H
+#include "typesafe.h"
+
/* values for vertex->type */
#define OSPF_VERTEX_ROUTER 1 /* for a Router-LSA */
#define OSPF_VERTEX_NETWORK 2 /* for a Network-LSA */
@@ -31,13 +33,15 @@
/* The "root" is the node running the SPF calculation */
+PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue)
/* A router or network in an area */
struct vertex {
+ struct vertex_pqueue_item pqi;
uint8_t flags;
uint8_t type; /* copied from LSA header */
struct in_addr id; /* copied from LSA header */
+ struct ospf_lsa *lsa_p;
struct lsa_header *lsa; /* Router or Network LSA */
- int *stat; /* Link to LSA status. */
uint32_t distance; /* from root to this vertex */
struct list *parents; /* list of parents in SPF tree */
struct list *children; /* list of children in SPF tree*/
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index a493520868..6947393a60 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -86,7 +86,7 @@ static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe);
*/
/* Hash function for Segment Routing entry */
-static unsigned int sr_hash(void *p)
+static unsigned int sr_hash(const void *p)
{
const struct in_addr *rid = p;
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index bd8cbee11a..1488aa88cd 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -397,53 +397,13 @@ static void set_linkparams_link_type(struct ospf_interface *oi,
return;
}
-static void set_linkparams_link_id(struct ospf_interface *oi,
- struct mpls_te_link *lp)
+static void set_linkparams_link_id(struct mpls_te_link *lp,
+ struct in_addr link_id)
{
- struct ospf_neighbor *nbr;
- int done = 0;
lp->link_id.header.type = htons(TE_LINK_SUBTLV_LINK_ID);
lp->link_id.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE);
-
- /*
- * The Link ID is identical to the contents of the Link ID field
- * in the Router LSA for these link types.
- */
- switch (oi->type) {
- case OSPF_IFTYPE_POINTOPOINT:
- /* Take the router ID of the neighbor. */
- if ((nbr = ospf_nbr_lookup_ptop(oi))
- && nbr->state == NSM_Full) {
- lp->link_id.value = nbr->router_id;
- done = 1;
- }
- break;
- case OSPF_IFTYPE_BROADCAST:
- case OSPF_IFTYPE_NBMA:
- /* Take the interface address of the designated router. */
- if ((nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi))) == NULL)
- break;
-
- if (nbr->state == NSM_Full
- || (IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi))
- && ospf_nbr_count(oi, NSM_Full) > 0)) {
- lp->link_id.value = DR(oi);
- done = 1;
- }
- break;
- default:
- /* Not supported yet. */ /* XXX */
- lp->link_id.header.type = htons(0);
- break;
- }
-
- if (!done) {
- struct in_addr mask;
- masklen2ip(oi->address->prefixlen, &mask);
- lp->link_id.value.s_addr =
- oi->address->u.prefix4.s_addr & mask.s_addr;
- }
+ lp->link_id.value = link_id;
return;
}
@@ -958,40 +918,33 @@ void ospf_mpls_te_update_if(struct interface *ifp)
return;
}
+/*
+ * Just add interface and set available information. Other information
+ * and flooding of LSA will be done later when adjacency will be up
+ * See ospf_mpls_te_nsm_change() after
+ */
static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state)
{
- struct te_link_subtlv_link_type old_type;
- struct te_link_subtlv_link_id old_id;
+
struct mpls_te_link *lp;
- if ((lp = lookup_linkparams_by_ifp(oi->ifp)) == NULL) {
+ lp = lookup_linkparams_by_ifp(oi->ifp);
+ if (lp == NULL) {
flog_warn(
EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_ism_change: Cannot get linkparams from OI(%s)?",
- IF_NAME(oi));
+ "MPLS-TE (%s): Cannot get linkparams from OI(%s)?",
+ __func__, IF_NAME(oi));
return;
}
if (oi->area == NULL || oi->area->ospf == NULL) {
flog_warn(
EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?",
- IF_NAME(oi));
+ "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?",
+ __func__, IF_NAME(oi));
return;
}
-#ifdef notyet
- if ((lp->area != NULL
- && !IPV4_ADDR_SAME(&lp->area->area_id, &oi->area->area_id))
- || (lp->area != NULL && oi->area == NULL)) {
- /* How should we consider this case? */
- flog_warn(
- EC_OSPF_TE_UNEXPECTED,
- "MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs",
- IF_NAME(oi),
- oi->area ? inet_ntoa(oi->area->area_id) : "N/A");
- ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
- }
-#endif
+
/* Keep Area information in combination with linkparams. */
lp->area = oi->area;
@@ -1003,55 +956,103 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state)
case ISM_DROther:
case ISM_Backup:
case ISM_DR:
- old_type = lp->link_type;
- old_id = lp->link_id;
-
- /* Set Link type, Link ID, Local and Remote IP addr */
+ /* Set Link type and Local IP addr */
set_linkparams_link_type(oi, lp);
- set_linkparams_link_id(oi, lp);
set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4);
- if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP) {
- struct prefix *pref = CONNECTED_PREFIX(oi->connected);
- if (pref != NULL)
- set_linkparams_rmtif_ipaddr(lp,
- pref->u.prefix4);
- }
-
- /* Update TE parameters */
- update_linkparams(lp);
-
- /* Try to Schedule LSA */
- if ((ntohs(old_type.header.type)
- != ntohs(lp->link_type.header.type)
- || old_type.link_type.value
- != lp->link_type.link_type.value)
- || (ntohs(old_id.header.type)
- != ntohs(lp->link_id.header.type)
- || ntohl(old_id.value.s_addr)
- != ntohl(lp->link_id.value.s_addr))) {
- if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
- ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA);
- else
- ospf_mpls_te_lsa_schedule(lp,
- REORIGINATE_THIS_LSA);
- }
break;
default:
- lp->link_type.header.type = htons(0);
- lp->link_id.header.type = htons(0);
-
+ /* State is undefined: Flush LSA if engaged */
if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
break;
}
+ if (IS_DEBUG_OSPF_TE)
+ zlog_debug(
+ "MPLS-TE(%s): Update Link parameters for interface %s",
+ __func__, IF_NAME(oi));
+
return;
}
+/*
+ * Complete TE info and schedule LSA flooding
+ * Link-ID and Remote IP address must be set with neighbor info
+ * which are only valid once NSM state is FULL
+ */
static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state)
{
- /* Nothing to do here */
+ 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)
+ return;
+
+ /* Get interface information for Traffic Engineering */
+ lp = lookup_linkparams_by_ifp(oi->ifp);
+ if (lp == NULL) {
+ flog_warn(
+ EC_OSPF_TE_UNEXPECTED,
+ "MPLS-TE (%s): Cannot get linkparams from OI(%s)?",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ if (oi->area == NULL || oi->area->ospf == NULL) {
+ flog_warn(
+ EC_OSPF_TE_UNEXPECTED,
+ "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ /* Keep Area information in combination with SR info. */
+ lp->area = oi->area;
+
+ /* Keep interface MPLS-TE status */
+ lp->flags = HAS_LINK_PARAMS(oi->ifp);
+
+ /*
+ * The Link ID is identical to the contents of the Link ID field
+ * in the Router LSA for these link types.
+ */
+ switch (oi->state) {
+ case ISM_PointToPoint:
+ /* Set Link ID with neighbor Router ID */
+ set_linkparams_link_id(lp, nbr->router_id);
+ /* Set Remote IP address */
+ set_linkparams_rmtif_ipaddr(lp, nbr->address.u.prefix4);
+ break;
+
+ case ISM_DR:
+ case ISM_DROther:
+ case ISM_Backup:
+ /* Set Link ID with the Designated Router ID */
+ set_linkparams_link_id(lp, DR(oi));
+ break;
+
+ default:
+ /* State is undefined: Flush LSA if engaged */
+ if (OspfMplsTE.enabled &&
+ CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
+ ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
+ return;
+ }
+
+ if (IS_DEBUG_OSPF_TE)
+ zlog_debug(
+ "MPLS-TE (%s): Add Link-ID %s for interface %s ",
+ __func__, inet_ntoa(lp->link_id.value), oi->ifp->name);
+
+ /* Try to Schedule LSA */
+ if (OspfMplsTE.enabled) {
+ if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
+ ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA);
+ else
+ ospf_mpls_te_lsa_schedule(lp, REORIGINATE_THIS_LSA);
+ }
return;
}
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 4cbd817ad8..c178e367d3 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -65,8 +65,7 @@ struct zclient *zclient = NULL;
extern struct thread_master *master;
/* Router-id update message from zebra. */
-static int ospf_router_id_update_zebra(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct ospf *ospf = NULL;
struct prefix router_id;
@@ -99,8 +98,7 @@ static int ospf_router_id_update_zebra(int command, struct zclient *zclient,
}
/* Inteface addition message from zebra. */
-static int ospf_interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp = NULL;
struct ospf *ospf = NULL;
@@ -138,8 +136,7 @@ static int ospf_interface_add(int command, struct zclient *zclient,
return 0;
}
-static int ospf_interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -181,8 +178,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s,
return if_lookup_by_name(ifname_tmp, vrf_id);
}
-static int ospf_interface_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_state_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct ospf_interface *oi;
@@ -238,8 +234,7 @@ static int ospf_interface_state_up(int command, struct zclient *zclient,
return 0;
}
-static int ospf_interface_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_state_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct ospf_interface *oi;
@@ -263,14 +258,13 @@ static int ospf_interface_state_down(int command, struct zclient *zclient,
return 0;
}
-static int ospf_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct ospf *ospf = NULL;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (c == NULL)
return 0;
@@ -294,8 +288,7 @@ static int ospf_interface_address_add(int command, struct zclient *zclient,
return 0;
}
-static int ospf_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct interface *ifp;
@@ -303,7 +296,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient,
struct route_node *rn;
struct prefix p;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (c == NULL)
return 0;
@@ -339,8 +332,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int ospf_interface_link_params(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -356,8 +348,7 @@ static int ospf_interface_link_params(int command, struct zclient *zclient,
}
/* VRF update for an interface. */
-static int ospf_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp = NULL;
vrf_id_t new_vrf_id;
@@ -1003,8 +994,7 @@ void ospf_routemap_unset(struct ospf_redist *red)
}
/* Zebra route add and delete treatment. */
-static int ospf_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
struct prefix_ipv4 p;
@@ -1047,7 +1037,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient,
zebra_route_string(api.type), vrf_id, buf_prefix);
}
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
/* XXX|HACK|TODO|FIXME:
* Maybe we should ignore reject/blackhole routes? Testing
* shows that there is no problems though and this is only way
@@ -1108,7 +1098,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient,
}
}
}
- } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */
+ } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */
{
ospf_external_info_delete(ospf, rt_type, api.instance, p);
if (is_prefix_default(&p))
@@ -1575,7 +1565,7 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf)
static void ospf_zebra_connected(struct zclient *zclient)
{
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index 7504752725..52506542bc 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -129,10 +129,10 @@ static void pbr_nh_delete_iterate(struct hash_bucket *b, void *p)
pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
}
-static uint32_t pbr_nh_hash_key(void *arg)
+static uint32_t pbr_nh_hash_key(const void *arg)
{
uint32_t key;
- struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg;
+ const struct pbr_nexthop_cache *pbrnc = arg;
key = nexthop_hash(pbrnc->nexthop);
@@ -789,10 +789,9 @@ void pbr_nht_nexthop_interface_update(struct interface *ifp)
ifp);
}
-static uint32_t pbr_nhg_hash_key(void *arg)
+static uint32_t pbr_nhg_hash_key(const void *arg)
{
- struct pbr_nexthop_group_cache *nhgc =
- (struct pbr_nexthop_group_cache *)arg;
+ const struct pbr_nexthop_group_cache *nhgc = arg;
return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
}
@@ -940,7 +939,7 @@ void pbr_nht_init(void)
pbr_nhg_hash = hash_create_size(
16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
pbr_nhrc_hash =
- hash_create_size(16, (unsigned int (*)(void *))nexthop_hash,
+ hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
pbr_nhrc_hash_equal, "PBR NH Hash");
pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index 4f8f50556b..466a9a13ae 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -59,8 +59,7 @@ struct pbr_interface *pbr_if_new(struct interface *ifp)
}
/* Inteface addition message from zebra. */
-static int interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -80,8 +79,7 @@ static int interface_add(int command, struct zclient *zclient,
return 0;
}
-static int interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -102,28 +100,27 @@ static int interface_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
char buf[PREFIX_STRLEN];
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
DEBUGD(&pbr_dbg_zebra,
- "%s: %s added %s", __PRETTY_FUNCTION__, c->ifp->name,
- prefix2str(c->address, buf, sizeof(buf)));
+ "%s: %s added %s", __PRETTY_FUNCTION__,
+ c ? c->ifp->name : "Unknown",
+ c ? prefix2str(c->address, buf, sizeof(buf)) : "Unknown");
return 0;
}
-static int interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
char buf[PREFIX_STRLEN];
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
@@ -136,8 +133,7 @@ static int interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -151,8 +147,7 @@ static int interface_state_up(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -166,8 +161,7 @@ static int interface_state_down(int command, struct zclient *zclient,
return 0;
}
-static int route_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
{
struct prefix p;
enum zapi_route_notify_owner note;
@@ -212,8 +206,7 @@ static int route_notify_owner(int command, struct zclient *zclient,
return 0;
}
-static int rule_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
{
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
@@ -356,6 +349,11 @@ void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg,
"%s: Asked to install unsupported route type: L2VPN",
__PRETTY_FUNCTION__);
break;
+ case AFI_UNSPEC:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to install unspecified route type",
+ __PRETTY_FUNCTION__);
+ break;
}
}
@@ -398,11 +396,15 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
"%s: Asked to delete unsupported route type: L2VPN",
__PRETTY_FUNCTION__);
break;
+ case AFI_UNSPEC:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to delete unspecified route type",
+ __PRETTY_FUNCTION__);
+ break;
}
}
-static int pbr_zebra_nexthop_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
{
struct zapi_route nhr;
char buf[PREFIX2STR_BUFFER];
diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c
index 466cc60643..87d0f9fa22 100644
--- a/pimd/pim_bfd.c
+++ b/pimd/pim_bfd.c
@@ -111,6 +111,7 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command)
struct pim_interface *pim_ifp = NULL;
struct bfd_info *bfd_info = NULL;
struct zclient *zclient = NULL;
+ int cbit;
zclient = pim_zebra_zclient_get();
@@ -127,8 +128,12 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command)
zlog_debug("%s Nbr %s %s with BFD", __PRETTY_FUNCTION__, str,
bfd_get_command_dbg_str(command));
}
+
+ cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
+
bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->source_addr, NULL,
- nbr->interface->name, 0, 0, command, 0, VRF_DEFAULT);
+ nbr->interface->name, 0, 0, cbit,
+ command, 0, VRF_DEFAULT);
}
/*
@@ -208,8 +213,7 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx,
* connectivity if the BFD status changed to
* down.
*/
-static int pim_bfd_interface_dest_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp = NULL;
struct pim_interface *pim_ifp = NULL;
@@ -223,7 +227,8 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient,
struct listnode *neigh_nextnode = NULL;
struct pim_neighbor *neigh = NULL;
- ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id);
+ ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status,
+ NULL, vrf_id);
if ((ifp == NULL) || (p.family != AF_INET))
return 0;
@@ -288,8 +293,7 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient,
* pim_bfd_nbr_replay - Replay all the neighbors that have BFD enabled
* to zebra
*/
-static int pim_bfd_nbr_replay(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp = NULL;
struct pim_interface *pim_ifp = NULL;
@@ -299,7 +303,7 @@ static int pim_bfd_nbr_replay(int command, struct zclient *zclient,
struct vrf *vrf = NULL;
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
FOR_ALL_INTERFACES (vrf, ifp) {
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index b44b64be49..a2357067f9 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1208,6 +1208,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
print_header = 1;
for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
up)) {
+ if (!up->rpf.source_nexthop.interface)
+ continue;
if (strcmp(ifp->name,
up->rpf.source_nexthop
@@ -2325,6 +2327,41 @@ static void json_object_pim_upstream_add(json_object *json,
/* XXX: need to print ths flag in the plain text display as well */
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
json_object_boolean_true_add(json, "sourceMsdp");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE)
+ json_object_boolean_true_add(json, "sendSGRptPrune");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)
+ json_object_boolean_true_add(json, "lastHopRouter");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY)
+ json_object_boolean_true_add(json, "disableKATExpiry");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_STATIC_IIF)
+ json_object_boolean_true_add(json, "staticIncomingInterface");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL)
+ json_object_boolean_true_add(json,
+ "allowIncomingInterfaceinOil");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA)
+ json_object_boolean_true_add(json, "noPimRegistrationData");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG)
+ json_object_boolean_true_add(json, "forcePimRegistration");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG)
+ json_object_boolean_true_add(json, "sourceVxlanOrigination");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)
+ json_object_boolean_true_add(json, "sourceVxlanTermination");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
+ json_object_boolean_true_add(json, "mlagVxlan");
+
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
+ json_object_boolean_true_add(json,
+ "mlagNonDesignatedForwarder");
}
static const char *
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index cbc3c6a640..9b242e9be5 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -135,9 +135,20 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
mask = PIM_OIF_FLAG_PROTO_IGMP;
- /* SGRpt entry could have empty oil */
- pim_channel_del_oif(ch->upstream->channel_oil, ch->interface,
- mask);
+ /*
+ * A S,G RPT channel can have an empty oil, we also
+ * need to take into account the fact that a ifchannel
+ * might have been suppressing a *,G ifchannel from
+ * being inherited. So let's figure out what
+ * needs to be done here
+ */
+ if (pim_upstream_evaluate_join_desired_interface(
+ ch->upstream, ch, ch->parent))
+ pim_channel_add_oif(ch->upstream->channel_oil,
+ ch->interface, mask);
+ else
+ pim_channel_del_oif(ch->upstream->channel_oil,
+ ch->interface, mask);
/*
* Do we have any S,G's that are inheriting?
* Nuke from on high too.
@@ -1393,7 +1404,9 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
child->ifjoin_state = PIM_IFJOIN_NOINFO;
- if (I_am_RP(pim, child->sg.grp)) {
+ if ((I_am_RP(pim, child->sg.grp)) &&
+ (!pim_upstream_empty_inherited_olist(
+ child->upstream))) {
pim_channel_add_oif(
child->upstream->channel_oil,
ch->interface, PIM_OIF_FLAG_PROTO_STAR);
@@ -1414,9 +1427,9 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
}
-unsigned int pim_ifchannel_hash_key(void *arg)
+unsigned int pim_ifchannel_hash_key(const void *arg)
{
- struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
+ const struct pim_ifchannel *ch = arg;
return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
}
diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h
index b9d4d291d8..b36c3236b0 100644
--- a/pimd/pim_ifchannel.h
+++ b/pimd/pim_ifchannel.h
@@ -155,5 +155,5 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
int pim_ifchannel_compare(const struct pim_ifchannel *ch1,
const struct pim_ifchannel *ch2);
-unsigned int pim_ifchannel_hash_key(void *arg);
+unsigned int pim_ifchannel_hash_key(const void *arg);
#endif /* PIM_IFCHANNEL_H */
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index cdd156b96f..213ca48bb5 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -829,9 +829,9 @@ void igmp_sock_delete_all(struct interface *ifp)
}
}
-static unsigned int igmp_group_hash_key(void *arg)
+static unsigned int igmp_group_hash_key(const void *arg)
{
- struct igmp_group *group = (struct igmp_group *)arg;
+ const struct igmp_group *group = arg;
return jhash_1word(group->group_addr.s_addr, 0);
}
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
index 395c4af35f..3287e13719 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -680,9 +680,9 @@ void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg)
}
/* sa hash and peer list helpers */
-static unsigned int pim_msdp_sa_hash_key_make(void *p)
+static unsigned int pim_msdp_sa_hash_key_make(const void *p)
{
- struct pim_msdp_sa *sa = p;
+ const struct pim_msdp_sa *sa = p;
return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0));
}
@@ -1215,9 +1215,9 @@ enum pim_msdp_err pim_msdp_peer_del(struct pim_instance *pim,
}
/* peer hash and peer list helpers */
-static unsigned int pim_msdp_peer_hash_key_make(void *p)
+static unsigned int pim_msdp_peer_hash_key_make(const void *p)
{
- struct pim_msdp_peer *mp = p;
+ const struct pim_msdp_peer *mp = p;
return (jhash_1word(mp->peer.s_addr, 0));
}
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index d0611a4ae4..1a2f451524 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -725,8 +725,7 @@ 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(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
{
struct nexthop *nexthop;
struct nexthop *nhlist_head = NULL;
@@ -753,7 +752,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
return 0;
}
- if (command == ZEBRA_NEXTHOP_UPDATE) {
+ if (cmd == ZEBRA_NEXTHOP_UPDATE) {
prefix_copy(&rpf.rpf_addr, &nhr.prefix);
pnc = pim_nexthop_cache_find(pim, &rpf);
if (!pnc) {
diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h
index e3b746b19b..12dbf167d1 100644
--- a/pimd/pim_nht.h
+++ b/pimd/pim_nht.h
@@ -53,8 +53,7 @@ struct pim_nexthop_cache {
bool bsr_tracking;
};
-int pim_parse_nexthop_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id);
+int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS);
int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
struct pim_upstream *up, struct rp_info *rp,
bool bsr_track_needed,
diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c
index 5945bc55fd..22045c2d33 100644
--- a/pimd/pim_oil.c
+++ b/pimd/pim_oil.c
@@ -91,9 +91,9 @@ static bool pim_oil_equal(const void *arg1, const void *arg2)
return false;
}
-static unsigned int pim_oil_hash_key(void *arg)
+static unsigned int pim_oil_hash_key(const void *arg)
{
- struct channel_oil *oil = (struct channel_oil *)arg;
+ const struct channel_oil *oil = arg;
return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
oil->oil.mfcc_origin.s_addr, 0);
diff --git a/pimd/pim_routemap.c b/pimd/pim_routemap.c
index 4230c127ad..2de94e9031 100644
--- a/pimd/pim_routemap.c
+++ b/pimd/pim_routemap.c
@@ -36,7 +36,7 @@ static void pim_route_map_delete(const char *rmap_name)
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
}
-static void pim_route_map_event(route_map_event_t event, const char *rmap_name)
+static void pim_route_map_event(const char *rmap_name)
{
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED);
}
diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c
index 9b923086ef..d388802454 100644
--- a/pimd/pim_rpf.c
+++ b/pimd/pim_rpf.c
@@ -426,9 +426,9 @@ int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2)
return 0;
}
-unsigned int pim_rpf_hash_key(void *arg)
+unsigned int pim_rpf_hash_key(const void *arg)
{
- struct pim_nexthop_cache *r = (struct pim_nexthop_cache *)arg;
+ const struct pim_nexthop_cache *r = arg;
return jhash_1word(r->rpf.rpf_addr.u.prefix4.s_addr, 0);
}
diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h
index 57bb22674f..1172acb4b2 100644
--- a/pimd/pim_rpf.h
+++ b/pimd/pim_rpf.h
@@ -56,7 +56,7 @@ enum pim_rpf_result { PIM_RPF_OK = 0, PIM_RPF_CHANGED, PIM_RPF_FAILURE };
struct pim_upstream;
-unsigned int pim_rpf_hash_key(void *arg);
+unsigned int pim_rpf_hash_key(const void *arg);
bool pim_rpf_equal(const void *arg1, const void *arg2);
bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop,
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
index 33506342e4..a823962b23 100644
--- a/pimd/pim_upstream.c
+++ b/pimd/pim_upstream.c
@@ -1181,8 +1181,16 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc(
"kat expired on %s[%s]; remove stream reference",
up->sg_str, pim->vrf->name);
PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
- up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
- } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) {
+
+ /* Return if upstream entry got deleted.*/
+ if (!pim_upstream_del(pim, up, __PRETTY_FUNCTION__))
+ return NULL;
+ }
+ /* upstream reference would have been added to track the local
+ * membership if it is LHR. We have to clear it when KAT expires.
+ * Otherwise would result in stale entry with uncleared ref count.
+ */
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) {
struct pim_upstream *parent = up->parent;
PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags);
@@ -1621,9 +1629,9 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim)
}
}
-unsigned int pim_upstream_hash_key(void *arg)
+unsigned int pim_upstream_hash_key(const void *arg)
{
- struct pim_upstream *up = (struct pim_upstream *)arg;
+ const struct pim_upstream *up = arg;
return jhash_2words(up->sg.src.s_addr, up->sg.grp.s_addr, 0);
}
diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h
index 13a3dcdf8c..102826ac71 100644
--- a/pimd/pim_upstream.h
+++ b/pimd/pim_upstream.h
@@ -308,7 +308,7 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim,
void pim_upstream_spt_prefix_list_update(struct pim_instance *pim,
struct prefix_list *pl);
-unsigned int pim_upstream_hash_key(void *arg);
+unsigned int pim_upstream_hash_key(const void *arg);
bool pim_upstream_equal(const void *arg1, const void *arg2);
struct pim_upstream *pim_upstream_keep_alive_timer_proc(
struct pim_upstream *up);
diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c
index c893fbe7e3..09669e206e 100644
--- a/pimd/pim_vxlan.c
+++ b/pimd/pim_vxlan.c
@@ -623,9 +623,9 @@ static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg)
}
/************************** vxlan SG cache management ************************/
-static unsigned int pim_vxlan_sg_hash_key_make(void *p)
+static unsigned int pim_vxlan_sg_hash_key_make(const void *p)
{
- struct pim_vxlan_sg *vxlan_sg = p;
+ const struct pim_vxlan_sg *vxlan_sg = p;
return (jhash_2words(vxlan_sg->sg.src.s_addr,
vxlan_sg->sg.grp.s_addr, 0));
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index aeaea7d69f..25ac307ac4 100644
--- a/pimd/pim_zebra.c
+++ b/pimd/pim_zebra.c
@@ -54,8 +54,7 @@ static struct zclient *zclient = NULL;
/* Router-id update message from zebra. */
-static int pim_router_id_update_zebra(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
{
struct prefix router_id;
@@ -64,8 +63,7 @@ static int pim_router_id_update_zebra(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_if_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct pim_instance *pim;
@@ -126,8 +124,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_if_del(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_del(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct pim_instance *pim;
@@ -166,8 +163,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_if_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_state_up(ZAPI_CALLBACK_ARGS)
{
struct pim_instance *pim;
struct interface *ifp;
@@ -235,8 +231,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_if_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_state_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -280,8 +275,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t new_vrf_id;
@@ -326,8 +320,7 @@ static void dump_if_address(struct interface *ifp)
}
#endif
-static int pim_zebra_if_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
@@ -342,7 +335,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
will add address to interface list by calling
connected_add_by_prefix()
*/
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
@@ -406,8 +399,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
return 0;
}
-static int pim_zebra_if_address_del(int command, struct zclient *client,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
@@ -426,7 +418,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client,
will remove address from interface list by calling
connected_delete_by_prefix()
*/
- c = zebra_interface_address_read(command, client->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
@@ -554,8 +546,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
pim_upstream_update_join_desired(pim, up);
}
-static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct pim_instance *pim;
@@ -577,11 +568,11 @@ static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient,
pim_str_sg_set(&sg, sg_str);
zlog_debug("%u:recv SG %s %s", vrf_id,
- (command == ZEBRA_VXLAN_SG_ADD)?"add":"del",
+ (cmd == ZEBRA_VXLAN_SG_ADD)?"add":"del",
sg_str);
}
- if (command == ZEBRA_VXLAN_SG_ADD)
+ if (cmd == ZEBRA_VXLAN_SG_ADD)
pim_vxlan_sg_add(pim, &sg);
else
pim_vxlan_sg_del(pim, &sg);
@@ -789,7 +780,7 @@ void sched_rpf_cache_refresh(struct pim_instance *pim)
static void pim_zebra_connected(struct zclient *zclient)
{
/* Send the client registration */
- bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id);
zclient_send_reg_requests(zclient, router->vrf_id);
}
@@ -1305,8 +1296,16 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it)
install_it, up->channel_oil->installed);
}
- pim_channel_del_oif(up->channel_oil, ch->interface,
- PIM_OIF_FLAG_PROTO_PIM);
+ /*
+ * If a channel is being removed, check to see if we still need
+ * to inherit the interface. If so make sure it is added in
+ */
+ if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent))
+ pim_channel_add_oif(up->channel_oil, ch->interface,
+ PIM_OIF_FLAG_PROTO_PIM);
+ else
+ pim_channel_del_oif(up->channel_oil, ch->interface,
+ PIM_OIF_FLAG_PROTO_PIM);
if (install_it && !up->channel_oil->installed)
pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 36f9259865..ebd9ac3f47 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -24,6 +24,7 @@
%{!?with_pam: %global with_pam 0 }
%{!?with_pbrd: %global with_pbrd 1 }
%{!?with_pimd: %global with_pimd 1 }
+%{!?with_vrrpd: %global with_vrrpd 1 }
%{!?with_rpki: %global with_rpki 0 }
%{!?with_rtadv: %global with_rtadv 1 }
%{!?with_watchfrr: %global with_watchfrr 1 }
@@ -124,6 +125,12 @@
%define daemon_babeld ""
%endif
+%if %{with_vrrpd}
+ %define daemon_vrrpd vrrpd
+%else
+ %define daemon_vrrpd ""
+%endif
+
%if %{with_watchfrr}
%define daemon_watchfrr watchfrr
%else
@@ -136,7 +143,7 @@
%define daemon_bfdd ""
%endif
-%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd}
+%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd}
#release sub-revision (the two digits after the CONFDATE)
%{!?release_rev: %global release_rev 01 }
@@ -306,6 +313,11 @@ developing OSPF-API and frr applications.
%else
--disable-babeld \
%endif
+%if %{with_vrrpd}
+ --enable-vrrpd \
+%else
+ --disable-vrrpd \
+%endif
%if %{with_pam}
--with-libpam \
%endif
@@ -461,6 +473,9 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty"
zebra_spec_add_service bfdd 2617/tcp "BFDd vty"
%endif
zebra_spec_add_service fabricd 2618/tcp "Fabricd vty"
+%if %{with_vrrpd}
+ zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty"
+%endif
%if "%{initsystem}" == "systemd"
for daemon in %all_daemons ; do
@@ -596,6 +611,9 @@ fi
%if %{with_pbrd}
%{_sbindir}/pbrd
%endif
+%if %{with_vrrpd}
+ %{_sbindir}/vrrpd
+%endif
%{_sbindir}/isisd
%{_sbindir}/fabricd
%if %{with_ldpd}
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index b909cbcb2b..634fee0b30 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -344,8 +344,7 @@ int if_check_address(struct rip *rip, struct in_addr addr)
}
/* Inteface link down message processing. */
-int rip_interface_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int rip_interface_down(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -372,8 +371,7 @@ int rip_interface_down(int command, struct zclient *zclient,
}
/* Inteface link up message processing */
-int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+int rip_interface_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -405,8 +403,7 @@ int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length,
}
/* Inteface addition message from zebra. */
-int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length,
- vrf_id_t vrf_id)
+int rip_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -436,8 +433,7 @@ int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length,
return 0;
}
-int rip_interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int rip_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -468,8 +464,7 @@ int rip_interface_delete(int command, struct zclient *zclient,
}
/* VRF update for an interface. */
-int rip_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t new_vrf_id;
@@ -615,8 +610,7 @@ static void rip_apply_address_add(struct connected *ifc)
0);
}
-int rip_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int rip_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct prefix *p;
@@ -669,8 +663,7 @@ static void rip_apply_address_del(struct connected *ifc)
&address, ifc->ifp->ifindex);
}
-int rip_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int rip_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct prefix *p;
diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h
index 303be0315d..6befda0e28 100644
--- a/ripd/rip_interface.h
+++ b/ripd/rip_interface.h
@@ -30,8 +30,7 @@ extern int rip_interface_address_add(int, struct zclient *, zebra_size_t,
vrf_id_t);
extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t,
vrf_id_t);
-extern int rip_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id);
+extern int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS);
extern void rip_interface_sync(struct interface *ifp);
#endif /* _QUAGGA_RIP_INTERFACE_H */
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index 65da51f83a..773cb1d0fe 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -41,10 +41,7 @@
#include "ripd/rip_errors.h"
/* ripd options. */
-#if CONFDATE > 20190521
- CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
-static struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}};
+static struct option longopts[] = {{0}};
/* ripd privileges */
zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN};
@@ -129,10 +126,7 @@ FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT,
.privs = &ripd_privs, .yang_modules = ripd_yang_modules,
.n_yang_modules = array_size(ripd_yang_modules), )
-#if CONFDATE > 20190521
-CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
-#define DEPRECATED_OPTIONS "r"
+#define DEPRECATED_OPTIONS ""
/* Main routine of ripd. */
int main(int argc, char **argv)
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
index 4f0df12232..0c88cb202b 100644
--- a/ripd/rip_zebra.c
+++ b/ripd/rip_zebra.c
@@ -118,8 +118,7 @@ void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp)
}
/* Zebra route add and delete treatment. */
-static int rip_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct rip *rip;
struct zapi_route api;
@@ -138,11 +137,11 @@ static int rip_zebra_read_route(int command, struct zclient *zclient,
nh.ifindex = api.nexthops[0].ifindex;
/* Then fetch IPv4 prefixes. */
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
rip_redistribute_add(rip, api.type, RIP_ROUTE_REDISTRIBUTE,
(struct prefix_ipv4 *)&api.prefix, &nh,
api.metric, api.distance, api.tag);
- else if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL)
+ else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL)
rip_redistribute_delete(rip, api.type, RIP_ROUTE_REDISTRIBUTE,
(struct prefix_ipv4 *)&api.prefix,
nh.ifindex);
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
index e35652b1ac..5a4087b177 100644
--- a/ripngd/ripng_interface.c
+++ b/ripngd/ripng_interface.c
@@ -193,8 +193,7 @@ static int ripng_if_down(struct interface *ifp)
}
/* Inteface link up message processing. */
-int ripng_interface_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_up(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct interface *ifp;
@@ -228,8 +227,7 @@ int ripng_interface_up(int command, struct zclient *zclient,
}
/* Inteface link down message processing. */
-int ripng_interface_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_down(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct interface *ifp;
@@ -255,8 +253,7 @@ int ripng_interface_down(int command, struct zclient *zclient,
}
/* Inteface addition message from zebra. */
-int ripng_interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -281,8 +278,7 @@ int ripng_interface_add(int command, struct zclient *zclient,
return 0;
}
-int ripng_interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -313,8 +309,7 @@ int ripng_interface_delete(int command, struct zclient *zclient,
}
/* VRF update for an interface. */
-int ripng_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
vrf_id_t new_vrf_id;
@@ -383,8 +378,7 @@ static void ripng_apply_address_add(struct connected *ifc)
ifc->ifp->ifindex, NULL, 0);
}
-int ripng_interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
struct prefix *p;
@@ -450,8 +444,7 @@ static void ripng_apply_address_del(struct connected *ifc)
ifc->ifp->ifindex);
}
-int ripng_interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct prefix *p;
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index c755bd83ce..4b027019c0 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -41,10 +41,7 @@
#include "ripngd/ripngd.h"
/* RIPngd options. */
-#if CONFDATE > 20190521
- CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
-struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}};
+struct option longopts[] = {{0}};
/* ripngd privileges */
zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN};
@@ -132,10 +129,7 @@ FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT,
.yang_modules = ripngd_yang_modules,
.n_yang_modules = array_size(ripngd_yang_modules), )
-#if CONFDATE > 20190521
-CPP_NOTICE("-r / --retain has reached deprecation EOL, remove")
-#endif
-#define DEPRECATED_OPTIONS "r"
+#define DEPRECATED_OPTIONS ""
/* RIPngd main routine. */
int main(int argc, char **argv)
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
index cf60de2de9..a557a90c82 100644
--- a/ripngd/ripng_zebra.c
+++ b/ripngd/ripng_zebra.c
@@ -113,8 +113,7 @@ void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *rp)
}
/* Zebra route add and delete treatment. */
-static int ripng_zebra_read_route(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct ripng *ripng;
struct zapi_route api;
@@ -138,7 +137,7 @@ static int ripng_zebra_read_route(int command, struct zclient *zclient,
nexthop = api.nexthops[0].gate.ipv6;
ifindex = api.nexthops[0].ifindex;
- if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
ripng_redistribute_add(ripng, api.type,
RIPNG_ROUTE_REDISTRIBUTE,
(struct prefix_ipv6 *)&api.prefix,
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
index dc425b6958..a2686304fc 100644
--- a/ripngd/ripngd.h
+++ b/ripngd/ripngd.h
@@ -468,20 +468,13 @@ extern int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
extern void ripng_packet_dump(struct ripng_packet *packet, int size,
const char *sndrcv);
-extern int ripng_interface_up(int command, struct zclient *, zebra_size_t,
- vrf_id_t);
-extern int ripng_interface_down(int command, struct zclient *, zebra_size_t,
- vrf_id_t);
-extern int ripng_interface_add(int command, struct zclient *, zebra_size_t,
- vrf_id_t);
-extern int ripng_interface_delete(int command, struct zclient *, zebra_size_t,
- vrf_id_t);
-extern int ripng_interface_address_add(int command, struct zclient *,
- zebra_size_t, vrf_id_t);
-extern int ripng_interface_address_delete(int command, struct zclient *,
- zebra_size_t, vrf_id_t);
-extern int ripng_interface_vrf_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id);
+extern int ripng_interface_up(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_down(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_add(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_delete(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_address_add(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS);
+extern int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS);
extern void ripng_interface_sync(struct interface *ifp);
extern struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id);
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index f1e83628c2..19c7e556ca 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -58,8 +58,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s)
}
/* Inteface addition message from zebra. */
-static int interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -71,8 +70,7 @@ static int interface_add(int command, struct zclient *zclient,
return 0;
}
-static int interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -90,21 +88,19 @@ static int interface_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
{
- zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
return 0;
}
-static int interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
@@ -113,8 +109,7 @@ static int interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_up(ZAPI_CALLBACK_ARGS)
{
zebra_interface_if_lookup(zclient->ibuf);
@@ -122,8 +117,7 @@ static int interface_state_up(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_down(ZAPI_CALLBACK_ARGS)
{
zebra_interface_state_read(zclient->ibuf, vrf_id);
@@ -202,8 +196,7 @@ static void handle_repeated(bool installed)
}
}
-static int route_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
{
struct timeval r;
struct prefix p;
@@ -345,8 +338,7 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
__PRETTY_FUNCTION__);
}
-static int sharp_nexthop_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS)
{
struct sharp_nh_tracker *nht;
struct zapi_route nhr;
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
index 3f31177524..c6da00418b 100644
--- a/staticd/static_zebra.c
+++ b/staticd/static_zebra.c
@@ -59,8 +59,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s)
}
/* Inteface addition message from zebra. */
-static int interface_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_add(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -73,8 +72,7 @@ static int interface_add(int command, struct zclient *zclient,
return 0;
}
-static int interface_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_delete(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
struct stream *s;
@@ -93,20 +91,18 @@ static int interface_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_address_add(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
{
- zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
return 0;
}
-static int interface_address_delete(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
- c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (!c)
return 0;
@@ -115,8 +111,7 @@ static int interface_address_delete(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_up(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_up(ZAPI_CALLBACK_ARGS)
{
struct interface *ifp;
@@ -138,16 +133,14 @@ static int interface_state_up(int command, struct zclient *zclient,
return 0;
}
-static int interface_state_down(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int interface_state_down(ZAPI_CALLBACK_ARGS)
{
zebra_interface_state_read(zclient->ibuf, vrf_id);
return 0;
}
-static int route_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
{
struct prefix p;
enum zapi_route_notify_owner note;
@@ -194,8 +187,7 @@ struct static_nht_data {
uint8_t nh_num;
};
-static int static_zebra_nexthop_update(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
{
struct static_nht_data *nhtd, lookup;
struct zapi_route nhr;
@@ -231,9 +223,9 @@ static void static_zebra_capabilities(struct zclient_capabilities *cap)
mpls_enabled = cap->mpls_enabled;
}
-static unsigned int static_nht_hash_key(void *data)
+static unsigned int static_nht_hash_key(const void *data)
{
- struct static_nht_data *nhtd = data;
+ const struct static_nht_data *nhtd = data;
unsigned int key = 0;
key = prefix_hash_key(nhtd->nh);
diff --git a/tests/.gitignore b/tests/.gitignore
index de648015f1..380172487d 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -20,6 +20,7 @@
/lib/cli/test_commands_defun.c
/lib/northbound/test_oper_data
/lib/cxxcompat
+/lib/test_atomlist
/lib/test_buffer
/lib/test_checksum
/lib/test_graph
@@ -32,6 +33,7 @@
/lib/test_privs
/lib/test_ringbuf
/lib/test_segv
+/lib/test_seqlock
/lib/test_sig
/lib/test_srcdest_table
/lib/test_stream
@@ -39,6 +41,7 @@
/lib/test_timer_correctness
/lib/test_timer_performance
/lib/test_ttable
+/lib/test_typelist
/lib/test_zlog
/lib/test_zmq
/ospf6d/test_lsdb
diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
index 4a89bda84e..6f8bc2218e 100644
--- a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
+++ b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
Binary files differ
diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c
index b9c6f2bbb2..f0baa482c7 100644
--- a/tests/isisd/test_isis_lspdb.c
+++ b/tests/isisd/test_isis_lspdb.c
@@ -28,21 +28,22 @@ static void test_lsp_build_list_nonzero_ht(void)
area->lsp_mtu = 1500;
- dict_t *lspdb = lsp_db_init();
+ struct lspdb_head _lspdb, *lspdb = &_lspdb;
+ lsp_db_init(&_lspdb);
struct isis_lsp *lsp1 = lsp_new(area, lsp_id1, 6000, 0, 0, 0, NULL,
ISIS_LEVEL2);
- lsp_insert(lsp1, lspdb);
+ lsp_insert(lspdb, lsp1);
struct isis_lsp *lsp2 = lsp_new(area, lsp_id2, 6000, 0, 0, 0, NULL,
ISIS_LEVEL2);
- lsp_insert(lsp2, lspdb);
+ lsp_insert(lspdb, lsp2);
struct list *list = list_new();
- lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb);
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
assert(list->count == 1);
assert(listgetdata(listhead(list)) == lsp1);
list_delete_all_node(list);
@@ -50,7 +51,7 @@ static void test_lsp_build_list_nonzero_ht(void)
lsp_id_end[5] = 0x03;
lsp_id_end[6] = 0x00;
- lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb);
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
assert(list->count == 2);
assert(listgetdata(listhead(list)) == lsp1);
assert(listgetdata(listtail(list)) == lsp2);
@@ -58,7 +59,7 @@ static void test_lsp_build_list_nonzero_ht(void)
memcpy(lsp_id1, lsp_id2, sizeof(lsp_id1));
- lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb);
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
assert(list->count == 1);
assert(listgetdata(listhead(list)) == lsp2);
list_delete_all_node(list);
@@ -66,13 +67,13 @@ static void test_lsp_build_list_nonzero_ht(void)
lsp_id1[5] = 0x03;
lsp_id_end[5] = 0x04;
- lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb);
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
assert(list->count == 0);
list_delete_all_node(list);
lsp_id1[5] = 0x00;
- lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb);
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
assert(list->count == 2);
assert(listgetdata(listhead(list)) == lsp1);
assert(listgetdata(listtail(list)) == lsp2);
diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c
index d10962993d..48fa0ec8a9 100644
--- a/tests/lib/cxxcompat.c
+++ b/tests/lib/cxxcompat.c
@@ -32,7 +32,6 @@
#include "lib/debug.h"
#include "lib/distribute.h"
#include "lib/ferr.h"
-#include "lib/fifo.h"
#include "lib/filter.h"
#include "lib/frr_pthread.h"
#include "lib/frratomic.h"
@@ -93,6 +92,8 @@
#include "lib/table.h"
#include "lib/termtable.h"
#include "lib/thread.h"
+#include "lib/typesafe.h"
+#include "lib/typerb.h"
#include "lib/vector.h"
#include "lib/vlan.h"
#include "lib/vrf.h"
diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c
new file mode 100644
index 0000000000..249fff8edb
--- /dev/null
+++ b/tests/lib/test_atomlist.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "atomlist.h"
+#include "seqlock.h"
+#include "monotime.h"
+
+/*
+ * maybe test:
+ * - alist_del_hint
+ * - alist_next_safe
+ * - asort_del_hint
+ * - asort_next_safe
+ */
+
+static struct seqlock sqlo;
+
+PREDECL_ATOMLIST(alist)
+PREDECL_ATOMSORT_UNIQ(asort)
+struct item {
+ uint64_t val1;
+ struct alist_item chain;
+ struct asort_item sortc;
+ uint64_t val2;
+};
+DECLARE_ATOMLIST(alist, struct item, chain)
+
+static int icmp(const struct item *a, const struct item *b);
+DECLARE_ATOMSORT_UNIQ(asort, struct item, sortc, icmp)
+
+static int icmp(const struct item *a, const struct item *b)
+{
+ if (a->val1 > b->val1)
+ return 1;
+ if (a->val1 < b->val1)
+ return -1;
+ return 0;
+}
+
+#define NITEM 10000
+struct item itm[NITEM];
+
+static struct alist_head ahead;
+static struct asort_head shead;
+
+#define NTHREADS 4
+static struct testthread {
+ pthread_t pt;
+ struct seqlock sqlo;
+ size_t counter, nullops;
+} thr[NTHREADS];
+
+struct testrun {
+ struct testrun *next;
+ int lineno;
+ const char *desc;
+ ssize_t prefill;
+ bool sorted;
+ void (*func)(unsigned int offset);
+};
+struct testrun *runs = NULL;
+
+#define NOCLEAR -1
+
+#define deftestrun(name, _desc, _prefill, _sorted) \
+static void trfunc_##name(unsigned int offset); \
+struct testrun tr_##name = { \
+ .desc = _desc, \
+ .lineno = __LINE__, \
+ .prefill = _prefill, \
+ .func = &trfunc_##name, \
+ .sorted = _sorted }; \
+static void __attribute__((constructor)) trsetup_##name(void) \
+{ \
+ struct testrun **inspos = &runs; \
+ while (*inspos && (*inspos)->lineno < tr_##name.lineno) \
+ inspos = &(*inspos)->next; \
+ tr_##name.next = *inspos; \
+ *inspos = &tr_##name; \
+} \
+static void trfunc_##name(unsigned int offset) \
+{ \
+ size_t i = 0, n = 0;
+
+#define endtestrun \
+ thr[offset].counter = i; \
+ thr[offset].nullops = n; \
+}
+
+deftestrun(add, "add vs. add", 0, false)
+ for (; i < NITEM / NTHREADS; i++)
+ alist_add_head(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(del, "del vs. del", NOCLEAR, false)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ alist_del(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(addtail, "add_tail vs. add_tail", 0, false)
+ for (; i < NITEM / NTHREADS; i++)
+ alist_add_tail(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(pop, "pop vs. pop", NOCLEAR, false)
+ for (; i < NITEM / NTHREADS; )
+ if (alist_pop(&ahead))
+ i++;
+ else
+ n++;
+endtestrun
+
+deftestrun(headN_vs_pop1, "add_head(N) vs. pop(1)", 1, false);
+ if (offset == 0) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(head1_vs_popN, "add_head(1) vs. pop(N)", 0, false);
+ if (offset < NTHREADS - 1) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = 0; i < NITEM; i++)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(headN_vs_popN, "add_head(N) vs. pop(N)", NTHREADS / 2, false)
+ if (offset < NTHREADS / 2) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM * 2 / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(tailN_vs_pop1, "add_tail(N) vs. pop(1)", 1, false)
+ if (offset == 0) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM - (NITEM / NTHREADS); ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_tail(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(tail1_vs_popN, "add_tail(1) vs. pop(N)", 0, false)
+ if (offset < NTHREADS - 1) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = 0; i < NITEM; i++)
+ alist_add_tail(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(sort_add, "add_sort vs. add_sort", 0, true)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_add(&shead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(sort_del, "del_sort vs. del_sort", NOCLEAR, true)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_del(&shead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(sort_add_del, "add_sort vs. del_sort", NTHREADS / 2, true)
+ if (offset < NTHREADS / 2) {
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_del(&shead, &itm[i * NTHREADS + offset]);
+ } else {
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_add(&shead, &itm[i * NTHREADS + offset]);
+ }
+endtestrun
+
+static void *thr1func(void *arg)
+{
+ struct testthread *p = arg;
+ unsigned int offset = (unsigned int)(p - &thr[0]);
+ seqlock_val_t sv;
+ struct testrun *tr;
+
+ for (tr = runs; tr; tr = tr->next) {
+ sv = seqlock_bump(&p->sqlo);
+ seqlock_wait(&sqlo, sv);
+
+ tr->func(offset);
+ }
+ seqlock_bump(&p->sqlo);
+
+ return NULL;
+}
+
+static void clear_list(size_t prefill)
+{
+ size_t i;
+
+ memset(&ahead, 0, sizeof(ahead));
+ memset(&shead, 0, sizeof(shead));
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++) {
+ itm[i].val1 = itm[i].val2 = i;
+ if ((i % NTHREADS) < prefill) {
+ alist_add_tail(&ahead, &itm[i]);
+ asort_add(&shead, &itm[i]);
+ }
+ }
+}
+
+static void run_tr(struct testrun *tr)
+{
+ const char *desc = tr->desc;
+ struct timeval tv;
+ int64_t delta;
+ seqlock_val_t sv;
+ size_t c = 0, s = 0, n = 0;
+ struct item *item, *prev, dummy;
+
+ printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc);
+ fflush(stdout);
+
+ if (tr->prefill != NOCLEAR)
+ clear_list(tr->prefill);
+
+ monotime(&tv);
+ sv = seqlock_bump(&sqlo);
+ for (size_t i = 0; i < NTHREADS; i++) {
+ seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo));
+ s += thr[i].counter;
+ n += thr[i].nullops;
+ thr[i].counter = 0;
+ thr[i].nullops = 0;
+ }
+
+ delta = monotime_since(&tv, NULL);
+ if (tr->sorted) {
+ uint64_t prevval = 0;
+
+ frr_each(asort, &shead, item) {
+ assert(item->val1 >= prevval);
+ prevval = item->val1;
+ c++;
+ }
+ assert(c == asort_count(&shead));
+ } else {
+ prev = &dummy;
+ frr_each(alist, &ahead, item) {
+ assert(item != prev);
+ prev = item;
+ c++;
+ assert(c <= NITEM);
+ }
+ assert(c == alist_count(&ahead));
+ }
+ printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n",
+ sv >> 1, delta, c, s, n, desc);
+}
+
+#ifdef BASIC_TESTS
+static void dump(const char *lbl)
+{
+ struct item *item, *safe;
+ size_t ctr = 0;
+
+ printf("dumping %s:\n", lbl);
+ frr_each_safe(alist, &ahead, item) {
+ printf("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++,
+ (void *)item, item->val1, item->val2);
+ }
+}
+
+static void basic_tests(void)
+{
+ size_t i;
+
+ memset(&ahead, 0, sizeof(ahead));
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++)
+ itm[i].val1 = itm[i].val2 = i;
+
+ assert(alist_first(&ahead) == NULL);
+ dump("");
+ alist_add_head(&ahead, &itm[0]);
+ dump("");
+ alist_add_head(&ahead, &itm[1]);
+ dump("");
+ alist_add_tail(&ahead, &itm[2]);
+ dump("");
+ alist_add_tail(&ahead, &itm[3]);
+ dump("");
+ alist_del(&ahead, &itm[1]);
+ dump("");
+ printf("POP: %p\n", alist_pop(&ahead));
+ dump("");
+ printf("POP: %p\n", alist_pop(&ahead));
+ printf("POP: %p\n", alist_pop(&ahead));
+ printf("POP: %p\n", alist_pop(&ahead));
+ printf("POP: %p\n", alist_pop(&ahead));
+ dump("");
+}
+#else
+#define basic_tests() do { } while (0)
+#endif
+
+int main(int argc, char **argv)
+{
+ size_t i;
+
+ basic_tests();
+
+ seqlock_init(&sqlo);
+ seqlock_acquire_val(&sqlo, 1);
+
+ for (i = 0; i < NTHREADS; i++) {
+ seqlock_init(&thr[i].sqlo);
+ seqlock_acquire(&thr[i].sqlo, &sqlo);
+ thr[i].counter = 0;
+ thr[i].nullops = 0;
+
+ pthread_create(&thr[i].pt, NULL, thr1func, &thr[i]);
+ }
+
+ struct testrun *tr;
+
+ for (tr = runs; tr; tr = tr->next)
+ run_tr(tr);
+
+ for (i = 0; i < NTHREADS; i++)
+ pthread_join(thr[i].pt, NULL);
+
+ return 0;
+}
diff --git a/tests/lib/test_atomlist.py b/tests/lib/test_atomlist.py
new file mode 100644
index 0000000000..293d47f316
--- /dev/null
+++ b/tests/lib/test_atomlist.py
@@ -0,0 +1,6 @@
+import frrtest
+
+class TestAtomlist(frrtest.TestMultiOut):
+ program = './test_atomlist'
+
+TestAtomlist.exit_cleanly()
diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c
new file mode 100644
index 0000000000..6b2b9ed8a5
--- /dev/null
+++ b/tests/lib/test_seqlock.c
@@ -0,0 +1,122 @@
+/*
+ * basic test for seqlock
+ *
+ * Copyright (C) 2015 David Lamparter
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/uio.h>
+
+#include "monotime.h"
+#include "seqlock.h"
+
+static struct seqlock sqlo;
+static pthread_t thr1;
+static struct timeval start;
+
+static void writestr(const char *str)
+{
+ struct iovec iov[2];
+ char buf[32];
+ int64_t usec = monotime_since(&start, NULL);
+
+ snprintf(buf, sizeof(buf), "[%02"PRId64"] ", usec / 100000);
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = strlen(buf);
+ iov[1].iov_base = (char *)str;
+ iov[1].iov_len = strlen(str);
+ writev(1, iov, 2);
+}
+
+static void *thr1func(void *arg)
+{
+ assert(!seqlock_held(&sqlo));
+ assert(seqlock_check(&sqlo, 1));
+ seqlock_wait(&sqlo, 1);
+ writestr("thr1 (unheld)\n");
+
+ sleep(2);
+
+ assert(seqlock_held(&sqlo));
+ assert(seqlock_check(&sqlo, 1));
+ seqlock_wait(&sqlo, 1);
+ writestr("thr1 @1\n");
+
+ seqlock_wait(&sqlo, 3);
+ writestr("thr1 @3\n");
+
+ seqlock_wait(&sqlo, 5);
+ writestr("thr1 @5\n");
+
+ seqlock_wait(&sqlo, 7);
+ writestr("thr1 @7\n");
+
+ seqlock_wait(&sqlo, 9);
+ writestr("thr1 @9\n");
+
+ seqlock_wait(&sqlo, 11);
+ writestr("thr1 @11\n");
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ monotime(&start);
+
+ seqlock_init(&sqlo);
+
+ assert(!seqlock_held(&sqlo));
+ seqlock_acquire_val(&sqlo, 1);
+ assert(seqlock_held(&sqlo));
+
+ assert(seqlock_cur(&sqlo) == 1);
+ assert(seqlock_bump(&sqlo) == 1);
+ assert(seqlock_cur(&sqlo) == 3);
+ assert(seqlock_bump(&sqlo) == 3);
+ assert(seqlock_bump(&sqlo) == 5);
+ assert(seqlock_bump(&sqlo) == 7);
+ assert(seqlock_cur(&sqlo) == 9);
+
+ assert(seqlock_held(&sqlo));
+ seqlock_release(&sqlo);
+ assert(!seqlock_held(&sqlo));
+
+ pthread_create(&thr1, NULL, thr1func, NULL);
+ sleep(1);
+
+ writestr("main @3\n");
+ seqlock_acquire_val(&sqlo, 3);
+ sleep(2);
+
+ writestr("main @5\n");
+ seqlock_bump(&sqlo);
+ sleep(1);
+
+ writestr("main @9\n");
+ seqlock_acquire_val(&sqlo, 9);
+ sleep(1);
+
+ writestr("main @release\n");
+ seqlock_release(&sqlo);
+ sleep(1);
+}
diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c
index 19a40b2184..0fca571d28 100644
--- a/tests/lib/test_srcdest_table.c
+++ b/tests/lib/test_srcdest_table.c
@@ -81,9 +81,9 @@ static char *format_srcdest(const struct prefix_ipv6 *dst_p,
return rv;
}
-static unsigned int log_key(void *data)
+static unsigned int log_key(const void *data)
{
- struct prefix *hash_entry = data;
+ const struct prefix *hash_entry = data;
struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0];
struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1];
unsigned int hash = 0;
diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c
new file mode 100644
index 0000000000..2438fb5f08
--- /dev/null
+++ b/tests/lib/test_typelist.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#define WNO_ATOMLIST_UNSAFE_FIND
+
+#include "typesafe.h"
+#include "atomlist.h"
+#include "memory.h"
+#include "monotime.h"
+#include "jhash.h"
+#include "sha256.h"
+
+#include "tests/helpers/c/prng.h"
+
+/* note: these macros are layered 2-deep because that makes the C
+ * preprocessor expand the "type" argument. Otherwise, you get
+ * "PREDECL_type" instead of "PREDECL_LIST"
+ */
+#define _concat(a, b) a ## b
+#define concat(a, b) _concat(a, b)
+#define _str(x) #x
+#define str(x) _str(x)
+
+#define _PREDECL(type, ...) PREDECL_##type(__VA_ARGS__)
+#define PREDECL(type, ...) _PREDECL(type, __VA_ARGS__)
+#define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__)
+#define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__)
+
+#define T_SORTED (1 << 0)
+#define T_UNIQ (1 << 1)
+#define T_HASH (1 << 2)
+#define T_HEAP (1 << 3)
+#define T_ATOMIC (1 << 4)
+
+#define _T_LIST (0)
+#define _T_DLIST (0)
+#define _T_ATOMLIST (0 | T_ATOMIC)
+#define _T_HEAP (T_SORTED | T_HEAP)
+#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ)
+#define _T_SORTLIST_NONUNIQ (T_SORTED)
+#define _T_HASH (T_SORTED | T_UNIQ | T_HASH)
+#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ)
+#define _T_SKIPLIST_NONUNIQ (T_SORTED)
+#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ)
+#define _T_RBTREE_NONUNIQ (T_SORTED)
+#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC)
+#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC)
+
+#define _T_TYPE(type) _T_##type
+#define IS_SORTED(type) (_T_TYPE(type) & T_SORTED)
+#define IS_UNIQ(type) (_T_TYPE(type) & T_UNIQ)
+#define IS_HASH(type) (_T_TYPE(type) & T_HASH)
+#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP)
+#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC)
+
+static struct timeval ref, ref0;
+
+static void ts_start(void)
+{
+ monotime(&ref0);
+ monotime(&ref);
+}
+static void ts_ref(const char *text)
+{
+ int64_t us;
+ us = monotime_since(&ref, NULL);
+ printf("%7"PRId64"us %s\n", us, text);
+ monotime(&ref);
+}
+static void ts_end(void)
+{
+ int64_t us;
+ us = monotime_since(&ref0, NULL);
+ printf("%7"PRId64"us total\n", us);
+}
+
+#define TYPE LIST
+#include "test_typelist.h"
+
+#define TYPE DLIST
+#include "test_typelist.h"
+
+#define TYPE ATOMLIST
+#include "test_typelist.h"
+
+#define TYPE HEAP
+#include "test_typelist.h"
+
+#define TYPE SORTLIST_UNIQ
+#include "test_typelist.h"
+
+#define TYPE SORTLIST_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE HASH
+#include "test_typelist.h"
+
+#define TYPE HASH_collisions
+#define REALTYPE HASH
+#define SHITTY_HASH
+#include "test_typelist.h"
+#undef SHITTY_HASH
+
+#define TYPE SKIPLIST_UNIQ
+#include "test_typelist.h"
+
+#define TYPE SKIPLIST_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE RBTREE_UNIQ
+#include "test_typelist.h"
+
+#define TYPE RBTREE_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE ATOMSORT_UNIQ
+#include "test_typelist.h"
+
+#define TYPE ATOMSORT_NONUNIQ
+#include "test_typelist.h"
+
+int main(int argc, char **argv)
+{
+ srandom(1);
+
+ test_LIST();
+ test_DLIST();
+ test_ATOMLIST();
+ test_HEAP();
+ test_SORTLIST_UNIQ();
+ test_SORTLIST_NONUNIQ();
+ test_HASH();
+ test_HASH_collisions();
+ test_SKIPLIST_UNIQ();
+ test_SKIPLIST_NONUNIQ();
+ test_RBTREE_UNIQ();
+ test_RBTREE_NONUNIQ();
+ test_ATOMSORT_UNIQ();
+ test_ATOMSORT_NONUNIQ();
+
+ log_memstats_stderr("test: ");
+ return 0;
+}
diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h
new file mode 100644
index 0000000000..b288f0bd8e
--- /dev/null
+++ b/tests/lib/test_typelist.h
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* C++ called, they want their templates back */
+#define item concat(item_, TYPE)
+#define itm concat(itm_, TYPE)
+#define head concat(head_, TYPE)
+#define list concat(TYPE, )
+#define list_head concat(TYPE, _head)
+#define list_item concat(TYPE, _item)
+#define list_cmp concat(TYPE, _cmp)
+#define list_hash concat(TYPE, _hash)
+#define list_init concat(TYPE, _init)
+#define list_fini concat(TYPE, _fini)
+#define list_first concat(TYPE, _first)
+#define list_next concat(TYPE, _next)
+#define list_next_safe concat(TYPE, _next_safe)
+#define list_count concat(TYPE, _count)
+#define list_add concat(TYPE, _add)
+#define list_add_head concat(TYPE, _add_head)
+#define list_add_tail concat(TYPE, _add_tail)
+#define list_add_after concat(TYPE, _add_after)
+#define list_find concat(TYPE, _find)
+#define list_find_lt concat(TYPE, _find_lt)
+#define list_find_gteq concat(TYPE, _find_gteq)
+#define list_del concat(TYPE, _del)
+#define list_pop concat(TYPE, _pop)
+
+#define ts_hash concat(ts_hash_, TYPE)
+
+#ifndef REALTYPE
+#define REALTYPE TYPE
+#endif
+
+PREDECL(REALTYPE, list)
+struct item {
+ uint64_t val;
+ struct list_item itm;
+ int scratchpad;
+};
+
+#if IS_SORTED(REALTYPE)
+static int list_cmp(const struct item *a, const struct item *b);
+
+#if IS_HASH(REALTYPE)
+static uint32_t list_hash(const struct item *a);
+DECLARE(REALTYPE, list, struct item, itm, list_cmp, list_hash)
+
+static uint32_t list_hash(const struct item *a)
+{
+#ifdef SHITTY_HASH
+ /* crappy hash to get some hash collisions */
+ return a->val ^ (a->val << 29) ^ 0x55AA0000U;
+#else
+ return jhash_1word(a->val, 0xdeadbeef);
+#endif
+}
+
+#else
+DECLARE(REALTYPE, list, struct item, itm, list_cmp)
+#endif
+
+static int list_cmp(const struct item *a, const struct item *b)
+{
+ if (a->val > b->val)
+ return 1;
+ if (a->val < b->val)
+ return -1;
+ return 0;
+}
+
+#else /* !IS_SORTED */
+DECLARE(REALTYPE, list, struct item, itm)
+#endif
+
+#define NITEM 10000
+struct item itm[NITEM];
+static struct list_head head = concat(INIT_, REALTYPE)(head);
+
+static void ts_hash(const char *text, const char *expect)
+{
+ int64_t us = monotime_since(&ref, NULL);
+ SHA256_CTX ctx;
+ struct item *item;
+ unsigned i = 0;
+ uint8_t hash[32];
+ char hashtext[65];
+ uint32_t count;
+
+ count = htonl(list_count(&head));
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, &count, sizeof(count));
+
+ frr_each (list, &head, item) {
+ struct {
+ uint32_t val_upper, val_lower, index;
+ } hashitem = {
+ htonl(item->val >> 32),
+ htonl(item->val & 0xFFFFFFFFULL),
+ htonl(i),
+ };
+ SHA256_Update(&ctx, &hashitem, sizeof(hashitem));
+ i++;
+ assert(i < count);
+ }
+ SHA256_Final(hash, &ctx);
+
+ for (i = 0; i < sizeof(hash); i++)
+ sprintf(hashtext + i * 2, "%02x", hash[i]);
+
+ printf("%7"PRId64"us %-25s %s%s\n", us, text,
+ expect ? " " : "*", hashtext);
+ if (expect && strcmp(expect, hashtext)) {
+ printf("%-21s %s\n", "EXPECTED:", expect);
+ assert(0);
+ }
+ monotime(&ref);
+}
+/* hashes will have different item ordering */
+#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE)
+#define ts_hashx(pos, csum) ts_hash(pos, NULL)
+#else
+#define ts_hashx(pos, csum) ts_hash(pos, csum)
+#endif
+
+static void concat(test_, TYPE)(void)
+{
+ size_t i, j, k, l;
+ struct prng *prng;
+ struct item *item, *prev __attribute__((unused));
+ struct item dummy __attribute__((unused));
+
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++)
+ itm[i].val = i;
+
+ printf("%s start\n", str(TYPE));
+ ts_start();
+
+ list_init(&head);
+ assert(list_first(&head) == NULL);
+
+ ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+#if IS_SORTED(REALTYPE)
+ prng = prng_new(0);
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+#if !IS_HEAP(REALTYPE)
+ else
+ assert(list_add(&head, &itm[j]) == &itm[j]);
+#endif
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+ k = 0;
+ prev = NULL;
+ frr_each(list, &head, item) {
+#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE)
+ /* hash table doesn't give sorting */
+ (void)prev;
+#else
+ assert(!prev || prev->val < item->val);
+#endif
+ prev = item;
+ k++;
+ }
+ assert(list_count(&head) == k);
+ ts_ref("walk");
+
+#if IS_UNIQ(REALTYPE)
+ prng_free(prng);
+ prng = prng_new(0);
+
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ dummy.val = j;
+ assert(list_find(&head, &dummy) == &itm[j]);
+ }
+ ts_ref("find");
+
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.val = j;
+ if (itm[j].scratchpad)
+ assert(list_add(&head, &dummy) == &itm[j]);
+ else {
+ assert(list_add(&head, &dummy) == NULL);
+ list_del(&head, &dummy);
+ }
+ }
+ ts_hashx("add-dup", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+#elif IS_HEAP(REALTYPE)
+ /* heap - partially sorted. */
+ prev = NULL;
+ l = k / 2;
+ for (i = 0; i < l; i++) {
+ item = list_pop(&head);
+ if (prev)
+ assert(prev->val < item->val);
+ item->scratchpad = 0;
+ k--;
+ prev = item;
+ }
+ ts_hash("pop", NULL);
+
+#else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.val = j;
+
+ list_add(&head, &dummy);
+ if (itm[j].scratchpad) {
+ struct item *lt, *gteq, dummy2;
+
+ assert(list_next(&head, &itm[j]) == &dummy ||
+ list_next(&head, &dummy) == &itm[j]);
+
+ memset(&dummy2, 0, sizeof(dummy));
+ dummy2.val = j;
+ lt = list_find_lt(&head, &dummy2);
+ gteq = list_find_gteq(&head, &dummy2);
+
+ assert(gteq == &itm[j] || gteq == &dummy);
+ if (lt)
+ assert(list_next(&head, lt) == &itm[j] ||
+ list_next(&head, lt) == &dummy);
+ else
+ assert(list_first(&head) == &itm[j] ||
+ list_first(&head) == &dummy);
+ } else if (list_next(&head, &dummy))
+ assert(list_next(&head, &dummy)->val > j);
+ list_del(&head, &dummy);
+ }
+ ts_hash("add-dup+find_{lt,gteq}", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+#endif
+#if !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE)
+ prng_free(prng);
+ prng = prng_new(123456);
+
+ l = 0;
+ for (i = 0; i < NITEM; i++) {
+ struct item *lt, *gteq, *tmp;
+
+ j = prng_rand(prng) % NITEM;
+ dummy.val = j;
+
+ lt = list_find_lt(&head, &dummy);
+ gteq = list_find_gteq(&head, &dummy);
+
+ if (lt) {
+ assert(lt->val < j);
+ tmp = list_next(&head, lt);
+ assert(tmp == gteq);
+ assert(!tmp || tmp->val >= j);
+ } else
+ assert(gteq == list_first(&head));
+
+ if (gteq)
+ assert(gteq->val >= j);
+ }
+ ts_ref("find_{lt,gteq}");
+#endif /* !IS_HASH */
+
+ prng_free(prng);
+ prng = prng_new(0);
+
+ l = 0;
+ for (i = 0; i < NITEM; i++) {
+ (void)prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ list_del(&head, &itm[j]);
+ itm[j].scratchpad = 0;
+ l++;
+ }
+ }
+ assert(l + list_count(&head) == k);
+ ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca");
+
+ frr_each_safe(list, &head, item) {
+ assert(item->scratchpad != 0);
+
+ if (item->val & 1) {
+ list_del(&head, item);
+ item->scratchpad = 0;
+ l++;
+ }
+ }
+ assert(l + list_count(&head) == k);
+ ts_hashx("frr_each_safe+del", "e0beb71dd963a75af05b722b8e71b61b304587d860c8accdc4349067542b86bb");
+
+#else /* !IS_SORTED */
+ prng = prng_new(0);
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add_tail(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
+
+ for (i = 0; i < NITEM / 2; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ list_del(&head, &itm[j]);
+ itm[j].scratchpad = 0;
+ k--;
+ }
+ }
+ ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564");
+
+ l = 0;
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ prng_free(prng);
+ prng = prng_new(0x1e5a2d69);
+
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add_head(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("fill / add_head", "3084d8f8a28b8c756ccc0a92d60d86f6d776273734ddc3f9e1d89526f5ca2795");
+
+ for (i = 0; i < NITEM / 2; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ list_del(&head, &itm[j]);
+ itm[j].scratchpad = 0;
+ k--;
+ }
+ }
+ ts_hash("del-prng", "dc916fa7ea4418792c7c8232d74df2887f9975ead4222f4b977be6bc0b52285e");
+
+ l = 0;
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ prng_free(prng);
+ prng = prng_new(0x692d1e5a);
+
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ if (prng_rand(prng) & 1) {
+ list_add_tail(&head, &itm[j]);
+ } else {
+ list_add_head(&head, &itm[j]);
+ }
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("fill / add_{head,tail}", "93fa180a575c96e4b6c3775c2de7843ee3254dd6ed5af699bbe155f994114b06");
+
+ for (i = 0; i < NITEM * 3; i++) {
+ int op = prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+
+ if (op & 1) {
+ /* delete or pop */
+ if (op & 2) {
+ item = list_pop(&head);
+ if (!item)
+ continue;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad == 0)
+ continue;
+ list_del(&head, item);
+ }
+ item->scratchpad = 0;
+ k--;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad != 0)
+ continue;
+
+ item->scratchpad = 1;
+ k++;
+
+ switch ((op >> 1) & 1) {
+ case 0:
+ list_add_head(&head, item);
+ break;
+ case 1:
+ list_add_tail(&head, item);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("prng add/del", "4909f31d06bb006efca4dfeebddb8de071733ddf502f89b6d532155208bbc6df");
+
+#if !IS_ATOMIC(REALTYPE)
+ /* variant with add_after */
+
+ for (i = 0; i < NITEM * 3; i++) {
+ int op = prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+
+ if (op & 1) {
+ /* delete or pop */
+ if (op & 2) {
+ item = list_pop(&head);
+ if (!item)
+ continue;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad == 0)
+ continue;
+ list_del(&head, item);
+ }
+ item->scratchpad = 0;
+ k--;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad != 0)
+ continue;
+
+ item->scratchpad = 1;
+ k++;
+
+ switch ((op >> 1) & 3) {
+ case 0:
+ list_add_head(&head, item);
+ break;
+ case 1:
+ list_add_tail(&head, item);
+ break;
+ case 2:
+ case 3:
+ prev = NULL;
+ l = 0;
+ do {
+ j = prng_rand(prng) % NITEM;
+ prev = &itm[j];
+ if (prev->scratchpad == 0
+ || prev == item)
+ prev = NULL;
+ l++;
+ } while (!prev && l < 10);
+ list_add_after(&head, prev, item);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("prng add/after/del", "84c5fc83294eabebb9808ccbba32a303c4fca084db87ed1277d2bae1f8c5bee4");
+#endif
+
+ l = 0;
+#endif
+
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ list_fini(&head);
+ ts_ref("fini");
+ ts_end();
+ printf("%s end\n", str(TYPE));
+}
+
+#undef ts_hashx
+
+#undef item
+#undef itm
+#undef head
+#undef list
+#undef list_head
+#undef list_item
+#undef list_cmp
+#undef list_hash
+#undef list_init
+#undef list_fini
+#undef list_first
+#undef list_next
+#undef list_next_safe
+#undef list_count
+#undef list_add
+#undef list_add_head
+#undef list_add_tail
+#undef list_add_after
+#undef list_find
+#undef list_find_lt
+#undef list_find_gteq
+#undef list_del
+#undef list_pop
+
+#undef REALTYPE
+#undef TYPE
diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py
new file mode 100644
index 0000000000..0b3c743971
--- /dev/null
+++ b/tests/lib/test_typelist.py
@@ -0,0 +1,19 @@
+import frrtest
+
+class TestTypelist(frrtest.TestMultiOut):
+ program = './test_typelist'
+
+TestTypelist.onesimple('LIST end')
+TestTypelist.onesimple('DLIST end')
+TestTypelist.onesimple('ATOMLIST end')
+TestTypelist.onesimple('HEAP end')
+TestTypelist.onesimple('SORTLIST_UNIQ end')
+TestTypelist.onesimple('SORTLIST_NONUNIQ end')
+TestTypelist.onesimple('HASH end')
+TestTypelist.onesimple('HASH_collisions end')
+TestTypelist.onesimple('SKIPLIST_UNIQ end')
+TestTypelist.onesimple('SKIPLIST_NONUNIQ end')
+TestTypelist.onesimple('RBTREE_UNIQ end')
+TestTypelist.onesimple('RBTREE_NONUNIQ end')
+TestTypelist.onesimple('ATOMSORT_UNIQ end')
+TestTypelist.onesimple('ATOMSORT_NONUNIQ end')
diff --git a/tests/subdir.am b/tests/subdir.am
index 365fe00cc6..ec5fea705e 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -47,6 +47,7 @@ tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c
check_PROGRAMS = \
tests/lib/cxxcompat \
+ tests/lib/test_atomlist \
tests/lib/test_buffer \
tests/lib/test_checksum \
tests/lib/test_heavy_thread \
@@ -59,12 +60,14 @@ check_PROGRAMS = \
tests/lib/test_ringbuf \
tests/lib/test_srcdest_table \
tests/lib/test_segv \
+ tests/lib/test_seqlock \
tests/lib/test_sig \
tests/lib/test_stream \
tests/lib/test_table \
tests/lib/test_timer_correctness \
tests/lib/test_timer_performance \
tests/lib/test_ttable \
+ tests/lib/test_typelist \
tests/lib/test_zlog \
tests/lib/test_graph \
tests/lib/cli/test_cli \
@@ -103,6 +106,7 @@ noinst_HEADERS += \
tests/helpers/c/prng.h \
tests/helpers/c/tests.h \
tests/lib/cli/common_cli.h \
+ tests/lib/test_typelist.h \
# end
#
@@ -189,6 +193,10 @@ tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD)
tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c
nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c
+tests_lib_test_atomlist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_atomlist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_atomlist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_atomlist_SOURCES = tests/lib/test_atomlist.c
tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD)
@@ -236,6 +244,10 @@ tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_segv_SOURCES = tests/lib/test_segv.c
+tests_lib_test_seqlock_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_seqlock_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_seqlock_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_seqlock_SOURCES = tests/lib/test_seqlock.c
tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
@@ -264,6 +276,10 @@ tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c
+tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c
tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD)
@@ -301,6 +317,7 @@ EXTRA_DIST += \
tests/lib/northbound/test_oper_data.in \
tests/lib/northbound/test_oper_data.py \
tests/lib/northbound/test_oper_data.refout \
+ tests/lib/test_atomlist.py \
tests/lib/test_nexthop_iter.py \
tests/lib/test_ringbuf.py \
tests/lib/test_srcdest_table.py \
@@ -310,6 +327,7 @@ EXTRA_DIST += \
tests/lib/test_timer_correctness.py \
tests/lib/test_ttable.py \
tests/lib/test_ttable.refout \
+ tests/lib/test_typelist.py \
tests/lib/test_zlog.py \
tests/lib/test_graph.py \
tests/lib/test_graph.refout \
diff --git a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref
index c29ed3db61..1e8f67f3f9 100644
--- a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref
+++ b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref
@@ -1,5 +1,5 @@
r1-eth0 is up
- ifindex 2, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
+ ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0
MTU mismatch detection: enabled
Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
@@ -10,7 +10,7 @@ r1-eth0 is up
Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0
r1-eth3 is up
- ifindex 5, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
+ ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
MTU mismatch detection: enabled
Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
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 239de55bd6..9658c080c0 100755
--- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py
+++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py
@@ -480,6 +480,8 @@ def test_ospfv2_interfaces():
actual = net['r%s' % i].cmd('vtysh -c "show ip ospf interface" 2> /dev/null').rstrip()
# Mask out Bandwidth portion. They may change..
actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
+ actual = re.sub(r"ifindex [0-9]", "ifindex X", actual)
+
# Drop time in next due
actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
# Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/__init__.py b/tests/topotests/bfd-bgp-cbit-topo3/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/__init__.py
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json
new file mode 100644
index 0000000000..54ae57f7be
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json
@@ -0,0 +1,103 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "10.254.254.1",
+ "localAS": 101,
+ "routes":
+ {
+ "2001:db8:6::/64": [
+ {
+ "stale": true,
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:6::",
+ "prefixLen": 64,
+ "network": "2001:db8:6::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001:db8:4::1",
+ "origin": "IGP",
+ "nexthops": [
+ { "ip": "2001:db8:4::1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [
+ {
+ "stale": true,
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:7::",
+ "prefixLen": 64, "network":
+ "2001:db8:7::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001:db8:4::1",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "2001:db8:4::1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:8::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:8::",
+ "prefixLen": 64,
+ "network": "2001:db8:8::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:9::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:9::",
+ "prefixLen": 64,
+ "network": "2001:db8:9::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf
new file mode 100644
index 0000000000..fa6d60a8fc
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf
@@ -0,0 +1,20 @@
+debug bgp neighbor-events
+router bgp 101
+ bgp router-id 10.254.254.1
+ timers bgp 8 24
+ bgp graceful-restart
+ neighbor 2001:db8:4::1 remote-as 102
+ neighbor 2001:db8:4::1 remote-as external
+ neighbor 2001:db8:4::1 bfd
+ neighbor 2001:db8:4::1 bfd check-control-plane-failure
+ neighbor 2001:db8:4::1 update-source 2001:db8:1::1
+ neighbor 2001:db8:4::1 ebgp-multihop 5
+ address-family ipv4 unicast
+ no neighbor 2001:db8:4::1 activate
+ exit-address-family
+ address-family ipv6 unicast
+ network 2001:db8:8::/64
+ network 2001:db8:9::/64
+ neighbor 2001:db8:4::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json
new file mode 100644
index 0000000000..8eea183285
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json
@@ -0,0 +1,80 @@
+{
+ "2001:db8:1::/64": [{
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [{
+ "directlyConnected": true,
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:4::/64": [{
+ "distance": 1,
+ "protocol": "static",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [{
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "2001:db8:6::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:6::/64",
+ "nexthops": [{
+ "ip":"2001:db8:4::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:1::2",
+ "afi": "ipv6",
+ "interfaceName": "r1-eth0"
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:7::/64",
+ "nexthops": [{
+ "ip":"2001:db8:4::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive": true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:1::2",
+ "afi": "ipv6",
+ "interfaceName":"r1-eth0"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json
new file mode 100644
index 0000000000..d1927ae49a
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json
@@ -0,0 +1,16 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:4::1",
+ "local":"2001:db8:1::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-interval":50
+ }
+]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json
new file mode 100644
index 0000000000..25b47f18ec
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json
@@ -0,0 +1,14 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:4::1",
+ "local":"2001:db8:1::1",
+ "status":"up",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-interval":50
+ }
+]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf
new file mode 100644
index 0000000000..3a30cd42fb
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf
@@ -0,0 +1,8 @@
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ipv6 address 2001:db8:1::1/64
+!
+ipv6 route 2001:db8:4::/64 2001:db8:1::2
+
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf
new file mode 100644
index 0000000000..0f70be1bda
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf
@@ -0,0 +1,9 @@
+ip forwarding
+ipv6 forwarding
+!
+interface r2-eth0
+ ipv6 address 2001:db8:1::2/64
+!
+interface r2-eth1
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json
new file mode 100644
index 0000000000..a3bb222504
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json
@@ -0,0 +1,55 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "10.254.254.3",
+ "localAS": 102,
+ "routes":
+ {
+ "2001:db8:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:6::",
+ "prefixLen": 64,
+ "network": "2001:db8:6::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:7::",
+ "prefixLen": 64,
+ "network": "2001:db8:7::\/64",
+ "med": 0,
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf
new file mode 100644
index 0000000000..ea5334029c
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf
@@ -0,0 +1,25 @@
+debug bgp neighbor-events
+router bgp 102
+ bgp router-id 10.254.254.3
+ timers bgp 20 60
+ bgp graceful-restart
+ ! simulate NSF machine
+ bgp graceful-restart preserve-fw-state
+ bgp graceful-restart stalepath-time 900
+ bgp graceful-restart restart-time 900
+ neighbor 2001:db8:1::1 remote-as 101
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 update-source 2001:db8:4::1
+ neighbor 2001:db8:1::1 bfd
+ neighbor 2001:db8:1::1 ebgp-multihop 5
+ !
+ address-family ipv4 unicast
+ no neighbor 2001:db8:1::1 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ neighbor 2001:db8:1::1 activate
+ network 2001:db8:6::/64
+ network 2001:db8:7::/64
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json
new file mode 100644
index 0000000000..09808cc09a
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json
@@ -0,0 +1,80 @@
+{
+ "2001:db8:1::/64": [{
+ "distance": 1,
+ "protocol": "static",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [{
+ "interfaceName": "r3-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "2001:db8:4::/64": [{
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [{
+ "directlyConnected": true,
+ "interfaceName": "r3-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:8::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:8::/64",
+ "nexthops": [{
+ "ip":"2001:db8:1::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:4::2",
+ "afi": "ipv6",
+ "interfaceName":"r3-eth0"
+ }
+ ]
+ }
+ ],
+ "2001:db8:9::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:9::/64",
+ "nexthops": [{
+ "ip":"2001:db8:1::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:4::2",
+ "afi": "ipv6",
+ "interfaceName":"r3-eth0"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json
new file mode 100644
index 0000000000..5193f2a6e2
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json
@@ -0,0 +1,16 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:1::1",
+ "local":"2001:db8:4::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-interval":50
+ }
+]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json
new file mode 100644
index 0000000000..9e4bd2633f
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json
@@ -0,0 +1,14 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:1::1",
+ "local":"2001:db8:4::1",
+ "status":"down",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-interval":50
+ }
+]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf
new file mode 100644
index 0000000000..7759251dc5
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf
@@ -0,0 +1,7 @@
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ipv6 address 2001:db8:4::1/64
+!
+ipv6 route 2001:db8:1::/64 2001:db8:4::2
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot
new file mode 100644
index 0000000000..270de829c3
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot
@@ -0,0 +1,58 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="bfd-topo2";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+ r2 -- sw2 [label="eth1"];
+ r3 -- sw2 [label="eth0"];
+}
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py
new file mode 100755
index 0000000000..59858d6fd3
--- /dev/null
+++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_bgp_cbit_topo3.py
+#
+# Copyright (c) 2019 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bfd_bgp_cbit_topo3.py: Test the FRR/Quagga BFD daemon with multihop and BGP
+unnumbered.
+"""
+
+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.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+class BFDTopo(Topo):
+ "Test topology builder"
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 4 routers.
+ 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 = tgen.add_switch('s2')
+ switch.add_link(tgen.gears['r2'])
+ switch.add_link(tgen.gears['r3'])
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(BFDTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_BFD,
+ os.path.join(CWD, '{}/bfdd.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # Verify that we are using the proper version and that the BFD
+ # daemon exists.
+ for router in router_list.values():
+ # Check for Version
+ if router.has_version('<', '5.1'):
+ tgen.set_error('Unsupported FRR version')
+ break
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged before checking for the BFD
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv6 routing tables.
+ logger.info("Checking IPv6 routes for convergence")
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info('skipping file {}'.format(json_file))
+ continue
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show ipv6 route json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=40,
+ wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('waiting for bfd peers to go up')
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ json_file = '{}/{}/peers.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bfd peers json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+def test_bfd_loss_intermediate():
+ """
+ Assert that BFD notices the bfd link down failure.
+ but BGP entries should still be present
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('removing IPv6 address from r2 to simulate loss of connectivity')
+ # Disable r2-eth0 ipv6 address
+ cmd = 'vtysh -c \"configure terminal\" -c \"interface r2-eth1\" -c "no ipv6 address 2001:db8:4::2/64\"'
+ tgen.net['r2'].cmd(cmd)
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info('waiting for BFD converge down')
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ json_file = '{}/{}/peers_down.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bfd peers json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info('waiting for BGP entries to become stale')
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ json_file = '{}/{}/bgp_ipv6_routes_down.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bgp ipv6 json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info("Checking IPv6 routes on r1 should still be present")
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ if router.name == 'r3':
+ continue
+ json_file = '{}/r1/ipv6_routes.json'.format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show ipv6 route json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=30,
+ wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+def test_bfd_comes_back_again():
+ """
+ Assert that BFD notices the bfd link up
+ and that ipv6 entries appear back
+ """
+ tgen = get_topogen()
+ logger.info('re-adding IPv6 address from r2 to simulate connectivity is back')
+ # adds back r2-eth0 ipv6 address
+ cmd = 'vtysh -c \"configure terminal\" -c \"interface r2-eth1\" -c "ipv6 address 2001:db8:4::2/64\"'
+ tgen.net['r2'].cmd(cmd)
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info('waiting for BFD to converge up')
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ if router.name == 'r2':
+ continue
+ json_file = '{}/{}/peers.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bfd peers json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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/bfd-vrf-topo1/__init__.py b/tests/topotests/bfd-vrf-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/__init__.py
diff --git a/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf
new file mode 100644
index 0000000000..3466e6a3ca
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ peer 192.168.0.2 vrf r1-cust1
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json
new file mode 100644
index 0000000000..4b2cc1ad62
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.2/32": [
+ {
+ "aspath": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "aspath": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "aspath": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json
new file mode 100644
index 0000000000..fa07d60df9
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 101,
+ "peers": {
+ "192.168.0.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf
new file mode 100644
index 0000000000..7ad4e2bd74
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 101 vrf r1-cust1
+ neighbor 192.168.0.2 remote-as 102
+! neighbor 192.168.0.2 ebgp-multihop 10
+ neighbor 192.168.0.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r1/peers.json b/tests/topotests/bfd-vrf-topo1/r1/peers.json
new file mode 100644
index 0000000000..f49768ff75
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/peers.json
@@ -0,0 +1,8 @@
+[
+ {
+ "remote-receive-interval": 1000,
+ "remote-transmit-interval": 500,
+ "peer": "192.168.0.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd-vrf-topo1/r1/zebra.conf b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf
new file mode 100644
index 0000000000..fcd1e7db17
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf
@@ -0,0 +1,3 @@
+interface r1-eth0 vrf r1-cust1
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf
new file mode 100644
index 0000000000..3481ea8c87
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf
@@ -0,0 +1,12 @@
+bfd
+ peer 192.168.0.1 vrf r2-cust1
+ receive-interval 1000
+ transmit-interval 500
+ echo-mode
+ no shutdown
+ !
+ peer 192.168.1.1 vrf r2-cust1
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json
new file mode 100644
index 0000000000..39f3c0a835
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "aspath": "101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.0.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "aspath": "103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.1.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "aspath": "104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.2.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json
new file mode 100644
index 0000000000..c0ef11ac5f
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json
@@ -0,0 +1,19 @@
+{
+ "ipv4Unicast": {
+ "as": 102,
+ "peers": {
+ "192.168.0.1": {
+ "remoteAs": 101,
+ "state": "Established"
+ },
+ "192.168.1.1": {
+ "remoteAs": 103,
+ "state": "Established"
+ },
+ "192.168.2.1": {
+ "remoteAs": 104,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf
new file mode 100644
index 0000000000..0715ea32a5
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 102 vrf r2-cust1
+ neighbor 192.168.0.1 remote-as 101
+ neighbor 192.168.0.1 bfd
+ neighbor 192.168.1.1 remote-as 103
+ neighbor 192.168.1.1 bfd
+ neighbor 192.168.2.1 remote-as 104
+ neighbor 192.168.2.1 bfd
+ address-family ipv4 unicast
+ network 10.254.254.2/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r2/peers.json b/tests/topotests/bfd-vrf-topo1/r2/peers.json
new file mode 100644
index 0000000000..5035d643c5
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "peer": "192.168.0.1",
+ "status": "up"
+ },
+ {
+ "remote-echo-interval": 100,
+ "peer": "192.168.1.1",
+ "status": "up"
+ },
+ {
+ "remote-transmit-interval": 2000,
+ "remote-receive-interval": 2000,
+ "peer": "192.168.2.1",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd-vrf-topo1/r2/zebra.conf b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf
new file mode 100644
index 0000000000..daffd1912e
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf
@@ -0,0 +1,9 @@
+interface r2-eth0 vrf r2-cust1
+ ip address 192.168.0.2/24
+!
+interface r2-eth1 vrf r2-cust1
+ ip address 192.168.1.2/24
+!
+interface r2-eth2 vrf r2-cust1
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf
new file mode 100644
index 0000000000..f6921b7818
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf
@@ -0,0 +1,7 @@
+bfd
+ peer 192.168.1.2 vrf r3-cust1
+ echo-interval 100
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json
new file mode 100644
index 0000000000..c92d4e052a
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "aspath": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "aspath": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "aspath": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json
new file mode 100644
index 0000000000..d47833377b
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 103,
+ "peers": {
+ "192.168.1.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf
new file mode 100644
index 0000000000..277f027d5b
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 103 vrf r3-cust1
+ neighbor 192.168.1.2 remote-as 102
+ neighbor 192.168.1.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.3/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r3/peers.json b/tests/topotests/bfd-vrf-topo1/r3/peers.json
new file mode 100644
index 0000000000..ef38008643
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.1.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd-vrf-topo1/r3/zebra.conf b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf
new file mode 100644
index 0000000000..f727c2d633
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf
@@ -0,0 +1,3 @@
+interface r3-eth0 vrf r3-cust1
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf
new file mode 100644
index 0000000000..a56a3a0d37
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf
@@ -0,0 +1,7 @@
+bfd
+ peer 192.168.2.2 vrf r4-cust1
+ transmit-interval 2000
+ receive-interval 2000
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json
new file mode 100644
index 0000000000..cc8510dd61
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "aspath": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "aspath": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "aspath": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json
new file mode 100644
index 0000000000..7d81784b56
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 104,
+ "peers": {
+ "192.168.2.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf
new file mode 100644
index 0000000000..66bf28587d
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 104 vrf r4-cust1
+ neighbor 192.168.2.2 remote-as 102
+ neighbor 192.168.2.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.4/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-vrf-topo1/r4/peers.json b/tests/topotests/bfd-vrf-topo1/r4/peers.json
new file mode 100644
index 0000000000..37140089e1
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.2.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd-vrf-topo1/r4/zebra.conf b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf
new file mode 100644
index 0000000000..69770dd2bf
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf
@@ -0,0 +1,3 @@
+interface r4-eth0 vrf r4-cust1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot
new file mode 100644
index 0000000000..c84ace2780
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot
@@ -0,0 +1,73 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="bfd-topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n192.168.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n192.168.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0\n.1"];
+ r2 -- sw1 [label="eth0\n.2"];
+
+ r3 -- sw2 [label="eth0\n.1"];
+ r2 -- sw2 [label="eth1\n.2"];
+
+ r4 -- sw3 [label="eth0\n.1"];
+ r2 -- sw3 [label="eth2\n.2"];
+}
diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg
new file mode 100644
index 0000000000..4d6d56e072
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg
Binary files differ
diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py
new file mode 100755
index 0000000000..e2933820bd
--- /dev/null
+++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_vrf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+# Copyright (c) 2019 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bfd_vrf_topo1.py: Test the FRR/Quagga BFD daemon.
+"""
+
+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.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+class BFDTopo(Topo):
+ "Test topology builder"
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 4 routers
+ 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):
+ "Sets up the pytest environment"
+ tgen = Topogen(BFDTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # check for zebra capability
+ for rname, router in router_list.iteritems():
+ if router.check_capability(
+ TopoRouter.RD_ZEBRA,
+ '--vrfwnetns'
+ ) == False:
+ return pytest.skip('Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR')
+
+ if os.system('ip netns list') != 0:
+ return pytest.skip('Skipping BFD Topo1 VRF NETNS Test. NETNS not available on System')
+
+ logger.info('Testing with VRF Namespace support')
+
+ cmds = ['if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi',
+ 'ip netns add {0}-cust1',
+ 'ip link set dev {0}-eth0 netns {0}-cust1',
+ 'ip netns exec {0}-cust1 ifconfig {0}-eth0 up']
+ cmds2 = ['ip link set dev {0}-eth1 netns {0}-cust1',
+ 'ip netns exec {0}-cust1 ifconfig {0}-eth1 up',
+ 'ip link set dev {0}-eth2 netns {0}-cust1',
+ 'ip netns exec {0}-cust1 ifconfig {0}-eth2 up']
+
+ for rname, router in router_list.iteritems():
+ # create VRF rx-cust1 and link rx-eth0 to rx-cust1
+ for cmd in cmds:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+ if rname == 'r2':
+ for cmd in cmds2:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)),
+ '--vrfwnetns'
+ )
+ router.load_config(
+ TopoRouter.RD_BFD,
+ os.path.join(CWD, '{}/bfdd.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # Verify that we are using the proper version and that the BFD
+ # daemon exists.
+ for router in router_list.values():
+ # Check for Version
+ if router.has_version('<', '5.1'):
+ tgen.set_error('Unsupported FRR version')
+ break
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ # move back rx-eth0 to default VRF
+ # delete rx-vrf
+ cmds = ['ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1',
+ 'ip netns delete {0}-cust1']
+ cmds2 = ['ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1',
+ 'ip netns exec {0}-cust2 ip link set {0}-eth1 netns 1']
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ if rname == 'r2':
+ for cmd in cmds2:
+ tgen.net[rname].cmd(cmd.format(rname))
+ for cmd in cmds:
+ tgen.net[rname].cmd(cmd.format(rname))
+ tgen.stop_topology()
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('waiting for bfd peers to go up')
+ for router in tgen.routers().values():
+ json_file = '{}/{}/peers.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bfd peers json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bgp_convergence():
+ "Assert that BGP is converging."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('waiting for bgp peers to go up')
+
+ for router in tgen.routers().values():
+ ref_file = '{}/{}/bgp_summary.json'.format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show ip bgp vrf {}-cust1 summary json'.format(router.name), expected)
+ _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0)
+ assertmsg = '{}: bgp did not converge'.format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_convergence():
+ "Assert that BGP is converging before setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('waiting for bgp peers converge')
+
+ for router in tgen.routers().values():
+ ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show ip bgp vrf {}-cust1 json'.format(router.name), expected)
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+ assertmsg = '{}: bgp did not converge'.format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bfd_fast_convergence():
+ """
+ Assert that BFD notices the link down after simulating network
+ failure.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Disable r2-eth0 link
+ router2 = tgen.gears['r2']
+ topotest.interface_set_status(router2, 'r2-eth0', ifaceaction=False, vrf_name='r2-cust1')
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info('waiting for BFD converge')
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ json_file = '{}/{}/peers.json'.format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ # Load the same file as previous test, but expect R1 to be down.
+ if router.name == 'r1':
+ for peer in expected:
+ if peer['peer'] == '192.168.0.2':
+ peer['status'] = 'down'
+ else:
+ for peer in expected:
+ if peer['peer'] == '192.168.0.1':
+ peer['status'] = 'down'
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show bfd peers json', expected)
+ _, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_reconvergence():
+ "Assert that BGP is converging after setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('waiting for BGP re convergence')
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+
+ # Load the same file as previous test, but set networks to None
+ # to test absence.
+ if router.name == 'r1':
+ expected['routes']['10.254.254.2/32'] = None
+ expected['routes']['10.254.254.3/32'] = None
+ expected['routes']['10.254.254.4/32'] = None
+ else:
+ expected['routes']['10.254.254.1/32'] = None
+
+ test_func = partial(topotest.router_json_cmp,
+ router, 'show ip bgp vrf {}-cust1 json'.format(router.name), expected)
+ _, res = topotest.run_and_expect(
+ test_func,
+ None,
+ count=3,
+ wait=1
+ )
+ assertmsg = '{}: bgp did not converge'.format(router.name)
+ assert res is None, assertmsg
+
+
+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-vrf-route-leak-basic/test_bgp.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py
index b0d60403db..b0d60403db 100644..100755
--- a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py
+++ b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py
diff --git a/tests/topotests/bgp_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/__init__.py
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf
new file mode 100644
index 0000000000..e06fa08b57
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.255.2 route-map outgoing out
+!
+ip prefix-list peer-out permit 172.16.255.254/32
+route-map outgoing permit 10
+ match ip address prefix-list peer-out
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf
new file mode 100644
index 0000000000..0a283c06d5
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf
new file mode 100644
index 0000000000..0549697ff0
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf
@@ -0,0 +1,2 @@
+router bgp 1000
+ neighbor 192.168.255.1 remote-as 500
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf
new file mode 100644
index 0000000000..606c17bec9
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf
new file mode 100644
index 0000000000..b4e304d82a
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ redistribute connected
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf
new file mode 100644
index 0000000000..39499a198d
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r3-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf
new file mode 100644
index 0000000000..0549697ff0
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf
@@ -0,0 +1,2 @@
+router bgp 1000
+ neighbor 192.168.255.1 remote-as 500
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf
new file mode 100644
index 0000000000..b85911504e
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
new file mode 100644
index 0000000000..eecacfd00c
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# bgp_ebgp_requires_policy.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+bgp_ebgp_requires_policy.py:
+
+Test if eBGP sender without a filter applied to the peer is allowed
+to send advertisements.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ 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['r3'])
+ switch.add_link(tgen.gears['r4'])
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+ 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))
+ )
+
+ tgen.start_router()
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_bgp_remove_private_as():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ while True:
+ cmd = "show ip bgp neighbor 192.168.255.1 json"
+ output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
+ if output['192.168.255.1']['bgpState'] == 'Established':
+ time.sleep(3)
+ return True
+
+ def _bgp_ebgp_requires_policy(router):
+ cmd = "show ip bgp 172.16.255.254/32 json"
+ output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
+ if 'prefix' in output:
+ return True
+ return False
+
+ if _bgp_converge('r2'):
+ assert _bgp_ebgp_requires_policy('r2') == True
+
+ if _bgp_converge('r4'):
+ assert _bgp_ebgp_requires_policy('r4') == False
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
index 4bd0f95f2c..a38fb1e9a1 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -15,6 +15,7 @@ router bgp 5227
network 99.0.0.1/32
network 5.1.0.0/24 route-map rm-nh
network 5.1.1.0/24 route-map rm-nh
+ redistribute sharp route-map sharp-nh
neighbor 192.168.1.1 activate
exit-address-family
!
@@ -29,6 +30,15 @@ route-map rm-nh permit 10
set extcommunity rt 89:123
set community 0:67
!
+route-map sharp-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.1
+ set local-preference 200
+ set metric 200
+ set large-community 90:12:34
+ set extcommunity rt 80:987
+ set community 0:65
+!
end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
index 2115f08741..3aeb9f9c9f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -15,6 +15,7 @@ router bgp 5227
network 99.0.0.2/32
network 5.1.0.0/24 route-map rm-nh
network 5.1.1.0/24 route-map rm-nh
+ redistribute sharp route-map sharp-nh
neighbor 192.168.1.1 activate
exit-address-family
!
@@ -29,6 +30,15 @@ route-map rm-nh permit 10
set extcommunity rt 89:123
set community 0:67
!
+route-map sharp-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.2
+ set local-preference 200
+ set metric 200
+ set large-community 78:90:12
+ set extcommunity rt 70:456
+ set community 0:66
+!
end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
index 2004612557..c4b6ac9bb4 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
@@ -7,7 +7,7 @@ log monitor notifications
log commands
log file bgpd.log
-debug bgp vpn label
+#debug bgp vpn label
router bgp 5226
bgp router-id 3.3.3.3
bgp cluster-id 3.3.3.3
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
index b2df5990ce..6295406e69 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
@@ -7,9 +7,9 @@ log monitor notifications
log commands
log file bgpd.log debug
-debug bgp vpn label
-debug bgp nht
-debug bgp zebra
+#debug bgp vpn label
+#debug bgp nht
+#debug bgp zebra
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py
index f5d73a8c49..149a420a32 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py
@@ -1,5 +1,6 @@
from lutil import luCommand
from customize import l3mdev_accept
+
l3mdev_rtrs = ['r1', 'r3', 'r4', 'ce4']
for rtr in l3mdev_rtrs:
luCommand(rtr,'sysctl net.ipv4.tcp_l3mdev_accept',' = \d*','none','')
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
new file mode 100644
index 0000000000..897fc48436
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py
@@ -0,0 +1,20 @@
+from lutil import luCommand
+ret = luCommand('ce1', 'vtysh -c "show ip route" | grep -c \\ 10\\.\\*/32','(.*)','pass', 'Looking for sharp routes')
+found = luLast()
+if ret != False and found != None:
+ num = int(found.group())
+ luCommand('ce3', 'vtysh -c "show bgp sum"',
+ '.', 'pass', 'See %s sharp routes' % num)
+ if num > 0:
+ wait = num/500
+ luCommand('ce1', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num))
+ luCommand('ce2', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num))
+ rtrs = ['ce1', 'ce2', 'ce3']
+ for rtr in rtrs:
+ luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32','^0$', 'wait', 'BGP routes removed', wait)
+ for rtr in rtrs:
+ luCommand(rtr, 'ip route show | grep -c \\^10\\.','^0$', 'wait', 'Linux routes removed', wait)
+ rtrs = ['r1', 'r3', 'r4']
+ for rtr in rtrs:
+ luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr),'^0$','wait','VRF route removed',wait)
+#done
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
new file mode 100644
index 0000000000..3b3aac5fbe
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
@@ -0,0 +1,66 @@
+from lutil import luCommand
+num = 50000
+b = int(num/(256*256))
+if b > 0:
+ r = num - b * (256*256)
+else:
+ r = num
+c = int(r/256)
+if c > 0:
+ d = r - c * 256 - 1
+else:
+ d = r
+wait = num/1000
+mem = {}
+rtrs = ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3', 'r4']
+for rtr in rtrs:
+ mem[rtr] = {'value': 0, 'units': 'unknown'}
+ ret = luCommand(rtr, 'vtysh -c "show memory"', 'bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)', 'none', 'collect bgpd memory stats')
+ found = luLast()
+ if ret != False and found != None:
+ mem[rtr] = {'value': int(found.group(1)), 'units': found.group(2)}
+
+luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'none','check if sharpd running')
+doSharp = True
+found = luLast()
+if ret != False and found != None:
+ if len(found.group()):
+ luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'pass','sharpd NOT running, skipping test')
+ doSharp = False
+
+if doSharp == True:
+ luCommand('ce1', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),'','pass','Adding {} routes'.format(num))
+ luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num))
+ rtrs = ['ce1', 'ce2', 'ce3']
+ for rtr in rtrs:
+ luCommand(rtr, 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b,c,d), 'Last update:', 'wait', 'RXed last route, 10.{}.{}.{}'.format(b,c,d), wait)
+ luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', str(num), 'wait', 'See all sharp routes in BGP', wait)
+ luCommand('r1', 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','RXed -> 10.{}.{}.{} from CE1'.format(b,c,d), wait)
+ luCommand('r3', 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','RXed -> 10.{}.{}.{} from CE2'.format(b,c,d), wait)
+ luCommand('r1', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d))
+ luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d))
+ luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'1.1.1.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d))
+ luCommand('r1', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'3.3.3.3','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d))
+ luCommand('r4', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'1.1.1.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d))
+ luCommand('r4', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'3.3.3.3','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d))
+ rtrs = ['ce1', 'ce2', 'ce3']
+ for rtr in rtrs:
+ luCommand(rtr, 'ip route get 10.{}.{}.{}'.format(b,c,d),'dev','wait','Route to 10.{}.{}.{} available'.format(b,c,d), wait)
+ luCommand(rtr, 'ip route show | grep -c \\^10\\.', str(num), 'wait', 'See {} linux routes'.format(num), wait)
+
+ rtrs = ['r1', 'r3', 'r4']
+ for rtr in rtrs:
+ luCommand(rtr, 'ip route get vrf {}-cust1 10.{}.{}.{}'.format(rtr,b,c,d),'dev','wait','VRF route available',wait)
+ luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr), str(num), 'wait','See {} linux routes'.format(num), wait)
+ rtrs = ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3', 'r4']
+ for rtr in rtrs:
+ ret = luCommand(rtr, 'vtysh -c "show memory"', 'bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)', 'none', 'collect bgpd memory stats')
+ found = luLast()
+ if ret != False and found != None:
+ val = int(found.group(1))
+ if mem[rtr]['units'] != found.group(2):
+ val *= 1000
+ delta = val - int(mem[rtr]['value'])
+ ave = float(delta)/float(num)
+ luCommand(rtr, 'vtysh -c "show thread cpu"', '.', 'pass', 'BGPd heap: {0} {1} --> {2} {3} ({4} {1}/route)'.format(mem[rtr]['value'], mem[rtr]['units'], found.group(1), found.group(2), round(ave,4)))
+#done
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
index 1da1066f0e..2dad5e7687 100755
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
@@ -75,6 +75,24 @@ def test_check_linux_mpls():
#CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
ltemplateTest('scripts/check_linux_mpls.py', False, CliOnFail, CheckFunc)
+def test_check_scale_up():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ #CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')'
+ #uncomment next line to start cli *before* script is run
+ #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest('scripts/scale_up.py', False, CliOnFail, CheckFunc)
+
+def test_check_scale_down():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ #CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')'
+ #uncomment next line to start cli *before* script is run
+ #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest('scripts/scale_down.py', False, CliOnFail, CheckFunc)
+
def SKIP_test_cleanup_all():
CliOnFail = None
# For debugging, uncomment the next line
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
index 096e97fa94..e9c1916f75 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
@@ -10,7 +10,8 @@ luCommand('r3','vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"','status
luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed')
luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed')
luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed')
-luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI')
+#luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI')
+luCommand('r4','vtysh -c "clear vnc nve *"','.', 'pass', 'Cleared NVEs')
luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared')
luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared')
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py b/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf
new file mode 100644
index 0000000000..235b42b3d5
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 65000
+ neighbor 192.168.255.2 remote-as 65001
+ address-family ipv4 unicast
+ redistribute connected
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf
new file mode 100644
index 0000000000..0a283c06d5
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf
new file mode 100644
index 0000000000..c05bfd5a6b
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65001
+ bgp default show-hostname
+ neighbor 192.168.255.1 remote-as 65000
+ address-family ipv4 unicast
+ redistribute connected
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf
new file mode 100644
index 0000000000..5abba71ce8
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.253/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
new file mode 100644
index 0000000000..59ffd36ef3
--- /dev/null
+++ b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_show_ip_bgp_fqdn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_show_ip_bgp_fqdn.py:
+Test if FQND is visible in `show [ip] bgp` output if
+`bgp default show-hostname` is toggled.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ for routern in range(1, 3):
+ tgen.add_router('r{}'.format(routern))
+
+ switch = tgen.add_switch('s1')
+ switch.add_link(tgen.gears['r1'])
+ switch.add_link(tgen.gears['r2'])
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+ 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))
+ )
+
+ tgen.start_router()
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_bgp_maximum_prefix_invalid():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router, neighbor):
+ cmd = "show ip bgp neighbor {0} json".format(neighbor)
+ while True:
+ output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
+ if output[neighbor]['bgpState'] == 'Established':
+ time.sleep(3)
+ return True
+
+ def _bgp_show_nexthop(router, prefix):
+ cmd = "show ip bgp json"
+ output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
+ for nh in output['routes'][prefix][0]['nexthops']:
+ if 'fqdn' in nh:
+ return 'fqdn'
+ return 'ip'
+
+ if _bgp_converge('r2', '192.168.255.1'):
+ assert _bgp_show_nexthop('r2', '172.16.255.254/32') == 'fqdn'
+
+ if _bgp_converge('r1', '192.168.255.2'):
+ assert _bgp_show_nexthop('r1', '172.16.255.253/32') == 'ip'
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index 327e4625f2..49e48ba927 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -7,6 +7,8 @@ from lib.topotest import json_cmp_result
from lib.topolog import logger
import pytest
+topology_only = False
+
def pytest_addoption(parser):
"""
Add topology-only option to the topology tester. This option makes pytest
@@ -20,9 +22,9 @@ def pytest_runtest_call():
This function must be run after setup_module(), it does standarized post
setup routines. It is only being used for the 'topology-only' option.
"""
- # pylint: disable=E1101
- # Trust me, 'config' exists.
- if pytest.config.getoption('--topology-only'):
+ global topology_only
+
+ if topology_only:
tgen = get_topogen()
if tgen is not None:
# Allow user to play with the setup.
@@ -44,9 +46,15 @@ def pytest_assertrepr_compare(op, left, right):
def pytest_configure(config):
"Assert that the environment is correctly configured."
+
+ global topology_only
+
if not diagnose_env():
pytest.exit('enviroment has errors, please read the logs')
+ if config.getoption('--topology-only'):
+ topology_only = True
+
def pytest_runtest_makereport(item, call):
"Log all assert messages to default logger with error level"
# Nothing happened
diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py
index 31eaec7009..1d12d11a26 100644
--- a/tests/topotests/lib/ltemplate.py
+++ b/tests/topotests/lib/ltemplate.py
@@ -83,22 +83,15 @@ class LTemplate():
# For all registred routers, load the zebra configuration file
for rname, router in router_list.iteritems():
- print("Setting up %s" % rname)
- config = os.path.join(self.testdir, '{}/zebra.conf'.format(rname))
- if os.path.exists(config):
- router.load_config(TopoRouter.RD_ZEBRA, config)
- config = os.path.join(self.testdir, '{}/ospfd.conf'.format(rname))
- if os.path.exists(config):
- router.load_config(TopoRouter.RD_OSPF, config)
- config = os.path.join(self.testdir, '{}/ldpd.conf'.format(rname))
- if os.path.exists(config):
- router.load_config(TopoRouter.RD_LDP, config)
- config = os.path.join(self.testdir, '{}/bgpd.conf'.format(rname))
- if os.path.exists(config):
- router.load_config(TopoRouter.RD_BGP, config)
- config = os.path.join(self.testdir, '{}/isisd.conf'.format(rname))
- if os.path.exists(config):
- router.load_config(TopoRouter.RD_ISIS, config)
+ logger.info("Setting up %s" % rname)
+ for rd_val in TopoRouter.RD:
+ config = os.path.join(self.testdir, '{}/{}.conf'.format(rname,TopoRouter.RD[rd_val]))
+ prog = os.path.join(tgen.net[rname].daemondir, TopoRouter.RD[rd_val])
+ if os.path.exists(config):
+ if os.path.exists(prog):
+ router.load_config(rd_val, config)
+ else:
+ logger.warning("{} not found, but have {}.conf file".format(prog, TopoRouter.RD[rd_val]))
# After loading the configurations, this function loads configured daemons.
logger.info('Starting routers')
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index d989240a16..d7145c3be0 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -42,7 +42,12 @@ import os
import sys
import logging
import json
-import ConfigParser
+
+if sys.version_info[0] > 2:
+ import configparser
+else:
+ import ConfigParser as configparser
+
import glob
import grp
import platform
@@ -153,7 +158,7 @@ class Topogen(object):
Loads the configuration file `pytest.ini` located at the root dir of
topotests.
"""
- self.config = ConfigParser.ConfigParser(tgen_defaults)
+ self.config = configparser.ConfigParser(tgen_defaults)
pytestini_path = os.path.join(CWD, '../pytest.ini')
self.config.read(pytestini_path)
@@ -535,6 +540,7 @@ class TopoRouter(TopoGear):
RD_NHRP = 11
RD_STATIC = 12
RD_BFD = 13
+ RD_SHARP = 14
RD = {
RD_ZEBRA: 'zebra',
RD_RIP: 'ripd',
@@ -549,6 +555,7 @@ class TopoRouter(TopoGear):
RD_NHRP: 'nhrpd',
RD_STATIC: 'staticd',
RD_BFD: 'bfdd',
+ RD_SHARP: 'sharpd',
}
def __init__(self, tgen, cls, name, **params):
@@ -599,7 +606,7 @@ class TopoRouter(TopoGear):
def _prepare_tmpfiles(self):
# Create directories if they don't exist
try:
- os.makedirs(self.logdir, 0755)
+ os.makedirs(self.logdir, 0o755)
except OSError:
pass
@@ -608,10 +615,10 @@ class TopoRouter(TopoGear):
# Only allow group, if it exist.
gid = grp.getgrnam(self.routertype)[2]
os.chown(self.logdir, 0, gid)
- os.chmod(self.logdir, 0775)
+ os.chmod(self.logdir, 0o775)
except KeyError:
# Allow anyone, but set the sticky bit to avoid file deletions
- os.chmod(self.logdir, 01777)
+ os.chmod(self.logdir, 0o1777)
# Try to find relevant old logfiles in /tmp and delete them
map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name)))
@@ -931,7 +938,7 @@ def diagnose_env_linux():
logger.info('Running environment diagnostics')
# Load configuration
- config = ConfigParser.ConfigParser(tgen_defaults)
+ config = configparser.ConfigParser(tgen_defaults)
pytestini_path = os.path.join(CWD, '../pytest.ini')
config.read(pytestini_path)
diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl
index 77e6e9a330..1b48d10a09 100755
--- a/tools/checkpatch.pl
+++ b/tools/checkpatch.pl
@@ -6347,6 +6347,35 @@ sub process {
"Please, only use 32 bit atomics.\n" . $herecurr);
}
+# check for use of strcpy()
+ if ($line =~ /\bstrcpy\s*\(.*\)/) {
+ ERROR("STRCPY",
+ "strcpy() is error-prone; please use strlcpy()" . $herecurr);
+ }
+
+# check for use of strncpy()
+ if ($line =~ /\bstrncpy\s*\(.*\)/) {
+ WARN("STRNCPY",
+ "strncpy() is error-prone; please use strlcpy() if possible, or memcpy()" . $herecurr);
+ }
+
+# check for use of strcat()
+ if ($line =~ /\bstrcat\s*\(.*\)/) {
+ ERROR("STRCAT",
+ "strcat() is error-prone; please use strlcat() if possible" . $herecurr);
+ }
+
+# check for use of strncat()
+ if ($line =~ /\bstrncat\s*\(.*\)/) {
+ WARN("STRNCAT",
+ "strncat() is error-prone; please use strlcat() if possible" . $herecurr);
+ }
+
+# check for use of bzero()
+ if ($line =~ /\bbzero\s*\(.*\)/) {
+ ERROR("BZERO",
+ "bzero() is deprecated; use memset()" . $herecurr);
+ }
}
# If we have no input at all, then there is nothing to report on
diff --git a/tools/coccinelle/hash_const.cocci b/tools/coccinelle/hash_const.cocci
new file mode 100644
index 0000000000..9c53cb01fb
--- /dev/null
+++ b/tools/coccinelle/hash_const.cocci
@@ -0,0 +1,76 @@
+//
+// Transition hash key signatures to take their argument as const.
+// Does not handle headers or weirdly named hash functions.
+//
+@noconst disable optional_qualifier@
+identifier A;
+identifier func =~ ".*key$|.*key_make$|.*hash_make$|.*hash_keymake$|.*hash_key$|.*hash_key.*";
+@@
+
+- func (void *A)
++ func (const void *A)
+ { ... }
+
+@ depends on noconst disable optional_qualifier @
+identifier noconst.A;
+identifier noconst.func;
+identifier b;
+type T;
+@@
+
+func( ... ) {
+<...
+- T b = A;
++ const T b = A;
+...>
+ }
+
+@ depends on noconst disable optional_qualifier @
+identifier noconst.A;
+identifier noconst.func;
+identifier b;
+type T;
+@@
+
+func(...)
+ {
+<...
+- T b = (T) A;
++ const T b = A;
+...>
+ }
+
+@ depends on noconst disable optional_qualifier @
+identifier noconst.A;
+identifier noconst.func;
+identifier b;
+type T;
+@@
+
+func(...)
+ {
+<...
+- T b;
++ const T b;
+...
+ b = A;
+...>
+ }
+
+@ depends on noconst disable optional_qualifier @
+identifier noconst.A;
+identifier noconst.func;
+identifier b;
+type T;
+@@
+
+func(...)
+ {
+<...
+- T b;
++ const T b;
+...
+- b = (T) A;
++ b = A;
+...>
+ }
diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons
index 2abff422c9..79c52d30d1 100644
--- a/tools/etc/frr/daemons
+++ b/tools/etc/frr/daemons
@@ -29,6 +29,7 @@ sharpd=no
pbrd=no
bfdd=no
fabricd=no
+vrrpd=no
#
# If this option is set the /etc/init.d/frr script automatically loads
@@ -53,6 +54,15 @@ pbrd_options=" -A 127.0.0.1"
staticd_options="-A 127.0.0.1"
bfdd_options=" -A 127.0.0.1"
fabricd_options="-A 127.0.0.1"
+vrrpd_options=" -A 127.0.0.1"
+
+#
+# This is the maximum number of FD's that will be available.
+# Upon startup this is read by the control files and ulimit
+# is called. Uncomment and use a reasonable value for your
+# setup if you are expecting a large number of peers in
+# say BGP.
+#MAX_FDS=1024
# The list of daemons to watch is automatically generated by the init script.
#watchfrr_options=""
diff --git a/tools/etc/rsyslog.d/45-frr.conf b/tools/etc/rsyslog.d/45-frr.conf
index 4612e8beaf..feeeb13f13 100644
--- a/tools/etc/rsyslog.d/45-frr.conf
+++ b/tools/etc/rsyslog.d/45-frr.conf
@@ -16,6 +16,7 @@ if $programname == 'babeld' or
$programname == 'pimd' or
$programname == 'ripd' or
$programname == 'ripngd' or
+ $programname == 'vrrpd' or
$programname == 'watchfrr' or
$programname == 'zebra'
then :omfile:$frr_log
@@ -33,6 +34,7 @@ if $programname == 'babeld' or
$programname == 'pimd' or
$programname == 'ripd' or
$programname == 'ripngd' or
+ $programname == 'vrrpd' or
$programname == 'watchfrr' or
$programname == 'zebra'
then stop
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 59f1bcf52b..23e8f3000d 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -410,7 +410,8 @@ end
"service ",
"table ",
"username ",
- "zebra ")
+ "zebra ",
+ "vrrp autoconfigure")
for line in self.lines:
@@ -742,6 +743,27 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_add_to_del.append((tmp_ctx_keys, swpx_peergroup))
'''
+ Changing the bfd timers on neighbors is allowed without doing
+ a delete/add process. Since doing a "no neighbor blah bfd ..."
+ will cause the peer to bounce unnecessarily, just skip the delete
+ and just do the add.
+ '''
+ re_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', line)
+
+ if re_nbr_bfd_timers:
+ nbr = re_nbr_bfd_timers.group(1)
+ bfd_nbr = "neighbor %s" % nbr
+
+ for (ctx_keys, add_line) in lines_to_add:
+ re_add_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', add_line)
+
+ if re_add_nbr_bfd_timers:
+ found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False)
+
+ if found_add_bfd_nbr:
+ lines_to_del_to_del.append((ctx_keys, line))
+
+ '''
We changed how we display the neighbor interface command. Older
versions of frr would display the following:
neighbor swp1 interface
diff --git a/tools/frr.in b/tools/frr.in
index 2e3a094589..d871afa42b 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -25,7 +25,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
MAX_INSTANCES=5
RELOAD_SCRIPT="$D_PATH/frr-reload.py"
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 897e6d6558..3fc38d4bed 100644
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -29,7 +29,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty
# - keep zebra first
# - watchfrr does NOT belong in this list
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
RELOAD_SCRIPT="$D_PATH/frr-reload.py"
#
diff --git a/vrrpd/.gitignore b/vrrpd/.gitignore
new file mode 100644
index 0000000000..e1751b28ac
--- /dev/null
+++ b/vrrpd/.gitignore
@@ -0,0 +1,2 @@
+libvrrp.a
+vrrpd
diff --git a/vrrpd/Makefile b/vrrpd/Makefile
new file mode 100644
index 0000000000..027c6ee1f8
--- /dev/null
+++ b/vrrpd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. vrrp/vrrp
+%: ALWAYS
+ @$(MAKE) -s -C .. vrrp/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am
new file mode 100644
index 0000000000..a328f969d6
--- /dev/null
+++ b/vrrpd/subdir.am
@@ -0,0 +1,39 @@
+#
+# vrrpd
+#
+
+if VRRPD
+noinst_LIBRARIES += vrrpd/libvrrp.a
+sbin_PROGRAMS += vrrpd/vrrpd
+# dist_examples_DATA += staticd/staticd.conf.sample
+vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c
+man8 += $(MANBUILD)/vrrpd.8
+endif
+
+vrrpd_libvrrp_a_SOURCES = \
+ vrrpd/vrrp.c \
+ vrrpd/vrrp_arp.c \
+ vrrpd/vrrp_debug.c \
+ vrrpd/vrrp_memory.c \
+ vrrpd/vrrp_ndisc.c \
+ vrrpd/vrrp_packet.c \
+ vrrpd/vrrp_vty.c \
+ vrrpd/vrrp_zebra.c \
+ # end
+
+noinst_HEADERS += \
+ vrrpd/vrrp.h \
+ vrrpd/vrrp_arp.h \
+ vrrpd/vrrp_debug.h \
+ vrrpd/vrrp_memory.h \
+ vrrpd/vrrp_ndisc.h \
+ vrrpd/vrrp_packet.h \
+ vrrpd/vrrp_vty.h \
+ vrrpd/vrrp_zebra.h \
+ # end
+
+vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS)
+vrrpd/vrrp_vty.$(OBJEXT): vrrpd/vrrp_vty_clippy.c
+
+vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c
+vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@
diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c
new file mode 100644
index 0000000000..3d535cbfba
--- /dev/null
+++ b/vrrpd/vrrp.c
@@ -0,0 +1,2378 @@
+/*
+ * VRRP global definitions and state machine.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "lib/hash.h"
+#include "lib/hook.h"
+#include "lib/if.h"
+#include "lib/linklist.h"
+#include "lib/memory.h"
+#include "lib/network.h"
+#include "lib/prefix.h"
+#include "lib/sockopt.h"
+#include "lib/sockunion.h"
+#include "lib/vrf.h"
+#include "lib/vty.h"
+
+#include "vrrp.h"
+#include "vrrp_arp.h"
+#include "vrrp_debug.h"
+#include "vrrp_memory.h"
+#include "vrrp_ndisc.h"
+#include "vrrp_packet.h"
+#include "vrrp_zebra.h"
+
+#define VRRP_LOGPFX "[CORE] "
+
+/* statics */
+struct hash *vrrp_vrouters_hash;
+bool vrrp_autoconfig_is_on;
+int vrrp_autoconfig_version;
+
+struct vrrp_defaults vd;
+
+const char *vrrp_state_names[3] = {
+ [VRRP_STATE_INITIALIZE] = "Initialize",
+ [VRRP_STATE_MASTER] = "Master",
+ [VRRP_STATE_BACKUP] = "Backup",
+};
+
+const char *vrrp_event_names[2] = {
+ [VRRP_EVENT_STARTUP] = "Startup",
+ [VRRP_EVENT_SHUTDOWN] = "Shutdown",
+};
+
+
+/* Utility functions ------------------------------------------------------- */
+
+/*
+ * Sets an ethaddr to RFC-defined Virtual Router MAC address.
+ *
+ * mac
+ * ethaddr to set
+ *
+ * v6
+ * Whether this is a V6 or V4 Virtual Router MAC
+ *
+ * vrid
+ * Virtual Router Identifier
+ */
+static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid)
+{
+ /*
+ * V4: 00-00-5E-00-01-{VRID}
+ * V6: 00-00-5E-00-02-{VRID}
+ */
+ mac->octet[0] = 0x00;
+ mac->octet[1] = 0x00;
+ mac->octet[2] = 0x5E;
+ mac->octet[3] = 0x00;
+ mac->octet[4] = v6 ? 0x02 : 0x01;
+ mac->octet[5] = vrid;
+}
+
+/*
+ * Recalculates and sets skew_time and master_down_interval based
+ * values.
+ *
+ * r
+ * VRRP Router to operate on
+ */
+static void vrrp_recalculate_timers(struct vrrp_router *r)
+{
+ uint16_t mdiadv = r->vr->version == 3 ? r->master_adver_interval
+ : r->vr->advertisement_interval;
+ uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100;
+
+ r->skew_time = ((256 - r->vr->priority) * skm) / 256;
+ r->master_down_interval = 3 * mdiadv;
+ r->master_down_interval += r->skew_time;
+}
+
+/*
+ * Determines if a VRRP router is the owner of the specified address.
+ *
+ * The determining factor for whether an interface is the address owner is
+ * simply whether the address is assigned to the VRRP base interface by someone
+ * other than vrrpd.
+ *
+ * This function should always return the correct answer regardless of
+ * master/backup status.
+ *
+ * ifp
+ * The interface to check owernship of. This should be the base interface of
+ * a VRRP router.
+ *
+ * vr
+ * Virtual Router
+ *
+ * Returns:
+ * whether or not vr owns the specified address
+ */
+static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr)
+{
+ /*
+ * This code sanity checks implicit ownership configuration. Ideally,
+ * the way we determine address ownership status for this VRRP router
+ * is by looking at whether our VIPs are also assigned to the base
+ * interface, and therefore count as "real" addresses. This frees the
+ * user from having to manually configure priority 255 to indicate
+ * address ownership. However, this means one of the VIPs will be used
+ * as the source address for VRRP advertisements, which in turn means
+ * that other VRRP routers will be receiving packets with a source
+ * address they themselves have. This causes lots of different issues
+ * so for now we're disabling this and forcing the user to configure
+ * priority 255 to indicate ownership.
+ */
+
+ return false;
+
+#if 0
+ struct prefix p;
+
+ p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6;
+ p.prefixlen = IS_IPADDR_V4(addr) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN;
+ memcpy(&p.u, &addr->ip, sizeof(addr->ip));
+
+ return !!connected_lookup_prefix_exact(ifp, &p);
+#endif
+}
+
+/*
+ * Whether an interface has a MAC address that matches the VRRP RFC.
+ *
+ * ifp
+ * Interface to check
+ *
+ * Returns:
+ * Whether the interface has a VRRP mac or not
+ */
+static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp)
+{
+ struct ethaddr vmac4;
+ struct ethaddr vmac6;
+
+ vrrp_mac_set(&vmac4, 0, 0x00);
+ vrrp_mac_set(&vmac6, 1, 0x00);
+
+ return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1)
+ || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1);
+}
+
+/*
+ * Lookup a Virtual Router instance given a macvlan subinterface.
+ *
+ * The VRID is extracted from the interface MAC and the 2-tuple (iface, vrid)
+ * is used to look up any existing instances that match the interface. It does
+ * not matter whether the instance is already bound to the interface or not.
+ *
+ * mvl_ifp
+ * Interface pointer to use to lookup. Should be a macvlan device.
+ *
+ * Returns:
+ * Virtual Router, if found
+ * NULL otherwise
+ */
+static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp)
+{
+ struct interface *p;
+
+ if (!mvl_ifp || !mvl_ifp->link_ifindex
+ || !vrrp_ifp_has_vrrp_mac(mvl_ifp))
+ return NULL;
+
+ p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT);
+ uint8_t vrid = mvl_ifp->hw_addr[5];
+
+ return vrrp_lookup(p, vrid);
+}
+
+/*
+ * Lookup the Virtual Router instances configured on a particular interface.
+ *
+ * ifp
+ * Interface pointer to use to lookup. Should not be a macvlan device.
+ *
+ * Returns:
+ * List of virtual routers found
+ */
+static struct list *vrrp_lookup_by_if(struct interface *ifp)
+{
+ struct list *l = hash_to_list(vrrp_vrouters_hash);
+ struct listnode *ln, *nn;
+ struct vrrp_vrouter *vr;
+
+ for (ALL_LIST_ELEMENTS(l, ln, nn, vr))
+ if (vr->ifp != ifp)
+ list_delete_node(l, ln);
+
+ return l;
+}
+
+/*
+ * Lookup any Virtual Router instances associated with a particular interface.
+ * This is a combination of the results from vrrp_lookup_by_if_mvl and
+ * vrrp_lookup_by_if.
+ *
+ * Suppose the system interface list looks like the following:
+ *
+ * eth0
+ * \- eth0-v0 00:00:5e:00:01:01
+ * \- eth0-v1 00:00:5e:00:02:01
+ * \- eth0-v2 00:00:5e:00:01:0a
+ *
+ * Passing eth0-v2 to this function will give you the VRRP instance configured
+ * on eth0 with VRID 10. Passing eth0-v0 or eth0-v1 will give you the VRRP
+ * instance configured on eth0 with VRID 1. Passing eth0 will give you both.
+ *
+ * ifp
+ * Interface pointer to use to lookup. Can be any interface.
+ *
+ * Returns:
+ * List of virtual routers found
+ */
+static struct list *vrrp_lookup_by_if_any(struct interface *ifp)
+{
+ struct vrrp_vrouter *vr;
+ struct list *vrs;
+
+ vr = vrrp_lookup_by_if_mvl(ifp);
+ vrs = vr ? list_new() : vrrp_lookup_by_if(ifp);
+
+ if (vr)
+ listnode_add(vrs, vr);
+
+ return vrs;
+}
+
+/* Configuration controllers ----------------------------------------------- */
+
+void vrrp_check_start(struct vrrp_vrouter *vr)
+{
+ struct vrrp_router *r;
+ bool start;
+ const char *whynot = NULL;
+
+ if (vr->shutdown || vr->ifp == NULL)
+ return;
+
+ r = vr->v4;
+ /* Must not already be started */
+ start = r->fsm.state == VRRP_STATE_INITIALIZE;
+ /* Must have a parent interface */
+ start = start && (vr->ifp != NULL);
+ whynot = (!start && !whynot) ? "No base interface" : NULL;
+#if 0
+ /* Parent interface must be up */
+ start = start && if_is_operative(vr->ifp);
+#endif
+ /* Parent interface must have at least one v4 */
+ start = start && vr->ifp->connected->count > 1;
+ whynot = (!start && !whynot) ? "No primary IPv4 address" : NULL;
+ /* Must have a macvlan interface */
+ start = start && (r->mvl_ifp != NULL);
+ whynot = (!start && !whynot) ? "No VRRP interface" : NULL;
+#if 0
+ /* Macvlan interface must be admin up */
+ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP);
+#endif
+ /* Must have at least one VIP configured */
+ start = start && r->addrs->count > 0;
+ whynot =
+ (!start && !whynot) ? "No Virtual IP address configured" : NULL;
+ if (start)
+ vrrp_event(r, VRRP_EVENT_STARTUP);
+ else if (whynot)
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Refusing to start Virtual Router: %s",
+ vr->vrid, family2str(r->family), whynot);
+
+ r = vr->v6;
+ /* Must not already be started */
+ start = r->fsm.state == VRRP_STATE_INITIALIZE;
+ /* Must not be v2 */
+ start = start && vr->version != 2;
+ whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : NULL;
+ /* Must have a parent interface */
+ start = start && (vr->ifp != NULL);
+ whynot = (!start && !whynot) ? "No base interface" : NULL;
+#if 0
+ /* Parent interface must be up */
+ start = start && if_is_operative(vr->ifp);
+#endif
+ /* Must have a macvlan interface */
+ start = start && (r->mvl_ifp != NULL);
+ whynot = (!start && !whynot) ? "No VRRP interface" : NULL;
+#if 0
+ /* Macvlan interface must be admin up */
+ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP);
+ /* Macvlan interface must have a link local */
+ start = start && connected_get_linklocal(r->mvl_ifp);
+ whynot =
+ (!start && !whynot) ? "No link local address configured" : NULL;
+ /* Macvlan interface must have a v6 IP besides the link local */
+ start = start && (r->mvl_ifp->connected->count >= 2);
+ whynot = (!start && !whynot)
+ ? "No Virtual IP configured on macvlan device"
+ : NULL;
+#endif
+ /* Must have at least one VIP configured */
+ start = start && r->addrs->count > 0;
+ whynot =
+ (!start && !whynot) ? "No Virtual IP address configured" : NULL;
+ if (start)
+ vrrp_event(r, VRRP_EVENT_STARTUP);
+ else if (whynot)
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Refusing to start Virtual Router: %s",
+ vr->vrid, family2str(r->family), whynot);
+}
+
+void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority)
+{
+ vr->priority = priority;
+ vr->v4->priority = priority;
+ vr->v6->priority = priority;
+}
+
+void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
+ uint16_t advertisement_interval)
+{
+ if (vr->advertisement_interval == advertisement_interval)
+ return;
+
+ vr->advertisement_interval = advertisement_interval;
+ vrrp_recalculate_timers(vr->v4);
+ vrrp_recalculate_timers(vr->v6);
+}
+
+static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
+{
+ struct vrrp_router *r = ip->ipa_type == IPADDR_V4 ? vr->v4 : vr->v6;
+ struct listnode *ln;
+ struct ipaddr *iter;
+
+ for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, iter))
+ if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip)))
+ return true;
+
+ return false;
+}
+
+int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip)
+{
+ int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET;
+
+ assert(r->family == af);
+ assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6));
+
+ if (vrrp_has_ip(r->vr, ip))
+ return 0;
+
+ if (!vrrp_is_owner(r->vr->ifp, ip) && r->is_owner) {
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf));
+ zlog_err(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.",
+ r->vr->vrid, family2str(r->family), ipbuf);
+ return -1;
+ }
+
+ struct ipaddr *new = XCALLOC(MTYPE_VRRP_IP, sizeof(struct ipaddr));
+
+ *new = *ip;
+ listnode_add(r->addrs, new);
+
+ if (r->fsm.state == VRRP_STATE_MASTER) {
+ switch (r->family) {
+ case AF_INET:
+ vrrp_garp_send(r, &new->ipaddr_v4);
+ break;
+ case AF_INET6:
+ vrrp_ndisc_una_send(r, new);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
+{
+ struct ipaddr ip;
+
+ ip.ipa_type = IPADDR_V4;
+ ip.ipaddr_v4 = v4;
+ return vrrp_add_ip(vr->v4, &ip);
+}
+
+int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
+{
+ assert(vr->version != 2);
+
+ struct ipaddr ip;
+
+ ip.ipa_type = IPADDR_V6;
+ ip.ipaddr_v6 = v6;
+ return vrrp_add_ip(vr->v6, &ip);
+}
+
+int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip)
+{
+ struct listnode *ln, *nn;
+ struct ipaddr *iter;
+ int ret = 0;
+
+ if (!vrrp_has_ip(r->vr, ip))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter))
+ if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip)))
+ list_delete_node(r->addrs, ln);
+
+ /*
+ * NB: Deleting the last address and then issuing a shutdown will cause
+ * transmission of a priority 0 VRRP Advertisement - as per the RFC -
+ * but it will have no addresses. This is not forbidden in the RFC but
+ * might confuse other implementations.
+ */
+ if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE)
+ ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN);
+
+ return ret;
+}
+
+int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
+{
+ struct ipaddr ip;
+
+ ip.ipa_type = IPADDR_V6;
+ ip.ipaddr_v6 = v6;
+ return vrrp_del_ip(vr->v6, &ip);
+}
+
+int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
+{
+ struct ipaddr ip;
+
+ ip.ipa_type = IPADDR_V4;
+ ip.ipaddr_v4 = v4;
+ return vrrp_del_ip(vr->v4, &ip);
+}
+
+
+/* Creation and destruction ------------------------------------------------ */
+
+static void vrrp_router_addr_list_del_cb(void *val)
+{
+ struct ipaddr *ip = val;
+
+ XFREE(MTYPE_VRRP_IP, ip);
+}
+
+/*
+ * Search for a suitable macvlan subinterface we can attach to, and if found,
+ * attach to it.
+ *
+ * r
+ * Router to attach to interface
+ *
+ * Returns:
+ * Whether an interface was successfully attached
+ */
+static bool vrrp_attach_interface(struct vrrp_router *r)
+{
+ /* Search for existing interface with computed MAC address */
+ struct interface **ifps;
+
+ size_t ifps_cnt = if_lookup_by_hwaddr(
+ r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT);
+
+ /*
+ * Filter to only those macvlan interfaces whose parent is the base
+ * interface this VRRP router is configured on.
+ *
+ * If there are still multiple interfaces we just select the first one,
+ * as it should be functionally identical to the others.
+ */
+ unsigned int candidates = 0;
+ struct interface *selection = NULL;
+
+ for (unsigned int i = 0; i < ifps_cnt; i++) {
+ if (ifps[i]->link_ifindex != r->vr->ifp->ifindex)
+ ifps[i] = NULL;
+ else {
+ selection = selection ? selection : ifps[i];
+ candidates++;
+ }
+ }
+
+ if (ifps_cnt)
+ XFREE(MTYPE_TMP, ifps);
+
+ char ethstr[ETHER_ADDR_STRLEN];
+
+ prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr));
+
+ assert(!!selection == !!candidates);
+
+ if (candidates == 0)
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Interface: None (no interface found w/ MAC %s)",
+ r->vr->vrid, family2str(r->family), ethstr);
+ else if (candidates > 1)
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Interface: Multiple interfaces found; using %s",
+ r->vr->vrid, family2str(r->family), selection->name);
+ else
+ zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Interface: %s",
+ r->vr->vrid, family2str(r->family), selection->name);
+
+ r->mvl_ifp = selection;
+
+ return !!r->mvl_ifp;
+}
+
+static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr,
+ int family)
+{
+ struct vrrp_router *r =
+ XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_router));
+
+ r->family = family;
+ r->sock_rx = -1;
+ r->sock_tx = -1;
+ r->vr = vr;
+ r->addrs = list_new();
+ r->addrs->del = vrrp_router_addr_list_del_cb;
+ r->priority = vr->priority;
+ r->fsm.state = VRRP_STATE_INITIALIZE;
+ vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid);
+
+ vrrp_attach_interface(r);
+
+ return r;
+}
+
+static void vrrp_router_destroy(struct vrrp_router *r)
+{
+ if (r->is_active)
+ vrrp_event(r, VRRP_EVENT_SHUTDOWN);
+
+ if (r->sock_rx >= 0)
+ close(r->sock_rx);
+ if (r->sock_tx >= 0)
+ close(r->sock_tx);
+
+ /* FIXME: also delete list elements */
+ list_delete(&r->addrs);
+ XFREE(MTYPE_VRRP_RTR, r);
+}
+
+struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid,
+ uint8_t version)
+{
+ struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
+
+ if (vr)
+ return vr;
+
+ if (version != 2 && version != 3)
+ return NULL;
+
+ vr = XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_vrouter));
+
+ vr->ifp = ifp;
+ vr->version = version;
+ vr->vrid = vrid;
+ vr->priority = vd.priority;
+ vr->preempt_mode = vd.preempt_mode;
+ vr->accept_mode = vd.accept_mode;
+ vr->shutdown = vd.shutdown;
+
+ vr->v4 = vrrp_router_create(vr, AF_INET);
+ vr->v6 = vrrp_router_create(vr, AF_INET6);
+
+ vrrp_set_advertisement_interval(vr, vd.advertisement_interval);
+
+ hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern);
+
+ return vr;
+}
+
+void vrrp_vrouter_destroy(struct vrrp_vrouter *vr)
+{
+ vrrp_router_destroy(vr->v4);
+ vrrp_router_destroy(vr->v6);
+ hash_release(vrrp_vrouters_hash, vr);
+ XFREE(MTYPE_VRRP_RTR, vr);
+}
+
+struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid)
+{
+ struct vrrp_vrouter vr;
+
+ vr.vrid = vrid;
+ vr.ifp = ifp;
+
+ return hash_lookup(vrrp_vrouters_hash, &vr);
+}
+
+/* Network ----------------------------------------------------------------- */
+
+/* Forward decls */
+static void vrrp_change_state(struct vrrp_router *r, int to);
+static int vrrp_adver_timer_expire(struct thread *thread);
+static int vrrp_master_down_timer_expire(struct thread *thread);
+
+/*
+ * Finds the first connected address of the appropriate family on a VRRP
+ * router's interface and binds the Tx socket of the VRRP router to that
+ * address.
+ *
+ * Also sets src field of vrrp_router.
+ *
+ * r
+ * VRRP router to operate on
+ *
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ */
+static int vrrp_bind_to_primary_connected(struct vrrp_router *r)
+{
+ char ipstr[INET6_ADDRSTRLEN];
+ struct interface *ifp;
+
+ /*
+ * A slight quirk: the RFC specifies that advertisements under IPv6 must
+ * be transmitted using the link local address of the source interface
+ */
+ 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))
+ if (c->address->family == r->family) {
+ if (r->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
+ break;
+ else if (r->family == AF_INET)
+ break;
+ }
+
+ if (c == NULL) {
+ zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to find address to bind on %s",
+ r->vr->vrid, family2str(r->family), ifp->name);
+ return -1;
+ }
+
+ union sockunion su;
+
+ memset(&su, 0x00, sizeof(su));
+
+ switch (r->family) {
+ case AF_INET:
+ r->src.ipa_type = IPADDR_V4;
+ r->src.ipaddr_v4 = c->address->u.prefix4;
+ su.sin.sin_family = AF_INET;
+ su.sin.sin_addr = c->address->u.prefix4;
+ break;
+ case AF_INET6:
+ r->src.ipa_type = IPADDR_V6;
+ r->src.ipaddr_v6 = c->address->u.prefix6;
+ su.sin6.sin6_family = AF_INET6;
+ su.sin6.sin6_scope_id = ifp->ifindex;
+ su.sin6.sin6_addr = c->address->u.prefix6;
+ break;
+ }
+
+ int ret = 0;
+
+ sockopt_reuseaddr(r->sock_tx);
+ if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) {
+ zlog_err(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to bind Tx socket to primary IP address %s: %s",
+ r->vr->vrid, family2str(r->family),
+ inet_ntop(r->family,
+ (const void *)&c->address->u.prefix, ipstr,
+ sizeof(ipstr)),
+ safe_strerror(errno));
+ ret = -1;
+ } else {
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Bound Tx socket to primary IP address %s",
+ r->vr->vrid, family2str(r->family),
+ inet_ntop(r->family, (const void *)&c->address->u.prefix,
+ ipstr, sizeof(ipstr)));
+ }
+
+ return ret;
+}
+
+
+/*
+ * Create and multicast a VRRP ADVERTISEMENT message.
+ *
+ * r
+ * VRRP Router for which to send ADVERTISEMENT
+ */
+static void vrrp_send_advertisement(struct vrrp_router *r)
+{
+ struct vrrp_pkt *pkt;
+ ssize_t pktsz;
+ struct ipaddr *addrs[r->addrs->count];
+ union sockunion dest;
+
+ if (r->src.ipa_type == IPADDR_NONE
+ && vrrp_bind_to_primary_connected(r) < 0)
+ return;
+
+ list_to_array(r->addrs, (void **)addrs, r->addrs->count);
+
+ pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid,
+ r->priority, r->vr->advertisement_interval,
+ r->addrs->count, (struct ipaddr **)&addrs);
+
+ if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL))
+ zlog_hexdump(pkt, (size_t)pktsz);
+
+ const char *group = r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR
+ : VRRP_MCASTV6_GROUP_STR;
+ (void)str2sockunion(group, &dest);
+
+ ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktsz, 0, &dest.sa,
+ sockunion_sizeof(&dest));
+
+ XFREE(MTYPE_VRRP_PKT, pkt);
+
+ if (sent < 0) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to send VRRP Advertisement: %s",
+ r->vr->vrid, family2str(r->family),
+ safe_strerror(errno));
+ } else {
+ ++r->stats.adver_tx_cnt;
+ }
+}
+
+/*
+ * Receive and parse VRRP advertisement.
+ *
+ * By the time we get here all fields have been validated for basic correctness
+ * and the packet is a valid VRRP packet.
+ *
+ * However, we have not validated whether the VRID is correct for this virtual
+ * router, nor whether the priority is correct (i.e. is not 255 when we are the
+ * address owner), nor whether the advertisement interval equals our own
+ * configured value (this check is only performed in VRRPv2).
+ *
+ * r
+ * VRRP Router associated with the socket this advertisement was received on
+ *
+ * src
+ * Source address of sender
+ *
+ * pkt
+ * The advertisement they sent
+ *
+ * pktsize
+ * Size of advertisement
+ *
+ * Returns:
+ * -1 if advertisement is invalid
+ * 0 otherwise
+ */
+static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
+ struct vrrp_pkt *pkt, size_t pktsize)
+{
+ char sipstr[INET6_ADDRSTRLEN];
+ char dipstr[INET6_ADDRSTRLEN];
+
+ ipaddr2str(src, sipstr, sizeof(sipstr));
+ ipaddr2str(&r->src, dipstr, sizeof(dipstr));
+
+ char dumpbuf[BUFSIZ];
+
+ vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt);
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Received VRRP Advertisement from %s:\n%s",
+ r->vr->vrid, family2str(r->family), sipstr, dumpbuf);
+
+ /* Check that VRID matches our configured VRID */
+ if (pkt->hdr.vrid != r->vr->vrid) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram invalid: Advertisement contains VRID %" PRIu8
+ " which does not match our instance",
+ r->vr->vrid, family2str(r->family), pkt->hdr.vrid);
+ return -1;
+ }
+
+ /* Verify that we are not the IPvX address owner */
+ if (r->is_owner) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram invalid: Received advertisement but we are the address owner",
+ r->vr->vrid, family2str(r->family));
+ return -1;
+ }
+
+ /* If v2, verify that adver time matches ours */
+ bool adveq = (pkt->hdr.v2.adver_int
+ == MAX(r->vr->advertisement_interval / 100, 1));
+ if (r->vr->version == 2 && !adveq) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram invalid: Received advertisement with advertisement interval %" PRIu8
+ " unequal to our configured value %u",
+ r->vr->vrid, family2str(r->family),
+ pkt->hdr.v2.adver_int,
+ MAX(r->vr->advertisement_interval / 100, 1));
+ return -1;
+ }
+
+
+ /* Check that # IPs received matches our # configured IPs */
+ if (pkt->hdr.naddr != r->addrs->count)
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram has %" PRIu8
+ " addresses, but this VRRP instance has %u",
+ r->vr->vrid, family2str(r->family), pkt->hdr.naddr,
+ r->addrs->count);
+
+ ++r->stats.adver_rx_cnt;
+
+ int addrcmp;
+
+ switch (r->fsm.state) {
+ case VRRP_STATE_MASTER:
+ addrcmp = memcmp(&src->ip, &r->src.ip, IPADDRSZ(src));
+
+ if (pkt->hdr.priority == 0) {
+ vrrp_send_advertisement(r);
+ THREAD_OFF(r->t_adver_timer);
+ thread_add_timer_msec(
+ master, vrrp_adver_timer_expire, r,
+ r->vr->advertisement_interval * 10,
+ &r->t_adver_timer);
+ } else if (pkt->hdr.priority > r->priority
+ || ((pkt->hdr.priority == r->priority)
+ && addrcmp > 0)) {
+ zlog_info(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Received advertisement from %s w/ priority %" PRIu8
+ "; switching to Backup",
+ r->vr->vrid, family2str(r->family), sipstr,
+ pkt->hdr.priority);
+ THREAD_OFF(r->t_adver_timer);
+ if (r->vr->version == 3) {
+ r->master_adver_interval =
+ htons(pkt->hdr.v3.adver_int);
+ }
+ vrrp_recalculate_timers(r);
+ THREAD_OFF(r->t_master_down_timer);
+ thread_add_timer_msec(master,
+ vrrp_master_down_timer_expire, r,
+ r->master_down_interval * 10,
+ &r->t_master_down_timer);
+ vrrp_change_state(r, VRRP_STATE_BACKUP);
+ } else {
+ /* Discard advertisement */
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Discarding advertisement from %s (%" PRIu8
+ " <= %" PRIu8 " & %s <= %s)",
+ r->vr->vrid, family2str(r->family), sipstr,
+ pkt->hdr.priority, r->priority, sipstr, dipstr);
+ }
+ break;
+ case VRRP_STATE_BACKUP:
+ if (pkt->hdr.priority == 0) {
+ THREAD_OFF(r->t_master_down_timer);
+ thread_add_timer_msec(
+ master, vrrp_master_down_timer_expire, r,
+ r->skew_time * 10, &r->t_master_down_timer);
+ } else if (r->vr->preempt_mode == false
+ || pkt->hdr.priority >= r->priority) {
+ if (r->vr->version == 3) {
+ r->master_adver_interval =
+ ntohs(pkt->hdr.v3.adver_int);
+ }
+ vrrp_recalculate_timers(r);
+ THREAD_OFF(r->t_master_down_timer);
+ thread_add_timer_msec(master,
+ vrrp_master_down_timer_expire, r,
+ r->master_down_interval * 10,
+ &r->t_master_down_timer);
+ } else if (r->vr->preempt_mode == true
+ && pkt->hdr.priority < r->priority) {
+ /* Discard advertisement */
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Discarding advertisement from %s (%" PRIu8
+ " < %" PRIu8 " & preempt = true)",
+ r->vr->vrid, family2str(r->family), sipstr,
+ pkt->hdr.priority, r->priority);
+ }
+ break;
+ case VRRP_STATE_INITIALIZE:
+ zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Received ADVERTISEMENT in state %s; this is a bug",
+ r->vr->vrid, family2str(r->family),
+ vrrp_state_names[r->fsm.state]);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Read and process next IPvX datagram.
+ */
+static int vrrp_read(struct thread *thread)
+{
+ struct vrrp_router *r = thread->arg;
+
+ struct vrrp_pkt *pkt;
+ ssize_t pktsize;
+ ssize_t nbytes;
+ bool resched;
+ char errbuf[BUFSIZ];
+ struct sockaddr_storage sa;
+ uint8_t control[64];
+ struct ipaddr src = {};
+
+ struct msghdr m = {};
+ struct iovec iov;
+
+ iov.iov_base = r->ibuf;
+ iov.iov_len = sizeof(r->ibuf);
+ m.msg_name = &sa;
+ m.msg_namelen = sizeof(sa);
+ m.msg_iov = &iov;
+ m.msg_iovlen = 1;
+ m.msg_control = control;
+ m.msg_controllen = sizeof(control);
+
+ nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT);
+
+ if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) {
+ resched = true;
+ goto done;
+ } else if (nbytes <= 0) {
+ vrrp_event(r, VRRP_EVENT_SHUTDOWN);
+ resched = false;
+ goto done;
+ }
+
+ if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) {
+ DEBUGD(&vrrp_dbg_pkt,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram rx: ",
+ r->vr->vrid, family2str(r->family));
+ zlog_hexdump(r->ibuf, nbytes);
+ }
+
+ pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes,
+ &src, &pkt, errbuf, sizeof(errbuf));
+
+ if (pktsize < 0)
+ DEBUGD(&vrrp_dbg_pkt,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Datagram invalid: %s",
+ r->vr->vrid, family2str(r->family), errbuf);
+ else
+ vrrp_recv_advertisement(r, &src, pkt, pktsize);
+
+ resched = true;
+
+done:
+ memset(r->ibuf, 0x00, sizeof(r->ibuf));
+
+ if (resched)
+ thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read);
+
+ return 0;
+}
+
+/*
+ * Creates and configures VRRP router sockets.
+ *
+ * This function:
+ * - Creates two sockets, one for Tx, one for Rx
+ * - Joins the Rx socket to the appropriate VRRP multicast group
+ * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for
+ * all transmitted IPvX packets
+ * - Requests the kernel to deliver IPv6 header values needed to validate VRRP
+ * packets
+ *
+ * If any of the above fail, the sockets are closed. The only exception is if
+ * the TTL / Hop Limit settings fail; these are logged, but configuration
+ * proceeds.
+ *
+ * The first connected address on the Virtual Router's interface is used as the
+ * interface address.
+ *
+ * r
+ * VRRP Router for which to create listen socket
+ *
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ */
+static int vrrp_socket(struct vrrp_router *r)
+{
+ int ret;
+ bool failed = false;
+
+ frr_elevate_privs(&vrrp_privs)
+ {
+ r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
+ r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
+ }
+
+ if (r->sock_rx < 0 || r->sock_tx < 0) {
+ const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx";
+
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Can't create VRRP %s socket",
+ r->vr->vrid, family2str(r->family), rxtx);
+ failed = true;
+ goto done;
+ }
+
+ /* Configure sockets */
+ if (r->family == AF_INET) {
+ /* Set Tx socket to always Tx with TTL set to 255 */
+ int ttl = 255;
+
+ ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl));
+ if (ret < 0) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets",
+ r->vr->vrid, family2str(r->family));
+ }
+
+ /* Set Tx socket DSCP byte */
+ setsockopt_ipv4_tos(r->sock_tx, IPTOS_PREC_INTERNETCONTROL);
+
+ /* Turn off multicast loop on Tx */
+ setsockopt_ipv4_multicast_loop(r->sock_tx, 0);
+
+ /* Bind Rx socket to exact interface */
+ frr_elevate_privs(&vrrp_privs)
+ {
+ ret = setsockopt(r->sock_rx, SOL_SOCKET,
+ SO_BINDTODEVICE, r->vr->ifp->name,
+ strlen(r->vr->ifp->name));
+ }
+ if (ret) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to bind Rx socket to %s: %s",
+ r->vr->vrid, family2str(r->family),
+ r->vr->ifp->name, safe_strerror(errno));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Bound Rx socket to %s",
+ r->vr->vrid, family2str(r->family), r->vr->ifp->name);
+
+ /* Bind Rx socket to v4 multicast address */
+ struct sockaddr_in sa = {0};
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP);
+ if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) {
+ zlog_err(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to bind Rx socket to VRRP multicast group: %s",
+ r->vr->vrid, family2str(r->family),
+ safe_strerror(errno));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Bound Rx socket to VRRP multicast group",
+ 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;
+
+ ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP,
+ v4, htonl(VRRP_MCASTV4_GROUP),
+ r->vr->ifp->ifindex);
+ if (ret < 0) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
+ "Failed to join VRRP %s multicast group",
+ r->vr->vrid, family2str(r->family));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Joined VRRP multicast group",
+ r->vr->vrid, family2str(r->family));
+
+ /* Set outgoing interface for advertisements */
+ struct ip_mreqn mreqn = {};
+
+ mreqn.imr_ifindex = r->mvl_ifp->ifindex;
+ ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF,
+ (void *)&mreqn, sizeof(mreqn));
+ if (ret < 0) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Could not set %s as outgoing multicast interface",
+ r->vr->vrid, family2str(r->family),
+ r->mvl_ifp->name);
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Set %s as outgoing multicast interface",
+ r->vr->vrid, family2str(r->family), r->mvl_ifp->name);
+
+ /* Select and bind source address */
+ if (vrrp_bind_to_primary_connected(r) < 0) {
+ failed = true;
+ goto done;
+ }
+
+ } else if (r->family == AF_INET6) {
+ /* Always transmit IPv6 packets with hop limit set to 255 */
+ ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255);
+ if (ret < 0) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets",
+ r->vr->vrid, family2str(r->family));
+ }
+
+ /* Set Tx socket DSCP byte */
+ setsockopt_ipv6_tclass(r->sock_tx, IPTOS_PREC_INTERNETCONTROL);
+
+ /* Request hop limit delivery */
+ setsockopt_ipv6_hoplimit(r->sock_rx, 1);
+ if (ret < 0) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to request IPv6 Hop Limit delivery",
+ r->vr->vrid, family2str(r->family));
+ failed = true;
+ goto done;
+ }
+
+ /* Turn off multicast loop on Tx */
+ setsockopt_ipv6_multicast_loop(r->sock_tx, 0);
+
+ /* Bind Rx socket to exact interface */
+ frr_elevate_privs(&vrrp_privs)
+ {
+ ret = setsockopt(r->sock_rx, SOL_SOCKET,
+ SO_BINDTODEVICE, r->vr->ifp->name,
+ strlen(r->vr->ifp->name));
+ }
+ if (ret) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to bind Rx socket to %s: %s",
+ r->vr->vrid, family2str(r->family),
+ r->vr->ifp->name, safe_strerror(errno));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Bound Rx socket to %s",
+ r->vr->vrid, family2str(r->family), r->vr->ifp->name);
+
+ /* Bind Rx socket to v6 multicast address */
+ struct sockaddr_in6 sa = {0};
+
+ sa.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr);
+ if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) {
+ zlog_err(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to bind Rx socket to VRRP multicast group: %s",
+ r->vr->vrid, family2str(r->family),
+ safe_strerror(errno));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Bound Rx socket to VRRP multicast group",
+ r->vr->vrid, family2str(r->family));
+
+ /* Join VRRP IPv6 multicast group */
+ struct ipv6_mreq mreq;
+
+ inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR,
+ &mreq.ipv6mr_multiaddr);
+ mreq.ipv6mr_interface = r->vr->ifp->ifindex;
+ ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq));
+ if (ret < 0) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to join VRRP multicast group",
+ r->vr->vrid, family2str(r->family));
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Joined VRRP multicast group",
+ r->vr->vrid, family2str(r->family));
+
+ /* Set outgoing interface for advertisements */
+ ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &r->mvl_ifp->ifindex, sizeof(ifindex_t));
+ if (ret < 0) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Could not set %s as outgoing multicast interface",
+ r->vr->vrid, family2str(r->family),
+ r->mvl_ifp->name);
+ failed = true;
+ goto done;
+ }
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Set %s as outgoing multicast interface",
+ r->vr->vrid, family2str(r->family), r->mvl_ifp->name);
+ }
+
+done:
+ ret = 0;
+ if (failed) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to initialize VRRP router",
+ r->vr->vrid, family2str(r->family));
+ if (r->sock_rx >= 0) {
+ close(r->sock_rx);
+ r->sock_rx = -1;
+ }
+ if (r->sock_tx >= 0) {
+ close(r->sock_tx);
+ r->sock_tx = -1;
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/* State machine ----------------------------------------------------------- */
+
+DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to));
+
+/*
+ * Handle any necessary actions during state change to MASTER state.
+ *
+ * r
+ * VRRP Router to operate on
+ */
+static void vrrp_change_state_master(struct vrrp_router *r)
+{
+ /* Enable ND Router Advertisements */
+ if (r->family == AF_INET6)
+ vrrp_zebra_radv_set(r, true);
+
+ /* Set protodown off */
+ vrrp_zclient_send_interface_protodown(r->mvl_ifp, false);
+
+ /*
+ * If protodown is already off, we can send our stuff, otherwise we
+ * have to delay until the interface is all the way up
+ */
+ if (if_is_operative(r->mvl_ifp)) {
+ vrrp_send_advertisement(r);
+
+ if (r->family == AF_INET)
+ vrrp_garp_send_all(r);
+ else if (r->family == AF_INET6)
+ vrrp_ndisc_una_send_all(r);
+ } else {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Delaying VRRP advertisement until interface is up",
+ r->vr->vrid, family2str(r->family));
+ r->advert_pending = true;
+
+ if (r->family == AF_INET) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Delaying VRRP gratuitous ARPs until interface is up",
+ r->vr->vrid, family2str(r->family));
+ r->garp_pending = true;
+ } else if (r->family == AF_INET6) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Delaying VRRP unsolicited neighbor advertisement until interface is up",
+ r->vr->vrid, family2str(r->family));
+ r->ndisc_pending = true;
+ }
+ }
+}
+
+/*
+ * Handle any necessary actions during state change to BACKUP state.
+ *
+ * r
+ * Virtual Router to operate on
+ */
+static void vrrp_change_state_backup(struct vrrp_router *r)
+{
+ /* Disable ND Router Advertisements */
+ if (r->family == AF_INET6)
+ vrrp_zebra_radv_set(r, false);
+
+ /* Disable Adver_Timer */
+ THREAD_OFF(r->t_adver_timer);
+
+ r->advert_pending = false;
+ r->garp_pending = false;
+ r->ndisc_pending = false;
+ memset(&r->src, 0x00, sizeof(r->src));
+
+ vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
+}
+
+/*
+ * Handle any necessary actions during state change to INITIALIZE state.
+ *
+ * This is not called for initial startup, only when transitioning from MASTER
+ * or BACKUP.
+ *
+ * r
+ * VRRP Router to operate on
+ */
+static void vrrp_change_state_initialize(struct vrrp_router *r)
+{
+ r->master_adver_interval = 0;
+ vrrp_recalculate_timers(r);
+
+ r->advert_pending = false;
+ r->garp_pending = false;
+ r->ndisc_pending = false;
+
+ /* Disable ND Router Advertisements */
+ if (r->family == AF_INET6)
+ vrrp_zebra_radv_set(r, false);
+}
+
+void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = {
+ [VRRP_STATE_MASTER] = vrrp_change_state_master,
+ [VRRP_STATE_BACKUP] = vrrp_change_state_backup,
+ [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize,
+};
+
+/*
+ * Change Virtual Router FSM position. Handles transitional actions and calls
+ * any subscribers to the state change hook.
+ *
+ * r
+ * Virtual Router for which to change state
+ *
+ * to
+ * State to change to
+ */
+static void vrrp_change_state(struct vrrp_router *r, int to)
+{
+ if (r->fsm.state == to)
+ return;
+
+ /* Call our handlers, then any subscribers */
+ vrrp_change_state_handlers[to](r);
+ hook_call(vrrp_change_state_hook, r, to);
+ zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "%s -> %s",
+ r->vr->vrid, family2str(r->family),
+ vrrp_state_names[r->fsm.state], vrrp_state_names[to]);
+ r->fsm.state = to;
+
+ ++r->stats.trans_cnt;
+}
+
+/*
+ * Called when Adver_Timer expires.
+ */
+static int vrrp_adver_timer_expire(struct thread *thread)
+{
+ struct vrrp_router *r = thread->arg;
+
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Adver_Timer expired",
+ r->vr->vrid, family2str(r->family));
+
+ if (r->fsm.state == VRRP_STATE_MASTER) {
+ /* Send an ADVERTISEMENT */
+ vrrp_send_advertisement(r);
+
+ /* Reset the Adver_Timer to Advertisement_Interval */
+ thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
+ r->vr->advertisement_interval * 10,
+ &r->t_adver_timer);
+ } else {
+ zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Adver_Timer expired in state '%s'; this is a bug",
+ r->vr->vrid, family2str(r->family),
+ vrrp_state_names[r->fsm.state]);
+ }
+
+ return 0;
+}
+
+/*
+ * Called when Master_Down_Timer expires.
+ */
+static int vrrp_master_down_timer_expire(struct thread *thread)
+{
+ struct vrrp_router *r = thread->arg;
+
+ zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Master_Down_Timer expired",
+ r->vr->vrid, family2str(r->family));
+
+ thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
+ r->vr->advertisement_interval * 10,
+ &r->t_adver_timer);
+ vrrp_change_state(r, VRRP_STATE_MASTER);
+
+ return 0;
+}
+
+/*
+ * Event handler for Startup event.
+ *
+ * Creates sockets, sends advertisements and ARP requests, starts timers,
+ * and transitions the Virtual Router to either Master or Backup states.
+ *
+ * This function will also initialize the program's global ARP subsystem if it
+ * has not yet been initialized.
+ *
+ * r
+ * VRRP Router on which to apply Startup event
+ *
+ * Returns:
+ * < 0 if the session socket could not be created, or the state is not
+ * Initialize
+ * 0 on success
+ */
+static int vrrp_startup(struct vrrp_router *r)
+{
+ /* May only be called when the state is Initialize */
+ if (r->fsm.state != VRRP_STATE_INITIALIZE)
+ return -1;
+
+ /* Must have a valid macvlan interface available */
+ if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "No appropriate interface found",
+ r->vr->vrid, family2str(r->family));
+ return -1;
+ }
+
+ /* Initialize global gratuitous ARP socket if necessary */
+ if (r->family == AF_INET && !vrrp_garp_is_init())
+ vrrp_garp_init();
+ if (r->family == AF_INET6 && !vrrp_ndisc_is_init())
+ vrrp_ndisc_init();
+
+ /* Create socket */
+ if (r->sock_rx < 0 || r->sock_tx < 0) {
+ int ret = vrrp_socket(r);
+
+ if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0)
+ return ret;
+ }
+
+ /* Schedule listener */
+ thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read);
+
+ /* Configure effective priority */
+ assert(listhead(r->addrs));
+ struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data;
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf));
+
+ if (r->vr->priority == VRRP_PRIO_MASTER
+ || vrrp_is_owner(r->vr->ifp, primary)) {
+ r->priority = VRRP_PRIO_MASTER;
+ vrrp_recalculate_timers(r);
+
+ zlog_info(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "%s has priority set to 255 or owns primary Virtual Router IP %s; electing self as Master",
+ r->vr->vrid, family2str(r->family), r->vr->ifp->name,
+ ipbuf);
+ }
+
+ if (r->priority == VRRP_PRIO_MASTER) {
+ thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
+ r->vr->advertisement_interval * 10,
+ &r->t_adver_timer);
+ vrrp_change_state(r, VRRP_STATE_MASTER);
+ } else {
+ r->master_adver_interval = r->vr->advertisement_interval;
+ vrrp_recalculate_timers(r);
+ thread_add_timer_msec(master, vrrp_master_down_timer_expire, r,
+ r->master_down_interval * 10,
+ &r->t_master_down_timer);
+ vrrp_change_state(r, VRRP_STATE_BACKUP);
+ }
+
+ r->is_active = true;
+
+ return 0;
+}
+
+/*
+ * Shuts down a Virtual Router and transitions it to Initialize.
+ *
+ * This call must be idempotent; it is safe to call multiple times on the same
+ * VRRP Router.
+ */
+static int vrrp_shutdown(struct vrrp_router *r)
+{
+ uint8_t saved_prio;
+
+ switch (r->fsm.state) {
+ case VRRP_STATE_MASTER:
+ /* Send an ADVERTISEMENT with Priority = 0 */
+ saved_prio = r->priority;
+ r->priority = 0;
+ vrrp_send_advertisement(r);
+ r->priority = saved_prio;
+ break;
+ case VRRP_STATE_BACKUP:
+ break;
+ case VRRP_STATE_INITIALIZE:
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Received '%s' event in '%s' state; ignoring",
+ r->vr->vrid, family2str(r->family),
+ vrrp_event_names[VRRP_EVENT_SHUTDOWN],
+ vrrp_state_names[VRRP_STATE_INITIALIZE]);
+ break;
+ }
+
+ /* Cancel all timers */
+ THREAD_OFF(r->t_adver_timer);
+ THREAD_OFF(r->t_master_down_timer);
+ THREAD_OFF(r->t_read);
+ THREAD_OFF(r->t_write);
+
+ /* Protodown macvlan */
+ vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
+
+ /* Throw away our source address */
+ memset(&r->src, 0x00, sizeof(r->src));
+
+ if (r->sock_rx > 0) {
+ close(r->sock_rx);
+ r->sock_rx = -1;
+ }
+ if (r->sock_tx > 0) {
+ close(r->sock_tx);
+ r->sock_tx = -1;
+ }
+
+ vrrp_change_state(r, VRRP_STATE_INITIALIZE);
+
+ r->is_active = false;
+
+ return 0;
+}
+
+static int (*vrrp_event_handlers[])(struct vrrp_router *r) = {
+ [VRRP_EVENT_STARTUP] = vrrp_startup,
+ [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown,
+};
+
+/*
+ * Spawn a VRRP FSM event on a VRRP Router.
+ *
+ * vr
+ * VRRP Router on which to spawn event
+ *
+ * event
+ * The event to spawn
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+int vrrp_event(struct vrrp_router *r, int event)
+{
+ zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "'%s' event",
+ r->vr->vrid, family2str(r->family), vrrp_event_names[event]);
+ return vrrp_event_handlers[event](r);
+}
+
+
+/* Autoconfig -------------------------------------------------------------- */
+
+/*
+ * Set the configured addresses for this VRRP instance to exactly the addresses
+ * present on its macvlan subinterface(s).
+ *
+ * vr
+ * VRRP router to act on
+ */
+static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r)
+{
+ struct listnode *ln;
+ struct connected *c = NULL;
+ bool is_v6_ll;
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (!r->mvl_ifp)
+ return;
+
+ DEBUGD(&vrrp_dbg_auto,
+ 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)) {
+ 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) {
+ inet_ntop(r->family, &c->address->u.prefix, ipbuf,
+ sizeof(ipbuf));
+ DEBUGD(&vrrp_dbg_auto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Adding %s",
+ r->vr->vrid, family2str(r->family), ipbuf);
+ if (r->family == AF_INET)
+ vrrp_add_ipv4(r->vr, c->address->u.prefix4);
+ else if (r->vr->version == 3)
+ vrrp_add_ipv6(r->vr, c->address->u.prefix6);
+ }
+ }
+
+ vrrp_check_start(r->vr);
+
+ if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) {
+ DEBUGD(&vrrp_dbg_auto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Virtual IP list is empty; shutting down",
+ r->vr->vrid, family2str(r->family));
+ vrrp_event(r, VRRP_EVENT_SHUTDOWN);
+ }
+}
+
+static struct vrrp_vrouter *
+vrrp_autoconfig_autocreate(struct interface *mvl_ifp)
+{
+ struct interface *p;
+ struct vrrp_vrouter *vr;
+
+ p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT);
+
+ if (!p)
+ return NULL;
+
+ uint8_t vrid = mvl_ifp->hw_addr[5];
+ uint8_t fam = mvl_ifp->hw_addr[4];
+
+ DEBUGD(&vrrp_dbg_auto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Autoconfiguring VRRP on %s",
+ vrid, family2str(fam), p->name);
+
+ vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version);
+
+ if (!vr) {
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Failed to autoconfigure VRRP on %s",
+ vrid, family2str(fam), p->name);
+ return NULL;
+ }
+
+ vr->autoconf = true;
+
+ /*
+ * If these interfaces are protodown on, we need to un-protodown them
+ * in order to get Zebra to send us their addresses so we can
+ * autoconfigure them.
+ */
+ if (vr->v4->mvl_ifp)
+ vrrp_zclient_send_interface_protodown(vr->v4->mvl_ifp, false);
+ if (vr->v6->mvl_ifp)
+ vrrp_zclient_send_interface_protodown(vr->v6->mvl_ifp, false);
+
+ /* If they're not, we can go ahead and add the addresses we have */
+ vrrp_autoconfig_autoaddrupdate(vr->v4);
+ vrrp_autoconfig_autoaddrupdate(vr->v6);
+
+ return vr;
+}
+
+/*
+ * Callback to notify autoconfig of interface add.
+ *
+ * If the interface is a VRRP-compatible device, and there is no existing VRRP
+ * router running on it, one is created. All addresses on the interface are
+ * added to the router.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+static int vrrp_autoconfig_if_add(struct interface *ifp)
+{
+ bool created = false;
+ struct vrrp_vrouter *vr;
+
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp))
+ return -1;
+
+ vr = vrrp_lookup_by_if_mvl(ifp);
+
+ if (!vr) {
+ vr = vrrp_autoconfig_autocreate(ifp);
+ created = true;
+ }
+
+ if (!vr || vr->autoconf == false)
+ return 0;
+
+ if (!created) {
+ /*
+ * We didn't create it, but it has already been autoconfigured.
+ * Try to attach this interface to the existing instance.
+ */
+ if (!vr->v4->mvl_ifp) {
+ vrrp_attach_interface(vr->v4);
+ /* If we just attached it, make sure it's turned on */
+ if (vr->v4->mvl_ifp) {
+ vrrp_zclient_send_interface_protodown(
+ vr->v4->mvl_ifp, false);
+ /*
+ * If it's already up, we can go ahead and add
+ * the addresses we have
+ */
+ vrrp_autoconfig_autoaddrupdate(vr->v4);
+ }
+ }
+ if (!vr->v6->mvl_ifp) {
+ vrrp_attach_interface(vr->v6);
+ /* If we just attached it, make sure it's turned on */
+ if (vr->v6->mvl_ifp) {
+ vrrp_zclient_send_interface_protodown(
+ vr->v6->mvl_ifp, false);
+ /*
+ * If it's already up, we can go ahead and add
+ * the addresses we have
+ */
+ vrrp_autoconfig_autoaddrupdate(vr->v6);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Callback to notify autoconfig of interface delete.
+ *
+ * If the interface is a VRRP-compatible device, and a VRRP router is running
+ * on it, and that VRRP router was automatically configured, it will be
+ * deleted. If that was the last router for the corresponding VRID (i.e., if
+ * this interface was a v4 VRRP interface and no v6 router is configured for
+ * the same VRID) then the entire virtual router is deleted.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+static int vrrp_autoconfig_if_del(struct interface *ifp)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *vrs;
+
+ vrs = vrrp_lookup_by_if_any(ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr))
+ if (vr->autoconf
+ && (!vr->ifp || (!vr->v4->mvl_ifp && !vr->v6->mvl_ifp))) {
+ DEBUGD(&vrrp_dbg_auto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router",
+ vr->vrid);
+ vrrp_vrouter_destroy(vr);
+ }
+
+ list_delete(&vrs);
+
+ return 0;
+}
+
+/*
+ * Callback to notify autoconfig of interface up.
+ *
+ * Creates VRRP instance on interface if it does not exist. Otherwise does
+ * nothing.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+static int vrrp_autoconfig_if_up(struct interface *ifp)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp);
+
+ if (vr && !vr->autoconf)
+ return 0;
+
+ if (!vr) {
+ vrrp_autoconfig_if_add(ifp);
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Callback to notify autoconfig of interface down.
+ *
+ * Does nothing. An interface down event is accompanied by address deletion
+ * events for all the addresses on the interface; if an autoconfigured VRRP
+ * router exists on this interface, then it will have all its addresses deleted
+ * and end up in Initialize.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+static int vrrp_autoconfig_if_down(struct interface *ifp)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ return 0;
+}
+
+/*
+ * Callback to notify autoconfig of a new interface address.
+ *
+ * If a VRRP router exists on this interface, its address list is updated to
+ * match the new address list. If no addresses remain, a Shutdown event is
+ * issued to the VRRP router.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ *
+ */
+static int vrrp_autoconfig_if_address_add(struct interface *ifp)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp);
+
+ if (vr && vr->autoconf) {
+ if (vr->v4->mvl_ifp == ifp)
+ vrrp_autoconfig_autoaddrupdate(vr->v4);
+ else if (vr->v6->mvl_ifp == ifp)
+ vrrp_autoconfig_autoaddrupdate(vr->v6);
+ }
+
+ return 0;
+}
+
+/*
+ * Callback to notify autoconfig of a removed interface address.
+ *
+ * If a VRRP router exists on this interface, its address list is updated to
+ * match the new address list. If no addresses remain, a Shutdown event is
+ * issued to the VRRP router.
+ *
+ * ifp
+ * Interface to operate on
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ *
+ */
+static int vrrp_autoconfig_if_address_del(struct interface *ifp)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp);
+
+ if (vr && vr->autoconf) {
+ if (vr->v4->mvl_ifp == ifp)
+ vrrp_autoconfig_autoaddrupdate(vr->v4);
+ else if (vr->v6->mvl_ifp == ifp)
+ vrrp_autoconfig_autoaddrupdate(vr->v6);
+ }
+
+ return 0;
+}
+
+int vrrp_autoconfig(void)
+{
+ if (!vrrp_autoconfig_is_on)
+ return 0;
+
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ vrrp_autoconfig_if_add(ifp);
+
+ return 0;
+}
+
+void vrrp_autoconfig_on(int version)
+{
+ vrrp_autoconfig_is_on = true;
+ vrrp_autoconfig_version = version;
+
+ vrrp_autoconfig();
+}
+
+void vrrp_autoconfig_off(void)
+{
+ vrrp_autoconfig_is_on = false;
+
+ struct list *ll = hash_to_list(vrrp_vrouters_hash);
+
+ struct listnode *ln;
+ struct vrrp_vrouter *vr;
+
+ for (ALL_LIST_ELEMENTS_RO(ll, ln, vr))
+ if (vr->autoconf)
+ vrrp_vrouter_destroy(vr);
+
+ list_delete(&ll);
+}
+
+/* Interface tracking ------------------------------------------------------ */
+
+/*
+ * Bind any pending interfaces.
+ *
+ * mvl_ifp
+ * macvlan interface that some VRRP instances might want to bind to
+ */
+static void vrrp_bind_pending(struct interface *mvl_ifp)
+{
+ struct vrrp_vrouter *vr;
+
+ vr = vrrp_lookup_by_if_mvl(mvl_ifp);
+
+ if (vr) {
+ if (mvl_ifp->hw_addr[4] == 0x01 && !vr->v4->mvl_ifp)
+ vrrp_attach_interface(vr->v4);
+ else if (mvl_ifp->hw_addr[4] == 0x02 && !vr->v6->mvl_ifp)
+ vrrp_attach_interface(vr->v6);
+ }
+}
+
+void vrrp_if_up(struct interface *ifp)
+{
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *vrs;
+
+ vrrp_bind_pending(ifp);
+
+ vrs = vrrp_lookup_by_if_any(ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
+ vrrp_check_start(vr);
+
+ if (!if_is_operative(ifp))
+ continue;
+
+ /*
+ * Handle the situation in which we performed a state
+ * transition on this VRRP router but needed to wait for the
+ * macvlan interface to come up to perform some actions
+ */
+ if (ifp == vr->v4->mvl_ifp) {
+ if (vr->v4->advert_pending) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ VRRP_LOGPFX_FAM
+ "Interface up; sending pending advertisement",
+ vr->vrid, family2str(vr->v4->family));
+ vrrp_send_advertisement(vr->v4);
+ vr->v4->advert_pending = false;
+ }
+ if (vr->v4->garp_pending) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ VRRP_LOGPFX_FAM
+ "Interface up; sending pending gratuitous ARP",
+ vr->vrid, family2str(vr->v4->family));
+ vrrp_garp_send_all(vr->v4);
+ vr->v4->garp_pending = false;
+ }
+ }
+ if (ifp == vr->v6->mvl_ifp) {
+ if (vr->v6->advert_pending) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ VRRP_LOGPFX_FAM
+ "Interface up; sending pending advertisement",
+ vr->vrid, family2str(vr->v6->family));
+ vrrp_send_advertisement(vr->v6);
+ vr->v6->advert_pending = false;
+ }
+ if (vr->v6->ndisc_pending) {
+ DEBUGD(&vrrp_dbg_proto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ VRRP_LOGPFX_FAM
+ "Interface up; sending pending Unsolicited Neighbor Advertisement",
+ vr->vrid, family2str(vr->v6->family));
+ vrrp_ndisc_una_send_all(vr->v6);
+ vr->v6->ndisc_pending = false;
+ }
+ }
+ }
+
+ list_delete(&vrs);
+
+ vrrp_autoconfig_if_up(ifp);
+}
+
+void vrrp_if_down(struct interface *ifp)
+{
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *vrs;
+
+ vrs = vrrp_lookup_by_if_any(ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
+ if (vr->ifp == ifp || vr->v4->mvl_ifp == ifp
+ || vr->v6->mvl_ifp == ifp) {
+ DEBUGD(&vrrp_dbg_auto,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID "Interface %s down",
+ vr->vrid, ifp->name);
+ }
+ }
+
+ list_delete(&vrs);
+
+ vrrp_autoconfig_if_down(ifp);
+}
+
+void vrrp_if_add(struct interface *ifp)
+{
+ vrrp_bind_pending(ifp);
+
+ /* thanks, zebra */
+ if (CHECK_FLAG(ifp->flags, IFF_UP))
+ vrrp_if_up(ifp);
+
+ vrrp_autoconfig_if_add(ifp);
+}
+
+void vrrp_if_del(struct interface *ifp)
+{
+ struct listnode *ln;
+ struct vrrp_vrouter *vr;
+ struct list *vrs = vrrp_lookup_by_if_any(ifp);
+
+ vrrp_if_down(ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
+ if ((vr->v4->mvl_ifp == ifp || vr->ifp == ifp)
+ && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) {
+ vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+ vr->v4->mvl_ifp = NULL;
+ } else if ((vr->v6->mvl_ifp == ifp || vr->ifp == ifp)
+ && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) {
+ vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+ vr->v6->mvl_ifp = NULL;
+ }
+ }
+
+ list_delete(&vrs);
+
+ vrrp_autoconfig_if_del(ifp);
+}
+
+void vrrp_if_address_add(struct interface *ifp)
+{
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *vrs;
+
+ /*
+ * We have to do a wide search here, because we need to know when a v6
+ * macvlan device gets a new address. This is because the macvlan link
+ * local is used as the source address for v6 advertisements, and hence
+ * "do I have a link local" constitutes an activation condition for v6
+ * virtual routers.
+ */
+ vrs = vrrp_lookup_by_if_any(ifp);
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr))
+ vrrp_check_start(vr);
+
+ list_delete(&vrs);
+
+ vrrp_autoconfig_if_address_add(ifp);
+}
+
+void vrrp_if_address_del(struct interface *ifp)
+{
+ /*
+ * Zebra is stupid and sends us address deletion notifications
+ * when any of the following condition sets are met:
+ *
+ * - if_is_operative && address deleted
+ * - if_is_operative -> !if_is_operative
+ *
+ * Note that the second one is nonsense, because Zebra behaves as
+ * though an interface going down means all the addresses on that
+ * interface got deleted. Which is a problem for autoconfig because all
+ * the addresses on an interface going away means the VRRP session goes
+ * to Initialize. However interfaces go down whenever we transition to
+ * Backup, so this effectively means that for autoconfigured instances
+ * we actually end up in Initialize whenever we try to go into Backup.
+ *
+ * Also, Zebra does NOT send us notifications when:
+ * - !if_is_operative && address deleted
+ *
+ * Which means if we're in backup and an address is deleted out from
+ * under us, we won't even know.
+ *
+ * The only solution here is to only resynchronize our address list
+ * when:
+ *
+ * - An interfaces comes up
+ * - An interface address is added
+ * - An interface address is deleted AND the interface is up
+ *
+ * Even though this is only a problem with autoconfig at the moment I'm
+ * papering over Zebra's braindead semantics here. Every piece of code
+ * in this function should be protected by a check that the interface
+ * is up.
+ */
+ if (if_is_operative(ifp))
+ vrrp_autoconfig_if_address_del(ifp);
+}
+
+/* Other ------------------------------------------------------------------- */
+
+int vrrp_config_write_interface(struct vty *vty)
+{
+ struct list *vrs = hash_to_list(vrrp_vrouters_hash);
+ struct listnode *ln, *ipln;
+ struct vrrp_vrouter *vr;
+ int writes = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
+ vty_frame(vty, "interface %s\n", vr->ifp->name);
+ ++writes;
+
+ vty_out(vty, " vrrp %" PRIu8 "%s\n", vr->vrid,
+ vr->version == 2 ? " version 2" : "");
+ ++writes;
+
+ if (vr->shutdown != vd.shutdown && ++writes)
+ vty_out(vty, " %svrrp %" PRIu8 " shutdown\n",
+ vr->shutdown ? "" : "no ", vr->vrid);
+
+ if (vr->preempt_mode != vd.preempt_mode && ++writes)
+ vty_out(vty, " %svrrp %" PRIu8 " preempt\n",
+ vr->preempt_mode ? "" : "no ", vr->vrid);
+
+ if (vr->accept_mode != vd.accept_mode && ++writes)
+ vty_out(vty, " %svrrp %" PRIu8 " accept\n",
+ vr->accept_mode ? "" : "no ", vr->vrid);
+
+ if (vr->advertisement_interval != vd.advertisement_interval
+ && ++writes)
+ vty_out(vty,
+ " vrrp %" PRIu8
+ " advertisement-interval %d\n",
+ vr->vrid, vr->advertisement_interval * CS2MS);
+
+ if (vr->priority != vd.priority && ++writes)
+ vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n",
+ vr->vrid, vr->priority);
+
+ struct ipaddr *ip;
+
+ for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) {
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ ipaddr2str(ip, ipbuf, sizeof(ipbuf));
+ vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid,
+ ipbuf);
+ ++writes;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) {
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ ipaddr2str(ip, ipbuf, sizeof(ipbuf));
+ vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid,
+ ipbuf);
+ ++writes;
+ }
+ vty_endframe(vty, "!\n");
+ }
+
+ list_delete(&vrs);
+
+ return writes;
+}
+
+int vrrp_config_write_global(struct vty *vty)
+{
+ unsigned int writes = 0;
+
+ if (vrrp_autoconfig_is_on && ++writes)
+ vty_out(vty, "vrrp autoconfigure%s\n",
+ vrrp_autoconfig_version == 2 ? " version 2" : "");
+
+ if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes)
+ vty_out(vty, "vrrp default priority %" PRIu8 "\n", vd.priority);
+
+ if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes)
+ vty_out(vty,
+ "vrrp default advertisement-interval %" PRIu16 "\n",
+ vd.advertisement_interval * CS2MS);
+
+ if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes)
+ vty_out(vty, "%svrrp default preempt\n",
+ !vd.preempt_mode ? "no " : "");
+
+ if (vd.accept_mode != VRRP_DEFAULT_ACCEPT && ++writes)
+ vty_out(vty, "%svrrp default accept\n",
+ !vd.accept_mode ? "no " : "");
+
+ if (vd.shutdown != VRRP_DEFAULT_SHUTDOWN && ++writes)
+ vty_out(vty, "%svrrp default shutdown\n",
+ !vd.shutdown ? "no " : "");
+
+ return writes;
+}
+
+static unsigned int vrrp_hash_key(const void *arg)
+{
+ const struct vrrp_vrouter *vr = arg;
+ char key[IFNAMSIZ + 64];
+
+ snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid);
+
+ return string_hash_make(key);
+}
+
+static bool vrrp_hash_cmp(const void *arg1, const void *arg2)
+{
+ const struct vrrp_vrouter *vr1 = arg1;
+ const struct vrrp_vrouter *vr2 = arg2;
+
+ if (vr1->ifp != vr2->ifp)
+ return 0;
+ if (vr1->vrid != vr2->vrid)
+ return 0;
+
+ return 1;
+}
+
+void vrrp_init(void)
+{
+ /* Set default defaults */
+ vd.priority = VRRP_DEFAULT_PRIORITY;
+ vd.advertisement_interval = VRRP_DEFAULT_ADVINT;
+ vd.preempt_mode = VRRP_DEFAULT_PREEMPT;
+ vd.accept_mode = VRRP_DEFAULT_ACCEPT;
+ vd.shutdown = VRRP_DEFAULT_SHUTDOWN;
+
+ vrrp_autoconfig_version = 3;
+ vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp,
+ "VRRP virtual router hash");
+ vrf_init(NULL, NULL, NULL, NULL, NULL);
+}
+
+void vrrp_fini(void)
+{
+ /* Destroy all instances */
+ struct list *vrs = hash_to_list(vrrp_vrouters_hash);
+
+ struct listnode *ln;
+ struct vrrp_vrouter *vr;
+
+ for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr))
+ vrrp_vrouter_destroy(vr);
+
+ list_delete(&vrs);
+
+ hash_clean(vrrp_vrouters_hash, NULL);
+ hash_free(vrrp_vrouters_hash);
+}
diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h
new file mode 100644
index 0000000000..fd4901fe22
--- /dev/null
+++ b/vrrpd/vrrp.h
@@ -0,0 +1,570 @@
+/*
+ * VRRP global definitions and state machine.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_H__
+#define __VRRP_H__
+
+#include <zebra.h>
+#include <netinet/ip.h>
+
+#include "lib/hash.h"
+#include "lib/hook.h"
+#include "lib/if.h"
+#include "lib/linklist.h"
+#include "lib/privs.h"
+#include "lib/stream.h"
+#include "lib/thread.h"
+#include "lib/vty.h"
+
+/* Global definitions */
+#define VRRP_RADV_INT 16
+#define VRRP_PRIO_MASTER 255
+#define VRRP_MCASTV4_GROUP_STR "224.0.0.18"
+#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12"
+#define VRRP_MCASTV4_GROUP 0xe0000012
+#define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012
+#define IPPROTO_VRRP 112
+
+#define VRRP_LOGPFX_VRID "[VRID %u] "
+#define VRRP_LOGPFX_FAM "[%s] "
+
+/* Default defaults */
+#define VRRP_DEFAULT_PRIORITY 100
+#define VRRP_DEFAULT_ADVINT 100
+#define VRRP_DEFAULT_PREEMPT true
+#define VRRP_DEFAULT_ACCEPT true
+#define VRRP_DEFAULT_SHUTDOWN false
+
+/* User compatibility constant */
+#define CS2MS 10
+
+/* Configured defaults */
+struct vrrp_defaults {
+ uint8_t priority;
+ uint16_t advertisement_interval;
+ bool preempt_mode;
+ bool accept_mode;
+ bool shutdown;
+};
+
+extern struct vrrp_defaults vd;
+
+/* threadmaster */
+extern struct thread_master *master;
+
+/* privileges */
+extern struct zebra_privs_t vrrp_privs;
+
+/* Global hash of all Virtual Routers */
+extern struct hash *vrrp_vrouters_hash;
+
+/*
+ * VRRP Router.
+ *
+ * This struct contains all state for a particular VRRP Router operating
+ * in a Virtual Router for either IPv4 or IPv6.
+ */
+struct vrrp_router {
+ /*
+ * Whether this VRRP Router is active.
+ */
+ bool is_active;
+
+ /* Whether we are the address owner */
+ bool is_owner;
+
+ /* Rx socket: Rx from parent of mvl_ifp */
+ int sock_rx;
+ /* Tx socket; Tx from mvl_ifp */
+ int sock_tx;
+
+ /* macvlan interface */
+ struct interface *mvl_ifp;
+
+ /* Source address for advertisements */
+ struct ipaddr src;
+
+ /* Socket read buffer */
+ uint8_t ibuf[IP_MAXPACKET];
+
+ /*
+ * Address family of this Virtual Router.
+ * Either AF_INET or AF_INET6.
+ */
+ int family;
+
+ /*
+ * Virtual Router this VRRP Router is participating in.
+ */
+ struct vrrp_vrouter *vr;
+
+ /*
+ * One or more IPvX addresses associated with this Virtual
+ * Router. The first address must be the "primary" address this
+ * Virtual Router is backing up in the case of IPv4. In the case of
+ * IPv6 it must be the link-local address of vr->ifp.
+ *
+ * Type: struct ipaddr *
+ */
+ struct list *addrs;
+
+ /*
+ * This flag says whether we are waiting on an interface up
+ * notification from Zebra before we send an ADVERTISEMENT.
+ */
+ bool advert_pending;
+
+ /*
+ * If this is an IPv4 VRRP router, this flag says whether we are
+ * waiting on an interface up notification from Zebra before we send
+ * gratuitous ARP packets for all our addresses. Should never be true
+ * if family == AF_INET6.
+ */
+ bool garp_pending;
+ /*
+ * If this is an IPv6 VRRP router, this flag says whether we are
+ * waiting on an interface up notification from Zebra before we send
+ * Unsolicited Neighbor Advertisement packets for all our addresses.
+ * Should never be true if family == AF_INET.
+ */
+ bool ndisc_pending;
+
+ /*
+ * Effective priority
+ * => vr->priority if we are Backup
+ * => 255 if we are Master
+ */
+ uint8_t priority;
+
+ /*
+ * Advertisement interval contained in ADVERTISEMENTS received from the
+ * Master (centiseconds)
+ */
+ uint16_t master_adver_interval;
+
+ /*
+ * Time to skew Master_Down_Interval in centiseconds. Calculated as:
+ * (((256 - priority) * Master_Adver_Interval) / 256)
+ */
+ uint16_t skew_time;
+
+ /*
+ * Time interval for Backup to declare Master down (centiseconds).
+ * Calculated as:
+ * (3 * Master_Adver_Interval) + Skew_time
+ */
+ uint16_t master_down_interval;
+
+ /*
+ * The MAC address used for the source MAC address in VRRP
+ * advertisements, advertised in ARP requests/responses, and advertised
+ * in ND Neighbor Advertisements.
+ */
+ struct ethaddr vmac;
+
+ struct {
+ int state;
+ } fsm;
+
+ struct {
+ /* Total number of advertisements sent and received */
+ uint32_t adver_tx_cnt;
+ uint32_t adver_rx_cnt;
+ /* Total number of gratuitous ARPs sent */
+ uint32_t garp_tx_cnt;
+ /* Total number of unsolicited Neighbor Advertisements sent */
+ uint32_t una_tx_cnt;
+ /* Total number of state transitions */
+ uint32_t trans_cnt;
+ } stats;
+
+ struct thread *t_master_down_timer;
+ struct thread *t_adver_timer;
+ struct thread *t_read;
+ struct thread *t_write;
+};
+
+/*
+ * VRRP Virtual Router.
+ *
+ * This struct contains all state and configuration for a given Virtual Router
+ * Identifier on a given interface, both v4 and v6.
+ *
+ * RFC5798 s. 1 states:
+ * "Within a VRRP router, the virtual routers in each of the IPv4 and IPv6
+ * address families are a domain unto themselves and do not overlap."
+ *
+ * This implementation has chosen the tuple (interface, VRID) as the key for a
+ * particular VRRP Router, and the rest of the program is designed around this
+ * assumption. Additionally, base protocol configuration parameters such as the
+ * advertisement interval and (configured) priority are shared between v4 and
+ * v6 instances. This corresponds to the choice made by other industrial
+ * implementations.
+ */
+struct vrrp_vrouter {
+ /* Whether this instance was automatically configured */
+ bool autoconf;
+
+ /* Whether this VRRP router is in administrative shutdown */
+ bool shutdown;
+
+ /* Interface */
+ struct interface *ifp;
+
+ /* Version */
+ uint8_t version;
+
+ /* Virtual Router Identifier */
+ uint32_t vrid;
+
+ /* Configured priority */
+ uint8_t priority;
+
+ /*
+ * Time interval between ADVERTISEMENTS (centiseconds). Default is 100
+ * centiseconds (1 second).
+ */
+ uint16_t advertisement_interval;
+
+ /*
+ * Controls whether a (starting or restarting) higher-priority Backup
+ * router preempts a lower-priority Master router. Values are True to
+ * allow preemption and False to prohibit preemption. Default is True.
+ */
+ bool preempt_mode;
+
+ /*
+ * Controls whether a virtual router in Master state will accept
+ * packets addressed to the address owner's IPvX address as its own if
+ * it is not the IPvX address owner. The default is False.
+ */
+ bool accept_mode;
+
+ struct vrrp_router *v4;
+ struct vrrp_router *v6;
+};
+
+/*
+ * Initialize VRRP global datastructures.
+ */
+void vrrp_init(void);
+
+/*
+ * Destroy all VRRP instances and gracefully shutdown.
+ *
+ * For instances in Master state, VRRP advertisements with 0 priority will be
+ * sent if possible to notify Backup routers that we are going away.
+ */
+void vrrp_fini(void);
+
+
+/* Creation and destruction ------------------------------------------------ */
+
+/*
+ * Create and register a new VRRP Virtual Router.
+ *
+ * ifp
+ * Base interface to configure VRRP on
+ *
+ * vrid
+ * Virtual Router Identifier
+ */
+struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid,
+ uint8_t version);
+
+/*
+ * Destroy a VRRP Virtual Router, freeing all its resources.
+ *
+ * If there are any running VRRP instances, these are stopped and destroyed.
+ */
+void vrrp_vrouter_destroy(struct vrrp_vrouter *vr);
+
+
+/* Configuration controllers ----------------------------------------------- */
+
+/*
+ * Check if a Virtual Router ought to be started, and if so, start it.
+ *
+ * vr
+ * Virtual Router to checkstart
+ */
+void vrrp_check_start(struct vrrp_vrouter *vr);
+
+/*
+ * Change the configured priority of a VRRP Virtual Router.
+ *
+ * Note that this only changes the configured priority of the Virtual Router.
+ * The currently effective priority will not be changed; to change the
+ * effective priority, the Virtual Router must be restarted by issuing a
+ * VRRP_EVENT_SHUTDOWN followed by a VRRP_EVENT_STARTUP.
+ *
+ * vr
+ * Virtual Router to change priority of
+ *
+ * priority
+ * New priority
+ */
+void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority);
+
+/*
+ * Set Advertisement Interval on this Virtual Router.
+ *
+ * vr
+ * Virtual Router to change priority of
+ *
+ * advertisement_interval
+ * New advertisement interval
+ */
+void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
+ uint16_t advertisement_interval);
+
+/*
+ * Add an IPvX address to a VRRP Virtual Router.
+ *
+ * r
+ * Virtual Router to add IPvx address to
+ *
+ * ip
+ * Address to add
+ *
+ * activate
+ * Whether to automatically start the VRRP router if this is the first IP
+ * address added.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip);
+
+/*
+ * Add an IPv4 address to a VRRP Virtual Router.
+ *
+ * vr
+ * Virtual Router to add IPv4 address to
+ *
+ * v4
+ * Address to add
+ *
+ * activate
+ * Whether to automatically start the VRRP router if this is the first IP
+ * address added.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4);
+
+/*
+ * Add an IPv6 address to a VRRP Virtual Router.
+ *
+ * vr
+ * Virtual Router to add IPv6 address to
+ *
+ * v6
+ * Address to add
+ *
+ * activate
+ * Whether to automatically start the VRRP router if this is the first IP
+ * address added.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
+
+/*
+ * Remove an IP address from a VRRP Virtual Router.
+ *
+ * r
+ * Virtual Router to remove IP address from
+ *
+ * ip
+ * Address to remove
+ *
+ * deactivate
+ * Whether to automatically stop the VRRP router if removing v4 would leave
+ * us with an empty address list. If this is not true and ip is the only IP
+ * address backed up by this virtual router, this function will not remove
+ * the address and return failure.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip);
+
+/*
+ * Remove an IPv4 address from a VRRP Virtual Router.
+ *
+ * vr
+ * Virtual Router to remove IPv4 address from
+ *
+ * v4
+ * Address to remove
+ *
+ * deactivate
+ * Whether to automatically stop the VRRP router if removing v4 would leave
+ * us with an empty address list. If this is not true and v4 is the only
+ * IPv4 address backed up by this virtual router, this function will not
+ * remove the address and return failure.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4);
+
+/*
+ * Remove an IPv6 address from a VRRP Virtual Router.
+ *
+ * vr
+ * Virtual Router to remove IPv6 address from
+ *
+ * v6
+ * Address to remove
+ *
+ * deactivate
+ * Whether to automatically stop the VRRP router if removing v5 would leave
+ * us with an empty address list. If this is not true and v4 is the only
+ * IPv6 address backed up by this virtual router, this function will not
+ * remove the address and return failure.
+ *
+ * Returns:
+ * -1 on error
+ * 0 otherwise
+ */
+int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
+
+/* State machine ----------------------------------------------------------- */
+
+#define VRRP_STATE_INITIALIZE 0
+#define VRRP_STATE_MASTER 1
+#define VRRP_STATE_BACKUP 2
+#define VRRP_EVENT_STARTUP 0
+#define VRRP_EVENT_SHUTDOWN 1
+
+extern const char *vrrp_state_names[3];
+extern const char *vrrp_event_names[2];
+
+/*
+ * This hook called whenever the state of a Virtual Router changes, after the
+ * specific internal state handlers have run.
+ *
+ * Use this if you need to react to state changes to perform non-critical
+ * tasks. Critical tasks should go in the internal state change handlers.
+ */
+DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to));
+
+/*
+ * Trigger a VRRP event on a given Virtual Router..
+ *
+ * vr
+ * Virtual Router to operate on
+ *
+ * event
+ * Event to kick off. All event related processing will have completed upon
+ * return of this function.
+ *
+ * Returns:
+ * < 0 if the event created an error
+ * 0 otherwise
+ */
+int vrrp_event(struct vrrp_router *r, int event);
+
+/* Autoconfig -------------------------------------------------------------- */
+
+/*
+ * Search for and automatically configure VRRP instances on interfaces.
+ *
+ * ifp
+ * Interface to autoconfig. If it is a macvlan interface and has a VRRP MAC,
+ * a VRRP instance corresponding to VMAC assigned to macvlan will be created
+ * on the parent interface and all addresses on the macvlan interface except
+ * the v6 link local will be configured as VRRP addresses. If NULL, this
+ * treatment will be applied to all existing interfaces matching the above
+ * criterion.
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+int vrrp_autoconfig(void);
+
+/*
+ * Enable autoconfiguration.
+ *
+ * Calling this function will cause vrrpd to automatically configure VRRP
+ * instances on existing compatible macvlan interfaces. These instances will
+ * react to interface up/down and address add/delete events to keep themselves
+ * in sync with the available interfaces.
+ *
+ * version
+ * VRRP version to use for autoconfigured instances. Must be 2 or 3.
+ */
+void vrrp_autoconfig_on(int version);
+
+/*
+ * Disable autoconfiguration.
+ *
+ * Calling this function will delete all existing autoconfigured VRRP instances.
+ */
+void vrrp_autoconfig_off(void);
+
+/* Interface Tracking ------------------------------------------------------ */
+
+void vrrp_if_add(struct interface *ifp);
+void vrrp_if_del(struct interface *ifp);
+void vrrp_if_up(struct interface *ifp);
+void vrrp_if_down(struct interface *ifp);
+void vrrp_if_address_add(struct interface *ifp);
+void vrrp_if_address_del(struct interface *ifp);
+
+/* Other ------------------------------------------------------------------- */
+
+/*
+ * Write interface block-level configuration to vty.
+ *
+ * vty
+ * vty to write config to
+ *
+ * Returns:
+ * # of lines written
+ */
+int vrrp_config_write_interface(struct vty *vty);
+
+/*
+ * Write global level configuration to vty.
+ *
+ * vty
+ * vty to write config to
+ *
+ * Returns:
+ * # of lines written
+ */
+int vrrp_config_write_global(struct vty *vty);
+
+/*
+ * Find VRRP Virtual Router by Virtual Router ID
+ */
+struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid);
+
+#endif /* __VRRP_H__ */
diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c
new file mode 100644
index 0000000000..78e153a082
--- /dev/null
+++ b/vrrpd/vrrp_arp.c
@@ -0,0 +1,219 @@
+/*
+ * VRRP ARP handling.
+ * Copyright (C) 2001-2017 Alexandre Cassen
+ * Portions:
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <linux/if_packet.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include "lib/if.h"
+#include "lib/linklist.h"
+#include "lib/log.h"
+#include "lib/memory.h"
+#include "lib/prefix.h"
+
+#include "vrrp.h"
+#include "vrrp_arp.h"
+#include "vrrp_debug.h"
+
+#define VRRP_LOGPFX "[ARP] "
+
+/*
+ * The size of the garp packet buffer should be the large enough to hold the
+ * largest arp packet to be sent + the size of the link layer header for the
+ * corresponding protocol. In this case we hardcode for Ethernet.
+ */
+#define GARP_BUFFER_SIZE \
+ sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \
+ + 2 * sizeof(struct in_addr)
+
+/* static vars */
+static int garp_fd = -1;
+
+/* Send the gratuitous ARP message */
+static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf,
+ ssize_t pack_len)
+{
+ struct sockaddr_ll sll;
+ ssize_t len;
+
+ /* Build the dst device */
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_protocol = ETH_P_ARP;
+ sll.sll_ifindex = (int)ifp->ifindex;
+ sll.sll_halen = ifp->hw_addr_len;
+ memset(sll.sll_addr, 0xFF, ETH_ALEN);
+
+ /* Send packet */
+ len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll,
+ sizeof(sll));
+
+ return len;
+}
+
+/* Build a gratuitous ARP message over a specific interface */
+static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp,
+ struct in_addr *v4)
+{
+ uint8_t *arp_ptr;
+
+ if (ifp->hw_addr_len == 0)
+ return -1;
+
+ /* Build Ethernet header */
+ struct ether_header *eth = (struct ether_header *)buf;
+
+ memset(eth->ether_dhost, 0xFF, ETH_ALEN);
+ memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN);
+ eth->ether_type = htons(ETHERTYPE_ARP);
+
+ /* Build ARP payload */
+ struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN);
+
+ arph->ar_hrd = htons(HWTYPE_ETHER);
+ arph->ar_pro = htons(ETHERTYPE_IP);
+ arph->ar_hln = ifp->hw_addr_len;
+ arph->ar_pln = sizeof(struct in_addr);
+ arph->ar_op = htons(ARPOP_REQUEST);
+ arp_ptr = (uint8_t *)(arph + 1);
+ /* Source MAC: us */
+ memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len);
+ arp_ptr += ifp->hw_addr_len;
+ /* Source IP: us */
+ memcpy(arp_ptr, v4, sizeof(struct in_addr));
+ arp_ptr += sizeof(struct in_addr);
+ /* Dest MAC: broadcast */
+ memset(arp_ptr, 0xFF, ETH_ALEN);
+ arp_ptr += ifp->hw_addr_len;
+ /* Dest IP: us */
+ memcpy(arp_ptr, v4, sizeof(struct in_addr));
+ arp_ptr += sizeof(struct in_addr);
+
+ return arp_ptr - buf;
+}
+
+void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4)
+{
+ struct interface *ifp = r->mvl_ifp;
+ uint8_t garpbuf[GARP_BUFFER_SIZE];
+ ssize_t garpbuf_len;
+ ssize_t sent_len;
+ char astr[INET_ADDRSTRLEN];
+
+ /* If the interface doesn't support ARP, don't try sending */
+ if (ifp->flags & IFF_NOARP) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Unable to send gratuitous ARP on %s; has IFF_NOARP",
+ r->vr->vrid, family2str(r->family), ifp->name);
+ return;
+ }
+
+ /* Build garp */
+ garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4);
+
+ if (garpbuf_len < 0) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Unable to send gratuitous ARP on %s; MAC address unknown",
+ r->vr->vrid, family2str(r->family), ifp->name);
+ return;
+ };
+
+ /* Send garp */
+ inet_ntop(AF_INET, v4, astr, sizeof(astr));
+
+ DEBUGD(&vrrp_dbg_arp,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Sending gratuitous ARP on %s for %s",
+ r->vr->vrid, family2str(r->family), ifp->name, astr);
+ if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL))
+ zlog_hexdump(garpbuf, garpbuf_len);
+
+ sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len);
+
+ if (sent_len < 0)
+ zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Error sending gratuitous ARP on %s for %s",
+ r->vr->vrid, family2str(r->family), ifp->name, astr);
+ else
+ ++r->stats.garp_tx_cnt;
+}
+
+void vrrp_garp_send_all(struct vrrp_router *r)
+{
+ assert(r->family == AF_INET);
+
+ struct interface *ifp = r->mvl_ifp;
+
+ /* If the interface doesn't support ARP, don't try sending */
+ if (ifp->flags & IFF_NOARP) {
+ zlog_warn(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Unable to send gratuitous ARP on %s; has IFF_NOARP\n",
+ r->vr->vrid, family2str(r->family), ifp->name);
+ return;
+ }
+
+ struct listnode *ln;
+ struct ipaddr *ip;
+
+ for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
+ vrrp_garp_send(r, &ip->ipaddr_v4);
+}
+
+
+void vrrp_garp_init(void)
+{
+ /* Create the socket descriptor */
+ /* FIXME: why ETH_P_RARP? */
+ errno = 0;
+ frr_elevate_privs(&vrrp_privs) {
+ garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC,
+ htons(ETH_P_RARP));
+ }
+
+ if (garp_fd > 0) {
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX "Initialized gratuitous ARP socket");
+ DEBUGD(&vrrp_dbg_arp,
+ VRRP_LOGPFX "Initialized gratuitous ARP subsystem");
+ } else {
+ zlog_err(VRRP_LOGPFX
+ "Error initializing gratuitous ARP subsystem");
+ }
+}
+
+void vrrp_garp_fini(void)
+{
+ close(garp_fd);
+ garp_fd = -1;
+
+ DEBUGD(&vrrp_dbg_arp,
+ VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem");
+}
+
+bool vrrp_garp_is_init(void)
+{
+ return garp_fd > 0;
+}
diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h
new file mode 100644
index 0000000000..21f2c4edd1
--- /dev/null
+++ b/vrrpd/vrrp_arp.h
@@ -0,0 +1,36 @@
+/*
+ * VRRP ARP handling.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_ARP_H__
+#define __VRRP_ARP_H__
+
+#include <zebra.h>
+
+#include "vrrp.h"
+
+/* FIXME: Use the kernel define for this */
+#define HWTYPE_ETHER 1
+
+extern void vrrp_garp_init(void);
+extern void vrrp_garp_fini(void);
+extern bool vrrp_garp_is_init(void);
+extern void vrrp_garp_send(struct vrrp_router *vr, struct in_addr *v4);
+extern void vrrp_garp_send_all(struct vrrp_router *vr);
+
+#endif /* __VRRP_ARP_H__ */
diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c
new file mode 100644
index 0000000000..09d5e780cf
--- /dev/null
+++ b/vrrpd/vrrp_debug.c
@@ -0,0 +1,131 @@
+/*
+ * VRRP debugging.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/debug.h"
+#include "lib/vector.h"
+
+#include "vrrp_debug.h"
+
+/* clang-format off */
+struct debug vrrp_dbg_arp = {0, "VRRP ARP"};
+struct debug vrrp_dbg_auto = {0, "VRRP autoconfiguration events"};
+struct debug vrrp_dbg_ndisc = {0, "VRRP Neighbor Discovery"};
+struct debug vrrp_dbg_pkt = {0, "VRRP packets"};
+struct debug vrrp_dbg_proto = {0, "VRRP protocol events"};
+struct debug vrrp_dbg_sock = {0, "VRRP sockets"};
+struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"};
+
+struct debug *vrrp_debugs[] = {
+ &vrrp_dbg_arp,
+ &vrrp_dbg_auto,
+ &vrrp_dbg_ndisc,
+ &vrrp_dbg_pkt,
+ &vrrp_dbg_proto,
+ &vrrp_dbg_sock,
+ &vrrp_dbg_zebra
+};
+
+const char *vrrp_debugs_conflines[] = {
+ "debug vrrp arp",
+ "debug vrrp autoconfigure",
+ "debug vrrp ndisc",
+ "debug vrrp packets",
+ "debug vrrp protocol",
+ "debug vrrp sockets",
+ "debug vrrp zebra",
+};
+/* clang-format on */
+
+/*
+ * Set or unset flags on all debugs for vrrpd.
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+static void vrrp_debug_set_all(uint32_t flags, bool set)
+{
+ for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) {
+ DEBUG_FLAGS_SET(vrrp_debugs[i], flags, set);
+
+ /* if all modes have been turned off, don't preserve options */
+ if (!DEBUG_MODE_CHECK(vrrp_debugs[i], DEBUG_MODE_ALL))
+ DEBUG_CLEAR(vrrp_debugs[i]);
+ }
+}
+
+static int vrrp_debug_config_write_helper(struct vty *vty, bool config)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ if (config)
+ mode = DEBUG_MODE_CONF;
+
+ for (unsigned int i = 0; i < array_size(vrrp_debugs); i++)
+ if (DEBUG_MODE_CHECK(vrrp_debugs[i], mode))
+ vty_out(vty, "%s\n", vrrp_debugs_conflines[i]);
+
+ return 0;
+}
+
+int vrrp_config_write_debug(struct vty *vty)
+{
+ return vrrp_debug_config_write_helper(vty, true);
+}
+
+int vrrp_debug_status_write(struct vty *vty)
+{
+ return vrrp_debug_config_write_helper(vty, false);
+}
+
+void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode,
+ bool onoff, bool proto, bool autoconf, bool pkt, bool sock,
+ bool ndisc, bool arp, bool zebra)
+{
+ uint32_t mode = DEBUG_NODE2MODE(vtynode);
+
+ if (proto)
+ DEBUG_MODE_SET(&vrrp_dbg_proto, mode, onoff);
+ if (autoconf)
+ DEBUG_MODE_SET(&vrrp_dbg_auto, mode, onoff);
+ if (pkt)
+ DEBUG_MODE_SET(&vrrp_dbg_pkt, mode, onoff);
+ if (sock)
+ DEBUG_MODE_SET(&vrrp_dbg_sock, mode, onoff);
+ if (ndisc)
+ DEBUG_MODE_SET(&vrrp_dbg_ndisc, mode, onoff);
+ if (arp)
+ DEBUG_MODE_SET(&vrrp_dbg_arp, mode, onoff);
+ if (zebra)
+ DEBUG_MODE_SET(&vrrp_dbg_zebra, mode, onoff);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct debug_callbacks vrrp_dbg_cbs = {.debug_set_all = vrrp_debug_set_all};
+
+void vrrp_debug_init(void)
+{
+ debug_init(&vrrp_dbg_cbs);
+}
diff --git a/vrrpd/vrrp_debug.h b/vrrpd/vrrp_debug.h
new file mode 100644
index 0000000000..20f9930955
--- /dev/null
+++ b/vrrpd/vrrp_debug.h
@@ -0,0 +1,87 @@
+/*
+ * VRRP debugging.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_DEBUG_H__
+#define __VRRP_DEBUG_H__
+
+#include <zebra.h>
+
+#include "lib/debug.h"
+
+/* VRRP debugging records */
+struct debug vrrp_dbg_arp;
+struct debug vrrp_dbg_auto;
+struct debug vrrp_dbg_ndisc;
+struct debug vrrp_dbg_pkt;
+struct debug vrrp_dbg_proto;
+struct debug vrrp_dbg_sock;
+struct debug vrrp_dbg_zebra;
+
+/*
+ * Initialize VRRP debugging.
+ *
+ * Installs VTY commands and registers callbacks.
+ */
+void vrrp_debug_init(void);
+
+/*
+ * Print VRRP debugging configuration.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int vrrp_config_write_debug(struct vty *vty);
+
+/*
+ * Print VRRP debugging configuration, human readable form.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int vrrp_debug_status_write(struct vty *vty);
+
+/*
+ * Set debugging status.
+ *
+ * ifp
+ * Interface to set status on
+ *
+ * vrid
+ * VRID of instance to set status on
+ *
+ * vtynode
+ * vty->node
+ *
+ * onoff
+ * Whether to turn the specified debugs on or off
+ *
+ * proto
+ * Turn protocol debugging on or off
+ *
+ * autoconf
+ * Turn autoconfiguration debugging on or off
+ *
+ * pkt
+ * Turn packet debugging on or off
+ */
+void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode,
+ bool onoff, bool proto, bool autoconf, bool pkt, bool sock,
+ bool ndisc, bool arp, bool zebra);
+
+#endif /* __VRRP_DEBUG_H__ */
diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c
new file mode 100644
index 0000000000..46a92d936a
--- /dev/null
+++ b/vrrpd/vrrp_main.c
@@ -0,0 +1,159 @@
+/*
+ * VRRP entry point.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+
+#include "lib/command.h"
+#include "lib/filter.h"
+#include "lib/getopt.h"
+#include "lib/if.h"
+#include "lib/libfrr.h"
+#include "lib/log.h"
+#include "lib/memory.h"
+#include "lib/nexthop.h"
+#include "lib/privs.h"
+#include "lib/sigevent.h"
+#include "lib/thread.h"
+#include "lib/vrf.h"
+
+#include "vrrp.h"
+#include "vrrp_debug.h"
+#include "vrrp_vty.h"
+#include "vrrp_zebra.h"
+
+char backup_config_file[256];
+
+zebra_capabilities_t _caps_p[] = {
+ ZCAP_NET_RAW,
+};
+
+struct zebra_privs_t vrrp_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0};
+
+struct option longopts[] = { {0} };
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+}
+
+/* SIGINT / SIGTERM handler. */
+static void __attribute__((noreturn)) sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ vrrp_fini();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct quagga_signal_t vrrp_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+static const struct frr_yang_module_info *vrrp_yang_modules[] = {
+ &frr_interface_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),
+)
+
+int main(int argc, char **argv, char **envp)
+{
+ frr_preinit(&vrrpd_di, argc, argv);
+ frr_opt_add("", longopts, "");
+
+ while (1) {
+ int opt;
+
+ opt = frr_getopt(argc, argv, NULL);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ break;
+ }
+ }
+
+ master = frr_init();
+
+ vrrp_debug_init();
+ vrrp_zebra_init();
+ vrrp_vty_init();
+ vrrp_init();
+
+ snprintf(backup_config_file, sizeof(backup_config_file),
+ "%s/vrrpd.conf", frr_sysconfdir);
+ vrrpd_di.backup_config_file = backup_config_file;
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/vrrpd/vrrp_memory.c b/vrrpd/vrrp_memory.c
new file mode 100644
index 0000000000..30eef523cd
--- /dev/null
+++ b/vrrpd/vrrp_memory.c
@@ -0,0 +1,29 @@
+/*
+ * VRRP memory types.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "lib/memory.h"
+
+#include "vrrp_memory.h"
+
+DEFINE_MGROUP(VRRPD, "vrrpd");
+DEFINE_MTYPE(VRRPD, VRRP_IP, "VRRP IP address");
+DEFINE_MTYPE(VRRPD, VRRP_PKT, "VRRP packet");
+DEFINE_MTYPE(VRRPD, VRRP_RTR, "VRRP Router");
diff --git a/vrrpd/vrrp_memory.h b/vrrpd/vrrp_memory.h
new file mode 100644
index 0000000000..c3025d1acb
--- /dev/null
+++ b/vrrpd/vrrp_memory.h
@@ -0,0 +1,32 @@
+/*
+ * VRRP memory types.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_MEMORY_H__
+#define __VRRP_MEMORY_H__
+
+#include <zebra.h>
+
+#include "lib/memory.h"
+
+DECLARE_MGROUP(VRRPD);
+DECLARE_MTYPE(VRRP_IP);
+DECLARE_MTYPE(VRRP_PKT);
+DECLARE_MTYPE(VRRP_RTR);
+
+#endif /* __VRRP_MEMORY_H__ */
diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c
new file mode 100644
index 0000000000..348958509a
--- /dev/null
+++ b/vrrpd/vrrp_ndisc.c
@@ -0,0 +1,245 @@
+/*
+ * VRRP Neighbor Discovery.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * Portions:
+ * Copyright (C) 2001-2017 Alexandre Cassen
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+
+#include "lib/checksum.h"
+#include "lib/if.h"
+#include "lib/ipaddr.h"
+#include "lib/log.h"
+
+#include "vrrp_debug.h"
+#include "vrrp_ndisc.h"
+
+#define VRRP_LOGPFX "[NDISC] "
+
+#define VRRP_NDISC_HOPLIMIT 255
+#define VRRP_NDISC_SIZE \
+ ETHER_HDR_LEN + sizeof(struct ip6_hdr) \
+ + sizeof(struct nd_neighbor_advert) \
+ + sizeof(struct nd_opt_hdr) + ETH_ALEN
+
+/* static vars */
+static int ndisc_fd = -1;
+
+/*
+ * Build an unsolicited Neighbour Advertisement.
+ *
+ * ifp
+ * Interface to send Neighbor Advertisement on
+ *
+ * ip
+ * IP address to send Neighbor Advertisement for
+ *
+ * buf
+ * Buffer to fill with IPv6 Neighbor Advertisement message. Includes
+ * Ethernet header.
+ *
+ * bufsiz
+ * Size of buf.
+ *
+ * Returns;
+ * -1 if bufsiz is too small
+ * 0 otherwise
+ */
+static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip,
+ uint8_t *buf, size_t bufsiz)
+{
+ if (bufsiz < VRRP_NDISC_SIZE)
+ return -1;
+
+ memset(buf, 0x00, bufsiz);
+
+ struct ether_header *eth = (struct ether_header *)buf;
+ struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN);
+ struct nd_neighbor_advert *ndh =
+ (struct nd_neighbor_advert *)((char *)ip6h
+ + sizeof(struct ip6_hdr));
+ struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr;
+ struct nd_opt_hdr *nd_opt_h =
+ (struct nd_opt_hdr *)((char *)ndh
+ + sizeof(struct nd_neighbor_advert));
+ char *nd_opt_lladdr =
+ (char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr));
+ char *lladdr = (char *)ifp->hw_addr;
+
+ /*
+ * An IPv6 packet with a multicast destination address DST, consisting
+ * of the sixteen octets DST[1] through DST[16], is transmitted to the
+ * Ethernet multicast address whose first two octets are the value 3333
+ * hexadecimal and whose last four octets are the last four octets of
+ * DST.
+ * - RFC2464.7
+ *
+ * In this case we are sending to the all nodes multicast address, so
+ * the last four octets are 0x00 0x00 0x00 0x01.
+ */
+ memset(eth->ether_dhost, 0, ETH_ALEN);
+ eth->ether_dhost[0] = 0x33;
+ eth->ether_dhost[1] = 0x33;
+ eth->ether_dhost[5] = 1;
+
+ /* Set source Ethernet address to interface link layer address */
+ memcpy(eth->ether_shost, lladdr, ETH_ALEN);
+ eth->ether_type = htons(ETHERTYPE_IPV6);
+
+ /* IPv6 Header */
+ ip6h->ip6_vfc = 6 << 4;
+ ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert)
+ + sizeof(struct nd_opt_hdr) + ETH_ALEN);
+ ip6h->ip6_nxt = IPPROTO_ICMPV6;
+ ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT;
+ memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr));
+ /* All nodes multicast address */
+ ip6h->ip6_dst.s6_addr[0] = 0xFF;
+ ip6h->ip6_dst.s6_addr[1] = 0x02;
+ ip6h->ip6_dst.s6_addr[15] = 0x01;
+
+ /* ICMPv6 Header */
+ ndh->nd_na_type = ND_NEIGHBOR_ADVERT;
+ ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
+ ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE;
+ memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr));
+
+ /* NDISC Option header */
+ nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ nd_opt_h->nd_opt_len = 1;
+ memcpy(nd_opt_lladdr, lladdr, ETH_ALEN);
+
+ /* Compute checksum */
+ uint32_t len = sizeof(struct nd_neighbor_advert)
+ + sizeof(struct nd_opt_hdr) + ETH_ALEN;
+ struct ipv6_ph ph = {};
+
+ ph.src = ip6h->ip6_src;
+ ph.dst = ip6h->ip6_dst;
+ ph.ulpl = htonl(len);
+ ph.next_hdr = IPPROTO_ICMPV6;
+
+ /* Suppress static analysis warnings about accessing icmp6 oob */
+ void *offset = icmp6h;
+ icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, offset, len);
+
+ return 0;
+}
+
+int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip)
+{
+ assert(r->family == AF_INET6);
+
+ int ret = 0;
+ struct interface *ifp = r->mvl_ifp;
+ uint8_t buf[VRRP_NDISC_SIZE];
+
+ ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf));
+
+ if (ret == -1)
+ return ret;
+
+ struct sockaddr_ll sll;
+ ssize_t len;
+
+ /* Build the dst device */
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN);
+ sll.sll_halen = ETH_ALEN;
+ sll.sll_ifindex = (int)ifp->ifindex;
+
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ ipaddr2str(ip, ipbuf, sizeof(ipbuf));
+
+ DEBUGD(&vrrp_dbg_ndisc,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Sending unsolicited Neighbor Advertisement on %s for %s",
+ r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
+
+ if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL)
+ && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL))
+ zlog_hexdump(buf, VRRP_NDISC_SIZE);
+
+ len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll,
+ sizeof(sll));
+
+ if (len < 0) {
+ zlog_err(
+ VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+ "Error sending unsolicited Neighbor Advertisement on %s for %s",
+ r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
+ ret = -1;
+ } else {
+ ++r->stats.una_tx_cnt;
+ }
+
+ return ret;
+}
+
+int vrrp_ndisc_una_send_all(struct vrrp_router *r)
+{
+ assert(r->family == AF_INET6);
+
+ struct listnode *ln;
+ struct ipaddr *ip;
+
+ for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
+ vrrp_ndisc_una_send(r, ip);
+
+ return 0;
+}
+
+void vrrp_ndisc_init(void)
+{
+ frr_elevate_privs(&vrrp_privs)
+ {
+ ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
+ }
+
+ if (ndisc_fd > 0) {
+ DEBUGD(&vrrp_dbg_sock,
+ VRRP_LOGPFX "Initialized Neighbor Discovery socket");
+ DEBUGD(&vrrp_dbg_ndisc,
+ VRRP_LOGPFX "Initialized Neighbor Discovery subsystem");
+ } else {
+ zlog_err(VRRP_LOGPFX
+ "Error initializing Neighbor Discovery socket");
+ }
+}
+
+void vrrp_ndisc_fini(void)
+{
+ close(ndisc_fd);
+ ndisc_fd = -1;
+
+ DEBUGD(&vrrp_dbg_ndisc,
+ VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem");
+}
+
+bool vrrp_ndisc_is_init(void)
+{
+ return ndisc_fd > 0;
+}
diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h
new file mode 100644
index 0000000000..efbef348d0
--- /dev/null
+++ b/vrrpd/vrrp_ndisc.h
@@ -0,0 +1,74 @@
+/*
+ * VRRP Neighbor Discovery.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_NDISC_H__
+#define __VRRP_NDISC_H__
+
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include "vrrp.h"
+
+/*
+ * Initialize VRRP neighbor discovery.
+ */
+extern void vrrp_ndisc_init(void);
+
+/*
+ * Check whether VRRP Neighbor Discovery is initialized.
+ *
+ * Returns:
+ * True if initialized, false otherwise
+ */
+extern bool vrrp_ndisc_is_init(void);
+
+/*
+ * Finish VRRP Neighbor Discovery.
+ */
+extern void vrrp_ndisc_fini(void);
+
+/*
+ * Send VRRP Neighbor Advertisement.
+ *
+ * ifp
+ * Interface to transmit on
+ *
+ * ip
+ * IPv6 address to send Neighbor Advertisement for
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip);
+
+/*
+ * Send VRRP Neighbor Advertisements for all virtual IPs.
+ *
+ * r
+ * Virtual Router to send NA's for
+ *
+ * Returns:
+ * -1 on failure
+ * 0 otherwise
+ */
+extern int vrrp_ndisc_una_send_all(struct vrrp_router *r);
+
+#endif /* __VRRP_NDISC_H__ */
diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c
new file mode 100644
index 0000000000..c3f2afba4c
--- /dev/null
+++ b/vrrpd/vrrp_packet.c
@@ -0,0 +1,321 @@
+/*
+ * VRRP packet crafting.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include "lib/checksum.h"
+#include "lib/ipaddr.h"
+#include "lib/memory.h"
+
+#include "vrrp.h"
+#include "vrrp_debug.h"
+#include "vrrp_memory.h"
+#include "vrrp_packet.h"
+
+/* clang-format off */
+const char *vrrp_packet_names[16] = {
+ [0] = "Unknown",
+ [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
+ [2] = "Unknown",
+ [3] = "Unknown",
+ [4] = "Unknown",
+ [5] = "Unknown",
+ [6] = "Unknown",
+ [7] = "Unknown",
+ [8] = "Unknown",
+ [9] = "Unknown",
+ [10] = "Unknown",
+ [11] = "Unknown",
+ [12] = "Unknown",
+ [13] = "Unknown",
+ [14] = "Unknown",
+ [15] = "Unknown",
+};
+/* clang-format on */
+
+/*
+ * Compute the VRRP checksum.
+ *
+ * Checksum is not set in the packet, just computed.
+ *
+ * pkt
+ * VRRP packet, fully filled out except for checksum field.
+ *
+ * pktsize
+ * sizeof(*pkt)
+ *
+ * src
+ * IP address that pkt will be transmitted from.
+ *
+ * Returns:
+ * VRRP checksum in network byte order.
+ */
+static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
+ struct ipaddr *src)
+{
+ uint16_t chksum;
+ bool v6 = (src->ipa_type == IPADDR_V6);
+
+ uint16_t chksum_pre = pkt->hdr.chksum;
+
+ pkt->hdr.chksum = 0;
+
+ if (v6) {
+ struct ipv6_ph ph = {};
+
+ ph.src = src->ipaddr_v6;
+ inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
+ ph.ulpl = htons(pktsize);
+ ph.next_hdr = 112;
+ chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
+ } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
+ struct ipv4_ph ph = {};
+
+ ph.src = src->ipaddr_v4;
+ inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
+ ph.proto = 112;
+ ph.len = htons(pktsize);
+ chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
+ } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
+ chksum = in_cksum(pkt, pktsize);
+ } else {
+ assert(!"Invalid VRRP protocol version");
+ }
+
+ pkt->hdr.chksum = chksum_pre;
+
+ return chksum;
+}
+
+ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
+ uint8_t version, uint8_t vrid, uint8_t prio,
+ uint16_t max_adver_int, uint8_t numip,
+ struct ipaddr **ips)
+{
+ bool v6 = false;
+ size_t addrsz = 0;
+
+ assert(version >= 2 && version <= 3);
+
+ if (numip > 0) {
+ v6 = IS_IPADDR_V6(ips[0]);
+ addrsz = IPADDRSZ(ips[0]);
+ }
+
+ assert(!(version == 2 && v6));
+
+ size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip);
+
+ *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
+
+ (*pkt)->hdr.vertype |= version << 4;
+ (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
+ (*pkt)->hdr.vrid = vrid;
+ (*pkt)->hdr.priority = prio;
+ (*pkt)->hdr.naddr = numip;
+ if (version == 3)
+ (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
+ else if (version == 2) {
+ (*pkt)->hdr.v2.auth_type = 0;
+ (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1);
+ }
+
+ uint8_t *aptr = (void *)(*pkt)->addrs;
+
+ for (int i = 0; i < numip; i++) {
+ memcpy(aptr, &ips[i]->ip.addr, addrsz);
+ aptr += addrsz;
+ }
+
+ (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
+
+ return pktsize;
+}
+
+size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
+{
+ if (buflen < 1)
+ return 0;
+
+ char tmpbuf[BUFSIZ];
+ size_t rs = 0;
+ struct vrrp_hdr *hdr = &pkt->hdr;
+
+ buf[0] = 0x00;
+ snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4));
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ",
+ (hdr->vertype & 0x0F),
+ vrrp_packet_names[(hdr->vertype & 0x0F)]);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ",
+ ntohs(hdr->v3.adver_int));
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum));
+ rs += strlcat(buf, tmpbuf, buflen);
+
+ return rs;
+}
+
+ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
+ size_t read, struct ipaddr *src,
+ struct vrrp_pkt **pkt, char *errmsg,
+ size_t errmsg_len)
+{
+ /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
+ size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
+ : sizeof(struct in6_addr);
+
+ size_t pktsize;
+ uint8_t *buf = m->msg_iov->iov_base;
+
+#define VRRP_PKT_VCHECK(cond, _f, ...) \
+ do { \
+ if (!(cond)) { \
+ if (errmsg) \
+ snprintf(errmsg, errmsg_len, (_f), \
+ ##__VA_ARGS__); \
+ return -1; \
+ } \
+ } while (0)
+
+ /* IPvX header check */
+
+ if (family == AF_INET) {
+ VRRP_PKT_VCHECK(
+ read >= sizeof(struct ip),
+ "Datagram not large enough to contain IP header");
+
+ struct ip *ip = (struct ip *)buf;
+
+ /* IP total length check */
+ VRRP_PKT_VCHECK(
+ ntohs(ip->ip_len) == read,
+ "IPv4 packet length field does not match # received bytes; %" PRIu16
+ "!= %zu",
+ ntohs(ip->ip_len), read);
+
+ /* TTL check */
+ VRRP_PKT_VCHECK(ip->ip_ttl == 255,
+ "IPv4 TTL is %" PRIu8 "; should be 255",
+ ip->ip_ttl);
+
+ *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
+ pktsize = read - (ip->ip_hl << 2);
+
+ /* IP empty packet check */
+ VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
+
+ /* Extract source address */
+ struct sockaddr_in *sa = m->msg_name;
+
+ src->ipa_type = IPADDR_V4;
+ src->ipaddr_v4 = sa->sin_addr;
+ } else if (family == AF_INET6) {
+ struct cmsghdr *c;
+
+ for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
+ if (c->cmsg_level == IPPROTO_IPV6
+ && c->cmsg_type == IPV6_HOPLIMIT)
+ break;
+ }
+
+ VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
+
+ uint8_t *hoplimit = CMSG_DATA(c);
+
+ VRRP_PKT_VCHECK(*hoplimit == 255,
+ "IPv6 Hop Limit is %" PRIu8 "; should be 255",
+ *hoplimit);
+
+ *pkt = (struct vrrp_pkt *)buf;
+ pktsize = read;
+
+ /* Extract source address */
+ struct sockaddr_in6 *sa = m->msg_name;
+
+ src->ipa_type = IPADDR_V6;
+ memcpy(&src->ipaddr_v6, &sa->sin6_addr,
+ sizeof(struct in6_addr));
+ } else {
+ assert(!"Unknown address family");
+ }
+
+ /* Size check */
+ size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
+ : VRRP_MIN_PKT_SIZE_V6;
+ size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
+ : VRRP_MAX_PKT_SIZE_V6;
+ VRRP_PKT_VCHECK(pktsize >= minsize,
+ "VRRP packet is undersized (%zu < %zu)", pktsize,
+ minsize);
+ VRRP_PKT_VCHECK(pktsize <= maxsize,
+ "VRRP packet is oversized (%zu > %zu)", pktsize,
+ maxsize);
+
+ /* Version check */
+ uint8_t pktver = (*pkt)->hdr.vertype >> 4;
+
+ VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
+
+ /* Checksum check */
+ uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
+
+ VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
+ "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "",
+ (*pkt)->hdr.chksum, chksum);
+
+ /* Type check */
+ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8,
+ (*pkt)->hdr.vertype & 0x0f);
+
+ /* Exact size check */
+ size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr);
+
+ VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s",
+ pktver == 2 ? " or missing auth fields" : "");
+
+ /* auth type check */
+ if (version == 2)
+ VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
+ "Bad authentication type %" PRIu8,
+ (*pkt)->hdr.v2.auth_type);
+
+ /* Addresses check */
+ char vbuf[INET6_ADDRSTRLEN];
+ uint8_t *p = (uint8_t *)(*pkt)->addrs;
+
+ for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
+ VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
+ "Bad IP address, #%" PRIu8, i);
+ p += addrsz;
+ }
+
+ /* Everything checks out */
+ return pktsize;
+}
diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h
new file mode 100644
index 0000000000..475e4780d5
--- /dev/null
+++ b/vrrpd/vrrp_packet.h
@@ -0,0 +1,202 @@
+/*
+ * VRRP packet crafting.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_PACKET_H__
+#define __VRRP_PACKET_H__
+
+#include <zebra.h>
+
+#include "lib/ipaddr.h"
+#include "lib/memory.h"
+#include "lib/prefix.h"
+
+#define VRRP_TYPE_ADVERTISEMENT 1
+
+extern const char *vrrp_packet_names[16];
+
+/*
+ * Shared header for VRRPv2/v3 packets.
+ */
+struct vrrp_hdr {
+ /*
+ * H L H L
+ * 0000 0000
+ * ver type
+ */
+ uint8_t vertype;
+ uint8_t vrid;
+ uint8_t priority;
+ uint8_t naddr;
+ union {
+ struct {
+ uint8_t auth_type;
+ /* advertisement interval (in sec) */
+ uint8_t adver_int;
+ } v2;
+ struct {
+ /*
+ * advertisement interval (in centiseconds)
+ * H L H L
+ * 0000 000000000000
+ * rsvd adver_int
+ */
+ uint16_t adver_int;
+ } v3;
+ };
+ uint16_t chksum;
+} __attribute__((packed));
+
+#define VRRP_HDR_SIZE sizeof(struct vrrp_hdr)
+
+struct vrrp_pkt {
+ struct vrrp_hdr hdr;
+ /*
+ * When used, this is actually an array of one or the other, not an
+ * array of union. If N v4 addresses are stored then
+ * sizeof(addrs) == N * sizeof(struct in_addr).
+ *
+ * Under v2, the last 2 entries in this array are the authentication
+ * data fields. We don't support auth in v2 so these are always just 8
+ * bytes of 0x00.
+ */
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } addrs[];
+} __attribute__((packed));
+
+#define VRRP_PKT_SIZE(_f, _ver, _naddr) \
+ ({ \
+ size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \
+ : sizeof(struct in6_addr); \
+ size_t _auth = 2 * sizeof(uint32_t) * (3 - (_ver)); \
+ sizeof(struct vrrp_hdr) + (_asz * (_naddr)) + _auth; \
+ })
+
+#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 3, 1)
+#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 2, 255)
+#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 1)
+#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 255)
+
+#define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4
+#define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6
+
+/*
+ * Builds a VRRP ADVERTISEMENT packet.
+ *
+ * pkt
+ * Pointer to store pointer to result buffer in
+ *
+ * src
+ * Source address packet will be transmitted from. This is needed to compute
+ * the VRRP checksum. The returned packet must be sent in an IP datagram with
+ * the source address equal to this field, or the checksum will be invalid.
+ *
+ * version
+ * VRRP version; must be 2 or 3
+ *
+ * vrid
+ * Virtual Router Identifier
+ *
+ * prio
+ * Virtual Router Priority
+ *
+ * max_adver_int
+ * time between ADVERTISEMENTs
+ *
+ * v6
+ * whether 'ips' is an array of v4 or v6 addresses
+ *
+ * numip
+ * number of IPvX addresses in 'ips'
+ *
+ * ips
+ * array of pointer to either struct in_addr (v6 = false) or struct in6_addr
+ * (v6 = true)
+ */
+ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
+ uint8_t version, uint8_t vrid, uint8_t prio,
+ uint16_t max_adver_int, uint8_t numip,
+ struct ipaddr **ips);
+
+/*
+ * Dumps a VRRP ADVERTISEMENT packet to a string.
+ *
+ * Currently only dumps the header.
+ *
+ * buf
+ * Buffer to store string representation
+ *
+ * buflen
+ * Size of buf
+ *
+ * pkt
+ * Packet to dump to a string
+ *
+ * Returns:
+ * # bytes written to buf
+ */
+size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt);
+
+
+/*
+ * Parses a VRRP packet, checking for illegal or invalid data.
+ *
+ * This function parses both VRRPv2 and VRRPv3 packets. Which version is
+ * expected is determined by the version argument. For example, if version is 3
+ * and the received packet has version field 2 it will fail to parse.
+ *
+ * Note that this function only checks whether the packet itself is a valid
+ * VRRP packet. It is up to the caller to validate whether the VRID is correct,
+ * priority and timer values are correct, etc.
+ *
+ * family
+ * Address family of received packet
+ *
+ * version
+ * VRRP version to use for validation
+ *
+ * m
+ * msghdr containing results of recvmsg() on VRRP router socket
+ *
+ * read
+ * Return value of recvmsg() on VRRP router socket; must be non-negative
+ *
+ * src
+ * Pointer to struct ipaddr to store address of datagram sender
+ *
+ * pkt
+ * Pointer to pointer to set to location of VRRP packet within buf
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error; may be
+ * NULL, in which case no message will be stored
+ *
+ * errmsg_len
+ * Size of errmsg
+ *
+ * Returns:
+ * Size of VRRP packet, or -1 upon error
+ */
+ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
+ size_t read, struct ipaddr *src,
+ struct vrrp_pkt **pkt, char *errmsg,
+ size_t errmsg_len);
+
+#endif /* __VRRP_PACKET_H__ */
diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c
new file mode 100644
index 0000000000..48d81b0258
--- /dev/null
+++ b/vrrpd/vrrp_vty.c
@@ -0,0 +1,751 @@
+/*
+ * VRRP CLI commands.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/if.h"
+#include "lib/ipaddr.h"
+#include "lib/json.h"
+#include "lib/prefix.h"
+#include "lib/termtable.h"
+#include "lib/vty.h"
+
+#include "vrrp.h"
+#include "vrrp_debug.h"
+#include "vrrp_memory.h"
+#include "vrrp_vty.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "vrrpd/vrrp_vty_clippy.c"
+#endif
+
+
+#define VRRP_STR "Virtual Router Redundancy Protocol\n"
+#define VRRP_VRID_STR "Virtual Router ID\n"
+#define VRRP_PRIORITY_STR "Virtual Router Priority\n"
+#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n"
+#define VRRP_IP_STR "Virtual Router IPv4 address\n"
+#define VRRP_VERSION_STR "VRRP protocol version\n"
+
+#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \
+ do { \
+ _vr = vrrp_lookup(_ifp, _vrid); \
+ if (!_vr) { \
+ vty_out(_vty, \
+ "%% Please configure VRRP instance %u\n", \
+ (unsigned int)_vrid); \
+ return CMD_WARNING_CONFIG_FAILED; \
+ } \
+ } while (0)
+
+/* clang-format off */
+
+DEFPY(vrrp_vrid,
+ vrrp_vrid_cmd,
+ "[no] vrrp (1-255)$vrid [version (2-3)]",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ VRRP_VERSION_STR
+ VRRP_VERSION_STR)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
+
+ if (version == 0)
+ version = 3;
+
+ if (no && vr)
+ vrrp_vrouter_destroy(vr);
+ else if (no && !vr)
+ vty_out(vty, "%% VRRP instance %ld does not exist on %s\n",
+ vrid, ifp->name);
+ else if (!vr)
+ vrrp_vrouter_create(ifp, vrid, version);
+ else if (vr)
+ vty_out(vty, "%% VRRP instance %ld already exists on %s\n",
+ vrid, ifp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_shutdown,
+ vrrp_shutdown_cmd,
+ "[no] vrrp (1-255)$vrid shutdown",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ "Force VRRP router into administrative shutdown\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+
+ if (!no) {
+ if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE)
+ vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+ if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE)
+ vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+ vr->shutdown = true;
+ } else {
+ vr->shutdown = false;
+ vrrp_check_start(vr);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_priority,
+ vrrp_priority_cmd,
+ "[no] vrrp (1-255)$vrid priority (1-254)",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ VRRP_PRIORITY_STR
+ "Priority value")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+ uint8_t newprio = no ? vd.priority : priority;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+
+ vrrp_set_priority(vr, newprio);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_advertisement_interval,
+ vrrp_advertisement_interval_cmd,
+ "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)",
+ NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
+ "Advertisement interval in milliseconds; must be multiple of 10")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+ uint16_t newadvint =
+ no ? vd.advertisement_interval * 10 : advertisement_interval;
+
+ if (newadvint % 10 != 0) {
+ vty_out(vty, "%% Value must be a multiple of 10\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* all internal computations are in centiseconds */
+ newadvint /= CS2MS;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+ vrrp_set_advertisement_interval(vr, newadvint);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_ip,
+ vrrp_ip_cmd,
+ "[no] vrrp (1-255)$vrid ip A.B.C.D",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ "Add IPv4 address\n"
+ VRRP_IP_STR)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+ bool deactivated = false;
+ bool activated = false;
+ bool failed = false;
+ int ret = CMD_SUCCESS;
+ int oldstate;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+
+ bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE);
+
+ if (no) {
+ oldstate = vr->v4->fsm.state;
+ failed = vrrp_del_ipv4(vr, ip);
+ vrrp_check_start(vr);
+ deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE
+ && oldstate != VRRP_STATE_INITIALIZE);
+ } else {
+ oldstate = vr->v4->fsm.state;
+ failed = vrrp_add_ipv4(vr, ip);
+ vrrp_check_start(vr);
+ activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE
+ && oldstate == VRRP_STATE_INITIALIZE);
+ }
+
+ if (activated)
+ vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid);
+ if (deactivated)
+ vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid);
+ if (failed) {
+ vty_out(vty, "%% Failed to %s virtual IP\n",
+ no ? "remove" : "add");
+ ret = CMD_WARNING_CONFIG_FAILED;
+ if (will_activate && !activated) {
+ vty_out(vty,
+ "%% Failed to activate IPv4 Virtual Router %ld\n",
+ vrid);
+ }
+ }
+
+ return ret;
+}
+
+DEFPY(vrrp_ip6,
+ vrrp_ip6_cmd,
+ "[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ "Add IPv6 address\n"
+ VRRP_IP_STR)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+ bool deactivated = false;
+ bool activated = false;
+ bool failed = false;
+ int ret = CMD_SUCCESS;
+ int oldstate;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+
+ if (vr->version != 3) {
+ vty_out(vty,
+ "%% Cannot add IPv6 address to VRRPv2 virtual router\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE);
+
+ if (no) {
+ oldstate = vr->v6->fsm.state;
+ failed = vrrp_del_ipv6(vr, ipv6);
+ vrrp_check_start(vr);
+ deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE
+ && oldstate != VRRP_STATE_INITIALIZE);
+ } else {
+ oldstate = vr->v6->fsm.state;
+ failed = vrrp_add_ipv6(vr, ipv6);
+ vrrp_check_start(vr);
+ activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE
+ && oldstate == VRRP_STATE_INITIALIZE);
+ }
+
+ if (activated)
+ vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid);
+ if (deactivated)
+ vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid);
+ if (failed) {
+ vty_out(vty, "%% Failed to %s virtual IP\n",
+ no ? "remove" : "add");
+ ret = CMD_WARNING_CONFIG_FAILED;
+ if (will_activate && !activated) {
+ vty_out(vty,
+ "%% Failed to activate IPv6 Virtual Router %ld\n",
+ vrid);
+ }
+ }
+
+ return ret;
+}
+
+DEFPY(vrrp_preempt,
+ vrrp_preempt_cmd,
+ "[no] vrrp (1-255)$vrid preempt",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ "Preempt mode\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ struct vrrp_vrouter *vr;
+
+ VROUTER_GET_VTY(vty, ifp, vrid, vr);
+
+ vr->preempt_mode = !no;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_autoconfigure,
+ vrrp_autoconfigure_cmd,
+ "[no] vrrp autoconfigure [version (2-3)]",
+ NO_STR
+ VRRP_STR
+ "Automatically set up VRRP instances on VRRP-compatible interfaces\n"
+ "Version for automatically configured instances\n"
+ VRRP_VERSION_STR)
+{
+ version = version ? version : 3;
+
+ if (!no)
+ vrrp_autoconfig_on(version);
+ else
+ vrrp_autoconfig_off();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_default,
+ vrrp_default_cmd,
+ "[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
+ NO_STR
+ VRRP_STR
+ "Configure defaults for new VRRP instances\n"
+ VRRP_ADVINT_STR
+ "Advertisement interval in milliseconds\n"
+ "Preempt mode\n"
+ VRRP_PRIORITY_STR
+ "Priority value\n"
+ "Force VRRP router into administrative shutdown\n")
+{
+ if (adv) {
+ if (advint % 10 != 0) {
+ vty_out(vty, "%% Value must be a multiple of 10\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* all internal computations are in centiseconds */
+ advint /= CS2MS;
+ vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint;
+ }
+ if (p)
+ vd.preempt_mode = !no;
+ if (prio)
+ vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval;
+ if (s)
+ vd.shutdown = !no;
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format on */
+
+/*
+ * Build JSON representation of VRRP instance.
+ *
+ * vr
+ * VRRP router to build json object from
+ *
+ * Returns:
+ * JSON representation of VRRP instance. Must be freed by caller.
+ */
+static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr)
+{
+ char ethstr4[ETHER_ADDR_STRLEN];
+ char ethstr6[ETHER_ADDR_STRLEN];
+ char ipstr[INET6_ADDRSTRLEN];
+ const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
+ const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
+ char sipstr4[INET6_ADDRSTRLEN] = {};
+ char sipstr6[INET6_ADDRSTRLEN] = {};
+ struct listnode *ln;
+ struct ipaddr *ip;
+ struct json_object *j = json_object_new_object();
+ struct json_object *v4 = json_object_new_object();
+ struct json_object *v4_stats = json_object_new_object();
+ struct json_object *v4_addrs = json_object_new_array();
+ struct json_object *v6 = json_object_new_object();
+ struct json_object *v6_stats = json_object_new_object();
+ struct json_object *v6_addrs = json_object_new_array();
+
+ prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
+ prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
+
+ json_object_int_add(j, "vrid", vr->vrid);
+ json_object_int_add(j, "version", vr->version);
+ json_object_boolean_add(j, "autoconfigured", vr->autoconf);
+ json_object_boolean_add(j, "shutdown", vr->shutdown);
+ json_object_boolean_add(j, "preemptMode", vr->preempt_mode);
+ json_object_boolean_add(j, "acceptMode", vr->accept_mode);
+ json_object_string_add(j, "interface", vr->ifp->name);
+ json_object_int_add(j, "advertisementInterval",
+ vr->advertisement_interval * CS2MS);
+ /* v4 */
+ json_object_string_add(v4, "interface",
+ vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "");
+ json_object_string_add(v4, "vmac", ethstr4);
+ ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
+ json_object_string_add(v4, "primaryAddress", sipstr4);
+ json_object_string_add(v4, "status", stastr4);
+ json_object_int_add(v4, "effectivePriority", vr->v4->priority);
+ json_object_int_add(v4, "masterAdverInterval",
+ vr->v4->master_adver_interval * CS2MS);
+ json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS);
+ json_object_int_add(v4, "masterDownInterval",
+ vr->v4->master_down_interval * CS2MS);
+ /* v4 stats */
+ json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt);
+ json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt);
+ json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt);
+ json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt);
+ json_object_object_add(v4, "stats", v4_stats);
+ /* v4 addrs */
+ if (vr->v4->addrs->count) {
+ for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
+ inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
+ sizeof(ipstr));
+ json_object_array_add(v4_addrs,
+ json_object_new_string(ipstr));
+ }
+ }
+ json_object_object_add(v4, "addresses", v4_addrs);
+ json_object_object_add(j, "v4", v4);
+
+ /* v6 */
+ json_object_string_add(v6, "interface",
+ vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "");
+ json_object_string_add(v6, "vmac", ethstr6);
+ ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
+ if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
+ strlcat(sipstr6, "::", sizeof(sipstr6));
+ json_object_string_add(v6, "primaryAddress", sipstr6);
+ json_object_string_add(v6, "status", stastr6);
+ json_object_int_add(v6, "effectivePriority", vr->v6->priority);
+ json_object_int_add(v6, "masterAdverInterval",
+ vr->v6->master_adver_interval * CS2MS);
+ json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS);
+ json_object_int_add(v6, "masterDownInterval",
+ vr->v6->master_down_interval * CS2MS);
+ /* v6 stats */
+ json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt);
+ json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt);
+ json_object_int_add(v6_stats, "neighborAdverTx",
+ vr->v6->stats.una_tx_cnt);
+ json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt);
+ json_object_object_add(v6, "stats", v6_stats);
+ /* v6 addrs */
+ if (vr->v6->addrs->count) {
+ for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
+ inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
+ sizeof(ipstr));
+ json_object_array_add(v6_addrs,
+ json_object_new_string(ipstr));
+ }
+ }
+ json_object_object_add(v6, "addresses", v6_addrs);
+ json_object_object_add(j, "v6", v6);
+
+ return j;
+}
+
+/*
+ * Dump VRRP instance status to VTY.
+ *
+ * vty
+ * vty to dump to
+ *
+ * vr
+ * VRRP router to dump
+ */
+static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
+{
+ char ethstr4[ETHER_ADDR_STRLEN];
+ char ethstr6[ETHER_ADDR_STRLEN];
+ char ipstr[INET6_ADDRSTRLEN];
+ const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
+ const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
+ char sipstr4[INET6_ADDRSTRLEN] = {};
+ char sipstr6[INET6_ADDRSTRLEN] = {};
+ struct listnode *ln;
+ struct ipaddr *ip;
+
+ struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+
+ ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid);
+ ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version);
+ ttable_add_row(tt, "%s|%s", "Autoconfigured",
+ vr->autoconf ? "Yes" : "No");
+ ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No");
+ ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name);
+ prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
+ prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
+ ttable_add_row(tt, "%s|%s", "VRRP interface (v4)",
+ vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None");
+ ttable_add_row(tt, "%s|%s", "VRRP interface (v6)",
+ vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None");
+ ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
+ ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
+ if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
+ strlcat(sipstr6, "::", sizeof(sipstr6));
+ ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4);
+ ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6);
+ ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4);
+ ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6);
+ ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4);
+ ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6);
+ ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority);
+ ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)",
+ vr->v4->priority);
+ ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)",
+ vr->v6->priority);
+ ttable_add_row(tt, "%s|%s", "Preempt Mode",
+ vr->preempt_mode ? "Yes" : "No");
+ ttable_add_row(tt, "%s|%s", "Accept Mode",
+ vr->accept_mode ? "Yes" : "No");
+ ttable_add_row(tt, "%s|%d ms", "Advertisement Interval",
+ vr->advertisement_interval * CS2MS);
+ ttable_add_row(tt, "%s|%d ms",
+ "Master Advertisement Interval (v4)",
+ vr->v4->master_adver_interval * CS2MS);
+ ttable_add_row(tt, "%s|%d ms",
+ "Master Advertisement Interval (v6)",
+ vr->v6->master_adver_interval * CS2MS);
+ ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)",
+ vr->v4->stats.adver_tx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)",
+ vr->v6->stats.adver_tx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)",
+ vr->v4->stats.adver_rx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)",
+ vr->v6->stats.adver_rx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)",
+ vr->v4->stats.garp_tx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)",
+ vr->v6->stats.una_tx_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)",
+ vr->v4->stats.trans_cnt);
+ ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)",
+ vr->v6->stats.trans_cnt);
+ ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)",
+ vr->v4->skew_time * CS2MS);
+ ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)",
+ vr->v6->skew_time * CS2MS);
+ ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)",
+ vr->v4->master_down_interval * CS2MS);
+ ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)",
+ vr->v6->master_down_interval * CS2MS);
+ ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count);
+
+ char fill[35];
+
+ memset(fill, '.', sizeof(fill));
+ fill[sizeof(fill) - 1] = 0x00;
+ if (vr->v4->addrs->count) {
+ for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
+ inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
+ sizeof(ipstr));
+ ttable_add_row(tt, "%s|%s", fill, ipstr);
+ }
+ }
+
+ ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count);
+
+ if (vr->v6->addrs->count) {
+ for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
+ inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
+ sizeof(ipstr));
+ ttable_add_row(tt, "%s|%s", fill, ipstr);
+ }
+ }
+
+ char *table = ttable_dump(tt, "\n");
+
+ vty_out(vty, "\n%s\n", table);
+ XFREE(MTYPE_TMP, table);
+ ttable_del(tt);
+}
+
+/*
+ * Sort comparator, used when sorting VRRP instances for display purposes.
+ *
+ * Sorts by interface name first, then by VRID ascending.
+ */
+static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2)
+{
+ const struct vrrp_vrouter *vr1 = *d1;
+ const struct vrrp_vrouter *vr2 = *d2;
+ int result;
+
+ result = strcmp(vr1->ifp->name, vr2->ifp->name);
+ result += !result * (vr1->vrid - vr2->vrid);
+
+ return result;
+}
+
+/* clang-format off */
+
+DEFPY(vrrp_vrid_show,
+ vrrp_vrid_show_cmd,
+ "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]",
+ SHOW_STR
+ VRRP_STR
+ INTERFACE_STR
+ "Only show VRRP instances on this interface\n"
+ VRRP_VRID_STR
+ JSON_STR)
+{
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *ll = hash_to_list(vrrp_vrouters_hash);
+ struct json_object *j = json_object_new_array();
+
+ list_sort(ll, vrrp_instance_display_sort_cmp);
+
+ for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
+ if (ifn && !strmatch(ifn, vr->ifp->name))
+ continue;
+ if (vrid && ((uint8_t) vrid) != vr->vrid)
+ continue;
+
+ if (!json)
+ vrrp_show(vty, vr);
+ else
+ json_object_array_add(j, vrrp_build_json(vr));
+ }
+
+ if (json)
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ j, JSON_C_TO_STRING_PRETTY));
+
+ json_object_free(j);
+
+ list_delete(&ll);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrrp_vrid_show_summary,
+ vrrp_vrid_show_summary_cmd,
+ "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary",
+ SHOW_STR
+ VRRP_STR
+ INTERFACE_STR
+ "Only show VRRP instances on this interface\n"
+ VRRP_VRID_STR
+ "Summarize all VRRP instances\n")
+{
+ struct vrrp_vrouter *vr;
+ struct listnode *ln;
+ struct list *ll = hash_to_list(vrrp_vrouters_hash);
+
+ list_sort(ll, vrrp_instance_display_sort_cmp);
+
+ struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+
+ ttable_add_row(
+ tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)");
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
+ if (ifn && !strmatch(ifn, vr->ifp->name))
+ continue;
+ if (vrid && ((uint8_t)vrid) != vr->vrid)
+ continue;
+
+ ttable_add_row(
+ tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s",
+ vr->ifp->name, vr->vrid, vr->priority,
+ vr->v4->addrs->count, vr->v6->addrs->count,
+ vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master"
+ : "Backup",
+ vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master"
+ : "Backup");
+ }
+
+ char *table = ttable_dump(tt, "\n");
+
+ vty_out(vty, "\n%s\n", table);
+ XFREE(MTYPE_TMP, table);
+ ttable_del(tt);
+
+ list_delete(&ll);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFPY(debug_vrrp,
+ debug_vrrp_cmd,
+ "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]",
+ NO_STR
+ DEBUG_STR
+ VRRP_STR
+ "Debug protocol state\n"
+ "Debug autoconfiguration\n"
+ "Debug sent and received packets\n"
+ "Debug socket creation and configuration\n"
+ "Debug Neighbor Discovery\n"
+ "Debug ARP\n"
+ "Debug Zebra events\n")
+{
+ /* If no specific are given on/off them all */
+ if (strmatch(argv[argc - 1]->text, "vrrp"))
+ vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true,
+ true, true, true);
+ else
+ vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt,
+ !!sock, !!ndisc, !!arp, !!zebra);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_vrrp,
+ show_debugging_vrrp_cmd,
+ "show debugging [vrrp]",
+ SHOW_STR
+ DEBUG_STR
+ "VRRP information\n")
+{
+ vty_out(vty, "VRRP debugging status:\n");
+
+ vrrp_debug_status_write(vty);
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format on */
+
+static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1};
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+static struct cmd_node vrrp_node = {VRRP_NODE, "", 1};
+
+void vrrp_vty_init(void)
+{
+ install_node(&debug_node, vrrp_config_write_debug);
+ install_node(&interface_node, vrrp_config_write_interface);
+ install_node(&vrrp_node, vrrp_config_write_global);
+ if_cmd_init();
+
+ install_element(VIEW_NODE, &vrrp_vrid_show_cmd);
+ install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd);
+ install_element(VIEW_NODE, &show_debugging_vrrp_cmd);
+ install_element(VIEW_NODE, &debug_vrrp_cmd);
+ install_element(CONFIG_NODE, &debug_vrrp_cmd);
+ install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd);
+ install_element(CONFIG_NODE, &vrrp_default_cmd);
+ install_element(INTERFACE_NODE, &vrrp_vrid_cmd);
+ install_element(INTERFACE_NODE, &vrrp_shutdown_cmd);
+ install_element(INTERFACE_NODE, &vrrp_priority_cmd);
+ install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
+ install_element(INTERFACE_NODE, &vrrp_ip_cmd);
+ install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
+ install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
+}
diff --git a/vrrpd/vrrp_vty.h b/vrrpd/vrrp_vty.h
new file mode 100644
index 0000000000..377321ec4a
--- /dev/null
+++ b/vrrpd/vrrp_vty.h
@@ -0,0 +1,25 @@
+/*
+ * VRRP CLI commands.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_VTY_H__
+#define __VRRP_VTY_H__
+
+void vrrp_vty_init(void);
+
+#endif /* __VRRP_VTY_H__ */
diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c
new file mode 100644
index 0000000000..7503034de3
--- /dev/null
+++ b/vrrpd/vrrp_zebra.c
@@ -0,0 +1,252 @@
+/*
+ * VRRP Zebra interfacing.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "lib/if.h"
+#include "lib/linklist.h"
+#include "lib/log.h"
+#include "lib/prefix.h"
+#include "lib/vty.h"
+#include "lib/zclient.h"
+
+#include "vrrp.h"
+#include "vrrp_debug.h"
+#include "vrrp_zebra.h"
+
+#define VRRP_LOGPFX "[ZEBRA] "
+
+static struct zclient *zclient;
+
+static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id,
+ const char *func)
+{
+ DEBUGD(&vrrp_dbg_zebra,
+ "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
+ func, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags,
+ ifp->metric, ifp->mtu, if_is_operative(ifp));
+}
+
+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)) {
+ struct prefix *p = ifc->address;
+
+ DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func,
+ ifp->name, inet_ntoa(p->u.prefix4),
+ CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary"
+ : "primary");
+ }
+}
+
+
+static void vrrp_zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+}
+
+/* Router-id update message from zebra. */
+static int vrrp_router_id_update_zebra(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct prefix router_id;
+
+ zebra_router_id_update_read(zclient->ibuf, &router_id);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_add(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ /*
+ * zebra api adds/dels interfaces using the same call
+ * interface_add_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
+
+ if (!ifp)
+ return 0;
+
+ vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
+
+ vrrp_if_add(ifp);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_del(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+
+ if (!ifp)
+ return 0;
+
+ vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
+
+ vrrp_if_del(ifp);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_state_up(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ /*
+ * zebra api notifies interface up/down events by using the same call
+ * zebra_interface_state_read below, see comments in lib/zclient.c ifp =
+ * zebra_interface_state_read(zclient->ibuf, vrf_id);
+ */
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+
+ if (!ifp)
+ return 0;
+
+ vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
+
+ vrrp_if_up(ifp);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_state_down(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ /*
+ * zebra api notifies interface up/down events by using the same call
+ * zebra_interface_state_read below, see comments in lib/zclient.c
+ */
+ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+
+ if (!ifp)
+ return 0;
+
+ vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
+
+ vrrp_if_down(ifp);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_address_add(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct connected *c;
+
+ /*
+ * zebra api notifies address adds/dels events by using the same call
+ * interface_add_read below, see comments in lib/zclient.c
+ *
+ * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
+ * will add address to interface list by calling
+ * connected_add_by_prefix()
+ */
+ c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__);
+ vrrp_zebra_debug_if_dump_address(c->ifp, __func__);
+
+ vrrp_if_address_add(c->ifp);
+
+ return 0;
+}
+
+static int vrrp_zebra_if_address_del(int command, struct zclient *client,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct connected *c;
+
+ /*
+ * zebra api notifies address adds/dels events by using the same call
+ * interface_add_read below, see comments in lib/zclient.c
+ *
+ * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
+ * will remove address from interface list by calling
+ * connected_delete_by_prefix()
+ */
+ c = zebra_interface_address_read(command, client->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__);
+ vrrp_zebra_debug_if_dump_address(c->ifp, __func__);
+
+ vrrp_if_address_del(c->ifp);
+
+ return 0;
+}
+
+void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable)
+{
+ DEBUGD(&vrrp_dbg_zebra,
+ VRRP_LOGPFX VRRP_LOGPFX_VRID
+ "Requesting Zebra to turn router advertisements %s for %s",
+ r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name);
+
+ zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp,
+ enable, VRRP_RADV_INT);
+}
+
+int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down)
+{
+ DEBUGD(&vrrp_dbg_zebra,
+ VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name,
+ down ? "on" : "off");
+
+ return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp,
+ down);
+}
+
+void vrrp_zebra_init(void)
+{
+ /* Socket for receiving updates from Zebra daemon */
+ zclient = zclient_new(master, &zclient_options_default);
+
+ zclient->zebra_connected = vrrp_zebra_connected;
+ zclient->router_id_update = vrrp_router_id_update_zebra;
+ zclient->interface_add = vrrp_zebra_if_add;
+ zclient->interface_delete = vrrp_zebra_if_del;
+ zclient->interface_up = vrrp_zebra_if_state_up;
+ zclient->interface_down = vrrp_zebra_if_state_down;
+ zclient->interface_address_add = vrrp_zebra_if_address_add;
+ zclient->interface_address_delete = vrrp_zebra_if_address_del;
+
+ zclient_init(zclient, ZEBRA_ROUTE_VRRP, 0, &vrrp_privs);
+
+ zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__);
+}
diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h
new file mode 100644
index 0000000000..84bcba23c1
--- /dev/null
+++ b/vrrpd/vrrp_zebra.h
@@ -0,0 +1,32 @@
+/*
+ * VRRP Zebra interfacing.
+ * Copyright (C) 2018-2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __VRRP_ZEBRA_H__
+#define __VRRP_ZEBRA_H__
+
+#include <zebra.h>
+
+#include "lib/if.h"
+
+extern void vrrp_zebra_init(void);
+extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable);
+extern int vrrp_zclient_send_interface_protodown(struct interface *ifp,
+ bool down);
+
+#endif /* __VRRP_ZEBRA_H__ */
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index 332fd248ca..4dc34d10ef 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -40,7 +40,7 @@ sub scan_file {
$cppadd = $fabricd ? "-DFABRICD=1" : "";
- open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $cppadd $file |");
+ open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |");
local $/; undef $/;
$line = <FH>;
close (FH);
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index b8da90ca8e..a0b119c3eb 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -137,6 +137,7 @@ struct vtysh_client vtysh_client[] = {
{.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL},
{.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL},
{.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL},
+ {.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL},
};
enum vtysh_write_integrated vtysh_write_integrated =
@@ -502,7 +503,7 @@ static int vtysh_execute_func(const char *line, int pager)
vtysh_execute("exit");
} else if (tried) {
vtysh_execute("end");
- vtysh_execute("configure terminal");
+ vtysh_execute("configure");
}
}
/*
@@ -540,7 +541,7 @@ static int vtysh_execute_func(const char *line, int pager)
if (pager && strncmp(line, "exit", 4))
vty_open_pager(vty);
- if (!strcmp(cmd->string, "configure terminal")) {
+ if (!strcmp(cmd->string, "configure")) {
for (i = 0; i < array_size(vtysh_client); i++) {
cmd_stat = vtysh_client_execute(
&vtysh_client[i], line);
@@ -674,7 +675,7 @@ int vtysh_mark_file(const char *filename)
vty->node = CONFIG_NODE;
vtysh_execute_no_pager("enable");
- vtysh_execute_no_pager("configure terminal");
+ vtysh_execute_no_pager("configure");
vty_buf_copy = XCALLOC(MTYPE_VTYSH_CMD, VTY_BUFSIZ);
while (fgets(vty->buf, VTY_BUFSIZ, confp)) {
@@ -1744,7 +1745,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_disable, vtysh_disable_cmd, "disable",
}
DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd,
- "configure terminal",
+ "configure [terminal]",
"Configuration from vty interface\n"
"Configuration terminal\n")
{
@@ -1786,7 +1787,7 @@ static int vtysh_exit(struct vty *vty)
case BFD_NODE:
case RPKI_NODE:
vtysh_execute("end");
- vtysh_execute("configure terminal");
+ vtysh_execute("configure");
vty->node = CONFIG_NODE;
break;
case BGP_VPNV4_NODE:
@@ -2172,7 +2173,7 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit",
return vtysh_exit_vrf(self, vty, argc, argv);
}
-DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
+DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
"exit", "Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
@@ -3346,7 +3347,7 @@ static void vtysh_update_all_instances(struct vtysh_client *head_client)
dir = opendir(vtydir);
if (dir) {
while ((file = readdir(dir)) != NULL) {
- if (begins_with(file->d_name, "ospfd-")
+ if (frrstr_startswith(file->d_name, "ospfd-")
&& ends_with(file->d_name, ".vty")) {
if (n == MAXIMUM_INSTANCES) {
fprintf(stderr,
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index eb69a20b83..3b0b570a56 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -42,6 +42,7 @@ DECLARE_MGROUP(MVTYSH)
#define VTYSH_STATICD 0x08000
#define VTYSH_BFDD 0x10000
#define VTYSH_FABRICD 0x20000
+#define VTYSH_VRRPD 0x40000
#define VTYSH_WAS_ACTIVE (-2)
@@ -50,9 +51,9 @@ DECLARE_MGROUP(MVTYSH)
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
-#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD
+#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD
-#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD
+#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD
#define VTYSH_NS VTYSH_ZEBRA
#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD
#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 7ca3ed9c5e..cf94ab643a 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -257,6 +257,10 @@ void vtysh_config_parse_line(void *arg, const char *line)
strlen(" exit-vrf"))
== 0) {
config_add_line_uniq_end(config->line, line);
+ } else if (!strncmp(line, " vrrp", strlen(" vrrp"))
+ || !strncmp(line, " no vrrp",
+ strlen(" no vrrp"))) {
+ config_add_line(config->line, line);
} else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE
|| config->index == LOGICALROUTER_NODE
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index d0d11c8676..7b132cb61e 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -707,16 +707,16 @@ module frr-isisd {
description
"Log changes to the IS-IS adjacencies in this area.";
}
- }
- container mpls-te {
- presence "Present if MPLS-TE is enabled.";
- description
- "Enable MPLS-TE functionality.";
- leaf router-address {
- type inet:ipv4-address;
+ container mpls-te {
+ presence "Present if MPLS-TE is enabled.";
description
- "Stable IP address of the advertising router.";
+ "Enable MPLS-TE functionality.";
+ leaf router-address {
+ type inet:ipv4-address;
+ description
+ "Stable IP address of the advertising router.";
+ }
}
}
}
diff --git a/yang/libyang_plugins/subdir.am b/yang/libyang_plugins/subdir.am
index fe5f34a28a..837908a1b3 100644
--- a/yang/libyang_plugins/subdir.am
+++ b/yang/libyang_plugins/subdir.am
@@ -2,13 +2,6 @@
# libyang user types
#
-if LIBYANG_EXT_BUILTIN
-lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c
-else
-libyang_plugins_LTLIBRARIES += yang/libyang_plugins/frr_user_types.la
-endif
-
-yang_libyang_plugins_frr_user_types_la_CFLAGS = $(WERROR) $(LIBYANG_CFLAGS)
-yang_libyang_plugins_frr_user_types_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
-yang_libyang_plugins_frr_user_types_la_LIBADD =
-yang_libyang_plugins_frr_user_types_la_SOURCES = yang/libyang_plugins/frr_user_types.c
+# XXX: disable support for libyang custom user types temporarily to facilitate
+# the transition from libyang 0.x to libyang 1.x.
+#lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c
diff --git a/zebra/connected.c b/zebra/connected.c
index 7114a3286b..bba221c2cf 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -209,8 +209,16 @@ void connected_up(struct interface *ifp, struct connected *ifc)
.ifindex = ifp->ifindex,
.vrf_id = ifp->vrf_id,
};
+ struct zebra_vrf *zvrf;
uint32_t metric;
+ zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ if (!zvrf) {
+ flog_err(EC_ZEBRA_VRF_NOT_FOUND,
+ "%s: Received Up for interface but no associated zvrf: %d",
+ __PRETTY_FUNCTION__, ifp->vrf_id);
+ return;
+ }
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
return;
@@ -246,11 +254,11 @@ void connected_up(struct interface *ifp, struct connected *ifc)
metric = (ifc->metric < (uint32_t)METRIC_MAX) ?
ifc->metric : ifp->metric;
- rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p,
- NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0);
+ rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
+ 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0);
- rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p,
- NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0);
+ rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
+ 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
@@ -260,19 +268,19 @@ void connected_up(struct interface *ifp, struct connected *ifc)
ifp->vrf_id, ifp->name,
prefix2str(&p, buf, sizeof(buf)));
}
- rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+ rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE);
/* Schedule LSP forwarding entries for processing, if appropriate. */
- if (ifp->vrf_id == VRF_DEFAULT) {
+ if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
if (IS_ZEBRA_DEBUG_MPLS) {
char buf[PREFIX_STRLEN];
zlog_debug(
"%u: IF %s IP %s address add/up, scheduling MPLS processing",
- ifp->vrf_id, ifp->name,
+ zvrf->vrf->vrf_id, ifp->name,
prefix2str(&p, buf, sizeof(buf)));
}
- mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p);
+ mpls_mark_lsps_for_processing(zvrf, &p);
}
}
@@ -377,6 +385,15 @@ void connected_down(struct interface *ifp, struct connected *ifc)
.ifindex = ifp->ifindex,
.vrf_id = ifp->vrf_id,
};
+ struct zebra_vrf *zvrf;
+
+ zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ if (!zvrf) {
+ flog_err(EC_ZEBRA_VRF_NOT_FOUND,
+ "%s: Received Up for interface but no associated zvrf: %d",
+ __PRETTY_FUNCTION__, ifp->vrf_id);
+ return;
+ }
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
return;
@@ -410,34 +427,34 @@ 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, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, 0, false);
+ rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
+ 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false);
- rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, 0, false);
+ rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT,
+ 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
zlog_debug(
"%u: IF %s IP %s address down, scheduling RIB processing",
- ifp->vrf_id, ifp->name,
+ zvrf->vrf->vrf_id, ifp->name,
prefix2str(&p, buf, sizeof(buf)));
}
- rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+ rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE);
/* Schedule LSP forwarding entries for processing, if appropriate. */
- if (ifp->vrf_id == VRF_DEFAULT) {
+ if (zvrf->vrf->vrf_id == VRF_DEFAULT) {
if (IS_ZEBRA_DEBUG_MPLS) {
char buf[PREFIX_STRLEN];
zlog_debug(
"%u: IF %s IP %s address down, scheduling MPLS processing",
- ifp->vrf_id, ifp->name,
+ zvrf->vrf->vrf_id, ifp->name,
prefix2str(&p, buf, sizeof(buf)));
}
- mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p);
+ mpls_mark_lsps_for_processing(zvrf, &p);
}
}
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index ba518ea576..df8d4bfe15 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -690,9 +690,6 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ifp->speed = get_iflink_speed(ifp);
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
- if (desc)
- ifp->desc = XSTRDUP(MTYPE_TMP, desc);
-
/* Set zebra interface type */
zebra_if_set_ziftype(ifp, zif_type, zif_slave_type);
if (IS_ZEBRA_IF_VRF(ifp))
@@ -707,6 +704,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
zif = (struct zebra_if *)ifp->info;
zif->link_ifindex = link_ifindex;
+ if (desc) {
+ XFREE(MTYPE_TMP, zif->desc);
+ zif->desc = XSTRDUP(MTYPE_TMP, desc);
+ }
+
/* Hardware type and address. */
ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type);
netlink_interface_update_hw_addr(tb, ifp);
@@ -1106,7 +1108,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ifindex_t bond_ifindex = IFINDEX_INTERNAL;
ifindex_t link_ifindex = IFINDEX_INTERNAL;
uint8_t old_hw_addr[INTERFACE_HWADDR_MAX];
-
+ struct zebra_if *zif;
zns = zebra_ns_lookup(ns_id);
ifi = NLMSG_DATA(h);
@@ -1186,12 +1188,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* See if interface is present. */
ifp = if_lookup_by_name_per_ns(zns, name);
- if (ifp) {
- XFREE(MTYPE_TMP, ifp->desc);
- if (desc)
- ifp->desc = XSTRDUP(MTYPE_TMP, desc);
- }
-
if (h->nlmsg_type == RTM_NEWLINK) {
if (tb[IFLA_MASTER]) {
if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
@@ -1390,9 +1386,42 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if_delete_update(ifp);
}
+ zif = ifp->info;
+ if (zif) {
+ XFREE(MTYPE_TMP, zif->desc);
+ if (desc)
+ zif->desc = XSTRDUP(MTYPE_TMP, desc);
+ }
+
return 0;
}
+int netlink_protodown(struct interface *ifp, bool down)
+{
+ struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifa;
+ char buf[NL_PKT_BUF_SIZE];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_SETLINK;
+ req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
+
+ req.ifa.ifi_index = ifp->ifindex;
+
+ addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4);
+ addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4);
+
+ return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
+ 0);
+}
+
/* Interface information read by netlink. */
void interface_list(struct zebra_ns *zns)
{
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index 710fd52558..29fd2aca35 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -32,6 +32,20 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
+/*
+ * Set protodown status of interface.
+ *
+ * ifp
+ * Interface to set protodown on.
+ *
+ * down
+ * If true, set protodown on. If false, set protodown off.
+ *
+ * Returns:
+ * 0
+ */
+int netlink_protodown(struct interface *ifp, bool down);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/interface.c b/zebra/interface.c
index 10f1f92100..13582008a7 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -47,6 +47,7 @@
#include "zebra/irdp.h"
#include "zebra/zebra_ptm.h"
#include "zebra/rt_netlink.h"
+#include "zebra/if_netlink.h"
#include "zebra/interface.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
@@ -181,6 +182,7 @@ static int if_zebra_delete_hook(struct interface *ifp)
list_delete(&rtadv->AdvDNSSLList);
#endif /* HAVE_RTADV */
+ XFREE(MTYPE_TMP, zebra_if->desc);
THREAD_OFF(zebra_if->speed_update);
XFREE(MTYPE_ZINFO, zebra_if);
@@ -1062,7 +1064,14 @@ void zebra_if_update_all_links(void)
}
}
-
+void zebra_if_set_protodown(struct interface *ifp, bool down)
+{
+#ifdef HAVE_NETLINK
+ netlink_protodown(ifp, down);
+#else
+ zlog_warn("Protodown is not supported on this platform");
+#endif
+}
/* Output prefix string to vty. */
static int prefix_vty_out(struct vty *vty, struct prefix *p)
@@ -1303,6 +1312,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
if (ifp->desc)
vty_out(vty, " Description: %s\n", ifp->desc);
+ if (zebra_if->desc)
+ vty_out(vty, " OS Description: %s\n", zebra_if->desc);
+
if (ifp->ifindex == IFINDEX_INTERNAL) {
vty_out(vty, " pseudo interface\n");
return;
@@ -1696,6 +1708,10 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id)
vty_out(vty, "Interface Status Protocol Description\n");
FOR_ALL_INTERFACES (vrf, ifp) {
int len;
+ struct zebra_if *zif;
+ bool intf_desc;
+
+ intf_desc = false;
len = vty_out(vty, "%s", ifp->name);
vty_out(vty, "%*s", (16 - len), " ");
@@ -1715,8 +1731,19 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id)
vty_out(vty, "down down ");
}
- if (ifp->desc)
+ if (ifp->desc) {
+ intf_desc = true;
vty_out(vty, "%s", ifp->desc);
+ }
+ zif = ifp->info;
+ if (zif && zif->desc) {
+ vty_out(vty, "%s%s",
+ intf_desc
+ ? "\n "
+ : "",
+ zif->desc);
+ }
+
vty_out(vty, "\n");
}
}
diff --git a/zebra/interface.h b/zebra/interface.h
index ce404e8253..6a3914451a 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -342,6 +342,9 @@ struct zebra_if {
bool v6_2_v4_ll_neigh_entry;
char neigh_mac[6];
struct in6_addr v6_2_v4_ll_addr6;
+
+ /* The description of the interface */
+ char *desc;
};
DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
@@ -419,6 +422,7 @@ 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);
extern void zebra_if_update_all_links(void);
+extern void zebra_if_set_protodown(struct interface *ifp, bool down);
extern void vrf_add_update(struct vrf *vrfp);
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 13d2185b0f..5f4bd3bbc6 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1138,14 +1138,16 @@ void rtm_read(struct rt_msghdr *rtm)
*/
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL,
- 0, zebra_flags, &p, NULL, NULL, 0, 0, 0, true);
+ 0, zebra_flags, &p, NULL, NULL, RT_TABLE_MAIN,
+ 0, 0, true);
if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD
|| rtm->rtm_type == RTM_CHANGE)
rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0,
- zebra_flags, &p, NULL, &nh, 0, 0, 0, 0, 0);
+ zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, 0, 0, 0, 0);
else
rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL,
- 0, zebra_flags, &p, NULL, &nh, 0, 0, 0, true);
+ 0, zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN,
+ 0, 0, true);
}
/* Interface function for the kernel routing table updates. Support
diff --git a/zebra/main.c b/zebra/main.c
index 184e798bd0..5797a5846a 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -74,8 +74,7 @@ int retain_mode = 0;
/* Allow non-quagga entities to delete quagga routes */
int allow_delete = 0;
-/* Don't delete kernel route. */
-int keep_kernel_mode = 0;
+int graceful_restart;
bool v6_rr_semantics = false;
@@ -95,6 +94,7 @@ struct option longopts[] = {
{"label_socket", no_argument, NULL, 'l'},
{"retain", no_argument, NULL, 'r'},
{"vrfdefaultname", required_argument, NULL, 'o'},
+ {"graceful_restart", required_argument, NULL, 'K'},
#ifdef HAVE_NETLINK
{"vrfwnetns", no_argument, NULL, 'n'},
{"nl-bufsize", required_argument, NULL, 's'},
@@ -119,8 +119,6 @@ struct zebra_privs_t zserv_privs = {
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0};
-unsigned int multipath_num = MULTIPATH_NUM;
-
/* SIGHUP handler. */
static void sighup(void)
{
@@ -264,13 +262,14 @@ int main(int argc, char **argv)
char *netlink_fuzzing = NULL;
#endif /* HANDLE_NETLINK_FUZZING */
+ graceful_restart = 0;
vrf_configure_backend(VRF_BACKEND_VRF_LITE);
logicalrouter_configure_backend(LOGICALROUTER_BACKEND_NETNS);
frr_preinit(&zebra_di, argc, argv);
frr_opt_add(
- "bakz:e:l:o:r"
+ "baz:e:l:o:rK:"
#ifdef HAVE_NETLINK
"s:n"
#endif
@@ -282,24 +281,24 @@ int main(int argc, char **argv)
#endif /* HANDLE_NETLINK_FUZZING */
,
longopts,
- " -b, --batch Runs in batch mode\n"
- " -a, --allow_delete Allow other processes to delete zebra routes\n"
- " -z, --socket Set path of zebra socket\n"
- " -e, --ecmp Specify ECMP to use.\n"
- " -l, --label_socket Socket to external label manager\n"
- " -k, --keep_kernel Don't delete old routes which were installed by zebra.\n"
- " -r, --retain When program terminates, retain added route by zebra.\n"
- " -o, --vrfdefaultname Set default VRF name.\n"
+ " -b, --batch Runs in batch mode\n"
+ " -a, --allow_delete Allow other processes to delete zebra routes\n"
+ " -z, --socket Set path of zebra socket\n"
+ " -e, --ecmp Specify ECMP to use.\n"
+ " -l, --label_socket Socket to external label manager\n"
+ " -r, --retain When program terminates, retain added route by zebra.\n"
+ " -o, --vrfdefaultname Set default VRF name.\n"
+ " -K, --graceful_restart Graceful restart at the kernel level, timer in seconds for expiration\n"
#ifdef HAVE_NETLINK
- " -n, --vrfwnetns Use NetNS as VRF backend\n"
- " -s, --nl-bufsize Set netlink receive buffer size\n"
- " --v6-rr-semantics Use v6 RR semantics\n"
+ " -n, --vrfwnetns Use NetNS as VRF backend\n"
+ " -s, --nl-bufsize Set netlink receive buffer size\n"
+ " --v6-rr-semantics Use v6 RR semantics\n"
#endif /* HAVE_NETLINK */
#if defined(HANDLE_ZAPI_FUZZING)
- " -c <file> Bypass normal startup and use this file for testing of zapi\n"
+ " -c <file> Bypass normal startup and use this file for testing of zapi\n"
#endif /* HANDLE_ZAPI_FUZZING */
#if defined(HANDLE_NETLINK_FUZZING)
- " -w <file> Bypass normal startup and use this file for testing of netlink input\n"
+ " -w <file> Bypass normal startup and use this file for testing of netlink input\n"
#endif /* HANDLE_NETLINK_FUZZING */
);
@@ -318,13 +317,10 @@ int main(int argc, char **argv)
case 'a':
allow_delete = 1;
break;
- case 'k':
- keep_kernel_mode = 1;
- break;
case 'e':
- multipath_num = atoi(optarg);
- if (multipath_num > MULTIPATH_NUM
- || multipath_num <= 0) {
+ zrouter.multipath_num = atoi(optarg);
+ if (zrouter.multipath_num > MULTIPATH_NUM
+ || zrouter.multipath_num <= 0) {
flog_err(
EC_ZEBRA_BAD_MULTIPATH_NUM,
"Multipath Number specified must be less than %d and greater than 0",
@@ -350,6 +346,9 @@ int main(int argc, char **argv)
case 'r':
retain_mode = 1;
break;
+ case 'K':
+ graceful_restart = atoi(optarg);
+ break;
#ifdef HAVE_NETLINK
case 's':
nl_rcvbufsize = atoi(optarg);
@@ -437,8 +436,9 @@ int main(int argc, char **argv)
* will be equal to the current getpid(). To know about such routes,
* we have to have route_read() called before.
*/
- if (!keep_kernel_mode)
- rib_sweep_route();
+ zrouter.startup_time = monotime(NULL);
+ thread_add_timer(zrouter.master, rib_sweep_route,
+ NULL, graceful_restart, NULL);
/* Needed for BSD routing socket. */
pid = getpid();
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index f98a4c02c3..b13f1170cd 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -612,9 +612,9 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re,
newre->flags = re->flags;
newre->metric = re->metric;
newre->mtu = re->mtu;
- newre->table = zrouter.rtm_table_default;
+ newre->table = 0;
newre->nexthop_num = 0;
- newre->uptime = time(NULL);
+ newre->uptime = monotime(NULL);
newre->instance = re->table;
route_entry_copy_nexthops(newre, re->ng.nexthop);
@@ -632,8 +632,8 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re)
prefix_copy(&p, &rn->p);
rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table,
- re->flags, &p, NULL, re->ng.nexthop,
- zrouter.rtm_table_default, re->metric, re->distance, false);
+ re->flags, &p, NULL, re->ng.nexthop, 0, re->metric,
+ re->distance, false);
return 0;
}
@@ -647,14 +647,14 @@ int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance,
struct route_node *rn;
if (!is_zebra_valid_kernel_table(table_id)
- || ((table_id == RT_TABLE_MAIN)
- || (table_id == zrouter.rtm_table_default)))
+ || (table_id == RT_TABLE_MAIN))
return (-1);
if (afi >= AFI_MAX)
return (-1);
- table = zebra_vrf_other_route_table(afi, table_id, VRF_DEFAULT);
+ table = zebra_vrf_table_with_table_id(afi, SAFI_UNICAST,
+ table_id, VRF_DEFAULT);
if (table == NULL) {
return 0;
} else if (IS_ZEBRA_DEBUG_RIB) {
@@ -768,8 +768,15 @@ void zebra_import_table_rm_update(const char *rmap)
rmap_name = zebra_get_import_table_route_map(afi, i);
if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0))
continue;
- table = zebra_vrf_other_route_table(afi, i,
- VRF_DEFAULT);
+ table = zebra_vrf_table_with_table_id(afi, SAFI_UNICAST,
+ i, VRF_DEFAULT);
+ if (!table) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: Table id=%d not found",
+ __func__, i);
+ continue;
+ }
+
for (rn = route_top(table); rn; rn = route_next(rn)) {
/* For each entry in the non-default
* routing table,
diff --git a/zebra/rib.h b/zebra/rib.h
index e26831e1a6..0353c9bb99 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -24,6 +24,7 @@
#include "zebra.h"
#include "hook.h"
+#include "typesafe.h"
#include "linklist.h"
#include "prefix.h"
#include "table.h"
@@ -39,13 +40,52 @@
extern "C" {
#endif
+typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t;
+
+PREDECL_LIST(rnh_list)
+
+/* Nexthop structure. */
+struct rnh {
+ uint8_t flags;
+
+#define ZEBRA_NHT_CONNECTED 0x1
+#define ZEBRA_NHT_DELETED 0x2
+#define ZEBRA_NHT_EXACT_MATCH 0x4
+
+ /* VRF identifier. */
+ vrf_id_t vrf_id;
+
+ afi_t afi;
+
+ rnh_type_t type;
+
+ uint32_t seqno;
+
+ struct route_entry *state;
+ struct prefix resolved_route;
+ struct list *client_list;
+
+ /* pseudowires dependent on this nh */
+ struct list *zebra_pseudowire_list;
+
+ struct route_node *node;
+
+ /*
+ * if this has been filtered for the client
+ */
+ int filtered[ZEBRA_ROUTE_MAX];
+
+ struct rnh_list_item rnh_list_item;
+};
+
#define DISTANCE_INFINITY 255
#define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */
+PREDECL_LIST(re_list)
+
struct route_entry {
/* Link list. */
- struct route_entry *next;
- struct route_entry *prev;
+ struct re_list_item next;
/* Nexthop structure */
struct nexthop_group ng;
@@ -59,9 +99,6 @@ struct route_entry {
/* Type fo this route. */
int type;
- /* Source protocol instance */
- unsigned short instance;
-
/* VRF identifier. */
vrf_id_t vrf_id;
@@ -75,9 +112,6 @@ struct route_entry {
uint32_t mtu;
uint32_t nexthop_mtu;
- /* Distance. */
- uint8_t distance;
-
/* Flags of this route.
* This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed
* to clients via Zserv
@@ -106,8 +140,18 @@ struct route_entry {
/* Sequence value incremented for each dataplane operation */
uint32_t dplane_sequence;
+
+ /* Source protocol instance */
+ uint16_t instance;
+
+ /* Distance. */
+ uint8_t distance;
};
+#define RIB_SYSTEM_ROUTE(R) RSYSTEM_ROUTE((R)->type)
+
+#define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type)
+
/* meta-queue structure:
* sub-queue 0: connected, kernel
* sub-queue 1: static
@@ -135,7 +179,7 @@ typedef struct rib_dest_t_ {
/*
* Doubly-linked list of routes for this prefix.
*/
- struct route_entry *routes;
+ struct re_list_head routes;
struct route_entry *selected_fib;
@@ -151,7 +195,7 @@ typedef struct rib_dest_t_ {
* the data plane we will run evaluate_rnh
* on these prefixes.
*/
- struct list *nht;
+ struct rnh_list_head nht;
/*
* Linkage to put dest on the FPM processing queue.
@@ -160,6 +204,9 @@ typedef struct rib_dest_t_ {
} rib_dest_t;
+DECLARE_LIST(rnh_list, struct rnh, rnh_list_item);
+DECLARE_LIST(re_list, struct route_entry, next);
+
#define RIB_ROUTE_QUEUED(x) (1 << (x))
// If MQ_SIZE is modified this value needs to be updated.
#define RIB_ROUTE_ANY_QUEUED 0x1F
@@ -187,14 +234,16 @@ typedef struct rib_dest_t_ {
* Macro to iterate over each route for a destination (prefix).
*/
#define RE_DEST_FOREACH_ROUTE(dest, re) \
- for ((re) = (dest) ? (dest)->routes : NULL; (re); (re) = (re)->next)
+ for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; (re); \
+ (re) = re_list_next(&((dest)->routes), (re)))
/*
* Same as above, but allows the current node to be unlinked.
*/
#define RE_DEST_FOREACH_ROUTE_SAFE(dest, re, next) \
- for ((re) = (dest) ? (dest)->routes : NULL; \
- (re) && ((next) = (re)->next, 1); (re) = (next))
+ for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; \
+ (re) && ((next) = re_list_next(&((dest)->routes), (re)), 1); \
+ (re) = (next))
#define RNODE_FOREACH_RE(rn, re) \
RE_DEST_FOREACH_ROUTE (rib_dest_from_rnode(rn), re)
@@ -351,7 +400,7 @@ extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p,
extern void rib_update(vrf_id_t vrf_id, rib_update_event_t event);
extern void rib_update_table(struct route_table *table,
rib_update_event_t event);
-extern void rib_sweep_route(void);
+extern int rib_sweep_route(struct thread *t);
extern void rib_sweep_table(struct route_table *table);
extern void rib_close_table(struct route_table *table);
extern void rib_init(void);
@@ -406,7 +455,7 @@ static inline struct route_entry *rnode_to_ribs(struct route_node *rn)
if (!dest)
return NULL;
- return dest->routes;
+ return re_list_first(&dest->routes);
}
/*
diff --git a/zebra/rt.h b/zebra/rt.h
index 08b51fcc0b..04576671fe 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -35,8 +35,10 @@
extern "C" {
#endif
-#define RSYSTEM_ROUTE(type) \
- ((type) == ZEBRA_ROUTE_KERNEL || (type) == ZEBRA_ROUTE_CONNECT)
+#define RKERNEL_ROUTE(type) ((type) == ZEBRA_ROUTE_KERNEL)
+
+#define RSYSTEM_ROUTE(type) \
+ ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
/*
* Update or delete a route, LSP, or pseudowire from the kernel,
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 289ed5a15b..def5bf7d88 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -583,7 +583,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
re->vrf_id = vrf_id;
re->table = table;
re->nexthop_num = 0;
- re->uptime = time(NULL);
+ re->uptime = monotime(NULL);
re->tag = tag;
for (;;) {
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index f31fb53a34..94bfa34b38 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -71,6 +71,8 @@
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_putl(s, ifp->ifindex);
stream_putc(s, ifp->status);
@@ -82,6 +84,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp)
stream_putl(s, ifp->mtu);
stream_putl(s, ifp->mtu6);
stream_putl(s, ifp->bandwidth);
+ stream_putl(s, zif->link_ifindex);
stream_putl(s, ifp->ll_type);
stream_putl(s, ifp->hw_addr_len);
if (ifp->hw_addr_len)
@@ -1336,17 +1339,48 @@ static void zread_interface_delete(ZAPI_HANDLER_ARGS)
{
}
+/*
+ * Handle message requesting interface be set up or down.
+ */
+static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)
+{
+ ifindex_t ifindex;
+ struct interface *ifp;
+ char down;
+
+ STREAM_GETL(msg, ifindex);
+ STREAM_GETC(msg, down);
+
+ /* set ifdown */
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex);
+
+ if (ifp) {
+ zlog_info("Setting interface %s (%u): protodown %s", ifp->name,
+ ifindex, down ? "on" : "off");
+ zebra_if_set_protodown(ifp, down);
+ } else {
+ zlog_warn(
+ "Cannot set protodown %s for interface %u; does not exist",
+ down ? "on" : "off", ifindex);
+ }
+
+
+stream_failure:
+ return;
+}
+
+
void zserv_nexthop_num_warn(const char *caller, const struct prefix *p,
const unsigned int nexthop_num)
{
- if (nexthop_num > multipath_num) {
+ if (nexthop_num > zrouter.multipath_num) {
char buff[PREFIX2STR_BUFFER];
prefix2str(p, buff, sizeof(buff));
flog_warn(
EC_ZEBRA_MORE_NH_THAN_MULTIPATH,
"%s: Prefix %s has %d nexthops, but we can only use the first %d",
- caller, buff, nexthop_num, multipath_num);
+ caller, buff, nexthop_num, zrouter.multipath_num);
}
}
@@ -1388,7 +1422,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
re->type = api.type;
re->instance = api.instance;
re->flags = api.flags;
- re->uptime = time(NULL);
+ re->uptime = monotime(NULL);
re->vrf_id = vrf_id;
if (api.tableid && vrf_id == VRF_DEFAULT)
re->table = api.tableid;
@@ -1651,7 +1685,7 @@ static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf)
zclient_create_header(s, ZEBRA_CAPABILITIES, zvrf->vrf->vrf_id);
stream_putl(s, vrf_get_backend());
stream_putc(s, mpls_enabled);
- stream_putl(s, multipath_num);
+ stream_putl(s, zrouter.multipath_num);
stream_putc(s, zebra_mlag_get_role());
stream_putw_at(s, 0, stream_get_endp(s));
@@ -2412,6 +2446,7 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
[ZEBRA_INTERFACE_ADD] = zread_interface_add,
[ZEBRA_INTERFACE_DELETE] = zread_interface_delete,
+ [ZEBRA_INTERFACE_SET_PROTODOWN] = zread_interface_set_protodown,
[ZEBRA_ROUTE_ADD] = zread_route_add,
[ZEBRA_ROUTE_DELETE] = zread_route_del,
[ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add,
@@ -2507,6 +2542,9 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg)
zapi_parse_header(msg, &hdr);
+ if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+ zserv_log_message(NULL, msg, &hdr);
+
#if defined(HANDLE_ZAPI_FUZZING)
zserv_write_incoming(msg, hdr.command);
#endif
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index d1b28227c3..6fc62147c8 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -1344,10 +1344,6 @@ dplane_route_update_internal(struct route_node *rn,
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
/* Init context with info from zebra data structs */
ret = dplane_ctx_route_init(ctx, op, rn, re);
@@ -1382,7 +1378,6 @@ dplane_route_update_internal(struct route_node *rn,
ret = dplane_route_enqueue(ctx);
}
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1,
memory_order_relaxed);
@@ -1562,10 +1557,6 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
ret = dplane_ctx_lsp_init(ctx, op, lsp);
if (ret != AOK)
@@ -1583,8 +1574,7 @@ done:
else {
atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
memory_order_relaxed);
- if (ctx)
- dplane_ctx_free(&ctx);
+ dplane_ctx_free(&ctx);
}
return result;
@@ -1601,10 +1591,6 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
struct zebra_dplane_ctx *ctx = NULL;
ctx = dplane_ctx_alloc();
- if (ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
ret = dplane_ctx_pw_init(ctx, op, pw);
if (ret != AOK)
@@ -1622,8 +1608,7 @@ done:
else {
atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1,
memory_order_relaxed);
- if (ctx)
- dplane_ctx_free(&ctx);
+ dplane_ctx_free(&ctx);
}
return result;
@@ -1691,10 +1676,6 @@ static enum zebra_dplane_result intf_addr_update_internal(
}
ctx = dplane_ctx_alloc();
- if (ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
@@ -1706,7 +1687,7 @@ static enum zebra_dplane_result intf_addr_update_internal(
/* Init the interface-addr-specific area */
memset(&ctx->u.intf, 0, sizeof(ctx->u.intf));
- strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
+ strlcpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
ctx->u.intf.ifindex = ifp->ifindex;
ctx->u.intf.prefix = *(ifc->address);
@@ -1734,7 +1715,7 @@ static enum zebra_dplane_result intf_addr_update_internal(
len = strlen(ifc->label);
if (len < sizeof(ctx->u.intf.label_buf)) {
- strncpy(ctx->u.intf.label_buf, ifc->label,
+ strlcpy(ctx->u.intf.label_buf, ifc->label,
sizeof(ctx->u.intf.label_buf));
ctx->u.intf.label = ctx->u.intf.label_buf;
} else {
@@ -1744,8 +1725,6 @@ static enum zebra_dplane_result intf_addr_update_internal(
ret = dplane_route_enqueue(ctx);
-done:
-
/* Increment counter */
atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1,
memory_order_relaxed);
@@ -1756,8 +1735,7 @@ done:
/* Error counter */
atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
1, memory_order_relaxed);
- if (ctx)
- dplane_ctx_free(&ctx);
+ dplane_ctx_free(&ctx);
}
return result;
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 28333526a7..2ac79b100c 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -32,6 +32,7 @@
#include "prefix.h"
#include "zebra/zserv.h"
+#include "zebra/zebra_router.h"
#include "zebra/zebra_dplane.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
@@ -40,6 +41,7 @@
#include "nexthop.h"
#include "zebra/zebra_fpm_private.h"
+#include "zebra/zebra_vxlan_private.h"
/*
* addr_to_a
@@ -101,6 +103,51 @@ static size_t af_addr_size(uint8_t af)
}
/*
+ * We plan to use RTA_ENCAP_TYPE attribute for VxLAN encap as well.
+ * Currently, values 0 to 8 for this attribute are used by lwtunnel_encap_types
+ * So, we cannot use these values for VxLAN encap.
+ */
+enum fpm_nh_encap_type_t {
+ FPM_NH_ENCAP_NONE = 0,
+ FPM_NH_ENCAP_VXLAN = 100,
+ FPM_NH_ENCAP_MAX,
+};
+
+/*
+ * fpm_nh_encap_type_to_str
+ */
+static const char *fpm_nh_encap_type_to_str(enum fpm_nh_encap_type_t encap_type)
+{
+ switch (encap_type) {
+ case FPM_NH_ENCAP_NONE:
+ return "none";
+
+ case FPM_NH_ENCAP_VXLAN:
+ return "VxLAN";
+
+ case FPM_NH_ENCAP_MAX:
+ return "invalid";
+ }
+
+ return "invalid";
+}
+
+struct vxlan_encap_info_t {
+ vni_t vni;
+};
+
+enum vxlan_encap_info_type_t {
+ VXLAN_VNI = 0,
+};
+
+struct fpm_nh_encap_info_t {
+ enum fpm_nh_encap_type_t encap_type;
+ union {
+ struct vxlan_encap_info_t vxlan_encap;
+ };
+};
+
+/*
* netlink_nh_info_t
*
* Holds information about a single nexthop for netlink. These info
@@ -117,6 +164,7 @@ typedef struct netlink_nh_info_t_ {
*/
int recursive;
enum nexthop_types_t type;
+ struct fpm_nh_encap_info_t encap_info;
} netlink_nh_info_t;
/*
@@ -150,15 +198,17 @@ typedef struct netlink_route_info_t_ {
* Returns TRUE if a nexthop was added, FALSE otherwise.
*/
static int netlink_route_info_add_nh(netlink_route_info_t *ri,
- struct nexthop *nexthop)
+ struct nexthop *nexthop,
+ struct route_entry *re)
{
netlink_nh_info_t nhi;
union g_addr *src;
+ zebra_l3vni_t *zl3vni = NULL;
memset(&nhi, 0, sizeof(nhi));
src = NULL;
- if (ri->num_nhs >= (int)ZEBRA_NUM_OF(ri->nhs))
+ if (ri->num_nhs >= (int)array_size(ri->nhs))
return 0;
nhi.recursive = nexthop->rparent ? 1 : 0;
@@ -185,6 +235,17 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri,
if (!nhi.gateway && nhi.if_index == 0)
return 0;
+ if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
+ nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
+
+ zl3vni = zl3vni_from_vrf(ri->rtm_table);
+ if (zl3vni && is_l3vni_oper_up(zl3vni)) {
+
+ /* Add VNI to VxLAN encap info */
+ nhi.encap_info.vxlan_encap.vni = zl3vni->vni;
+ }
+ }
+
/*
* We have a valid nhi. Copy the structure over to the route_info.
*/
@@ -223,6 +284,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
rib_dest_t *dest, struct route_entry *re)
{
struct nexthop *nexthop;
+ struct zebra_vrf *zvrf;
memset(ri, 0, sizeof(*ri));
@@ -230,7 +292,9 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
ri->af = rib_dest_af(dest);
ri->nlmsg_type = cmd;
- ri->rtm_table = zvrf_id(rib_dest_vrf(dest));
+ zvrf = rib_dest_vrf(dest);
+ if (zvrf)
+ ri->rtm_table = zvrf->table_id;
ri->rtm_protocol = RTPROT_UNSPEC;
/*
@@ -251,7 +315,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
ri->metric = &re->metric;
for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (ri->num_nhs >= multipath_num)
+ if (ri->num_nhs >= zrouter.multipath_num)
break;
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -270,14 +334,13 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
ri->rtm_type = RTN_BLACKHOLE;
break;
}
- return 1;
}
if ((cmd == RTM_NEWROUTE
&& CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
|| (cmd == RTM_DELROUTE
&& CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))) {
- netlink_route_info_add_nh(ri, nexthop);
+ netlink_route_info_add_nh(ri, nexthop, re);
}
}
@@ -303,6 +366,10 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
unsigned int nexthop_num = 0;
size_t buf_offset;
netlink_nh_info_t *nhi;
+ enum fpm_nh_encap_type_t encap;
+ struct rtattr *nest;
+ struct vxlan_encap_info_t *vxlan;
+ int nest_len;
struct {
struct nlmsghdr n;
@@ -327,7 +394,21 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
req->n.nlmsg_type = ri->nlmsg_type;
req->r.rtm_family = ri->af;
- req->r.rtm_table = ri->rtm_table;
+
+ /*
+ * rtm_table field is a uchar field which can accomodate table_id less
+ * than 256.
+ * To support table id greater than 255, if the table_id is greater than
+ * 255, set rtm_table to RT_TABLE_UNSPEC and add RTA_TABLE attribute
+ * with 32 bit value as the table_id.
+ */
+ if (ri->rtm_table < 256)
+ req->r.rtm_table = ri->rtm_table;
+ else {
+ req->r.rtm_table = RT_TABLE_UNSPEC;
+ addattr32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table);
+ }
+
req->r.rtm_dst_len = ri->prefix->prefixlen;
req->r.rtm_protocol = ri->rtm_protocol;
req->r.rtm_scope = RT_SCOPE_UNIVERSE;
@@ -355,6 +436,26 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
addattr32(&req->n, in_buf_len, RTA_OIF, nhi->if_index);
}
+ encap = nhi->encap_info.encap_type;
+ if (encap > FPM_NH_ENCAP_NONE) {
+ addattr_l(&req->n, in_buf_len, RTA_ENCAP_TYPE, &encap,
+ sizeof(uint16_t));
+ switch (encap) {
+ case FPM_NH_ENCAP_NONE:
+ break;
+ case FPM_NH_ENCAP_VXLAN:
+ vxlan = &nhi->encap_info.vxlan_encap;
+ nest = addattr_nest(&req->n, in_buf_len,
+ RTA_ENCAP);
+ addattr32(&req->n, in_buf_len, VXLAN_VNI,
+ vxlan->vni);
+ addattr_nest_end(&req->n, nest);
+ break;
+ case FPM_NH_ENCAP_MAX:
+ break;
+ }
+ }
+
goto done;
}
@@ -388,6 +489,28 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
rtnh->rtnh_ifindex = nhi->if_index;
}
+ encap = nhi->encap_info.encap_type;
+ if (encap > FPM_NH_ENCAP_NONE) {
+ rta_addattr_l(rta, sizeof(buf), RTA_ENCAP_TYPE,
+ &encap, sizeof(uint16_t));
+ rtnh->rtnh_len += sizeof(struct rtattr) +
+ sizeof(uint16_t);
+ switch (encap) {
+ case FPM_NH_ENCAP_NONE:
+ break;
+ case FPM_NH_ENCAP_VXLAN:
+ vxlan = &nhi->encap_info.vxlan_encap;
+ nest = rta_nest(rta, sizeof(buf), RTA_ENCAP);
+ rta_addattr_l(rta, sizeof(buf), VXLAN_VNI,
+ &vxlan->vni, sizeof(uint32_t));
+ nest_len = rta_nest_end(rta, nest);
+ rtnh->rtnh_len += nest_len;
+ break;
+ case FPM_NH_ENCAP_MAX:
+ break;
+ }
+ }
+
rtnh = RTNH_NEXT(rtnh);
}
@@ -424,10 +547,12 @@ static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label)
for (i = 0; i < ri->num_nhs; i++) {
nhi = &ri->nhs[i];
- zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s",
+ zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s",
nhi->if_index, addr_to_a(ri->af, nhi->gateway),
nhi->recursive ? "yes" : "no",
- nexthop_type_to_str(nhi->type));
+ nexthop_type_to_str(nhi->type),
+ fpm_nh_encap_type_to_str(nhi->encap_info.encap_type)
+ );
}
}
diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c
index be0f6a23be..3054b8a34d 100644
--- a/zebra/zebra_fpm_protobuf.c
+++ b/zebra/zebra_fpm_protobuf.c
@@ -34,6 +34,7 @@
#include "qpb/linear_allocator.h"
#include "fpm/fpm_pb.h"
+#include "zebra_router.h"
#include "zebra_fpm_private.h"
/*
@@ -173,10 +174,10 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator,
*/
num_nhs = 0;
for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (num_nhs >= multipath_num)
+ if (num_nhs >= zrouter.multipath_num)
break;
- if (num_nhs >= ZEBRA_NUM_OF(nexthops))
+ if (num_nhs >= array_size(nexthops))
break;
if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 5c375a6bef..5356a7f498 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -76,7 +76,7 @@ static zebra_fec_t *fec_add(struct route_table *table, struct prefix *p,
uint32_t label_index);
static int fec_del(zebra_fec_t *fec);
-static unsigned int label_hash(void *p);
+static unsigned int label_hash(const void *p);
static bool label_cmp(const void *p1, const void *p2);
static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe,
struct nexthop *nexthop);
@@ -577,7 +577,7 @@ static int fec_del(zebra_fec_t *fec)
/*
* Hash function for label.
*/
-static unsigned int label_hash(void *p)
+static unsigned int label_hash(const void *p)
{
const zebra_ile_t *ile = p;
@@ -2874,7 +2874,11 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf,
ifp = if_lookup_by_index_per_ns(
zns,
nexthop->ifindex);
- vty_out(vty, "%15s", ifp->name);
+ if (ifp)
+ vty_out(vty, "%15s", ifp->name);
+ else
+ vty_out(vty, "%15s", "Null");
+
break;
}
case NEXTHOP_TYPE_IPV4:
@@ -2999,11 +3003,30 @@ int zebra_mpls_write_label_block_config(struct vty *vty, struct zebra_vrf *zvrf)
/*
* Called when VRF becomes inactive, cleans up information but keeps
* the table itself.
- * NOTE: Currently supported only for default VRF.
*/
void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf)
{
- hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL);
+ struct zebra_vrf *def_zvrf;
+ afi_t afi;
+
+ if (zvrf_id(zvrf) == VRF_DEFAULT)
+ hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL);
+ else {
+ /*
+ * For other vrfs, we try to remove associated LSPs; we locate
+ * the LSPs in the default vrf.
+ */
+ def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+
+ /* At shutdown, the default may be gone already */
+ if (def_zvrf == NULL)
+ return;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ if (zvrf->label[afi] != MPLS_LABEL_NONE)
+ lsp_uninstall(def_zvrf, zvrf->label[afi]);
+ }
+ }
}
/*
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index 977a8eaf3c..e7fdaf127d 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -27,6 +27,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/debug.h"
#include "zebra/zebra_errors.h"
+#include "zebra/zebra_router.h"
#include "privs.h"
#include "prefix.h"
@@ -262,7 +263,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx)
if (!nexthop)
continue;
- if (nexthop_num >= multipath_num)
+ if (nexthop_num >= zrouter.multipath_num)
break;
if (((action == RTM_ADD || action == RTM_CHANGE)
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index 73db567eac..a82dd4c24a 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -135,12 +135,12 @@ void zebra_pbr_rules_free(void *arg)
XFREE(MTYPE_TMP, rule);
}
-uint32_t zebra_pbr_rules_hash_key(void *arg)
+uint32_t zebra_pbr_rules_hash_key(const void *arg)
{
- struct zebra_pbr_rule *rule;
+ const struct zebra_pbr_rule *rule;
uint32_t key;
- rule = (struct zebra_pbr_rule *)arg;
+ rule = arg;
key = jhash_3words(rule->rule.seq, rule->rule.priority,
rule->rule.action.table,
prefix_hash_key(&rule->rule.filter.src_ip));
@@ -250,9 +250,9 @@ void zebra_pbr_ipset_free(void *arg)
XFREE(MTYPE_TMP, ipset);
}
-uint32_t zebra_pbr_ipset_hash_key(void *arg)
+uint32_t zebra_pbr_ipset_hash_key(const void *arg)
{
- struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg;
+ const struct zebra_pbr_ipset *ipset = arg;
uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de);
@@ -290,12 +290,12 @@ void zebra_pbr_ipset_entry_free(void *arg)
XFREE(MTYPE_TMP, ipset);
}
-uint32_t zebra_pbr_ipset_entry_hash_key(void *arg)
+uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg)
{
- struct zebra_pbr_ipset_entry *ipset;
+ const struct zebra_pbr_ipset_entry *ipset;
uint32_t key;
- ipset = (struct zebra_pbr_ipset_entry *)arg;
+ ipset = arg;
key = prefix_hash_key(&ipset->src);
key = jhash_1word(ipset->unique, key);
key = jhash_1word(prefix_hash_key(&ipset->dst), key);
@@ -359,9 +359,9 @@ void zebra_pbr_iptable_free(void *arg)
XFREE(MTYPE_TMP, iptable);
}
-uint32_t zebra_pbr_iptable_hash_key(void *arg)
+uint32_t zebra_pbr_iptable_hash_key(const void *arg)
{
- struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg;
+ const struct zebra_pbr_iptable *iptable = arg;
uint32_t *pnt = (uint32_t *)&(iptable->ipset_name);
uint32_t key;
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index 0d55491107..cc1cc5acd5 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -211,7 +211,7 @@ extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule);
extern void zebra_pbr_rules_free(void *arg);
-extern uint32_t zebra_pbr_rules_hash_key(void *arg);
+extern uint32_t zebra_pbr_rules_hash_key(const void *arg);
extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2);
/* has operates on 32bit pointer
@@ -220,16 +220,16 @@ extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2);
#define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4)
extern void zebra_pbr_ipset_free(void *arg);
-extern uint32_t zebra_pbr_ipset_hash_key(void *arg);
+extern uint32_t zebra_pbr_ipset_hash_key(const void *arg);
extern bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2);
extern void zebra_pbr_ipset_entry_free(void *arg);
-extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg);
+extern uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg);
extern bool zebra_pbr_ipset_entry_hash_equal(const void *arg1,
const void *arg2);
extern void zebra_pbr_iptable_free(void *arg);
-extern uint32_t zebra_pbr_iptable_hash_key(void *arg);
+extern uint32_t zebra_pbr_iptable_hash_key(const void *arg);
extern bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2);
extern void zebra_pbr_init(void);
diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c
index bb352dc2ff..46f1385520 100644
--- a/zebra/zebra_ptm.c
+++ b/zebra/zebra_ptm.c
@@ -93,6 +93,7 @@ const char ZEBRA_PTM_BFD_IFNAME_FIELD[] = "ifName";
const char ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD[] = "maxHopCnt";
const char ZEBRA_PTM_BFD_SEND_EVENT[] = "sendEvent";
const char ZEBRA_PTM_BFD_VRF_NAME_FIELD[] = "vrfName";
+const char ZEBRA_PTM_BFD_CBIT_FIELD[] = "bfdcbit";
static ptm_lib_handle_t *ptm_hdl;
@@ -688,6 +689,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS)
char tmp_buf[64];
int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
unsigned int pid;
+ uint8_t cbit_set;
if (hdr->command == ZEBRA_BFD_DEST_UPDATE)
client->bfd_peer_upd8_cnt++;
@@ -813,6 +815,10 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS)
ptm_lib_append_msg(ptm_hdl, out_ctxt,
ZEBRA_PTM_BFD_IFNAME_FIELD, if_name);
}
+ STREAM_GETC(s, cbit_set);
+ sprintf(tmp_buf, "%d", cbit_set);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_CBIT_FIELD, tmp_buf);
sprintf(tmp_buf, "%d", 1);
ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEND_EVENT,
@@ -1188,8 +1194,8 @@ static void pp_free_all(void);
static void zebra_ptm_send_bfdd(struct stream *msg);
static void zebra_ptm_send_clients(struct stream *msg);
static int _zebra_ptm_bfd_client_deregister(struct zserv *zs);
-static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg,
- uint32_t command);
+static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf,
+ struct stream *msg, uint32_t command);
/*
@@ -1392,8 +1398,8 @@ void zebra_ptm_finish(void)
/*
* Message handling.
*/
-static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg,
- uint32_t command)
+static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf,
+ struct stream *msg, uint32_t command)
{
struct stream *msgc;
size_t zmsglen, zhdrlen;
@@ -1420,7 +1426,7 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg,
* one callback at the `bfdd` side, however the real command
* number will be included right after the zebra header.
*/
- zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, 0);
+ zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id);
stream_putl(msgc, command);
/* Update the data pointers. */
@@ -1446,7 +1452,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS)
zlog_debug("bfd_dst_register msg from client %s: length=%d",
zebra_route_string(client->proto), hdr->length);
- _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REGISTER);
+ _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REGISTER);
}
void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS)
@@ -1455,7 +1461,7 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS)
zlog_debug("bfd_dst_deregister msg from client %s: length=%d",
zebra_route_string(client->proto), hdr->length);
- _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_DEREGISTER);
+ _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_DEREGISTER);
}
void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS)
@@ -1464,7 +1470,7 @@ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS)
zlog_debug("bfd_client_register msg from client %s: length=%d",
zebra_route_string(client->proto), hdr->length);
- _zebra_ptm_reroute(client, msg, ZEBRA_BFD_CLIENT_REGISTER);
+ _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_CLIENT_REGISTER);
}
void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS)
@@ -1488,7 +1494,7 @@ void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS)
* special treatment.
*/
if (client->proto != ZEBRA_ROUTE_BFD) {
- _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REPLAY);
+ _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REPLAY);
return;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 0e1df1cc35..b31b6a1250 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -71,7 +71,7 @@ extern int allow_delete;
/* Each route type's string and default distance value. */
static const struct {
int key;
- int distance;
+ uint8_t distance;
uint8_t meta_q_map;
} route_info[ZEBRA_ROUTE_MAX] = {
[ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 4},
@@ -98,6 +98,11 @@ static const struct {
[ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 3},
[ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2},
[ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4},
+ [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4},
+ [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4},
+ [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2},
+ [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 4}
+ /* Any new route type added to zebra, should be mirrored here */
/* no entry/default: 150 */
};
@@ -161,8 +166,7 @@ int is_zebra_valid_kernel_table(uint32_t table_id)
int is_zebra_main_routing_table(uint32_t table_id)
{
- if ((table_id == RT_TABLE_MAIN)
- || (table_id == zrouter.rtm_table_default))
+ if (table_id == RT_TABLE_MAIN)
return 1;
return 0;
}
@@ -470,8 +474,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
"\t%s: Interface %s is not unnumbered",
- __PRETTY_FUNCTION__,
- ifp ? ifp->name : "Unknown");
+ __PRETTY_FUNCTION__, ifp->name);
return 0;
}
}
@@ -795,12 +798,6 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id)
return NULL;
}
-#define RIB_SYSTEM_ROUTE(R) \
- ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
-
-#define RIB_KERNEL_ROUTE(R) \
- ((R)->type == ZEBRA_ROUTE_KERNEL)
-
/* This function verifies reachability of one given nexthop, which can be
* numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
* in nexthop->flags field. The nexthop->ifindex will be updated
@@ -959,7 +956,8 @@ static int nexthop_active_update(struct route_node *rn, struct route_entry *re)
* decision point.
*/
new_active = nexthop_active_check(rn, re, nexthop);
- if (new_active && re->nexthop_active_num >= multipath_num) {
+ if (new_active
+ && re->nexthop_active_num >= zrouter.multipath_num) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
new_active = 0;
}
@@ -1172,7 +1170,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
*/
static int rib_can_delete_dest(rib_dest_t *dest)
{
- if (dest->routes) {
+ if (re_list_first(&dest->routes)) {
return 0;
}
@@ -1200,7 +1198,6 @@ static int rib_can_delete_dest(rib_dest_t *dest)
void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq)
{
rib_dest_t *dest = rib_dest_from_rnode(rn);
- struct listnode *node, *nnode;
struct rnh *rnh;
/*
@@ -1232,7 +1229,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq)
* nht resolution and as such we need to call the
* nexthop tracking evaluation code
*/
- for (ALL_LIST_ELEMENTS(dest->nht, node, nnode, rnh)) {
+ frr_each (rnh_list, &dest->nht, rnh) {
struct zebra_vrf *zvrf =
zebra_vrf_lookup_by_id(rnh->vrf_id);
struct prefix *p = &rnh->node->p;
@@ -1308,7 +1305,7 @@ int rib_gc_dest(struct route_node *rn)
zebra_rib_evaluate_rn_nexthops(rn, zebra_router_get_next_sequence());
dest->rnode = NULL;
- list_delete(&dest->nht);
+ rnh_list_fini(&dest->nht);
XFREE(MTYPE_RIB_DEST, dest);
rn->info = NULL;
@@ -2353,7 +2350,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn)
rib_dest_t *dest;
dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t));
- dest->nht = list_new();
+ rnh_list_init(&dest->nht);
route_lock_node(rn); /* rn route table reference */
rn->info = dest;
dest->rnode = rn;
@@ -2401,7 +2398,6 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn)
/* Add RE to head of the route node. */
static void rib_link(struct route_node *rn, struct route_entry *re, int process)
{
- struct route_entry *head;
rib_dest_t *dest;
afi_t afi;
const char *rmap_name;
@@ -2416,12 +2412,7 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process)
dest = zebra_rib_create_dest(rn);
}
- head = dest->routes;
- if (head) {
- head->prev = re;
- }
- re->next = head;
- dest->routes = re;
+ re_list_add_head(&dest->routes, re);
afi = (rn->p.family == AF_INET)
? AFI_IP
@@ -2471,14 +2462,7 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
dest = rib_dest_from_rnode(rn);
- if (re->next)
- re->next->prev = re->prev;
-
- if (re->prev)
- re->prev->next = re->next;
- else {
- dest->routes = re->next;
- }
+ re_list_del(&dest->routes, re);
if (dest->selected_fib == re)
dest->selected_fib = NULL;
@@ -2655,7 +2639,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
{
struct route_table *table;
struct route_node *rn;
- unsigned changed = 0;
rib_dest_t *dest;
if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) {
@@ -2682,7 +2665,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
* of the rest of the RE.
*/
if (dest->selected_fib) {
- changed = 1;
if (IS_ZEBRA_DEBUG_RIB) {
char buf[PREFIX_STRLEN];
@@ -2692,9 +2674,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
route_entry_dump(&rn->p, NULL, dest->selected_fib);
}
rib_uninstall(rn, dest->selected_fib);
- }
- if (changed)
rib_queue_add(rn);
+ }
}
int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
@@ -2874,7 +2855,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
break;
}
for (ALL_NEXTHOPS(re->ng, rtnh))
- if (nexthop_same_no_recurse(rtnh, nh)) {
+ /*
+ * No guarantee all kernel send nh with labels
+ * on delete.
+ */
+ if (nexthop_same_no_labels(rtnh, nh)) {
same = re;
break;
}
@@ -3018,7 +3003,7 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
re->table = table_id;
re->vrf_id = vrf_id;
re->nexthop_num = 0;
- re->uptime = time(NULL);
+ re->uptime = monotime(NULL);
re->tag = tag;
/* Add nexthop. */
@@ -3069,6 +3054,8 @@ void rib_update_table(struct route_table *table, rib_update_event_t event)
continue;
if (re->type != ZEBRA_ROUTE_STATIC) {
+ SET_FLAG(re->status,
+ ROUTE_ENTRY_CHANGED);
rib_queue_add(rn);
continue;
}
@@ -3082,8 +3069,11 @@ void rib_update_table(struct route_table *table, rib_update_event_t event)
* gateway, NHT will
* take care.
*/
- if (nh)
+ if (nh) {
+ SET_FLAG(re->status,
+ ROUTE_ENTRY_CHANGED);
rib_queue_add(rn);
+ }
}
break;
@@ -3093,8 +3083,12 @@ void rib_update_table(struct route_table *table, rib_update_event_t event)
* protocol in
* some cases (TODO).
*/
- if (rnode_to_ribs(rn))
+ if (rnode_to_ribs(rn)) {
+ RNODE_FOREACH_RE_SAFE (rn, re, next)
+ SET_FLAG(re->status,
+ ROUTE_ENTRY_CHANGED);
rib_queue_add(rn);
+ }
break;
default:
@@ -3137,6 +3131,7 @@ void rib_sweep_table(struct route_table *table)
for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
RNODE_FOREACH_RE_SAFE (rn, re, next) {
+
if (IS_ZEBRA_DEBUG_RIB)
route_entry_dump(&rn->p, NULL, re);
@@ -3147,6 +3142,14 @@ void rib_sweep_table(struct route_table *table)
continue;
/*
+ * If routes are older than startup_time then
+ * we know we read them in from the kernel.
+ * As such we can safely remove them.
+ */
+ if (zrouter.startup_time < re->uptime)
+ continue;
+
+ /*
* So we are starting up and have received
* routes from the kernel that we have installed
* from a previous run of zebra but not cleaned
@@ -3175,7 +3178,7 @@ void rib_sweep_table(struct route_table *table)
}
/* Sweep all RIB tables. */
-void rib_sweep_route(void)
+int rib_sweep_route(struct thread *t)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
@@ -3189,6 +3192,8 @@ void rib_sweep_route(void)
}
zebra_router_sweep_route();
+
+ return 0;
}
/* Remove specific by protocol routes from 'table'. */
@@ -3219,18 +3224,23 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
+ struct other_route_table *ort;
unsigned long cnt = 0;
- RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id)
- if ((zvrf = vrf->info) != NULL)
- cnt += rib_score_proto_table(
- proto, instance,
- zvrf->table[AFI_IP][SAFI_UNICAST])
- + rib_score_proto_table(
- proto, instance,
- zvrf->table[AFI_IP6][SAFI_UNICAST]);
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ cnt += rib_score_proto_table(proto, instance,
+ zvrf->table[AFI_IP][SAFI_UNICAST])
+ + rib_score_proto_table(
+ proto, instance,
+ zvrf->table[AFI_IP6][SAFI_UNICAST]);
- cnt += zebra_router_score_proto(proto, instance);
+ frr_each(otable, &zvrf->other_tables, ort) cnt +=
+ rib_score_proto_table(proto, instance, ort->table);
+ }
return cnt;
}
@@ -3380,9 +3390,33 @@ static int rib_dplane_results(struct dplane_ctx_q *ctxlist)
return 0;
}
+/*
+ * Ensure there are no empty slots in the route_info array.
+ * Every route type in zebra should be present there.
+ */
+static void check_route_info(void)
+{
+ int len = array_size(route_info);
+
+ /*
+ * ZEBRA_ROUTE_SYSTEM is special cased since
+ * its key is 0 anyway.
+ *
+ * ZEBRA_ROUTE_ALL is also ignored.
+ */
+ for (int i = 0; i < len; i++) {
+ if (i == ZEBRA_ROUTE_SYSTEM || i == ZEBRA_ROUTE_ALL)
+ continue;
+ assert(route_info[i].key);
+ assert(route_info[i].meta_q_map < MQ_SIZE);
+ }
+}
+
/* Routing information base initialize. */
void rib_init(void)
{
+ check_route_info();
+
rib_queue_init();
/* Init dataplane, and register for results */
@@ -3451,7 +3485,7 @@ struct route_table *rib_tables_iter_next(rib_tables_iter_t *iter)
while (1) {
while (iter->afi_safi_ix
- < (int)ZEBRA_NUM_OF(afi_safis)) {
+ < (int)array_size(afi_safis)) {
table = zebra_vrf_table(
afi_safis[iter->afi_safi_ix].afi,
afi_safis[iter->afi_safi_ix].safi,
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index 220a8006d0..2917d0e7a8 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -119,7 +119,7 @@ static void zebra_rnh_remove_from_routing_table(struct rnh *rnh)
}
dest = rib_dest_from_rnode(rn);
- listnode_delete(dest->nht, rnh);
+ rnh_list_del(&dest->nht, rnh);
route_unlock_node(rn);
}
@@ -145,7 +145,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh)
}
dest = rib_dest_from_rnode(rn);
- listnode_add(dest->nht, rnh);
+ rnh_list_add_tail(&dest->nht, rnh);
route_unlock_node(rn);
}
@@ -251,7 +251,7 @@ void zebra_free_rnh(struct rnh *rnh)
route_unlock_node(rern);
dest = rib_dest_from_rnode(rern);
- listnode_delete(dest->nht, rnh);
+ rnh_list_del(&dest->nht, rnh);
}
}
free_state(rnh->vrf_id, rnh->state, rnh->node);
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index 9cd9116eed..95a3941181 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -29,40 +29,6 @@
extern "C" {
#endif
-typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t;
-
-/* Nexthop structure. */
-struct rnh {
- uint8_t flags;
-
-#define ZEBRA_NHT_CONNECTED 0x1
-#define ZEBRA_NHT_DELETED 0x2
-#define ZEBRA_NHT_EXACT_MATCH 0x4
-
- /* VRF identifier. */
- vrf_id_t vrf_id;
-
- afi_t afi;
-
- rnh_type_t type;
-
- uint32_t seqno;
-
- struct route_entry *state;
- struct prefix resolved_route;
- struct list *client_list;
-
- /* pseudowires dependent on this nh */
- struct list *zebra_pseudowire_list;
-
- struct route_node *node;
-
- /*
- * if this has been filtered for the client
- */
- int filtered[ZEBRA_ROUTE_MAX];
-};
-
extern int zebra_rnh_ip_default_route;
extern int zebra_rnh_ipv6_default_route;
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
index 5d1cbbe781..f48bf3b033 100644
--- a/zebra/zebra_routemap.c
+++ b/zebra/zebra_routemap.c
@@ -705,6 +705,9 @@ DEFPY (ip_protocol,
{
int ret, rtype;
+ assert(proto);
+ assert(rmap);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -737,6 +740,8 @@ DEFPY (no_ip_protocol,
{
int ret, rtype;
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -781,6 +786,9 @@ DEFPY (ipv6_protocol,
{
int ret, rtype;
+ assert(rmap);
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -813,6 +821,8 @@ DEFPY (no_ipv6_protocol,
{
int ret, rtype;
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -858,6 +868,9 @@ DEFPY (ip_protocol_nht_rmap,
int ret, rtype;
+ assert(proto);
+ assert(rmap);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -890,6 +903,8 @@ DEFPY (no_ip_protocol_nht_rmap,
{
int ret, rtype;
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -935,6 +950,9 @@ DEFPY (ipv6_protocol_nht_rmap,
{
int ret, rtype;
+ assert(rmap);
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -967,6 +985,8 @@ DEFPY (no_ipv6_protocol_nht_rmap,
{
int ret, rtype;
+ assert(proto);
+
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
@@ -1798,8 +1818,7 @@ static void zebra_route_map_delete(const char *rmap_name)
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
}
-static void zebra_route_map_event(route_map_event_t event,
- const char *rmap_name)
+static void zebra_route_map_event(const char *rmap_name)
{
if (route_map_mark_updated(rmap_name) == 0)
zebra_route_map_mark_update(rmap_name);
diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
index a81752d205..610d51d3ea 100644
--- a/zebra/zebra_router.c
+++ b/zebra/zebra_router.c
@@ -30,7 +30,9 @@
#include "zebra_vxlan.h"
#include "zebra_mlag.h"
-struct zebra_router zrouter;
+struct zebra_router zrouter = {
+ .multipath_num = MULTIPATH_NUM,
+};
static inline int
zebra_router_table_entry_compare(const struct zebra_router_table *e1,
@@ -117,19 +119,6 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf,
return zrt->table;
}
-unsigned long zebra_router_score_proto(uint8_t proto, unsigned short instance)
-{
- struct zebra_router_table *zrt;
- unsigned long cnt = 0;
-
- RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) {
- if (zrt->ns_id != NS_DEFAULT)
- continue;
- cnt += rib_score_proto_table(proto, instance, zrt->table);
- }
- return cnt;
-}
-
void zebra_router_show_table_summary(struct vty *vty)
{
struct zebra_router_table *zrt;
@@ -226,7 +215,6 @@ void zebra_router_init(void)
{
zrouter.sequence_num = 0;
- zrouter.rtm_table_default = 0;
zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS;
zebra_vxlan_init();
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 72b5e9b9b1..6c9f3a0f28 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -89,9 +89,6 @@ struct zebra_router {
/* A sequence number used for tracking routes */
_Atomic uint32_t sequence_num;
- /* The default table used for this router */
- uint32_t rtm_table_default;
-
/* rib work queue */
#define ZEBRA_RIB_PROCESS_HOLD_TIME 10
#define ZEBRA_RIB_PROCESS_RETRY_TIME 1
@@ -113,8 +110,17 @@ struct zebra_router {
* The EVPN instance, if any
*/
struct zebra_vrf *evpn_vrf;
+
+ uint32_t multipath_num;
+
+ /*
+ * Time for when we sweep the rib from old routes
+ */
+ time_t startup_time;
};
+#define GRACEFUL_RESTART_TIME 60
+
extern struct zebra_router zrouter;
extern void zebra_router_init(void);
@@ -131,8 +137,6 @@ extern void zebra_router_release_table(struct zebra_vrf *zvrf, uint32_t tableid,
extern int zebra_router_config_write(struct vty *vty);
-extern unsigned long zebra_router_score_proto(uint8_t proto,
- unsigned short instance);
extern void zebra_router_sweep_route(void);
extern void zebra_router_show_table_summary(struct vty *vty);
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index 2d721ec8a1..6343054943 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -47,6 +47,8 @@ static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
static void zebra_rnhtable_node_cleanup(struct route_table *table,
struct route_node *node);
+DEFINE_MTYPE_STATIC(ZEBRA, OTHER_TABLE, "Other Table");
+
/* VRF information update. */
static void zebra_vrf_add_update(struct zebra_vrf *zvrf)
{
@@ -93,6 +95,9 @@ static int zebra_vrf_new(struct vrf *vrf)
zvrf = zebra_vrf_alloc();
vrf->info = zvrf;
zvrf->vrf = vrf;
+
+ otable_init(&zvrf->other_tables);
+
router_id_init(zvrf);
return 0;
}
@@ -226,6 +231,7 @@ static int zebra_vrf_disable(struct vrf *vrf)
static int zebra_vrf_delete(struct vrf *vrf)
{
struct zebra_vrf *zvrf = vrf->info;
+ struct other_route_table *otable;
struct route_table *table;
afi_t afi;
safi_t safi;
@@ -274,11 +280,22 @@ static int zebra_vrf_delete(struct vrf *vrf)
route_table_finish(zvrf->import_check_table[afi]);
}
+ otable = otable_pop(&zvrf->other_tables);
+ while (otable) {
+ zebra_router_release_table(zvrf, otable->table_id,
+ otable->afi, otable->safi);
+ XFREE(MTYPE_OTHER_TABLE, otable);
+
+ otable = otable_pop(&zvrf->other_tables);
+ }
+
/* Cleanup EVPN states for vrf */
zebra_vxlan_vrf_delete(zvrf);
list_delete_all_node(zvrf->rid_all_sorted_list);
list_delete_all_node(zvrf->rid_lo_sorted_list);
+
+ otable_fini(&zvrf->other_tables);
XFREE(MTYPE_ZEBRA_VRF, zvrf);
vrf->info = NULL;
@@ -320,27 +337,34 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
vrf_id_t vrf_id,
uint32_t table_id)
{
- struct route_table *table = NULL;
+ struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
+ struct other_route_table ort, *otable;
+ struct route_table *table;
+
+ if (!zvrf)
+ return NULL;
if (afi >= AFI_MAX || safi >= SAFI_MAX)
return NULL;
- if (vrf_id == VRF_DEFAULT) {
- if (table_id == RT_TABLE_MAIN
- || table_id == zrouter.rtm_table_default)
- table = zebra_vrf_table(afi, safi, vrf_id);
- else
- table = zebra_vrf_other_route_table(afi, table_id,
- vrf_id);
- } else if (vrf_is_backend_netns()) {
- if (table_id == RT_TABLE_MAIN
- || table_id == zrouter.rtm_table_default)
- table = zebra_vrf_table(afi, safi, vrf_id);
- else
- table = zebra_vrf_other_route_table(afi, table_id,
- vrf_id);
- } else
- table = zebra_vrf_table(afi, safi, vrf_id);
+ if (table_id == zvrf->table_id)
+ return zebra_vrf_table(afi, safi, vrf_id);
+
+ ort.afi = afi;
+ ort.safi = safi;
+ ort.table_id = table_id;
+ otable = otable_find(&zvrf->other_tables, &ort);
+ if (otable)
+ return otable->table;
+
+ table = zebra_router_get_table(zvrf, table_id, afi, safi);
+
+ otable = XCALLOC(MTYPE_OTHER_TABLE, sizeof(*otable));
+ otable->afi = afi;
+ otable->safi = safi;
+ otable->table_id = table_id;
+ otable->table = table;
+ otable_add(&zvrf->other_tables, otable);
return table;
}
@@ -357,7 +381,7 @@ void zebra_rtable_node_cleanup(struct route_table *table,
if (node->info) {
rib_dest_t *dest = node->info;
- list_delete(&dest->nht);
+ rnh_list_fini(&dest->nht);
XFREE(MTYPE_RIB_DEST, node->info);
}
}
@@ -440,34 +464,6 @@ struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id)
return zvrf->table[afi][safi];
}
-struct route_table *zebra_vrf_other_route_table(afi_t afi, uint32_t table_id,
- vrf_id_t vrf_id)
-{
- struct zebra_vrf *zvrf;
-
- zvrf = vrf_info_lookup(vrf_id);
- if (!zvrf)
- return NULL;
-
- if (afi >= AFI_MAX)
- return NULL;
-
- if ((table_id != RT_TABLE_MAIN)
- && (table_id != zrouter.rtm_table_default)) {
- if (zvrf->table_id == RT_TABLE_MAIN ||
- zvrf->table_id == zrouter.rtm_table_default) {
- /* this VRF use default table
- * so in all cases, it does not use specific table
- * so it is possible to configure tables in this VRF
- */
- return zebra_router_get_table(zvrf, table_id, afi,
- SAFI_UNICAST);
- }
- }
-
- return zvrf->table[afi][SAFI_UNICAST];
-}
-
static int vrf_config_write(struct vty *vty)
{
struct vrf *vrf;
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index c7a64d300a..febaf3c844 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -43,6 +43,18 @@ struct zebra_rmap {
struct route_map *map;
};
+PREDECL_RBTREE_UNIQ(otable);
+
+struct other_route_table {
+ struct otable_item next;
+
+ afi_t afi;
+ safi_t safi;
+ uint32_t table_id;
+
+ struct route_table *table;
+};
+
/* Routing table instance. */
struct zebra_vrf {
/* Back pointer */
@@ -69,6 +81,8 @@ struct zebra_vrf {
/* Import check table (used mostly by BGP */
struct route_table *import_check_table[AFI_MAX];
+ struct otable_head other_tables;
+
/* 2nd pointer type used primarily to quell a warning on
* ALL_LIST_ELEMENTS_RO
*/
@@ -192,6 +206,25 @@ static inline bool zvrf_is_active(struct zebra_vrf *zvrf)
return zvrf->vrf->status & VRF_ACTIVE;
}
+static inline int
+zvrf_other_table_compare_func(const struct other_route_table *a,
+ const struct other_route_table *b)
+{
+ if (a->afi != b->afi)
+ return a->afi - b->afi;
+
+ if (a->safi != b->safi)
+ return a->safi - b->safi;
+
+ if (a->table_id != b->table_id)
+ return a->table_id - b->table_id;
+
+ return 0;
+}
+
+DECLARE_RBTREE_UNIQ(otable, struct other_route_table, next,
+ zvrf_other_table_compare_func)
+
struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
vrf_id_t vrf_id,
uint32_t table_id);
@@ -202,8 +235,6 @@ extern struct zebra_vrf *zebra_vrf_lookup_by_name(const char *);
extern struct zebra_vrf *zebra_vrf_alloc(void);
extern struct route_table *zebra_vrf_table(afi_t, safi_t, vrf_id_t);
-extern struct route_table *
-zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, vrf_id_t vrf_id);
extern int zebra_vrf_has_config(struct zebra_vrf *zvrf);
extern void zebra_vrf_init(void);
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 8cde07a109..ece8f40dcf 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -62,7 +62,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
bool supernets_only, int type,
unsigned short ospf_instance_id);
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
- int mcast);
+ int mcast, bool use_fib);
static void vty_show_ip_route_summary(struct vty *vty,
struct route_table *table);
static void vty_show_ip_route_summary_prefix(struct vty *vty,
@@ -154,7 +154,7 @@ DEFUN (show_ip_rpf_addr,
re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn);
if (re)
- vty_show_ip_route_detail(vty, rn, 1);
+ vty_show_ip_route_detail(vty, rn, 1, false);
else
vty_out(vty, "%% No match for RPF lookup\n");
@@ -186,14 +186,24 @@ static char re_status_output_char(struct route_entry *re, struct nexthop *nhop)
/* New RIB. Detailed information for IPv4 route. */
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
- int mcast)
+ int mcast, bool use_fib)
{
struct route_entry *re;
struct nexthop *nexthop;
char buf[SRCDEST2STR_BUFFER];
struct zebra_vrf *zvrf;
+ rib_dest_t *dest;
+
+ dest = rib_dest_from_rnode(rn);
RNODE_FOREACH_RE (rn, re) {
+ /*
+ * If re not selected for forwarding, skip re
+ * for "show ip/ipv6 fib <prefix>"
+ */
+ if (use_fib && re != dest->selected_fib)
+ continue;
+
const char *mcast_info = "";
if (mcast) {
rib_table_info_t *info = srcdest_rnode_table_info(rn);
@@ -230,7 +240,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
time_t uptime;
struct tm *tm;
- uptime = time(NULL);
+ uptime = monotime(NULL);
uptime -= re->uptime;
tm = gmtime(&uptime);
@@ -385,7 +395,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
struct tm *tm;
rib_dest_t *dest = rib_dest_from_rnode(rn);
- uptime = time(NULL);
+ uptime = monotime(NULL);
uptime -= re->uptime;
tm = gmtime(&uptime);
@@ -749,17 +759,26 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
}
static void vty_show_ip_route_detail_json(struct vty *vty,
- struct route_node *rn)
+ struct route_node *rn, bool use_fib)
{
json_object *json = NULL;
json_object *json_prefix = NULL;
struct route_entry *re;
char buf[BUFSIZ];
+ rib_dest_t *dest;
+
+ dest = rib_dest_from_rnode(rn);
json = json_object_new_object();
json_prefix = json_object_new_array();
RNODE_FOREACH_RE (rn, re) {
+ /*
+ * If re not selected for forwarding, skip re
+ * for "show ip/ipv6 fib <prefix> json"
+ */
+ if (use_fib && re != dest->selected_fib)
+ continue;
vty_show_ip_route(vty, rn, re, json_prefix);
}
@@ -1177,12 +1196,12 @@ DEFPY (show_route_detail,
show_route_detail_cmd,
"show\
<\
- ip$ipv4 route [vrf <NAME$vrf_name|all$vrf_all>]\
+ ip$ipv4 <fib$fib|route> [vrf <NAME$vrf_name|all$vrf_all>]\
<\
A.B.C.D$address\
|A.B.C.D/M$prefix\
>\
- |ipv6$ipv6 route [vrf <NAME$vrf_name|all$vrf_all>]\
+ |ipv6$ipv6 <fib$fib|route> [vrf <NAME$vrf_name|all$vrf_all>]\
<\
X:X::X:X$address\
|X:X::X:X/M$prefix\
@@ -1191,12 +1210,14 @@ DEFPY (show_route_detail,
[json$json]",
SHOW_STR
IP_STR
+ "IPv6 forwarding table\n"
"IP routing table\n"
VRF_FULL_CMD_HELP_STR
"Network in the IP routing table to display\n"
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
IP6_STR
- "IP routing table\n"
+ "IPv6 forwarding table\n"
+ "IPv6 routing table\n"
VRF_FULL_CMD_HELP_STR
"IPv6 Address\n"
"IPv6 prefix\n"
@@ -1206,6 +1227,9 @@ DEFPY (show_route_detail,
struct route_table *table;
struct prefix p;
struct route_node *rn;
+ bool use_fib = !!fib;
+ rib_dest_t *dest;
+ bool network_found = false;
if (address_str)
prefix_str = address_str;
@@ -1231,13 +1255,35 @@ DEFPY (show_route_detail,
continue;
}
+ dest = rib_dest_from_rnode(rn);
+ if (use_fib && !dest->selected_fib) {
+ route_unlock_node(rn);
+ continue;
+ }
+
+ network_found = true;
if (json)
- vty_show_ip_route_detail_json(vty, rn);
+ vty_show_ip_route_detail_json(vty, rn,
+ use_fib);
else
- vty_show_ip_route_detail(vty, rn, 0);
+ vty_show_ip_route_detail(vty, rn, 0, use_fib);
route_unlock_node(rn);
}
+
+ if (!network_found) {
+ if (json)
+ vty_out(vty, "{}\n");
+ else {
+ if (use_fib)
+ vty_out(vty,
+ "%% Network not in FIB\n");
+ else
+ vty_out(vty,
+ "%% Network not in RIB\n");
+ }
+ return CMD_WARNING;
+ }
} else {
vrf_id_t vrf_id = VRF_DEFAULT;
@@ -1249,20 +1295,30 @@ DEFPY (show_route_detail,
return CMD_SUCCESS;
rn = route_node_match(table, &p);
- if (!rn) {
- vty_out(vty, "%% Network not in table\n");
- return CMD_WARNING;
- }
- if (!address_str && rn->p.prefixlen != p.prefixlen) {
- vty_out(vty, "%% Network not in table\n");
- route_unlock_node(rn);
+ if (rn)
+ dest = rib_dest_from_rnode(rn);
+
+ if (!rn || (!address_str && rn->p.prefixlen != p.prefixlen) ||
+ (use_fib && dest && !dest->selected_fib)) {
+ if (json)
+ vty_out(vty, "{}\n");
+ else {
+ if (use_fib)
+ vty_out(vty,
+ "%% Network not in FIB\n");
+ else
+ vty_out(vty,
+ "%% Network not in table\n");
+ }
+ if (rn)
+ route_unlock_node(rn);
return CMD_WARNING;
}
if (json)
- vty_show_ip_route_detail_json(vty, rn);
+ vty_show_ip_route_detail_json(vty, rn, use_fib);
else
- vty_show_ip_route_detail(vty, rn, 0);
+ vty_show_ip_route_detail(vty, rn, 0, use_fib);
route_unlock_node(rn);
}
@@ -2556,40 +2612,6 @@ static int config_write_protocol(struct vty *vty)
return 1;
}
-#ifdef HAVE_NETLINK
-/* Display default rtm_table for all clients. */
-DEFUN (show_table,
- show_table_cmd,
- "show table",
- SHOW_STR
- "default routing table to use for all clients\n")
-{
- vty_out(vty, "table %d\n", zrouter.rtm_table_default);
- return CMD_SUCCESS;
-}
-
-DEFUN (config_table,
- config_table_cmd,
- "table TABLENO",
- "Configure target kernel routing table\n"
- "TABLE integer\n")
-{
- zrouter.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_table,
- no_config_table_cmd,
- "no table [TABLENO]",
- NO_STR
- "Configure target kernel routing table\n"
- "TABLE integer\n")
-{
- zrouter.rtm_table_default = 0;
- return CMD_SUCCESS;
-}
-#endif
-
DEFUN (show_zebra,
show_zebra_cmd,
"show zebra",
@@ -2833,8 +2855,6 @@ DEFUN (zebra_show_routing_tables_summary,
/* Table configuration write function. */
static int config_write_table(struct vty *vty)
{
- if (zrouter.rtm_table_default)
- vty_out(vty, "table %d\n", zrouter.rtm_table_default);
return 0;
}
@@ -2938,12 +2958,6 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &no_ip_forwarding_cmd);
install_element(ENABLE_NODE, &show_zebra_cmd);
-#ifdef HAVE_NETLINK
- install_element(VIEW_NODE, &show_table_cmd);
- install_element(CONFIG_NODE, &config_table_cmd);
- install_element(CONFIG_NODE, &no_config_table_cmd);
-#endif /* HAVE_NETLINK */
-
install_element(VIEW_NODE, &show_ipv6_forwarding_cmd);
install_element(CONFIG_NODE, &ipv6_forwarding_cmd);
install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd);
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 4f6e4e8597..f2f8a2a059 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -93,7 +93,7 @@ static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]);
static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
struct ipaddr *ip, uint8_t flags,
uint32_t seq, int state, uint16_t cmd);
-static unsigned int neigh_hash_keymake(void *p);
+static unsigned int neigh_hash_keymake(const void *p);
static void *zvni_neigh_alloc(void *p);
static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
struct ethaddr *mac);
@@ -143,13 +143,12 @@ static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
static void *zl3vni_alloc(void *p);
static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
static int zl3vni_del(zebra_l3vni_t *zl3vni);
-static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t);
static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
-static unsigned int mac_hash_keymake(void *p);
+static unsigned int mac_hash_keymake(const void *p);
static bool mac_cmp(const void *p1, const void *p2);
static void *zvni_mac_alloc(void *p);
static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr);
@@ -168,7 +167,7 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac);
static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac);
static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt);
-static unsigned int vni_hash_keymake(void *p);
+static unsigned int vni_hash_keymake(const void *p);
static void *zvni_alloc(void *p);
static zebra_vni_t *zvni_lookup(vni_t vni);
static zebra_vni_t *zvni_add(vni_t vni);
@@ -213,7 +212,7 @@ static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
bool do_dad,
bool *is_dup_detect,
bool is_local);
-static unsigned int zebra_vxlan_sg_hash_key_make(void *p);
+static unsigned int zebra_vxlan_sg_hash_key_make(const void *p);
static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2);
static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf,
struct in_addr sip, struct in_addr mcast_grp);
@@ -2158,16 +2157,16 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
/*
* Make hash key for neighbors.
*/
-static unsigned int neigh_hash_keymake(void *p)
+static unsigned int neigh_hash_keymake(const void *p)
{
- zebra_neigh_t *n = p;
- struct ipaddr *ip = &n->ip;
+ const zebra_neigh_t *n = p;
+ const struct ipaddr *ip = &n->ip;
if (IS_IPADDR_V4(ip))
return jhash_1word(ip->ipaddr_v4.s_addr, 0);
return jhash2(ip->ipaddr_v6.s6_addr32,
- ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0);
+ array_size(ip->ipaddr_v6.s6_addr32), 0);
}
/*
@@ -3296,9 +3295,9 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni,
/*
* Make hash key for MAC.
*/
-static unsigned int mac_hash_keymake(void *p)
+static unsigned int mac_hash_keymake(const void *p)
{
- zebra_mac_t *pmac = p;
+ const zebra_mac_t *pmac = p;
const void *pnt = (void *)pmac->macaddr.octet;
return jhash(pnt, ETH_ALEN, 0xa5a5a55a);
@@ -3815,7 +3814,7 @@ static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp)
/*
* Hash function for VNI.
*/
-static unsigned int vni_hash_keymake(void *p)
+static unsigned int vni_hash_keymake(const void *p)
{
const zebra_vni_t *zvni = p;
@@ -4688,7 +4687,7 @@ static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *ip)
/*
* Hash function for L3 VNI.
*/
-static unsigned int l3vni_hash_keymake(void *p)
+static unsigned int l3vni_hash_keymake(const void *p)
{
const zebra_l3vni_t *zl3vni = p;
@@ -4842,7 +4841,7 @@ static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
}
-static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
+zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
{
struct zebra_vrf *zvrf = NULL;
@@ -7505,9 +7504,9 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Add/update remote MAC %s intf %s(%u) VNI %u - del local",
+ "Add/update remote MAC %s intf %s(%u) VNI %u flags 0x%x - del local",
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, vni);
+ ifp->ifindex, vni, mac->flags);
/* Remove MAC from BGP. */
zvni_mac_send_del_to_client(zvni->vni, macaddr);
@@ -7520,6 +7519,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
zvni_mac_del(zvni, mac);
} else {
UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
}
@@ -7603,11 +7603,6 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
return -1;
}
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, vid, zvni->vni);
-
/* If entry doesn't exist, nothing to do. */
mac = zvni_mac_lookup(zvni, macaddr);
if (!mac)
@@ -7617,6 +7612,11 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
return 0;
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u flags 0x%x",
+ prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
+ ifp->ifindex, vid, zvni->vni, mac->flags);
+
/* Update all the neigh entries associated with this mac */
zvni_process_neigh_on_local_mac_del(zvni, mac);
@@ -7631,6 +7631,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
zvni_mac_del(zvni, mac);
} else {
UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
}
@@ -7858,12 +7859,18 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
s = msg;
while (l < hdr->length) {
+ int flood_control __attribute__((unused));
+
/* Obtain each remote VTEP and process. */
STREAM_GETL(s, vni);
l += 4;
STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
+ /* Flood control is intentionally ignored right now */
+ STREAM_GETL(s, flood_control);
+ l += 4;
+
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Recv VTEP_DEL %s VNI %u from %s",
inet_ntoa(vtep_ip), vni,
@@ -7949,7 +7956,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
l += 4;
STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
STREAM_GETL(s, flood_control);
- l += IPV4_MAX_BYTELEN;
+ l += IPV4_MAX_BYTELEN + 4;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Recv VTEP_ADD %s VNI %u flood %d from %s",
@@ -9449,9 +9456,9 @@ static int zebra_vxlan_sg_send(struct prefix_sg *sg,
return zserv_send_message(client, s);
}
-static unsigned int zebra_vxlan_sg_hash_key_make(void *p)
+static unsigned int zebra_vxlan_sg_hash_key_make(const void *p)
{
- zebra_vxlan_sg_t *vxlan_sg = p;
+ const zebra_vxlan_sg_t *vxlan_sg = p;
return (jhash_2words(vxlan_sg->sg.src.s_addr,
vxlan_sg->sg.grp.s_addr, 0));
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index 9f945442bb..1dd42b7083 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -430,6 +430,8 @@ struct nh_walk_ctx {
struct json_object *json;
};
+extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/zserv.c b/zebra/zserv.c
index df5f236c04..fbb5af875d 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -149,8 +149,8 @@ static void zserv_event(struct zserv *client, enum zserv_event event);
* hdr (optional)
* The message header
*/
-static void zserv_log_message(const char *errmsg, struct stream *msg,
- struct zmsghdr *hdr)
+void zserv_log_message(const char *errmsg, struct stream *msg,
+ struct zmsghdr *hdr)
{
zlog_debug("Rx'd ZAPI message");
if (errmsg)
@@ -411,9 +411,6 @@ static int zserv_read(struct thread *thread)
hdr.vrf_id, hdr.length,
sock);
- if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
- zserv_log_message(NULL, client->ibuf_work, &hdr);
-
stream_set_getp(client->ibuf_work, 0);
struct stream *msg = stream_dup(client->ibuf_work);
@@ -700,9 +697,6 @@ static struct zserv *zserv_client_create(int sock)
pthread_mutex_init(&client->obuf_mtx, NULL);
client->wb = buffer_new(0);
- /* Set table number. */
- client->rtm_table = zrouter.rtm_table_default;
-
atomic_store_explicit(&client->connect_time, (uint32_t) monotime(NULL),
memory_order_relaxed);
@@ -910,7 +904,6 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client)
vty_out(vty, "------------------------ \n");
vty_out(vty, "FD: %d \n", client->sock);
- vty_out(vty, "Route Table ID: %d \n", client->rtm_table);
connect_time = (time_t) atomic_load_explicit(&client->connect_time,
memory_order_relaxed);
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 90fd195712..34965618f2 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -83,9 +83,6 @@ struct zserv {
/* Threads for the main pthread */
struct thread *t_cleanup;
- /* default routing table this client munges */
- int rtm_table;
-
/* This client's redistribute flag. */
struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX];
vrf_bitmap_t redist[AFI_MAX][ZEBRA_ROUTE_MAX];
@@ -176,8 +173,6 @@ struct zserv {
DECLARE_HOOK(zserv_client_connect, (struct zserv *client), (client));
DECLARE_KOOH(zserv_client_close, (struct zserv *client), (client));
-extern unsigned int multipath_num;
-
/*
* Initialize Zebra API server.
*
@@ -240,6 +235,22 @@ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance);
*/
extern void zserv_close_client(struct zserv *client);
+
+/*
+ * Log a ZAPI message hexdump.
+ *
+ * errmsg
+ * Error message to include with packet hexdump
+ *
+ * msg
+ * Message to log
+ *
+ * hdr
+ * Message header
+ */
+void zserv_log_message(const char *errmsg, struct stream *msg,
+ struct zmsghdr *hdr);
+
#if defined(HANDLE_ZAPI_FUZZING)
extern void zserv_read_file(char *input);
#endif