frr: makefile cleanup and misc fixes 12039/head
authorLucian Cristian <lucian.cristian@gmail.com>
Fri, 1 May 2020 17:41:08 +0000 (20:41 +0300)
committerLucian Cristian <lucian.cristian@gmail.com>
Sun, 3 May 2020 00:06:20 +0000 (03:06 +0300)
fix mips runtime by backporting some yang changes from master
added commited fixes to 7.3
also add option for snmp support

Signed-off-by: Lucian Cristian <lucian.cristian@gmail.com>
17 files changed:
net/frr/Config.in
net/frr/Makefile
net/frr/files/vtysh.conf [new file with mode: 0644]
net/frr/patches/010-add_yahng_filter.patch [new file with mode: 0644]
net/frr/patches/010-add_yang_routemap.patch [new file with mode: 0644]
net/frr/patches/011-mod_yang_routemap_model.patch [new file with mode: 0644]
net/frr/patches/012-add_yang_filter.patch [new file with mode: 0644]
net/frr/patches/013-backport_northbound.patch [new file with mode: 0644]
net/frr/patches/014-backport_northbound.patch [new file with mode: 0644]
net/frr/patches/020-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/021-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/022-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/023-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/024-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/025-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/026-7.3_backports.patch [new file with mode: 0644]
net/frr/patches/098-fix_mips_libyang.patch [new file with mode: 0644]

index 4154c33a61ee47fe49c61ea10c790af5388dc6ec..7a7ea3fec59de173c85ab250182e0c4d4bf93cac 100644 (file)
@@ -13,6 +13,20 @@ choice
                 bool "internal SSL support"
 endchoice
 
+comment "SNMP support"
+
+choice
+        prompt "Enable SNMP support"
+        default FRR_NO_SNMP
+
+        config FRR_SNMP
+                bool "SNMP enable"
+
+        config FRR_NO_SNMP
+                bool "SNMP disable"
+endchoice
+
+
 comment "Packages"
 
 endif
index 1e6cdc8def5ec86e7b3e1cc4242ca1c2089e5bc5..03d4b315dca52228a6f707ecd676ea5df94189c2 100644 (file)
@@ -8,13 +8,15 @@
 include $(TOPDIR)/rules.mk
 PKG_NAME:=frr
 PKG_VERSION:=7.3
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE_URL:=https://github.com/FRRouting/frr/releases/download/$(PKG_NAME)-$(PKG_VERSION)/
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
 PKG_HASH:=529e1bbc3a20d55e94c38a95513bcf971d4b403ecb00afdaf0c229e3f560b2b6
 PKG_MAINTAINER:=Lucian Cristian <lucian.cristian@gmail.com>
 
+PKG_LICENSE:=GPL-2.0-only LGPL-2.1-only
+
 PKG_DAEMON_AVAILABLE:= \
        babeld \
        bfdd \
@@ -38,15 +40,20 @@ PKG_CONFIG_DEPENDS:= \
        CONFIG_IPV6 \
        CONFIG_FRR_OPENSSL \
        CONFIG_FRR_INTERNAL \
+       CONFIG_FRR_SNMP \
+       CONFIG_FRR_NO_SNMP \
        CONFIG_PACKAGE_frr-libfrr \
        CONFIG_PACKAGE_frr-vtysh \
        CONFIG_PACKAGE_frr-watchfrr \
        CONFIG_PACKAGE_frr-zebra \
        $(patsubst %,CONFIG_PACKAGE_frr-%,$(PKG_DAEMON_AVAILABLE)) \
 
+PKG_FIXUP:=autoreconf
 PKG_BUILD_PARALLEL:=1
-PKG_BUILD_DEPENDS:=python3/host
-PKG_LICENSE:=GPL-2.0
+PKG_INSTALL:=1
+
+PKG_BUILD_DEPENDS:=frr/host
+HOST_BUILD_DEPENDS:=python3/host
 
 include $(INCLUDE_DIR)/package.mk
 include $(INCLUDE_DIR)/host-build.mk
@@ -82,7 +89,7 @@ endef
 define Package/frr-libfrr
   $(call Package/frr/Default)
   TITLE:=zebra library
-  DEPENDS+=+librt +libatomic +libjson-c +libyang +FRR_OPENSSL:libopenssl
+  DEPENDS+=+librt +libatomic +libcap +libjson-c +libyang +FRR_OPENSSL:libopenssl +FRR_SNMP:libnetsnmp
   CONFLICTS:=quagga-libzebra
 endef
 
@@ -128,98 +135,85 @@ define BuildDaemon
 
     define Package/frr-$(1)/install
        $(INSTALL_DIR) $$(1)/usr/sbin
-       if [ "$(1)" != "fabricd" ]; then \
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/$(1)/.libs/$(1) $$(1)/usr/sbin/; \
-       else \
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/isisd/.libs/$(1) $$(1)/usr/sbin/; fi
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/$(1) $$(1)/usr/sbin/; \
        if [ "$(1)" == "nhrpd" ]; then \
        $(INSTALL_DIR) $$(1)/usr/lib; \
-       $(CP) $(PKG_BUILD_DIR)/build/lib/.libs/libfrrcares.so* $$(1)/usr/lib/; fi
+       $(CP) $(PKG_INSTALL_DIR)/usr/lib/libfrrcares.so* $$(1)/usr/lib/; fi
     endef
 
     $$(eval $$(call BuildPackage,frr-$(1)))
 endef
 
 define Package/frr-libfrr/conffiles
-/etc/frr/
+/etc/frr/daemons
+/etc/frr/frr.conf
+/etc/frr/vtysh.conf
+endef
+
+TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed -latomic
+TARGET_CFLAGS += -flto
+
+define Host/Configure
+       $(Host/Configure/Default)
+       $(SED) 's/$$$$(MAKE) $$$$(AM_MAKEFLAGS) install-am/# $$$$(MAKE) $$$$(AM_MAKEFLAGS) install-am/' $(HOST_BUILD_DIR)/Makefile.in
 endef
 
