frr: update to 7.4 and latest backports
authorLucian Cristian <redacted>
Fri, 24 Jul 2020 15:50:07 +0000 (18:50 +0300)
committerLucian Cristian <redacted>
Fri, 24 Jul 2020 15:50:07 +0000 (18:50 +0300)
changelogs: https://github.com/FRRouting/frr/releases/tag/frr-7.4

Signed-off-by: Lucian Cristian <redacted>
17 files changed:
net/frr/Makefile
net/frr/patches/000-bgpd_Actually_find_the_sequence_number.patch [new file with mode: 0644]
net/frr/patches/001-bgpd_Some_backports.patch [new file with mode: 0644]
net/frr/patches/001-vti_interface_fix.patch [deleted file]
net/frr/patches/002-lib_fix_route_map_description_memory_leak.patch [new file with mode: 0644]
net/frr/patches/003-bgpd_Add_command_to_show_only_established_sessions.patch [new file with mode: 0644]
net/frr/patches/004-Add_BFD_peer_awareness_to_frr-reload.patch [new file with mode: 0644]
net/frr/patches/005-vtysh_fixes.patch [new file with mode: 0644]
net/frr/patches/006-bgpd_how_the_real_next_hop_address.patch [new file with mode: 0644]
net/frr/patches/007-bgpd_Fix_the_bug_BGP_MRAI.patch [new file with mode: 0644]
net/frr/patches/010-add_yahng_filter.patch [deleted file]
net/frr/patches/010-add_yang_routemap.patch [deleted file]
net/frr/patches/011-mod_yang_routemap_model.patch [deleted file]
net/frr/patches/012-add_yang_filter.patch [deleted file]
net/frr/patches/013-backport_northbound.patch [deleted file]
net/frr/patches/014-backport_northbound.patch [deleted file]
net/frr/patches/098-fix_mips_libyang.patch [deleted file]

index eff07ee3c6920816f5a1987ef17471ff380cf799..c85a3200c6ac99d0173a736bcba6a1e49ad27fae 100644 (file)
@@ -7,14 +7,17 @@
 
 include $(TOPDIR)/rules.mk
 PKG_NAME:=frr
-PKG_VERSION:=7.3.1
+PKG_VERSION:=7.4
 PKG_RELEASE:=1
 
-PKG_SOURCE_URL:=https://github.com/FRRouting/frr/releases/download/$(PKG_NAME)-$(PKG_VERSION)/
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
-PKG_HASH:=85571b63d2774329b7e97871e4761f852066a17e99a8daae9972c6bd7a533e05
+PKG_SOURCE_URL:=https://github.com/FRRouting/frr/archive/
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_HASH:=3c8204fda1c9b178d8446562579bbbc49d134b98f3ad02aa56f68724a2f9e40a
 PKG_MAINTAINER:=Lucian Cristian <lucian.cristian@gmail.com>
 
+HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/$(PKG_NAME)-$(PKG_NAME)-$(PKG_VERSION)
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_NAME)-$(PKG_VERSION)
+
 PKG_LICENSE:=GPL-2.0-only LGPL-2.1-only
 
 PKG_DAEMON_AVAILABLE:= \
