]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #4027 from pguibert6WIND/fix_interface_rtadv
authorDavid Lamparter <equinox@opensourcerouting.org>
Tue, 14 May 2019 13:53:04 +0000 (15:53 +0200)
committerGitHub <noreply@github.com>
Tue, 14 May 2019 13:53:04 +0000 (15:53 +0200)
Fix interface rtadv

287 files changed:
.gitignore
Makefile.am
babeld/babel_interface.c
babeld/babel_zebra.c
bfdd/bfd.c
bfdd/bfd.h
bfdd/bfd_packet.c
bfdd/bfdd.c
bfdd/bfdd_vty.c
bfdd/bsd.c [deleted file]
bfdd/config.c
bfdd/linux.c [deleted file]
bfdd/ptm_adapter.c
bfdd/subdir.am
bgpd/bgp_advertise.c
bgpd/bgp_advertise.h
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_bfd.c
bgpd/bgp_clist.c
bgpd/bgp_evpn.c
bgpd/bgp_evpn_vty.c
bgpd/bgp_flowspec.c
bgpd/bgp_fsm.c
bgpd/bgp_fsm.h
bgpd/bgp_label.c
bgpd/bgp_labelpool.c
bgpd/bgp_labelpool.h
bgpd/bgp_mplsvpn.c
bgpd/bgp_nht.c
bgpd/bgp_packet.c
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_rpki.c
bgpd/bgp_updgrp.c
bgpd/bgp_updgrp.h
bgpd/bgp_updgrp_adv.c
bgpd/bgp_updgrp_packet.c
bgpd/bgp_vty.c
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/rfapi/bgp_rfapi_cfg.c
bgpd/rfapi/vnc_zebra.c
configure.ac
debian/copyright
doc/developer/building-frr-for-ubuntu1804.rst
doc/developer/library.rst
doc/developer/lists.rst [new file with mode: 0644]
doc/developer/subdir.am
doc/developer/topotests.rst
doc/user/basic.rst
doc/user/bfd.rst
doc/user/bgp.rst
doc/user/ospf6d.rst
doc/user/static.rst
doc/user/zebra.rst
eigrpd/eigrp_zebra.c
grpc/Makefile [new file with mode: 0644]
grpc/frr-northbound.proto [new file with mode: 0644]
grpc/subdir.am [new file with mode: 0644]
isisd/dict.c [deleted file]
isisd/dict.h [deleted file]
isisd/isis_adjacency.c
isisd/isis_bfd.c
isisd/isis_bpf.c
isisd/isis_circuit.c
isisd/isis_circuit.h
isisd/isis_cli.c
isisd/isis_csm.c
isisd/isis_dlpi.c
isisd/isis_dr.c
isisd/isis_dynhn.c
isisd/isis_events.c
isisd/isis_lsp.c
isisd/isis_lsp.h
isisd/isis_main.c
isisd/isis_misc.c
isisd/isis_northbound.c
isisd/isis_pdu.c
isisd/isis_pfpacket.c
isisd/isis_redist.c
isisd/isis_route.c
isisd/isis_routemap.c
isisd/isis_spf.c
isisd/isis_spf_private.h
isisd/isis_te.c
isisd/isis_te.h
isisd/isis_tlvs.c
isisd/isis_tlvs.h
isisd/isis_tx_queue.c
isisd/isis_vty_fabricd.c
isisd/isis_zebra.c
isisd/isisd.c
isisd/isisd.h
isisd/subdir.am
ldpd/ldp_zebra.c
lib/atomlist.c [new file with mode: 0644]
lib/atomlist.h [new file with mode: 0644]
lib/bfd.c
lib/bfd.h
lib/command.c
lib/compiler.h
lib/fifo.h [deleted file]
lib/frratomic.h
lib/frrlua.c [new file with mode: 0644]
lib/frrlua.h [new file with mode: 0644]
lib/frrstr.c
lib/frrstr.h
lib/grammar_sandbox_main.c
lib/if.c
lib/lib_errors.c
lib/lib_errors.h
lib/libfrr.c
lib/lua.c [deleted file]
lib/lua.h [deleted file]
lib/memory.h
lib/northbound.c
lib/northbound.h
lib/northbound_cli.c
lib/northbound_confd.c
lib/northbound_grpc.cpp [new file with mode: 0644]
lib/northbound_sysrepo.c
lib/openbsd-tree.c
lib/openbsd-tree.h
lib/prefix.c
lib/prefix.h
lib/privs.c
lib/qobj.c
lib/qobj.h
lib/routemap.c
lib/routemap.h
lib/seqlock.c [new file with mode: 0644]
lib/seqlock.h [new file with mode: 0644]
lib/sockunion.c
lib/subdir.am
lib/table.c
lib/table.h
lib/thread.c
lib/thread.h
lib/typerb.c [new file with mode: 0644]
lib/typerb.h [new file with mode: 0644]
lib/typesafe.c [new file with mode: 0644]
lib/typesafe.h [new file with mode: 0644]
lib/vrf.c
lib/vty.c
lib/vty.h
lib/workqueue.c
lib/yang.c
lib/yang_translator.c
lib/yang_wrappers.c
lib/zclient.c
lib/zclient.h
lib/zebra.h
m4/ax_lua.m4 [new file with mode: 0644]
nhrpd/nhrp_interface.c
nhrpd/nhrp_main.c
nhrpd/nhrp_route.c
nhrpd/nhrp_vc.c
nhrpd/nhrpd.h
nhrpd/resolver.c
nhrpd/zbuf.c
ospf6d/ospf6_asbr.c
ospf6d/ospf6_bfd.c
ospf6d/ospf6_spf.c
ospf6d/ospf6_spf.h
ospf6d/ospf6_zebra.c
ospfd/ospf_bfd.c
ospfd/ospf_lsa.h
ospfd/ospf_lsdb.c
ospfd/ospf_lsdb.h
ospfd/ospf_packet.c
ospfd/ospf_routemap.c
ospfd/ospf_spf.c
ospfd/ospf_spf.h
ospfd/ospf_te.c
ospfd/ospf_zebra.c
pbrd/pbr_zebra.c
pimd/pim_bfd.c
pimd/pim_cmd.c
pimd/pim_ifchannel.c
pimd/pim_nht.c
pimd/pim_nht.h
pimd/pim_routemap.c
pimd/pim_upstream.c
pimd/pim_zebra.c
ripd/rip_interface.c
ripd/rip_interface.h
ripd/rip_zebra.c
ripngd/ripng_interface.c
ripngd/ripng_zebra.c
ripngd/ripngd.h
sharpd/sharp_zebra.c
staticd/static_zebra.c
tests/.gitignore
tests/isisd/test_fuzz_isis_tlv_tests.h.gz
tests/isisd/test_isis_lspdb.c
tests/lib/cxxcompat.c
tests/lib/test_atomlist.c [new file with mode: 0644]
tests/lib/test_atomlist.py [new file with mode: 0644]
tests/lib/test_seqlock.c [new file with mode: 0644]
tests/lib/test_typelist.c [new file with mode: 0644]
tests/lib/test_typelist.h [new file with mode: 0644]
tests/lib/test_typelist.py [new file with mode: 0644]
tests/subdir.am
tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref
tests/topotests/all-protocol-startup/test_all_protocol_startup.py
tests/topotests/bfd-vrf-topo1/__init__.py [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/peers.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/peers.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/peers.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r3/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/peers.json [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/r4/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg [new file with mode: 0644]
tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py [new file with mode: 0755]
tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py [changed mode: 0644->0755]
tests/topotests/bgp_ebgp_requires_policy/__init__.py [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py [new file with mode: 0644]
tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
tests/topotests/conftest.py
tests/topotests/lib/topogen.py
tools/checkpatch.pl
tools/frr-reload.py
vtysh/extract.pl.in
vtysh/vtysh.c
yang/frr-isisd.yang
yang/libyang_plugins/subdir.am
zebra/connected.c
zebra/if_netlink.c
zebra/interface.c
zebra/interface.h
zebra/kernel_socket.c
zebra/redistribute.c
zebra/rib.h
zebra/rt.h
zebra/rt_netlink.c
zebra/zapi_msg.c
zebra/zebra_dplane.c
zebra/zebra_fpm_netlink.c
zebra/zebra_fpm_protobuf.c
zebra/zebra_mpls.c
zebra/zebra_ptm.c
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_rnh.h
zebra/zebra_routemap.c
zebra/zebra_router.c
zebra/zebra_router.h
zebra/zebra_vrf.c
zebra/zebra_vty.c
zebra/zebra_vxlan.c
zebra/zserv.c
zebra/zserv.h

index 5003c97572b7b0d51aa74f5d7bd52cb965e2e8b9..6cfe23e9214740d26a7cbe8eb88b3a84d04bcd39 100644 (file)
@@ -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
index 9c6c8663ee535fa82090b56c8517acb8e0fdcc4e..11188ea157d78fb9023bf9f4482ddcc21195d6ff 100644 (file)
@@ -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
 
@@ -198,6 +199,7 @@ EXTRA_DIST += \
        doc/user/Makefile \
        eigrpd/Makefile \
        fpm/Makefile \
+       grpc/Makefile \
        isisd/Makefile \
        ldpd/Makefile \
        lib/Makefile \
index 0ff89abc495ab38fc1867c6619e1beb393d836d0..b84bc39cd8dfb58b1e6662f3b87440d2492d7bf6 100644 (file)
@@ -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;
index e909f8ea7a4e81880654fe0cf7d13747772ba2ea..d70823544af27b9565da1c00ea3062614f21df19 100644 (file)
@@ -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);
index e9645824f283d406de816b9d113388e50a7cabf9..a2e84e928d15c4cdfa4131a55edea2f79876eced 100644 (file)
@@ -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;
        }
 
@@ -725,7 +729,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 +809,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 +836,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 +851,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 +1201,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;
@@ -1322,32 +1322,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 +1516,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;
+}
index a69ff9a1a7b9a33b7756d6ef6305a125904220d2..3f3d60383248127272ded2bf08ac1640f06fd238 100644 (file)
@@ -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;
@@ -166,6 +167,7 @@ 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 */
 };
 
 #define BFD_SET_FLAG(field, flag) (field |= flag)
@@ -308,8 +310,8 @@ TAILQ_HEAD(obslist, bfd_session_observer);
 #define BFD_PKT_INFO_VAL 1
 #define BFD_IPV6_PKT_INFO_VAL 1
 #define BFD_IPV6_ONLY_VAL 1
-#define BFD_SRCPORTINIT 49142
-#define BFD_SRCPORTMAX 65536
+#define BFD_SRCPORTINIT 49152
+#define BFD_SRCPORTMAX 65535
 #define BFD_DEFDESTPORT 3784
 #define BFD_DEF_ECHO_PORT 3785
 #define BFD_DEF_MHOP_DEST_PORT 4784
@@ -378,15 +380,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 +400,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 +466,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 +512,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 +546,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 +586,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);
 
index 93677ec85aceeb7709385a07c652682b843d73aa..8edba05d126d6a531dc1cc502cc4953071733eb3 100644 (file)
 
 #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 */
@@ -441,32 +444,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 +521,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 +540,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);
        }
@@ -682,7 +688,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 +698,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 +716,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 +879,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 +915,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;
+
+       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;
 
-       sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
+       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 +945,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 +982,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 +1022,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 +1106,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 +1122,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 +1138,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 +1154,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));
 