-TARGET_LDFLAGS += -latomic
+HOST_CONFIGURE_ARGS+= \
+       --enable-clippy-only
 
-define Build/Prepare
-       $(Build/Prepare/Default)
-       mkdir -p $(PKG_BUILD_DIR)/build
+define Host/Install
+       $(INSTALL_DIR) $(STAGING_DIR_HOSTPKG)/bin
+       $(INSTALL_BIN) $(HOST_BUILD_DIR)/lib/clippy $(STAGING_DIR_HOSTPKG)/bin/
 endef
 
-define Build/Configure
-    ( cd $(PKG_BUILD_DIR)/build/ ; \
-    ../configure \
-       --host="$(GNU_TARGET_NAME)" \
-       --build="$(GNU_HOST_NAME)" \
-       CFLAGS="$(TARGET_CFLAGS) $(EXTRA_CFLAGS)" \
-       CXXFLAGS="$(TARGET_CFLAGS) $(EXTRA_CFLAGS)" \
-       LDFLAGS="$(TARGET_LDFLAGS) $(EXTRA_LDFLAGS)" \
-       HOST_CFLAGS="$(HOST_CFLAGS)" \
-       HOST_LDFLAGS="$(HOST_LDFLAGS)" \
-       BUILD_CPPFLAGS="$(TARGET_CPPLAGS)" \
-       BUILD_CFLAGS="$(TARGET_CFLAGS)" \
-       BUILD_LDFLAGS="$(TARGET_LDFLAGS)" \
+CONFIGURE_ARGS+= \
+       --with-clippy=$(STAGING_DIR_HOSTPKG)/bin/clippy \
        --prefix=/usr \
        --enable-shared \
        --disable-static \
        --enable-user=network \
        --enable-group=network \
-       --enable-multipath=16 \
-       --disable-capabilities \
        --disable-ospfclient \
        --disable-doc \
        --disable-backtrace \
        --localstatedir=/var/run/frr \
        --sysconfdir=/etc/frr/ \
        $(if $(CONFIG_FRR_OPENSSL),--with-crypto=openssl,) \
+       $(if $(CONFIG_FRR_SNMP),--enable-snmp,) \
        $(foreach m,$(PKG_DAEMON_AVAILABLE), \
        $(call autoconf_bool,CONFIG_PACKAGE_frr-$(m),$(m)) ) \
        $(call autoconf_bool,CONFIG_PACKAGE_frr-vtysh,vtysh) \
-       $(call autoconf_bool,CONFIG_PACKAGE_frr-libfrr,zebra) \
-)
-endef
-
-#      just speed it up
-NUM_CORES ?= $(shell grep -c "vendor_id" /proc/cpuinfo)
+       $(call autoconf_bool,CONFIG_PACKAGE_frr-libfrr,zebra)
 
-define Build/Compile
-       $(MAKE) -C $(PKG_BUILD_DIR)/build -j$(NUM_CORES)
-endef
 
 define Package/frr/install
-       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/init.d
        $(INSTALL_BIN) ./files/frrcommon.sh $(1)/usr/sbin/
-       $(INSTALL_DIR) $(1)/etc/init.d
        $(INSTALL_BIN) ./files/frr $(1)/etc/init.d/
 endef
 
 define Package/frr-watchfrr/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) ./files/watchfrr.sh $(1)/usr/sbin/
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/watchfrr/.libs/watchfrr $(1)/usr/sbin/
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/watchfrr $(1)/usr/sbin/
 endef
 
 define Package/frr-zebra/install
        $(INSTALL_DIR) $(1)/usr/sbin
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/zebra/.libs/zebra $(1)/usr/sbin/
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/zebra $(1)/usr/sbin/
 endef
 
 define Package/frr-libfrr/install
-       $(INSTALL_DIR) $(1)/usr/lib
-       $(CP) $(PKG_BUILD_DIR)/build/lib/.libs/libfrr.so* $(1)/usr/lib/
-       $(INSTALL_DIR) $(1)/etc/frr
-       chmod 0750 $(1)/etc/frr
+       $(INSTALL_DIR) $(1)/usr/lib $(1)/etc/frr
+       $(CP) $(PKG_INSTALL_DIR)/usr/lib/libfrr.so* $(1)/usr/lib/
+       $(if $(CONFIG_FRR_SNMP),$(CP) $(PKG_INSTALL_DIR)/usr/lib/libfrrsnmp.so* $(1)/usr/lib/,)
        $(INSTALL_CONF) ./files/{frr.conf,daemons} $(1)/etc/frr/
 endef
 
 define Package/frr-vtysh/install
-       $(INSTALL_DIR) $(1)/usr/bin
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/vtysh/.libs/vtysh $(1)/usr/bin/
+       $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/frr
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/vtysh $(1)/usr/bin/
+       $(INSTALL_CONF) ./files/vtysh.conf $(1)/etc/frr/
 endef
 
 $(eval $(call HostBuild))