diff --git a/net/frr/patches/000-bgpd_Actually_find_the_sequence_number.patch b/net/frr/patches/000-bgpd_Actually_find_the_sequence_number.patch
new file mode 100644 (file)
index 0000000..ac3a7b4
--- /dev/null
@@ -0,0 +1,69 @@
+From 34f6d0c67a48e2117c061f6ccdcf1f512982fe8f Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@cumulusnetworks.com>
+Date: Tue, 2 Jun 2020 16:10:48 -0400
+Subject: [PATCH] bgpd: Actually find the sequence number for `bgp
+ extcommunity-list...`
+
+The code in the bgp extcommunity-list function was using
+argv_find to get the correct idx.  The problem was that
+we had already done argv_finds before and idx was non-zero
+thus having us always set the seq pointer to what was last
+looked up.  This causes us to pass in a value to the
+underlying function and it would just wisely ignore it
+causing a seq number of 0.
+
+We would then write this seq number of 0 and then immediately
+reject it on read in again.  BOO!
+
+Actually handle argv_find the way it was meant to be.
+
+Ticket:CM-29926
+Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
+---
+ bgpd/bgp_vty.c | 12 ++++--------
+ 1 file changed, 4 insertions(+), 8 deletions(-)
+
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 3669205ee3..9c8f1e1def 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -17617,8 +17617,7 @@ DEFUN (extcommunity_list_standard,
+       argv_find(argv, argc, "WORD", &idx);
+       cl_number_or_name = argv[idx]->arg;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+@@ -17663,8 +17662,7 @@ DEFUN (extcommunity_list_name_expanded,
+       argv_find(argv, argc, "WORD", &idx);
+       cl_number_or_name = argv[idx]->arg;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+@@ -17707,8 +17705,7 @@ DEFUN (no_extcommunity_list_standard_all,
+       char *seq = NULL;
+       int idx = 0;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       idx = 0;
+@@ -17772,8 +17769,7 @@ DEFUN (no_extcommunity_list_expanded_all,
+       char *seq = NULL;
+       int idx = 0;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       idx = 0;
diff --git a/net/frr/patches/001-bgpd_Some_backports.patch b/net/frr/patches/001-bgpd_Some_backports.patch
new file mode 100644 (file)
index 0000000..b67593a
--- /dev/null
@@ -0,0 +1,83 @@
+From acf6f22d150b0050afbdaf5887b8e25d1614db4c Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Thu, 2 Jul 2020 11:08:29 +0300
+Subject: [PATCH 1/2] bgpd: Return bool type for ecommunity_add_val and
+ subgroup_announce_check
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_ecommunity.c | 6 +++---
+ bgpd/bgp_route.c      | 2 +-
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
+index d13da74b04..7d5cac4d62 100644
+--- a/bgpd/bgp_ecommunity.c
++++ b/bgpd/bgp_ecommunity.c
+@@ -107,14 +107,14 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
+                           p[1] == eval->val[1]) {
+                               if (overwrite) {
+                                       memcpy(p, eval->val, ECOMMUNITY_SIZE);
+-                                      return 1;
++                                      return true;
+                               }
+-                              return 0;
++                              return false;
+                       }
+               }
+               int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
+               if (ret == 0)
+-                      return 0;
++                      return false;
+               if (ret > 0) {
+                       if (!unique)
+                               break;
+diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
+index 6ae7a59a14..7bfefde482 100644
+--- a/bgpd/bgp_route.c
++++ b/bgpd/bgp_route.c
+@@ -1941,7 +1941,7 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
+       /* Codification of AS 0 Processing */
+       if (aspath_check_as_zero(attr->aspath))
+-              return 0;
++              return false;
+       if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+               if (peer->sort == BGP_PEER_IBGP
+
+From d5a157b7c377081d23b136b5ba4849abdcbecd97 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Thu, 2 Jul 2020 11:39:40 +0300
+Subject: [PATCH 2/2] bgpd: Actually find the sequence number for
+ large-community-list
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_vty.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 9c8f1e1def..67ff31df8f 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -17235,8 +17235,7 @@ static int lcommunity_list_set_vty(struct vty *vty, int argc,
+       char *cl_name;
+       char *seq = NULL;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       idx = 0;
+@@ -17285,8 +17284,7 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc,
+       int idx = 0;
+       char *seq = NULL;
+-      argv_find(argv, argc, "(1-4294967295)", &idx);
+-      if (idx)
++      if (argv_find(argv, argc, "(1-4294967295)", &idx))
+               seq = argv[idx]->arg;
+       idx = 0;
diff --git a/net/frr/patches/001-vti_interface_fix.patch b/net/frr/patches/001-vti_interface_fix.patch
deleted file mode 100644 (file)
index 5f9573e..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
---- a/zebra/zebra_nhg.c        2019-10-18 01:59:17.582282539 +0300
-+++ b/zebra/zebra_nhg.c        2019-10-18 02:00:17.501997253 +0300
-@@ -1456,20 +1456,9 @@
-       while (rn) {
-               route_unlock_node(rn);
--              /* Lookup should halt if we've matched against ourselves ('top',
--               * if specified) - i.e., we cannot have a nexthop NH1 is
--               * resolved by a route NH1. The exception is if the route is a
--               * host route.
--               */
--              if (top && rn == top)
--                      if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
--                          || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
--                              if (IS_ZEBRA_DEBUG_RIB_DETAILED)
--                                      zlog_debug(
--                                              "\t%s: Matched against ourself and prefix length is not max bit length",
--                                              __PRETTY_FUNCTION__);
--                              return 0;
--                      }
-+              /* If lookup self prefix return immediately. */
-+              if (rn == top)
-+                  return 0;
-               /* Pick up selected route. */
-               /* However, do not resolve over default route unless explicitly
diff --git a/net/frr/patches/002-lib_fix_route_map_description_memory_leak.patch b/net/frr/patches/002-lib_fix_route_map_description_memory_leak.patch
new file mode 100644 (file)
index 0000000..abdfe34
--- /dev/null
@@ -0,0 +1,29 @@
+From cc45875e0d2af0b53100ec78364dc51b39a12ac9 Mon Sep 17 00:00:00 2001
+From: Rafael Zalamena <rzalamena@opensourcerouting.org>
+Date: Mon, 6 Jul 2020 11:39:27 -0300
+Subject: [PATCH] lib: fix route map description memory leak
+
+Route map entries are not getting a chance to call `description` string
+deallocation on shutdown or when the parent entry is destroyed, so lets
+add a code to handle this in the `route_map_index_delete` function.
+
+Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
+(cherry picked from commit f0951335830203426074ddca4317f84b477e4afb)
+---
+ lib/routemap.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/routemap.c b/lib/routemap.c
+index 3d69a3495a..3b45133450 100644
+--- a/lib/routemap.c
++++ b/lib/routemap.c
+@@ -971,6 +971,9 @@ void route_map_index_delete(struct route_map_index *index, int notify)
+               zlog_debug("Deleting route-map %s sequence %d",
+                          index->map->name, index->pref);
++      /* Free route map entry description. */
++      XFREE(MTYPE_TMP, index->description);
++
+       /* Free route map northbound hook contexts. */
+       while ((rhc = TAILQ_FIRST(&index->rhclist)) != NULL)
+               routemap_hook_context_free(rhc);
diff --git a/net/frr/patches/003-bgpd_Add_command_to_show_only_established_sessions.patch b/net/frr/patches/003-bgpd_Add_command_to_show_only_established_sessions.patch
new file mode 100644 (file)
index 0000000..c2b1214
--- /dev/null
@@ -0,0 +1,288 @@
+From 2939f712d152f7e3ae438cc0f1d96dd9485e7487 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Thu, 9 Jul 2020 16:00:27 +0300
+Subject: [PATCH 1/2] bgpd: Add command to show only established sessions
+
+```
+exit1-debian-9# show bgp summary
+
+IPv4 Unicast Summary:
+BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
+BGP table version 8
+RIB entries 15, using 2880 bytes of memory
+Peers 2, using 43 KiB of memory
+
+Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt
+192.168.0.2     4        200        10         6        0    0    0 00:00:35            8        8
+2a02:4780::2    4          0         0         1        0    0    0    never       Active        0
+
+Total number of neighbors 2
+exit1-debian-9# show bgp summary established
+
+IPv4 Unicast Summary:
+BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
+BGP table version 8
+RIB entries 15, using 2880 bytes of memory
+Peers 2, using 43 KiB of memory
+
+Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt
+192.168.0.2     4        200        10         6        0    0    0 00:00:39            8        8
+
+Total number of neighbors 2
+exit1-debian-9# show bgp summary failed
+
+IPv4 Unicast Summary:
+BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
+BGP table version 8
+RIB entries 15, using 2880 bytes of memory
+Peers 2, using 43 KiB of memory
+
+Neighbor        EstdCnt DropCnt ResetTime Reason
+2a02:4780::2          0       0     never Waiting for peer OPEN
+
+Total number of neighbors 2
+exit1-debian-9#
+```
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_evpn_vty.c | 11 ++++++++---
+ bgpd/bgp_vty.c      | 43 +++++++++++++++++++++++++++++++------------
+ bgpd/bgp_vty.h      |  3 ++-
+ 3 files changed, 41 insertions(+), 16 deletions(-)
+
+diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
+index 85604d856d..42987117d4 100644
+--- a/bgpd/bgp_evpn_vty.c
++++ b/bgpd/bgp_evpn_vty.c
+@@ -4077,7 +4077,7 @@ DEFUN(show_bgp_l2vpn_evpn_es,
+  */
+ DEFUN(show_bgp_l2vpn_evpn_summary,
+       show_bgp_l2vpn_evpn_summary_cmd,
+-      "show bgp [vrf VRFNAME] l2vpn evpn summary [failed] [json]",
++      "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [json]",
+       SHOW_STR
+       BGP_STR
+       "bgp vrf\n"
+@@ -4085,6 +4085,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary,
+       L2VPN_HELP_STR
+       EVPN_HELP_STR
+       "Summary of BGP neighbor status\n"
++      "Show only sessions in Established state\n"
+       "Show only sessions not in Established state\n"
+       JSON_STR)
+ {
+@@ -4092,13 +4093,17 @@ DEFUN(show_bgp_l2vpn_evpn_summary,
+       bool uj = use_json(argc, argv);
+       char *vrf = NULL;
+       bool show_failed = false;
++      bool show_established = false;
+       if (argv_find(argv, argc, "vrf", &idx_vrf))
+               vrf = argv[++idx_vrf]->arg;
+       if (argv_find(argv, argc, "failed", &idx_vrf))
+               show_failed = true;
+-      return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN,
+-                                  show_failed, uj);
++      if (argv_find(argv, argc, "established", &idx_vrf))
++              show_established = true;
++
++      return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, show_failed,
++                                  show_established, uj);
+ }
+ /*
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 67ff31df8f..78521457fd 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -8772,7 +8772,8 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
+ /* Show BGP peer's summary information. */
+ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
+-                          bool show_failed, bool use_json)
++                          bool show_failed, bool show_established,
++                          bool use_json)
+ {
+       struct peer *peer;
+       struct listnode *node, *nnode;
+@@ -9104,6 +9105,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
+                               bgp_show_failed_summary(vty, bgp, peer,
+                                                       json_peer, 0, use_json);
+                       } else if (!show_failed) {
++                              if (show_established
++                                  && bgp_has_peer_failed(peer, afi, safi))
++                                      continue;
++
+                               json_peer = json_object_new_object();
+                               if (peer_dynamic_neighbor(peer)) {
+                                       json_object_boolean_true_add(json_peer,
+@@ -9193,6 +9198,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
+                                                       max_neighbor_width,
+                                                       use_json);
+                       } else if (!show_failed) {
++                              if (show_established
++                                  && bgp_has_peer_failed(peer, afi, safi))
++                                      continue;
++
+                               memset(dn_flag, '\0', sizeof(dn_flag));
+                               if (peer_dynamic_neighbor(peer)) {
+                                       dn_flag[0] = '*';
+@@ -9315,7 +9324,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
+ }
+ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
+-                                    int safi, bool show_failed, bool use_json)
++                                    int safi, bool show_failed,
++                                    bool show_established, bool use_json)
+ {
+       int is_first = 1;
+       int afi_wildcard = (afi == AFI_MAX);
+@@ -9358,7 +9368,8 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
+                                                                        false));
+                                       }
+                               }
+-                              bgp_show_summary(vty, bgp, afi, safi, show_failed,
++                              bgp_show_summary(vty, bgp, afi, safi,
++                                               show_failed, show_established,
+                                                use_json);
+                       }
+                       safi++;
+@@ -9382,6 +9393,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
+ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,
+                                              safi_t safi, bool show_failed,
++                                             bool show_established,
+                                              bool use_json)
+ {
+       struct listnode *node, *nnode;
+@@ -9411,7 +9423,7 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,
+                                       : bgp->name);
+               }
+               bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed,
+-                                        use_json);
++                                        show_established, use_json);
+       }
+       if (use_json)
+@@ -9421,15 +9433,16 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,
+ }
+ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+-                       safi_t safi, bool show_failed, bool use_json)
++                       safi_t safi, bool show_failed, bool show_established,
++                       bool use_json)
+ {
+       struct bgp *bgp;
+       if (name) {
+               if (strmatch(name, "all")) {
+-                      bgp_show_all_instances_summary_vty(vty, afi, safi,
+-                                                         show_failed,
+-                                                         use_json);
++                      bgp_show_all_instances_summary_vty(
++                              vty, afi, safi, show_failed, show_established,
++                              use_json);
+                       return CMD_SUCCESS;
+               } else {
+                       bgp = bgp_lookup_by_name(name);
+@@ -9444,7 +9457,8 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+                       }
+                       bgp_show_summary_afi_safi(vty, bgp, afi, safi,
+-                                                show_failed, use_json);
++                                                show_failed, show_established,
++                                                use_json);
+                       return CMD_SUCCESS;
+               }
+       }
+@@ -9453,7 +9467,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+       if (bgp)
+               bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed,
+-                                        use_json);
++                                        show_established, use_json);
+       else {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+@@ -9468,7 +9482,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+ /* `show [ip] bgp summary' commands. */
+ DEFUN (show_ip_bgp_summary,
+        show_ip_bgp_summary_cmd,
+-       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [failed] [json]",
++       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [established|failed] [json]",
+        SHOW_STR
+        IP_STR
+        BGP_STR
+@@ -9476,6 +9490,7 @@ DEFUN (show_ip_bgp_summary,
+        BGP_AFI_HELP_STR
+        BGP_SAFI_WITH_LABEL_HELP_STR
+        "Summary of BGP neighbor status\n"
++       "Show only sessions in Established state\n"
+        "Show only sessions not in Established state\n"
+        JSON_STR)
+ {
+@@ -9483,6 +9498,7 @@ DEFUN (show_ip_bgp_summary,
+       afi_t afi = AFI_MAX;
+       safi_t safi = SAFI_MAX;
+       bool show_failed = false;
++      bool show_established = false;
+       int idx = 0;
+@@ -9504,10 +9520,13 @@ DEFUN (show_ip_bgp_summary,
+       if (argv_find(argv, argc, "failed", &idx))
+               show_failed = true;
++      if (argv_find(argv, argc, "established", &idx))
++              show_established = true;
+       bool uj = use_json(argc, argv);
+-      return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, uj);
++      return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed,
++                                  show_established, uj);
+ }
+ const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json)
+diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
+index d6ca198d09..95eefbc36f 100644
+--- a/bgpd/bgp_vty.h
++++ b/bgpd/bgp_vty.h
+@@ -178,6 +178,7 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
+ int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv,
+                              int argc, struct bgp **bgp, bool use_json);
+ extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+-                              safi_t safi, bool show_failed, bool use_json);
++                              safi_t safi, bool show_failed,
++                              bool show_established, bool use_json);
+ #endif /* _QUAGGA_BGP_VTY_H */
+
+From 2600443342d8e21d30df2b6ca095a5f2d0d4de2d Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Thu, 9 Jul 2020 16:05:08 +0300
+Subject: [PATCH 2/2] doc: Add 'show bgp summary established' command
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ doc/user/bgp.rst | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
+index cb343e8dad..36227db604 100644
+--- a/doc/user/bgp.rst
++++ b/doc/user/bgp.rst
+@@ -2710,6 +2710,12 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`.
+    Show a bgp peer summary for peers that are not succesfully exchanging routes
+    for the specified address family, and subsequent address-family.
++.. index:: show bgp [afi] [safi] summary established [json]
++.. clicmd:: show bgp [afi] [safi] summary established [json]
++
++   Show a bgp peer summary for peers that are succesfully exchanging routes
++   for the specified address family, and subsequent address-family.
++
+ .. index:: show bgp [afi] [safi] neighbor [PEER]
+ .. clicmd:: show bgp [afi] [safi] neighbor [PEER]
diff --git a/net/frr/patches/004-Add_BFD_peer_awareness_to_frr-reload.patch b/net/frr/patches/004-Add_BFD_peer_awareness_to_frr-reload.patch
new file mode 100644 (file)
index 0000000..a8a612b
--- /dev/null
@@ -0,0 +1,57 @@
+From 692ce87393de9497a7821e9e0856ff70a7973ff6 Mon Sep 17 00:00:00 2001
+From: Paul Manley <paul.manley@wholefoods.com>
+Date: Thu, 9 Jul 2020 11:21:16 -0500
+Subject: [PATCH 1/2] tools: create sub-context for bfd peers
+
+add lines starting with 'peer' to the list of sub-contexts that are handled by frr-reload.py.
+
+https://github.com/FRRouting/frr/issues/6511#issuecomment-655163833
+
+Signed-off-by: Paul Manley <paul.manley@wholefoods.com>
+(cherry picked from commit 1c23a0aaa1c5d20af50af75b070e93e1eff21222)
+---
+ tools/frr-reload.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/frr-reload.py b/tools/frr-reload.py
+index d4020cdfc9..e9641b2b13 100755
+--- a/tools/frr-reload.py
++++ b/tools/frr-reload.py
+@@ -496,6 +496,7 @@ def load_contexts(self):
+                   line.startswith("vnc defaults") or
+                   line.startswith("vnc l2-group") or
+                   line.startswith("vnc nve-group") or
++                  line.startswith("peer") or
+                   line.startswith("member pseudowire")):
+                 main_ctx_key = []
+
+From 2604086c3d9face0aca2497a982782c865bb2b59 Mon Sep 17 00:00:00 2001
+From: Paul Manley <paul.manley@wholefoods.com>
+Date: Thu, 9 Jul 2020 11:25:34 -0500
+Subject: [PATCH 2/2] vtysh: properly exit BFD_PEER_NODE when marking file
+
+vtysh needs to be aware of how to properly exit a bfd peer when subsequent commands only succeed in a higher context.
+
+https://github.com/FRRouting/frr/issues/6511#issuecomment-656166206
+
+Signed-off-by: Paul Manley <paul.manley@wholefoods.com>
+(cherry picked from commit b727c12aabf1afc2b6e33f8590c9786e349e4fcb)
+---
+ vtysh/vtysh.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
+index 15ec866fc9..4fdf68c0e6 100644
+--- a/vtysh/vtysh.c
++++ b/vtysh/vtysh.c
+@@ -809,6 +809,9 @@ int vtysh_mark_file(const char *filename)
+                       } else if ((prev_node == KEYCHAIN_KEY_NODE)
+                                  && (tried == 1)) {
+                               vty_out(vty, "exit\n");
++                      } else if ((prev_node == BFD_PEER_NODE)
++                                 && (tried == 1)) {
++                              vty_out(vty, "exit\n");
+                       } else if (tried) {
+                               vty_out(vty, "end\n");
+                       }
diff --git a/net/frr/patches/005-vtysh_fixes.patch b/net/frr/patches/005-vtysh_fixes.patch
new file mode 100644 (file)
index 0000000..83983a0
--- /dev/null
@@ -0,0 +1,120 @@
+From cc5934ed5939315ba5d95bfaf052625762107205 Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@cumulusnetworks.com>
+Date: Tue, 30 Jun 2020 08:59:46 -0400
+Subject: [PATCH 1/2] vtysh: master is a non-sorted list
+
+The commit:
+a798241265a5808083a06b14ce1637d1ddf6a45a
+
+attempted to use sorted master lists to do faster lookups
+by using a RB Tree.  Unfortunately the original code
+was creating a list->cmp function *but* never using it.
+If you look at the commit, it clearly shows that the
+function listnode_add is used to insert but when you
+look at that function it is a tail push.
+
+Fixes: #6573
+
+Namely now this ordering is preserved:
+bgp as-path access-list originate-only permit ^$
+bgp as-path access-list originate-only deny .*
+
+Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
+---
+ vtysh/vtysh_config.c | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+
+diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
+index abbb111f9d..2ab9dd5a9a 100644
+--- a/vtysh/vtysh_config.c
++++ b/vtysh/vtysh_config.c
+@@ -34,7 +34,7 @@ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line")
+ vector configvec;
+-PREDECL_RBTREE_UNIQ(config_master);
++PREDECL_LIST(config_master);
+ struct config {
+       /* Configuration node name. */
+@@ -72,11 +72,6 @@ static struct config *config_new(void)
+       return config;
+ }
+-static int config_cmp(const struct config *c1, const struct config *c2)
+-{
+-      return strcmp(c1->name, c2->name);
+-}
+-
+ static void config_del(struct config *config)
+ {
+       list_delete(&config->line);
+@@ -84,13 +79,15 @@ static void config_del(struct config *config)
+       XFREE(MTYPE_VTYSH_CONFIG, config);
+ }
+-DECLARE_RBTREE_UNIQ(config_master, struct config, rbt_item, config_cmp)
++DECLARE_LIST(config_master, struct config, rbt_item)
+ static struct config *config_get(int index, const char *line)
+ {
+-      struct config *config;
++      struct config *config, *config_loop;
+       struct config_master_head *master;
++      config = config_loop = NULL;
++
+       master = vector_lookup_ensure(configvec, index);
+       if (!master) {
+@@ -99,8 +96,10 @@ static struct config *config_get(int index, const char *line)
+               vector_set_index(configvec, index, master);
+       }
+-      const struct config config_ref = { .name = (char *)line };
+-      config = config_master_find(master, &config_ref);
++      frr_each (config_master, master, config_loop) {
++              if (strcmp(config_loop->name, line) == 0)
++                      config = config_loop;
++      }
+       if (!config) {
+               config = config_new();
+@@ -109,7 +108,7 @@ static struct config *config_get(int index, const char *line)
+               config->line->cmp = (int (*)(void *, void *))line_cmp;
+               config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
+               config->index = index;
+-              config_master_add(master, config);
++              config_master_add_tail(master, config);
+       }
+       return config;
+ }
+
+From 3e4d90ec556649e11954f2f56b5282f95e7e013b Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@cumulusnetworks.com>
+Date: Tue, 30 Jun 2020 09:03:55 -0400
+Subject: [PATCH 2/2] vtysh: Improve lookup performance
+
+When we find the line we are interested in, stop looking.
+
+Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
+---
+ vtysh/vtysh_config.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
+index 2ab9dd5a9a..61bcf3b658 100644
+--- a/vtysh/vtysh_config.c
++++ b/vtysh/vtysh_config.c
+@@ -97,8 +97,10 @@ static struct config *config_get(int index, const char *line)
+       }
+       frr_each (config_master, master, config_loop) {
+-              if (strcmp(config_loop->name, line) == 0)
++              if (strcmp(config_loop->name, line) == 0) {
+                       config = config_loop;
++                      break;
++              }
+       }
+       if (!config) {
diff --git a/net/frr/patches/006-bgpd_how_the_real_next_hop_address.patch b/net/frr/patches/006-bgpd_how_the_real_next_hop_address.patch
new file mode 100644 (file)
index 0000000..b819d69
--- /dev/null
@@ -0,0 +1,835 @@
+From c6a5994609deec62c8aefa1fa15c517e32575ca3 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Wed, 6 May 2020 17:45:31 +0300
+Subject: [PATCH 1/4] tests: Remove bgp_show_ip_bgp_fqdn test
+
+Not really relevant for now.
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ .../bgp_show_ip_bgp_fqdn/__init__.py          |   0
+ .../bgp_show_ip_bgp_fqdn/r1/bgpd.conf         |   5 -
+ .../bgp_show_ip_bgp_fqdn/r1/zebra.conf        |   9 --
+ .../bgp_show_ip_bgp_fqdn/r2/bgpd.conf         |   5 -
+ .../bgp_show_ip_bgp_fqdn/r2/zebra.conf        |  12 --
+ .../bgp_show_ip_bgp_fqdn/r3/bgpd.conf         |   3 -
+ .../bgp_show_ip_bgp_fqdn/r3/zebra.conf        |   6 -
+ .../test_bgp_show_ip_bgp_fqdn.py              | 133 ------------------
+ 8 files changed, 173 deletions(-)
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r3/bgpd.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/r3/zebra.conf
+ delete mode 100644 tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
+
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py b/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py
+deleted file mode 100644
+index e69de29bb2..0000000000
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf
+deleted file mode 100644
+index f0df56e947..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf
++++ /dev/null
+@@ -1,5 +0,0 @@
+-router bgp 65000
+-  no bgp ebgp-requires-policy
+-  neighbor 192.168.255.2 remote-as 65001
+-  address-family ipv4 unicast
+-    redistribute connected
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf
+deleted file mode 100644
+index 0a283c06d5..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf
++++ /dev/null
+@@ -1,9 +0,0 @@
+-!
+-interface lo
+- ip address 172.16.255.254/32
+-!
+-interface r1-eth0
+- ip address 192.168.255.1/24
+-!
+-ip forwarding
+-!
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf
+deleted file mode 100644
+index 422a7345f9..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf
++++ /dev/null
+@@ -1,5 +0,0 @@
+-router bgp 65001
+-  no bgp ebgp-requires-policy
+-  bgp default show-hostname
+-  neighbor 192.168.255.1 remote-as 65000
+-  neighbor 192.168.254.1 remote-as 65001
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf
+deleted file mode 100644
+index e9e2e4391f..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf
++++ /dev/null
+@@ -1,12 +0,0 @@
+-!
+-interface lo
+- ip address 172.16.255.253/32
+-!
+-interface r2-eth0
+- ip address 192.168.255.2/24
+-!
+-interface r2-eth1
+- ip address 192.168.254.2/24
+-!
+-ip forwarding
+-!
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r3/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r3/bgpd.conf
+deleted file mode 100644
+index 8fcf6a736d..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r3/bgpd.conf
++++ /dev/null
+@@ -1,3 +0,0 @@
+-router bgp 65001
+-  bgp default show-hostname
+-  neighbor 192.168.254.2 remote-as 65001
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r3/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r3/zebra.conf
+deleted file mode 100644
+index a8b8bc38c5..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/r3/zebra.conf
++++ /dev/null
+@@ -1,6 +0,0 @@
+-!
+-interface r3-eth0
+- ip address 192.168.254.1/24
+-!
+-ip forwarding
+-!
+diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
+deleted file mode 100644
+index e8ad180935..0000000000
+--- a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
++++ /dev/null
+@@ -1,133 +0,0 @@
+-#!/usr/bin/env python
+-
+-#
+-# test_bgp_show_ip_bgp_fqdn.py
+-# Part of NetDEF Topology Tests
+-#
+-# Copyright (c) 2019 by
+-# Donatas Abraitis <donatas.abraitis@gmail.com>
+-#
+-# Permission to use, copy, modify, and/or distribute this software
+-# for any purpose with or without fee is hereby granted, provided
+-# that the above copyright notice and this permission notice appear
+-# in all copies.
+-#
+-# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+-# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+-# OF THIS SOFTWARE.
+-#
+-
+-"""
+-test_bgp_show_ip_bgp_fqdn.py:
+-Test if FQND is visible in `show [ip] bgp` output if
+-`bgp default show-hostname` is toggled.
+-
+-Topology:
+-r1 <-- eBGP --> r2 <-- iBGP --> r3
+-
+-1. Check if both hostname and ip are added to JSON output
+-for 172.16.255.254/32 on r2.
+-2. Check if only ip is added to JSON output for 172.16.255.254/32 on r3.
+-"""
+-
+-import os
+-import sys
+-import json
+-import time
+-import pytest
+-import functools
+-
+-CWD = os.path.dirname(os.path.realpath(__file__))
+-sys.path.append(os.path.join(CWD, "../"))
+-
+-# pylint: disable=C0413
+-from lib import topotest
+-from lib.topogen import Topogen, TopoRouter, get_topogen
+-from lib.topolog import logger
+-from mininet.topo import Topo
+-
+-
+-class TemplateTopo(Topo):
+-    def build(self, *_args, **_opts):
+-        tgen = get_topogen(self)
+-
+-        for routern in range(1, 4):
+-            tgen.add_router("r{}".format(routern))
+-
+-        switch = tgen.add_switch("s1")
+-        switch.add_link(tgen.gears["r1"])
+-        switch.add_link(tgen.gears["r2"])
+-
+-        switch = tgen.add_switch("s2")
+-        switch.add_link(tgen.gears["r2"])
+-        switch.add_link(tgen.gears["r3"])
+-
+-
+-def setup_module(mod):
+-    tgen = Topogen(TemplateTopo, mod.__name__)
+-    tgen.start_topology()
+-
+-    router_list = tgen.routers()
+-
+-    for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+-        router.load_config(
+-            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+-        )
+-        router.load_config(
+-            TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+-        )
+-
+-    tgen.start_router()
+-
+-
+-def teardown_module(mod):
+-    tgen = get_topogen()
+-    tgen.stop_topology()
+-
+-
+-def test_bgp_show_ip_bgp_hostname():
+-    tgen = get_topogen()
+-
+-    if tgen.routers_have_failure():
+-        pytest.skip(tgen.errors)
+-
+-    def _bgp_converge(router):
+-        output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+-        expected = {"prefix": "172.16.255.254/32"}
+-        return topotest.json_cmp(output, expected)
+-
+-    def _bgp_show_nexthop_hostname_and_ip(router):
+-        output = json.loads(router.vtysh_cmd("show ip bgp json"))
+-        for nh in output["routes"]["172.16.255.254/32"][0]["nexthops"]:
+-            if "hostname" in nh and "ip" in nh:
+-                return True
+-        return False
+-
+-    def _bgp_show_nexthop_ip_only(router):
+-        output = json.loads(router.vtysh_cmd("show ip bgp json"))
+-        for nh in output["routes"]["172.16.255.254/32"][0]["nexthops"]:
+-            if "ip" in nh and not "hostname" in nh:
+-                return True
+-        return False
+-
+-    test_func = functools.partial(_bgp_converge, tgen.gears["r2"])
+-    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+-
+-    test_func = functools.partial(_bgp_converge, tgen.gears["r3"])
+-    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+-
+-    assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"])
+-    assert _bgp_show_nexthop_hostname_and_ip(tgen.gears["r2"]) == True
+-
+-    assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r3"])
+-    assert _bgp_show_nexthop_ip_only(tgen.gears["r3"]) == True
+-
+-
+-if __name__ == "__main__":
+-    args = ["-s"] + sys.argv[1:]
+-    sys.exit(pytest.main(args))
+
+From e7cc3d21452bd771a97bc46ab5a1e4853c46f944 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Wed, 6 May 2020 17:46:10 +0300
+Subject: [PATCH 2/4] bgpd: Show the real next-hop address in addition to
+ hostname in `show bgp`
+
+It's hard to cope with cases when next-hop is changed/unchanged or
+peers are non-direct.
+
+It would be better to show the hostname and nexthop IP address (both)
+under `show bgp` to quickly identify the source and the real next-hop
+of the route.
+
+If `bgp default show-nexthop-hostname` is toggled the output looks like:
+```
+spine1-debian-9# show bgp
+BGP table version is 1, local router ID is 2.2.2.2, vrf id 0
+Default local pref 100, local AS 65002
+Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+               i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network          Next Hop            Metric LocPrf Weight Path
+*  2a02:4780::/64   fe80::a00:27ff:fe09:f8a3(exit1-debian-9)
+                                             0             0 65001 ?
+
+spine1-debian-9# show ip bgp
+BGP table version is 5, local router ID is 2.2.2.2, vrf id 0
+Default local pref 100, local AS 65002
+Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+               i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network          Next Hop            Metric LocPrf Weight Path
+*> 10.255.255.0/24  192.168.0.1(exit1-debian-9)
+                                             0             0 65001 ?
+```
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_route.c | 161 ++++++++++++++++++++++++++++++-----------------
+ bgpd/bgp_vty.c   |  45 +++++++++++++
+ bgpd/bgpd.h      |   1 +
+ 3 files changed, 149 insertions(+), 58 deletions(-)
+
+diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
+index 7bfefde482..f033f525e5 100644
+--- a/bgpd/bgp_route.c
++++ b/bgpd/bgp_route.c
+@@ -7559,8 +7559,7 @@ static char *bgp_nexthop_hostname(struct peer *peer,
+                                 struct bgp_nexthop_cache *bnc)
+ {
+       if (peer->hostname
+-          && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_HOSTNAME) && bnc
+-          && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
++          && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME))
+               return peer->hostname;
+       return NULL;
+ }
+@@ -7570,6 +7569,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                  struct bgp_path_info *path, int display, safi_t safi,
+                  json_object *json_paths)
+ {
++      int len;
+       struct attr *attr = path->attr;
+       json_object *json_path = NULL;
+       json_object *json_nexthops = NULL;
+@@ -7681,10 +7681,19 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                                              : "ipv6");
+                       json_object_boolean_true_add(json_nexthop_global,
+                                                    "used");
+-              } else
+-                      vty_out(vty, "%s%s",
+-                              nexthop_hostname ? nexthop_hostname : nexthop,
+-                              vrf_id_str);
++              } else {
++                      if (nexthop_hostname)
++                              len = vty_out(vty, "%s(%s)%s", nexthop,
++                                            nexthop_hostname, vrf_id_str);
++                      else
++                              len = vty_out(vty, "%s%s", nexthop, vrf_id_str);
++
++                      len = 16 - len;
++                      if (len < 1)
++                              vty_out(vty, "\n%*s", 36, " ");
++                      else
++                              vty_out(vty, "%*s", len, " ");
++              }
+       } else if (safi == SAFI_EVPN) {
+               if (json_paths) {
+                       json_nexthop_global = json_object_new_object();
+@@ -7701,11 +7710,20 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                              "ipv4");
+                       json_object_boolean_true_add(json_nexthop_global,
+                                                    "used");
+-              } else
+-                      vty_out(vty, "%-16s%s",
+-                              nexthop_hostname ? nexthop_hostname
+-                                               : inet_ntoa(attr->nexthop),
+-                              vrf_id_str);
++              } else {
++                      if (nexthop_hostname)
++                              len = vty_out(vty, "%pI4(%s)%s", &attr->nexthop,
++                                            nexthop_hostname, vrf_id_str);
++                      else
++                              len = vty_out(vty, "%pI4%s", &attr->nexthop,
++                                            vrf_id_str);
++
++                      len = 16 - len;
++                      if (len < 1)
++                              vty_out(vty, "\n%*s", 36, " ");
++                      else
++                              vty_out(vty, "%*s", len, " ");
++              }
+       } else if (safi == SAFI_FLOWSPEC) {
+               if (attr->nexthop.s_addr != INADDR_ANY) {
+                       if (json_paths) {
+@@ -7726,10 +7744,21 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                                       json_nexthop_global,
+                                                            "used");
+                       } else {
+-                              vty_out(vty, "%-16s",
+-                                      nexthop_hostname
+-                                              ? nexthop_hostname
+-                                              : inet_ntoa(attr->nexthop));
++                              if (nexthop_hostname)
++                                      len = vty_out(vty, "%pI4(%s)%s",
++                                                    &attr->nexthop,
++                                                    nexthop_hostname,
++                                                    vrf_id_str);
++                              else
++                                      len = vty_out(vty, "%pI4%s",
++                                                    &attr->nexthop,
++                                                    vrf_id_str);
++
++                              len = 16 - len;
++                              if (len < 1)
++                                      vty_out(vty, "\n%*s", 36, " ");
++                              else
++                                      vty_out(vty, "%*s", len, " ");
+                       }
+               }
+       } else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+@@ -7749,19 +7778,23 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                       json_object_boolean_true_add(json_nexthop_global,
+                                                    "used");
+               } else {
+-                      char buf[BUFSIZ];
++                      if (nexthop_hostname)
++                              len = vty_out(vty, "%pI4(%s)%s", &attr->nexthop,
++                                            nexthop_hostname, vrf_id_str);
++                      else
++                              len = vty_out(vty, "%pI4%s", &attr->nexthop,
++                                            vrf_id_str);
+-                      snprintf(buf, sizeof(buf), "%s%s",
+-                               nexthop_hostname ? nexthop_hostname
+-                                                : inet_ntoa(attr->nexthop),
+-                               vrf_id_str);
+-                      vty_out(vty, "%-16s", buf);
++                      len = 16 - len;
++                      if (len < 1)
++                              vty_out(vty, "\n%*s", 36, " ");
++                      else
++                              vty_out(vty, "%*s", len, " ");
+               }
+       }
+       /* IPv6 Next Hop */
+       else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+-              int len;
+               char buf[BUFSIZ];
+               if (json_paths) {
+@@ -7835,15 +7868,18 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                       else
+                                               vty_out(vty, "%*s", len, " ");
+                               } else {
+-                                      len = vty_out(
+-                                              vty, "%s%s",
+-                                              nexthop_hostname
+-                                                      ? nexthop_hostname
+-                                                      : inet_ntop(
+-                                                                AF_INET6,
+-                                                                &attr->mp_nexthop_local,
+-                                                                buf, BUFSIZ),
+-                                              vrf_id_str);
++                                      if (nexthop_hostname)
++                                              len = vty_out(
++                                                      vty, "%pI6(%s)%s",
++                                                      &attr->mp_nexthop_local,
++                                                      nexthop_hostname,
++                                                      vrf_id_str);
++                                      else
++                                              len = vty_out(
++                                                      vty, "%pI6%s",
++                                                      &attr->mp_nexthop_local,
++                                                      vrf_id_str);
++
+                                       len = 16 - len;
+                                       if (len < 1)
+@@ -7852,15 +7888,16 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                               vty_out(vty, "%*s", len, " ");
+                               }
+                       } else {
+-                              len = vty_out(
+-                                      vty, "%s%s",
+-                                      nexthop_hostname
+-                                              ? nexthop_hostname
+-                                              : inet_ntop(
+-                                                        AF_INET6,
+-                                                        &attr->mp_nexthop_global,
+-                                                        buf, BUFSIZ),
+-                                      vrf_id_str);
++                              if (nexthop_hostname)
++                                      len = vty_out(vty, "%pI6(%s)%s",
++                                                    &attr->mp_nexthop_global,
++                                                    nexthop_hostname,
++                                                    vrf_id_str);
++                              else
++                                      len = vty_out(vty, "%pI6%s",
++                                                    &attr->mp_nexthop_global,
++                                                    vrf_id_str);
++
+                               len = 16 - len;
+                               if (len < 1)
+@@ -7986,6 +8023,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
+ {
+       json_object *json_status = NULL;
+       json_object *json_net = NULL;
++      int len;
+       char buff[BUFSIZ];
+       /* Route status display. */
+@@ -8079,7 +8117,6 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
+                                               inet_ntoa(attr->nexthop));
+                       } else if (p->family == AF_INET6
+                                  || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+-                              int len;
+                               char buf[BUFSIZ];
+                               len = vty_out(
+@@ -8823,12 +8860,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+-                      } else
+-                              vty_out(vty, "    %s",
+-                                      nexthop_hostname
+-                                              ? nexthop_hostname
+-                                              : inet_ntoa(
+-                                                        attr->mp_nexthop_global_in));
++                      } else {
++                              if (nexthop_hostname)
++                                      vty_out(vty, "    %pI4(%s)",
++                                              &attr->mp_nexthop_global_in,
++                                              nexthop_hostname);
++                              else
++                                      vty_out(vty, "    %pI4",
++                                              &attr->mp_nexthop_global_in);
++                      }
+               } else {
+                       if (json_paths) {
+                               json_object_string_add(
+@@ -8839,11 +8879,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+-                      } else
+-                              vty_out(vty, "    %s",
+-                                      nexthop_hostname
+-                                              ? nexthop_hostname
+-                                              : inet_ntoa(attr->nexthop));
++                      } else {
++                              if (nexthop_hostname)
++                                      vty_out(vty, "    %pI4(%s)",
++                                              &attr->nexthop,
++                                              nexthop_hostname);
++                              else
++                                      vty_out(vty, "    %pI4",
++                                              &attr->nexthop);
++                      }
+               }
+               if (json_paths)
+@@ -8866,12 +8910,13 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                       json_object_string_add(json_nexthop_global, "scope",
+                                              "global");
+               } else {
+-                      vty_out(vty, "    %s",
+-                              nexthop_hostname
+-                                      ? nexthop_hostname
+-                                      : inet_ntop(AF_INET6,
+-                                                  &attr->mp_nexthop_global,
+-                                                  buf, INET6_ADDRSTRLEN));
++                      if (nexthop_hostname)
++                              vty_out(vty, "    %pI6(%s)",
++                                      &attr->mp_nexthop_global,
++                                      nexthop_hostname);
++                      else
++                              vty_out(vty, "    %pI6",
++                                      &attr->mp_nexthop_global);
+               }
+       }
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 78521457fd..7f00ff3fbe 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -84,6 +84,10 @@ FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME,
+       { .val_bool = true, .match_profile = "datacenter", },
+       { .val_bool = false },
+ )
++FRR_CFG_DEFAULT_BOOL(BGP_SHOW_NEXTHOP_HOSTNAME,
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
++)
+ FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES,
+       { .val_bool = true, .match_profile = "datacenter", },
+       { .val_bool = false },
+@@ -422,6 +426,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+                       SET_FLAG((*bgp)->flags, BGP_FLAG_IMPORT_CHECK);
+               if (DFLT_BGP_SHOW_HOSTNAME)
+                       SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_HOSTNAME);
++              if (DFLT_BGP_SHOW_NEXTHOP_HOSTNAME)
++                      SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
+               if (DFLT_BGP_LOG_NEIGHBOR_CHANGES)
+                       SET_FLAG((*bgp)->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+               if (DFLT_BGP_DETERMINISTIC_MED)
+@@ -3100,6 +3106,32 @@ DEFUN (no_bgp_default_show_hostname,
+       return CMD_SUCCESS;
+ }
++/* Display hostname in certain command outputs */
++DEFUN (bgp_default_show_nexthop_hostname,
++       bgp_default_show_nexthop_hostname_cmd,
++       "bgp default show-nexthop-hostname",
++       "BGP specific commands\n"
++       "Configure BGP defaults\n"
++       "Show hostname for nexthop in certain command outputs\n")
++{
++      VTY_DECLVAR_CONTEXT(bgp, bgp);
++      SET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
++      return CMD_SUCCESS;
++}
++
++DEFUN (no_bgp_default_show_nexthop_hostname,
++       no_bgp_default_show_nexthop_hostname_cmd,
++       "no bgp default show-nexthop-hostname",
++       NO_STR
++       "BGP specific commands\n"
++       "Configure BGP defaults\n"
++       "Show hostname for nexthop in certain command outputs\n")
++{
++      VTY_DECLVAR_CONTEXT(bgp, bgp);
++      UNSET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
++      return CMD_SUCCESS;
++}
++
+ /* "bgp network import-check" configuration.  */
+ DEFUN (bgp_network_import_check,
+        bgp_network_import_check_cmd,
+@@ -15190,6 +15222,15 @@ int bgp_config_write(struct vty *vty)
+                                       ? ""
+                                       : "no ");
++              /* BGP default show-nexthop-hostname */
++              if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME)
++                  != SAVE_BGP_SHOW_HOSTNAME)
++                      vty_out(vty, " %sbgp default show-nexthop-hostname\n",
++                              CHECK_FLAG(bgp->flags,
++                                         BGP_FLAG_SHOW_NEXTHOP_HOSTNAME)
++                                      ? ""
++                                      : "no ");
++
+               /* BGP default subgroup-pkt-queue-max. */
+               if (bgp->default_subgroup_pkt_queue_max
+                   != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
+@@ -15815,6 +15856,10 @@ void bgp_vty_init(void)
+       install_element(BGP_NODE, &bgp_default_show_hostname_cmd);
+       install_element(BGP_NODE, &no_bgp_default_show_hostname_cmd);
++      /* bgp default show-nexthop-hostname */
++      install_element(BGP_NODE, &bgp_default_show_nexthop_hostname_cmd);
++      install_element(BGP_NODE, &no_bgp_default_show_nexthop_hostname_cmd);
++
+       /* "bgp default subgroup-pkt-queue-max" commands. */
+       install_element(BGP_NODE, &bgp_default_subgroup_pkt_queue_max_cmd);
+       install_element(BGP_NODE, &no_bgp_default_subgroup_pkt_queue_max_cmd);
+diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
+index 4a5772a53b..4efc068dea 100644
+--- a/bgpd/bgpd.h
++++ b/bgpd/bgpd.h
+@@ -447,6 +447,7 @@ struct bgp {
+ #define BGP_FLAG_SELECT_DEFER_DISABLE     (1 << 23)
+ #define BGP_FLAG_GR_DISABLE_EOR           (1 << 24)
+ #define BGP_FLAG_EBGP_REQUIRES_POLICY (1 << 25)
++#define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1 << 26)
+       enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE]
+                                     [BGP_GLOBAL_GR_EVENT_CMD];
+
+From 104dfe5258cbeb0443fa4d6577794a1e5a5dafd3 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Wed, 6 May 2020 17:50:04 +0300
+Subject: [PATCH 3/4] bgpd: Add "hostname" in JSON output for `show bgp` family
+ outputs
+
+This adds hostname regardless if `bgp default show-hostname` enabled or not.
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_route.c | 40 ++++++++++++++++++++--------------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
+index f033f525e5..5f645fa871 100644
+--- a/bgpd/bgp_route.c
++++ b/bgpd/bgp_route.c
+@@ -7671,10 +7671,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              nexthop);
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              (af == AF_INET) ? "ipv4"
+@@ -7701,10 +7701,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              "ipv4");
+@@ -7735,10 +7735,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+-                              if (nexthop_hostname)
++                              if (path->peer->hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+-                                              nexthop_hostname);
++                                              path->peer->hostname);
+                               json_object_boolean_true_add(
+                                                       json_nexthop_global,
+@@ -7768,10 +7768,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              "ipv4");
+@@ -7804,10 +7804,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, BUFSIZ));
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              "ipv6");
+@@ -7826,10 +7826,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
+                                                 &attr->mp_nexthop_local, buf,
+                                                 BUFSIZ));
+-                              if (nexthop_hostname)
++                              if (path->peer->hostname)
+                                       json_object_string_add(
+                                               json_nexthop_ll, "hostname",
+-                                              nexthop_hostname);
++                                              path->peer->hostname);
+                               json_object_string_add(json_nexthop_ll, "afi",
+                                                      "ipv6");
+@@ -8856,10 +8856,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->mp_nexthop_global_in));
+-                              if (nexthop_hostname)
++                              if (path->peer->hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+-                                              nexthop_hostname);
++                                              path->peer->hostname);
+                       } else {
+                               if (nexthop_hostname)
+                                       vty_out(vty, "    %pI4(%s)",
+@@ -8875,10 +8875,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+-                              if (nexthop_hostname)
++                              if (path->peer->hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+-                                              nexthop_hostname);
++                                              path->peer->hostname);
+                       } else {
+                               if (nexthop_hostname)
+                                       vty_out(vty, "    %pI4(%s)",
+@@ -8900,10 +8900,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, INET6_ADDRSTRLEN));
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              "ipv6");
+@@ -9094,10 +9094,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_local,
+                                         buf, INET6_ADDRSTRLEN));
+-                      if (nexthop_hostname)
++                      if (path->peer->hostname)
+                               json_object_string_add(json_nexthop_ll,
+                                                      "hostname",
+-                                                     nexthop_hostname);
++                                                     path->peer->hostname);
+                       json_object_string_add(json_nexthop_ll, "afi", "ipv6");
+                       json_object_string_add(json_nexthop_ll, "scope",
+
+From 8df39282ea64e2a65a7910012627f78d080833b1 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Wed, 24 Jun 2020 17:26:27 +0300
+Subject: [PATCH 4/4] doc: Add some words about `bgp default
+ show-[nexthop]-hostname`
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ doc/user/bgp.rst | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
+index 36227db604..a6c29724b0 100644
+--- a/doc/user/bgp.rst
++++ b/doc/user/bgp.rst
+@@ -1366,6 +1366,19 @@ Configuring Peers
+    on by default or not.  This command defaults to on and is not displayed.
+    The `no bgp default ipv4-unicast` form of the command is displayed.
++.. index:: [no] bgp default show-hostname
++.. clicmd:: [no] bgp default show-hostname
++
++   This command shows the hostname of the peer in certain BGP commands
++   outputs. It's easier to troubleshoot if you have a number of BGP peers.
++
++.. index:: [no] bgp default show-nexthop-hostname
++.. clicmd:: [no] bgp default show-nexthop-hostname
++
++   This command shows the hostname of the next-hop in certain BGP commands
++   outputs. It's easier to troubleshoot if you have a number of BGP peers
++   and a number of routes to check.
++
+ .. index:: [no] neighbor PEER advertisement-interval (0-600)
+ .. clicmd:: [no] neighbor PEER advertisement-interval (0-600)
diff --git a/net/frr/patches/007-bgpd_Fix_the_bug_BGP_MRAI.patch b/net/frr/patches/007-bgpd_Fix_the_bug_BGP_MRAI.patch
new file mode 100644 (file)
index 0000000..a58f26c
--- /dev/null
@@ -0,0 +1,30 @@
+From 2bbe7133eb5cb97ba4b745cd251a8615cd2bd008 Mon Sep 17 00:00:00 2001
+From: Richard Wu <wutong23@baidu.com>
+Date: Fri, 5 Jun 2020 17:54:57 +0800
+Subject: [PATCH] bgpd: Fix the bug that BGP MRAI does not work.
+
+Issue: bgp_process_writes will be called when the fd is writable.
+       And it will bgp_generate_updgrp_packets to generate the
+       update packets no matter MRAI is set or not.
+Fix:   bgp_generate_updgrp_packets thread will return without sending
+       any update when MRAI timer is still running.
+
+Signed-off-by: Richard Wu <wutong23@baidu.com>
+---
+ bgpd/bgp_packet.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
+index 29c03f4014..6f1c033f2a 100644
+--- a/bgpd/bgp_packet.c
++++ b/bgpd/bgp_packet.c
+@@ -408,6 +408,9 @@ int bgp_generate_updgrp_packets(struct thread *thread)
+       if (peer->bgp->main_peers_update_hold)
+               return 0;
++      if (peer->t_routeadv)
++              return 0;
++
+       do {
+               s = NULL;
+               FOREACH_AFI_SAFI (afi, safi) {
diff --git a/net/frr/patches/010-add_yahng_filter.patch b/net/frr/patches/010-add_yahng_filter.patch
deleted file mode 100644 (file)
index 2409dd8..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-From 2332428d3c80ac3d3b4e1c0bdba830b098ef440f Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Fri, 5 Jul 2019 11:07:30 -0300
-Subject: [PATCH] yang: initial filter YANG model import
-
-This model contains the description of access-list, prefix-list and
-other lists used by route map and other filtering interfaces.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- yang/frr-filter.yang | 365 +++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 365 insertions(+)
- create mode 100644 yang/frr-filter.yang
-
-diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang
-new file mode 100644
-index 0000000000..92af6aebfd
---- /dev/null
-+++ b/yang/frr-filter.yang
-@@ -0,0 +1,365 @@
-+module frr-filter {
-+  yang-version 1.1;
-+  namespace "http://frrouting.org/yang/filter";
-+  prefix frr-filter;
-+
-+  import ietf-inet-types {
-+    prefix inet;
-+  }
-+  import ietf-yang-types {
-+    prefix yang;
-+  }
-+
-+  organization "Free Range Routing";
-+  contact
-+    "FRR Users List:       <mailto:frog@lists.frrouting.org>
-+     FRR Development List: <mailto:dev@lists.frrouting.org>";
-+  description "This module defines filter settings";
-+
-+  revision 2019-07-04 {
-+    description "Initial revision";
-+  }
-+
-+  /*
-+   * Types.
-+   */
-+  typedef access-list-standard {
-+    description "Standard IPv4 access list (any, host or a prefix)";
-+    type uint16 {
-+      range "1..99 | 1300..1999";
-+    }
-+  }
-+
-+  typedef access-list-extended {
-+    description
-+      "Extended IPv4 access list (source / destination any, hosts or prefixes)";
-+    type uint16 {
-+      range "100..199 | 2000..2699";
-+    }
-+  }
-+
-+  typedef access-list-legacy {
-+    description "Standard/Extended IPv4 access list";
-+    type uint16 {
-+      range "1..199 | 1300..2699";
-+    }
-+  }
-+
-+  typedef access-list-name {
-+    description "Access list name formatting";
-+    type string;
-+  }
-+
-+  typedef access-list-sequence {
-+    description "Access list sequence number";
-+    type uint32 {
-+      range "1..4294967295";
-+    }
-+  }
-+
-+  typedef access-list-action {
-+    description "Access list return action on match";
-+    type enumeration {
-+      enum deny {
-+        description "Deny an entry";
-+        value 0;
-+      }
-+      enum permit {
-+        description "Accept an entry";
-+        value 1;
-+      }
-+    }
-+  }
-+
-+  /*
-+   * Configuration data.
-+   */
-+  container filter-list {
-+    list access-list-legacy {
-+      description "Access list legacy instance";
-+
-+      key "number sequence";
-+
-+      leaf number {
-+        description "Access list sequence value";
-+        type access-list-legacy;
-+      }
-+
-+      leaf sequence {
-+        description "Access list sequence value";
-+        type access-list-sequence;
-+      }
-+
-+      leaf action {
-+        description "Access list action on match";
-+        type access-list-action;
-+        mandatory true;
-+      }
-+
-+      leaf remark {
-+        description "Access list remark";
-+        type string;
-+      }
-+
-+      choice value {
-+        description
-+          "Standard access list: value to match.
-+           Extended access list: source value to match.";
-+        mandatory true;
-+
-+        case host {
-+          leaf host {
-+            description "Host to match";
-+            type inet:ipv4-address;
-+          }
-+        }
-+        case network {
-+          leaf network {
-+            description "Network to match";
-+            type inet:ipv4-prefix;
-+          }
-+        }
-+        case any {
-+          leaf any {
-+            description "Match any";
-+            type empty;
-+          }
-+        }
-+      }
-+
-+      choice extended-value {
-+        when "./sequence >= 100 and ./sequence <= 199 or
-+              ./sequence >= 2000 and ./sequence <= 2699";
-+        description "Destination value to match";
-+
-+        case destination-host {
-+          leaf destination-host {
-+            description "Host to match";
-+            type inet:ipv4-address;
-+          }
-+        }
-+        case destination-network {
-+          leaf destination-network {
-+            description "Network to match";
-+            type inet:ipv4-prefix;
-+          }
-+        }
-+        case destination-any {
-+          leaf destination-any {
-+            description "Match any";
-+            type empty;
-+          }
-+        }
-+      }
-+    }
-+
-+    list access-list {
-+      description "Access list instance";
-+
-+      key "type identifier sequence";
-+
-+      leaf type {
-+        description "Access list content type";
-+        type enumeration {
-+          enum ipv4 {
-+            description "Internet Protocol address version 4";
-+            value 0;
-+          }
-+          enum ipv6 {
-+            description "Internet Protocol address version 6";
-+            value 1;
-+          }
-+          enum mac {
-+            description "Media Access Control address";
-+            value 2;
-+          }
-+
-+          /*
-+           * Protocol YANG models should augment the parent node to
-+           * contain the routing protocol specific value. The protocol
-+           * must also augment `value` leaf to include its specific
-+           * values or expand the `when` statement on the existing cases.
-+           */
-+          enum custom {
-+            description "Custom data type";
-+            value 100;
-+          }
-+        }
-+      }
-+
-+      leaf identifier {
-+        description "Access list identifier";
-+        type access-list-name;
-+      }
-+
-+      leaf sequence {
-+        description "Access list sequence value";
-+        type access-list-sequence;
-+      }
-+
-+      leaf action {
-+        description "Access list action on match";
-+        type access-list-action;
-+        mandatory true;
-+      }
-+
-+      leaf remark {
-+        description "Access list remark";
-+        type string;
-+      }
-+
-+      choice value {
-+        description "Access list value to match";
-+        mandatory true;
-+
-+        case ipv4-prefix {
-+          when "./type = 'ipv4'";
-+
-+          leaf ipv4-prefix {
-+            description "Configure IPv4 prefix to match";
-+            type inet:ipv4-prefix;
-+          }
-+
-+          leaf ipv4-exact-match {
-+            description "Exact match of prefix";
-+            type boolean;
-+            default false;
-+          }
-+        }
-+        case ipv6-prefix {
-+          when "./type = 'ipv6'";
-+
-+          leaf ipv6-prefix {
-+            description "Configure IPv6 prefix to match";
-+            type inet:ipv6-prefix;
-+          }
-+
-+          leaf ipv6-exact-match {
-+            description "Exact match of prefix";
-+            type boolean;
-+            default false;
-+          }
-+        }
-+        case mac {
-+          when "./type = 'mac'";
-+
-+          leaf mac {
-+            description "Configure MAC address to match";
-+            type yang:mac-address;
-+          }
-+        }
-+        case any {
-+          leaf any {
-+            description "Match anything";
-+            type empty;
-+          }
-+        }
-+      }
-+    }
-+
-+    list prefix-list {
-+      description "Prefix list instance";
-+
-+      key "type name sequence";
-+
-+      leaf type {
-+        description "Prefix list type";
-+        type enumeration {
-+          enum ipv4 {
-+            description "Internet Protocol address version 4";
-+            value 0;
-+          }
-+          enum ipv6 {
-+            description "Internet Protocol address version 6";
-+            value 1;
-+          }
-+        }
-+      }
-+
-+      leaf name {
-+        description "Prefix list name";
-+        type access-list-name;
-+      }
-+
-+      leaf sequence {
-+        description "Access list sequence value";
-+        type access-list-sequence;
-+      }
-+
-+      leaf action {
-+        description "Prefix list action on match";
-+        type access-list-action;
-+        mandatory true;
-+      }
-+
-+      leaf description {
-+        description "Prefix list user description";
-+        type string;
-+      }
-+
-+      choice value {
-+        description "Prefix list value to match";
-+        mandatory true;
-+
-+        case ipv4-prefix {
-+          when "./type = 'ipv4'";
-+
-+          leaf ipv4-prefix {
-+            description "Configure IPv4 prefix to match";
-+            type inet:ipv4-prefix;
-+          }
-+
-+          leaf ipv4-prefix-length-greater-or-equal {
-+            description
-+              "Specifies if matching prefixes with length greater than
-+               or equal to value";
-+            type uint8 {
-+              range "0..32";
-+            }
-+          }
-+
-+          leaf ipv4-prefix-length-lesser-or-equal {
-+            description
-+              "Specifies if matching prefixes with length lesser than
-+               or equal to value";
-+            type uint8 {
-+              range "0..32";
-+            }
-+          }
-+        }
-+        case ipv6-prefix {
-+          when "./type = 'ipv6'";
-+
-+          leaf ipv6-prefix {
-+            description "Configure IPv6 prefix to match";
-+            type inet:ipv6-prefix;
-+          }
-+
-+          leaf ipv6-prefix-length-greater-or-equal {
-+            description
-+              "Specifies if matching prefixes with length greater than
-+               or equal to value";
-+            type uint8 {
-+              range "0..128";
-+            }
-+          }
-+
-+          leaf ipv6-prefix-length-lesser-or-equal {
-+            description
-+              "Specifies if matching prefixes with length lesser than
-+               or equal to value";
-+            type uint8 {
-+              range "0..128";
-+            }
-+          }
-+        }
-+        case any {
-+          leaf any {
-+            description "Match anything";
-+            type empty;
-+          }
-+        }
-+      }
-+    }
-+  }
-+}
diff --git a/net/frr/patches/010-add_yang_routemap.patch b/net/frr/patches/010-add_yang_routemap.patch
deleted file mode 100644 (file)
index 7026acb..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
---- a/dev/null 2020-04-10 18:48:03.582667900 +0300
-+++ b/yang/frr-route-map.yang  2020-05-02 11:43:04.182956847 +0300
-@@ -0,0 +1,387 @@
-+module frr-route-map {
-+  yang-version 1.1;
-+  namespace "http://frrouting.org/yang/route-map";
-+  prefix frr-route-map;
-+
-+  import ietf-inet-types {
-+    prefix inet;
-+  }
-+  import frr-filter {
-+    prefix filter;
-+  }
-+  import frr-interface {
-+    prefix frr-interface;
-+  }
-+
-+  organization "FRRouting";
-+  contact
-+    "FRR Users List:       <mailto:frog@lists.frrouting.org>
-+     FRR Development List: <mailto:dev@lists.frrouting.org>";
-+  description "This module defines route map settings";
-+
-+  revision 2019-07-01 {
-+    description "Initial revision";
-+  }
-+
-+  /*
-+   * Types.
-+   */
-+  typedef route-map-sequence {
-+    description "Route map valid sequence numbers";
-+    type uint16 {
-+      range "1..65535";
-+    }
-+  }
-+
-+  typedef route-map-name {
-+    description "Route map name format";
-+    type string;
-+  }
-+
-+  /*
-+   * Operational data.
-+   */
-+  container lib {
-+    list route-map {
-+      description "Route map instance";
-+
-+      key "name";
-+
-+      leaf name {
-+        description "Route map instance name";
-+        type route-map-name;
-+      }
-+
-+      list entry {
-+        description "Route map entry";
-+
-+        key "sequence";
-+
-+        leaf sequence {
-+          description
-+            "Route map instance priority (low number means higher priority)";
-+          type route-map-sequence;
-+        }
-+
-+        leaf description {
-+          description "Route map description";
-+          type string;
-+        }
-+
-+        leaf action {
-+          description
-+            "Route map actions: permit (executes action), deny (quits evaluation)";
-+          mandatory true;
-+          type enumeration {
-+            enum permit {
-+              description
-+                "Executes configured action and permits the prefix/route
-+                 if the conditions matched. An alternative exit action can
-+                 be configured to continue processing the route map list
-+                 or jump to process another route map.";
-+              value 0;
-+            }
-+            enum deny {
-+              description
-+                "If all conditions are met the prefix/route is denied and
-+                 route map processing stops.";
-+              value 1;
-+            }
-+          }
-+        }
-+
-+        leaf call {
-+          description
-+            "Call another route map before calling `exit-policy`. If the
-+             called route map returns deny then this route map will also
-+             return deny";
-+          type route-map-name;
-+        }
-+
-+        leaf exit-policy {
-+          description "What do to after route map successful match, set and call";
-+          type enumeration {
-+            enum permit-or-deny {
-+              description "End route map evaluation and return";
-+              value 0;
-+            }
-+            enum next {
-+              description
-+                "Proceed evaluating next route map entry per sequence";
-+              value 1;
-+            }
-+            enum goto {
-+              description
-+                "Go to route map entry with the provided sequence number";
-+              value 2;
-+            }
-+          }
-+          default "permit-or-deny";
-+        }
-+
-+        leaf goto-value {
-+          when "../exit-policy = 'goto'";
-+          description
-+            "Sequence number to jump (when using `goto` exit policy)";
-+          mandatory true;
-+          type route-map-sequence;
-+        }
-+
-+        list match-condition {
-+          description "Route map match conditions";
-+
-+          key "condition";
-+
-+          leaf condition {
-+            description "Match condition";
-+            type enumeration {
-+              enum interface {
-+                description "Match interface";
-+                value 0;
-+              }
-+              enum ipv4-address-list {
-+                description "Match an IPv4 access-list";
-+                value 1;
-+              }
-+              enum ipv4-prefix-list {
-+                description "Match an IPv4 prefix-list";
-+                value 2;
-+              }
-+              enum ipv4-next-hop-list {
-+                description "Match an IPv4 next-hop";
-+                value 3;
-+              }
-+              enum ipv4-next-hop-prefix-list {
-+                description "Match an IPv4 next-hop prefix list";
-+                value 4;
-+              }
-+              enum ipv4-next-hop-type {
-+                description "Match an IPv4 next-hop type";
-+                value 5;
-+              }
-+              enum ipv6-address-list {
-+                description "Match an IPv6 access-list";
-+                value 6;
-+              }
-+              enum ipv6-prefix-list {
-+                description "Match an IPv6 prefix-list";
-+                value 7;
-+              }
-+              enum ipv6-next-hop-type {
-+                description "Match an IPv6 next-hop type";
-+                value 8;
-+              }
-+              enum metric {
-+                description "Match a route metric";
-+                value 9;
-+              }
-+              enum tag {
-+                description "Match a route tag";
-+                value 10;
-+              }
-+              /* zebra specific conditions. */
-+              enum ipv4-prefix-length {
-+                description "Match IPv4 prefix length";
-+                value 100;
-+              }
-+              enum ipv6-prefix-length {
-+                description "Match IPv6 prefix length";
-+                value 101;
-+              }
-+              enum ipv4-next-hop-prefix-length {
-+                description "Match next-hop prefix length";
-+                value 102;
-+              }
-+              enum source-protocol {
-+                description "Match source protocol";
-+                value 103;
-+              }
-+              enum source-instance {
-+                description "Match source protocol instance";
-+                value 104;
-+              }
-+            }
-+          }
-+
-+          choice condition-value {
-+            description
-+              "Value to match (interpretation depends on condition type)";
-+            mandatory true;
-+            case interface {
-+              when "./condition = 'interface'";
-+              leaf interface {
-+                type string;
-+              }
-+            }
-+            case access-list-num {
-+              when "./condition = 'ipv4-address-list' or
-+                    ./condition = 'ipv4-next-hop-list'";
-+              leaf access-list-num {
-+                type filter:access-list-standard;
-+              }
-+            }
-+            case access-list-num-extended {
-+              when "./condition = 'ipv4-address-list' or
-+                    ./condition = 'ipv4-next-hop-list'";
-+              leaf access-list-num-extended {
-+                type filter:access-list-extended;
-+              }
-+            }
-+            case list-name {
-+              when "./condition = 'ipv4-address-list' or
-+                    ./condition = 'ipv4-prefix-list' or
-+                    ./condition = 'ipv4-next-hop-list' or
-+                    ./condition = 'ipv4-next-hop-prefix-list' or
-+                    ./condition = 'ipv6-address-list' or
-+                    ./condition = 'ipv6-prefix-list'";
-+              leaf list-name {
-+                type filter:access-list-name;
-+              }
-+            }
-+            case ipv4-next-hop-type {
-+              when "./condition = 'ipv4-next-hop-type'";
-+              leaf ipv4-next-hop-type {
-+                type enumeration {
-+                  enum blackhole {
-+                    value 0;
-+                  }
-+                }
-+              }
-+            }
-+            case ipv6-next-hop-type {
-+              when "./condition = 'ipv6-next-hop-type'";
-+              leaf ipv6-next-hop-type {
-+                type enumeration {
-+                  enum blackhole {
-+                    value 0;
-+                  }
-+                }
-+              }
-+            }
-+            case metric {
-+              when "./condition = 'metric'";
-+              leaf metric {
-+                type uint32 {
-+                  range "1..4294967295";
-+                }
-+              }
-+            }
-+            case tag {
-+              when "./condition = 'tag'";
-+              leaf tag {
-+                type uint32 {
-+                  range "1..4294967295";
-+                }
-+              }
-+            }
-+          }
-+        }
-+
-+        list set-action {
-+          description "Route map set actions";
-+
-+          key "action";
-+
-+          leaf action {
-+            description "Action to do when the route map matches";
-+            type enumeration {
-+              enum ipv4-next-hop {
-+                description "Set IPv4 address of the next hop";
-+                value 0;
-+              }
-+              enum ipv6-next-hop {
-+                description "Set IPv6 address of the next hop";
-+                value 1;
-+              }
-+              enum metric {
-+                description "Set prefix/route metric";
-+                value 2;
-+              }
-+              enum tag {
-+                description "Set tag";
-+                value 3;
-+              }
-+              /* zebra specific conditions. */
-+              enum source {
-+                description "Set source address for route";
-+                value 100;
-+              }
-+            }
-+          }
-+
-+          choice action-value {
-+            description
-+              "Value to set (interpretation depends on action-type)";
-+            case ipv4-address {
-+              when "./action = 'ipv4-next-hop'";
-+              leaf ipv4-address {
-+                description "IPv4 address";
-+                type inet:ipv4-address;
-+              }
-+            }
-+            case ipv6-address {
-+              when "./action = 'ipv6-next-hop'";
-+              leaf ipv6-address {
-+                description "IPv6 address";
-+                type inet:ipv6-address;
-+              }
-+            }
-+            case metric {
-+              when "./action = 'metric'";
-+              choice metric-value {
-+                description "Metric to set or use";
-+                case value {
-+                  leaf value {
-+                    description "Use the following metric value";
-+                    type uint32 {
-+                      range "0..4294967295";
-+                    }
-+                  }
-+                }
-+                case add-metric {
-+                  leaf add-metric {
-+                    description "Add unit to metric";
-+                    type boolean;
-+                  }
-+                }
-+                case subtract-metric {
-+                  leaf subtract-metric {
-+                    description "Subtract unit from metric";
-+                    type boolean;
-+                  }
-+                }
-+                case use-round-trip-time {
-+                  leaf use-round-trip-time {
-+                    description "Use the round trip time as metric";
-+                    type boolean;
-+                  }
-+                }
-+                case add-round-trip-time {
-+                  leaf add-round-trip-time {
-+                    description "Add round trip time to metric";
-+                    type boolean;
-+                  }
-+                }
-+                case subtract-round-trip-time {
-+                  leaf subtract-round-trip-time {
-+                    description "Subtract round trip time to metric";
-+                    type boolean;
-+                  }
-+                }
-+              }
-+            }
-+            case tag {
-+              when "./action = 'tag'";
-+              leaf tag {
-+                description "Tag value";
-+                type uint32 {
-+                  range "0..4294967295";
-+                }
-+              }
-+            }
-+          }
-+        }
-+      }
-+    }
-+  }
-+}
diff --git a/net/frr/patches/011-mod_yang_routemap_model.patch b/net/frr/patches/011-mod_yang_routemap_model.patch
deleted file mode 100644 (file)
index ac56137..0000000
+++ /dev/null
@@ -1,5045 +0,0 @@
-From 0c0e73045b1898610eef9309b9f5927254356710 Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Fri, 27 Sep 2019 19:32:10 -0300
-Subject: [PATCH 01/10] yang: update route map model
-
-Important changes:
-
-  * Rename top container `route-map` to `lib`;
-  * Rename list `instance` to `route-map`;
-  * Move route map repeated data to list `entry`;
-  * Use interface reference instead of typedef'ed string;
-  * Remove some zebra specific route map conditions;
-  * Protect `tag` set value with `when "./action = 'tag'"`;
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
-From a7282663eff6f036a427165b7fa73c75dccd47ff Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Mon, 30 Sep 2019 10:17:33 -0300
-Subject: [PATCH 02/10] lib: export route map structures and functions
-
-These exported items are going to be used by the new northbound CLI.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap.c | 213 ++-----------------------------------------------
- lib/routemap.h | 209 ++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 216 insertions(+), 206 deletions(-)
-
-diff --git a/lib/routemap.c b/lib/routemap.c
-index 14fec0283c..a8feebd313 100644
---- a/lib/routemap.c
-+++ b/lib/routemap.c
-@@ -50,178 +50,7 @@ static vector route_match_vec;
- /* Vector for route set rules. */
- static vector route_set_vec;
--struct route_map_match_set_hooks {
--      /* match interface */
--      int (*match_interface)(struct vty *vty, struct route_map_index *index,
--                             const char *command, const char *arg,
--                             route_map_event_t type);
--
--      /* no match interface */
--      int (*no_match_interface)(struct vty *vty,
--                                struct route_map_index *index,
--                                const char *command, const char *arg,
--                                route_map_event_t type);
--
--      /* match ip address */
--      int (*match_ip_address)(struct vty *vty, struct route_map_index *index,
--                              const char *command, const char *arg,
--                              route_map_event_t type);
--
--      /* no match ip address */
--      int (*no_match_ip_address)(struct vty *vty,
--                                 struct route_map_index *index,
--                                 const char *command, const char *arg,
--                                 route_map_event_t type);
--
--      /* match ip address prefix list */
--      int (*match_ip_address_prefix_list)(struct vty *vty,
--                                          struct route_map_index *index,
--                                          const char *command,
--                                          const char *arg,
--                                          route_map_event_t type);
--
--      /* no match ip address prefix list */
--      int (*no_match_ip_address_prefix_list)(struct vty *vty,
--                                             struct route_map_index *index,
--                                             const char *command,
--                                             const char *arg,
--                                             route_map_event_t type);
--
--      /* match ip next hop */
--      int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index,
--                               const char *command, const char *arg,
--                               route_map_event_t type);
--
--      /* no match ip next hop */
--      int (*no_match_ip_next_hop)(struct vty *vty,
--                                  struct route_map_index *index,
--                                  const char *command, const char *arg,
--                                  route_map_event_t type);
--
--      /* match ip next hop prefix list */
--      int (*match_ip_next_hop_prefix_list)(struct vty *vty,
--                                           struct route_map_index *index,
--                                           const char *command,
--                                           const char *arg,
--                                           route_map_event_t type);
--
--      /* no match ip next hop prefix list */
--      int (*no_match_ip_next_hop_prefix_list)(struct vty *vty,
--                                              struct route_map_index *index,
--                                              const char *command,
--                                              const char *arg,
--                                              route_map_event_t type);
--
--      /* match ip next-hop type */
--      int (*match_ip_next_hop_type)(struct vty *vty,
--                                           struct route_map_index *index,
--                                           const char *command,
--                                           const char *arg,
--                                           route_map_event_t type);
--
--      /* no match ip next-hop type */
--      int (*no_match_ip_next_hop_type)(struct vty *vty,
--                                              struct route_map_index *index,
--                                              const char *command,
--                                              const char *arg,
--                                              route_map_event_t type);
--
--      /* match ipv6 address */
--      int (*match_ipv6_address)(struct vty *vty,
--                                struct route_map_index *index,
--                                const char *command, const char *arg,
--                                route_map_event_t type);
--
--      /* no match ipv6 address */
--      int (*no_match_ipv6_address)(struct vty *vty,
--                                   struct route_map_index *index,
--                                   const char *command, const char *arg,
--                                   route_map_event_t type);
--
--
--      /* match ipv6 address prefix list */
--      int (*match_ipv6_address_prefix_list)(struct vty *vty,
--                                            struct route_map_index *index,
--                                            const char *command,
--                                            const char *arg,
--                                            route_map_event_t type);
--
--      /* no match ipv6 address prefix list */
--      int (*no_match_ipv6_address_prefix_list)(struct vty *vty,
--                                               struct route_map_index *index,
--                                               const char *command,
--                                               const char *arg,
--                                               route_map_event_t type);
--
--      /* match ipv6 next-hop type */
--      int (*match_ipv6_next_hop_type)(struct vty *vty,
--                                            struct route_map_index *index,
--                                            const char *command,
--                                            const char *arg,
--                                            route_map_event_t type);
--
--      /* no match ipv6 next-hop type */
--      int (*no_match_ipv6_next_hop_type)(struct vty *vty,
--                                         struct route_map_index *index,
--                                         const char *command, const char *arg,
--                                         route_map_event_t type);
--
--      /* match metric */
--      int (*match_metric)(struct vty *vty, struct route_map_index *index,
--                          const char *command, const char *arg,
--                          route_map_event_t type);
--
--      /* no match metric */
--      int (*no_match_metric)(struct vty *vty, struct route_map_index *index,
--                             const char *command, const char *arg,
--                             route_map_event_t type);
--
--      /* match tag */
--      int (*match_tag)(struct vty *vty, struct route_map_index *index,
--                       const char *command, const char *arg,
--                       route_map_event_t type);
--
--      /* no match tag */
--      int (*no_match_tag)(struct vty *vty, struct route_map_index *index,
--                          const char *command, const char *arg,
--                          route_map_event_t type);
--
--      /* set ip nexthop */
--      int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
--                            const char *command, const char *arg);
--
--      /* no set ip nexthop */
--      int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
--                               const char *command, const char *arg);
--
--      /* set ipv6 nexthop local */
--      int (*set_ipv6_nexthop_local)(struct vty *vty,
--                                    struct route_map_index *index,
--                                    const char *command, const char *arg);
--
--      /* no set ipv6 nexthop local */
--      int (*no_set_ipv6_nexthop_local)(struct vty *vty,
--                                       struct route_map_index *index,
--                                       const char *command, const char *arg);
--
--      /* set metric */
--      int (*set_metric)(struct vty *vty, struct route_map_index *index,
--                        const char *command, const char *arg);
--
--      /* no set metric */
--      int (*no_set_metric)(struct vty *vty, struct route_map_index *index,
--                           const char *command, const char *arg);
--
--      /* set tag */
--      int (*set_tag)(struct vty *vty, struct route_map_index *index,
--                     const char *command, const char *arg);
--
--      /* no set tag */
--      int (*no_set_tag)(struct vty *vty, struct route_map_index *index,
--                        const char *command, const char *arg);
--};
--
--static struct route_map_match_set_hooks rmap_match_set_hook;
-+struct route_map_match_set_hooks rmap_match_set_hook;
- /* match interface */
- void route_map_match_interface_hook(int (*func)(
-@@ -595,35 +424,9 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index,
- }
--/* Route map rule. This rule has both `match' rule and `set' rule. */
--struct route_map_rule {
--      /* Rule type. */
--      const struct route_map_rule_cmd *cmd;
--
--      /* For pretty printing. */
--      char *rule_str;
--
--      /* Pre-compiled match rule. */
--      void *value;
--
--      /* Linked list. */
--      struct route_map_rule *next;
--      struct route_map_rule *prev;
--};
--
--/* Making route map list. */
--struct route_map_list {
--      struct route_map *head;
--      struct route_map *tail;
--
--      void (*add_hook)(const char *);
--      void (*delete_hook)(const char *);
--      void (*event_hook)(const char *);
--};
--
- /* Master list of route map. */
--static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
--static struct hash *route_map_master_hash = NULL;
-+struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
-+struct hash *route_map_master_hash = NULL;
- static unsigned int route_map_hash_key_make(const void *p)
- {
-@@ -691,8 +494,6 @@ static void route_map_rule_delete(struct route_map_rule_list *,
-                                 struct route_map_rule *);
- static bool rmap_debug;
--static void route_map_index_delete(struct route_map_index *, int);
--
- /* New route map allocation. Please note route map's name must be
-    specified. */
- static struct route_map *route_map_new(const char *name)
-@@ -784,7 +585,7 @@ static void route_map_free_map(struct route_map *map)
- }
- /* Route map delete from list. */
--static void route_map_delete(struct route_map *map)
-+void route_map_delete(struct route_map *map)
- {
-       struct route_map_index *index;
-       char *name;
-@@ -883,7 +684,7 @@ static int route_map_clear_updated(struct route_map *map)
- /* Lookup route map.  If there isn't route map create one and return
-    it. */
--static struct route_map *route_map_get(const char *name)
-+struct route_map *route_map_get(const char *name)
- {
-       struct route_map *map;
-@@ -1097,7 +898,7 @@ static struct route_map_index *route_map_index_new(void)
- }
- /* Free route map index. */
--static void route_map_index_delete(struct route_map_index *index, int notify)
-+void route_map_index_delete(struct route_map_index *index, int notify)
- {
-       struct route_map_rule *rule;
-@@ -1202,7 +1003,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
- }
- /* Get route map index. */
--static struct route_map_index *
-+struct route_map_index *
- route_map_index_get(struct route_map *map, enum route_map_type type, int pref)
- {
-       struct route_map_index *index;
-diff --git a/lib/routemap.h b/lib/routemap.h
-index 1ffd0525ae..41959c24e5 100644
---- a/lib/routemap.h
-+++ b/lib/routemap.h
-@@ -140,6 +140,22 @@ enum rmap_compile_rets {
- };
-+/* Route map rule. This rule has both `match' rule and `set' rule. */
-+struct route_map_rule {
-+      /* Rule type. */
-+      const struct route_map_rule_cmd *cmd;
-+
-+      /* For pretty printing. */
-+      char *rule_str;
-+
-+      /* Pre-compiled match rule. */
-+      void *value;
-+
-+      /* Linked list. */
-+      struct route_map_rule *next;
-+      struct route_map_rule *prev;
-+};
-+
- /* Route map rule list. */
- struct route_map_rule_list {
-       struct route_map_rule *head;
-@@ -435,6 +451,199 @@ extern void route_map_counter_increment(struct route_map *map);
- /* Decrement the route-map used counter */
- extern void route_map_counter_decrement(struct route_map *map);
-+/* Route map hooks data structure. */
-+struct route_map_match_set_hooks {
-+      /* match interface */
-+      int (*match_interface)(struct vty *vty, struct route_map_index *index,
-+                             const char *command, const char *arg,
-+                             route_map_event_t type);
-+
-+      /* no match interface */
-+      int (*no_match_interface)(struct vty *vty,
-+                                struct route_map_index *index,
-+                                const char *command, const char *arg,
-+                                route_map_event_t type);
-+
-+      /* match ip address */
-+      int (*match_ip_address)(struct vty *vty, struct route_map_index *index,
-+                              const char *command, const char *arg,
-+                              route_map_event_t type);
-+
-+      /* no match ip address */
-+      int (*no_match_ip_address)(struct vty *vty,
-+                                 struct route_map_index *index,
-+                                 const char *command, const char *arg,
-+                                 route_map_event_t type);
-+
-+      /* match ip address prefix list */
-+      int (*match_ip_address_prefix_list)(struct vty *vty,
-+                                          struct route_map_index *index,
-+                                          const char *command,
-+                                          const char *arg,
-+                                          route_map_event_t type);
-+
-+      /* no match ip address prefix list */
-+      int (*no_match_ip_address_prefix_list)(struct vty *vty,
-+                                             struct route_map_index *index,
-+                                             const char *command,
-+                                             const char *arg,
-+                                             route_map_event_t type);
-+
-+      /* match ip next hop */
-+      int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index,
-+                               const char *command, const char *arg,
-+                               route_map_event_t type);
-+
-+      /* no match ip next hop */
-+      int (*no_match_ip_next_hop)(struct vty *vty,
-+                                  struct route_map_index *index,
-+                                  const char *command, const char *arg,
-+                                  route_map_event_t type);
-+
-+      /* match ip next hop prefix list */
-+      int (*match_ip_next_hop_prefix_list)(struct vty *vty,
-+                                           struct route_map_index *index,
-+                                           const char *command,
-+                                           const char *arg,
-+                                           route_map_event_t type);
-+
-+      /* no match ip next hop prefix list */
-+      int (*no_match_ip_next_hop_prefix_list)(struct vty *vty,
-+                                              struct route_map_index *index,
-+                                              const char *command,
-+                                              const char *arg,
-+                                              route_map_event_t type);
-+
-+      /* match ip next-hop type */
-+      int (*match_ip_next_hop_type)(struct vty *vty,
-+                                           struct route_map_index *index,
-+                                           const char *command,
-+                                           const char *arg,
-+                                           route_map_event_t type);
-+
-+      /* no match ip next-hop type */
-+      int (*no_match_ip_next_hop_type)(struct vty *vty,
-+                                              struct route_map_index *index,
-+                                              const char *command,
-+                                              const char *arg,
-+                                              route_map_event_t type);
-+
-+      /* match ipv6 address */
-+      int (*match_ipv6_address)(struct vty *vty,
-+                                struct route_map_index *index,
-+                                const char *command, const char *arg,
-+                                route_map_event_t type);
-+
-+      /* no match ipv6 address */
-+      int (*no_match_ipv6_address)(struct vty *vty,
-+                                   struct route_map_index *index,
-+                                   const char *command, const char *arg,
-+                                   route_map_event_t type);
-+
-+
-+      /* match ipv6 address prefix list */
-+      int (*match_ipv6_address_prefix_list)(struct vty *vty,
-+                                            struct route_map_index *index,
-+                                            const char *command,
-+                                            const char *arg,
-+                                            route_map_event_t type);
-+
-+      /* no match ipv6 address prefix list */
-+      int (*no_match_ipv6_address_prefix_list)(struct vty *vty,
-+                                               struct route_map_index *index,
-+                                               const char *command,
-+                                               const char *arg,
-+                                               route_map_event_t type);
-+
-+      /* match ipv6 next-hop type */
-+      int (*match_ipv6_next_hop_type)(struct vty *vty,
-+                                            struct route_map_index *index,
-+                                            const char *command,
-+                                            const char *arg,
-+                                            route_map_event_t type);
-+
-+      /* no match ipv6 next-hop type */
-+      int (*no_match_ipv6_next_hop_type)(struct vty *vty,
-+                                         struct route_map_index *index,
-+                                         const char *command, const char *arg,
-+                                         route_map_event_t type);
-+
-+      /* match metric */
-+      int (*match_metric)(struct vty *vty, struct route_map_index *index,
-+                          const char *command, const char *arg,
-+                          route_map_event_t type);
-+
-+      /* no match metric */
-+      int (*no_match_metric)(struct vty *vty, struct route_map_index *index,
-+                             const char *command, const char *arg,
-+                             route_map_event_t type);
-+
-+      /* match tag */
-+      int (*match_tag)(struct vty *vty, struct route_map_index *index,
-+                       const char *command, const char *arg,
-+                       route_map_event_t type);
-+
-+      /* no match tag */
-+      int (*no_match_tag)(struct vty *vty, struct route_map_index *index,
-+                          const char *command, const char *arg,
-+                          route_map_event_t type);
-+
-+      /* set ip nexthop */
-+      int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
-+                            const char *command, const char *arg);
-+
-+      /* no set ip nexthop */
-+      int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
-+                               const char *command, const char *arg);
-+
-+      /* set ipv6 nexthop local */
-+      int (*set_ipv6_nexthop_local)(struct vty *vty,
-+                                    struct route_map_index *index,
-+                                    const char *command, const char *arg);
-+
-+      /* no set ipv6 nexthop local */
-+      int (*no_set_ipv6_nexthop_local)(struct vty *vty,
-+                                       struct route_map_index *index,
-+                                       const char *command, const char *arg);
-+
-+      /* set metric */
-+      int (*set_metric)(struct vty *vty, struct route_map_index *index,
-+                        const char *command, const char *arg);
-+
-+      /* no set metric */
-+      int (*no_set_metric)(struct vty *vty, struct route_map_index *index,
-+                           const char *command, const char *arg);
-+
-+      /* set tag */
-+      int (*set_tag)(struct vty *vty, struct route_map_index *index,
-+                     const char *command, const char *arg);
-+
-+      /* no set tag */
-+      int (*no_set_tag)(struct vty *vty, struct route_map_index *index,
-+                        const char *command, const char *arg);
-+};
-+
-+extern struct route_map_match_set_hooks rmap_match_set_hook;
-+
-+/* Making route map list. */
-+struct route_map_list {
-+      struct route_map *head;
-+      struct route_map *tail;
-+
-+      void (*add_hook)(const char *);
-+      void (*delete_hook)(const char *);
-+      void (*event_hook)(const char *);
-+};
-+
-+extern struct route_map_list route_map_master;
-+
-+extern struct route_map *route_map_get(const char *name);
-+extern void route_map_delete(struct route_map *map);
-+extern struct route_map_index *route_map_index_get(struct route_map *map,
-+                                                 enum route_map_type type,
-+                                                 int pref);
-+extern void route_map_index_delete(struct route_map_index *index, int notify);
-+
- #ifdef __cplusplus
- }
- #endif
-
-From 686d244f00d87fa0b76c8e4644550d413fc3400b Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Mon, 30 Sep 2019 10:34:49 -0300
-Subject: [PATCH 03/10] lib: implement route map northbound
-
-Based on the route map old CLI, implement the route map handling using
-the exported functions.
-
-Use a curry-like programming pattern avoid code repetition when
-destroying match/set entries. This is needed by other daemons that
-implement custom route map functions and need to pass to lib their
-specific destroy functions.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap.h            |   24 +
- lib/routemap_northbound.c | 1393 +++++++++++++++++++++++++++++++++++++
- lib/subdir.am             |    2 +
- yang/subdir.am            |    1 +
- 4 files changed, 1420 insertions(+)
- create mode 100644 lib/routemap_northbound.c
-
-diff --git a/lib/routemap.h b/lib/routemap.h
-index 41959c24e5..d9e7f73f81 100644
---- a/lib/routemap.h
-+++ b/lib/routemap.h
-@@ -644,6 +644,30 @@ extern struct route_map_index *route_map_index_get(struct route_map *map,
-                                                  int pref);
- extern void route_map_index_delete(struct route_map_index *index, int notify);
-+/* routemap_northbound.c */
-+typedef int (*routemap_match_hook_fun)(struct vty *vty,
-+                                     struct route_map_index *rmi,
-+                                     const char *command, const char *arg,
-+                                     route_map_event_t event);
-+
-+typedef int (*routemap_set_hook_fun)(struct vty *vty,
-+                                   struct route_map_index *rmi,
-+                                   const char *command, const char *arg);
-+
-+struct routemap_hook_context {
-+      struct route_map_index *rhc_rmi;
-+      const char *rhc_rule;
-+      route_map_event_t rhc_event;
-+      routemap_set_hook_fun rhc_shook;
-+      routemap_match_hook_fun rhc_mhook;
-+};
-+
-+int lib_route_map_entry_match_destroy(enum nb_event event,
-+                                    const struct lyd_node *dnode);
-+int lib_route_map_entry_set_destroy(enum nb_event event,
-+                                  const struct lyd_node *dnode);
-+extern const struct frr_yang_module_info frr_route_map_info;
-+
- #ifdef __cplusplus
- }
- #endif
-diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
-new file mode 100644
-index 0000000000..02eb756334
---- /dev/null
-+++ b/lib/routemap_northbound.c
-@@ -0,0 +1,1393 @@
-+/*
-+ * Route map northbound implementation.
-+ *
-+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
-+ *                    Rafael Zalamena
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-+ * 02110-1301 USA.
-+ */
-+
-+#include <zebra.h>
-+
-+#include "lib/command.h"
-+#include "lib/log.h"
-+#include "lib/northbound.h"
-+#include "lib/routemap.h"
-+
-+/*
-+ * Auxiliary functions to avoid code duplication:
-+ *
-+ * lib_route_map_entry_set_destroy: unset `set` commands.
-+ * lib_route_map_entry_match_destroy: unset `match` commands.
-+ */
-+int lib_route_map_entry_match_destroy(enum nb_event event,
-+                                    const struct lyd_node *dnode)
-+{
-+      struct routemap_hook_context *rhc;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      if (rhc->rhc_mhook == NULL)
-+              return NB_OK;
-+
-+      rv = rhc->rhc_mhook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL,
-+                          rhc->rhc_event);
-+      if (rv != CMD_SUCCESS)
-+              return NB_ERR_INCONSISTENCY;
-+
-+      return NB_OK;
-+}
-+
-+int lib_route_map_entry_set_destroy(enum nb_event event,
-+                                  const struct lyd_node *dnode)
-+{
-+      struct routemap_hook_context *rhc;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      if (rhc->rhc_shook == NULL)
-+              return NB_OK;
-+
-+      rv = rhc->rhc_shook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL);
-+      if (rv != CMD_SUCCESS)
-+              return NB_ERR_INCONSISTENCY;
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map
-+ */
-+static int lib_route_map_create(enum nb_event event,
-+                              const struct lyd_node *dnode,
-+                              union nb_resource *resource)
-+{
-+      struct route_map *rm;
-+      const char *rm_name;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rm_name = yang_dnode_get_string(dnode, "./name");
-+              rm = route_map_get(rm_name);
-+              nb_running_set_entry(dnode, rm);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_destroy(enum nb_event event,
-+                               const struct lyd_node *dnode)
-+{
-+      struct route_map *rm;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rm = nb_running_unset_entry(dnode);
-+              route_map_delete(rm);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry
-+ */
-+static int lib_route_map_entry_create(enum nb_event event,
-+                                    const struct lyd_node *dnode,
-+                                    union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+      struct route_map *rm;
-+      uint16_t sequence;
-+      int action;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              sequence = yang_dnode_get_uint16(dnode, "./sequence");
-+              action = yang_dnode_get_enum(dnode, "./action") == 0
-+                               ? RMAP_PERMIT
-+                               : RMAP_DENY;
-+              rm = nb_running_get_entry(dnode, NULL, true);
-+              rmi = route_map_index_get(rm, action, sequence);
-+              nb_running_set_entry(dnode, rmi);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_destroy(enum nb_event event,
-+                                     const struct lyd_node *dnode)
-+{
-+      struct route_map_index *rmi;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_unset_entry(dnode);
-+              route_map_index_delete(rmi, 1);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/description
-+ */
-+static int lib_route_map_entry_description_modify(enum nb_event event,
-+                                                const struct lyd_node *dnode,
-+                                                union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+      const char *description;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_PREPARE:
-+              description = yang_dnode_get_string(dnode, NULL);
-+              resource->ptr = XSTRDUP(MTYPE_TMP, description);
-+              if (resource->ptr == NULL)
-+                      return NB_ERR_RESOURCE;
-+              break;
-+      case NB_EV_ABORT:
-+              XFREE(MTYPE_TMP, resource->ptr);
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              if (rmi->description != NULL)
-+                      XFREE(MTYPE_TMP, rmi->description);
-+              rmi->description = resource->ptr;
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_description_destroy(enum nb_event event,
-+                                                 const struct lyd_node *dnode)
-+{
-+      struct route_map_index *rmi;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              if (rmi->description != NULL)
-+                      XFREE(MTYPE_TMP, rmi->description);
-+              rmi->description = NULL;
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/action
-+ */
-+static int lib_route_map_entry_action_modify(enum nb_event event,
-+                                           const struct lyd_node *dnode,
-+                                           union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              rmi->type = yang_dnode_get_enum(dnode, NULL);
-+              /* TODO: notify? */
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/call
-+ */
-+static int lib_route_map_entry_call_modify(enum nb_event event,
-+                                         const struct lyd_node *dnode,
-+                                         union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+      const char *rm_name, *rmn_name;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              rm_name = yang_dnode_get_string(dnode, "../../name");
-+              rmn_name = yang_dnode_get_string(dnode, NULL);
-+              /* Don't allow to jump to the same route map instance. */
-+              if (strcmp(rm_name, rmn_name) == 0)
-+                      return NB_ERR_VALIDATION;
-+
-+              /* TODO: detect circular route map sequences. */
-+              break;
-+      case NB_EV_PREPARE:
-+              rmn_name = yang_dnode_get_string(dnode, NULL);
-+              resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name);
-+              break;
-+      case NB_EV_ABORT:
-+              XFREE(MTYPE_ROUTE_MAP_NAME, resource->ptr);
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              if (rmi->nextrm) {
-+                      route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED,
-+                                                rmi->nextrm, rmi->map->name);
-+                      XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm);
-+              }
-+              rmi->nextrm = resource->ptr;
-+              route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, rmi->nextrm,
-+                                        rmi->map->name);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_call_destroy(enum nb_event event,
-+                                          const struct lyd_node *dnode)
-+{
-+      struct route_map_index *rmi;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm,
-+                                        rmi->map->name);
-+              XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm);
-+              rmi->nextrm = NULL;
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/exit-policy
-+ */
-+static int lib_route_map_entry_exit_policy_modify(enum nb_event event,
-+                                                const struct lyd_node *dnode,
-+                                                union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+      int rm_action;
-+      int policy;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              policy = yang_dnode_get_enum(dnode, NULL);
-+              switch (policy) {
-+              case 0: /* permit-or-deny */
-+                      break;
-+              case 1: /* next */
-+                      /* FALLTHROUGH */
-+              case 2: /* goto */
-+                      rm_action = yang_dnode_get_enum(dnode, "../action");
-+                      if (rm_action == 1 /* deny */) {
-+                              /*
-+                               * On deny it is not possible to 'goto'
-+                               * anywhere.
-+                               */
-+                              return NB_ERR_VALIDATION;
-+                      }
-+                      break;
-+              }
-+              break;
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              policy = yang_dnode_get_enum(dnode, NULL);
-+
-+              switch (policy) {
-+              case 0: /* permit-or-deny */
-+                      rmi->exitpolicy = RMAP_EXIT;
-+                      break;
-+              case 1: /* next */
-+                      rmi->exitpolicy = RMAP_NEXT;
-+                      break;
-+              case 2: /* goto */
-+                      rmi->exitpolicy = RMAP_GOTO;
-+                      break;
-+              }
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/goto-value
-+ */
-+static int lib_route_map_entry_goto_value_modify(enum nb_event event,
-+                                               const struct lyd_node *dnode,
-+                                               union nb_resource *resource)
-+{
-+      struct route_map_index *rmi;
-+      uint16_t rmi_index;
-+      uint16_t rmi_next;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              rmi_index = yang_dnode_get_uint16(dnode, "../sequence");
-+              rmi_next = yang_dnode_get_uint16(dnode, NULL);
-+              if (rmi_next <= rmi_index) {
-+                      /* Can't jump backwards on a route map. */
-+                      return NB_ERR_VALIDATION;
-+              }
-+              break;
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              rmi->nextpref = yang_dnode_get_uint16(dnode, NULL);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_goto_value_destroy(enum nb_event event,
-+                                                const struct lyd_node *dnode)
-+{
-+      struct route_map_index *rmi;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_APPLY:
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              rmi->nextpref = 0;
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition
-+ */
-+static int
-+lib_route_map_entry_match_condition_create(enum nb_event event,
-+                                         const struct lyd_node *dnode,
-+                                         union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              /* NOTHING */
-+              break;
-+      case NB_EV_PREPARE:
-+              resource->ptr = XCALLOC(MTYPE_TMP, sizeof(*rhc));
-+              break;
-+      case NB_EV_ABORT:
-+              XFREE(MTYPE_TMP, resource->ptr);
-+              break;
-+      case NB_EV_APPLY:
-+              rhc = resource->ptr;
-+              rhc->rhc_rmi = nb_running_get_entry(dnode, NULL, true);
-+              nb_running_set_entry(dnode, rhc);
-+              break;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int
-+lib_route_map_entry_match_condition_destroy(enum nb_event event,
-+                                          const struct lyd_node *dnode)
-+{
-+      struct routemap_hook_context *rhc;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      rv = lib_route_map_entry_match_destroy(event, dnode);
-+      rhc = nb_running_unset_entry(dnode);
-+      XFREE(MTYPE_TMP, rhc);
-+
-+      return rv;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/interface
-+ */
-+static int lib_route_map_entry_match_condition_interface_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *ifname;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.match_interface == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      ifname = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_mhook = rmap_match_set_hook.no_match_interface;
-+      rhc->rhc_rule = "interface";
-+      rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
-+
-+      rv = rmap_match_set_hook.match_interface(NULL, rhc->rhc_rmi,
-+                                               "interface", ifname,
-+                                               RMAP_EVENT_MATCH_ADDED);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_match_condition_interface_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/access-list-num
-+ */
-+static int lib_route_map_entry_match_condition_access_list_num_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *acl;
-+      int condition, rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      rv = CMD_SUCCESS;
-+      acl = yang_dnode_get_string(dnode, NULL);
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      condition = yang_dnode_get_enum(dnode, "../condition");
-+      switch (condition) {
-+      case 1: /* ipv4-address-list */
-+              if (rmap_match_set_hook.match_ip_address == NULL)
-+                      break;
-+              rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address;
-+              rhc->rhc_rule = "ip address";
-+              rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
-+              rv = rmap_match_set_hook.match_ip_address(
-+                      NULL, rhc->rhc_rmi, "ip address", acl,
-+                      RMAP_EVENT_FILTER_ADDED);
-+              break;
-+      case 3: /* ipv4-next-hop-list */
-+              if (rmap_match_set_hook.match_ip_next_hop == NULL)
-+                      break;
-+              rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop;
-+              rhc->rhc_rule = "ip next-hop";
-+              rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
-+              rv = rmap_match_set_hook.match_ip_next_hop(
-+                      NULL, rhc->rhc_rmi, "ip next-hop", acl,
-+                      RMAP_EVENT_FILTER_ADDED);
-+              break;
-+      }
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_match_condition_access_list_num_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath:
-+ * /frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended
-+ */
-+static int lib_route_map_entry_match_condition_access_list_num_extended_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      return lib_route_map_entry_match_condition_access_list_num_modify(
-+              event, dnode, resource);
-+}
-+
-+static int lib_route_map_entry_match_condition_access_list_num_extended_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_condition_access_list_num_destroy(
-+              event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name
-+ */
-+static int lib_route_map_entry_match_condition_list_name_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *acl;
-+      int condition;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook installation, otherwise we can just stop. */
-+      acl = yang_dnode_get_string(dnode, NULL);
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      condition = yang_dnode_get_enum(dnode, "../condition");
-+      switch (condition) {
-+      case 1: /* ipv4-address-list */
-+              if (rmap_match_set_hook.match_ip_address == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address;
-+              rhc->rhc_rule = "ip address";
-+              rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
-+              rv = rmap_match_set_hook.match_ip_address(
-+                      NULL, rhc->rhc_rmi, "ip address", acl,
-+                      RMAP_EVENT_FILTER_ADDED);
-+              break;
-+      case 2: /* ipv4-prefix-list */
-+              if (rmap_match_set_hook.match_ip_address_prefix_list == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook =
-+                      rmap_match_set_hook.no_match_ip_address_prefix_list;
-+              rhc->rhc_rule = "ip address prefix-list";
-+              rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
-+              rv = rmap_match_set_hook.match_ip_address_prefix_list(
-+                      NULL, rhc->rhc_rmi, "ip address prefix-list", acl,
-+                      RMAP_EVENT_PLIST_ADDED);
-+              break;
-+      case 3: /* ipv4-next-hop-list */
-+              if (rmap_match_set_hook.match_ip_next_hop == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop;
-+              rhc->rhc_rule = "ip next-hop";
-+              rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
-+              rv = rmap_match_set_hook.match_ip_next_hop(
-+                      NULL, rhc->rhc_rmi, "ip next-hop", acl,
-+                      RMAP_EVENT_FILTER_ADDED);
-+              break;
-+      case 4: /* ipv4-next-hop-prefix-list */
-+              if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook =
-+                      rmap_match_set_hook.no_match_ip_next_hop_prefix_list;
-+              rhc->rhc_rule = "ip next-hop prefix-list";
-+              rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
-+              rv = rmap_match_set_hook.match_ip_next_hop_prefix_list(
-+                      NULL, rhc->rhc_rmi, "ip next-hop prefix-list", acl,
-+                      RMAP_EVENT_PLIST_ADDED);
-+              break;
-+      case 6: /* ipv6-address-list */
-+              if (rmap_match_set_hook.match_ipv6_address == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_address;
-+              rhc->rhc_rule = "ipv6 address";
-+              rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
-+              rv = rmap_match_set_hook.match_ipv6_address(
-+                      NULL, rhc->rhc_rmi, "ipv6 address", acl,
-+                      RMAP_EVENT_FILTER_ADDED);
-+              break;
-+      case 7: /* ipv6-prefix-list */
-+              if (rmap_match_set_hook.match_ipv6_address_prefix_list == NULL)
-+                      return NB_OK;
-+              rhc->rhc_mhook =
-+                      rmap_match_set_hook.no_match_ipv6_address_prefix_list;
-+              rhc->rhc_rule = "ipv6 address prefix-list";
-+              rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
-+              rv = rmap_match_set_hook.match_ipv6_address_prefix_list(
-+                      NULL, rhc->rhc_rmi, "ipv6 address prefix-list", acl,
-+                      RMAP_EVENT_PLIST_ADDED);
-+              break;
-+      default:
-+              rv = CMD_ERR_NO_MATCH;
-+              break;
-+      }
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_match_condition_list_name_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type
-+ */
-+static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *type;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.match_ip_next_hop_type == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      type = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop_type;
-+      rhc->rhc_rule = "ip next-hop type";
-+      rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
-+
-+      rv = rmap_match_set_hook.match_ip_next_hop_type(
-+              NULL, rhc->rhc_rmi, "ip next-hop type", type,
-+              RMAP_EVENT_MATCH_ADDED);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type
-+ */
-+static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *type;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.match_ipv6_next_hop_type == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      type = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop_type;
-+      rhc->rhc_rule = "ipv6 next-hop type";
-+      rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
-+
-+      rv = rmap_match_set_hook.match_ipv6_next_hop_type(
-+              NULL, rhc->rhc_rmi, "ipv6 next-hop type", type,
-+              RMAP_EVENT_MATCH_ADDED);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/metric
-+ */
-+static int
-+lib_route_map_entry_match_condition_metric_modify(enum nb_event event,
-+                                                const struct lyd_node *dnode,
-+                                                union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *type;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.match_metric == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      type = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_mhook = rmap_match_set_hook.no_match_metric;
-+      rhc->rhc_rule = "metric";
-+      rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
-+
-+      rv = rmap_match_set_hook.match_metric(NULL, rhc->rhc_rmi, "metric",
-+                                            type, RMAP_EVENT_MATCH_ADDED);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int
-+lib_route_map_entry_match_condition_metric_destroy(enum nb_event event,
-+                                                 const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/tag
-+ */
-+static int
-+lib_route_map_entry_match_condition_tag_modify(enum nb_event event,
-+                                             const struct lyd_node *dnode,
-+                                             union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *tag;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.match_tag == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      tag = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_mhook = rmap_match_set_hook.no_match_tag;
-+      rhc->rhc_rule = "tag";
-+      rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
-+
-+      rv = rmap_match_set_hook.match_tag(NULL, rhc->rhc_rmi, "tag", tag,
-+                                         RMAP_EVENT_MATCH_ADDED);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_mhook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int
-+lib_route_map_entry_match_condition_tag_destroy(enum nb_event event,
-+                                              const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_match_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action
-+ */
-+static int lib_route_map_entry_set_action_create(enum nb_event event,
-+                                               const struct lyd_node *dnode,
-+                                               union nb_resource *resource)
-+{
-+      return lib_route_map_entry_match_condition_create(event, dnode,
-+                                                        resource);
-+}
-+
-+static int lib_route_map_entry_set_action_destroy(enum nb_event event,
-+                                                const struct lyd_node *dnode)
-+{
-+      struct routemap_hook_context *rhc;
-+      int rv;
-+
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      rv = lib_route_map_entry_set_destroy(event, dnode);
-+      rhc = nb_running_unset_entry(dnode);
-+      XFREE(MTYPE_TMP, rhc);
-+
-+      return rv;
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv4-address
-+ */
-+static int
-+lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event,
-+                                                 const struct lyd_node *dnode,
-+                                                 union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *address;
-+      struct in_addr ia;
-+      int rv;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              /*
-+               * NOTE: validate if 'action' is 'ipv4-next-hop',
-+               * currently it is not necessary because this is the
-+               * only implemented action.
-+               */
-+              yang_dnode_get_ipv4(&ia, dnode, NULL);
-+              if (ia.s_addr == 0 || IPV4_CLASS_DE(ntohl(ia.s_addr)))
-+                      return NB_ERR_VALIDATION;
-+              /* FALLTHROUGH */
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              return NB_OK;
-+      case NB_EV_APPLY:
-+              break;
-+      }
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.set_ip_nexthop == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      address = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop;
-+      rhc->rhc_rule = "ip next-hop";
-+
-+      rv = rmap_match_set_hook.set_ip_nexthop(NULL, rhc->rhc_rmi,
-+                                              "ip next-hop", address);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_shook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_set_action_ipv4_address_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv6-address
-+ */
-+static int
-+lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event,
-+                                                 const struct lyd_node *dnode,
-+                                                 union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *address;
-+      struct in6_addr i6a;
-+      int rv;
-+
-+      switch (event) {
-+      case NB_EV_VALIDATE:
-+              /*
-+               * NOTE: validate if 'action' is 'ipv6-next-hop',
-+               * currently it is not necessary because this is the
-+               * only implemented action. Other actions might have
-+               * different validations.
-+               */
-+              yang_dnode_get_ipv6(&i6a, dnode, NULL);
-+              if (!IN6_IS_ADDR_LINKLOCAL(&i6a))
-+                      return NB_ERR_VALIDATION;
-+              /* FALLTHROUGH */
-+      case NB_EV_PREPARE:
-+      case NB_EV_ABORT:
-+              return NB_OK;
-+      case NB_EV_APPLY:
-+              break;
-+      }
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.set_ipv6_nexthop_local == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      address = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_shook = rmap_match_set_hook.no_set_ipv6_nexthop_local;
-+      rhc->rhc_rule = "ipv6 next-hop local";
-+
-+      rv = rmap_match_set_hook.set_ipv6_nexthop_local(
-+              NULL, rhc->rhc_rmi, "ipv6 next-hop local", address);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_shook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int lib_route_map_entry_set_action_ipv6_address_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/value
-+ */
-+static int set_action_modify(enum nb_event event, const struct lyd_node *dnode,
-+                           union nb_resource *resource, const char *value)
-+{
-+      struct routemap_hook_context *rhc;
-+      int rv;
-+
-+      /*
-+       * NOTE: validate if 'action' is 'metric', currently it is not
-+       * necessary because this is the only implemented action. Other
-+       * actions might have different validations.
-+       */
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.set_metric == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_shook = rmap_match_set_hook.no_set_metric;
-+      rhc->rhc_rule = "metric";
-+
-+      rv = rmap_match_set_hook.set_metric(NULL, rhc->rhc_rmi, "metric",
-+                                          value);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_shook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int
-+lib_route_map_entry_set_action_value_modify(enum nb_event event,
-+                                          const struct lyd_node *dnode,
-+                                          union nb_resource *resource)
-+{
-+      const char *metric = yang_dnode_get_string(dnode, NULL);
-+
-+      return set_action_modify(event, dnode, resource, metric);
-+}
-+
-+static int
-+lib_route_map_entry_set_action_value_destroy(enum nb_event event,
-+                                           const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric
-+ */
-+static int
-+lib_route_map_entry_set_action_add_metric_modify(enum nb_event event,
-+                                               const struct lyd_node *dnode,
-+                                               union nb_resource *resource)
-+{
-+      return set_action_modify(event, dnode, resource, "+metric");
-+}
-+
-+static int
-+lib_route_map_entry_set_action_add_metric_destroy(enum nb_event event,
-+                                                const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_action_value_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric
-+ */
-+static int lib_route_map_entry_set_action_subtract_metric_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      return set_action_modify(event, dnode, resource, "-metric");
-+}
-+
-+static int lib_route_map_entry_set_action_subtract_metric_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_action_value_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time
-+ */
-+static int lib_route_map_entry_set_action_use_round_trip_time_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      return set_action_modify(event, dnode, resource, "rtt");
-+}
-+
-+static int lib_route_map_entry_set_action_use_round_trip_time_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_action_value_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time
-+ */
-+static int lib_route_map_entry_set_action_add_round_trip_time_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      return set_action_modify(event, dnode, resource, "+rtt");
-+}
-+
-+static int lib_route_map_entry_set_action_add_round_trip_time_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_action_value_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time
-+ */
-+static int lib_route_map_entry_set_action_subtract_round_trip_time_modify(
-+      enum nb_event event, const struct lyd_node *dnode,
-+      union nb_resource *resource)
-+{
-+      return set_action_modify(event, dnode, resource, "-rtt");
-+}
-+
-+static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy(
-+      enum nb_event event, const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_action_value_destroy(event, dnode);
-+}
-+
-+/*
-+ * XPath: /frr-route-map:lib/route-map/entry/set-action/tag
-+ */
-+static int
-+lib_route_map_entry_set_action_tag_modify(enum nb_event event,
-+                                        const struct lyd_node *dnode,
-+                                        union nb_resource *resource)
-+{
-+      struct routemap_hook_context *rhc;
-+      const char *tag;
-+      int rv;
-+
-+      /*
-+       * NOTE: validate if 'action' is 'tag', currently it is not
-+       * necessary because this is the only implemented action. Other
-+       * actions might have different validations.
-+       */
-+      if (event != NB_EV_APPLY)
-+              return NB_OK;
-+
-+      /* Check for hook function. */
-+      if (rmap_match_set_hook.set_tag == NULL)
-+              return NB_OK;
-+
-+      /* Add configuration. */
-+      rhc = nb_running_get_entry(dnode, NULL, true);
-+      tag = yang_dnode_get_string(dnode, NULL);
-+
-+      /* Set destroy information. */
-+      rhc->rhc_shook = rmap_match_set_hook.no_set_tag;
-+      rhc->rhc_rule = "tag";
-+
-+      rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag);
-+      if (rv != CMD_SUCCESS) {
-+              rhc->rhc_shook = NULL;
-+              return NB_ERR_INCONSISTENCY;
-+      }
-+
-+      return NB_OK;
-+}
-+
-+static int
-+lib_route_map_entry_set_action_tag_destroy(enum nb_event event,
-+                                         const struct lyd_node *dnode)
-+{
-+      return lib_route_map_entry_set_destroy(event, dnode);
-+}
-+
-+/* clang-format off */
-+const struct frr_yang_module_info frr_route_map_info = {
-+      .name = "frr-route-map",
-+      .nodes = {
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map",
-+                      .cbs = {
-+                              .create = lib_route_map_create,
-+                              .destroy = lib_route_map_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry",
-+                      .cbs = {
-+                              .create = lib_route_map_entry_create,
-+                              .destroy = lib_route_map_entry_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/description",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_description_modify,
-+                              .destroy = lib_route_map_entry_description_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/action",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_action_modify,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/call",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_call_modify,
-+                              .destroy = lib_route_map_entry_call_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/exit-policy",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_exit_policy_modify,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/goto-value",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_goto_value_modify,
-+                              .destroy = lib_route_map_entry_goto_value_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition",
-+                      .cbs = {
-+                              .create = lib_route_map_entry_match_condition_create,
-+                              .destroy = lib_route_map_entry_match_condition_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_interface_modify,
-+                              .destroy = lib_route_map_entry_match_condition_interface_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_access_list_num_modify,
-+                              .destroy = lib_route_map_entry_match_condition_access_list_num_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_access_list_num_extended_modify,
-+                              .destroy = lib_route_map_entry_match_condition_access_list_num_extended_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_list_name_modify,
-+                              .destroy = lib_route_map_entry_match_condition_list_name_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify,
-+                              .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify,
-+                              .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_metric_modify,
-+                              .destroy = lib_route_map_entry_match_condition_metric_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_match_condition_tag_modify,
-+                              .destroy = lib_route_map_entry_match_condition_tag_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action",
-+                      .cbs = {
-+                              .create = lib_route_map_entry_set_action_create,
-+                              .destroy = lib_route_map_entry_set_action_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_ipv4_address_modify,
-+                              .destroy = lib_route_map_entry_set_action_ipv4_address_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_ipv6_address_modify,
-+                              .destroy = lib_route_map_entry_set_action_ipv6_address_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/value",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_value_modify,
-+                              .destroy = lib_route_map_entry_set_action_value_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_add_metric_modify,
-+                              .destroy = lib_route_map_entry_set_action_add_metric_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_subtract_metric_modify,
-+                              .destroy = lib_route_map_entry_set_action_subtract_metric_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_use_round_trip_time_modify,
-+                              .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_add_round_trip_time_modify,
-+                              .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify,
-+                              .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag",
-+                      .cbs = {
-+                              .modify = lib_route_map_entry_set_action_tag_modify,
-+                              .destroy = lib_route_map_entry_set_action_tag_destroy,
-+                      }
-+              },
-+              {
-+                      .xpath = NULL,
-+              },
-+      }
-+};
-diff --git a/lib/subdir.am b/lib/subdir.am
-index d804d839db..94b3d933ac 100644
---- a/lib/subdir.am
-+++ b/lib/subdir.am
-@@ -71,6 +71,7 @@ lib_libfrr_la_SOURCES = \
-       lib/qobj.c \
-       lib/ringbuf.c \
-       lib/routemap.c \
-+      lib/routemap_northbound.c \
-       lib/sbuf.c \
-       lib/seqlock.c \
-       lib/sha256.c \
-@@ -105,6 +106,7 @@ lib_libfrr_la_SOURCES = \
- nodist_lib_libfrr_la_SOURCES = \
-       yang/frr-interface.yang.c \
-+      yang/frr-route-map.yang.c \
-       yang/frr-route-types.yang.c \
-       yang/ietf/ietf-routing-types.yang.c \
-       yang/frr-module-translator.yang.c \
-diff --git a/yang/subdir.am b/yang/subdir.am
-index cfaf1a6401..7a15a6a309 100644
---- a/yang/subdir.am
-+++ b/yang/subdir.am
-@@ -22,6 +22,7 @@ EXTRA_DIST += yang/embedmodel.py
- dist_yangmodels_DATA += yang/frr-module-translator.yang
- dist_yangmodels_DATA += yang/frr-test-module.yang
- dist_yangmodels_DATA += yang/frr-interface.yang
-+dist_yangmodels_DATA += yang/frr-route-map.yang
- dist_yangmodels_DATA += yang/frr-route-types.yang
- dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang
-
-From 2b3e4807ecf4d2586fe4d651b904967ea8d759c0 Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Mon, 30 Sep 2019 15:01:46 -0300
-Subject: [PATCH 04/10] lib: implement new route map CLI
-
-Use the northbound back-end instead of the old route map CLI.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap.c            | 1103 +------------------------------------
- lib/routemap.h            |   19 +
- lib/routemap_cli.c        | 1075 ++++++++++++++++++++++++++++++++++++
- lib/routemap_northbound.c |    7 +
- lib/subdir.am             |    4 +
- vtysh/extract.pl.in       |    2 +-
- 6 files changed, 1108 insertions(+), 1102 deletions(-)
- create mode 100644 lib/routemap_cli.c
-
-diff --git a/lib/routemap.c b/lib/routemap.c
-index a8feebd313..e07ad08123 100644
---- a/lib/routemap.c
-+++ b/lib/routemap.c
-@@ -759,14 +759,6 @@ static const char *route_map_result_str(route_map_result_t res)
-       return "invalid";
- }
--static int route_map_empty(struct route_map *map)
--{
--      if (map->head == NULL && map->tail == NULL)
--              return 1;
--      else
--              return 0;
--}
--
- /* show route-map */
- static void vty_show_route_map_entry(struct vty *vty, struct route_map *map)
- {
-@@ -2010,871 +2002,6 @@ void route_map_notify_dependencies(const char *affected_name,
- /* VTY related functions. */
--DEFUN (match_interface,
--       match_interface_cmd,
--       "match interface WORD",
--       MATCH_STR
--       "match first hop interface of route\n"
--       "Interface name\n")
--{
--      int idx_word = 2;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_interface)
--              return rmap_match_set_hook.match_interface(
--                      vty, index, "interface", argv[idx_word]->arg,
--                      RMAP_EVENT_MATCH_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_match_interface,
--       no_match_interface_cmd,
--       "no match interface [WORD]",
--       NO_STR
--       MATCH_STR
--       "Match first hop interface of route\n"
--       "Interface name\n")
--{
--      char *iface = (argc == 4) ? argv[3]->arg : NULL;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_interface)
--              return rmap_match_set_hook.no_match_interface(
--                      vty, index, "interface", iface,
--                      RMAP_EVENT_MATCH_DELETED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ip_address,
--       match_ip_address_cmd,
--       "match ip address <(1-199)|(1300-2699)|WORD>",
--       MATCH_STR
--       IP_STR
--       "Match address of route\n"
--       "IP access-list number\n"
--       "IP access-list number (expanded range)\n"
--       "IP Access-list name\n")
--{
--      int idx_acl = 3;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ip_address)
--              return rmap_match_set_hook.match_ip_address(
--                      vty, index, "ip address", argv[idx_acl]->arg,
--                      RMAP_EVENT_FILTER_ADDED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_match_ip_address,
--       no_match_ip_address_cmd,
--       "no match ip address [<(1-199)|(1300-2699)|WORD>]",
--       NO_STR
--       MATCH_STR
--       IP_STR
--       "Match address of route\n"
--       "IP access-list number\n"
--       "IP access-list number (expanded range)\n"
--       "IP Access-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ip_address) {
--              if (argc <= idx_word)
--                      return rmap_match_set_hook.no_match_ip_address(
--                              vty, index, "ip address", NULL,
--                              RMAP_EVENT_FILTER_DELETED);
--              return rmap_match_set_hook.no_match_ip_address(
--                      vty, index, "ip address", argv[idx_word]->arg,
--                      RMAP_EVENT_FILTER_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ip_address_prefix_list,
--       match_ip_address_prefix_list_cmd,
--       "match ip address prefix-list WORD",
--       MATCH_STR
--       IP_STR
--       "Match address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ip_address_prefix_list)
--              return rmap_match_set_hook.match_ip_address_prefix_list(
--                      vty, index, "ip address prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_match_ip_address_prefix_list,
--       no_match_ip_address_prefix_list_cmd,
--       "no match ip address prefix-list [WORD]",
--       NO_STR
--       MATCH_STR
--       IP_STR
--       "Match address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ip_address_prefix_list) {
--              if (argc <= idx_word)
--                      return rmap_match_set_hook
--                              .no_match_ip_address_prefix_list(
--                                      vty, index, "ip address prefix-list",
--                                      NULL, RMAP_EVENT_PLIST_DELETED);
--              return rmap_match_set_hook.no_match_ip_address_prefix_list(
--                      vty, index, "ip address prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ip_next_hop,
--       match_ip_next_hop_cmd,
--       "match ip next-hop <(1-199)|(1300-2699)|WORD>",
--       MATCH_STR
--       IP_STR
--       "Match next-hop address of route\n"
--       "IP access-list number\n"
--       "IP access-list number (expanded range)\n"
--       "IP Access-list name\n")
--{
--      int idx_acl = 3;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ip_next_hop)
--              return rmap_match_set_hook.match_ip_next_hop(
--                      vty, index, "ip next-hop", argv[idx_acl]->arg,
--                      RMAP_EVENT_FILTER_ADDED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_match_ip_next_hop,
--       no_match_ip_next_hop_cmd,
--       "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
--       NO_STR
--       MATCH_STR
--       IP_STR
--       "Match next-hop address of route\n"
--       "IP access-list number\n"
--       "IP access-list number (expanded range)\n"
--       "IP Access-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ip_next_hop) {
--              if (argc <= idx_word)
--                      return rmap_match_set_hook.no_match_ip_next_hop(
--                              vty, index, "ip next-hop", NULL,
--                              RMAP_EVENT_FILTER_DELETED);
--              return rmap_match_set_hook.no_match_ip_next_hop(
--                      vty, index, "ip next-hop", argv[idx_word]->arg,
--                      RMAP_EVENT_FILTER_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ip_next_hop_prefix_list,
--       match_ip_next_hop_prefix_list_cmd,
--       "match ip next-hop prefix-list WORD",
--       MATCH_STR
--       IP_STR
--       "Match next-hop address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ip_next_hop_prefix_list)
--              return rmap_match_set_hook.match_ip_next_hop_prefix_list(
--                      vty, index, "ip next-hop prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_match_ip_next_hop_prefix_list,
--       no_match_ip_next_hop_prefix_list_cmd,
--       "no match ip next-hop prefix-list [WORD]",
--       NO_STR
--       MATCH_STR
--       IP_STR
--       "Match next-hop address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ip_next_hop) {
--              if (argc <= idx_word)
--                      return rmap_match_set_hook.no_match_ip_next_hop(
--                              vty, index, "ip next-hop prefix-list", NULL,
--                              RMAP_EVENT_PLIST_DELETED);
--              return rmap_match_set_hook.no_match_ip_next_hop(
--                      vty, index, "ip next-hop prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--DEFUN(match_ip_next_hop_type, match_ip_next_hop_type_cmd,
--      "match ip next-hop type <blackhole>",
--      MATCH_STR IP_STR
--      "Match next-hop address of route\n"
--      "Match entries by type\n"
--      "Blackhole\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ip_next_hop_type)
--              return rmap_match_set_hook.match_ip_next_hop_type(
--                      vty, index, "ip next-hop type", argv[idx_word]->arg,
--                      RMAP_EVENT_MATCH_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN(no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
--      "no match ip next-hop type [<blackhole>]",
--      NO_STR MATCH_STR IP_STR
--      "Match next-hop address of route\n"
--      "Match entries by type\n"
--      "Blackhole\n")
--{
--      int idx_word = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ip_next_hop) {
--              if (argc <= idx_word)
--                      return rmap_match_set_hook.no_match_ip_next_hop(
--                              vty, index, "ip next-hop type", NULL,
--                              RMAP_EVENT_MATCH_DELETED);
--              return rmap_match_set_hook.no_match_ip_next_hop(
--                      vty, index, "ip next-hop type", argv[idx_word]->arg,
--                      RMAP_EVENT_MATCH_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ipv6_address,
--       match_ipv6_address_cmd,
--       "match ipv6 address WORD",
--       MATCH_STR
--       IPV6_STR
--       "Match IPv6 address of route\n"
--       "IPv6 access-list name\n")
--{
--      int idx_word = 3;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ipv6_address)
--              return rmap_match_set_hook.match_ipv6_address(
--                      vty, index, "ipv6 address", argv[idx_word]->arg,
--                      RMAP_EVENT_FILTER_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_match_ipv6_address,
--       no_match_ipv6_address_cmd,
--       "no match ipv6 address WORD",
--       NO_STR
--       MATCH_STR
--       IPV6_STR
--       "Match IPv6 address of route\n"
--       "IPv6 access-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ipv6_address)
--              return rmap_match_set_hook.no_match_ipv6_address(
--                      vty, index, "ipv6 address", argv[idx_word]->arg,
--                      RMAP_EVENT_FILTER_DELETED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_ipv6_address_prefix_list,
--       match_ipv6_address_prefix_list_cmd,
--       "match ipv6 address prefix-list WORD",
--       MATCH_STR
--       IPV6_STR
--       "Match address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ipv6_address_prefix_list)
--              return rmap_match_set_hook.match_ipv6_address_prefix_list(
--                      vty, index, "ipv6 address prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_match_ipv6_address_prefix_list,
--       no_match_ipv6_address_prefix_list_cmd,
--       "no match ipv6 address prefix-list WORD",
--       NO_STR
--       MATCH_STR
--       IPV6_STR
--       "Match address of route\n"
--       "Match entries of prefix-lists\n"
--       "IP prefix-list name\n")
--{
--      int idx_word = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ipv6_address_prefix_list)
--              return rmap_match_set_hook.no_match_ipv6_address_prefix_list(
--                      vty, index, "ipv6 address prefix-list",
--                      argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED);
--      return CMD_SUCCESS;
--}
--
--DEFUN(match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
--      "match ipv6 next-hop type <blackhole>",
--      MATCH_STR IPV6_STR
--      "Match next-hop address of route\n"
--      "Match entries by type\n"
--      "Blackhole\n")
--{
--      int idx_word = 4;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_ipv6_next_hop_type)
--              return rmap_match_set_hook.match_ipv6_next_hop_type(
--                      vty, index, "ipv6 next-hop type", argv[idx_word]->arg,
--                      RMAP_EVENT_MATCH_ADDED);
--      return CMD_SUCCESS;
--}
--
--DEFUN(no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
--      "no match ipv6 next-hop type [<blackhole>]",
--      NO_STR MATCH_STR IPV6_STR
--      "Match address of route\n"
--      "Match entries by type\n"
--      "Blackhole\n")
--{
--      int idx_word = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_ipv6_next_hop_type)
--              return rmap_match_set_hook.no_match_ipv6_next_hop_type(
--                      vty, index, "ipv6 next-hop type",
--                      (argc <= idx_word) ? NULL : argv[idx_word]->arg,
--                      RMAP_EVENT_MATCH_DELETED);
--      return CMD_SUCCESS;
--}
--
--DEFUN (match_metric,
--       match_metric_cmd,
--       "match metric (0-4294967295)",
--       MATCH_STR
--       "Match metric of route\n"
--       "Metric value\n")
--{
--      int idx_number = 2;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_metric)
--              return rmap_match_set_hook.match_metric(vty, index, "metric",
--                                                      argv[idx_number]->arg,
--                                                      RMAP_EVENT_MATCH_ADDED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_match_metric,
--       no_match_metric_cmd,
--       "no match metric [(0-4294967295)]",
--       NO_STR
--       MATCH_STR
--       "Match metric of route\n"
--       "Metric value\n")
--{
--      int idx_number = 3;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_match_metric) {
--              if (argc <= idx_number)
--                      return rmap_match_set_hook.no_match_metric(
--                              vty, index, "metric", NULL,
--                              RMAP_EVENT_MATCH_DELETED);
--              return rmap_match_set_hook.no_match_metric(
--                      vty, index, "metric", argv[idx_number]->arg,
--                      RMAP_EVENT_MATCH_DELETED);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (match_tag,
--       match_tag_cmd,
--       "match tag (1-4294967295)",
--       MATCH_STR
--       "Match tag of route\n"
--       "Tag value\n")
--{
--      int idx_number = 2;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.match_tag)
--              return rmap_match_set_hook.match_tag(vty, index, "tag",
--                                                   argv[idx_number]->arg,
--                                                   RMAP_EVENT_MATCH_ADDED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_match_tag,
--       no_match_tag_cmd,
--       "no match tag [(1-4294967295)]",
--       NO_STR
--       MATCH_STR
--       "Match tag of route\n"
--       "Tag value\n")
--{
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      int idx = 0;
--      char *arg = argv_find(argv, argc, "(1-4294967295)", &idx)
--                          ? argv[idx]->arg
--                          : NULL;
--
--      if (rmap_match_set_hook.no_match_tag)
--              return rmap_match_set_hook.no_match_tag(
--                      vty, index, "tag", arg, RMAP_EVENT_MATCH_DELETED);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (set_ip_nexthop,
--       set_ip_nexthop_cmd,
--       "set ip next-hop A.B.C.D",
--       SET_STR
--       IP_STR
--       "Next hop address\n"
--       "IP address of next hop\n")
--{
--      int idx_ipv4 = 3;
--      union sockunion su;
--      int ret;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      ret = str2sockunion(argv[idx_ipv4]->arg, &su);
--      if (ret < 0) {
--              vty_out(vty, "%% Malformed nexthop address\n");
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--      if (su.sin.sin_addr.s_addr == 0
--          || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) {
--              vty_out(vty,
--                      "%% nexthop address cannot be 0.0.0.0, multicast or reserved\n");
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--
--      if (rmap_match_set_hook.set_ip_nexthop)
--              return rmap_match_set_hook.set_ip_nexthop(
--                      vty, index, "ip next-hop", argv[idx_ipv4]->arg);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_set_ip_nexthop,
--       no_set_ip_nexthop_cmd,
--       "no set ip next-hop [A.B.C.D]",
--       NO_STR
--       SET_STR
--       IP_STR
--       "Next hop address\n"
--       "IP address of next hop\n")
--{
--      int idx = 0;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--      const char *arg = NULL;
--
--      if (argv_find(argv, argc, "A.B.C.D", &idx))
--              arg = argv[idx]->arg;
--
--      if (rmap_match_set_hook.no_set_ip_nexthop)
--              return rmap_match_set_hook.no_set_ip_nexthop(
--                      vty, index, "ip next-hop", arg);
--
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (set_ipv6_nexthop_local,
--       set_ipv6_nexthop_local_cmd,
--       "set ipv6 next-hop local X:X::X:X",
--       SET_STR
--       IPV6_STR
--       "IPv6 next-hop address\n"
--       "IPv6 local address\n"
--       "IPv6 address of next hop\n")
--{
--      int idx_ipv6 = 4;
--      struct in6_addr addr;
--      int ret;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      ret = inet_pton(AF_INET6, argv[idx_ipv6]->arg, &addr);
--      if (!ret) {
--              vty_out(vty, "%% Malformed nexthop address\n");
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--      if (!IN6_IS_ADDR_LINKLOCAL(&addr)) {
--              vty_out(vty, "%% Invalid link-local nexthop address\n");
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--
--      if (rmap_match_set_hook.set_ipv6_nexthop_local)
--              return rmap_match_set_hook.set_ipv6_nexthop_local(
--                      vty, index, "ipv6 next-hop local", argv[idx_ipv6]->arg);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_set_ipv6_nexthop_local,
--       no_set_ipv6_nexthop_local_cmd,
--       "no set ipv6 next-hop local [X:X::X:X]",
--       NO_STR
--       SET_STR
--       IPV6_STR
--       "IPv6 next-hop address\n"
--       "IPv6 local address\n"
--       "IPv6 address of next hop\n")
--{
--      int idx_ipv6 = 5;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_set_ipv6_nexthop_local) {
--              if (argc <= idx_ipv6)
--                      return rmap_match_set_hook.no_set_ipv6_nexthop_local(
--                              vty, index, "ipv6 next-hop local", NULL);
--              return rmap_match_set_hook.no_set_ipv6_nexthop_local(
--                      vty, index, "ipv6 next-hop local", argv[5]->arg);
--      }
--      return CMD_SUCCESS;
--}
--
--DEFUN (set_metric,
--       set_metric_cmd,
--       "set metric <(0-4294967295)|rtt|+rtt|-rtt|+metric|-metric>",
--       SET_STR
--       "Metric value for destination routing protocol\n"
--       "Metric value\n"
--       "Assign round trip time\n"
--       "Add round trip time\n"
--       "Subtract round trip time\n"
--       "Add metric\n"
--       "Subtract metric\n")
--{
--      int idx_number = 2;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      const char *pass = (argv[idx_number]->type == RANGE_TKN)
--                                 ? argv[idx_number]->arg
--                                 : argv[idx_number]->text;
--
--      if (rmap_match_set_hook.set_metric)
--              return rmap_match_set_hook.set_metric(vty, index, "metric",
--                                                    pass);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_set_metric,
--       no_set_metric_cmd,
--       "no set metric [(0-4294967295)]",
--       NO_STR
--       SET_STR
--       "Metric value for destination routing protocol\n"
--       "Metric value\n")
--{
--      int idx_number = 3;
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      if (rmap_match_set_hook.no_set_metric) {
--              if (argc <= idx_number)
--                      return rmap_match_set_hook.no_set_metric(
--                              vty, index, "metric", NULL);
--              return rmap_match_set_hook.no_set_metric(vty, index, "metric",
--                                                       argv[idx_number]->arg);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (set_tag,
--       set_tag_cmd,
--       "set tag (1-4294967295)",
--       SET_STR
--       "Tag value for routing protocol\n"
--       "Tag value\n")
--{
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      int idx_number = 2;
--      if (rmap_match_set_hook.set_tag)
--              return rmap_match_set_hook.set_tag(vty, index, "tag",
--                                                 argv[idx_number]->arg);
--      return CMD_SUCCESS;
--}
--
--
--DEFUN (no_set_tag,
--       no_set_tag_cmd,
--       "no set tag [(1-4294967295)]",
--       NO_STR
--       SET_STR
--       "Tag value for routing protocol\n"
--       "Tag value\n")
--{
--      VTY_DECLVAR_CONTEXT(route_map_index, index);
--
--      int idx_number = 3;
--      if (rmap_match_set_hook.no_set_tag) {
--              if (argc <= idx_number)
--                      return rmap_match_set_hook.no_set_tag(vty, index, "tag",
--                                                            NULL);
--              return rmap_match_set_hook.no_set_tag(vty, index, "tag",
--                                                    argv[idx_number]->arg);
--      }
--      return CMD_SUCCESS;
--}
--
--
--DEFUN_NOSH (route_map,
--       route_map_cmd,
--       "route-map WORD <deny|permit> (1-65535)",
--       "Create route-map or enter route-map command mode\n"
--       "Route map tag\n"
--       "Route map denies set operations\n"
--       "Route map permits set operations\n"
--       "Sequence to insert to/delete from existing route-map entry\n")
--{
--      int idx_word = 1;
--      int idx_permit_deny = 2;
--      int idx_number = 3;
--      struct route_map *map;
--      struct route_map_index *index;
--      char *endptr = NULL;
--      int permit =
--              argv[idx_permit_deny]->arg[0] == 'p' ? RMAP_PERMIT : RMAP_DENY;
--      unsigned long pref = strtoul(argv[idx_number]->arg, &endptr, 10);
--      const char *mapname = argv[idx_word]->arg;
--
--      /* Get route map. */
--      map = route_map_get(mapname);
--      index = route_map_index_get(map, permit, pref);
--
--      VTY_PUSH_CONTEXT(RMAP_NODE, index);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_route_map_all,
--       no_route_map_all_cmd,
--       "no route-map WORD",
--       NO_STR
--       "Create route-map or enter route-map command mode\n"
--       "Route map tag\n")
--{
--      int idx_word = 2;
--      const char *mapname = argv[idx_word]->arg;
--      struct route_map *map;
--
--      map = route_map_lookup_by_name(mapname);
--      if (map == NULL) {
--              vty_out(vty, "%% Could not find route-map %s\n", mapname);
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--
--      route_map_delete(map);
--
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_route_map,
--       no_route_map_cmd,
--       "no route-map WORD <deny|permit> (1-65535)",
--       NO_STR
--       "Create route-map or enter route-map command mode\n"
--       "Route map tag\n"
--       "Route map denies set operations\n"
--       "Route map permits set operations\n"
--       "Sequence to insert to/delete from existing route-map entry\n")
--{
--      int idx_word = 2;
--      int idx_permit_deny = 3;
--      int idx_number = 4;
--      struct route_map *map;
--      struct route_map_index *index;
--      char *endptr = NULL;
--      int permit = strmatch(argv[idx_permit_deny]->text, "permit")
--                           ? RMAP_PERMIT
--                           : RMAP_DENY;
--      const char *prefstr = argv[idx_number]->arg;
--      const char *mapname = argv[idx_word]->arg;
--      unsigned long pref = strtoul(prefstr, &endptr, 10);
--
--      /* Existence check. */
--      map = route_map_lookup_by_name(mapname);
--      if (map == NULL) {
--              vty_out(vty, "%% Could not find route-map %s\n", mapname);
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--
--      /* Lookup route map index. */
--      index = route_map_index_lookup(map, permit, pref);
--      if (index == NULL) {
--              vty_out(vty, "%% Could not find route-map entry %s %s\n",
--                      mapname, prefstr);
--              return CMD_WARNING_CONFIG_FAILED;
--      }
--
--      /* Delete index from route map. */
--      route_map_index_delete(index, 1);
--
--      /* If this route rule is the last one, delete route map itself. */
--      if (route_map_empty(map))
--              route_map_delete(map);
--
--      return CMD_SUCCESS;
--}
--
--DEFUN (rmap_onmatch_next,
--       rmap_onmatch_next_cmd,
--       "on-match next",
--       "Exit policy on matches\n"
--       "Next clause\n")
--{
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index) {
--              if (index->type == RMAP_DENY) {
--                      /* Under a deny clause, match means it's finished. No
--                       * need to set next */
--                      vty_out(vty,
--                              "on-match next not supported under route-map deny\n");
--                      return CMD_WARNING_CONFIG_FAILED;
--              }
--              index->exitpolicy = RMAP_NEXT;
--      }
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_rmap_onmatch_next,
--       no_rmap_onmatch_next_cmd,
--       "no on-match next",
--       NO_STR
--       "Exit policy on matches\n"
--       "Next clause\n")
--{
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index)
--              index->exitpolicy = RMAP_EXIT;
--
--      return CMD_SUCCESS;
--}
--
--DEFUN (rmap_onmatch_goto,
--       rmap_onmatch_goto_cmd,
--       "on-match goto (1-65535)",
--       "Exit policy on matches\n"
--       "Goto Clause number\n"
--       "Number\n")
--{
--      int idx = 0;
--      char *num = argv_find(argv, argc, "(1-65535)", &idx) ? argv[idx]->arg
--                                                           : NULL;
--
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--      int d = 0;
--
--      if (index) {
--              if (index->type == RMAP_DENY) {
--                      /* Under a deny clause, match means it's finished. No
--                       * need to go anywhere */
--                      vty_out(vty,
--                              "on-match goto not supported under route-map deny\n");
--                      return CMD_WARNING_CONFIG_FAILED;
--              }
--
--              if (num)
--                      d = strtoul(num, NULL, 10);
--              else
--                      d = index->pref + 1;
--
--              if (d <= index->pref) {
--                      /* Can't allow you to do that, Dave */
--                      vty_out(vty, "can't jump backwards in route-maps\n");
--                      return CMD_WARNING_CONFIG_FAILED;
--              } else {
--                      index->exitpolicy = RMAP_GOTO;
--                      index->nextpref = d;
--              }
--      }
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_rmap_onmatch_goto,
--       no_rmap_onmatch_goto_cmd,
--       "no on-match goto",
--       NO_STR
--       "Exit policy on matches\n"
--       "Goto Clause number\n")
--{
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index)
--              index->exitpolicy = RMAP_EXIT;
--
--      return CMD_SUCCESS;
--}
--
--/* Cisco/GNU Zebra compatibility aliases */
--/* ALIAS_FIXME */
--DEFUN (rmap_continue,
--       rmap_continue_cmd,
--       "continue (1-65535)",
--       "Continue on a different entry within the route-map\n"
--       "Route-map entry sequence number\n")
--{
--      return rmap_onmatch_goto(self, vty, argc, argv);
--}
--
--/* ALIAS_FIXME */
--DEFUN (no_rmap_continue,
--       no_rmap_continue_cmd,
--       "no continue [(1-65535)]",
--       NO_STR
--       "Continue on a different entry within the route-map\n"
--       "Route-map entry sequence number\n")
--{
--      return no_rmap_onmatch_goto(self, vty, argc, argv);
--}
--
- static void clear_route_map_helper(struct route_map *map)
- {
-       struct route_map_index *index;
-@@ -2937,89 +2064,6 @@ DEFUN (rmap_show_unused,
-       return vty_show_unused_route_map(vty);
- }
--DEFUN (rmap_call,
--       rmap_call_cmd,
--       "call WORD",
--       "Jump to another Route-Map after match+set\n"
--       "Target route-map name\n")
--{
--      int idx_word = 1;
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--      const char *rmap = argv[idx_word]->arg;
--
--      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);
--              XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm);
--      }
--      index->nextrm = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
--
--      /* Execute event hook. */
--      route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, index->nextrm,
--                                index->map->name);
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_rmap_call,
--       no_rmap_call_cmd,
--       "no call",
--       NO_STR
--       "Jump to another Route-Map after match+set\n")
--{
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index->nextrm) {
--              route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED,
--                                        index->nextrm, index->map->name);
--              XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm);
--              index->nextrm = NULL;
--      }
--
--      return CMD_SUCCESS;
--}
--
--DEFUN (rmap_description,
--       rmap_description_cmd,
--       "description LINE...",
--       "Route-map comment\n"
--       "Comment describing this route-map rule\n")
--{
--      int idx_line = 1;
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index) {
--              if (index->description)
--                      XFREE(MTYPE_TMP, index->description);
--              index->description = argv_concat(argv, argc, idx_line);
--      }
--      return CMD_SUCCESS;
--}
--
--DEFUN (no_rmap_description,
--       no_rmap_description_cmd,
--       "no description",
--       NO_STR
--       "Route-map comment\n")
--{
--      struct route_map_index *index = VTY_GET_CONTEXT(route_map_index);
--
--      if (index) {
--              if (index->description)
--                      XFREE(MTYPE_TMP, index->description);
--              index->description = NULL;
--      }
--      return CMD_SUCCESS;
--}
--
- DEFUN (debug_rmap,
-        debug_rmap_cmd,
-        "debug route-map",
-@@ -3045,59 +2089,6 @@ DEFUN (no_debug_rmap,
- static struct cmd_node rmap_debug_node = {RMAP_DEBUG_NODE, "", 1};
- /* Configuration write function. */
--static int route_map_config_write(struct vty *vty)
--{
--      struct route_map *map;
--      struct route_map_index *index;
--      struct route_map_rule *rule;
--      int first = 1;
--      int write = 0;
--      struct listnode *ln;
--      struct list *maplist = list_new();
--
--      for (map = route_map_master.head; map; map = map->next)
--              listnode_add(maplist, map);
--
--      list_sort(maplist, sort_route_map);
--
--      for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
--              for (index = map->head; index; index = index->next) {
--                      if (!first)
--                              vty_out(vty, "!\n");
--                      else
--                              first = 0;
--
--                      vty_out(vty, "route-map %s %s %d\n", map->name,
--                              route_map_type_str(index->type), index->pref);
--
--                      if (index->description)
--                              vty_out(vty, " description %s\n",
--                                      index->description);
--
--                      for (rule = index->match_list.head; rule;
--                           rule = rule->next)
--                              vty_out(vty, " match %s %s\n", rule->cmd->str,
--                                      rule->rule_str ? rule->rule_str : "");
--
--                      for (rule = index->set_list.head; rule;
--                           rule = rule->next)
--                              vty_out(vty, " set %s %s\n", rule->cmd->str,
--                                      rule->rule_str ? rule->rule_str : "");
--                      if (index->nextrm)
--                              vty_out(vty, " call %s\n", index->nextrm);
--                      if (index->exitpolicy == RMAP_GOTO)
--                              vty_out(vty, " on-match goto %d\n",
--                                      index->nextpref);
--                      if (index->exitpolicy == RMAP_NEXT)
--                              vty_out(vty, " on-match next\n");
--
--                      write++;
--              }
--
--      list_delete(&maplist);
--      return write;
--}
--
- static int rmap_config_write_debug(struct vty *vty)
- {
-       int write = 0;
-@@ -3110,9 +2101,6 @@ static int rmap_config_write_debug(struct vty *vty)
-       return write;
- }
--/* Route map node structure. */
--static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1};
--
- /* Common route map rules */
- void *route_map_rule_tag_compile(const char *arg)
-@@ -3171,14 +2159,6 @@ void route_map_finish(void)
-       route_map_master_hash = NULL;
- }
--static void rmap_autocomplete(vector comps, struct cmd_token *token)
--{
--      struct route_map *map;
--
--      for (map = route_map_master.head; map; map = map->next)
--              vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name));
--}
--
- /* Increment the use_count counter while attaching the route map */
- void route_map_counter_increment(struct route_map *map)
- {
-@@ -3196,14 +2176,6 @@ void route_map_counter_decrement(struct route_map *map)
-       }
- }
--static const struct cmd_variable_handler rmap_var_handlers[] = {
--      {/* "route-map WORD" */
--       .varname = "route_map",
--       .completions = rmap_autocomplete},
--      {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete},
--      {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete},
--      {.completions = NULL}};
--
- /* Initialization of route map vector. */
- void route_map_init(void)
- {
-@@ -3221,43 +2193,17 @@ void route_map_init(void)
-                       8, route_map_dep_hash_make_key, route_map_dep_hash_cmp,
-                       "Route Map Dep Hash");
--      cmd_variable_handler_register(rmap_var_handlers);
--
-       rmap_debug = false;
--      /* Install route map top node. */
--      install_node(&rmap_node, route_map_config_write);
-+      route_map_cli_init();
-+      /* Install route map top node. */
-       install_node(&rmap_debug_node, rmap_config_write_debug);
-       /* Install route map commands. */
--      install_default(RMAP_NODE);
--      install_element(CONFIG_NODE, &route_map_cmd);
--      install_element(CONFIG_NODE, &no_route_map_cmd);
--      install_element(CONFIG_NODE, &no_route_map_all_cmd);
--
-       install_element(CONFIG_NODE, &debug_rmap_cmd);
-       install_element(CONFIG_NODE, &no_debug_rmap_cmd);
--      /* Install the on-match stuff */
--      install_element(RMAP_NODE, &route_map_cmd);
--      install_element(RMAP_NODE, &rmap_onmatch_next_cmd);
--      install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd);
--      install_element(RMAP_NODE, &rmap_onmatch_goto_cmd);
--      install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd);
--      install_element(RMAP_NODE, &rmap_continue_cmd);
--      install_element(RMAP_NODE, &no_rmap_continue_cmd);
--
--      /* Install the continue stuff (ALIAS of on-match). */
--
--      /* Install the call stuff. */
--      install_element(RMAP_NODE, &rmap_call_cmd);
--      install_element(RMAP_NODE, &no_rmap_call_cmd);
--
--      /* Install description commands. */
--      install_element(RMAP_NODE, &rmap_description_cmd);
--      install_element(RMAP_NODE, &no_rmap_description_cmd);
--
-       /* Install show command */
-       install_element(ENABLE_NODE, &rmap_clear_counters_cmd);
-@@ -3266,49 +2212,4 @@ void route_map_init(void)
-       install_element(ENABLE_NODE, &debug_rmap_cmd);
-       install_element(ENABLE_NODE, &no_debug_rmap_cmd);
--
--      install_element(RMAP_NODE, &match_interface_cmd);
--      install_element(RMAP_NODE, &no_match_interface_cmd);
--
--      install_element(RMAP_NODE, &match_ip_address_cmd);
--      install_element(RMAP_NODE, &no_match_ip_address_cmd);
--
--      install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd);
--      install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
--
--      install_element(RMAP_NODE, &match_ip_next_hop_cmd);
--      install_element(RMAP_NODE, &no_match_ip_next_hop_cmd);
--
--      install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
--      install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
--
--      install_element(RMAP_NODE, &match_ip_next_hop_type_cmd);
--      install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd);
--
--      install_element(RMAP_NODE, &match_ipv6_address_cmd);
--      install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
--
--      install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
--      install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
--
--      install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd);
--      install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd);
--
--      install_element(RMAP_NODE, &match_metric_cmd);
--      install_element(RMAP_NODE, &no_match_metric_cmd);
--
--      install_element(RMAP_NODE, &match_tag_cmd);
--      install_element(RMAP_NODE, &no_match_tag_cmd);
--
--      install_element(RMAP_NODE, &set_ip_nexthop_cmd);
--      install_element(RMAP_NODE, &no_set_ip_nexthop_cmd);
--
--      install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd);
--      install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
--
--      install_element(RMAP_NODE, &set_metric_cmd);
--      install_element(RMAP_NODE, &no_set_metric_cmd);
--
--      install_element(RMAP_NODE, &set_tag_cmd);
--      install_element(RMAP_NODE, &no_set_tag_cmd);
- }
-diff --git a/lib/routemap.h b/lib/routemap.h
-index d9e7f73f81..70e150c981 100644
---- a/lib/routemap.h
-+++ b/lib/routemap.h
-@@ -666,8 +666,27 @@ int lib_route_map_entry_match_destroy(enum nb_event event,
-                                     const struct lyd_node *dnode);
- int lib_route_map_entry_set_destroy(enum nb_event event,
-                                   const struct lyd_node *dnode);
-+
- extern const struct frr_yang_module_info frr_route_map_info;
-+/* routemap_cli.c */
-+extern void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
-+                                  bool show_defaults);
-+extern void route_map_instance_show_end(struct vty *vty,
-+                                      struct lyd_node *dnode);
-+extern void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
-+                                   bool show_defaults);
-+extern void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
-+                                bool show_defaults);
-+extern void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
-+                                     bool show_defaults);
-+extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
-+                              bool show_defaults);
-+extern void route_map_description_show(struct vty *vty,
-+                                     struct lyd_node *dnode,
-+                                     bool show_defaults);
-+extern void route_map_cli_init(void);
-+
- #ifdef __cplusplus
- }
- #endif
-diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
-new file mode 100644
-index 0000000000..987693c961
---- /dev/null
-+++ b/lib/routemap_cli.c
-@@ -0,0 +1,1075 @@
-+/*
-+ * Route map northbound CLI implementation.
-+ *
-+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
-+ *                    Rafael Zalamena
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-+ * 02110-1301 USA.
-+ */
-+
-+#include <zebra.h>
-+
-+#include "lib/command.h"
-+#include "lib/northbound_cli.h"
-+#include "lib/routemap.h"
-+
-+#ifndef VTYSH_EXTRACT_PL
-+#include "lib/routemap_cli_clippy.c"
-+#endif /* VTYSH_EXTRACT_PL */
-+
-+#define ROUTE_MAP_CMD_STR \
-+      "Create route-map or enter route-map command mode\n" \
-+      "Route map tag\n"
-+#define ROUTE_MAP_OP_CMD_STR \
-+      "Route map denies set operations\n" \
-+      "Route map permits set operations\n"
-+#define ROUTE_MAP_SEQUENCE_CMD_STR \
-+      "Sequence to insert to/delete from existing route-map entry\n"
-+
-+DEFPY_NOSH(
-+      route_map, route_map_cmd,
-+      "route-map WORD$name <deny|permit>$action (1-65535)$sequence",
-+      ROUTE_MAP_CMD_STR
-+      ROUTE_MAP_OP_CMD_STR
-+      ROUTE_MAP_SEQUENCE_CMD_STR)
-+{
-+      char xpath_action[XPATH_MAXLEN + 64];
-+      char xpath_index[XPATH_MAXLEN + 32];
-+      char xpath[XPATH_MAXLEN];
-+      int rv;
-+
-+      snprintf(xpath, sizeof(xpath),
-+               "/frr-route-map:lib/route-map[name='%s']", name);
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+
-+      snprintf(xpath_index, sizeof(xpath_index), "%s/entry[sequence='%lu']",
-+               xpath, sequence);
-+      nb_cli_enqueue_change(vty, xpath_index, NB_OP_CREATE, NULL);
-+
-+      snprintf(xpath_action, sizeof(xpath_action), "%s/action", xpath_index);
-+      nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action);
-+
-+      rv = nb_cli_apply_changes(vty, NULL);
-+      if (rv == CMD_SUCCESS)
-+              VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
-+
-+      return rv;
-+}
-+
-+DEFPY(
-+      no_route_map_all, no_route_map_all_cmd,
-+      "no route-map WORD$name",
-+      NO_STR
-+      ROUTE_MAP_CMD_STR)
-+{
-+      char xpath[XPATH_MAXLEN];
-+
-+      snprintf(xpath, sizeof(xpath),
-+               "/frr-route-map:lib/route-map[name='%s']", name);
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_route_map, no_route_map_cmd,
-+      "no route-map WORD$name <deny|permit>$action (1-65535)$sequence",
-+      NO_STR
-+      ROUTE_MAP_CMD_STR
-+      ROUTE_MAP_OP_CMD_STR
-+      ROUTE_MAP_SEQUENCE_CMD_STR)
-+{
-+      char xpath[XPATH_MAXLEN];
-+
-+      snprintf(xpath, sizeof(xpath),
-+               "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']",
-+               name, sequence);
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
-+                           bool show_defaults)
-+{
-+      const char *name = yang_dnode_get_string(dnode, "../name");
-+      const char *action = yang_dnode_get_string(dnode, "./action");
-+      const char *sequence = yang_dnode_get_string(dnode, "./sequence");
-+
-+      vty_out(vty, "route-map %s %s %s\n", name, action, sequence);
-+}
-+
-+void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
-+{
-+      vty_out(vty, "!\n");
-+}
-+
-+DEFPY(
-+      match_interface, match_interface_cmd,
-+      "match interface IFNAME",
-+      MATCH_STR
-+      "Match first hop interface of route\n"
-+      INTERFACE_STR)
-+{
-+      const char *xpath = "./match-condition[condition='interface']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_interface, no_match_interface_cmd,
-+      "no match interface [IFNAME]",
-+      NO_STR
-+      MATCH_STR
-+      "Match first hop interface of route\n"
-+      INTERFACE_STR)
-+{
-+      const char *xpath = "./match-condition[condition='interface']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ip_address, match_ip_address_cmd,
-+      "match ip address <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
-+      MATCH_STR
-+      IP_STR
-+      "Match address of route\n"
-+      "IP access-list number\n"
-+      "IP access-list number (expanded range)\n"
-+      "IP Access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-address-list']";
-+      char xpath_value[XPATH_MAXLEN + 32];
-+      int acln = acll ? acll : aclh;
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      if (name) {
-+              snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
-+                       xpath);
-+              nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+      } else /* if (acll || aclh) */ {
-+              if ((acln >= 1 && acln <= 99)
-+                  || (acln >= 1300 && acln <= 1999)) {
-+                      snprintf(xpath_value, sizeof(xpath_value),
-+                               "%s/access-list-num", xpath);
-+              } else {
-+                      /*
-+                       * if ((acln >= 100 && acln <= 199)
-+                       *     || (acln >= 2000 && acln <= 2699))
-+                       */
-+                      snprintf(xpath_value, sizeof(xpath_value),
-+                               "%s/access-list-num-extended", xpath);
-+              }
-+              nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
-+                                    acll_str ? acll_str : aclh_str);
-+      }
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ip_address, no_match_ip_address_cmd,
-+      "no match ip address [<(1-199)|(1300-2699)|WORD>]",
-+      NO_STR
-+      MATCH_STR
-+      IP_STR
-+      "Match address of route\n"
-+      "IP access-list number\n"
-+      "IP access-list number (expanded range)\n"
-+      "IP Access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-address-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ip_address_prefix_list,
-+      match_ip_address_prefix_list_cmd,
-+      "match ip address prefix-list WORD$name",
-+      MATCH_STR
-+      IP_STR
-+      "Match address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
-+      "no match ip address prefix-list [WORD]",
-+      NO_STR
-+      MATCH_STR
-+      IP_STR
-+      "Match address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ip_next_hop, match_ip_next_hop_cmd,
-+      "match ip next-hop <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
-+      MATCH_STR
-+      IP_STR
-+      "Match next-hop address of route\n"
-+      "IP access-list number\n"
-+      "IP access-list number (expanded range)\n"
-+      "IP Access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
-+      char xpath_value[XPATH_MAXLEN + 32];
-+      int acln = acll ? acll : aclh;
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      if (name) {
-+              snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
-+                       xpath);
-+              nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+      } else /* if (acll || aclh) */ {
-+              if ((acln >= 1 && acln <= 99)
-+                  || (acln >= 1300 && acln <= 1999)) {
-+                      snprintf(xpath_value, sizeof(xpath_value),
-+                               "%s/access-list-num", xpath);
-+              } else {
-+                      /*
-+                       * if ((acln >= 100 && acln <= 199)
-+                       *     || (acln >= 2000 && acln <= 2699))
-+                       */
-+                      snprintf(xpath_value, sizeof(xpath_value),
-+                               "%s/access-list-num-extended", xpath);
-+              }
-+              nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
-+                                    acll_str ? acll_str : aclh_str);
-+      }
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ip_next_hop, no_match_ip_next_hop_cmd,
-+      "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
-+      NO_STR
-+      MATCH_STR
-+      IP_STR
-+      "Match address of route\n"
-+      "IP access-list number\n"
-+      "IP access-list number (expanded range)\n"
-+      "IP Access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ip_next_hop_prefix_list,
-+      match_ip_next_hop_prefix_list_cmd,
-+      "match ip next-hop prefix-list WORD$name",
-+      MATCH_STR
-+      IP_STR
-+      "Match next-hop address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath =
-+              "./match-condition[condition='ipv4-next-hop-prefix-list']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ip_next_hop_prefix_list,
-+      no_match_ip_next_hop_prefix_list_cmd,
-+      "no match ip next-hop prefix-list [WORD]",
-+      NO_STR
-+      MATCH_STR
-+      IP_STR
-+      "Match next-hop address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath =
-+              "./match-condition[condition='ipv4-next-hop-prefix-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ip_next_hop_type, match_ip_next_hop_type_cmd,
-+      "match ip next-hop type <blackhole>$type",
-+      MATCH_STR
-+      IP_STR
-+      "Match next-hop address of route\n"
-+      "Match entries by type\n"
-+      "Blackhole\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type",
-+               xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
-+      "no match ip next-hop type [<blackhole>]",
-+      NO_STR MATCH_STR IP_STR
-+      "Match next-hop address of route\n"
-+      "Match entries by type\n"
-+      "Blackhole\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ipv6_address, match_ipv6_address_cmd,
-+      "match ipv6 address WORD$name",
-+      MATCH_STR
-+      IPV6_STR
-+      "Match IPv6 address of route\n"
-+      "IPv6 access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-address-list']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ipv6_address, no_match_ipv6_address_cmd,
-+      "no match ipv6 address [WORD]",
-+      NO_STR
-+      MATCH_STR
-+      IPV6_STR
-+      "Match IPv6 address of route\n"
-+      "IPv6 access-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-address-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
-+      "match ipv6 address prefix-list WORD$name",
-+      MATCH_STR
-+      IPV6_STR
-+      "Match address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ipv6_address_prefix_list,
-+      no_match_ipv6_address_prefix_list_cmd,
-+      "no match ipv6 address prefix-list [WORD]",
-+      NO_STR
-+      MATCH_STR
-+      IPV6_STR
-+      "Match address of route\n"
-+      "Match entries of prefix-lists\n"
-+      "IP prefix-list name\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
-+      "match ipv6 next-hop type <blackhole>$type",
-+      MATCH_STR IPV6_STR
-+      "Match next-hop address of route\n"
-+      "Match entries by type\n"
-+      "Blackhole\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type",
-+               xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
-+      "no match ipv6 next-hop type [<blackhole>]",
-+      NO_STR MATCH_STR IPV6_STR
-+      "Match address of route\n"
-+      "Match entries by type\n"
-+      "Blackhole\n")
-+{
-+      const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_metric, match_metric_cmd,
-+      "match metric (0-4294967295)$metric",
-+      MATCH_STR
-+      "Match metric of route\n"
-+      "Metric value\n")
-+{
-+      const char *xpath = "./match-condition[condition='metric']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_metric, no_match_metric_cmd,
-+      "no match metric [(0-4294967295)]",
-+      NO_STR
-+      MATCH_STR
-+      "Match metric of route\n"
-+      "Metric value\n")
-+{
-+      const char *xpath = "./match-condition[condition='metric']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      match_tag, match_tag_cmd,
-+      "match tag (1-4294967295)$tag",
-+      MATCH_STR
-+      "Match tag of route\n"
-+      "Tag value\n")
-+{
-+      const char *xpath = "./match-condition[condition='tag']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_match_tag, no_match_tag_cmd,
-+      "no match tag [(1-4294967295)]",
-+      NO_STR
-+      MATCH_STR
-+      "Match tag of route\n"
-+      "Tag value\n")
-+{
-+      const char *xpath = "./match-condition[condition='tag']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
-+                            bool show_defaults)
-+{
-+      int condition = yang_dnode_get_enum(dnode, "./condition");
-+      struct lyd_node *ln;
-+      const char *acl;
-+
-+      switch (condition) {
-+      case 0: /* interface */
-+              vty_out(vty, " match interface %s\n",
-+                      yang_dnode_get_string(dnode, "./interface"));
-+              break;
-+      case 1: /* ipv4-address-list */
-+      case 3: /* ipv4-next-hop-list */
-+              acl = NULL;
-+              if ((ln = yang_dnode_get(dnode, "./list-name")) != NULL)
-+                      acl = yang_dnode_get_string(ln, NULL);
-+              else if ((ln = yang_dnode_get(dnode, "./access-list-num"))
-+                       != NULL)
-+                      acl = yang_dnode_get_string(ln, NULL);
-+              else if ((ln = yang_dnode_get(dnode,
-+                                            "./access-list-num-extended"))
-+                       != NULL)
-+                      acl = yang_dnode_get_string(ln, NULL);
-+
-+              assert(acl);
-+
-+              switch (condition) {
-+              case 1:
-+                      vty_out(vty, " match ip address %s\n", acl);
-+                      break;
-+              case 3:
-+                      vty_out(vty, " match ip next-hop %s\n", acl);
-+                      break;
-+              }
-+              break;
-+      case 2: /* ipv4-prefix-list */
-+              vty_out(vty, " match ip address prefix-list %s\n",
-+                      yang_dnode_get_string(dnode, "./list-name"));
-+              break;
-+      case 4: /* ipv4-next-hop-prefix-list */
-+              vty_out(vty, " match ip next-hop prefix-list %s\n",
-+                      yang_dnode_get_string(dnode, "./list-name"));
-+              break;
-+      case 5: /* ipv4-next-hop-type */
-+              vty_out(vty, " match ip next-hop type %s\n",
-+                      yang_dnode_get_string(dnode, "./ipv4-next-hop-type"));
-+              break;
-+      case 6: /* ipv6-address-list */
-+              vty_out(vty, " match ipv6 address %s\n",
-+                      yang_dnode_get_string(dnode, "./list-name"));
-+              break;
-+      case 7: /* ipv6-prefix-list */
-+              vty_out(vty, " match ipv6 address prefix-list %s\n",
-+                      yang_dnode_get_string(dnode, "./list-name"));
-+              break;
-+      case 8: /* ipv6-next-hop-type */
-+              vty_out(vty, " match ipv6 next-hop type %s\n",
-+                      yang_dnode_get_string(dnode, "./ipv6-next-hop-type"));
-+              break;
-+      case 9: /* metric */
-+              vty_out(vty, " match metric %s\n",
-+                      yang_dnode_get_string(dnode, "./metric"));
-+              break;
-+      case 10: /* tag */
-+              vty_out(vty, " match tag %s\n",
-+                      yang_dnode_get_string(dnode, "./tag"));
-+              break;
-+      case 100:
-+              /* NOTHING: custom field, should be handled by daemon. */
-+              break;
-+      }
-+}
-+
-+DEFPY(
-+      set_ip_nexthop, set_ip_nexthop_cmd,
-+      "set ip next-hop A.B.C.D$addr",
-+      SET_STR
-+      IP_STR
-+      "Next hop address\n"
-+      "IP address of next hop\n")
-+{
-+      const char *xpath = "./set-action[action='ipv4-next-hop']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_set_ip_nexthop, no_set_ip_nexthop_cmd,
-+      "no set ip next-hop [A.B.C.D]",
-+      NO_STR
-+      SET_STR
-+      IP_STR
-+      "Next hop address\n"
-+      "IP address of next hop\n")
-+{
-+      const char *xpath = "./set-action[action='ipv4-next-hop']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd,
-+      "set ipv6 next-hop local X:X::X:X$addr",
-+      SET_STR
-+      IPV6_STR
-+      "IPv6 next-hop address\n"
-+      "IPv6 local address\n"
-+      "IPv6 address of next hop\n")
-+{
-+      const char *xpath = "./set-action[action='ipv6-next-hop']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd,
-+      "no set ipv6 next-hop local [X:X::X:X]",
-+      NO_STR
-+      SET_STR
-+      IPV6_STR
-+      "IPv6 next-hop address\n"
-+      "IPv6 local address\n"
-+      "IPv6 address of next hop\n")
-+{
-+      const char *xpath = "./set-action[action='ipv6-next-hop']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      set_metric, set_metric_cmd,
-+      "set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>",
-+      SET_STR
-+      "Metric value for destination routing protocol\n"
-+      "Metric value\n"
-+      "Assign round trip time\n"
-+      "Add round trip time\n"
-+      "Subtract round trip time\n"
-+      "Add metric\n"
-+      "Subtract metric\n")
-+{
-+      const char *xpath = "./set-action[action='metric']";
-+      char xpath_value[XPATH_MAXLEN];
-+      char value[64];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      if (rtt) {
-+              snprintf(xpath_value, sizeof(xpath_value),
-+                       "%s/use-round-trip-time", xpath);
-+              snprintf(value, sizeof(value), "true");
-+      } else if (artt) {
-+              snprintf(xpath_value, sizeof(xpath_value),
-+                       "%s/add-round-trip-time", xpath);
-+              snprintf(value, sizeof(value), "true");
-+      } else if (srtt) {
-+              snprintf(xpath_value, sizeof(xpath_value),
-+                       "%s/subtract-round-trip-time", xpath);
-+              snprintf(value, sizeof(value), "true");
-+      } else if (ametric) {
-+              snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric",
-+                       xpath);
-+              snprintf(value, sizeof(value), "true");
-+      } else if (smetric) {
-+              snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric",
-+                       xpath);
-+              snprintf(value, sizeof(value), "true");
-+      } else {
-+              snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath);
-+              snprintf(value, sizeof(value), "%lu", metric);
-+      }
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_set_metric, no_set_metric_cmd,
-+      "no set metric [(0-4294967295)]",
-+      NO_STR
-+      SET_STR
-+      "Metric value for destination routing protocol\n"
-+      "Metric value\n")
-+{
-+      const char *xpath = "./set-action[action='metric']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      set_tag, set_tag_cmd,
-+      "set tag (1-4294967295)$tag",
-+      SET_STR
-+      "Tag value for routing protocol\n"
-+      "Tag value\n")
-+{
-+      const char *xpath = "./set-action[action='tag']";
-+      char xpath_value[XPATH_MAXLEN];
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-+      snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
-+      nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_set_tag, no_set_tag_cmd,
-+      "no set tag [(1-4294967295)]",
-+      NO_STR
-+      SET_STR
-+      "Tag value for routing protocol\n"
-+      "Tag value\n")
-+{
-+      const char *xpath = "./set-action[action='tag']";
-+
-+      nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
-+                         bool show_defaults)
-+{
-+      int action = yang_dnode_get_enum(dnode, "./action");
-+
-+      switch (action) {
-+      case 0: /* ipv4-next-hop */
-+              vty_out(vty, " set ip next-hop %s\n",
-+                      yang_dnode_get_string(dnode, "./ipv4-address"));
-+              break;
-+      case 1: /* ipv6-next-hop */
-+              vty_out(vty, " set ipv6 next-hop local %s\n",
-+                      yang_dnode_get_string(dnode, "./ipv6-address"));
-+              break;
-+      case 2: /* metric */
-+              if (yang_dnode_get(dnode, "./use-round-trip-time")) {
-+                      vty_out(vty, " set metric rtt\n");
-+              } else if (yang_dnode_get(dnode, "./add-round-trip-time")) {
-+                      vty_out(vty, " set metric +rtt\n");
-+              } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) {
-+                      vty_out(vty, " set metric -rtt\n");
-+              } else if (yang_dnode_get(dnode, "./add-metric")) {
-+                      vty_out(vty, " set metric +metric\n");
-+              } else if (yang_dnode_get(dnode, "./subtract-metric")) {
-+                      vty_out(vty, " set metric -metric\n");
-+              } else {
-+                      vty_out(vty, " set metric %s\n",
-+                              yang_dnode_get_string(dnode, "./value"));
-+              }
-+              break;
-+      case 3: /* tag */
-+              vty_out(vty, " set tag %s\n",
-+                      yang_dnode_get_string(dnode, "./tag"));
-+              break;
-+      case 100:
-+              /* NOTHING: custom field, should be handled by daemon. */
-+              break;
-+      }
-+}
-+
-+DEFPY(
-+      rmap_onmatch_next, rmap_onmatch_next_cmd,
-+      "on-match next",
-+      "Exit policy on matches\n"
-+      "Next clause\n")
-+{
-+      nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "next");
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_rmap_onmatch_next,
-+      no_rmap_onmatch_next_cmd,
-+      "no on-match next",
-+      NO_STR
-+      "Exit policy on matches\n"
-+      "Next clause\n")
-+{
-+      nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      rmap_onmatch_goto, rmap_onmatch_goto_cmd,
-+      "on-match goto (1-65535)$rm_num",
-+      "Exit policy on matches\n"
-+      "Goto Clause number\n"
-+      "Number\n")
-+{
-+      nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "goto");
-+      nb_cli_enqueue_change(vty, "./goto-value", NB_OP_MODIFY, rm_num_str);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd,
-+      "no on-match goto",
-+      NO_STR
-+      "Exit policy on matches\n"
-+      "Goto Clause number\n")
-+{
-+      nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+/* Cisco/GNU Zebra compatibility aliases */
-+ALIAS(
-+      rmap_onmatch_goto, rmap_continue_cmd,
-+      "continue (1-65535)$rm_num",
-+      "Continue on a different entry within the route-map\n"
-+      "Route-map entry sequence number\n")
-+
-+ALIAS(
-+      no_rmap_onmatch_goto, no_rmap_continue_cmd,
-+      "no continue [(1-65535)]",
-+      NO_STR
-+      "Continue on a different entry within the route-map\n"
-+      "Route-map entry sequence number\n")
-+
-+void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
-+                              bool show_defaults)
-+{
-+      int exit_policy = yang_dnode_get_enum(dnode, NULL);
-+
-+      switch (exit_policy) {
-+      case 0: /* permit-or-deny */
-+              /* NOTHING: default option. */
-+              break;
-+      case 1: /* next */
-+              vty_out(vty, " on-match next\n");
-+              break;
-+      case 2: /* goto */
-+              vty_out(vty, " on-match goto %s\n",
-+                      yang_dnode_get_string(dnode, "../goto-value"));
-+              break;
-+      }
-+}
-+
-+DEFPY(
-+      rmap_call, rmap_call_cmd,
-+      "call WORD$name",
-+      "Jump to another Route-Map after match+set\n"
-+      "Target route-map name\n")
-+{
-+      nb_cli_enqueue_change(vty, "./call", NB_OP_MODIFY, name);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+DEFPY(
-+      no_rmap_call, no_rmap_call_cmd,
-+      "no call",
-+      NO_STR
-+      "Jump to another Route-Map after match+set\n")
-+{
-+      nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
-+                       bool show_defaults)
-+{
-+      vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL));
-+}
-+
-+DEFPY(
-+      rmap_description, rmap_description_cmd,
-+      "description LINE...",
-+      "Route-map comment\n"
-+      "Comment describing this route-map rule\n")
-+{
-+      char *desc;
-+      int rv;
-+
-+      desc = argv_concat(argv, argc, 1);
-+      nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc);
-+      rv = nb_cli_apply_changes(vty, NULL);
-+      XFREE(MTYPE_TMP, desc);
-+
-+      return rv;
-+}
-+
-+DEFUN (no_rmap_description,
-+       no_rmap_description_cmd,
-+       "no description",
-+       NO_STR
-+       "Route-map comment\n")
-+{
-+      nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL);
-+
-+      return nb_cli_apply_changes(vty, NULL);
-+}
-+
-+void route_map_description_show(struct vty *vty, struct lyd_node *dnode,
-+                              bool show_defaults)
-+{
-+      vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL));
-+}
-+
-+static int route_map_config_write(struct vty *vty)
-+{
-+      struct lyd_node *dnode;
-+      int written = 0;
-+
-+      dnode = yang_dnode_get(running_config->dnode,
-+                             "/frr-route-map:lib");
-+      if (dnode) {
-+              nb_cli_show_dnode_cmds(vty, dnode, false);
-+              written = 1;
-+      }
-+
-+      return written;
-+}
-+
-+/* Route map node structure. */
-+static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1};
-+
-+static void rmap_autocomplete(vector comps, struct cmd_token *token)
-+{
-+      struct route_map *map;
-+
-+      for (map = route_map_master.head; map; map = map->next)
-+              vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name));
-+}
-+
-+static const struct cmd_variable_handler rmap_var_handlers[] = {
-+      {.varname = "route_map", .completions = rmap_autocomplete},
-+      {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete},
-+      {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete},
-+      {.completions = NULL}
-+};
-+
-+void route_map_cli_init(void)
-+{
-+      /* Auto complete handler. */
-+      cmd_variable_handler_register(rmap_var_handlers);
-+
-+      /* CLI commands. */
-+      install_node(&rmap_node, route_map_config_write);
-+      install_default(RMAP_NODE);
-+      install_element(CONFIG_NODE, &route_map_cmd);
-+      install_element(CONFIG_NODE, &no_route_map_cmd);
-+      install_element(CONFIG_NODE, &no_route_map_all_cmd);
-+
-+      /* Install the on-match stuff */
-+      install_element(RMAP_NODE, &route_map_cmd);
-+      install_element(RMAP_NODE, &rmap_onmatch_next_cmd);
-+      install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd);
-+      install_element(RMAP_NODE, &rmap_onmatch_goto_cmd);
-+      install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd);
-+      install_element(RMAP_NODE, &rmap_continue_cmd);
-+      install_element(RMAP_NODE, &no_rmap_continue_cmd);
-+
-+      /* Install the call stuff. */
-+      install_element(RMAP_NODE, &rmap_call_cmd);
-+      install_element(RMAP_NODE, &no_rmap_call_cmd);
-+
-+      /* Install description commands. */
-+      install_element(RMAP_NODE, &rmap_description_cmd);
-+      install_element(RMAP_NODE, &no_rmap_description_cmd);
-+
-+      /* Install 'match' commands. */
-+      install_element(RMAP_NODE, &match_interface_cmd);
-+      install_element(RMAP_NODE, &no_match_interface_cmd);
-+
-+      install_element(RMAP_NODE, &match_ip_address_cmd);
-+      install_element(RMAP_NODE, &no_match_ip_address_cmd);
-+
-+      install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd);
-+      install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
-+
-+      install_element(RMAP_NODE, &match_ip_next_hop_cmd);
-+      install_element(RMAP_NODE, &no_match_ip_next_hop_cmd);
-+
-+      install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
-+      install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
-+
-+      install_element(RMAP_NODE, &match_ip_next_hop_type_cmd);
-+      install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd);
-+
-+      install_element(RMAP_NODE, &match_ipv6_address_cmd);
-+      install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
-+
-+      install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
-+      install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
-+
-+      install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd);
-+      install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd);
-+
-+      install_element(RMAP_NODE, &match_metric_cmd);
-+      install_element(RMAP_NODE, &no_match_metric_cmd);
-+
-+      install_element(RMAP_NODE, &match_tag_cmd);
-+      install_element(RMAP_NODE, &no_match_tag_cmd);
-+
-+      /* Install 'set' commands. */
-+      install_element(RMAP_NODE, &set_ip_nexthop_cmd);
-+      install_element(RMAP_NODE, &no_set_ip_nexthop_cmd);
-+
-+      install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd);
-+      install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
-+
-+      install_element(RMAP_NODE, &set_metric_cmd);
-+      install_element(RMAP_NODE, &no_set_metric_cmd);
-+
-+      install_element(RMAP_NODE, &set_tag_cmd);
-+      install_element(RMAP_NODE, &no_set_tag_cmd);
-+}
-diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
-index 02eb756334..b9ac01e865 100644
---- a/lib/routemap_northbound.c
-+++ b/lib/routemap_northbound.c
-@@ -1218,6 +1218,8 @@ const struct frr_yang_module_info frr_route_map_info = {
-                       .cbs = {
-                               .create = lib_route_map_entry_create,
-                               .destroy = lib_route_map_entry_destroy,
-+                              .cli_show = route_map_instance_show,
-+                              .cli_show_end = route_map_instance_show_end,
-                       }
-               },
-               {
-@@ -1225,6 +1227,7 @@ const struct frr_yang_module_info frr_route_map_info = {
-                       .cbs = {
-                               .modify = lib_route_map_entry_description_modify,
-                               .destroy = lib_route_map_entry_description_destroy,
-+                              .cli_show = route_map_description_show,
-                       }
-               },
-               {
-@@ -1238,12 +1241,14 @@ const struct frr_yang_module_info frr_route_map_info = {
-                       .cbs = {
-                               .modify = lib_route_map_entry_call_modify,
-                               .destroy = lib_route_map_entry_call_destroy,
-+                              .cli_show = route_map_call_show,
-                       }
-               },
-               {
-                       .xpath = "/frr-route-map:lib/route-map/entry/exit-policy",
-                       .cbs = {
-                               .modify = lib_route_map_entry_exit_policy_modify,
-+                              .cli_show = route_map_exit_policy_show,
-                       }
-               },
-               {
-@@ -1258,6 +1263,7 @@ const struct frr_yang_module_info frr_route_map_info = {
-                       .cbs = {
-                               .create = lib_route_map_entry_match_condition_create,
-                               .destroy = lib_route_map_entry_match_condition_destroy,
-+                              .cli_show = route_map_condition_show,
-                       }
-               },
-               {
-@@ -1321,6 +1327,7 @@ const struct frr_yang_module_info frr_route_map_info = {
-                       .cbs = {
-                               .create = lib_route_map_entry_set_action_create,
-                               .destroy = lib_route_map_entry_set_action_destroy,
-+                              .cli_show = route_map_action_show,
-                       }
-               },
-               {
-diff --git a/lib/subdir.am b/lib/subdir.am
-index 94b3d933ac..ffac721256 100644
---- a/lib/subdir.am
-+++ b/lib/subdir.am
-@@ -71,6 +71,7 @@ lib_libfrr_la_SOURCES = \
-       lib/qobj.c \
-       lib/ringbuf.c \
-       lib/routemap.c \
-+      lib/routemap_cli.c \
-       lib/routemap_northbound.c \
-       lib/sbuf.c \
-       lib/seqlock.c \
-@@ -122,6 +123,7 @@ vtysh_scan += \
-       $(top_srcdir)/lib/nexthop_group.c \
-       $(top_srcdir)/lib/plist.c \
-       $(top_srcdir)/lib/routemap.c \
-+      $(top_srcdir)/lib/routemap_cli.c \
-       $(top_srcdir)/lib/vrf.c \
-       $(top_srcdir)/lib/vty.c \
-       # end
-@@ -141,6 +143,8 @@ lib/nexthop_group_clippy.c: $(CLIPPY_DEPS)
- lib/nexthop_group.lo: lib/nexthop_group_clippy.c
- lib/northbound_cli_clippy.c: $(CLIPPY_DEPS)
- lib/northbound_cli.lo: lib/northbound_cli_clippy.c
-+lib/routemap_cli_clippy.c: $(CLIPPY_DEPS)
-+lib/routemap_cli.lo: lib/routemap_cli_clippy.c
- lib/vty_clippy.c: $(CLIPPY_DEPS)
- lib/vty.lo: lib/vty_clippy.c
- lib/log_vty_clippy.c: $(CLIPPY_DEPS)
-diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
-index 13413888bf..b7ac0abe02 100755
---- a/vtysh/extract.pl.in
-+++ b/vtysh/extract.pl.in
-@@ -87,7 +87,7 @@ sub scan_file {
-         if ($file =~ /lib\/keychain\.c$/) {
-             $protocol = "VTYSH_RIPD|VTYSH_EIGRPD";
-         }
--        elsif ($file =~ /lib\/routemap\.c$/) {
-+        elsif ($file =~ /lib\/routemap\.c$/ || $file =~ /lib\/routemap_cli\.c$/) {
-             $protocol = "VTYSH_RMAP";
-         }
-         elsif ($file =~ /lib\/vrf\.c$/) {
-
-From a513824c343971e51603471948c958430b602371 Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Tue, 1 Oct 2019 17:56:16 -0300
-Subject: [PATCH 05/10] yang/lib: add filter model to code
-
-This fixes a warning on daemons that use route map about filter yang
-model not being included in the binary.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/subdir.am  | 1 +
- yang/subdir.am | 1 +
- 2 files changed, 2 insertions(+)
-
-diff --git a/lib/subdir.am b/lib/subdir.am
-index ffac721256..4f62eb2264 100644
---- a/lib/subdir.am
-+++ b/lib/subdir.am
-@@ -106,6 +106,7 @@ lib_libfrr_la_SOURCES = \
-       # end
- nodist_lib_libfrr_la_SOURCES = \
-+      yang/frr-filter.yang.c \
-       yang/frr-interface.yang.c \
-       yang/frr-route-map.yang.c \
-       yang/frr-route-types.yang.c \
-diff --git a/yang/subdir.am b/yang/subdir.am
-index 7a15a6a309..c1297dafd5 100644
---- a/yang/subdir.am
-+++ b/yang/subdir.am
-@@ -19,6 +19,7 @@ EXTRA_DIST += yang/embedmodel.py
- # global symbols :(.  Just put it in the daemon.  Dynamic libraries.so work
- # without problems, as seen in libfrr.
-+dist_yangmodels_DATA += yang/frr-filter.yang
- dist_yangmodels_DATA += yang/frr-module-translator.yang
- dist_yangmodels_DATA += yang/frr-test-module.yang
- dist_yangmodels_DATA += yang/frr-interface.yang
-
-From a162869ef0798ef98d756238c6b89108a69f5a5d Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Mon, 30 Sep 2019 15:02:15 -0300
-Subject: [PATCH 06/10] lib: fix route map generic error output
-
-Two fixes here:
-
-*   Don't attempt to use `vty` pointer in vty;
-*   When `vty` is unavailable output to log;
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap.c | 68 ++++++++++++++++++++++++++++++++++----------------
- 1 file changed, 46 insertions(+), 22 deletions(-)
-
-diff --git a/lib/routemap.c b/lib/routemap.c
-index e07ad08123..5369fa771f 100644
---- a/lib/routemap.c
-+++ b/lib/routemap.c
-@@ -308,15 +308,21 @@ int generic_match_add(struct vty *vty, struct route_map_index *index,
-       ret = route_map_add_match(index, command, arg, type);
-       switch (ret) {
-       case RMAP_RULE_MISSING:
--              vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty, "%% [%s] Can't find rule.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Can't find rule: %s", command);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_ERROR:
--              vty_out(vty,
--                      "%% [%s] Argument form is unsupported or malformed.\n",
--                      frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty,
-+                              "%% [%s] Argument form is unsupported or malformed.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Argument form is unsupported or malformed: "
-+                                "%s %s", command, arg);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_SUCCESS:
-               /*
-                * Nothing to do here move along
-@@ -353,13 +359,21 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index,
-       ret = route_map_delete_match(index, command, dep_name, type);
-       switch (ret) {
-       case RMAP_RULE_MISSING:
--              vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty, "%% [%s] Can't find rule.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Can't find rule: %s", command);
-               retval = CMD_WARNING_CONFIG_FAILED;
-               break;
-       case RMAP_COMPILE_ERROR:
--              vty_out(vty,
--                      "%% [%s] Argument form is unsupported or malformed.\n",
--                      frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty,
-+                              "%% [%s] Argument form is unsupported or malformed.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Argument form is unsupported or malformed: "
-+                                "%s %s", command, arg);
-               retval = CMD_WARNING_CONFIG_FAILED;
-               break;
-       case RMAP_COMPILE_SUCCESS:
-@@ -383,15 +397,20 @@ int generic_set_add(struct vty *vty, struct route_map_index *index,
-       ret = route_map_add_set(index, command, arg);
-       switch (ret) {
-       case RMAP_RULE_MISSING:
--              vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              else
-+                      zlog_warn("Can't find rule: %s", command);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_ERROR:
--              vty_out(vty,
--                      "%% [%s] Argument form is unsupported or malformed.\n",
--                      frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty,
-+                              "%% [%s] Argument form is unsupported or malformed.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Argument form is unsupported or malformed: "
-+                                "%s %s", command, arg);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_SUCCESS:
-               break;
-       }
-@@ -407,15 +426,20 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index,
-       ret = route_map_delete_set(index, command, arg);
-       switch (ret) {
-       case RMAP_RULE_MISSING:
--              vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst);
-+              else
-+                      zlog_warn("Can't find rule: %s", command);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_ERROR:
--              vty_out(vty,
--                      "%% [%s] Argument form is unsupported or malformed.\n",
--                      frr_protonameinst);
-+              if (vty)
-+                      vty_out(vty,
-+                              "%% [%s] Argument form is unsupported or malformed.\n",
-+                              frr_protonameinst);
-+              else
-+                      zlog_warn("Argument form is unsupported or malformed: "
-+                                "%s %s", command, arg);
-               return CMD_WARNING_CONFIG_FAILED;
--              break;
-       case RMAP_COMPILE_SUCCESS:
-               break;
-       }
-
-From e324ef433ca611ddf8274015c0b36c8de1fb3075 Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Tue, 1 Oct 2019 15:52:51 -0300
-Subject: [PATCH 07/10] lib: add backward compatibility for route map
-
-Allow old CLI users to still print their configuration without migrating
-to northbound.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap_cli.c | 56 +++++++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 55 insertions(+), 1 deletion(-)
-
-diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
-index 987693c961..7023710564 100644
---- a/lib/routemap_cli.c
-+++ b/lib/routemap_cli.c
-@@ -46,6 +46,9 @@ DEFPY_NOSH(
-       ROUTE_MAP_OP_CMD_STR
-       ROUTE_MAP_SEQUENCE_CMD_STR)
- {
-+      struct route_map_index *rmi;
-+      struct route_map *rm;
-+      int action_type;
-       char xpath_action[XPATH_MAXLEN + 64];
-       char xpath_index[XPATH_MAXLEN + 32];
-       char xpath[XPATH_MAXLEN];
-@@ -63,9 +66,16 @@ DEFPY_NOSH(
-       nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action);
-       rv = nb_cli_apply_changes(vty, NULL);
--      if (rv == CMD_SUCCESS)
-+      if (rv == CMD_SUCCESS) {
-               VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
-+              /* Add support for non-migrated route map users. */
-+              rm = route_map_get(name);
-+              action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY;
-+              rmi = route_map_index_get(rm, action_type, sequence);
-+              VTY_PUSH_CONTEXT(RMAP_NODE, rmi);
-+      }
-+
-       return rv;
- }
-@@ -105,11 +115,55 @@ DEFPY(
- void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
-                            bool show_defaults)
- {
-+      const struct route_map_rule *rmr;
-+      const struct route_map_index *rmi;
-       const char *name = yang_dnode_get_string(dnode, "../name");
-       const char *action = yang_dnode_get_string(dnode, "./action");
-       const char *sequence = yang_dnode_get_string(dnode, "./sequence");
-       vty_out(vty, "route-map %s %s %s\n", name, action, sequence);
-+
-+      rmi = nb_running_get_entry(dnode, NULL, false);
-+      if (rmi == NULL) {
-+              /*
-+               * We can't have outdated rules if route map hasn't
-+               * been created yet.
-+               */
-+              return;
-+      }
-+
-+#define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue
-+
-+      /* Print route map `match` for old CLI users. */
-+      for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) {
-+              /* Skip all matches implemented by northbound. */
-+              SKIP_RULE("interface");
-+              SKIP_RULE("ip address");
-+              SKIP_RULE("ip address prefix-list");
-+              SKIP_RULE("ip next-hop");
-+              SKIP_RULE("ip next-hop prefix-list");
-+              SKIP_RULE("ip next-hop type");
-+              SKIP_RULE("ipv6 address");
-+              SKIP_RULE("ipv6 address prefix-list");
-+              SKIP_RULE("ipv6 next-hop type");
-+              SKIP_RULE("metric");
-+              SKIP_RULE("tag");
-+
-+              vty_out(vty, " match %s %s\n", rmr->cmd->str,
-+                      rmr->rule_str ? rmr->rule_str : "");
-+      }
-+
-+      /* Print route map `set` for old CLI users. */
-+      for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) {
-+              /* Skip all sets implemented by northbound. */
-+              SKIP_RULE("metric");
-+              SKIP_RULE("tag");
-+
-+              vty_out(vty, " set %s %s\n", rmr->cmd->str,
-+                      rmr->rule_str ? rmr->rule_str : "");
-+      }
-+
-+#undef SKIP_RULE
- }
- void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
-
-From 91835f1fd2a8dd05a5ba03c8961b699aaabed7e7 Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Tue, 1 Oct 2019 17:56:41 -0300
-Subject: [PATCH 08/10] *: fix route map integration
-
-Add the appropriated code to bootstrap route map northbound for all
-daemons.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- bgpd/bgp_main.c     | 2 ++
- eigrpd/eigrp_main.c | 1 +
- isisd/isis_main.c   | 2 ++
- ospf6d/ospf6_main.c | 1 +
- ospfd/ospf_main.c   | 2 ++
- ripd/rip_main.c     | 2 ++
- ripngd/ripng_main.c | 2 ++
- zebra/main.c        | 1 +
- 8 files changed, 13 insertions(+)
-
-diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
-index fab2a584c0..0b33f7e9d9 100644
---- a/bgpd/bgp_main.c
-+++ b/bgpd/bgp_main.c
-@@ -360,6 +360,8 @@ static void bgp_vrf_terminate(void)
- }
- static const struct frr_yang_module_info *const bgpd_yang_modules[] = {
-+      &frr_interface_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT,
-diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c
-index 0746b04edb..922c0fe3e7 100644
---- a/eigrpd/eigrp_main.c
-+++ b/eigrpd/eigrp_main.c
-@@ -140,6 +140,7 @@ struct quagga_signal_t eigrp_signals[] = {
- static const struct frr_yang_module_info *const eigrpd_yang_modules[] = {
-       &frr_eigrpd_info,
-       &frr_interface_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(eigrpd, EIGRP, .vty_port = EIGRP_VTY_PORT,
-diff --git a/isisd/isis_main.c b/isisd/isis_main.c
-index 364441f79d..f7fe089b99 100644
---- a/isisd/isis_main.c
-+++ b/isisd/isis_main.c
-@@ -39,6 +39,7 @@
- #include "vrf.h"
- #include "qobj.h"
- #include "libfrr.h"
-+#include "routemap.h"
- #include "isisd/isis_constants.h"
- #include "isisd/isis_common.h"
-@@ -166,6 +167,7 @@ static const struct frr_yang_module_info *const isisd_yang_modules[] = {
- #ifndef FABRICD
-       &frr_isisd_info,
- #endif /* ifndef FABRICD */
-+      &frr_route_map_info,
- };
- #ifdef FABRICD
-diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
-index 0aaefeb3c2..e4bed7a79d 100644
---- a/ospf6d/ospf6_main.c
-+++ b/ospf6d/ospf6_main.c
-@@ -167,6 +167,7 @@ struct quagga_signal_t ospf6_signals[] = {
- static const struct frr_yang_module_info *const ospf6d_yang_modules[] = {
-       &frr_interface_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT,
-diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
-index d02ffe0448..7caa79d207 100644
---- a/ospfd/ospf_main.c
-+++ b/ospfd/ospf_main.c
-@@ -40,6 +40,7 @@
- #include "zclient.h"
- #include "vrf.h"
- #include "libfrr.h"
-+#include "routemap.h"
- #include "ospfd/ospfd.h"
- #include "ospfd/ospf_interface.h"
-@@ -126,6 +127,7 @@ struct quagga_signal_t ospf_signals[] = {
- static const struct frr_yang_module_info *const ospfd_yang_modules[] = {
-       &frr_interface_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT,
-diff --git a/ripd/rip_main.c b/ripd/rip_main.c
-index 060bb76585..ca41afaea6 100644
---- a/ripd/rip_main.c
-+++ b/ripd/rip_main.c
-@@ -35,6 +35,7 @@
- #include "vrf.h"
- #include "if_rmap.h"
- #include "libfrr.h"
-+#include "routemap.h"
- #include "ripd/ripd.h"
- #include "ripd/rip_nb.h"
-@@ -115,6 +116,7 @@ static struct quagga_signal_t ripd_signals[] = {
- static const struct frr_yang_module_info *const ripd_yang_modules[] = {
-       &frr_interface_info,
-       &frr_ripd_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT,
-diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
-index 9daeeb9580..99adb2cba7 100644
---- a/ripngd/ripng_main.c
-+++ b/ripngd/ripng_main.c
-@@ -36,6 +36,7 @@
- #include "vrf.h"
- #include "if_rmap.h"
- #include "libfrr.h"
-+#include "routemap.h"
- #include "ripngd/ripngd.h"
- #include "ripngd/ripng_nb.h"
-@@ -115,6 +116,7 @@ struct quagga_signal_t ripng_signals[] = {
- static const struct frr_yang_module_info *const ripngd_yang_modules[] = {
-       &frr_interface_info,
-       &frr_ripngd_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT,
-diff --git a/zebra/main.c b/zebra/main.c
-index f23702d878..5951c7e280 100644
---- a/zebra/main.c
-+++ b/zebra/main.c
-@@ -237,6 +237,7 @@ struct quagga_signal_t zebra_signals[] = {
- static const struct frr_yang_module_info *const zebra_yang_modules[] = {
-       &frr_interface_info,
-+      &frr_route_map_info,
- };
- FRR_DAEMON_INFO(
-
-From 54a35ff48b600cd59b715b6e5aea4e69de1b995f Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Mon, 14 Oct 2019 23:29:19 -0300
-Subject: [PATCH 09/10] lib: fix route map northbound memory leak
-
-Keep a list of hook contexts used by northbound so we don't lose the
-pointer when free()ing the route map index entry data.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap.c            |  5 +++++
- lib/routemap.h            | 11 +++++++++++
- lib/routemap_northbound.c | 39 ++++++++++++++++++++++++++++++---------
- 3 files changed, 46 insertions(+), 9 deletions(-)
-
-diff --git a/lib/routemap.c b/lib/routemap.c
-index 5369fa771f..912cf28202 100644
---- a/lib/routemap.c
-+++ b/lib/routemap.c
-@@ -909,6 +909,7 @@ static struct route_map_index *route_map_index_new(void)
-       new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index));
-       new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
-+      TAILQ_INIT(&new->rhclist);
-       QOBJ_REG(new, route_map_index);
-       return new;
- }
-@@ -924,6 +925,10 @@ void route_map_index_delete(struct route_map_index *index, int notify)
-               zlog_debug("Deleting route-map %s sequence %d",
-                          index->map->name, index->pref);
-+      /* Free route map northbound hook contexts. */
-+      while (!TAILQ_EMPTY(&index->rhclist))
-+              routemap_hook_context_free(TAILQ_FIRST(&index->rhclist));
-+
-       /* Free route match. */
-       while ((rule = index->match_list.head) != NULL)
-               route_map_rule_delete(&index->match_list, rule);
-diff --git a/lib/routemap.h b/lib/routemap.h
-index 70e150c981..05c958967c 100644
---- a/lib/routemap.h
-+++ b/lib/routemap.h
-@@ -162,6 +162,9 @@ struct route_map_rule_list {
-       struct route_map_rule *tail;
- };
-+/* Forward struct declaration: the complete can be found later this file. */
-+struct routemap_hook_context;
-+
- /* Route map index structure. */
- struct route_map_index {
-       struct route_map *map;
-@@ -194,6 +197,9 @@ struct route_map_index {
-       uint64_t applied;
-       uint64_t applied_clear;
-+      /* List of match/sets contexts. */
-+      TAILQ_HEAD(, routemap_hook_context) rhclist;
-+
-       QOBJ_FIELDS
- };
- DECLARE_QOBJ_TYPE(route_map_index)
-@@ -660,6 +666,7 @@ struct routemap_hook_context {
-       route_map_event_t rhc_event;
-       routemap_set_hook_fun rhc_shook;
-       routemap_match_hook_fun rhc_mhook;
-+      TAILQ_ENTRY(routemap_hook_context) rhc_entry;
- };
- int lib_route_map_entry_match_destroy(enum nb_event event,
-@@ -667,6 +674,10 @@ int lib_route_map_entry_match_destroy(enum nb_event event,
- int lib_route_map_entry_set_destroy(enum nb_event event,
-                                   const struct lyd_node *dnode);
-+struct routemap_hook_context *
-+routemap_hook_context_insert(struct route_map_index *rmi);
-+void routemap_hook_context_free(struct routemap_hook_context *rhc);
-+
- extern const struct frr_yang_module_info frr_route_map_info;
- /* routemap_cli.c */
-diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
-index b9ac01e865..3173a708e4 100644
---- a/lib/routemap_northbound.c
-+++ b/lib/routemap_northbound.c
-@@ -74,6 +74,30 @@ int lib_route_map_entry_set_destroy(enum nb_event event,
-       return NB_OK;
- }
-+/*
-+ * Auxiliary hook context list manipulation functions.
-+ */
-+struct routemap_hook_context *
-+routemap_hook_context_insert(struct route_map_index *rmi)
-+{
-+      struct routemap_hook_context *rhc;
-+
-+      rhc = XCALLOC(MTYPE_TMP, sizeof(*rhc));
-+      rhc->rhc_rmi = rmi;
-+      TAILQ_INSERT_TAIL(&rmi->rhclist, rhc, rhc_entry);
-+
-+      return rhc;
-+}
-+
-+void
-+routemap_hook_context_free(struct routemap_hook_context *rhc)
-+{
-+      struct route_map_index *rmi = rhc->rhc_rmi;
-+
-+      TAILQ_REMOVE(&rmi->rhclist, rhc, rhc_entry);
-+      XFREE(MTYPE_TMP, rhc);
-+}
-+
- /*
-  * XPath: /frr-route-map:lib/route-map
-  */
-@@ -436,20 +460,17 @@ lib_route_map_entry_match_condition_create(enum nb_event event,
-                                          union nb_resource *resource)
- {
-       struct routemap_hook_context *rhc;
-+      struct route_map_index *rmi;
-       switch (event) {
-       case NB_EV_VALIDATE:
--              /* NOTHING */
--              break;
-       case NB_EV_PREPARE:
--              resource->ptr = XCALLOC(MTYPE_TMP, sizeof(*rhc));
--              break;
-       case NB_EV_ABORT:
--              XFREE(MTYPE_TMP, resource->ptr);
-+              /* NOTHING */
-               break;
-       case NB_EV_APPLY:
--              rhc = resource->ptr;
--              rhc->rhc_rmi = nb_running_get_entry(dnode, NULL, true);
-+              rmi = nb_running_get_entry(dnode, NULL, true);
-+              rhc = routemap_hook_context_insert(rmi);
-               nb_running_set_entry(dnode, rhc);
-               break;
-       }
-@@ -469,7 +490,7 @@ lib_route_map_entry_match_condition_destroy(enum nb_event event,
-       rv = lib_route_map_entry_match_destroy(event, dnode);
-       rhc = nb_running_unset_entry(dnode);
--      XFREE(MTYPE_TMP, rhc);
-+      routemap_hook_context_free(rhc);
-       return rv;
- }
-@@ -893,7 +914,7 @@ static int lib_route_map_entry_set_action_destroy(enum nb_event event,
-       rv = lib_route_map_entry_set_destroy(event, dnode);
-       rhc = nb_running_unset_entry(dnode);
--      XFREE(MTYPE_TMP, rhc);
-+      routemap_hook_context_free(rhc);
-       return rv;
- }
-
-From 79661106763d4f6d9c200a4f4d25439f1cfecf3a Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Wed, 5 Feb 2020 11:09:31 -0300
-Subject: [PATCH 10/10] lib: fix route-map YANG module on old gcc versions
-
-Copy the fix made in 'lib/if.c' to 'lib/routemap_northbound.c' so we can
-have a working YANG model when compiled with GCC version less than 5.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- lib/routemap_northbound.c | 25 +++++++++++++++++++++++++
- 1 file changed, 25 insertions(+)
-
-diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
-index 3173a708e4..78f2a5a018 100644
---- a/lib/routemap_northbound.c
-+++ b/lib/routemap_northbound.c
-@@ -1224,7 +1224,32 @@ lib_route_map_entry_set_action_tag_destroy(enum nb_event event,
- }
- /* clang-format off */
-+#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
-+/*
-+ * gcc versions before 5.x miscalculate the size for structs with variable
-+ * length arrays (they just count it as size 0)
-+ */
-+struct frr_yang_module_info_sizen {
-+      /* YANG module name. */
-+      const char *name;
-+
-+      /* Northbound callbacks. */
-+      const struct {
-+              /* Data path of this YANG node. */
-+              const char *xpath;
-+
-+              /* Callbacks implemented for this node. */
-+              struct nb_callbacks cbs;
-+
-+              /* Priority - lower priorities are processed first. */
-+              uint32_t priority;
-+      } nodes[28];
-+};
-+
-+const struct frr_yang_module_info_sizen frr_route_map_info_sizen asm("frr_route_map_info") = {
-+#else
- const struct frr_yang_module_info frr_route_map_info = {
-+#endif
-       .name = "frr-route-map",
-       .nodes = {
-               {
diff --git a/net/frr/patches/012-add_yang_filter.patch b/net/frr/patches/012-add_yang_filter.patch
deleted file mode 100644 (file)
index e3c4eba..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-From bec0aa85b1f404ac9800c7524070fcf8582e82bc Mon Sep 17 00:00:00 2001
-From: Rafael Zalamena <rzalamena@opensourcerouting.org>
-Date: Thu, 1 Aug 2019 19:56:46 -0300
-Subject: [PATCH] yang: simplify filter choice by removing cases
-
-Based on @rwestphal feedback, lets remove `case`s where we don't expect
-to add more items or items with more than one `leaf`.
-
-Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
----
- yang/frr-filter.yang | 48 +++++++++++++++++---------------------------
- 1 file changed, 18 insertions(+), 30 deletions(-)
-
-diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang
-index 92af6aebfd..e79ede87b7 100644
---- a/yang/frr-filter.yang
-+++ b/yang/frr-filter.yang
-@@ -107,23 +107,17 @@ module frr-filter {
-            Extended access list: source value to match.";
-         mandatory true;
--        case host {
--          leaf host {
--            description "Host to match";
--            type inet:ipv4-address;
--          }
-+        leaf host {
-+          description "Host to match";
-+          type inet:ipv4-address;
-         }
--        case network {
--          leaf network {
--            description "Network to match";
--            type inet:ipv4-prefix;
--          }
-+        leaf network {
-+          description "Network to match";
-+          type inet:ipv4-prefix;
-         }
--        case any {
--          leaf any {
--            description "Match any";
--            type empty;
--          }
-+        leaf any {
-+          description "Match any";
-+          type empty;
-         }
-       }
-@@ -132,23 +126,17 @@ module frr-filter {
-               ./sequence >= 2000 and ./sequence <= 2699";
-         description "Destination value to match";
--        case destination-host {
--          leaf destination-host {
--            description "Host to match";
--            type inet:ipv4-address;
--          }
-+        leaf destination-host {
-+          description "Host to match";
-+          type inet:ipv4-address;
-         }
--        case destination-network {
--          leaf destination-network {
--            description "Network to match";
--            type inet:ipv4-prefix;
--          }
-+        leaf destination-network {
-+          description "Network to match";
-+          type inet:ipv4-prefix;
-         }
--        case destination-any {
--          leaf destination-any {
--            description "Match any";
--            type empty;
--          }
-+        leaf destination-any {
-+          description "Match any";
-+          type empty;
-         }
-       }
-     }
diff --git a/net/frr/patches/013-backport_northbound.patch b/net/frr/patches/013-backport_northbound.patch
deleted file mode 100644 (file)
index dacb456..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-From dc397e4c0adc13982fc5d83a1afc42178708f4a5 Mon Sep 17 00:00:00 2001
-From: Renato Westphal <renato@opensourcerouting.org>
-Date: Fri, 3 Apr 2020 20:10:04 -0300
-Subject: [PATCH] lib: consolidate flexible array hack in a single place
-
-Old gcc versions (< 5.x) have a bug that prevents C99 flexible
-arrays from working properly on shared libraries.
-
-We already have a hack in place to work around this problem, but it
-needs to be replicated in every declaration of a frr_yang_module_info
-variable within libfrr. This clearly isn't a good solution if we
-consider that many more libfrr YANG modules are about to come in
-the future.
-
-This commit introduces a different workaround that operates within
-the northbound layer itself, such that implementers of libfrr YANG
-modules won't need to worry about this problem anymore.
-
-Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
----
- lib/if.c                  | 24 ------------------------
- lib/northbound.c          |  7 +++++++
- lib/northbound.h          | 11 +++++++++++
- lib/routemap_northbound.c | 25 -------------------------
- 4 files changed, 18 insertions(+), 49 deletions(-)
-
-diff --git a/lib/if.c b/lib/if.c
-index dabf66799d..24b103b3ff 100644
---- a/lib/if.c
-+++ b/lib/if.c
-@@ -1657,31 +1657,7 @@ static int lib_interface_description_destroy(enum nb_event event,
- /* clang-format off */
--#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
--/* gcc versions before 5.x miscalculate the size for structs with variable
-- * length arrays (they just count it as size 0)
-- */
--struct frr_yang_module_info_size3 {
--      /* YANG module name. */
--      const char *name;
--
--      /* Northbound callbacks. */
--      const struct {
--              /* Data path of this YANG node. */
--              const char *xpath;
--
--              /* Callbacks implemented for this node. */
--              struct nb_callbacks cbs;
--
--              /* Priority - lower priorities are processed first. */
--              uint32_t priority;
--      } nodes[3];
--};
--
--const struct frr_yang_module_info_size3 frr_interface_info_size3 asm("frr_interface_info") = {
--#else
- const struct frr_yang_module_info frr_interface_info = {
--#endif
-       .name = "frr-interface",
-       .nodes = {
-               {
-diff --git a/lib/northbound.c b/lib/northbound.c
-index cebedcff09..85e723d7cf 100644
---- a/lib/northbound.c
-+++ b/lib/northbound.c
-@@ -1866,6 +1866,13 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module)
-               struct nb_node *nb_node;
-               uint32_t priority;
-+              if (i > YANG_MODULE_MAX_NODES) {
-+                      zlog_err(
-+                              "%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.",
-+                              __func__, module->name, YANG_MODULE_MAX_NODES);
-+                      exit(1);
-+              }
-+
-               nb_node = nb_node_find(module->nodes[i].xpath);
-               if (!nb_node) {
-                       flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
-diff --git a/lib/northbound.h b/lib/northbound.h
-index 76a11e518c..19a2ba0865 100644
---- a/lib/northbound.h
-+++ b/lib/northbound.h
-@@ -403,6 +403,13 @@ struct nb_node {
- /* The YANG list doesn't contain key leafs. */
- #define F_NB_NODE_KEYLESS_LIST 0x02
-+/*
-+ * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays
-+ * from working properly on shared libraries. For those compilers, use a fixed
-+ * size array to work around the problem.
-+ */
-+#define YANG_MODULE_MAX_NODES 1024
-+
- struct frr_yang_module_info {
-       /* YANG module name. */
-       const char *name;
-@@ -417,7 +424,11 @@ struct frr_yang_module_info {
-               /* Priority - lower priorities are processed first. */
-               uint32_t priority;
-+#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
-+      } nodes[YANG_MODULE_MAX_NODES + 1];
-+#else
-       } nodes[];
-+#endif
- };
- /* Northbound error codes. */
-diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
-index 69cebbd2a1..dd4cbd7d99 100644
---- a/lib/routemap_northbound.c
-+++ b/lib/routemap_northbound.c
-@@ -1221,32 +1221,7 @@ lib_route_map_entry_set_action_tag_destroy(enum nb_event event,
- }
- /* clang-format off */
--#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
--/*
-- * gcc versions before 5.x miscalculate the size for structs with variable
-- * length arrays (they just count it as size 0)
-- */
--struct frr_yang_module_info_sizen {
--      /* YANG module name. */
--      const char *name;
--
--      /* Northbound callbacks. */
--      const struct {
--              /* Data path of this YANG node. */
--              const char *xpath;
--
--              /* Callbacks implemented for this node. */
--              struct nb_callbacks cbs;
--
--              /* Priority - lower priorities are processed first. */
--              uint32_t priority;
--      } nodes[28];
--};
--
--const struct frr_yang_module_info_sizen frr_route_map_info_sizen asm("frr_route_map_info") = {
--#else
- const struct frr_yang_module_info frr_route_map_info = {
--#endif
-       .name = "frr-route-map",
-       .nodes = {
-               {
diff --git a/net/frr/patches/014-backport_northbound.patch b/net/frr/patches/014-backport_northbound.patch
deleted file mode 100644 (file)
index 879c380..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-From 97cd849362b45ecbcb20194b5771c5ce777de6bc Mon Sep 17 00:00:00 2001
-From: Renato Westphal <renato@opensourcerouting.org>
-Date: Tue, 21 Apr 2020 21:27:47 -0300
-Subject: [PATCH] lib: create a wrapper function for all northbound callbacks
-
-The intention here is to keep the code more organized. These wrappers
-should be used by the northbound clients only, and never directly
-by any YANG backend code.
-
-Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
----
- lib/northbound.c        | 222 +++++++++++++++++++++++-----------------
- lib/northbound_grpc.cpp |   3 +-
- 2 files changed, 131 insertions(+), 94 deletions(-)
-
-diff --git a/lib/northbound.c b/lib/northbound.c
-index 85e723d7cf..d10e4713f5 100644
---- a/lib/northbound.c
-+++ b/lib/northbound.c
-@@ -62,11 +62,10 @@ static struct {
-  */
- static bool transaction_in_progress;
-+static int nb_callback_pre_validate(const struct nb_node *nb_node,
-+                                  const struct lyd_node *dnode);
- static int nb_callback_configuration(const enum nb_event event,
-                                    struct nb_config_change *change);
--static void nb_log_callback(const enum nb_event event,
--                          enum nb_operation operation, const char *xpath,
--                          const char *value);
- static struct nb_transaction *nb_transaction_new(struct nb_config *config,
-                                                struct nb_config_cbs *changes,
-                                                enum nb_client client,
-@@ -609,18 +608,7 @@ static int nb_candidate_validate_code(struct nb_config *candidate,
-                       if (!nb_node->cbs.pre_validate)
-                               goto next;
--                      if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config,
--                                           DEBUG_MODE_ALL)) {
--                              char xpath[XPATH_MAXLEN];
--
--                              yang_dnode_get_path(child, xpath,
--                                                  sizeof(xpath));
--                              nb_log_callback(NB_EV_VALIDATE,
--                                              NB_OP_PRE_VALIDATE, xpath,
--                                              NULL);
--                      }
--
--                      ret = (*nb_node->cbs.pre_validate)(child);
-+                      ret = nb_callback_pre_validate(nb_node, child);
-                       if (ret != NB_OK)
-                               return NB_ERR_VALIDATION;
-@@ -791,14 +779,128 @@ int nb_running_lock_check(enum nb_client client, const void *user)
-       return ret;
- }
--static void nb_log_callback(const enum nb_event event,
--                          enum nb_operation operation, const char *xpath,
--                          const char *value)
-+static void nb_log_config_callback(const enum nb_event event,
-+                                 enum nb_operation operation,
-+                                 const struct lyd_node *dnode)
- {
-+      const char *value;
-+      char xpath[XPATH_MAXLEN];
-+
-+      if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL))
-+              return;
-+
-+      yang_dnode_get_path(dnode, xpath, sizeof(xpath));
-+      if (yang_snode_is_typeless_data(dnode->schema))
-+              value = "(none)";
-+      else
-+              value = yang_dnode_get_string(dnode, NULL);
-+
-       zlog_debug(
-               "northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
-               nb_event_name(event), nb_operation_name(operation), xpath,
--              value ? value : "(NULL)");
-+              value);
-+}
-+
-+static int nb_callback_create(const struct nb_node *nb_node,
-+                            enum nb_event event, const struct lyd_node *dnode,
-+                            union nb_resource *resource)
-+{
-+      nb_log_config_callback(event, NB_OP_CREATE, dnode);
-+
-+      return nb_node->cbs.create(event, dnode, resource);
-+}
-+
-+static int nb_callback_modify(const struct nb_node *nb_node,
-+                            enum nb_event event, const struct lyd_node *dnode,
-+                            union nb_resource *resource)
-+{
-+      nb_log_config_callback(event, NB_OP_MODIFY, dnode);
-+
-+      return nb_node->cbs.modify(event, dnode, resource);
-+}
-+
-+static int nb_callback_destroy(const struct nb_node *nb_node,
-+                             enum nb_event event,
-+                             const struct lyd_node *dnode)
-+{
-+      nb_log_config_callback(event, NB_OP_DESTROY, dnode);
-+
-+      return nb_node->cbs.destroy(event, dnode);
-+}
-+
-+static int nb_callback_move(const struct nb_node *nb_node, enum nb_event event,
-+                          const struct lyd_node *dnode)
-+{
-+      nb_log_config_callback(event, NB_OP_MOVE, dnode);
-+
-+      return nb_node->cbs.move(event, dnode);
-+}
-+
-+static int nb_callback_pre_validate(const struct nb_node *nb_node,
-+                                  const struct lyd_node *dnode)
-+{
-+      nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
-+
-+      return nb_node->cbs.pre_validate(dnode);
-+}
-+
-+static void nb_callback_apply_finish(const struct nb_node *nb_node,
-+                                   const struct lyd_node *dnode)
-+{
-+      nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
-+
-+      nb_node->cbs.apply_finish(dnode);
-+}
-+
-+struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
-+                                     const char *xpath,
-+                                     const void *list_entry)
-+{
-+      DEBUGD(&nb_dbg_cbs_state,
-+             "northbound callback (get_elem): xpath [%s] list_entry [%p]",
-+             xpath, list_entry);
-+
-+      return nb_node->cbs.get_elem(xpath, list_entry);
-+}
-+
-+const void *nb_callback_get_next(const struct nb_node *nb_node,
-+                               const void *parent_list_entry,
-+                               const void *list_entry)
-+{
-+      DEBUGD(&nb_dbg_cbs_state,
-+             "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
-+             nb_node->xpath, parent_list_entry, list_entry);
-+
-+      return nb_node->cbs.get_next(parent_list_entry, list_entry);
-+}
-+
-+int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
-+                       struct yang_list_keys *keys)
-+{
-+      DEBUGD(&nb_dbg_cbs_state,
-+             "northbound callback (get_keys): node [%s] list_entry [%p]",
-+             nb_node->xpath, list_entry);
-+
-+      return nb_node->cbs.get_keys(list_entry, keys);
-+}
-+
-+const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
-+                                   const void *parent_list_entry,
-+                                   const struct yang_list_keys *keys)
-+{
-+      DEBUGD(&nb_dbg_cbs_state,
-+             "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
-+             nb_node->xpath, parent_list_entry);
-+
-+      return nb_node->cbs.lookup_entry(parent_list_entry, keys);
-+}
-+
-+int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
-+                  const struct list *input, struct list *output)
-+{
-+      DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
-+
-+      return nb_node->cbs.rpc(xpath, input, output);
- }
- /*
-@@ -815,15 +917,6 @@ static int nb_callback_configuration(const enum nb_event event,
-       union nb_resource *resource;
-       int ret = NB_ERR;
--      if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
--              const char *value = "(none)";
--
--              if (dnode && !yang_snode_is_typeless_data(dnode->schema))
--                      value = yang_dnode_get_string(dnode, NULL);
--
--              yang_dnode_get_path(dnode, xpath, sizeof(xpath));
--              nb_log_callback(event, operation, xpath, value);
--      }
-       if (event == NB_EV_VALIDATE)
-               resource = NULL;
-@@ -832,16 +925,16 @@ static int nb_callback_configuration(const enum nb_event event,
-       switch (operation) {
-       case NB_OP_CREATE:
--              ret = (*nb_node->cbs.create)(event, dnode, resource);
-+              ret = nb_callback_create(nb_node, event, dnode, resource);
-               break;
-       case NB_OP_MODIFY:
--              ret = (*nb_node->cbs.modify)(event, dnode, resource);
-+              ret = nb_callback_modify(nb_node, event, dnode, resource);
-               break;
-       case NB_OP_DESTROY:
--              ret = (*nb_node->cbs.destroy)(event, dnode);
-+              ret = nb_callback_destroy(nb_node, event, dnode);
-               break;
-       case NB_OP_MOVE:
--              ret = (*nb_node->cbs.move)(event, dnode);
-+              ret = nb_callback_move(nb_node, event, dnode);
-               break;
-       default:
-               yang_dnode_get_path(dnode, xpath, sizeof(xpath));
-@@ -890,57 +983,6 @@ static int nb_callback_configuration(const enum nb_event event,
-       return ret;
- }
--struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
--                                     const char *xpath,
--                                     const void *list_entry)
--{
--      DEBUGD(&nb_dbg_cbs_state,
--             "northbound callback (get_elem): xpath [%s] list_entry [%p]",
--             xpath, list_entry);
--
--      return nb_node->cbs.get_elem(xpath, list_entry);
--}
--
--const void *nb_callback_get_next(const struct nb_node *nb_node,
--                               const void *parent_list_entry,
--                               const void *list_entry)
--{
--      DEBUGD(&nb_dbg_cbs_state,
--             "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
--             nb_node->xpath, parent_list_entry, list_entry);
--
--      return nb_node->cbs.get_next(parent_list_entry, list_entry);
--}
--
--int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
--                       struct yang_list_keys *keys)
--{
--      DEBUGD(&nb_dbg_cbs_state,
--             "northbound callback (get_keys): node [%s] list_entry [%p]",
--             nb_node->xpath, list_entry);
--
--      return nb_node->cbs.get_keys(list_entry, keys);
--}
--
--const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
--                                   const void *parent_list_entry,
--                                   const struct yang_list_keys *keys)
--{
--      DEBUGD(&nb_dbg_cbs_state,
--             "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
--             nb_node->xpath, parent_list_entry);
--
--      return nb_node->cbs.lookup_entry(parent_list_entry, keys);
--}
--
--int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
--                  const struct list *input, struct list *output)
--{
--      DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", 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 void *user, const char *comment)
-@@ -1058,7 +1100,6 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
- {
-       struct nb_config_cbs cbs;
-       struct nb_config_cb *cb;
--      char xpath[XPATH_MAXLEN];
-       /* Initialize tree of 'apply_finish' callbacks. */
-       RB_INIT(nb_config_cbs, &cbs);
-@@ -1075,6 +1116,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
-                * be called though).
-                */
-               if (change->cb.operation == NB_OP_DESTROY) {
-+                      char xpath[XPATH_MAXLEN];
-+
-                       dnode = dnode->parent;
-                       if (!dnode)
-                               break;
-@@ -1111,15 +1154,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
-       }
-       /* Call the 'apply_finish' callbacks, sorted by their priorities. */
--      RB_FOREACH (cb, nb_config_cbs, &cbs) {
--              if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
--                      yang_dnode_get_path(cb->dnode, xpath, sizeof(xpath));
--                      nb_log_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, xpath,
--                                      NULL);
--              }
--
--              (*cb->nb_node->cbs.apply_finish)(cb->dnode);
--      }
-+      RB_FOREACH (cb, nb_config_cbs, &cbs)
-+              nb_callback_apply_finish(cb->nb_node, cb->dnode);
-       /* Release memory. */
-       while (!RB_EMPTY(nb_config_cbs, &cbs)) {
-diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
-index b195f1aeca..66bf05c1ab 100644
---- a/lib/northbound_grpc.cpp
-+++ b/lib/northbound_grpc.cpp
-@@ -545,7 +545,8 @@ class NorthboundImpl final : public frr::Northbound::Service
-               }
-               // Execute callback registered for this XPath.
--              if (nb_node->cbs.rpc(xpath, input_list, output_list) != NB_OK) {
-+              if (nb_callback_rpc(nb_node, xpath, input_list, output_list)
-+                  != NB_OK) {
-                       flog_warn(EC_LIB_NB_CB_RPC,
-                                 "%s: rpc callback failed: %s", __func__,
-                                 xpath);
diff --git a/net/frr/patches/098-fix_mips_libyang.patch b/net/frr/patches/098-fix_mips_libyang.patch
deleted file mode 100644 (file)
index 3785cfe..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
---- a/lib/northbound.h
-+++ b/lib/northbound.h
-@@ -504,11 +504,7 @@ struct frr_yang_module_info {
-               /* Priority - lower priorities are processed first. */
-               uint32_t priority;
--#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
-       } nodes[YANG_MODULE_MAX_NODES + 1];
--#else
--      } nodes[];
--#endif
- };
- /* Northbound error codes. */
git clone https://git.99rst.org/PROJECT