index 6023b5e4f0a533194727e3ef67f9a2bc9844107d..218f0883c5fbd6c32e8d725a468bfdbaa2a77d89 100644 (file)
@@ -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);
 
index c13949207642fe1e81673269291b48a957afda2f..75f6632db03a80b99f0e1ba4d293e3130751ccac 100644 (file)
@@ -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 (file)
index 923fbd9..0000000
+++ /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 */
index cd57ea9fe38b03ecd01aeb7dfe1cf4744b7299d9..74e7d63d0c6ee69790f43fb9085083a94e11f231 100644 (file)
@@ -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 (file)
index 3a76b45..0000000
+++ /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 */
index 8d80b9468db5374af1dbdeda8af84b00b05f9ff2..a12a3c196b7934bb062d8d56635ae633760c88fc 100644 (file)
@@ -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);
 
@@ -182,7 +182,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);
@@ -256,7 +259,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;
@@ -355,6 +358,18 @@ 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;
+               }
+       }
 
        /* Sanity check: peer and local address must match IP types. */
        if (bpc->bpc_local.sa_sin.sin_family != 0
@@ -370,7 +385,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 +393,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 +423,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 +431,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 +446,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 +502,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 +512,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 +563,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 +607,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 +666,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 +675,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 +686,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 +726,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 +767,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);
index 334e974b048a9eb1676666b4e63c8eaa65e3d97c..e88b982ec3bfcc5d9260d936f7536a613040cb95 100644 (file)
@@ -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
index 05eeeca1564ba7459f29fe39fca42a191b37d5ce..c9f68d037bebc733f51d9997f1ff7dded09690f0 100644 (file)
@@ -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;
        }
 }
index 9aa5a0eaff518c159dde3a2c03d1a4dd600ee543..cc845b93e751a025a7120231bd834080193fec3c 100644 (file)
 #ifndef _QUAGGA_BGP_ADVERTISE_H
 #define _QUAGGA_BGP_ADVERTISE_H
 
-#include <lib/fifo.h>
+#include "lib/typesafe.h"
 
-struct update_subgroup;
+PREDECL_LIST(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_LIST(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 *,
index 167ad89a5988f026f4424db02b90505601e26c6f..cbfa166cf36620daa333b3a39d9375c7df7c12d3 100644 (file)
@@ -1256,6 +1256,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 +1289,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 +1298,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;
@@ -2681,6 +2682,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)
index 6d5c647b21c296f4d794f03cc2c398350f295ad6..f6b23a36bd6e054d912a74726d7c71c0edd26a03 100644 (file)
@@ -344,6 +344,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)
 {
index 663bc4894a842aae8e262a7c4f7e3b5e60902d07..a9c30acafbf711d7cb314ae7317aa87ea5014403 100644 (file)
@@ -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;
+       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 =
@@ -234,8 +233,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 +243,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 */
 
@@ -276,10 +274,22 @@ 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)) {
                peer->last_reset = PEER_DOWN_BFD_DOWN;
                BGP_EVENT_ADD(peer, BGP_Stop);
        }
+       if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN)
+           && peer->status != Established) {
+               if (!BGP_PEER_START_SUPPRESSED(peer)) {
+                       bgp_fsm_event_update(peer, 1);
+                       BGP_EVENT_ADD(peer, BGP_Start);
+               }
+       }
 }
 
 /*
@@ -287,8 +297,7 @@ 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;
index 7b64f349d2330d3a2f0396a4c5d8daf6c9d3d5bd..e308e963b55f47e555fbb70d370e04fe48c605db 100644 (file)
@@ -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;
 }
index 5d191a787ccf1d6f353daff3518d0c290efa5ad1..52aa923959a91cc9870799f2dfec29694b773d4c 100644 (file)
@@ -4921,7 +4921,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                if (addpath_encoded) {
                        /* When packet overflow occurs return immediately. */
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                        addpath_id = ntohl(*((uint32_t *)pnt));
                        pnt += BGP_ADDPATH_ID_LEN;
@@ -4929,14 +4929,14 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
 
                /* All EVPN NLRI types start with type and length. */
                if (pnt + 2 > lim)
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE;
 
                rtype = *pnt++;
                psize = *pnt++;
 
                /* When packet overflow occur return immediately. */
                if (pnt + psize > lim)
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                switch (rtype) {
                case BGP_EVPN_MAC_IP_ROUTE:
@@ -4947,7 +4947,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                                        EC_BGP_EVPN_FAIL,
                                        "%u:%s - Error in processing EVPN type-2 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE;
                        }
                        break;
 
@@ -4959,7 +4959,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                                        EC_BGP_PKT_PROCESS,
                                        "%u:%s - Error in processing EVPN type-3 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE;
                        }
                        break;
 
@@ -4971,7 +4971,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                                        EC_BGP_PKT_PROCESS,
                                        "%u:%s - Error in processing EVPN type-4 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE;
                        }
                        break;
 
@@ -4983,7 +4983,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                                        EC_BGP_PKT_PROCESS,
                                        "%u:%s - Error in processing EVPN type-5 NLRI size %d",
                                        peer->bgp->vrf_id, peer->host, psize);
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE;
                        }
                        break;
 
@@ -4994,9 +4994,9 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
 
        /* Packet length consistency check. */
        if (pnt != lim)
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
 
-       return 0;
+       return BGP_NLRI_PARSE_OK;
 }
 
 /*
index 4dccc89f52357473cc147b556ed9a7164f57f95d..1bd153639bf1d03dac82b6a2315d0aeb0c6fc47b 100644 (file)
@@ -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;
 
@@ -1075,28 +1060,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 +1119,39 @@ 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();
+                                               json_prefix_info =
+                                                      json_object_new_object();
+                                               json_array =
+                                                       json_object_new_array();
                                                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);
+
+                                               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);
+
                                        } else {
                                                vty_out(vty,
                                                        "Route Distinguisher: ");
@@ -1176,10 +1174,6 @@ 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 +1186,31 @@ 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);
+                       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;
 }
 
@@ -3259,9 +3271,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
index ab8bfcb770c708268db3dcb21a91e1dbd85a433f..9554638735f163f68ec77a537806259d400c517a 100644 (file)
@@ -105,14 +105,14 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
 
        if (afi == AFI_IP6) {
                flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported");
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED;
        }
 
        if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT) {
                flog_err(EC_BGP_FLOWSPEC_PACKET,
                         "BGP flowspec nlri length maximum reached (%u)",
                         packet->length);
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT;
        }
 
        for (; pnt < lim; pnt += psize) {
@@ -121,7 +121,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
 
                /* All FlowSpec NLRI begin with length. */
                if (pnt + 1 > lim)
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                psize = *pnt++;
 
@@ -131,13 +131,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
                                EC_BGP_FLOWSPEC_PACKET,
                                "Flowspec NLRI length inconsistent ( size %u seen)",
                                psize);
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
                }
                if (bgp_fs_nlri_validate(pnt, psize) < 0) {
                        flog_err(
                                EC_BGP_FLOWSPEC_PACKET,
                                "Bad flowspec format or NLRI options not supported");
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT;
                }
                p.family = AF_FLOWSPEC;
                p.prefixlen = 0;
@@ -192,8 +192,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
                        flog_err(EC_BGP_FLOWSPEC_INSTALLATION,
                                 "Flowspec NLRI failed to be %s.",
                                 attr ? "added" : "withdrawn");
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR;
                }
        }
-       return 0;
+       return BGP_NLRI_PARSE_OK;
 }
index 447d8da613722bd17eb00d61694b61707f6c7fb3..9e37a6018859b9df31e43bc26fd7564db4c0018e 100644 (file)
@@ -1754,7 +1754,7 @@ static int bgp_fsm_exeption(struct peer *peer)
        return (bgp_stop(peer));
 }
 
-void bgp_fsm_nht_update(struct peer *peer, int valid)
+void bgp_fsm_event_update(struct peer *peer, int valid)
 {
        if (!peer)
                return;
@@ -1788,7 +1788,6 @@ void bgp_fsm_nht_update(struct peer *peer, int valid)
        }
 }
 
-
 /* Finite State Machine structure */
 static const struct {
        int (*func)(struct peer *);
index d021c9884a55acd011696c522b4282ab80f0930d..3476a3c3a9816c2d7208b05264c9fb02e6cb2792 100644 (file)
@@ -57,7 +57,7 @@
 #define FSM_PEER_TRANSITIONED   3
 
 /* Prototypes. */
-extern void bgp_fsm_nht_update(struct peer *, int valid);
+extern void bgp_fsm_event_update(struct peer *peer, int valid);
 extern int bgp_event(struct thread *);
 extern int bgp_event_update(struct peer *, int event);
 extern int bgp_stop(struct peer *peer);
index a219c407dabd861f71b68aae00dc0cab95bce357..95116508427776323934dbc191d9f97de548bf2b 100644 (file)
@@ -355,7 +355,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
 
                        /* When packet overflow occurs return immediately. */
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                        addpath_id = ntohl(*((uint32_t *)pnt));
                        pnt += BGP_ADDPATH_ID_LEN;
@@ -372,7 +372,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error / L-U (prefix length %d exceeds packet size %u)",
                                peer->host, prefixlen, (uint)(lim - pnt));
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
                }
 
                /* Fill in the labels */
@@ -387,12 +387,12 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
                                 peer->host, prefixlen);
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_INVAL_NETWORK);
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH;
                }
 
                if ((afi == AFI_IP && p.prefixlen > 32)
                    || (afi == AFI_IP6 && p.prefixlen > 128))
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
 
                /* Fetch prefix from NLRI packet */
                memcpy(&p.u.prefix, pnt + llen, psize - llen);
@@ -463,8 +463,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
                        EC_BGP_UPDATE_RCV,
                        "%s [Error] Update packet error / L-U (%zu data remaining after parsing)",
                        peer->host, lim - pnt);
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
        }
 
-       return 0;
+       return BGP_NLRI_PARSE_OK;
 }
index 69dd0f9daca6214d3302853e9415e93d4d6b264b..71c0c8c7c6ff2349731d3fe2472b27051cfa0a03 100644 (file)
@@ -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);
index 0507e65489e4cd7405cd43329d775e140c7ec983..eaa3fce20b107dc793d9f8980058f12cddc83b78 100644 (file)
 #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 */
 };
index 4c4659ad543959f989200bedb8c757b68c99d140..6eddd0e1e359f35c048eb04d1481371ddcf128c1 100644 (file)
@@ -140,7 +140,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
 
                        /* When packet overflow occurs return immediately. */
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                        addpath_id = ntohl(*((uint32_t *)pnt));
                        pnt += BGP_ADDPATH_ID_LEN;
@@ -156,7 +156,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)",
                                peer->host, prefixlen);
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
                }
 
                /* sanity check against packet data */
@@ -165,7 +165,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)",
                                peer->host, prefixlen, (uint)(lim - pnt));
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
                }
 
                /* sanity check against storage for the IP address portion */
@@ -176,7 +176,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                                peer->host,
                                prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8,
                                sizeof(p.u));
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
                }
 
                /* Sanity check against max bitlen of the address family */
@@ -187,7 +187,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                                peer->host,
                                prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8,
                                p.family, prefix_blen(&p));
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
                }
 
                /* Copy label to prefix. */
@@ -245,7 +245,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                        EC_BGP_UPDATE_RCV,
                        "%s [Error] Update packet error / VPN (%zu data remaining after parsing)",
                        peer->host, lim - pnt);
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
        }
 
        return 0;