diff --git a/net/frr/files/vtysh.conf b/net/frr/files/vtysh.conf
new file mode 100644 (file)
index 0000000..4e0a2be
--- /dev/null
@@ -0,0 +1,7 @@
+!
+! Sample configuration file for vtysh.
+!
+!service integrated-vtysh-config
+!hostname quagga-router
+!username root nopassword
+!
diff --git a/net/frr/patches/010-add_yahng_filter.patch b/net/frr/patches/010-add_yahng_filter.patch
new file mode 100644 (file)
index 0000000..2409dd8
--- /dev/null
@@ -0,0 +1,385 @@
+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
new file mode 100644 (file)
index 0000000..7026acb
--- /dev/null
@@ -0,0 +1,390 @@
+--- 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
new file mode 100644 (file)
index 0000000..ac56137
--- /dev/null
@@ -0,0 +1,5045 @@
+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
new file mode 100644 (file)
index 0000000..e3c4eba
--- /dev/null
@@ -0,0 +1,83 @@
+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
new file mode 100644 (file)
index 0000000..dacb456
--- /dev/null
@@ -0,0 +1,147 @@
+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
new file mode 100644 (file)
index 0000000..879c380
--- /dev/null
@@ -0,0 +1,330 @@
+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/020-7.3_backports.patch b/net/frr/patches/020-7.3_backports.patch
new file mode 100644 (file)
index 0000000..054c785
--- /dev/null
@@ -0,0 +1,33 @@
+From 0b7086494e9fe056f35560bcab53ff9eb0e68df4 Mon Sep 17 00:00:00 2001
+From: Mark Stapp <mjs@voltanet.io>
+Date: Wed, 19 Feb 2020 08:44:05 -0500
+Subject: [PATCH] zebra: fix missing route-advert stubs
+
+Stubs are used when frr is built without route-advert
+support; a couple of apis were missing, causing builds to fail.
+
+Signed-off-by: Mark Stapp <mjs@voltanet.io>
+---
+ zebra/rtadv.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/zebra/rtadv.c b/zebra/rtadv.c
+index e9a97d4b15..c710978d78 100644
+--- a/zebra/rtadv.c
++++ b/zebra/rtadv.c
+@@ -2399,4 +2399,15 @@ void rtadv_cmd_init(void)
+ {
+       /* Empty.*/;
+ }
++
++void rtadv_stop_ra(struct interface *ifp)
++{
++      /* Empty.*/;
++}
++
++void rtadv_stop_ra_all(void)
++{
++      /* Empty.*/;
++}
++
+ #endif /* HAVE_RTADV */
diff --git a/net/frr/patches/021-7.3_backports.patch b/net/frr/patches/021-7.3_backports.patch
new file mode 100644 (file)
index 0000000..1187559
--- /dev/null
@@ -0,0 +1,218 @@
+From 2b5eda4c0539dcd30a06c975be36f879cc454e9f Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@cumulusnetworks.com>
+Date: Mon, 10 Feb 2020 19:25:52 -0500
+Subject: [PATCH 1/3] bgpd: Update failed reason to distinguish some NHT
+ scenarios
+
+Current failed reasons for bgp when you have a peer that
+is not online yet is `Waiting for NHT`, even if NHT has
+succeeded.  Add some code to differentiate this.
+
+eva# show bgp ipv4 uni summ failed
+BGP router identifier 192.168.201.135, local AS number 3923 vrf-id 0
+BGP table version 0
+RIB entries 0, using 0 bytes of memory
+Peers 2, using 43 KiB of memory
+Neighbor        EstdCnt DropCnt ResetTime Reason
+192.168.44.1          0       0    never  Waiting for NHT
+192.168.201.139       0       0    never  Waiting for Open to Succeed
+Total number of neighbors 2
+eva#
+
+eva# show bgp nexthop
+Current BGP nexthop cache:
+ 192.168.44.1 invalid, peer 192.168.44.1
+  Must be Connected
+  Last update: Mon Feb 10 19:05:19 2020
+
+ 192.168.201.139 valid [IGP metric 0], #paths 0, peer 192.168.201.139
+
+So 192.168.201.139 is a peer for a connected route that has not been
+created on .139, while 44.1 nexthop tracking has not succeeded yet.
+
+Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
+---
+ bgpd/bgp_fsm.c |  4 +++-
+ bgpd/bgp_nht.c | 23 ++++++++++++++++-------
+ bgpd/bgpd.h    | 12 +++++++++---
+ 3 files changed, 28 insertions(+), 11 deletions(-)
+
+diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
+index 3667dae83d..107e9fc892 100644
+--- a/bgpd/bgp_fsm.c
++++ b/bgpd/bgp_fsm.c
+@@ -560,7 +560,9 @@ const char *const peer_down_str[] = {"",
+                              "Waiting for NHT",
+                              "Waiting for Peer IPv6 LLA",
+                              "Waiting for VRF to be initialized",
+-                             "No AFI/SAFI activated for peer"};
++                             "No AFI/SAFI activated for peer",
++                             "AS Set config change",
++                             "Waiting for peer OPEN"};
+ static int bgp_graceful_restart_timer_expire(struct thread *thread)
+ {
+diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
+index a50fc7d697..e9496e47a9 100644
+--- a/bgpd/bgp_nht.c
++++ b/bgpd/bgp_nht.c
+@@ -788,13 +788,22 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
+               bgp_process(bgp_path, rn, afi, safi);
+       }
+-      if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) {
+-              if (BGP_DEBUG(nht, NHT))
+-                      zlog_debug("%s: Updating peer (%s(%s)) status with NHT",
+-                                 __FUNCTION__, peer->host,
+-                                 peer->bgp->name_pretty);
+-              bgp_fsm_event_update(peer, bgp_isvalid_nexthop(bnc));
+-              SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
++      if (peer) {
++              int valid_nexthops = bgp_isvalid_nexthop(bnc);
++
++              if (valid_nexthops)
++                      peer->last_reset = PEER_DOWN_WAITING_OPEN;
++              else
++                      peer->last_reset = PEER_DOWN_WAITING_NHT;
++
++              if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) {
++                      if (BGP_DEBUG(nht, NHT))
++                              zlog_debug("%s: Updating peer (%s(%s)) status with NHT",
++                                         __FUNCTION__, peer->host,
++                                         peer->bgp->name_pretty);
++                      bgp_fsm_event_update(peer, valid_nexthops);
++                      SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
++              }
+       }
+       RESET_FLAG(bnc->change_flags);
+diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
+index 7d81579009..66d7633553 100644
+--- a/bgpd/bgpd.h
++++ b/bgpd/bgpd.h
+@@ -1185,10 +1185,10 @@ struct peer {
+ #define PEER_DOWN_REMOTE_AS_CHANGE       2 /* neighbor remote-as command */
+ #define PEER_DOWN_LOCAL_AS_CHANGE        3 /* neighbor local-as command */
+ #define PEER_DOWN_CLID_CHANGE            4 /* bgp cluster-id command */
+-#define PEER_DOWN_CONFED_ID_CHANGE       5 /* bgp confederation identifier command */
++#define PEER_DOWN_CONFED_ID_CHANGE       5 /* bgp confederation id command */
+ #define PEER_DOWN_CONFED_PEER_CHANGE     6 /* bgp confederation peer command */
+-#define PEER_DOWN_RR_CLIENT_CHANGE       7 /* neighbor route-reflector-client command */
+-#define PEER_DOWN_RS_CLIENT_CHANGE       8 /* neighbor route-server-client command */
++#define PEER_DOWN_RR_CLIENT_CHANGE       7 /* neighbor rr-client command */
++#define PEER_DOWN_RS_CLIENT_CHANGE       8 /* neighbor rs-client command */
+ #define PEER_DOWN_UPDATE_SOURCE_CHANGE   9 /* neighbor update-source command */
+ #define PEER_DOWN_AF_ACTIVATE           10 /* neighbor activate command */
+ #define PEER_DOWN_USER_SHUTDOWN         11 /* neighbor shutdown command */
+@@ -1212,6 +1212,12 @@ struct peer {
+ #define PEER_DOWN_VRF_UNINIT            29 /* Associated VRF is not init yet */
+ #define PEER_DOWN_NOAFI_ACTIVATED       30 /* No AFI/SAFI activated for peer */
+ #define PEER_DOWN_AS_SETS_REJECT        31 /* Reject routes with AS_SET */
++#define PEER_DOWN_WAITING_OPEN          32 /* Waiting for open to succeed */
++      /*
++       * Remember to update peer_down_str in bgp_fsm.c when you add
++       * a new value to the last_reset reason
++       */
++
+       size_t last_reset_cause_size;
+       uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE];
+
+From 4098f79949dd0e1e4ed7b89df8dc9b2be81fa9d6 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Fri, 14 Feb 2020 23:21:55 +0200
+Subject: [PATCH 2/3] bgpd: Show the real reason why the peer is failed
+
+If the peer was shutdown locally, it doesn't show up as admin. shutdown.
+Instead it's treated as "Waiting for peer OPEN".
+
+The same applies to when the peer reaches maximum-prefix count.
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_fsm.c | 7 ++++++-
+ bgpd/bgpd.h    | 1 +
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
+index 107e9fc892..c920c4e501 100644
+--- a/bgpd/bgp_fsm.c
++++ b/bgpd/bgp_fsm.c
+@@ -562,7 +562,8 @@ const char *const peer_down_str[] = {"",
+                              "Waiting for VRF to be initialized",
+                              "No AFI/SAFI activated for peer",
+                              "AS Set config change",
+-                             "Waiting for peer OPEN"};
++                             "Waiting for peer OPEN",
++                             "Reached received prefix count"};
+ static int bgp_graceful_restart_timer_expire(struct thread *thread)
+ {
+@@ -1431,6 +1432,10 @@ int bgp_start(struct peer *peer)
+                                "%s [FSM] Trying to start suppressed peer"
+                                " - this is never supposed to happen!",
+                                peer->host);
++              if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
++                      peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
++              else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
++                      peer->last_reset = PEER_DOWN_PFX_COUNT;
+               return -1;
+       }
+diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
+index 66d7633553..49e2a537fd 100644
+--- a/bgpd/bgpd.h
++++ b/bgpd/bgpd.h
+@@ -1213,6 +1213,7 @@ struct peer {
+ #define PEER_DOWN_NOAFI_ACTIVATED       30 /* No AFI/SAFI activated for peer */
+ #define PEER_DOWN_AS_SETS_REJECT        31 /* Reject routes with AS_SET */
+ #define PEER_DOWN_WAITING_OPEN          32 /* Waiting for open to succeed */
++#define PEER_DOWN_PFX_COUNT             33 /* Reached received prefix count */
+       /*
+        * Remember to update peer_down_str in bgp_fsm.c when you add
+        * a new value to the last_reset reason
+
+From 540528864d85a3b00e0794da769497ecfb8e0c27 Mon Sep 17 00:00:00 2001
+From: Donatas Abraitis <donatas.abraitis@gmail.com>
+Date: Tue, 11 Feb 2020 18:02:19 +0200
+Subject: [PATCH 3/3] bgpd: Format properly `show bgp summary failed`
+
+Before:
+```
+Neighbor        EstdCnt DropCnt ResetTime Reason
+192.168.0.1           0       0    never  Waiting for peer OPEN
+```
+
+After:
+```
+Neighbor        EstdCnt DropCnt ResetTime Reason
+192.168.0.1           0       0     never Waiting for peer OPEN
+```
+
+Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
+---
+ bgpd/bgp_vty.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 53d9732956..243822206c 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -8063,7 +8063,7 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
+                               : "received",
+                               code_str, subcode_str);
+               } else {
+-                      vty_out(vty, "  %s\n",
++                      vty_out(vty, " %s\n",
+                               peer_down_str[(int)peer->last_reset]);
+               }
+       }
+@@ -8119,7 +8119,7 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
+               if (len < max_neighbor_width)
+                       vty_out(vty, "%*s", max_neighbor_width - len,
+                               " ");
+-              vty_out(vty, "%7d %7d %8s", peer->established,
++              vty_out(vty, "%7d %7d %9s", peer->established,
+                       peer->dropped,
+                       peer_uptime(peer->uptime, timebuf,
+                                   BGP_UPTIME_LEN, 0, NULL));
diff --git a/net/frr/patches/022-7.3_backports.patch b/net/frr/patches/022-7.3_backports.patch
new file mode 100644 (file)
index 0000000..80c1139
--- /dev/null
@@ -0,0 +1,177 @@
+From 33a9ff0045adfa605832187e570dbe1374ceb22e Mon Sep 17 00:00:00 2001
+From: Mark Stapp <mjs@voltanet.io>
+Date: Tue, 28 Jan 2020 11:00:42 -0500
+Subject: [PATCH] zebra: add config to disable use of kernel nexthops
+
+Add a config that disables use of kernel-level nexthop ids.
+Currently, zebra always uses nexthop ids if the kernel supports
+them.
+
+Signed-off-by: Mark Stapp <mjs@voltanet.io>
+---
+ zebra/rt_netlink.c | 20 ++++++++++++++++----
+ zebra/zebra_nhg.c  | 18 ++++++++++++++++++
+ zebra/zebra_nhg.h  | 10 +++++++++-
+ zebra/zebra_vty.c  | 18 ++++++++++++++++++
+ 4 files changed, 61 insertions(+), 5 deletions(-)
+
+diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
+index 2abcd6ef2a..705536595b 100644
+--- a/zebra/rt_netlink.c
++++ b/zebra/rt_netlink.c
+@@ -75,6 +75,10 @@
+ static vlanid_t filter_vlan = 0;
++/* We capture whether the current kernel supports nexthop ids; by
++ * default, we'll use them if possible. There's also a configuration
++ * available to _disable_ use of kernel nexthops.
++ */
+ static bool supports_nh;
+ struct gw_family_t {
+@@ -86,6 +90,12 @@ struct gw_family_t {
+ static const char ipv4_ll_buf[16] = "169.254.0.1";
+ static struct in_addr ipv4_ll;
++/* Helper to control use of kernel-level nexthop ids */
++static bool kernel_nexthops_supported(void)
++{
++      return (supports_nh && zebra_nhg_kernel_nexthops_enabled());
++}
++
+ /*
+  * The ipv4_ll data structure is used for all 5549
+  * additions to the kernel.  Let's figure out the
+@@ -1628,7 +1638,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+                         RTA_PAYLOAD(rta));
+       }
+-      if (supports_nh) {
++      if (kernel_nexthops_supported()) {
+               /* Kernel supports nexthop objects */
+               addattr32(&req.n, sizeof(req), RTA_NH_ID,
+                         dplane_ctx_get_nhe_id(ctx));
+@@ -1943,7 +1953,7 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx)
+       size_t req_size = sizeof(req);
+       /* Nothing to do if the kernel doesn't support nexthop objects */
+-      if (!supports_nh)
++      if (!kernel_nexthops_supported())
+               return 0;
+       label_buf[0] = '\0';
+@@ -2504,8 +2514,10 @@ int netlink_nexthop_read(struct zebra_ns *zns)
+                * this kernel must support them.
+                */
+               supports_nh = true;
+-      else if (IS_ZEBRA_DEBUG_KERNEL)
+-              zlog_debug("Nexthop objects not supported on this kernel");
++
++      if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG)
++              zlog_debug("Nexthop objects %ssupported on this kernel",
++                         supports_nh ? "" : "not ");
+       return ret;
+ }
+diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
+index cbeb73aed4..62c478cf85 100644
+--- a/zebra/zebra_nhg.c
++++ b/zebra/zebra_nhg.c
+@@ -49,6 +49,9 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
+ /* id counter to keep in sync with kernel */
+ uint32_t id_counter;
++/*  */
++static bool g_nexthops_enabled = true;
++
+ static struct nhg_hash_entry *depends_find(const struct nexthop *nh,
+                                          afi_t afi);
+ static void depends_add(struct nhg_connected_tree_head *head,
+@@ -2004,3 +2007,18 @@ void zebra_nhg_sweep_table(struct hash *hash)
+ {
+       hash_iterate(hash, zebra_nhg_sweep_entry, NULL);
+ }
++
++/* Global control to disable use of kernel nexthops, if available. We can't
++ * force the kernel to support nexthop ids, of course, but we can disable
++ * zebra's use of them, for testing e.g. By default, if the kernel supports
++ * nexthop ids, zebra uses them.
++ */
++void zebra_nhg_enable_kernel_nexthops(bool set)
++{
++      g_nexthops_enabled = set;
++}
++
++bool zebra_nhg_kernel_nexthops_enabled(void)
++{
++      return g_nexthops_enabled;
++}
+diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
+index c2e173e094..4d001944b7 100644
+--- a/zebra/zebra_nhg.h
++++ b/zebra/zebra_nhg.h
+@@ -153,6 +153,13 @@ struct nhg_ctx {
+       enum nhg_ctx_status status;
+ };
++/* Global control to disable use of kernel nexthops, if available. We can't
++ * force the kernel to support nexthop ids, of course, but we can disable
++ * zebra's use of them, for testing e.g. By default, if the kernel supports
++ * nexthop ids, zebra uses them.
++ */
++void zebra_nhg_enable_kernel_nexthops(bool set);
++bool zebra_nhg_kernel_nexthops_enabled(void);
+ /**
+  * NHE abstracted tree functions.
+@@ -227,4 +234,5 @@ extern void zebra_nhg_sweep_table(struct hash *hash);
+ /* Nexthop resolution processing */
+ struct route_entry; /* Forward ref to avoid circular includes */
+ extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
+-#endif
++
++#endif        /* __ZEBRA_NHG_H__ */
+diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
+index 78001da170..866b38b47e 100644
+--- a/zebra/zebra_vty.c
++++ b/zebra/zebra_vty.c
+@@ -1410,6 +1410,19 @@ DEFPY (show_nexthop_group,
+       return CMD_SUCCESS;
+ }
++DEFPY_HIDDEN(nexthop_group_use_enable,
++           nexthop_group_use_enable_cmd,
++           "[no] zebra nexthop kernel enable",
++           NO_STR
++           ZEBRA_STR
++           "Nexthop configuration \n"
++           "Configure use of kernel nexthops\n"
++           "Enable kernel nexthops\n")
++{
++      zebra_nhg_enable_kernel_nexthops(!no);
++      return CMD_SUCCESS;
++}
++
+ DEFUN (no_ip_nht_default_route,
+        no_ip_nht_default_route_cmd,
+        "no ip nht resolve-via-default",
+@@ -3121,6 +3134,10 @@ static int config_write_protocol(struct vty *vty)
+       /* Include dataplane info */
+       dplane_config_write_helper(vty);
++      /* Include nexthop-group config */
++      if (!zebra_nhg_kernel_nexthops_enabled())
++              vty_out(vty, "no zebra nexthop kernel enable\n");
++
+       return 1;
+ }
+@@ -3492,6 +3509,7 @@ void zebra_vty_init(void)
+       install_element(CONFIG_NODE, &no_zebra_workqueue_timer_cmd);
+       install_element(CONFIG_NODE, &zebra_packet_process_cmd);
+       install_element(CONFIG_NODE, &no_zebra_packet_process_cmd);
++      install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd);
+       install_element(VIEW_NODE, &show_nexthop_group_cmd);
+       install_element(VIEW_NODE, &show_interface_nexthop_group_cmd);
diff --git a/net/frr/patches/023-7.3_backports.patch b/net/frr/patches/023-7.3_backports.patch
new file mode 100644 (file)
index 0000000..77d6f63
--- /dev/null
@@ -0,0 +1,166 @@
+From b49789f5d32429722542a1a7de4b371b43958a31 Mon Sep 17 00:00:00 2001
+From: Donald Sharp <sharpd@cumulusnetworks.com>
+Date: Wed, 18 Mar 2020 22:30:28 -0400
+Subject: [PATCH] yang: Partially revert code to restore functionality
+
+Partially revert code from commit:
+f22b9250853229c93617ffdad139a4762f5f7348
+
+since this broke passive-interface, network and offset-list
+commands in rip and ripng
+
+Fixes: #6001
+Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
+---
+ yang/frr-eigrpd.yang |  9 +++------
+ yang/frr-ripd.yang   | 27 ++++++++++-----------------
+ yang/frr-ripngd.yang | 23 +++++++----------------
+ 3 files changed, 20 insertions(+), 39 deletions(-)
+
+diff --git a/yang/frr-eigrpd.yang b/yang/frr-eigrpd.yang
+index 0c62954570..853d823880 100644
+--- a/yang/frr-eigrpd.yang
++++ b/yang/frr-eigrpd.yang
+@@ -23,11 +23,6 @@ module frr-eigrpd {
+   description
+     "This module defines a model for managing FRR eigrpd daemon.";
+-  revision 2019-09-09 {
+-    description
+-      "Changed interface references to use
+-      frr-interface:interface-ref typedef";
+-  }
+   revision 2019-06-19 {
+     description "Initial revision.";
+     reference
+@@ -99,7 +94,9 @@ module frr-eigrpd {
+       leaf-list passive-interface {
+         description "List of suppressed interfaces";
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+       }
+       leaf active-time {
+diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang
+index 94a9ebf3e1..07690793f0 100644
+--- a/yang/frr-ripd.yang
++++ b/yang/frr-ripd.yang
+@@ -24,11 +24,6 @@ module frr-ripd {
+   description
+     "This module defines a model for managing FRR ripd daemon.";
+-  revision 2019-09-09 {
+-    description
+-      "Changed interface references to use
+-      frr-interface:interface-ref typedef";
+-  }
+   revision 2017-12-06 {
+     description
+       "Initial revision.";
+@@ -118,7 +113,9 @@ module frr-ripd {
+           "Enable RIP on the specified IP network.";
+       }
+       leaf-list interface {
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+         description
+           "Enable RIP on the specified interface.";
+       }
+@@ -127,15 +124,7 @@ module frr-ripd {
+         description
+           "Offset-list to modify route metric.";
+         leaf interface {
+-          type union {
+-            type frr-interface:interface-ref;
+-            type enumeration {
+-              enum '*' {
+-                description
+-                  "Match all interfaces.";
+-              }
+-            }
+-          }
++          type string;
+           description
+             "Interface to match. Use '*' to match all interfaces.";
+         }
+@@ -179,14 +168,18 @@ module frr-ripd {
+       }
+       leaf-list passive-interface {
+         when "../passive-default = 'false'";
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+         description
+           "A list of interfaces where the sending of RIP packets
+            is disabled.";
+       }
+       leaf-list non-passive-interface {
+         when "../passive-default = 'true'";
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+         description
+           "A list of interfaces where the sending of RIP packets
+            is enabled.";
+diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang
+index 831758af86..b341b438a4 100644
+--- a/yang/frr-ripngd.yang
++++ b/yang/frr-ripngd.yang
+@@ -24,11 +24,6 @@ module frr-ripngd {
+   description
+     "This module defines a model for managing FRR ripngd daemon.";
+-  revision 2019-09-09 {
+-    description
+-      "Changed interface references to use
+-      frr-interface:interface-ref typedef";
+-  }
+   revision 2018-11-27 {
+     description
+       "Initial revision.";
+@@ -76,7 +71,9 @@ module frr-ripngd {
+           "Enable RIPng on the specified IPv6 network.";
+       }
+       leaf-list interface {
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+         description
+           "Enable RIPng on the specified interface.";
+       }
+@@ -85,15 +82,7 @@ module frr-ripngd {
+         description
+           "Offset-list to modify route metric.";
+         leaf interface {
+-          type union {
+-            type frr-interface:interface-ref;
+-            type enumeration {
+-              enum '*' {
+-                description
+-                  "Match all interfaces.";
+-              }
+-            }
+-          }
++          type string;
+           description
+             "Interface to match. Use '*' to match all interfaces.";
+         }
+@@ -129,7 +118,9 @@ module frr-ripngd {
+         }
+       }
+       leaf-list passive-interface {
+-        type frr-interface:interface-ref;
++        type string {
++          length "1..16";
++        }
+         description
+           "A list of interfaces where the sending of RIPng packets
+            is disabled.";
diff --git a/net/frr/patches/024-7.3_backports.patch b/net/frr/patches/024-7.3_backports.patch
new file mode 100644 (file)
index 0000000..25f3030
--- /dev/null
@@ -0,0 +1,174 @@
+From c9d7f8c0126b7b078b06f36096a2b3bbbc1f63b4 Mon Sep 17 00:00:00 2001
+From: Stephen Worley <sworley@cumulusnetworks.com>
+Date: Tue, 24 Mar 2020 17:10:08 -0400
+Subject: [PATCH 1/2] zebra: abstract route src determiniation into func
+
+Abstraction the route src determination from a nexthop in the
+netlink code into a function for both singlepath and mutlipath
+to call.
+
+Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
+(cherry picked from commit 762288f50f5fa29512864fcc7814be83e1b58ff4)
+---
+ zebra/rt_netlink.c | 81 ++++++++++++++++------------------------------
+ 1 file changed, 28 insertions(+), 53 deletions(-)
+
+diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
+index 705536595b..dcaf2155f0 100644
+--- a/zebra/rt_netlink.c
++++ b/zebra/rt_netlink.c
+@@ -1513,6 +1513,30 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
+                           0);
+ }
++static bool nexthop_set_src(const struct nexthop *nexthop, int family,
++                          union g_addr *src)
++{
++      if (family == AF_INET) {
++              if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) {
++                      src->ipv4 = nexthop->rmap_src.ipv4;
++                      return true;
++              } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) {
++                      src->ipv4 = nexthop->src.ipv4;
++                      return true;
++              }
++      } else if (family == AF_INET6) {
++              if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) {
++                      src->ipv6 = nexthop->rmap_src.ipv6;
++                      return true;
++              } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) {
++                      src->ipv6 = nexthop->src.ipv6;
++                      return true;
++              }
++      }
++
++      return false;
++}
++
+ /*
+  * Routing table change via netlink interface, using a dataplane context object
+  */
+@@ -1523,7 +1547,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+       unsigned int nexthop_num;
+       int family;
+       const char *routedesc;
+-      int setsrc = 0;
++      bool setsrc = false;
+       union g_addr src;
+       const struct prefix *p, *src_p;
+       uint32_t table_id;
+@@ -1689,32 +1713,8 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+                               if (setsrc)
+                                       continue;
+-                              if (family == AF_INET) {
+-                                      if (nexthop->rmap_src.ipv4.s_addr
+-                                          != 0) {
+-                                              src.ipv4 =
+-                                                      nexthop->rmap_src.ipv4;
+-                                              setsrc = 1;
+-                                      } else if (nexthop->src.ipv4.s_addr
+-                                                 != 0) {
+-                                              src.ipv4 =
+-                                                      nexthop->src.ipv4;
+-                                              setsrc = 1;
+-                                      }
+-                              } else if (family == AF_INET6) {
+-                                      if (!IN6_IS_ADDR_UNSPECIFIED(
+-                                                  &nexthop->rmap_src.ipv6)) {
+-                                              src.ipv6 =
+-                                                      nexthop->rmap_src.ipv6;
+-                                              setsrc = 1;
+-                                      } else if (
+-                                              !IN6_IS_ADDR_UNSPECIFIED(
+-                                                      &nexthop->src.ipv6)) {
+-                                              src.ipv6 =
+-                                                      nexthop->src.ipv6;
+-                                              setsrc = 1;
+-                                      }
+-                              }
++                              setsrc = nexthop_set_src(nexthop, family, &src);
++
+                               continue;
+                       }
+@@ -1757,32 +1757,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+                               if (setsrc)
+                                       continue;
+-                              if (family == AF_INET) {
+-                                      if (nexthop->rmap_src.ipv4.s_addr
+-                                          != 0) {
+-                                              src.ipv4 =
+-                                                      nexthop->rmap_src.ipv4;
+-                                              setsrc = 1;
+-                                      } else if (nexthop->src.ipv4.s_addr
+-                                                 != 0) {
+-                                              src.ipv4 =
+-                                                      nexthop->src.ipv4;
+-                                              setsrc = 1;
+-                                      }
+-                              } else if (family == AF_INET6) {
+-                                      if (!IN6_IS_ADDR_UNSPECIFIED(
+-                                                  &nexthop->rmap_src.ipv6)) {
+-                                              src.ipv6 =
+-                                                      nexthop->rmap_src.ipv6;
+-                                              setsrc = 1;
+-                                      } else if (
+-                                              !IN6_IS_ADDR_UNSPECIFIED(
+-                                                      &nexthop->src.ipv6)) {
+-                                              src.ipv6 =
+-                                                      nexthop->src.ipv6;
+-                                              setsrc = 1;
+-                                      }
+-                              }
++                              setsrc = nexthop_set_src(nexthop, family, &src);
+                               continue;
+                       }
+
+From e85c67d05decf340dcf5663a48c652719d04387f Mon Sep 17 00:00:00 2001
+From: Stephen Worley <sworley@cumulusnetworks.com>
+Date: Tue, 24 Mar 2020 17:32:21 -0400
+Subject: [PATCH 2/2] zebra: determine src when using nexthop objects
+
+Determine src based on nexthop data even when we are using
+kernel nexthop objects.
+
+Before, we were entirely skipping this step and just sending the
+nexthop ID, ignoring src determination.
+
+Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
+(cherry picked from commit d8bfd8dc9a899f841967257a6b5f30910fdc17c8)
+---
+ zebra/rt_netlink.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
+index dcaf2155f0..ee8ef6558f 100644
+--- a/zebra/rt_netlink.c
++++ b/zebra/rt_netlink.c
+@@ -1666,6 +1666,23 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+               /* Kernel supports nexthop objects */
+               addattr32(&req.n, sizeof(req), RTA_NH_ID,
+                         dplane_ctx_get_nhe_id(ctx));
++
++              /* Have to determine src still */
++              for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
++                      if (setsrc)
++                              break;
++
++                      setsrc = nexthop_set_src(nexthop, family, &src);
++              }
++
++              if (setsrc) {
++                      if (family == AF_INET)
++                              addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
++                                        &src.ipv4, bytelen);
++                      else if (family == AF_INET6)
++                              addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
++                                        &src.ipv6, bytelen);
++              }
+               goto skip;
+       }
diff --git a/net/frr/patches/025-7.3_backports.patch b/net/frr/patches/025-7.3_backports.patch
new file mode 100644 (file)
index 0000000..a79a480
--- /dev/null
@@ -0,0 +1,61 @@
+From 7cc9f2c7953d48cfb70b7e0c1b0c57e45ae68ce8 Mon Sep 17 00:00:00 2001
+From: Stephen Worley <sworley@cumulusnetworks.com>
+Date: Wed, 1 Apr 2020 15:31:40 -0400
+Subject: [PATCH] zebra: free unhashable (dup) NHEs via ID table cleanup
+
+Free unhashable (duplicate NHEs from the kernel) via ID table
+cleanup. Since the NHE ID hash table contains extra entries,
+that's the one we need to be calling zebra_nhg_hash_free()
+on, otherwise we will never free the unhashable NHEs.
+
+This was found via a memleak:
+
+==1478713== HEAP SUMMARY:
+==1478713==     in use at exit: 10,267 bytes in 46 blocks
+==1478713==   total heap usage: 76,810 allocs, 76,764 frees, 3,901,237 bytes allocated
+==1478713==
+==1478713== 208 (88 direct, 120 indirect) bytes in 1 blocks are definitely lost in loss record 35 of 41
+==1478713==    at 0x483BB1A: calloc (vg_replace_malloc.c:762)
+==1478713==    by 0x48E35E8: qcalloc (memory.c:110)
+==1478713==    by 0x451CCB: zebra_nhg_alloc (zebra_nhg.c:369)
+==1478713==    by 0x453DE3: zebra_nhg_copy (zebra_nhg.c:379)
+==1478713==    by 0x452670: nhg_ctx_process_new (zebra_nhg.c:1143)
+==1478713==    by 0x4523A8: nhg_ctx_process (zebra_nhg.c:1234)
+==1478713==    by 0x452A2D: zebra_nhg_kernel_find (zebra_nhg.c:1294)
+==1478713==    by 0x4326E0: netlink_nexthop_change (rt_netlink.c:2433)
+==1478713==    by 0x427320: netlink_parse_info (kernel_netlink.c:945)
+==1478713==    by 0x432DAD: netlink_nexthop_read (rt_netlink.c:2488)
+==1478713==    by 0x41B600: interface_list (if_netlink.c:1486)
+==1478713==    by 0x457275: zebra_ns_enable (zebra_ns.c:127)
+
+Repro with:
+ip next add id 1 blackhole
+ip next add id 2 blackhole
+
+valgrind /usr/lib/frr/zebra
+
+Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
+(cherry picked from commit c25c3ea57a3dcd3b36d86ba76dd34961bcb662f0)
+---
+ zebra/zebra_router.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
+index a891ffb76a..ea2b6752b3 100644
+--- a/zebra/zebra_router.c
++++ b/zebra/zebra_router.c
+@@ -223,10 +223,11 @@ void zebra_router_terminate(void)
+       zebra_vxlan_disable();
+       zebra_mlag_terminate();
+-      hash_clean(zrouter.nhgs, zebra_nhg_hash_free);
+-      hash_free(zrouter.nhgs);
+-      hash_clean(zrouter.nhgs_id, NULL);
++      /* Free NHE in ID table only since it has unhashable entries as well */
++      hash_clean(zrouter.nhgs_id, zebra_nhg_hash_free);
+       hash_free(zrouter.nhgs_id);
++      hash_clean(zrouter.nhgs, NULL);
++      hash_free(zrouter.nhgs);
+       hash_clean(zrouter.rules_hash, zebra_pbr_rules_free);
+       hash_free(zrouter.rules_hash);
diff --git a/net/frr/patches/026-7.3_backports.patch b/net/frr/patches/026-7.3_backports.patch
new file mode 100644 (file)
index 0000000..c4bbaf6
--- /dev/null
@@ -0,0 +1,83 @@
+From 21d5b651bbc4bcad3656a1804692c70e32797c69 Mon Sep 17 00:00:00 2001
+From: David Lamparter <equinox@diac24.net>
+Date: Thu, 2 Apr 2020 21:16:04 +0200
+Subject: [PATCH] bgpd, ospfd, ospf6d: long is not bool :(
+
+... Oops ...
+
+(for context, the defaults code originally didn't have a dedicated
+"bool" variant and just used long for bools...  I derp'd this when
+adding bool as a separate case :( )
+
+Reported-by: Donald Sharp <sharpd@cumulusnetworks.com>
+Signed-off-by: David Lamparter <equinox@diac24.net>
+(cherry picked from commit 4c1458b595282bff6a6e0b20767bb5cb655d0b4c)
+---
+ bgpd/bgp_vty.c     | 16 ++++++++--------
+ ospf6d/ospf6_top.c |  4 ++--
+ ospfd/ospf_vty.c   |  4 ++--
+ 3 files changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 243822206c..4f5ba285aa 100644
+--- a/bgpd/bgp_vty.c
++++ b/bgpd/bgp_vty.c
+@@ -73,20 +73,20 @@
+ #endif
+ FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY,
+       { .val_ulong = 10, .match_profile = "datacenter", },
+diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
+index 95537eb86e..ba3c1b8907 100644
+--- a/ospf6d/ospf6_top.c
++++ b/ospf6d/ospf6_top.c
+@@ -52,8 +52,8 @@
+ DEFINE_QOBJ_TYPE(ospf6)
+ FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ /* global ospf6d variable */
+diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
+index 152a7e83b7..92c9191801 100644
+--- a/ospfd/ospf_vty.c
++++ b/ospfd/ospf_vty.c
+@@ -54,8 +54,8 @@
+ #include "ospfd/ospf_bfd.h"
+ FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
+-      { .val_long = true, .match_profile = "datacenter", },
+-      { .val_long = false },
++      { .val_bool = true, .match_profile = "datacenter", },
++      { .val_bool = false },
+ )
+ static const char *const ospf_network_type_str[] = {
diff --git a/net/frr/patches/098-fix_mips_libyang.patch b/net/frr/patches/098-fix_mips_libyang.patch
new file mode 100644 (file)
index 0000000..3785cfe
--- /dev/null
@@ -0,0 +1,14 @@
+--- 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. */