]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #4272 from opensourcerouting/isis-prefix-sid-fix
authorDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 10 May 2019 15:06:56 +0000 (11:06 -0400)
committerGitHub <noreply@github.com>
Fri, 10 May 2019 15:06:56 +0000 (11:06 -0400)
isisd: fix display of the Extended IPv4 reachability TLV

57 files changed:
.gitignore
Makefile.am
bgpd/bgp_clist.c
bgpd/bgp_evpn_vty.c
bgpd/bgp_rpki.c
bgpd/bgp_vty.c
bgpd/bgpd.c
configure.ac
doc/developer/lists.rst
doc/user/basic.rst
doc/user/bgp.rst
doc/user/static.rst
doc/user/zebra.rst
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/isis_cli.c
lib/command.c
lib/frrstr.c
lib/frrstr.h
lib/if.c
lib/lib_errors.c
lib/lib_errors.h
lib/libfrr.c
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/prefix.c
lib/prefix.h
lib/routemap.c
lib/subdir.am
lib/table.c
lib/table.h
lib/typesafe.h
lib/vty.c
lib/vty.h
lib/yang.c
lib/yang_translator.c
lib/yang_wrappers.c
ospfd/ospf_te.c
pimd/pim_cmd.c
pimd/pim_ifchannel.c
pimd/pim_upstream.c
pimd/pim_zebra.c
tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref
tests/topotests/all-protocol-startup/test_all_protocol_startup.py
tools/checkpatch.pl
vtysh/vtysh.c
yang/libyang_plugins/subdir.am
zebra/rib.h
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_rnh.h
zebra/zebra_vrf.c

index 5003c97572b7b0d51aa74f5d7bd52cb965e2e8b9..df2b0a6bf39cad3077246b423ae980d55e6089c7 100644 (file)
@@ -49,6 +49,7 @@
 *.pb.h
 *.pb-c.h
 *.pb-c.c
+*.pb.cc
 *_clippy.c
 
 ### dist
@@ -87,5 +88,6 @@ GSYMS
 GRTAGS
 GPATH
 compile_commands.json
+.ccls-cache
 .dirstamp
 refix
index 9c6c8663ee535fa82090b56c8517acb8e0fdcc4e..546aa85fe79de8c910aa225610db609a33389961 100644 (file)
@@ -96,7 +96,6 @@ noinst_LIBRARIES =
 nodist_noinst_DATA =
 lib_LTLIBRARIES =
 module_LTLIBRARIES =
-libyang_plugins_LTLIBRARIES =
 pkginclude_HEADERS =
 nodist_pkginclude_HEADERS =
 dist_examples_DATA =
@@ -111,7 +110,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 +121,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 +197,7 @@ EXTRA_DIST += \
        doc/user/Makefile \
        eigrpd/Makefile \
        fpm/Makefile \
+       grpc/Makefile \
        isisd/Makefile \
        ldpd/Makefile \
        lib/Makefile \
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 63b3145b53cb1e421c9d5fc4a67004c2686da42c..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;
 }
 
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 01144f5c78ae94346297540e51ed5a1972b89a87..9a8da359ab11e2ae919a395cb6e62858212fbb1a 100644 (file)
@@ -14951,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 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 18c16634babeea50487be7afd7851a90cc49f222..86168a848731d1a88bf0ae03136959a44dba417a 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
 
@@ -447,6 +450,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],
@@ -1690,24 +1695,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 ---------------
@@ -1752,6 +1739,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 6d60420b2fb2b675596e513bf3f2678e7286b1f6..987b3b7f4fb3a849a8971a4a8fa46d5dbb05c791 100644 (file)
@@ -119,6 +119,8 @@ The common setup pattern will look like this:
 
 .. code-block:: c
 
+   #include <typesafe.h>
+
    PREDECL_XXX(Z)
    struct item {
        int otherdata;
@@ -159,26 +161,26 @@ Common iteration macros
 
 The following iteration macros work across all data structures:
 
-.. c:function:: for_each(Z, head, item)
+.. c:function:: for_each(Z, &head, item)
 
    Equivalent to:
 
    .. code-block:: c
 
-      for (item = Z_first(head); item; item = Z_next(head, item))
+      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)
+.. 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);
+      for (item = Z_first(&head); item; item = next) {
+          next = Z_next_safe(&head, item);
           ...
       }
 
@@ -189,7 +191,7 @@ The following iteration macros work across all data structures:
       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)