@@ -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;
 }
index 6e85abc8dfc8f260f1883b14bba0ecaede288940..7e721db49dbaf9b6a64711e824ab92e63d5a5b67 100644 (file)
@@ -793,7 +793,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
                if (BGP_DEBUG(nht, NHT))
                        zlog_debug("%s: Updating peer (%s) status with NHT",
                                   __FUNCTION__, peer->host);
-               bgp_fsm_nht_update(peer, bgp_isvalid_nexthop(bnc));
+               bgp_fsm_event_update(peer, bgp_isvalid_nexthop(bnc));
                SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
        }
 
index 7b76d7e83e881d1a7fd098bb9e23448cfb3179eb..b5934fb56e220a67359c29430c08b48a54581e3a 100644 (file)
@@ -308,7 +308,7 @@ int bgp_nlri_parse(struct peer *peer, struct attr *attr,
        case SAFI_FLOWSPEC:
                return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw);
        }
-       return -1;
+       return BGP_NLRI_PARSE_ERROR;
 }
 
 /*
@@ -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))
@@ -1568,10 +1579,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
                        nlri_ret = bgp_nlri_parse(peer, &attr, &nlris[i], 1);
                        break;
                default:
-                       nlri_ret = -1;
+                       nlri_ret = BGP_NLRI_PARSE_ERROR;
                }
 
-               if (nlri_ret < 0) {
+               if (nlri_ret < BGP_NLRI_PARSE_OK
+                   && nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) {
                        flog_err(EC_BGP_UPDATE_RCV,
                                 "%s [Error] Error parsing NLRI", peer->host);
                        if (peer->status == Established)
index 36d1369eb086e31d014b5b74093947d9c7f9514c..82a395d8e6d20221626b6f588442605616ded855 100644 (file)
@@ -1219,20 +1219,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 +1767,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;
                }
@@ -3049,6 +3039,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.
@@ -4340,7 +4346,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
 
                        /* When packet overflow occurs return immediately. */
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
-                               return -1;
+                               return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
                        addpath_id = ntohl(*((uint32_t *)pnt));
                        pnt += BGP_ADDPATH_ID_LEN;
@@ -4358,7 +4364,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error (wrong prefix length %d for afi %u)",
                                peer->host, p.prefixlen, packet->afi);
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
                }
 
                /* Packet size overflow check. */
@@ -4370,7 +4376,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error (prefix length %d overflows packet)",
                                peer->host, p.prefixlen);
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
                }
 
                /* Defensive coding, double-check the psize fits in a struct
@@ -4380,7 +4386,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                                EC_BGP_UPDATE_RCV,
                                "%s [Error] Update packet error (prefix length %d too large for prefix storage %zu)",
                                peer->host, p.prefixlen, sizeof(p.u));
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
                }
 
                /* Fetch prefix from NLRI packet. */
@@ -4445,10 +4451,14 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                                           BGP_ROUTE_NORMAL, NULL, NULL, 0,
                                           NULL);
 
-               /* Address family configuration mismatch or maximum-prefix count
-                  overflow. */
+               /* Do not send BGP notification twice when maximum-prefix count
+                * overflow. */
+               if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+                       return BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW;
+
+               /* Address family configuration mismatch. */
                if (ret < 0)
-                       return -1;
+                       return BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY;
        }
 
        /* Packet length consistency check. */
@@ -4457,10 +4467,10 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                        EC_BGP_UPDATE_RCV,
                        "%s [Error] Update packet error (prefix length mismatch with total length)",
                        peer->host);
-               return -1;
+               return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
        }
 
-       return 0;
+       return BGP_NLRI_PARSE_OK;
 }
 
 static struct bgp_static *bgp_static_new(void)
@@ -9008,6 +9018,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) {
index a559a6b58cb4390acd6045f7d5c0adf8de509190..7bbc14b46f2b11444d2a7ce32600ea02f24f40d9 100644 (file)
@@ -73,6 +73,24 @@ enum bgp_show_adj_route_type {
  */
 #define BGP_MAX_LABELS 2
 
+/* Error codes for handling NLRI */
+#define BGP_NLRI_PARSE_OK 0
+#define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1
+#define BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW -2
+#define BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH -3
+#define BGP_NLRI_PARSE_ERROR_PACKET_LENGTH -4
+#define BGP_NLRI_PARSE_ERROR_LABEL_LENGTH -5
+#define BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE -6
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE -7
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE -8
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE -9
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE -10
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED -11
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13
+#define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14
+#define BGP_NLRI_PARSE_ERROR -32
+
 /* Ancillary information to struct bgp_path_info,
  * used for uncommonly used data (aggregation, MPLS, etc.)
  * and lazily allocated to save memory.
index c276f5ef7b87ba4dbbd31f26f712f7193d3fda3b..9ff4196dae01c0b3d26b7d10b77ef5674b42ff5c 100644 (file)
@@ -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
@@ -3447,7 +3447,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);
index c63d4f9ad2fcdc58eccd50aa3c033fc9a54bcdb3..010322233d98eb15f67985d56b1747359712e39a 100644 (file)
@@ -1112,7 +1112,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 +1253,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 +1265,7 @@ DEFUN (show_rpki_cache_server,
                                        ->server_hostkey_path,
                                cache->tr_config.ssh_config
                                        ->client_privkey_path);
+#endif
                }
        }
 
index 49a435120d57d9c4ade22b839a2b855432934b80..57717bf59b5d0f22858228c087f5cfaf6618d49a 100644 (file)
@@ -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");
 
index 6b3bf9d1f77f0fcbd6f3cbe7a9cd307afacad2ef..39e67bf6084b7439d10b93cfdbc71e712de89f2a 100644 (file)
@@ -590,9 +590,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;
        }
 
index 3870df593f51a2278c1770167212aec5b8b59da7..b64c51f34140aef2caad45f8ec96aa954cb4dcbf 100644 (file)
@@ -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);
index 66e306cba23d4bad8c504066e2df6dc32e7912a7..3556b236a715d305ac2f58309c19dba3c7942499 100644 (file)
@@ -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;
index 9004926dee5edb5dcf9cdd821365b9999cd16fd5..6197a6d562cfc2059c398c6ae679ff253fba4ee6 100644 (file)
@@ -3943,6 +3943,13 @@ ALIAS_HIDDEN(neighbor_nexthop_self_force,
             "Disable the next hop calculation for this neighbor\n"
             "Set the next hop to self for reflected routes\n")
 
+ALIAS_HIDDEN(neighbor_nexthop_self_force,
+            neighbor_nexthop_self_all_hidden_cmd,
+            "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all",
+            NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+            "Disable the next hop calculation for this neighbor\n"
+            "Set the next hop to self for reflected routes\n")
+
 DEFUN (no_neighbor_nexthop_self,
        no_neighbor_nexthop_self_cmd,
        "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self",
@@ -3984,6 +3991,13 @@ ALIAS_HIDDEN(no_neighbor_nexthop_self_force,
             "Disable the next hop calculation for this neighbor\n"
             "Set the next hop to self for reflected routes\n")
 
+ALIAS_HIDDEN(no_neighbor_nexthop_self_force,
+            no_neighbor_nexthop_self_all_hidden_cmd,
+            "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all",
+            NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+            "Disable the next hop calculation for this neighbor\n"
+            "Set the next hop to self for reflected routes\n")
+
 /* neighbor as-override */
 DEFUN (neighbor_as_override,
        neighbor_as_override_cmd,
@@ -13234,6 +13248,8 @@ void bgp_vty_init(void)
        /* "neighbor next-hop-self force" commands. */
        install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd);
        install_element(BGP_NODE, &no_neighbor_nexthop_self_force_hidden_cmd);
+       install_element(BGP_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+       install_element(BGP_NODE, &no_neighbor_nexthop_self_all_hidden_cmd);
        install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_force_cmd);
        install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd);
        install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd);
@@ -14580,7 +14596,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);
@@ -14935,14 +14951,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);
index e42d6ee260358528b52e35efaacc0a602e865a6e..a45480fdc27c0d7a65c3978bc27b985893ca9fdb 100644 (file)
@@ -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;
index b2925cd5122849f17318af7fbca8ef1ae4f40254..6183bbd4714eebc6691263b7e4da1b3ec5dfdd58 100644 (file)
@@ -6186,8 +6186,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;
index 2220f0ed9abf4fd758c981288f6ac93b8ee61b35..cad33404fad3f86a1d5de781839e0e2d5df55abc 100644 (file)
@@ -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);
index b08e922962252340d462af725e748eed15972609..481500dfb445ec12130259908c14ab1a4276b89a 100644 (file)
@@ -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
index 9ae196fcb19e5fa5f2ae5ac3032f088edcec19fc..b7ddf87b43f6631402bf53d3f982cf96c6d3430b 100755 (executable)
@@ -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
@@ -447,6 +453,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 +934,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
@@ -1616,24 +1698,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 ---------------
@@ -1678,6 +1742,25 @@ if test "$enable_sysrepo" = "yes"; then
 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 ---------------
