diff --git a/dockers/docker-fpm-frr/base_image_files/prefix_list b/dockers/docker-fpm-frr/base_image_files/prefix_list new file mode 100755 index 000000000000..a3d09f140f27 --- /dev/null +++ b/dockers/docker-fpm-frr/base_image_files/prefix_list @@ -0,0 +1,178 @@ +#!/bin/bash + +# Function to display help message +display_help() { + echo "Usage: sudo prefix-list " + echo "" + echo "Commands:" + echo " add Add a prefix with prefix type and network." + echo " Requires: , ." + echo "" + echo " remove Remove a prefix with prefix type and network." + echo " Requires: , ." + echo "" + echo " status Display current prefix lists." + echo " No additional parameters required." + echo "" + echo "Arguments:" + echo " Type of prefix list. Allowed values: {$(printf "%s" "${supported_prefix_types[*]}" | tr ' ' '|')}." + echo " Network in CIDR format." + echo "" + echo "Options:" + echo " -h, --help Display this help message." + exit 0 +} + + +# Function to check if the user has root privileges +check_root_privileges() { + if [ "$EUID" -ne 0 ] ; then + echo "Root privileges are needed for this operation." >&2 + exit 1 + fi +} + +# Function to check if the device is supported device with type spine routers and subtype UpstreamLC +check_spine_router() { + type=$(sonic-cfggen -d -v DEVICE_METADATA.localhost.type) + sub_type=$(sonic-cfggen -d -v DEVICE_METADATA.localhost.sub_type) + + # only supported on spine routers and UpstreamLC + if [[ "$type" != "SpineRouter" || "$sub_type" != "UpstreamLC" ]]; then + echo "Operation is only supported on UpstreamLC of SpineRouter." >&2 + exit 1 + fi +} + +# Function to skip operation on chassis supervisor +skip_chassis_supervisor() { + if [ -f /etc/sonic/chassisdb.conf ]; then + echo "Skipping Operation on chassis supervisor" + exit 0 + fi +} + +# Function to validate the operation and prefix type parameters +validate_operation() { + local valid_operation=false + local valid_prefix_type=false + + for operation in "${prefix_list_operations[@]}"; do + if [[ "$1" == "$operation" ]]; then + valid_operation=true + break + fi + done + + if [ $valid_operation == false ]; then + echo "Invalid parameter $1, Operation not supported" >&2 + echo "" + display_help + exit 1 + fi + + # Check if the prefix type is supported or not if the operation is not status + if [ $1 != "status" ]; then + for prefix_type in "${supported_prefix_types[@]}"; do + if [[ "$2" == "$prefix_type" ]]; then + valid_prefix_type=true + break + fi + done + + if [ $valid_prefix_type == false ]; then + echo "Invalid parameter $2, Prefix type not supported" >&2 + echo "" + display_help + exit 1 + fi + fi +} + +# Function to handle prefix list operations for a specific ASIC +handle_prefix_list_asic() { + local asic=$1 + local operation=$2 + local PREFIX_TYPE=$3 + local network=$4 + local namespace_prefix='asic' + + if [ $operation == 'status' ] ; then + echo "BGP$asic: Current prefix lists:" + sonic-cfggen -d -v PREFIX_LIST -n $namespace_prefix$asic + else + if [ $operation == 'add' ]; then + local prefix_list_entry="{\"PREFIX_LIST\":{\"$PREFIX_TYPE|$network\":{}}}" + sonic-cfggen -a "$prefix_list_entry" -w -n $namespace_prefix$asic + logger -t $operation -p user.info "Added prefix list: $PREFIX_TYPE with network: $network" + echo "BGP$asic: Added prefix list: $PREFIX_TYPE with network: $network" + elif [ $operation == 'remove' ]; then + sonic-db-cli -n $namespace_prefix$asic CONFIG_DB DEL "PREFIX_LIST|$PREFIX_TYPE|$network" + logger -t $operation -p user.info "Removed prefix list: $PREFIX_TYPE with network: $network" + echo "BGP$asic: Removed prefix list: $PREFIX_TYPE with network: $network" + fi + fi +} + +# Function to handle prefix list operations for a single ASIC +handle_prefix_list_single() { + local operation=$1 + local PREFIX_TYPE=$2 + local network=$3 + + if [ $operation == 'status' ] ; then + echo "Current prefix lists:" + sonic-cfggen -d -v PREFIX_LIST + else + if [ $operation == 'add' ]; then + local prefix_list_entry="{\"PREFIX_LIST\":{\"$PREFIX_TYPE|$network\":{}}}" + sonic-cfggen -a "$prefix_list_entry" -w + logger -t $operation -p user.info "Added prefix list: $PREFIX_TYPE with network: $network" + echo "Added prefix list: $PREFIX_TYPE with network: $network" + elif [ $operation == 'remove' ]; then + sonic-db-cli CONFIG_DB DEL "PREFIX_LIST|$PREFIX_TYPE|$network" + logger -t $operation -p user.info "Removed prefix list: $PREFIX_TYPE with network: $network" + echo "Removed prefix list: $PREFIX_TYPE with network: $network" + fi + fi +} + +prefix_list_operations=("add" "remove" "status") +supported_prefix_types=("ANCHOR_PREFIX") +# Main script execution +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + display_help +fi + +check_root_privileges +check_spine_router +skip_chassis_supervisor + +validate_operation $1 $2 + +# Read SONiC immutable variables +[ -f /etc/sonic/sonic-environment ] && . /etc/sonic/sonic-environment + +PLATFORM=${PLATFORM:-`sonic-cfggen -H -v DEVICE_METADATA.localhost.platform`} + +# Parse the device specific asic conf file, if it exists +ASIC_CONF=/usr/share/sonic/device/$PLATFORM/asic.conf +[ -f $ASIC_CONF ] && . $ASIC_CONF + +if [[ ($NUM_ASIC -gt 1) ]]; then + asic=0 + while [ $asic -lt $NUM_ASIC ] + do + sub_role=`sonic-cfggen -d -v "DEVICE_METADATA['localhost']['sub_role']" -n asic$asic` + if [ $sub_role == 'FrontEnd' ]; then + handle_prefix_list_asic $asic $1 $2 $3 + fi + asic=$((asic+1)) + done +else + handle_prefix_list_single $1 $2 $3 +fi + +if [ $1 != 'status' ]; then + echo "Please execute 'sudo config save' to preserve prefix list after reboot or config reload" +fi diff --git a/dockers/docker-fpm-frr/frr/bgpd/radian/add_radian.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/radian/add_radian.conf.j2 new file mode 100644 index 000000000000..b17de1808427 --- /dev/null +++ b/dockers/docker-fpm-frr/frr/bgpd/radian/add_radian.conf.j2 @@ -0,0 +1,11 @@ +{{ data.ipv }} prefix-list ANCHOR_CONTRIBUTING_ROUTES permit {{ data.prefix }} ge 48 +{# #} +router bgp {{ data.bgp_asn }} +{% if data.ipv == 'ip' -%} + address-family ipv4 unicast +{% else -%} + address-family ipv6 unicast +{% endif %} + aggregate-address {{ data.prefix }} route-map TAG_ANCHOR_COMMUNITY + exit +exit \ No newline at end of file diff --git a/dockers/docker-fpm-frr/frr/bgpd/radian/del_radian.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/radian/del_radian.conf.j2 new file mode 100644 index 000000000000..77374be9ae9e --- /dev/null +++ b/dockers/docker-fpm-frr/frr/bgpd/radian/del_radian.conf.j2 @@ -0,0 +1,10 @@ +no {{ data.ipv }} prefix-list ANCHOR_CONTRIBUTING_ROUTES permit {{ data.prefix }} ge 48 +router bgp {{ data.bgp_asn }} +{% if data.ipv == 'ip' -%} + address-family ipv4 unicast +{% else %} + address-family ipv6 unicast +{% endif -%} + no aggregate-address {{ data.prefix }} route-map TAG_ANCHOR_COMMUNITY + exit +exit \ No newline at end of file diff --git a/dockers/docker-fpm-frr/frr/bgpd/templates/general/peer-group.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/templates/general/peer-group.conf.j2 index 2617cc94c2d2..69477cda2e3b 100644 --- a/dockers/docker-fpm-frr/frr/bgpd/templates/general/peer-group.conf.j2 +++ b/dockers/docker-fpm-frr/frr/bgpd/templates/general/peer-group.conf.j2 @@ -14,6 +14,9 @@ neighbor PEER_V4 soft-reconfiguration inbound neighbor PEER_V4 route-map FROM_BGP_PEER_V4 in neighbor PEER_V4 route-map TO_BGP_PEER_V4 out +{% if CONFIG_DB__DEVICE_METADATA['localhost']['type'] == 'SpineRouter' %} + table-map SELECTIVE_ROUTE_DOWNLOAD_V4 +{% endif %} exit-address-family address-family ipv6 {% if CONFIG_DB__DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} @@ -26,6 +29,9 @@ neighbor PEER_V6 soft-reconfiguration inbound neighbor PEER_V6 route-map FROM_BGP_PEER_V6 in neighbor PEER_V6 route-map TO_BGP_PEER_V6 out +{% if CONFIG_DB__DEVICE_METADATA['localhost']['type'] == 'SpineRouter' %} + table-map SELECTIVE_ROUTE_DOWNLOAD_V6 +{% endif %} exit-address-family ! ! end of template: bgpd/templates/general/peer-group.conf.j2 diff --git a/dockers/docker-fpm-frr/frr/bgpd/templates/general/policies.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/templates/general/policies.conf.j2 index 8db76a69f848..07cea2e22e96 100644 --- a/dockers/docker-fpm-frr/frr/bgpd/templates/general/policies.conf.j2 +++ b/dockers/docker-fpm-frr/frr/bgpd/templates/general/policies.conf.j2 @@ -73,7 +73,8 @@ route-map FROM_BGP_PEER_V6 permit 13 {% else %} set tag {{ constants.bgp.route_eligible_for_fallback_to_default_tag }} {% endif %} - set community {{ constants.bgp.internal_fallback_community }} additive {% endif %} + set community {{ constants.bgp.internal_fallback_community }} additive +{% endif %} {% endif %} ! {% endif %} @@ -97,5 +98,44 @@ route-map TO_BGP_PEER_V6 permit 100 ! route-map CHECK_IDF_ISOLATION permit 10 ! +! +! +{% if CONFIG_DB__DEVICE_METADATA and 'localhost' in CONFIG_DB__DEVICE_METADATA and 'type' in CONFIG_DB__DEVICE_METADATA['localhost'] and 'subtype' in CONFIG_DB__DEVICE_METADATA['localhost'] %} +{% if CONFIG_DB__DEVICE_METADATA['localhost']['type'] == 'SpineRouter' and CONFIG_DB__DEVICE_METADATA['localhost']['subtype'] == 'UpstreamLC' %} +bgp community-list standard ANCHOR_ROUTE_COMMUNITY permit {{ constants.bgp.anchor_route_community }} +bgp community-list standard LOCAL_ANCHOR_ROUTE_COMMUNITY permit {{ constants.bgp.local_anchor_route_community }} +bgp community-list standard ANCHOR_CONTRIBUTING_ROUTE_COMMUNITY permit {{ constants.bgp.anchor_contributing_route_community }} +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 permit 1000 +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 permit 1000 +! +route-map TAG_ANCHOR_COMMUNITY permit 10 + set community {{ constants.bgp.local_anchor_route_community }} {{ constants.bgp.anchor_route_community }} additive +! +route-map TO_BGP_PEER_V6 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community {{ constants.bgp.anchor_contributing_route_community }} additive + on-match next +! +route-map TO_BGP_PEER_V6 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! +route-map TO_BGP_PEER_V4 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community {{ constants.bgp.anchor_contributing_route_community }} additive + on-match next +! +route-map TO_BGP_PEER_V4 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! +{% endif %} +{% endif %} ! end of template: bgpd/templates/general/policies.conf.j2 ! diff --git a/dockers/docker-fpm-frr/frr/bgpd/templates/voq_chassis/policies.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/templates/voq_chassis/policies.conf.j2 index 961587c6ebb7..b1ad80c53a3d 100644 --- a/dockers/docker-fpm-frr/frr/bgpd/templates/voq_chassis/policies.conf.j2 +++ b/dockers/docker-fpm-frr/frr/bgpd/templates/voq_chassis/policies.conf.j2 @@ -1,6 +1,7 @@ ! ! template: bgpd/templates/voq_chassis/policies.conf.j2 ! +bgp community-list standard LOCAL_ANCHOR_ROUTE_COMMUNITY permit {{ constants.bgp.local_anchor_route_community }} bgp community-list standard DEVICE_INTERNAL_COMMUNITY permit {{ constants.bgp.internal_community }} bgp community-list standard DEVICE_INTERNAL_FALLBACK_COMMUNITY permit {{ constants.bgp.internal_fallback_community }} bgp community-list standard NO_EXPORT permit no-export @@ -31,6 +32,9 @@ route-map TO_VOQ_CHASSIS_V4_PEER permit 1 match ip address prefix-list PL_LoopbackV4 set community {{ constants.bgp.internal_community }} ! +route-map TO_VOQ_CHASSIS_V4_PEER deny 15 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! route-map TO_VOQ_CHASSIS_V4_PEER permit 100 ! route-map FROM_VOQ_CHASSIS_V6_PEER permit 1 @@ -63,6 +67,9 @@ route-map TO_VOQ_CHASSIS_V6_PEER permit 1 match ipv6 address prefix-list PL_LoopbackV6 set community {{ constants.bgp.internal_community }} ! +route-map TO_VOQ_CHASSIS_V6_PEER deny 15 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! route-map TO_VOQ_CHASSIS_V6_PEER permit 100 ! ! end of template: bgpd/templates/voq_chassis/policies.conf.j2 diff --git a/files/image_config/constants/constants.yml b/files/image_config/constants/constants.yml index 781055ba49b2..61c28d8be466 100644 --- a/files/image_config/constants/constants.yml +++ b/files/image_config/constants/constants.yml @@ -8,6 +8,9 @@ constants: internal_fallback_community: 22222:22222 sentinel_community: 12345:12346 internal_community_match_tag: 201 + local_anchor_route_community: 12345:555 + anchor_route_community: 12345:666 + anchor_contributing_route_community: 12345:777 route_do_not_send_appdb_tag: 202 route_eligible_for_fallback_to_default_tag: 203 families: diff --git a/rules/docker-fpm-frr.mk b/rules/docker-fpm-frr.mk index e81105caaec2..c914dbff1da6 100644 --- a/rules/docker-fpm-frr.mk +++ b/rules/docker-fpm-frr.mk @@ -41,6 +41,7 @@ $(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TSB:/usr/bin/TSB $(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TSC:/usr/bin/TSC $(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TS:/usr/bin/TS $(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += idf_isolation:/usr/bin/idf_isolation +$(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += prefix_list:/usr/bin/prefix_list SONIC_BOOKWORM_DOCKERS += $(DOCKER_FPM_FRR) SONIC_BOOKWORM_DBG_DOCKERS += $(DOCKER_FPM_FRR_DBG) diff --git a/src/sonic-bgpcfgd/bgpcfgd/main.py b/src/sonic-bgpcfgd/bgpcfgd/main.py index bc50f22161c7..a39080bcfdc0 100644 --- a/src/sonic-bgpcfgd/bgpcfgd/main.py +++ b/src/sonic-bgpcfgd/bgpcfgd/main.py @@ -24,6 +24,7 @@ from .managers_chassis_app_db import ChassisAppDbMgr from .managers_bfd import BfdMgr from .managers_srv6 import SRv6Mgr +from .managers_prefix_list import PrefixListMgr from .static_rt_timer import StaticRouteTimer from .runner import Runner, signal_handler from .template import TemplateFabric @@ -79,7 +80,9 @@ def do_work(): DeviceGlobalCfgMgr(common_objs, "CONFIG_DB", swsscommon.CFG_BGP_DEVICE_GLOBAL_TABLE_NAME), # SRv6 Manager SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_SIDS"), - SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_LOCATORS") + SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_LOCATORS"), + # Prefix List Manager + PrefixListMgr(common_objs, "CONFIG_DB", "PREFIX_LIST") ] if device_info.is_chassis(): diff --git a/src/sonic-bgpcfgd/bgpcfgd/managers_prefix_list.py b/src/sonic-bgpcfgd/bgpcfgd/managers_prefix_list.py new file mode 100644 index 000000000000..965dea9ff247 --- /dev/null +++ b/src/sonic-bgpcfgd/bgpcfgd/managers_prefix_list.py @@ -0,0 +1,119 @@ +from .manager import Manager +from .log import log_debug, log_warn, log_info +from swsscommon import swsscommon +import netaddr + +class PrefixListMgr(Manager): + """This class responds to changes in the PREFIX_LIST table""" + + def __init__(self, common_objs, db, table): + """ + Initialize the object + :param common_objs: common object dictionary + :param db: name of the db + :param table: name of the table in the db + """ + self.directory = common_objs['directory'] + self.cfg_mgr = common_objs['cfg_mgr'] + self.constants = common_objs['constants'] + self.templates = { + "add_radian": common_objs['tf'].from_file("bgpd/radian/add_radian.conf.j2"), + "del_radian": common_objs['tf'].from_file("bgpd/radian/del_radian.conf.j2"), + } + super(PrefixListMgr, self).__init__( + common_objs, + [], + db, + table, + ) + + def generate_prefix_list_config(self, data, add): + """ + Generate the prefix list configuration from the template + :param data: data from the PREFIX_LIST table + :return: rendered configuration + """ + cmd = "\n" + metadata = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"] + try: + bgp_asn = metadata["bgp_asn"] + localhost_type = metadata["type"] + subtype = metadata["subtype"] + except KeyError as e: + log_warn(f"PrefixListMgr:: Missing metadata key: {e}") + return False + + if data["prefix_list_name"] != "ANCHOR_PREFIX": + log_warn("PrefixListMgr:: Prefix list %s is not supported" % data["prefix_list_name"]) + return False + if localhost_type != "SpineRouter" or subtype != "UpstreamLC": + log_warn("PrefixListMgr:: Prefix list %s is only supported on UpstreamLC of SpineRouter" % data["prefix_list_name"]) + return False + + # Add the anchor prefix to the radian configuration + data["bgp_asn"] = bgp_asn + if add: + # add some way of getting this asn list from the database in the future + cmd += self.templates["add_radian"].render(data=data) + log_debug("PrefixListMgr:: Anchor prefix %s added to radian configuration" % data["prefix"]) + else: + cmd += self.templates["del_radian"].render(data=data) + log_debug("PrefixListMgr:: Anchor prefix %s removed from radian configuration" % data["prefix"]) + self.cfg_mgr.push(cmd) + return True + + + + def set_handler(self, key, data): + log_debug("PrefixListMgr:: set handler") + if '|' in key: + prefix_list_name, prefix_str = key.split('|', 1) + try: + prefix = netaddr.IPNetwork(str(prefix_str)) + except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError): + log_warn("PrefixListMgr:: Prefix '%s' format is wrong for prefix list '%s'" % (prefix_str, prefix_list_name)) + return True + data["prefix_list_name"] = prefix_list_name + data["prefix"] = str(prefix.cidr) + data["ipv"] = self.get_ip_type(prefix) + # Generate the prefix list configuration + if self.generate_prefix_list_config(data, add=True): + log_info("PrefixListMgr:: %s %s configuration generated" % (prefix_list_name, data["prefix"])) + + self.directory.put(self.db_name, self.table_name, key, data) + log_info("PrefixListMgr:: set %s" % key) + return True + + def del_handler(self, key): + log_debug("PrefixListMgr:: del handler") + if '|' in key: + prefix_list_name, prefix_str = key.split('|', 1) + try: + prefix = netaddr.IPNetwork(str(prefix_str)) + except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError): + log_warn("PrefixListMgr:: Prefix '%s' format is wrong for prefix list '%s'" % (prefix_str, prefix_list_name)) + return True + data = {} + data["prefix_list_name"] = prefix_list_name + data["prefix"] = str(prefix.cidr) + data["ipv"] = self.get_ip_type(prefix) + # remove the prefix list configuration + if self.generate_prefix_list_config(data, add=False): + log_info("PrefixListMgr:: %s %s configuration deleted" % (prefix_list_name, data["prefix"])) + self.directory.remove(self.db_name, self.table_name, key) + log_info("PrefixListMgr:: deleted %s" % key) + # Implement deletion logic if necessary + return True + + def get_ip_type(self, prefix: netaddr.IPNetwork): + """ + Determine the IP type (IPv4 or IPv6) of a prefix. + :param prefix: The prefix to check (e.g., "192.168.1.0/24" or "2001:db8::/32") + :return: "ip" if the prefix is an IPv4 address, "ipv6" if it is an IPv6 address, None if invalid + """ + if prefix.version == 4: + return "ip" + elif prefix.version == 6: + return "ipv6" + else: + return None \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt.json b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt.json index 0a96a1cacfd6..420577ef399e 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt.json +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt.json @@ -5,10 +5,13 @@ "allow_list": { "enabled": true, "drop_community": "12345:12345" - }, - "route_eligible_for_fallback_to_default_tag": "203", - "route_do_not_send_appdb_tag" : "202", - "internal_fallback_community": "1111:2222" + }, + "route_eligible_for_fallback_to_default_tag": "203", + "route_do_not_send_appdb_tag" : "202", + "internal_fallback_community": "1111:2222", + "local_anchor_route_community": "12345:555", + "anchor_route_community": "12345:666", + "anchor_contributing_route_community": "12345:777" } }, "allow_list_default_action": "permit", diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt_down.json b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt_down.json index 103e5a1fdcac..d8ef3a0c9071 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt_down.json +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_chassis_pkt_down.json @@ -5,10 +5,10 @@ "allow_list": { "enabled": true, "drop_community": "12345:12345" - }, + }, "route_eligible_for_fallback_to_default_tag": "203", "route_do_not_send_appdb_tag" : "202", - "internal_fallback_community": "1111:2222" + "internal_fallback_community": "1111:2222" } }, "allow_list_default_action": "permit", diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq.json b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq.json index a940effc8463..f4e0f9447f1f 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq.json +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq.json @@ -5,10 +5,13 @@ "allow_list": { "enabled": true, "drop_community": "12345:12345" - }, - "route_eligible_for_fallback_to_default_tag": "203", - "route_do_not_send_appdb_tag" : "202", - "internal_fallback_community": "1111:2222" + }, + "route_eligible_for_fallback_to_default_tag": "203", + "route_do_not_send_appdb_tag" : "202", + "internal_fallback_community": "1111:2222", + "local_anchor_route_community": "12345:555", + "anchor_route_community": "12345:666", + "anchor_contributing_route_community": "12345:777" } }, "allow_list_default_action": "permit", diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq_down.json b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq_down.json index 9671b7fcf25c..67aa23c92ff1 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq_down.json +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/param_all_voq_down.json @@ -5,10 +5,10 @@ "allow_list": { "enabled": true, "drop_community": "12345:12345" - }, + }, "route_eligible_for_fallback_to_default_tag": "203", "route_do_not_send_appdb_tag" : "202", - "internal_fallback_community": "1111:2222" + "internal_fallback_community": "1111:2222" } }, "allow_list_default_action": "permit", diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_chassis_pkt.conf b/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_chassis_pkt.conf index 7262a0c857d1..842b75ed47e7 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_chassis_pkt.conf +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_chassis_pkt.conf @@ -1,13 +1,17 @@ ! ! template: bgpd/templates/general/policies.conf.j2 ! +! ip prefix-list DEFAULT_IPV4 permit 0.0.0.0/0 ipv6 prefix-list DEFAULT_IPV6 permit ::/0 ! +! +! ! please don't remove. 65535 entries are default rules ! which works when allow_list is enabled, but new configuration ! is not applied ! +! route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535 set community 12345:12345 additive ! @@ -45,13 +49,17 @@ route-map FROM_BGP_PEER_V6 permit 12 ! route-map FROM_BGP_PEER_V6 permit 13 set tag 203 - set community 1111:2222 additive + set community 1111:2222 additive +! +! +! ! route-map FROM_BGP_PEER_V4 permit 100 ! route-map TO_BGP_PEER_V4 permit 100 call CHECK_IDF_ISOLATION ! +! route-map FROM_BGP_PEER_V6 permit 1 on-match next set ipv6 next-hop prefer-global @@ -63,5 +71,40 @@ route-map TO_BGP_PEER_V6 permit 100 ! route-map CHECK_IDF_ISOLATION permit 10 ! -! end of template: bgpd/templates/general/policies.conf.j2 ! +! +bgp community-list standard ANCHOR_ROUTE_COMMUNITY permit 12345:666 +bgp community-list standard LOCAL_ANCHOR_ROUTE_COMMUNITY permit 12345:555 +bgp community-list standard ANCHOR_CONTRIBUTING_ROUTE_COMMUNITY permit 12345:777 +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 permit 1000 +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 permit 1000 +! +route-map TAG_ANCHOR_COMMUNITY permit 10 + set community 12345:555 12345:666 additive +! +route-map TO_BGP_PEER_V6 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community 12345:777 additive + on-match next +! +route-map TO_BGP_PEER_V6 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! +route-map TO_BGP_PEER_V4 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community 12345:777 additive + on-match next +! +route-map TO_BGP_PEER_V4 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! +! end of template: bgpd/templates/general/policies.conf.j2 +! \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_voq.conf b/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_voq.conf index 9c6b1fc28422..1697cb0fd8ea 100644 --- a/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_voq.conf +++ b/src/sonic-bgpcfgd/tests/data/general/policies.conf/result_all_voq.conf @@ -1,13 +1,17 @@ ! ! template: bgpd/templates/general/policies.conf.j2 ! +! ip prefix-list DEFAULT_IPV4 permit 0.0.0.0/0 ipv6 prefix-list DEFAULT_IPV6 permit ::/0 ! +! +! ! please don't remove. 65535 entries are default rules ! which works when allow_list is enabled, but new configuration ! is not applied ! +! route-map ALLOW_LIST_DEPLOYMENT_ID_0_V4 permit 65535 set community 12345:12345 additive ! @@ -45,13 +49,17 @@ route-map FROM_BGP_PEER_V6 permit 12 ! route-map FROM_BGP_PEER_V6 permit 13 set tag 202 - set community 1111:2222 additive + set community 1111:2222 additive +! +! +! ! route-map FROM_BGP_PEER_V4 permit 100 ! route-map TO_BGP_PEER_V4 permit 100 call CHECK_IDF_ISOLATION ! +! route-map FROM_BGP_PEER_V6 permit 1 on-match next set ipv6 next-hop prefer-global @@ -63,5 +71,40 @@ route-map TO_BGP_PEER_V6 permit 100 ! route-map CHECK_IDF_ISOLATION permit 10 ! +! +! +bgp community-list standard ANCHOR_ROUTE_COMMUNITY permit 12345:666 +bgp community-list standard LOCAL_ANCHOR_ROUTE_COMMUNITY permit 12345:555 +bgp community-list standard ANCHOR_CONTRIBUTING_ROUTE_COMMUNITY permit 12345:777 +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V4 permit 1000 +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 deny 10 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! +route-map SELECTIVE_ROUTE_DOWNLOAD_V6 permit 1000 +! +route-map TAG_ANCHOR_COMMUNITY permit 10 + set community 12345:555 12345:666 additive +! +route-map TO_BGP_PEER_V6 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community 12345:777 additive + on-match next +! +route-map TO_BGP_PEER_V6 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! +route-map TO_BGP_PEER_V4 permit 30 + match ipv6 address prefix-list ANCHOR_CONTRIBUTING_ROUTES + set community 12345:777 additive + on-match next +! +route-map TO_BGP_PEER_V4 permit 40 + set comm-list LOCAL_ANCHOR_ROUTE_COMMUNITY delete +! ! end of template: bgpd/templates/general/policies.conf.j2 ! diff --git a/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.conf b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.conf new file mode 100644 index 000000000000..56158fe170a2 --- /dev/null +++ b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.conf @@ -0,0 +1,6 @@ +ipv6 prefix-list ANCHOR_CONTRIBUTING_ROUTES permit ffff::/64 ge 48 +router bgp 1234 +address-family ipv6 unicast + aggregate-address ffff::/64 route-map TAG_ANCHOR_COMMUNITY + exit +exit \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.json b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.json new file mode 100644 index 000000000000..d8e992aa8ae4 --- /dev/null +++ b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/add_radian.json @@ -0,0 +1,7 @@ +{ + "data": { + "ipv": "ipv6", + "prefix": "ffff::/64", + "bgp_asn": 1234 + } +} diff --git a/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.conf b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.conf new file mode 100644 index 000000000000..165976815187 --- /dev/null +++ b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.conf @@ -0,0 +1,6 @@ +no ipv6 prefix-list ANCHOR_CONTRIBUTING_ROUTES permit ffff::/64 ge 48 +router bgp 1234 + address-family ipv6 unicast +no aggregate-address ffff::/64 route-map TAG_ANCHOR_COMMUNITY + exit +exit \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.json b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.json new file mode 100644 index 000000000000..d8e992aa8ae4 --- /dev/null +++ b/src/sonic-bgpcfgd/tests/data/sonic-cfggen/radian/del_radian.json @@ -0,0 +1,7 @@ +{ + "data": { + "ipv": "ipv6", + "prefix": "ffff::/64", + "bgp_asn": 1234 + } +} diff --git a/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/param_base.json b/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/param_base.json index 1d2f80eed337..fd0aaf54580a 100644 --- a/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/param_base.json +++ b/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/param_base.json @@ -3,14 +3,15 @@ "localhost": { "type": "SpineRouter", "subtype": "DownstreamLC" - } + } }, "constants": { "bgp": { "internal_community": "12345:556", - "internal_community_match_tag": "101", + "internal_community_match_tag": "101", "route_eligible_for_fallback_to_default_tag": "203", - "internal_fallback_community": "1111:2222" + "internal_fallback_community": "1111:2222", + "local_anchor_route_community": "12345:555" } } } diff --git a/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/result_base.conf b/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/result_base.conf index 10fe3c832655..f63593b85d5c 100644 --- a/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/result_base.conf +++ b/src/sonic-bgpcfgd/tests/data/voq_chassis/policies.conf/result_base.conf @@ -1,6 +1,7 @@ ! ! template: bgpd/templates/voq_chassis/policies.conf.j2 ! +bgp community-list standard LOCAL_ANCHOR_ROUTE_COMMUNITY permit 12345:555 bgp community-list standard DEVICE_INTERNAL_COMMUNITY permit 12345:556 bgp community-list standard DEVICE_INTERNAL_FALLBACK_COMMUNITY permit 1111:2222 bgp community-list standard NO_EXPORT permit no-export @@ -26,6 +27,9 @@ route-map TO_VOQ_CHASSIS_V4_PEER permit 1 match ip address prefix-list PL_LoopbackV4 set community 12345:556 ! +route-map TO_VOQ_CHASSIS_V4_PEER deny 15 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! route-map TO_VOQ_CHASSIS_V4_PEER permit 100 ! route-map FROM_VOQ_CHASSIS_V6_PEER permit 1 @@ -53,7 +57,10 @@ route-map TO_VOQ_CHASSIS_V6_PEER permit 1 match ipv6 address prefix-list PL_LoopbackV6 set community 12345:556 ! +route-map TO_VOQ_CHASSIS_V6_PEER deny 15 + match community LOCAL_ANCHOR_ROUTE_COMMUNITY +! route-map TO_VOQ_CHASSIS_V6_PEER permit 100 ! ! end of template: bgpd/templates/voq_chassis/policies.conf.j2 -! +! \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/test_prefix_list.py b/src/sonic-bgpcfgd/tests/test_prefix_list.py new file mode 100644 index 000000000000..9f1ef6a48c99 --- /dev/null +++ b/src/sonic-bgpcfgd/tests/test_prefix_list.py @@ -0,0 +1,70 @@ +from unittest.mock import MagicMock, patch + +import os +from bgpcfgd.directory import Directory +from bgpcfgd.template import TemplateFabric +from . import swsscommon_test +from swsscommon import swsscommon + +from bgpcfgd.managers_prefix_list import PrefixListMgr + +TEMPLATE_PATH = os.path.abspath('../../dockers/docker-fpm-frr/frr') + +def constructor(): + cfg_mgr = MagicMock() + common_objs = { + 'directory': Directory(), + 'cfg_mgr': cfg_mgr, + 'tf': TemplateFabric(TEMPLATE_PATH), + 'constants': {}, + } + + m = PrefixListMgr(common_objs, "CONFIG_DB", "PREFIX_LIST") + m.directory.put("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, "localhost", {"bgp_asn": "65100", "type": "SpineRouter", "subtype": "UpstreamLC"}) + + return m + +def set_handler_test(manager, key, value): + res = manager.set_handler(key, value) + assert res, "Returns always True" + +def del_handler_test(manager, key): + res = manager.del_handler(key) + assert res, "Returns always True" + +# test if the ipv4 radian configs are set correctly +@patch('bgpcfgd.managers_prefix_list.log_debug') +def test_generate_prefix_list_config_ipv4(mocked_log_debug): + m = constructor() + set_handler_test(m, "ANCHOR_PREFIX|192.168.0.0/24", {}) + mocked_log_debug.assert_called_with("PrefixListMgr:: Anchor prefix 192.168.0.0/24 added to radian configuration") + +# test if the ipv6 radian configs are set correctly +@patch('bgpcfgd.managers_prefix_list.log_debug') +def test_generate_prefix_list_config_ipv6(mocked_log_debug): + m = constructor() + set_handler_test(m, "ANCHOR_PREFIX|fc02:100::/64", {}) + mocked_log_debug.assert_called_with("PrefixListMgr:: Anchor prefix fc02:100::/64 added to radian configuration") + +# test if invalid prefix is handled correctly +@patch('bgpcfgd.managers_prefix_list.log_warn') +def test_generate_prefix_list_config_invalid_prefix(mocked_log_warn): + m = constructor() + set_handler_test(m, "ANCHOR_PREFIX|invalid_prefix", {}) + mocked_log_warn.assert_called_with("PrefixListMgr:: Prefix 'invalid_prefix' format is wrong for prefix list 'ANCHOR_PREFIX'") + +# test if the ipv4 radian configs are deleted correctly +@patch('bgpcfgd.managers_prefix_list.log_debug') +def test_del_handler_ipv4(mocked_log_debug): + m = constructor() + set_handler_test(m, "ANCHOR_PREFIX|192.168.0.0/24", {}) + del_handler_test(m, "ANCHOR_PREFIX|192.168.0.0/24") + mocked_log_debug.assert_called_with("PrefixListMgr:: Anchor prefix 192.168.0.0/24 removed from radian configuration") + +# test if the ipv6 radian configs are deleted correctly +@patch('bgpcfgd.managers_prefix_list.log_debug') +def test_del_handler_ipv6(mocked_log_debug): + m = constructor() + set_handler_test(m, "ANCHOR_PREFIX|fc02:100::/64", {}) + del_handler_test(m, "ANCHOR_PREFIX|fc02:100::/64") + mocked_log_debug.assert_called_with("PrefixListMgr:: Anchor prefix fc02:100::/64 removed from radian configuration") \ No newline at end of file diff --git a/src/sonic-bgpcfgd/tests/test_sonic-cfggen.py b/src/sonic-bgpcfgd/tests/test_sonic-cfggen.py index 908c4d802e54..b259f41baf1a 100644 --- a/src/sonic-bgpcfgd/tests/test_sonic-cfggen.py +++ b/src/sonic-bgpcfgd/tests/test_sonic-cfggen.py @@ -206,3 +206,15 @@ def test_bgpd_main_conf_defaults_router_id(): "bgpd/bgpd.main.conf.j2", "bgpd.main.conf.j2/defaults_router_id.json", "bgpd.main.conf.j2/defaults_router_id.conf") + +def test_prefix_list_add_radian(): + run_test("Add radian configuration", + "bgpd/radian/add_radian.conf.j2", + "radian/add_radian.json", + "radian/add_radian.conf") + +def test_prefix_list_del_radian(): + run_test("Del radian configuration", + "bgpd/radian/del_radian.conf.j2", + "radian/del_radian.json", + "radian/del_radian.conf") \ No newline at end of file diff --git a/src/sonic-yang-models/doc/Configuration.md b/src/sonic-yang-models/doc/Configuration.md index 14a90731cd60..8196c7079b75 100644 --- a/src/sonic-yang-models/doc/Configuration.md +++ b/src/sonic-yang-models/doc/Configuration.md @@ -94,6 +94,7 @@ * [Static DNS](#static-dns) * [ASIC_SENSORS](#asic_sensors) * [SRv6](#srv6) + * [Prefix List](#prefix-list) * [For Developers](#for-developers) * [Generating Application Config by Jinja2 Template](#generating-application-config-by-jinja2-template) * [Incremental Configuration by Subscribing to ConfigDB](#incremental-configuration-by-subscribing-to-configdb) @@ -2913,6 +2914,18 @@ An example is as follows: } ``` +### Prefix List +Prefix list table stores a list of prefixes with type and prefix separated by `|`. The specific configuration for the prefix type are then rendered by the PrefixListMgr. Currently ANCHOR_PREFIX is supported to add RADIAN configuration. + +An example is as follows: +```json +{ + "PREFIX_LIST": { + "ANCHOR_PREFIX|fc00::/48": {} + } +} +``` + ### FIPS The FIPS table introduces FIPS configuration. diff --git a/src/sonic-yang-models/setup.py b/src/sonic-yang-models/setup.py index 02668030182c..ac670d49c1fe 100644 --- a/src/sonic-yang-models/setup.py +++ b/src/sonic-yang-models/setup.py @@ -205,6 +205,7 @@ def run(self): './yang-models/sonic-system-port.yang', './yang-models/sonic-macsec.yang', './yang-models/sonic-bgp-sentinel.yang', + './yang-models/sonic-bgp-prefix-list.yang', './yang-models/sonic-asic-sensors.yang', './yang-models/sonic-bmp.yang', './yang-models/sonic-xcvrd-log.yang', diff --git a/src/sonic-yang-models/tests/files/sample_config_db.json b/src/sonic-yang-models/tests/files/sample_config_db.json index 6a4ca851e76b..710151ef6662 100644 --- a/src/sonic-yang-models/tests/files/sample_config_db.json +++ b/src/sonic-yang-models/tests/files/sample_config_db.json @@ -2852,6 +2852,10 @@ "action": "uN", "decap_dscp_mode": "pipe" } + }, + "PREFIX_LIST": { + "ANCHOR_PREFIX|10.0.0.0/8" : {}, + "ANCHOR_PREFIX|FC00::/48" : {} } }, "SAMPLE_CONFIG_DB_UNKNOWN": { diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/bgp_prefix_list.json b/src/sonic-yang-models/tests/yang_model_tests/tests/bgp_prefix_list.json new file mode 100644 index 000000000000..85d3f7956a2b --- /dev/null +++ b/src/sonic-yang-models/tests/yang_model_tests/tests/bgp_prefix_list.json @@ -0,0 +1,13 @@ +{ + "BGP_PREFIX_LIST_WITH_VALID_IPV4_PREFIX": { + "desc": "Load BGP prefix list table with a valid IPv4 prefix" + }, + "BGP_PREFIX_LIST_WITH_VALID_IPV6_PREFIX": { + "desc": "Load BGP prefix list table with a valid IPv6 prefix" + }, + "BGP_PREFIX_LIST_WITH_INVALID_PREFIX": { + "desc": "Load BGP prefix list table with an invalid prefix", + "eStrKey": "InvalidValue", + "eStr": ["prefix"] + } +} diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests_config/bgp_prefix_list.json b/src/sonic-yang-models/tests/yang_model_tests/tests_config/bgp_prefix_list.json new file mode 100644 index 000000000000..5191ba7e8511 --- /dev/null +++ b/src/sonic-yang-models/tests/yang_model_tests/tests_config/bgp_prefix_list.json @@ -0,0 +1,40 @@ +{ + "BGP_PREFIX_LIST_WITH_VALID_IPV4_PREFIX": { + "sonic-bgp-prefix-list:sonic-bgp-prefix-list": { + "sonic-bgp-prefix-list:PREFIX_LIST": { + "PREFIX_LIST_LIST": [ + { + "prefix_type": "ANCHOR_PREFIX", + "ip-prefix": "10.0.0.0/8" + } + ] + } + } + }, + "BGP_PREFIX_LIST_WITH_VALID_IPV6_PREFIX": { + "sonic-bgp-prefix-list:sonic-bgp-prefix-list": { + "sonic-bgp-prefix-list:PREFIX_LIST": { + "PREFIX_LIST_LIST": [ + { + "prefix_type": "ANCHOR_PREFIX", + "ip-prefix": "fc00::/48" + } + ] + } + } + }, + "BGP_PREFIX_LIST_WITH_INVALID_PREFIX": { + "sonic-bgp-prefix-list:sonic-bgp-prefix-list": { + "sonic-bgp-prefix-list:PREFIX_LIST": { + "PREFIX_LIST_LIST": [ + { + "prefix_type": "ANCHOR_PREFIX", + "ip-prefix": "invalid_prefix" + } + ] + } + }, + "eStrKey": "InvalidValue", + "eStr": ["prefix"] + } +} \ No newline at end of file diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-prefix-list.yang b/src/sonic-yang-models/yang-models/sonic-bgp-prefix-list.yang new file mode 100644 index 000000000000..78441450addc --- /dev/null +++ b/src/sonic-yang-models/yang-models/sonic-bgp-prefix-list.yang @@ -0,0 +1,67 @@ + +module sonic-bgp-prefix-list { + + yang-version 1.1; + + namespace "http://github.com/sonic-net/sonic-bgp-prefix-list"; + prefix bgppl; + + import sonic-types { + prefix stypes; + } + + import sonic-extension { + prefix ext; + } + + description "SONIC Device-specfifc BGP prefix lists data"; + + revision 2025-02-17 { + description "Updated description and leafs for PREFIX_LIST_LIST"; + } + + revision 2025-02-05 { + description "Initial revision."; + } + + container sonic-bgp-prefix-list { + + container PREFIX_LIST { + + description "PREFIX_LIST container consumed in BGP"; + + list PREFIX_LIST_LIST { + + description "PREFIX_LIST part of config_db.json with prefix_type and ip-prefix"; + + key "prefix_type ip-prefix"; + + leaf prefix_type { + type string; + description "Prefix type"; + } + + leaf ip-prefix { + type union { + type stypes:sonic-ip4-prefix; + type stypes:sonic-ip6-prefix; + } + } + + leaf family { + + /* family leaf needed for backward compatibility + Both ip4 and ip6 address are string in IETF RFC 6021, + so must statement can check based on : or ., family + should be IPv4 or IPv6 according. + */ + + must "(contains(../ip-prefix, ':') and current()='IPv6') or + (contains(../ip-prefix, '.') and current()='IPv4')"; + type stypes:ip-family; + } + } + } + /* end of PREFIX_LIST */ + } +}