+.. 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:
@@ -197,7 +199,7 @@ The following iteration macros work across all data structures:
    .. code-block:: c
 
       for (item = from; item; item = from) {
-          from = Z_next_safe(head, item);
+          from = Z_next_safe(&head, item);
           ...
       }
 
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 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 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 bb3d6e491c349abccd27c4dd1cd962eb55b5cd3e..51ada8bc62d306d4ab103a560e3f88b08178c56f 100644 (file)
@@ -738,6 +738,22 @@ zebra Terminal Mode Commands
    Display various statistics related to the installation and deletion
    of routes, neighbor updates, and LSP's into the kernel.
 
+.. index:: show zebra client [summary]
+.. clicmd:: show zebra client [summary]
+
+   Display statistics about clients that are connected to zebra.  This is
+   useful for debugging and seeing how much data is being passed between
+   zebra and it's clients.  If the summary form of the command is choosen
+   a table is displayed with shortened information.
+
+.. index:: show zebra router table summary
+.. clicmd:: show zebra router table summary
+
+   Display summarized data about tables created, their afi/safi/tableid
+   and how many routes each table contains.  Please note this is the
+   total number of route nodes in the table.  Which will be higher than
+   the actual number of routes that are held.
+
 .. index:: show zebra fpm stats
 .. clicmd:: show zebra fpm stats
 
diff --git a/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)/$^
index 00948528259d56d158b297677134488c9bb7df98..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);
@@ -1769,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);
-
-       return nb_cli_apply_changes(vty, NULL);
+unlock:
+       pthread_rwlock_unlock(&running_config->lock);
 
-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);
 }
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 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 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;
 }
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 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 4898a8d0fa776e0650a6b440f4a3e8aa4b201e97..e8bdaa68fb4cd49ef75c1b2aef0172ae24331416 100644 (file)
@@ -1308,6 +1308,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;
                }
@@ -1828,6 +1838,8 @@ 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:
@@ -2803,6 +2815,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 7027f3f0daadd67674aaac0ab877508f018db590..0b7af18fcffaf54df27367f70e103841d6ae1d54 100644 (file)
@@ -309,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
 #
@@ -353,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 bbf3ce8f1cb508c8aaa447064f02d67d954509ba..94002da599c23ea4d66a3184bbc63486f2825cc3 100644 (file)
@@ -275,7 +275,7 @@ macro_pure size_t prefix ## _count(struct prefix##_head *h)                    \
 #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn)                      \
        _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn)                   \
                                                                                \
-macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item)  \
+macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item)  \
 {                                                                              \
        struct ssort_item *sitem = h->sh.first;                                \
        int cmpval = 0;                                                        \
@@ -383,7 +383,7 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item)         \
        *np = &item->field.hi;                                                 \
        return NULL;                                                           \
 }                                                                              \
-macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item)  \
+macro_inline type *prefix ## _find(const struct prefix##_head *h, const type *item)  \
 {                                                                              \
        if (!h->hh.tabshift)                                                   \
                return NULL;                                                   \
@@ -576,7 +576,7 @@ macro_inline int prefix ## __cmp(const struct sskip_item *a,                   \
        return cmpfn(container_of(a, type, field.si),                          \
                        container_of(b, type, field.si));                      \
 }                                                                              \
-macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item)  \
+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);                    \
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 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 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 9c1cb38012c7db142b5892eacdf72b42173ccc4c..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
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 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 11d39bb84f8582b7852e9bb9f0b673d5c9782433..b27d2e0581b76760073a0c1b72125c88695b7633 100644 (file)
@@ -1296,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 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
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 eff1e996ed144a95f69151e1dff2e82e71b6bdc1..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:
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 e26831e1a6427b7d4e2dda7f625aff06aa735d37..8a59cea6d0d5a7000c3999958c4d4faf0de0a93f 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;
@@ -135,7 +175,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 +191,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 +200,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 +230,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 +451,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 2945a5ef9d186c09a9356371671b86e1b401aefd..3623852afd0bab61071c9a930661420f33ea9ab9 100644 (file)
@@ -1174,7 +1174,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;
        }
 
@@ -1202,7 +1202,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;
 
        /*
@@ -1234,7 +1233,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;
@@ -1310,7 +1309,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;
 
@@ -2355,7 +2354,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;
@@ -2403,7 +2402,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;
@@ -2418,12 +2416,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
@@ -2473,14 +2466,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;
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 315d5b49053935e629e09f42be0bb4c2735d1af0..5a18534ac4c54bedf0ba0fd39e74000a2dbade44 100644 (file)
@@ -355,7 +355,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);
        }
 }