index 61d87260d82e7bc63e71cf7e4cb2c2870e43a657..d1f28a65a2f0ee89054082d6ea7731b95ffe4484 100644 (file)
@@ -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.
index 1320bda5777bbcdc6857331e014e94269b111fe2..8bdc2b9c760622de5d176154c784c23c1ff5462e 100644 (file)
@@ -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
 """"""""""""""""""""""
 
index 77b2f229b785a02e7fb2c871dbec5773df312b9d..4ba0c0ebc6cdb1766df386ceb505bbd6c33a0172 100644 (file)
@@ -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 (file)
index 0000000..987b3b7
--- /dev/null
@@ -0,0 +1,596 @@
+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.
+
+For unsorted lists, the following implementations exist:
+
+- single-linked list with tail pointer (e.g. STAILQ in BSD)
+
+- atomic single-linked list with tail pointer
+
+
+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.
+
+
+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)
+
+Cheat sheet
+-----------
+
+Available types:
+
+::
+
+   DECLARE_LIST
+   DECLARE_ATOMLIST
+
+   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 | HASH | \*_UNIQ | \*_NONUNIQ |
++====================================+======+======+=========+============+
+| _init, _fini                       | yes  | yes  | yes     | yes        |
++------------------------------------+------+------+---------+------------+
+| _first, _next, _next_safe          | yes  | yes  | yes     | yes        |
++------------------------------------+------+------+---------+------------+
+| _add_head, _add_tail, _add_after   | yes  | --   | --      | --         |
++------------------------------------+------+------+---------+------------+
+| _add                               | --   | yes  | yes     | yes        |
++------------------------------------+------+------+---------+------------+
+| _del, _pop                         | yes  | yes  | yes     | yes        |
++------------------------------------+------+------+---------+------------+
+| _find                              | --   | yes  | yes     | --         |
++------------------------------------+------+------+---------+------------+
+| _find_lt, _find_gteq               | --   | --   | yes     | yes        |
++------------------------------------+------+------+---------+------------+
+| use with for_each() macros         | 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:: for_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:: for_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:: for_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`` 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;
+
+      for_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.
+
+
+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);
+   for_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
+    * for_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
index 7ae48881abb87a338bf0d43ea8c2e13502bd1346..996f12d47f0ebd7584fc1320b1ce48f2378b71c5 100644 (file)
@@ -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 \
index 605b9c9a0cfeb588742850be73e8e94de4d13228..1e165a2444288637eb28e67687c40baf0d744150 100644 (file)
@@ -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``.
index 8fbea29ee7ec42aef3cc83d2101d35baa2877422..f55b1b9d73a77a88fd350f2a282802203f709363 100644 (file)
@@ -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.
index 986d1494a5476a782c2cb507bea660f61749082b..a7c5cf28bc5fc09996ad906fdc52bcf9e8a07e27 100644 (file)
@@ -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.
 
@@ -296,6 +295,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
 
index 35e42d95cb5176038a9e69dafb2c324df04bf315..d663b4fc10f920c979ff2b2fa5d9664bf8503568 100644 (file)
@@ -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
-
-.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as
-.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as
-
-.. index:: [no] neighbor PEER local-as AS-NUMBER
-.. clicmd:: [no] neighbor PEER local-as AS-NUMBER
+   Sets a maximum number of prefixes we can receive from a given peer. If this
+   number is exceeded, the BGP session will be destroyed.
+
+   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 [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
index 71bc04772021297ef35c843baeb4b2dca6870c60..6413c6299531639057335e3e559877498d55ae73 100644 (file)
@@ -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.
 
index 1705b6379e8aa9f0e1f129d09c22b737c68d8161..09bdc9cbea0ea238c0d71cd517514abb74b4fd2c 100644 (file)
@@ -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
index a7bf0c74da0cd6c4e91c045619c5349bc51936de..f38db9d24105d6b3fbccf827adfb51445b5f424c 100644 (file)
@@ -268,14 +268,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 +685,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 +772,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
 
index dc1ae675b04c7c653072adebe3dcf1efd54150aa..0a74e86263a320f1b65ec76a23cef07e9e186409 100644 (file)
 #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 (file)
index 0000000..8748286
--- /dev/null
@@ -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 (file)
index 0000000..d070d71
--- /dev/null
@@ -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 (file)
index 0000000..3fb163f
--- /dev/null
@@ -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 (file)
index d91f05d..0000000
+++ /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 (file)
index 32683c5..0000000
+++ /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
index 62814329eada9289378b4530175871926f5562b8..9b368cc4042675ca768a75f9b2c6ad70a6c279b0 100644 (file)
@@ -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"
index 81976f8dd293d33bf5d4475237122ac8cfb855e1..fccef0169e77c29c4a0671a94b64473394adf0de 100644 (file)
@@ -92,8 +92,7 @@ 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;
@@ -138,10 +137,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 +167,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,
index 28750278b017eee7c198153e7cc2c70bd9017d62..4e9aef47adf0f17f87fec51944dd4c498a720bea 100644 (file)
@@ -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"
index 8377638b9216af3cbde0bd36ffcfc1f98d5f2fb7..f9a0285872f652719ececbac422df9b3fe4f89bf 100644 (file)
@@ -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);
+               for_each (lspdb, &area->lspdb[level - 1], lsp) {
                        if (is_set) {
                                isis_tx_queue_add(circuit->tx_queue, lsp,
                                                  TX_LSP_NORMAL);
index e0ea4f78b44fbc26019bdd2511644dc366fd1ab3..2371c0b73a15c24444b05ef44de2fd847815ebd3 100644 (file)
@@ -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 */
index 5f7be160341a1f720744d7dddd516bd91e675124..0334b98a122fb71c54a0704b8d97cf068a266457 100644 (file)
@@ -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);
index e50f57ae1d2f9624c7802f04e1288a15d8d05f7a..88676dd990693bae2f5b0461b8920db9db599901 100644 (file)
@@ -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"
index 148b4386616e62979ade3146492a252d7d67a3e4..a96dd93804c047b974e338d7fd2d64a605dc8b02 100644 (file)
@@ -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"
index 449648656a6b617c50782dfbaf1bf4506f164c07..7be53075000fdb8200221f6cab303618d090f416 100644 (file)
@@ -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"
index 1d29d1086d8f55113bc7383d8d2fbd965953e60e..921e23d33a31ac8b752af4739beebf51b4e29ba1 100644 (file)
@@ -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"
index 4da23c591261a1b98588830877a98149003d4d39..6a5dcfb0756cc67ed65a7d97550742661c4211e5 100644 (file)
@@ -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"
index b56a56fa3ffd827f3a373ed4460185c692860f9c..1991666954b96f34f2641812e6920ee6d3ee8002 100644 (file)
@@ -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);
+       for_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;
+               for_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;
+               for_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]);
+               for_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;
index e6ea0b4eda7f47b3849a73b93c7e003b7f81e0cc..4cbca5d517c32d07a8d38b012a3375dfdaa35276 100644 (file)
 #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);
 
index e74a9baadd2a45cb41adfaa83f145870fd62dfcd..48ae760173511e4d8a18681a6e30e67355444f85 100644 (file)
@@ -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"
index 2ce68262ebd7e3822bc7df45cf66641d8df4cff2..0a42adea37ba4aade5673025b52bd10f1e02cac1 100644 (file)
@@ -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"
index 95595e37b9d904183ebb5d3a8467f912861e9274..d5cdec154b4d3c5817a8fc776016ea881f378abb 100644 (file)
@@ -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,
                },
                {
index 8e9302963dabdd6dd7c0d11d0c21adf893342474..9c633117b0f86a308cc493cb6667cf65484995c0 100644 (file)
@@ -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);
-
+               for_each (lspdb, &circuit->area->lspdb[level - 1], lsp) {
                        if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit))
                                isis_tlvs_add_lsp_entry(tlvs, lsp);
 
index 2f6526bc5efa551ac4fa6fa80b079db0716dc293..824acd0ff80f493ada4f48f60198515f5a550a21 100644 (file)
@@ -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"
index 9047707bf0dfb1bce2f5c86403c81292b955a09e..dc23e8ea499d18bad1f5efdfd6a784a73c3cc32c 100644 (file)
@@ -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"
index 1439a4229a1aec7591dba49bad4d96e30132825e..82005c911ea19db0db484fcede61ed9cf4eeff91 100644 (file)
@@ -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"
index 3c2cf7b3fcb50f13b01029628ff22db87a1f9ed3..d63676256b3b87944789b9b3027951b14ddeeea9 100644 (file)
@@ -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"
index 18eb857ec95efaea384ca4bb8240ad1e68f905ba..a28220eb260395d3b1c5013f1f14f72e6964c6b1 100644 (file)
@@ -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 "
index 3a05df3f1493fd8733c1589adc7f7e21fa1cf570..3a2a52afac28c5b04badd9edfe67d4e53ff6dd33 100644 (file)
@@ -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;
index 23a1f10a18adf3d20102a839a048ad11c4a914b6..4ea6c2c60d657901cb6c8f9a8ea1252348ea1baa 100644 (file)
@@ -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);
index e9eff08cd138b8cba5d47f6dc8a44ca3d20f91ee..beb0c1836ff9b58c74529b9204514bed853207d4 100644 (file)
@@ -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 */
index fbb1e5714cc97caf7af1d2042dcc0cbf8629debe..ae149a04284cd0cb6b8440fa13d49c82c5d17fd1 100644 (file)
@@ -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)
+       for_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;
        }
 }
 
index fce30d4ee7756c6c218e40088d1ac8b46749d577..4954d791d86e2fd5279e485623ada2b655f66bee 100644 (file)
@@ -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,
index 270dcae7d0a07b734fc110433aebce8e8bc22606..6f46e6bec0c24a1084feaceeb67318e2a1975994 100644 (file)
@@ -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"
index b2c0440de6ce8f1a453dab35f49742233de17adf..7f2061692f5be390b208b4949313ab4f8349eb0e 100644 (file)
@@ -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));
+               for_each (lspdb, head, lsp) {
+                       lsp_print_flooding(vty, lsp);
                        vty_out(vty, "\n");
                }
        }
index 79d79f8911647653be9c590c00fe21dc7235f15b..e2ef934696303d190b128adc513eed05dbb59c55 100644 (file)
@@ -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"
 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
index ad02220438f839576ac32c74cf4447a98e7a5c96..07be68d9aede9be778753614a220877d901375af 100644 (file)
@@ -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;
index fb879395c1b70ec8cf9c51140933a858fcc9e698..f8486ae0d6cd81ae32b90aeec870c933585d91c8 100644 (file)
@@ -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);
index 4371d5993af4e4224bf87632e0215565173dfa33..bae56309cf03efe0b7f65adbb90e054b8c15cb23 100644 (file)
@@ -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 \
index 9dc56773584aae7fb8d59a8aa2b3d32e70cda066..35a7d944d30c66d9dc77e55d87acbe851bc9c56c 100644 (file)
@@ -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 (file)
index 0000000..8169ba9
--- /dev/null
@@ -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 (file)
index 0000000..373c481
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * 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); }     \
+/* ... */
+
+/* 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 */
index 7e27a64de7bcc20f490b61e3240d0fc945977065..a8956c315cc68e2c5c845b6841d6a3aad83c65f9 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -433,7 +433,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 +451,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());
 
index a93875c4cf80e1a6ef74845f2ca93b681f28ef32..d02110a9970cea35d6833c157fc114bdebe46217 100644 (file)
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -102,7 +102,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);
 
index 559457c11998ec0bbd968b734b5a6fbf826aeabd..d6fd1fa561d4edfc20b876cb0d19dfa88085879f 100644 (file)
@@ -1386,7 +1386,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 +1705,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");
index cb4f7531ecdec7b6335efe148cd13ef51c0acb23..474adc7c8bb08a1f1c7da14e7674442080b28ff4 100644 (file)
@@ -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,13 @@ extern "C" {
 #ifndef _FALLTHROUGH
 #define _FALLTHROUGH
 #endif
+#ifndef _DEPRECATED
+#define _DEPRECATED(x) deprecated
+#endif
+
+/* 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 +103,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/fifo.h b/lib/fifo.h
deleted file mode 100644 (file)
index 6f9c59b..0000000
+++ /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__ */
index e86030f83c7c0109860e51f15e997f67a06118af..1e28253f2b2ea9e6c7688692dd5261a1b296b3ba 100644 (file)
@@ -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/frrlua.c b/lib/frrlua.c
new file mode 100644 (file)
index 0000000..b7d8eea
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file defines the lua interface into
+ * FRRouting.
+ *
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This file is part of FreeRangeRouting (FRR).
+ *
+ * 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 <stdio.h>
+
+#include <zebra.h>
+
+#if defined(HAVE_LUA)
+#include "prefix.h"
+#include "frrlua.h"
+#include "log.h"
+
+static int lua_zlog_debug(lua_State *L)
+{
+       int debug_lua = 1;
+       const char *string = lua_tostring(L, 1);
+
+       if (debug_lua)
+               zlog_debug("%s", string);
+
+       return 0;
+}
+
+const char *get_string(lua_State *L, const char *key)
+{
+       const char *str;
+
+       lua_pushstring(L, key);
+       lua_gettable(L, -2);
+
+       str = (const char *)lua_tostring(L, -1);
+       lua_pop(L, 1);
+
+       return str;
+}
+
+int get_integer(lua_State *L, const char *key)
+{
+       int result;
+
+       lua_pushstring(L, key);
+       lua_gettable(L, -2);
+
+       result = lua_tointeger(L, -1);
+       lua_pop(L, 1);
+
+       return result;
+}
+
+static void *lua_alloc(void *ud, void *ptr, size_t osize,
+                      size_t nsize)
+{
+       (void)ud;  (void)osize;  /* not used */
+       if (nsize == 0) {
+               free(ptr);
+               return NULL;
+       } else
+               return realloc(ptr, nsize);
+}
+
+lua_State *lua_initialize(const char *file)
+{
+       int status;
+       lua_State *L = lua_newstate(lua_alloc, NULL);
+
+       zlog_debug("Newstate: %p", L);
+       luaL_openlibs(L);
+       zlog_debug("Opened lib");
+       status = luaL_loadfile(L, file);
+       if (status) {
+               zlog_debug("Failure to open %s %d", file, status);
+               lua_close(L);
+               return NULL;
+       }
+
+       lua_pcall(L, 0, LUA_MULTRET, 0);
+       zlog_debug("Setting global function");
+       lua_pushcfunction(L, lua_zlog_debug);
+       lua_setglobal(L, "zlog_debug");
+
+       return L;
+}
+
+void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix)
+{
+       char buffer[100];
+
+       lua_newtable(L);
+       lua_pushstring(L, prefix2str(prefix, buffer, 100));
+       lua_setfield(L, -2, "route");
+       lua_pushinteger(L, prefix->family);
+       lua_setfield(L, -2, "family");
+       lua_setglobal(L, "prefix");
+}
+
+enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule)
+{
+       int status;
+
+       lua_getglobal(L, rule);
+       status = lua_pcall(L, 0, 1, 0);
+       if (status) {
+               zlog_debug("Executing Failure with function: %s: %d",
+                          rule, status);
+               return LUA_RM_FAILURE;
+       }
+
+       status = lua_tonumber(L, -1);
+       return status;
+}
+#endif
diff --git a/lib/frrlua.h b/lib/frrlua.h
new file mode 100644 (file)
index 0000000..374eb70
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file defines the lua interface into
+ * FRRouting.
+ *
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This file is part of FreeRangeRouting (FRR).
+ *
+ * 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.
+ */
+#ifndef __LUA_H__
+#define __LUA_H__
+
+#if defined(HAVE_LUA)
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These functions are helper functions that
+ * try to glom some of the lua_XXX functionality
+ * into what we actually need, instead of having
+ * to make multiple calls to set up what
+ * we want
+ */
+enum lua_rm_status {
+       /*
+        * Script function run failure.  This will translate into a
+        * deny
+        */
+       LUA_RM_FAILURE = 0,
+       /*
+        * No Match was found for the route map function
+        */
+       LUA_RM_NOMATCH,
+       /*
+        * Match was found but no changes were made to the
+        * incoming data.
+        */
+       LUA_RM_MATCH,
+       /*
+        * Match was found and data was modified, so
+        * figure out what changed
+        */
+       LUA_RM_MATCH_AND_CHANGE,
+};
+
+/*
+ * Open up the lua.scr file and parse
+ * initial global values, if any.
+ */
+lua_State *lua_initialize(const char *file);
+
+void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix);
+
+enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule);
+
+/*
+ * Get particular string/integer information
+ * from a table.  It is *assumed* that
+ * the table has already been selected
+ */
+const char *get_string(lua_State *L, const char *key);
+int get_integer(lua_State *L, const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+#endif
index fd337073f812d30aa77ffea4adf325e47f1eb10d..fbbc890ec69fe49cd9af1913fcc383a7907dcd31 100644 (file)
@@ -152,6 +152,32 @@ void frrstr_strvec_free(vector v)
        vector_free(v);
 }
 
+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 begins_with(const char *str, const char *prefix)
 {
        if (!str || !prefix)
index 8b591849a3e6687bc5a9d3cc6317ae95af045dfd..d40ca14ba5eae6ebec0151dda98aadbf277d6545 100644 (file)
@@ -87,6 +87,28 @@ 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.
  *
index c9c942f9bf897a02599dea196ce1dcea5463556a..38494fb0074e6186c0efb0acee294e801c71f819 100644 (file)
@@ -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);
 
index 86b850c059f785ab1926d957ab09c29156a7c34a..38f3f45ed1e020f079f4d85b4d6ae1c4a917f679 100644 (file)
--- 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)
 {
index 5f6c25b770e033c2ec55e08f453f01e599e8bf83..b6c764d8739b80203b10a250e7eadf94660b22a6 100644 (file)
@@ -332,6 +332,12 @@ static struct log_ref ferr_lib_err[] = {
                .description = "The northbound subsystem has detected that the libsysrepo library returned an error",
                .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",
index fc405c20983a3a2303def2bc57152bb4c486befd..39b39fb065b9b618f9cfc97a06d6704e400544d8 100644 (file)
@@ -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,
 };
index 0d4c8d6c0f1b4200895491cc3ef28c7fb5aa2847..5970e70a6bd493230f9b7cb5091cb963277178c5 100644 (file)
@@ -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/lua.c b/lib/lua.c
deleted file mode 100644 (file)
index 3d701a9..0000000
--- a/lib/lua.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * This file defines the lua interface into
- * FRRouting.
- *
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
- *
- * This file is part of FreeRangeRouting (FRR).
- *
- * 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 <stdio.h>
-
-#include <zebra.h>
-
-#if defined(HAVE_LUA)
-#include "prefix.h"
-#include "lua.h"
-#include "log.h"
-
-static int lua_zlog_debug(lua_State *L)
-{
-       int debug_lua = 1;
-       const char *string = lua_tostring(L, 1);
-
-       if (debug_lua)
-               zlog_debug("%s", string);
-
-       return 0;
-}
-
-const char *get_string(lua_State *L, const char *key)
-{
-       const char *str;
-
-       lua_pushstring(L, key);
-       lua_gettable(L, -2);
-
-       str = (const char *)lua_tostring(L, -1);
-       lua_pop(L, 1);
-
-       return str;
-}
-
-int get_integer(lua_State *L, const char *key)
-{
-       int result;
-
-       lua_pushstring(L, key);
-       lua_gettable(L, -2);
-
-       result = lua_tointeger(L, -1);
-       lua_pop(L, 1);
-
-       return result;
-}
-
-static void *lua_alloc(void *ud, void *ptr, size_t osize,
-                      size_t nsize)
-{
-       (void)ud;  (void)osize;  /* not used */
-       if (nsize == 0) {
-               free(ptr);
-               return NULL;
-       } else
-               return realloc(ptr, nsize);
-}
-
-lua_State *lua_initialize(const char *file)
-{
-       int status;
-       lua_State *L = lua_newstate(lua_alloc, NULL);
-
-       zlog_debug("Newstate: %p", L);
-       luaL_openlibs(L);
-       zlog_debug("Opened lib");
-       status = luaL_loadfile(L, file);
-       if (status) {
-               zlog_debug("Failure to open %s %d", file, status);
-               lua_close(L);
-               return NULL;
-       }
-
-       lua_pcall(L, 0, LUA_MULTRET, 0);
-       zlog_debug("Setting global function");
-       lua_pushcfunction(L, lua_zlog_debug);
-       lua_setglobal(L, "zlog_debug");
-
-       return L;
-}
-
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix)
-{
-       char buffer[100];
-
-       lua_newtable(L);
-       lua_pushstring(L, prefix2str(prefix, buffer, 100));
-       lua_setfield(L, -2, "route");
-       lua_pushinteger(L, prefix->family);
-       lua_setfield(L, -2, "family");
-       lua_setglobal(L, "prefix");
-}
-
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule)
-{
-       int status;
-
-       lua_getglobal(L, rule);
-       status = lua_pcall(L, 0, 1, 0);
-       if (status) {
-               zlog_debug("Executing Failure with function: %s: %d",
-                          rule, status);
-               return LUA_RM_FAILURE;
-       }
-
-       status = lua_tonumber(L, -1);
-       return status;
-}
-#endif
diff --git a/lib/lua.h b/lib/lua.h
deleted file mode 100644 (file)
index a864ab3..0000000
--- a/lib/lua.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * This file defines the lua interface into
- * FRRouting.
- *
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
- *
- * This file is part of FreeRangeRouting (FRR).
- *
- * 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.
- */
-#ifndef __LUA_H__
-#define __LUA_H__
-
-#if defined(HAVE_LUA)
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * These functions are helper functions that
- * try to glom some of the lua_XXX functionality
- * into what we actually need, instead of having
- * to make multiple calls to set up what
- * we want
- */
-enum lua_rm_status {
-       /*
-        * Script function run failure.  This will translate into a
-        * deny
-        */
-       LUA_RM_FAILURE = 0,
-       /*
-        * No Match was found for the route map function
-        */
-       LUA_RM_NOMATCH,
-       /*
-        * Match was found but no changes were made to the
-        * incoming data.
-        */
-       LUA_RM_MATCH,
-       /*
-        * Match was found and data was modified, so
-        * figure out what changed
-        */
-       LUA_RM_MATCH_AND_CHANGE,
-};
-
-/*
- * Open up the lua.scr file and parse
- * initial global values, if any.
- */
-lua_State *lua_initialize(const char *file);
-
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix);
-
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule);
-
-/*
- * Get particular string/integer information
- * from a table.  It is *assumed* that
- * the table has already been selected
- */
-const char *get_string(lua_State *L, const char *key);
-int get_integer(lua_State *L, const char *key);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
index 91a02b7966c2aad18882a9c7f84a32c47712a117..0002ea3349ad20e68175f3e3350a0e13dd48609a 100644 (file)
@@ -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
index 5e031ac2cea8ba4fdc9cbcb88f3ea0fbf5ef1572..e8b3e46c19deb2c624f35ee4c9ba654204781a59 100644 (file)
@@ -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;
 }
@@ -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);
 }
index 14f27c1d41034b07ce1989014f5ad1d85d4248a9..8f6753506b9de2cf3c942511291e4e5416197097 100644 (file)
@@ -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.
index 48faa7595aa04709730050e7e42487c6a27193b7..ae1b0578a004ef473da89b2c97ea8c74a6a2a906 100644 (file)
@@ -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) {
index 0df286511ecea668bad11fdb39a6fc590a75ea9a..e9669fc7e16c8d5cb6a313f022b0a5acb75c20e4 100644 (file)
@@ -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 (file)
index 0000000..a55da23
--- /dev/null
@@ -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, )
index 33b6c247825e86f3622d7f983892de53d6fc0f9a..44a55137f82e23e93227c74369b1aaa7a5f52695 100644 (file)
@@ -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. */
index eadef9902ba5df272f51354a4cf1b3db5e259f38..ddcc59fa8f5920d0e832a400c24128c90c46639a 100644 (file)
@@ -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;
index d2f078133340ef8da4d6ebaeb808d84e1d1c89ae..832a10141eb56516737a7ebf422a071b96b86572 100644 (file)
@@ -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                 \
index 6b91969218fdb0c704e179dfc0a880342b2c69e5..d2a4c3a432351193e2c24fcb4ecb40784924ad78 100644 (file)
@@ -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;
 
index d3c387e102945e9df619485f6e0db9749e0d250a..d57b43dac689e10d377a7afbef3510daf230b0ee 100644 (file)
@@ -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);
index a19707b1c928c1c7866ee5d5a3c08775cad52f57..a3314c6c3ca977a5782d26e6851ec3720336b5f2 100644 (file)
@@ -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;
                }
        }
index 811645f3c33e337387be81f2789a68d2b551b819..3e3860a96a9f7e0591b30c6e0e5ca011e0baf5fb 100644 (file)
 #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);
 }
index d63988cbab0b5f4b46170e8baa4128b97abafc52..415eae02efaccbba6b142d188f1ffa64f36cf5df 100644 (file)
@@ -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;
 };
 
index 4898a8d0fa776e0650a6b440f4a3e8aa4b201e97..0c75be3323bb19fd2237d28961b8ef444b1fdc54 100644 (file)
@@ -616,7 +616,7 @@ 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. */
@@ -902,7 +902,7 @@ static const char *route_map_type_str(enum route_map_type type)
        case RMAP_DENY:
                return "deny";
                break;
-       default:
+       case RMAP_ANY:
                return "";
                break;
        }
@@ -1077,8 +1077,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 +1136,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;
@@ -1308,6 +1306,16 @@ 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);
@@ -1415,10 +1418,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 +1442,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 +1639,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;
 }
@@ -1785,7 +1783,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 +1833,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 +1862,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 +2820,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);
index e43e74a633dda2424d1491f67c6b5c92698a0d55..9969936a6b9fecf684213bf43be015b05c5259bf 100644 (file)
@@ -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 (file)
index 0000000..223d149
--- /dev/null
@@ -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 (file)
index 0000000..eef05a4
--- /dev/null
@@ -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 */
index af4f41f37ca380ec45a2f11267ef78cff7df7bf4..bb5426d74a8d70faa8698880563a56fcb143251a 100644 (file)
@@ -472,7 +472,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;
 }
index 3b14be46767b5fbbb26bfce5f7e10c3c5872571d..4897f5e8e528b7447fe7ca32a8b38b459adf2906 100644 (file)
@@ -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
 
@@ -302,6 +309,18 @@ lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
 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
 #
@@ -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; }
index edba7f1932455181bf85657c238a40bab5c21a2d..2d42e2d55c904fe8942f497df267cd979c610df9 100644 (file)
@@ -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 void *a, const void *b)
 {
        const struct prefix *pa = a, *pb = b;
-       return prefix_cmp(pa, pb) == 0;
+       return prefix_cmp(pa, pb);
 }
 
+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;
 }
@@ -257,7 +256,7 @@ struct route_node *route_node_lookup(const struct route_table *table,
        prefix_copy(&p, pu.p);
        apply_mask(&p);
 
-       node = hash_get(table->hash, (void *)&p, NULL);
+       node = rn_hash_node_find(&table->hash, (void *)&p);
        return (node && node->info) ? route_lock_node(node) : NULL;
 }
 
@@ -270,7 +269,7 @@ struct route_node *route_node_lookup_maynull(const struct route_table *table,
        prefix_copy(&p, pu.p);
        apply_mask(&p);
 
-       node = hash_get(table->hash, (void *)&p, NULL);
+       node = rn_hash_node_find(&table->hash, (void *)&p);
        return node ? route_lock_node(node) : NULL;
 }
 
@@ -282,12 +281,11 @@ struct route_node *route_node_get(struct route_table *const table,
        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, (void *)p);
        if (node && node->info)
                return route_lock_node(node);
 
@@ -314,8 +312,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 +364,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
index ce578e795c648f782fbffc8c64aead792fb8d5bf..3e3fb658aebe011df1ded9fde4bab856489b1b7c 100644 (file)
@@ -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;                                                            \
 
index 2760b83fb3c4aadd4d3f31cda56e654d8c7f3288..5ca859a74d4c2d093c23a1ea1a5a6693711913d3 100644 (file)
@@ -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>
@@ -435,6 +437,9 @@ struct thread_master *thread_master_create(const char *name)
                (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 +492,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 +500,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 +508,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 +517,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 +565,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 +645,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 +926,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 +1018,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 +1033,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);
-                               }
+
+                       for_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);
-                               }
+                       for_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 +1093,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 +1248,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 +1327,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 +1373,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 +1406,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 */
index ec774a654309e9b5d2e36e481d374b17c36c12b0..789726512049ff64078fa153f7564dd0a79169ea 100644 (file)
@@ -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 (file)
index 0000000..d361e76
--- /dev/null
@@ -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 (file)
index 0000000..3d8db06
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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"
+
+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)      \
+/* ... */
+
+#endif /* _FRR_TYPERB_H */
diff --git a/lib/typesafe.c b/lib/typesafe.c
new file mode 100644 (file)
index 0000000..bd269e9
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * 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")
+
+#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));
+}
diff --git a/lib/typesafe.h b/lib/typesafe.h
new file mode 100644 (file)
index 0000000..94002da
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * 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"
+
+/* generic macros for all list-like types */
+
+#define for_each(prefix, head, item)                                           \
+       for (item = prefix##_first(head); item;                                \
+                       item = prefix##_next(head, item))
+#define for_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 for_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;                                                    \
+}                                                                              \
+/* ... */
+
+/* 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(const 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(const 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 = h->sh.hitem.next[0];                        \
+       if (!sitem)                                                            \
+               return NULL;                                                   \
+       typesafe_skiplist_del(&h->sh, sitem, cmpfn_uq);                        \
+       return container_of(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(const 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));
+
+/* 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 */
index de50e6a51743726e97d07a2113ee196996f06b26..6ec3cc8e0d288b4abfc4ca7387129eab5147bcd8 100644 (file)
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -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);
index dae8e825993ba84356c3ef5797bc2221400a2d00..0ee9b78b91f9afa77951178dbd734bd003396e08 100644 (file)
--- 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;
 
index 9c2653e1ac0ded45a43a604adbb9f5a63a5bcf69..98d75542fd3794796e84fdc5832704e5a51faf97 100644 (file)
--- 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 *);
index 066d81f35020e66998fe59ec3ce75ef1754ee2b1..54090d0d0f82720852ffa150e1786c431e3eb736 100644 (file)
@@ -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;
                }
index 2a2c155deede2928e979ad95a5d43d1c2c832ee2..2f9a9aa5a3d79f76c7f46a1070044a270aa30429 100644 (file)
@@ -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();
 }
 
index 76a6cc5fd1954d40b54683c436cb88c0fa783c38..69fff5dbffb0981f0dabd186d5115e34d005b9c5 100644 (file)
@@ -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];
@@ -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();
index 7ecea5f445469bbbb736b19e62c1f510ba1e31bf..0558383823e55d8fbb9c9816918dbda098c9f291 100644 (file)
@@ -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, ...)
index 4901c92743567dd60dcfa6e18280202202f90d29..96a78efad6d5abe125493d77e3337dba8c610c5e 100644 (file)
@@ -2371,9 +2371,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 +2384,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;
index 0926281f2eb0d770338537a2245494ac20862581..c46d63bfab4fe470219d391d267551a10873df03 100644 (file)
@@ -221,66 +221,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. */
@@ -597,9 +580,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,
index b96fb5a206834dbf414ec309dd76fe3a38f88387..b1ea43c747cdde0817356a8eebc3a32b97a45424 100644 (file)
@@ -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
diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4
new file mode 100644 (file)
index 0000000..9feb352
--- /dev/null
@@ -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])
+])
index b33eaa047849e38bad0f6c4e36be90aa5cce105e..8f1ba14fe4f200636ae85fcb9eedc505bf94539a 100644 (file)
@@ -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;
 
index 9b8599eded575dbce3184b67ab8d00874f0d668f..d7c485f0a0506895cb5e4722bb7ee7bb976ee0d9 100644 (file)
@@ -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)
index dae00bbcea00d3e0005becebd9c54dfbfa5afef4..a788eb2efb0f3a06a34b0ca6a4a2841a414f4f34 100644 (file)
@@ -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;
index f92ea4ac900ed065e7f5ae8f11c639616f784b0f..fa3549f5edac0f8902dbc056f438fa3388acc4b2 100644 (file)
@@ -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);
index 8f1c63457aa6d6e547e1b592ac91bfd7d81abdfa..89de145e65016f4a187b301c78bbccf1b8551087 100644 (file)
@@ -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,
index 830f0e1c84f8d62f168ff8b3d36073430334420f..64b16e7ee34d8d98dd1b4403568fb21b0de5a747 100644 (file)
@@ -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) {
index c662295083e79063f814c14bfeecdde45fd03880..7f1475cc69e1a2eb19f49e73f27534951b2c92fb 100644 (file)
@@ -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;
        }
 
index 2795bb9abdfb46c99ad369bb6d162bd359e8cea9..946bbf8cc9423f8fb9a5309f2c79203b4f3f6a16 100644 (file)
@@ -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;
 
index e7284a66593529f320e81a38a85758580b0049b0..7a26af1f09ac36fb08360fedf14c149c9e28e74f 100644 (file)
@@ -138,8 +138,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 +151,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 +181,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;
index f08426fb47994173b10b32250230d44c6330ba2e..aa4a9951731984eebe5c992743404e07837fe4e7 100644 (file)
@@ -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);
 
index da95ec80a32c18d9a8b15fdb2484c7dd1d89c350..a387d40a577e1fbda46ede28370b8028658ea68c 100644 (file)
@@ -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];
 
index abdc82a73841983085325bf3f5456aca87e4c217..af16c5aa7cded703f57dfca72dfd986a0347f913 100644 (file)
@@ -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);
 }
index 594735a08f8021919240b920308d250e1ff82bcd..c8ad6d04f49ec6de3352bffe4ffccd5f314e9143 100644 (file)
@@ -141,8 +141,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 +156,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 +194,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;
@@ -251,6 +249,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;
index a381cf7145fd3a2fc123321567f2d3c8af4ac670..5e3dabc27a1f3db41ccb5f129993f2403983021d 100644 (file)
@@ -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;
index 2e850c4e26705963f6b16bb79609ac40ecddf8ff..86eb141312ebb72464a6f96c7d659e234b4445e5 100644 (file)
@@ -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;
index 65c7e28fed37751f59e3daf4868c8147d49f7fd2..5cf5d05449eaf884e633642efa3fcbd712e5c894 100644 (file)
@@ -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);
index 43c5e338b0893b3b3cb8c0386cdf1ced1b748743..6bc8c25153bf1d48e833eb564ea783f1c9187e13 100644 (file)
@@ -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. */
index 30b2a50bb38f4c57826f8577c4172130c2a387dc..ab2d5ae5844cec5584445526bfb5a768baeb76f6 100644 (file)
@@ -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;
index 6e03fa9bdeb04648bc5f3c833397e973e784d31d..296a05bdf141ca5d769c447add2802a34b9e5d71 100644 (file)
@@ -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"
 
 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
index 85f42bcd18292cf9d0d7f6d2cd9cfd109c1f1417..09a0b6f1b7eb67ea59a3983ea41b2baf88130ed2 100644 (file)
@@ -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 */
 
 /* 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*/
index bd8cbee11ab2e2ed525649b13adc46b262686ecc..1488aa88cd4edadbc8f6ec8bcfa37f9807b179d1 100644 (file)
@@ -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;
 }
 
index 4cbd817ad871b08e8164c738394ca425ba8fcb9e..c178e367d390a032fcc37df14163950328d8ce69 100644 (file)
@@ -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);
 }
index 4f8f50556bb5d616a422a5d0bbe4d934e72797bb..aad0e4275fb913d1ca8d53b64e6316ed0261c8fc 100644 (file)
@@ -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;
@@ -401,8 +394,7 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
        }
 }
 
-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];
index 466cc60643aca3bbb2d5a6a97c5cb4446e14db34..300261e5a996909113c0737bcde1d545d78d75bc 100644 (file)
@@ -208,8 +208,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;
@@ -288,8 +287,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 +297,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) {
index 77ea314d54d5bf154cf30699681fefb8ed3ae84f..cb2ba87ec68fa55dcda2ef0dd778c399aa18ee6d 100644 (file)
@@ -1207,6 +1207,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
@@ -2311,6 +2313,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 *
index cbc3c6a640c747b9a94731192e4b9763983f0ab5..3a68176510213dbd9210c13351a16fe3167a5219 100644 (file)
@@ -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.
index 8a459fe86e57325183e6a9f0d6c00c541f42a041..48b9f1f284a16ba47cc5e371d3bc7690246c662c 100644 (file)
@@ -553,8 +553,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;
@@ -581,7 +580,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) {
index 13bb0fcb55cfde5ba68564149e631a140f9784da..e7a5fa77209ba7987192b5c2b00a6c39139f7c2d 100644 (file)
@@ -47,8 +47,7 @@ struct pim_nexthop_cache {
        struct hash *upstream_hash;
 };
 
-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,
                              struct pim_nexthop_cache *out_pnc);
index 4230c127ad31031f89ef4784ad3fd395bd847391..2de94e9031ebd4f783b4caff129bd955d1d2e217 100644 (file)
@@ -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);
 }
index b708e86a2050891625ab06f6a1c44e4beed6d5a7..d829d01347e4e613b78ac3a30d99851d62bc28f8 100644 (file)
@@ -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);
index aeaea7d69f08ea9eb8e1770bc394250f13de93a3..25ac307ac43defc1b51e27ba89d169df7da729db 100644 (file)
@@ -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__);
index b909cbcb2b81913cbc9bc54d68f35caf1edd6223..634fee0b3025ad57776ddfdcd230f8af57cbb94d 100644 (file)
@@ -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;
index 303be0315d96fd0c1b5d9b0260144fe313eea655..6befda0e28606c883690ee6e880de9fdd3bef9c7 100644 (file)
@@ -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 */
index 4f0df122329cc0aaa1dceb410c0448b1f854cf62..0c88cb202bd4db7c274ab81b155397fce83f346e 100644 (file)
@@ -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);
index e35652b1acd0564c5b30990fa23777adf0974628..5a4087b177e8e0427a9ceb63a40074a65a958d03 100644 (file)
@@ -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;
index cf60de2de914ec70e2ecbc2dc35e44c76a309939..a557a90c829a5e937f06104ad1b069a168794459 100644 (file)
@@ -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,
index dc425b6958c60228c6c0bdc331a1bdc7380300cc..a2686304fcfafd5b9f1d359bbb61b09f5be79104 100644 (file)
@@ -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);
index f1e83628c2ee5876eccb17fc1804f6ab281108d7..19c7e556ca53631dc967525ec56d0a997f4416db 100644 (file)
@@ -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;
index 3f3117752439a72b121eff0731f131424b165c0e..c03a371d885f4d2ef6383b355b9d7ca63c1a486a 100644 (file)
@@ -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;
index de648015f1d95be8d705c829e390e8668a9e1836..380172487d84062ec74f2d6a5ace0914e4de7462 100644 (file)
@@ -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
index 4a89bda84eac865059ab1755ffddc96497bc9691..6f8bc2218e2f7bcca388a9f7185bcb2816223ca9 100644 (file)
Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ
index b9c6f2bbb21dea8e532d8b0925a6892f0a693419..f0baa482c78ae27489085968ce11f14220a81c0c 100644 (file)
@@ -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);
index d10962993d219f1b8f0658b786f3d05a8f238ce1..12c333c8b6742990b041aececad906ab4af2ac52 100644 (file)
@@ -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"
diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c
new file mode 100644 (file)
index 0000000..078e05e
--- /dev/null
@@ -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;
+
+               for_each(asort, &shead, item) {
+                       assert(item->val1 >= prevval);
+                       prevval = item->val1;
+                       c++;
+               }
+               assert(c == asort_count(&shead));
+       } else {
+               prev = &dummy;
+               for_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);
+       for_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 (file)
index 0000000..293d47f
--- /dev/null
@@ -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 (file)
index 0000000..6b2b9ed
--- /dev/null
@@ -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_typelist.c b/tests/lib/test_typelist.c
new file mode 100644 (file)
index 0000000..68ea82e
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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>
+
+#define WNO_ATOMLIST_UNSAFE_FIND
+
+#include "typesafe.h"
+#include "atomlist.h"
+#include "memory.h"
+#include "monotime.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 _U_SORTLIST_UNIQ       1
+#define _U_SORTLIST_NONUNIQ    0
+#define _U_HASH                        1
+#define _U_SKIPLIST_UNIQ       1
+#define _U_SKIPLIST_NONUNIQ    0
+#define _U_RBTREE_UNIQ         1
+#define _U_RBTREE_NONUNIQ      0
+#define _U_ATOMSORT_UNIQ       1
+#define _U_ATOMSORT_NONUNIQ    0
+
+#define _IS_UNIQ(type)         _U_##type
+#define IS_UNIQ(type)          _IS_UNIQ(type)
+
+#define _H_SORTLIST_UNIQ       0
+#define _H_SORTLIST_NONUNIQ    0
+#define _H_HASH                        1
+#define _H_SKIPLIST_UNIQ       0
+#define _H_SKIPLIST_NONUNIQ    0
+#define _H_RBTREE_UNIQ         0
+#define _H_RBTREE_NONUNIQ      0
+#define _H_ATOMSORT_UNIQ       0
+#define _H_ATOMSORT_NONUNIQ    0
+
+#define _IS_HASH(type)         _H_##type
+#define IS_HASH(type)          _IS_HASH(type)
+
+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 SORTLIST_UNIQ
+#include "test_typelist.h"
+
+#define TYPE SORTLIST_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE HASH
+#include "test_typelist.h"
+
+#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_SORTLIST_UNIQ();
+       test_SORTLIST_NONUNIQ();
+       test_HASH();
+       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 (file)
index 0000000..60a37e8
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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_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)
+
+PREDECL(TYPE, list)
+struct item {
+       uint64_t val;
+       struct list_item itm;
+       int scratchpad;
+};
+
+static int list_cmp(const struct item *a, const struct item *b);
+
+#if IS_HASH(TYPE)
+static uint32_t list_hash(const struct item *a);
+DECLARE(TYPE, list, struct item, itm, list_cmp, list_hash)
+
+static uint32_t list_hash(const struct item *a)
+{
+       /* crappy hash to get some hash collisions */
+       return a->val ^ (a->val << 29) ^ 0x55AA0000U;
+}
+
+#else
+DECLARE(TYPE, 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;
+}
+
+#define NITEM 10000
+struct item itm[NITEM];
+static struct list_head head = concat(INIT_, TYPE)(head);
+
+static void concat(test_, TYPE)(void)
+{
+       size_t i, j, k, l;
+       struct prng *prng;
+       struct item *item, *prev;
+       struct item dummy;
+
+       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);
+       ts_ref("init");
+
+       assert(list_first(&head) == NULL);
+
+       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++;
+               } else
+                       assert(list_add(&head, &itm[j]) == &itm[j]);
+       }
+       assert(list_count(&head) == k);
+       assert(list_first(&head) != NULL);
+       ts_ref("fill");
+
+       k = 0;
+       prev = NULL;
+       for_each(list, &head, item) {
+#if IS_HASH(TYPE)
+               /* 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(TYPE)
+       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_ref("add-dup");
+#else /* !IS_UNIQ(TYPE) */
+       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_ref("add-dup+find_{lt,gteq}");
+#endif
+#if !IS_HASH(TYPE)
+       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_ref("del");
+
+       for_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_ref("for_each_safe+del");
+
+       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_ref("pop");
+
+       list_fini(&head);
+       ts_ref("fini");
+       ts_end();
+       printf("%s end\n", str(TYPE));
+}
+
+#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_find
+#undef list_find_lt
+#undef list_find_gteq
+#undef list_del
+#undef list_pop
+
+#undef TYPE
diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py
new file mode 100644 (file)
index 0000000..3b7373c
--- /dev/null
@@ -0,0 +1,14 @@
+import frrtest
+
+class TestTypelist(frrtest.TestMultiOut):
+    program = './test_typelist'
+
+TestTypelist.onesimple('SORTLIST_UNIQ end')
+TestTypelist.onesimple('SORTLIST_NONUNIQ end')
+TestTypelist.onesimple('HASH 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')
index 365fe00cc6af19d91e4940dcd2add384f0c2a3be..ec5fea705e74b5e4d73740a6e43bbd71da10bef0 100644 (file)
@@ -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 \
index c29ed3db6195d245a9c68cdd22d4e8c8d5960d6c..1e8f67f3f9a6d6e2749eee21c6a601497cc2a03b 100644 (file)
@@ -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
index 239de55bd6f27baf5cea9245cfc5da0945ae9ae4..9658c080c0c396a5f33ba1c34533db4943eff9ec 100755 (executable)
@@ -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-vrf-topo1/__init__.py b/tests/topotests/bfd-vrf-topo1/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf
new file mode 100644 (file)
index 0000000..3466e6a
--- /dev/null
@@ -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 (file)
index 0000000..4b2cc1a
--- /dev/null
@@ -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 (file)
index 0000000..fa07d60
--- /dev/null
@@ -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 (file)
index 0000000..7ad4e2b
--- /dev/null
@@ -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 (file)
index 0000000..f49768f
--- /dev/null
@@ -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 (file)
index 0000000..fcd1e7d
--- /dev/null
@@ -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 (file)
index 0000000..3481ea8
--- /dev/null
@@ -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 (file)
index 0000000..39f3c0a
--- /dev/null
@@ -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 (file)
index 0000000..c0ef11a
--- /dev/null
@@ -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 (file)
index 0000000..0715ea3
--- /dev/null
@@ -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 (file)
index 0000000..5035d64
--- /dev/null
@@ -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 (file)
index 0000000..daffd19
--- /dev/null
@@ -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 (file)
index 0000000..f6921b7
--- /dev/null
@@ -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 (file)
index 0000000..c92d4e0
--- /dev/null
@@ -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 (file)
index 0000000..d478333
--- /dev/null
@@ -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 (file)
index 0000000..277f027
--- /dev/null
@@ -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 (file)
index 0000000..ef38008
--- /dev/null
@@ -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 (file)
index 0000000..f727c2d
--- /dev/null
@@ -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 (file)
index 0000000..a56a3a0
--- /dev/null
@@ -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 (file)
index 0000000..cc8510d
--- /dev/null
@@ -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 (file)
index 0000000..7d81784
--- /dev/null
@@ -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 (file)
index 0000000..66bf285
--- /dev/null
@@ -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 (file)
index 0000000..3714008
--- /dev/null
@@ -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 (file)
index 0000000..69770dd
--- /dev/null
@@ -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 (file)
index 0000000..c84ace2
--- /dev/null
@@ -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 (file)
index 0000000..4d6d56e
Binary files /dev/null and b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg 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 (executable)
index 0000000..e293382
--- /dev/null
@@ -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_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
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 (file)
index 0000000..e06fa08
--- /dev/null
@@ -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 (file)
index 0000000..0a283c0
--- /dev/null
@@ -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 (file)
index 0000000..0549697
--- /dev/null
@@ -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 (file)
index 0000000..606c17b
--- /dev/null
@@ -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 (file)
index 0000000..b4e304d
--- /dev/null
@@ -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 (file)
index 0000000..39499a1
--- /dev/null
@@ -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 (file)
index 0000000..0549697
--- /dev/null
@@ -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 (file)
index 0000000..b859115
--- /dev/null
@@ -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 (file)
index 0000000..eecacfd
--- /dev/null
@@ -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_maximum_prefix_invalid_update/__init__.py b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..235b42b
--- /dev/null
@@ -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_maximum_prefix_invalid_update/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf
new file mode 100644 (file)
index 0000000..0a283c0
--- /dev/null
@@ -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_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..e016284
--- /dev/null
@@ -0,0 +1,4 @@
+router bgp 65001
+  neighbor 192.168.255.1 remote-as 65000
+  address-family ipv4
+    neighbor 192.168.255.1 maximum-prefix 1
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf
new file mode 100644 (file)
index 0000000..606c17b
--- /dev/null
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
new file mode 100644 (file)
index 0000000..69b8c7c
--- /dev/null
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# bgp_local_as_private_remove.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_maximum_prefix_invalid_update.py:
+Test if unnecesarry UPDATE message like below:
+
+[Error] Error parsing NLRI
+%NOTIFICATION: sent to neighbor X.X.X.X 3/10 (UPDATE Message Error/Invalid Network Field) 0 bytes
+
+is not sent if maximum-prefix count is overflow.
+"""
+
+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):
+        while True:
+            output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+            if output['192.168.255.1']['connectionsEstablished'] > 3:
+                return True
+            time.sleep(1)
+
+    def _bgp_parsing_nlri(router):
+        cmd_max_exceeded = 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log'
+        cmdt_error_parsing_nlri = 'grep "Error parsing NLRI" bgpd.log'
+        output_max_exceeded = tgen.gears[router].run(cmd_max_exceeded)
+        output_error_parsing_nlri = tgen.gears[router].run(cmdt_error_parsing_nlri)
+
+        if len(output_max_exceeded) > 0:
+            if len(output_error_parsing_nlri) > 0:
+                return False
+        return True
+
+
+    if _bgp_converge('r2'):
+        assert _bgp_parsing_nlri('r2') == True
+
+if __name__ == '__main__':
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 096e97fa94495a4fcca8bc8a7f2ba53bca953d01..e9c1916f7581fafe2aade4033d9e961946c9be78 100644 (file)
@@ -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')
index 327e4625f2465647edb2d60a9c5b416411cc7e45..49e48ba927c20027a631861781b3f926dbe9e051 100755 (executable)
@@ -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
index d989240a162ba91349620d5247cc36f4a90b9d6a..0bc1596eb24234e77e609885fdc747e704b80300 100644 (file)
@@ -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)
 
