From: Lucian Cristian Date: Fri, 1 May 2020 17:41:08 +0000 (+0300) Subject: frr: makefile cleanup and misc fixes X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=7d2c647f2dc0829aa84b08c8541b3112155e63c7;p=feed%2Fpackages.git frr: makefile cleanup and misc fixes 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 --- diff --git a/net/frr/Config.in b/net/frr/Config.in index 4154c33a61..7a7ea3fec5 100644 --- a/net/frr/Config.in +++ b/net/frr/Config.in @@ -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 diff --git a/net/frr/Makefile b/net/frr/Makefile index 1e6cdc8def..03d4b315dc 100644 --- a/net/frr/Makefile +++ b/net/frr/Makefile @@ -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 +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 index 0000000000..4e0a2beb44 --- /dev/null +++ b/net/frr/files/vtysh.conf @@ -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 index 0000000000..2409dd8463 --- /dev/null +++ b/net/frr/patches/010-add_yahng_filter.patch @@ -0,0 +1,385 @@ +From 2332428d3c80ac3d3b4e1c0bdba830b098ef440f Mon Sep 17 00:00:00 2001 +From: Rafael Zalamena +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 +--- + 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: ++ FRR Development List: "; ++ 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 index 0000000000..7026acb6d9 --- /dev/null +++ b/net/frr/patches/010-add_yang_routemap.patch @@ -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: ++ FRR Development List: "; ++ 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 index 0000000000..ac56137054 --- /dev/null +++ b/net/frr/patches/011-mod_yang_routemap_model.patch @@ -0,0 +1,5045 @@ +From 0c0e73045b1898610eef9309b9f5927254356710 Mon Sep 17 00:00:00 2001 +From: Rafael Zalamena +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 +--- +From a7282663eff6f036a427165b7fa73c75dccd47ff Mon Sep 17 00:00:00 2001 +From: Rafael Zalamena +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 +--- + 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 +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 +--- + 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 ++ ++#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 +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 +--- + 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 ", +- 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 []", +- 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 ", +- 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 []", +- 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 (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 (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 ++ ++#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 $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 $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 $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 []", ++ 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 $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 []", ++ 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 index 0000000000..e3c4ebaf38 --- /dev/null +++ b/net/frr/patches/012-add_yang_filter.patch @@ -0,0 +1,83 @@ +From bec0aa85b1f404ac9800c7524070fcf8582e82bc Mon Sep 17 00:00:00 2001 +From: Rafael Zalamena +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 +--- + 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 index 0000000000..dacb4562e7 --- /dev/null +++ b/net/frr/patches/013-backport_northbound.patch @@ -0,0 +1,147 @@ +From dc397e4c0adc13982fc5d83a1afc42178708f4a5 Mon Sep 17 00:00:00 2001 +From: Renato Westphal +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 +--- + 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 index 0000000000..879c380e84 --- /dev/null +++ b/net/frr/patches/014-backport_northbound.patch @@ -0,0 +1,330 @@ +From 97cd849362b45ecbcb20194b5771c5ce777de6bc Mon Sep 17 00:00:00 2001 +From: Renato Westphal +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 +--- + 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 index 0000000000..054c7856f9 --- /dev/null +++ b/net/frr/patches/020-7.3_backports.patch @@ -0,0 +1,33 @@ +From 0b7086494e9fe056f35560bcab53ff9eb0e68df4 Mon Sep 17 00:00:00 2001 +From: Mark Stapp +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 +--- + 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 index 0000000000..11875597fd --- /dev/null +++ b/net/frr/patches/021-7.3_backports.patch @@ -0,0 +1,218 @@ +From 2b5eda4c0539dcd30a06c975be36f879cc454e9f Mon Sep 17 00:00:00 2001 +From: Donald Sharp +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 index 0000000000..80c1139e10 --- /dev/null +++ b/net/frr/patches/022-7.3_backports.patch @@ -0,0 +1,177 @@ +From 33a9ff0045adfa605832187e570dbe1374ceb22e Mon Sep 17 00:00:00 2001 +From: Mark Stapp +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 +--- + 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 index 0000000000..77d6f63842 --- /dev/null +++ b/net/frr/patches/023-7.3_backports.patch @@ -0,0 +1,166 @@ +From b49789f5d32429722542a1a7de4b371b43958a31 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +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 +--- + 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 index 0000000000..25f3030030 --- /dev/null +++ b/net/frr/patches/024-7.3_backports.patch @@ -0,0 +1,174 @@ +From c9d7f8c0126b7b078b06f36096a2b3bbbc1f63b4 Mon Sep 17 00:00:00 2001 +From: Stephen Worley +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 +(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 +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 +(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 index 0000000000..a79a480a9f --- /dev/null +++ b/net/frr/patches/025-7.3_backports.patch @@ -0,0 +1,61 @@ +From 7cc9f2c7953d48cfb70b7e0c1b0c57e45ae68ce8 Mon Sep 17 00:00:00 2001 +From: Stephen Worley +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 +(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 index 0000000000..c4bbaf65de --- /dev/null +++ b/net/frr/patches/026-7.3_backports.patch @@ -0,0 +1,83 @@ +From 21d5b651bbc4bcad3656a1804692c70e32797c69 Mon Sep 17 00:00:00 2001 +From: David Lamparter +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 +Signed-off-by: David Lamparter +(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 index 0000000000..3785cfef3e --- /dev/null +++ b/net/frr/patches/098-fix_mips_libyang.patch @@ -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. */