@@ -599,7 +604,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 +613,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 +936,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)
 
index 77e6e9a3301fc79e0d53d62f0b6862f100b94539..1b48d10a09a0cab9b810a5c2017f9ef26730d28f 100755 (executable)
@@ -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
index 59f1bcf52b0103c73306d7572599b497020c4290..ce1db770d277bb32867260dcb2f718ba8f72c49b 100755 (executable)
@@ -741,6 +741,27 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
                         lines_to_add_to_del.append((ctx_keys, swpx_interface))
                         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:
index 332fd248caf5312c6fc865b1efabf34fb6a15286..4dc34d10efb7d39709c70e33a034fd3f5a053a70 100755 (executable)
@@ -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);
index b8da90ca8e8ad63dd3af4c2506da84e59b00e06a..24effa70472460f9ac90225471d7bfebf503c743 100644 (file)
@@ -502,7 +502,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 +540,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 +674,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 +1744,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 +1786,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 +2172,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);
index d0d11c867636c36c2344e82ea007e782aadf1075..7b132cb61ed929265433fbdf844a91110e18e8aa 100644 (file)
@@ -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.";
+        }
       }
     }
   }
index fe5f34a28a034dfc57d398e15f82ff7b51bcd48e..837908a1b3b09eb452e9b42eb40358e15e37591d 100644 (file)
@@ -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
index 7114a3286be1b3e36b7dca5584f1147f5b1d14d3..bba221c2cf13dbc0204ba06dbc09d280efb8e8f0 100644 (file)
@@ -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);
        }
 }
 
index ba518ea576fe0a8b0f7e9c54829cd68ea9e210f1..ce0834f19003d3c862207a86b363e394e043b1b9 100644 (file)
@@ -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,6 +1386,13 @@ 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;
 }
 
index 10f1f9210052b1dc24613d69e84b95160294cbd3..b0ddcaf8bc4b59da4aa53cf7a029f728cfe9a005 100644 (file)
@@ -181,6 +181,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);
@@ -1303,6 +1304,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 +1700,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 +1723,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");
        }
 }
index ce404e82537269a49ced567519399c8de4c5b4fa..bbb5445cc6dfd02a6e954be0fb6944a55933f7a8 100644 (file)
@@ -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),
index 13d2185b0fa04dfb26fcc337b567fa48b7b4a655..5f4bd3bbc6ef7656b21cebcbcbadcabb97d7ea72 100644 (file)
@@ -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
index f98a4c02c320d8779e89cfb66199b87fdcd3efdd..fe064f847a284e1f51863b43a91f737836506324 100644 (file)
@@ -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,8 +647,7 @@ 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)
index e26831e1a6427b7d4e2dda7f625aff06aa735d37..c39abaffccdc0a4b6188848c4fd7c3771a5cd680 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "zebra.h"
 #include "hook.h"
+#include "typesafe.h"
 #include "linklist.h"
 #include "prefix.h"
 #include "table.h"
 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;
@@ -108,6 +148,10 @@ struct route_entry {
        uint32_t dplane_sequence;
 };
 
+#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)
@@ -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);
 }
 
 /*
index 08b51fcc0bcf4ba5fdf11df10bd030049c3b2637..04576671fe54f085409b4759a04a4ee8ecb0574f 100644 (file)
 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,
index 289ed5a15b147acda0cfe996c384ba967cbeff29..def5bf7d88947db03ff7ec48e67feb78dadf4efe 100644 (file)
@@ -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 (;;) {
index f31fb53a3430b79e95265ad034e4c294079a6c10..974cc9de301e369c9b6cb606e4ac175810486fcc 100644 (file)
@@ -1388,7 +1388,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;
@@ -2507,6 +2507,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
index d1b28227c37d7f78cd14e123691a18bd03b16030..6fc62147c815c6c9ac4663bf1b08c160bdb6f3fa 100644 (file)
@@ -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;
index 28333526a7cf83c03ec8dddad822246aedceec23..bdc1dcdff3197716b1a39504bf0280030dd9c957 100644 (file)
@@ -158,7 +158,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri,
        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;
index be0f6a23be74224f4a83756306b5b30e784d4323..0f95c9ba8b7661e7c9065769994ae57915f758cd 100644 (file)
@@ -176,7 +176,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator,
                if (num_nhs >= multipath_num)
                        break;
 
-               if (num_nhs >= ZEBRA_NUM_OF(nexthops))
+               if (num_nhs >= array_size(nexthops))
                        break;
 
                if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
index 5c375a6befc00018e8015c09052021644036c824..f1082a5996cafe1a9e91e4cf4a4b3363212d9b7d 100644 (file)
@@ -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]);
+               }
+       }
 }
 
 /*
index bb352dc2ffd698fb3176500324cbf928b4c7979c..d3ecd3695adb6457e3eeb84b46b5eb76e3b65c2a 100644 (file)
@@ -1188,8 +1188,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 +1392,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 +1420,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 +1446,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 +1455,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 +1464,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 +1488,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;
        }
 
index 0e1df1cc35016ef1c9f1597cdafbd6a1ec13aa6c..eddf6c8158cf693905366f8a72e24ea5ea8230a6 100644 (file)
@@ -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,10 @@ 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},
+       /* Any new route type added to zebra, should be mirrored here */
 
        /* no entry/default: 150 */
 };
@@ -161,8 +165,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 +473,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 +797,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
@@ -1172,7 +1168,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 +1196,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 +1227,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)) {
+               for_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 +1303,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 +2348,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 +2396,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 +2410,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 +2460,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 +2637,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 +2663,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 +2672,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,
@@ -3018,7 +2997,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 +3048,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 +3063,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 +3077,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:
@@ -3380,9 +3368,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 +3463,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,
index 220a8006d011c8cd08d1c5dafbe778a4e9adf37b..2917d0e7a8ff19c8604df1a70609d137db79ecc0 100644 (file)
@@ -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);
index 9cd9116eed01c9673ad1ecb4593c9dde5d26bf02..95a39411816ff4be7920f95c5934cd548deb2205 100644 (file)
 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;
 
index 5d1cbbe781d5de5b2f5ebbb5ae45000e946ab719..5355fa062e9c127c60f8d2dc195259730550d3f8 100644 (file)
@@ -1798,8 +1798,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);
index a81752d205998e751f0e4d58367da26a0ba3b253..63724fc350dec0241fcc60c8eb7d21b5963a0deb 100644 (file)
@@ -226,7 +226,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();
index b6e4b5c7260b05fdba6d100191ad23d74ddf3bb8..c1f07397d02df962dbff18d44c6bae92573f4207 100644 (file)
@@ -88,9 +88,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
index 871830dcf3182c022cb83f89125105baf1356217..1f2ff63286489b17d9a491d7c426d4ca67b82aa3 100644 (file)
@@ -335,15 +335,13 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
                return NULL;
 
        if (vrf_id == VRF_DEFAULT) {
-               if (table_id == RT_TABLE_MAIN
-                   || table_id == zrouter.rtm_table_default)
+               if (table_id == RT_TABLE_MAIN)
                        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)
+               if (table_id == RT_TABLE_MAIN)
                        table = zebra_vrf_table(afi, safi, vrf_id);
                else
                        table = zebra_vrf_other_route_table(afi, table_id,
@@ -366,7 +364,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);
        }
 }
@@ -461,10 +459,8 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, uint32_t table_id,
        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) {
+       if (table_id != RT_TABLE_MAIN) {
+               if (zvrf->table_id == RT_TABLE_MAIN) {
                        /* 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
index 8cde07a1096b109cac9e8300d23dee554fd438c1..c4249b6366496dc9a9e4be9190c6251d931dc239 100644 (file)
@@ -230,7 +230,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 +385,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);
 
@@ -2556,40 +2556,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 +2799,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 +2902,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);
index 22c489e6073fc573ff1494a7a7560b6ba25088fc..baa050c9b90e1d19828f10f2c1b930a54b4a11c6 100644 (file)
@@ -2167,7 +2167,7 @@ static unsigned int neigh_hash_keymake(void *p)
                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);
 }
 
 /*
@@ -7858,12 +7858,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 +7955,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",
@@ -8779,6 +8785,13 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
                        return -1;
                }
 
+               if (zvrf->l3vni != vni) {
+                       snprintf(err, err_str_sz,
+                                       "VNI %d doesn't exist in VRF: %s",
+                                       vni, zvrf->vrf->name);
+                       return -1;
+               }
+
                if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) {
                        snprintf(err, ERR_STR_SZ,
                                 "prefix-routes-only is not set for the vni");
index df5f236c04f0d617687695058846951277bd6371..fbb5af875db20fd9c14db0f4e49d72bda7f4219a 100644 (file)
@@ -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);
index 90fd195712be79217cc84a98db35b5137d0c3bae..d6fdc0537419a7892f3916b10114f12ba4230c42 100644 (file)
@@ -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];
@@ -240,6 +237,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