Skip to content

Commit 9ce0f61

Browse files
committed
Added Anchor prefix support
Following files were altered or added: 1. Cli support to add a prefix - dockers/docker-fpm-fr/base_image_files/prefix_list - rules/docker-fpm-frr.mk 2. Manager to add appropriate prefix using jinja templates Signed-off-by: Mukul Chodhary <70460358+Muckthebuck@users.noreply.github.com>
1 parent 360d425 commit 9ce0f61

File tree

6 files changed

+304
-1
lines changed

6 files changed

+304
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/bin/bash
2+
3+
# Function to display help message
4+
display_help() {
5+
echo "Usage: sudo prefix-list <command> <PREFIX_TYPE> <NETWORK>"
6+
echo ""
7+
echo "Commands:"
8+
echo " add Add a prefix with prefix type and network."
9+
echo " Requires: <PREFIX_TYPE>, <NETWORK>."
10+
echo ""
11+
echo " remove Remove a prefix with prefix type and network."
12+
echo " Requires: <PREFIX_TYPE>, <NETWORK>."
13+
echo ""
14+
echo " status Display current prefix lists."
15+
echo " No additional parameters required."
16+
echo ""
17+
echo "Arguments:"
18+
echo " <PREFIX_TYPE> Type of prefix list. Allowed values: {$(IFS='|'; echo "${supported_prefix_types[*]}")}."
19+
echo " <NETWORK> Network in CIDR format."
20+
echo ""
21+
echo "Options:"
22+
echo " -h, --help Display this help message."
23+
exit 0
24+
}
25+
26+
27+
# Function to check if the user has root privileges
28+
check_root_privileges() {
29+
if [ "$EUID" -ne 0 ] ; then
30+
echo "Root privileges are needed for this operation"
31+
exit 1
32+
fi
33+
}
34+
35+
# Function to check if the command is supported on spine routers
36+
check_spine_router() {
37+
if [[ "$(sonic-cfggen -d -v DEVICE_METADATA.localhost.type)" != *"SpineRouter"* ]] ; then
38+
echo "Operation is not supported on this platform"
39+
exit 1
40+
fi
41+
}
42+
43+
# Function to skip operation on chassis supervisor
44+
skip_chassis_supervisor() {
45+
if [ -f /etc/sonic/chassisdb.conf ]; then
46+
echo "Skipping Operation on chassis supervisor"
47+
exit 0
48+
fi
49+
}
50+
51+
# Function to validate the operation and prefix type parameters
52+
validate_operation() {
53+
local valid_operation=false
54+
local valid_prefix_type=false
55+
56+
for operation in "${prefix_list_operations[@]}"; do
57+
if [[ "$1" == "$operation" ]]; then
58+
valid_operation=true
59+
break
60+
fi
61+
done
62+
63+
if [ $valid_operation == false ]; then
64+
echo "Invalid parameter $1, Operation not supported"
65+
echo ""
66+
display_help
67+
exit 0
68+
fi
69+
70+
# Check if the prefix type is supported or not if the operation is not status
71+
if [ $1 != "status" ]; then
72+
for prefix_type in "${supported_prefix_types[@]}"; do
73+
if [[ "$2" == "$prefix_type" ]]; then
74+
valid_prefix_type=true
75+
break
76+
fi
77+
done
78+
79+
if [ $valid_prefix_type == false ]; then
80+
echo "Invalid parameter $2, Prefix type not supported"
81+
echo ""
82+
display_help
83+
exit 0
84+
fi
85+
fi
86+
}
87+
88+
# Function to handle prefix list operations for a specific ASIC
89+
handle_prefix_list_asic() {
90+
local asic=$1
91+
local operation=$2
92+
local PREFIX_TYPE=$3
93+
local network=$4
94+
local namespace_prefix='asic'
95+
96+
if [ $operation == 'status' ] ; then
97+
echo "BGP$asic: Current prefix lists:"
98+
sonic-cfggen -d -v PREFIX_LIST -n $namespace_prefix$asic
99+
else
100+
if [ $operation == 'add' ]; then
101+
local prefix_list_entry="{\"PREFIX_LIST\":{\"$PREFIX_TYPE|$network\":{}}}"
102+
sonic-cfggen -a "$prefix_list_entry" -w -n $namespace_prefix$asic
103+
logger -t $operation -p user.info "Added prefix list: $PREFIX_TYPE with network: $network"
104+
echo "BGP$asic: Added prefix list: $PREFIX_TYPE with network: $network"
105+
elif [ $operation == 'remove' ]; then
106+
sonic-db-cli -n $namespace_prefix$asic CONFIG_DB DEL "PREFIX_LIST|$PREFIX_TYPE|$network"
107+
logger -t $operation -p user.info "Removed prefix list: $PREFIX_TYPE with network: $network"
108+
echo "BGP$asic: Removed prefix list: $PREFIX_TYPE with network: $network"
109+
fi
110+
fi
111+
}
112+
113+
# Function to handle prefix list operations for a single ASIC
114+
handle_prefix_list_single() {
115+
local operation=$1
116+
local PREFIX_TYPE=$2
117+
local network=$3
118+
119+
if [ $operation == 'status' ] ; then
120+
echo "Current prefix lists:"
121+
sonic-cfggen -d -v PREFIX_LIST
122+
else
123+
if [ $operation == 'add' ]; then
124+
local prefix_list_entry="{\"PREFIX_LIST\":{\"$PREFIX_TYPE|$network\":{}}}"
125+
sonic-cfggen -a "$prefix_list_entry" -w
126+
logger -t $operation -p user.info "Added prefix list: $PREFIX_TYPE with network: $network"
127+
echo "Added prefix list: $PREFIX_TYPE with network: $network"
128+
elif [ $operation == 'remove' ]; then
129+
sonic-db-cli CONFIG_DB DEL "PREFIX_LIST|$PREFIX_TYPE|$network"
130+
logger -t $operation -p user.info "Removed prefix list: $PREFIX_TYPE with network: $network"
131+
echo "Removed prefix list: $PREFIX_TYPE with network: $network"
132+
fi
133+
fi
134+
}
135+
136+
prefix_list_operations=("add" "remove" "status")
137+
supported_prefix_types=("ANCHOR_PREFIX")
138+
# Main script execution
139+
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
140+
display_help
141+
fi
142+
143+
check_root_privileges
144+
check_spine_router
145+
skip_chassis_supervisor
146+
147+
validate_operation $1 $2
148+
149+
# Read SONiC immutable variables
150+
[ -f /etc/sonic/sonic-environment ] && . /etc/sonic/sonic-environment
151+
152+
PLATFORM=${PLATFORM:-`sonic-cfggen -H -v DEVICE_METADATA.localhost.platform`}
153+
154+
# Parse the device specific asic conf file, if it exists
155+
ASIC_CONF=/usr/share/sonic/device/$PLATFORM/asic.conf
156+
[ -f $ASIC_CONF ] && . $ASIC_CONF
157+
158+
if [[ ($NUM_ASIC -gt 1) ]]; then
159+
asic=0
160+
while [ $asic -lt $NUM_ASIC ]
161+
do
162+
sub_role=`sonic-cfggen -d -v "DEVICE_METADATA['localhost']['sub_role']" -n asic$asic`
163+
if [ $sub_role == 'FrontEnd' ] ; then
164+
handle_prefix_list_asic $asic $1 $2 $3
165+
fi
166+
asic=$((asic+1))
167+
done
168+
else
169+
handle_prefix_list_single $1 $2 $3
170+
fi
171+
172+
if [ $1 != 'status' ]; then
173+
echo "Please execute 'sudo config save' to preserve prefix list after reboot or config reload"
174+
fi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{{ data.ipv }} prefix-list ANCHOR_CONTRIBUTING_ROUTES permit {{ data.prefix }} ge 48
2+
{# #}
3+
router bgp {{ data.bgp_asn }}
4+
{% if data.ipv == 'ip' -%}
5+
address-family ipv4 unicast
6+
{% else -%}
7+
address-family ipv6 unicast
8+
{% endif %}
9+
aggregate-address {{ data.prefix }} route-map TAG_ANCHOR_COMMUNITY
10+
exit
11+
exit
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
no {{ data.ipv }} prefix-list ANCHOR_CONTRIBUTING_ROUTES permit {{ data.prefix }} ge 48
2+
router bgp {{ data.bgp_asn }}
3+
{% if data.ipv == 'ip' -%}
4+
address-family ipv4 unicast
5+
{% else %}
6+
address-family ipv6 unicast
7+
{% endif -%}
8+
no aggregate-address {{ data.prefix }} route-map TAG_ANCHOR_COMMUNITY
9+
exit
10+
exit

rules/docker-fpm-frr.mk

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ $(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TSB:/usr/bin/TSB
4141
$(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TSC:/usr/bin/TSC
4242
$(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += TS:/usr/bin/TS
4343
$(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += idf_isolation:/usr/bin/idf_isolation
44+
$(DOCKER_FPM_FRR)_BASE_IMAGE_FILES += prefix_list:/usr/bin/prefix_list
4445

4546
SONIC_BOOKWORM_DOCKERS += $(DOCKER_FPM_FRR)
4647
SONIC_BOOKWORM_DBG_DOCKERS += $(DOCKER_FPM_FRR_DBG)

src/sonic-bgpcfgd/bgpcfgd/main.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .managers_chassis_app_db import ChassisAppDbMgr
2525
from .managers_bfd import BfdMgr
2626
from .managers_srv6 import SRv6Mgr
27+
from .managers_prefix_list import PrefixListMgr
2728
from .static_rt_timer import StaticRouteTimer
2829
from .runner import Runner, signal_handler
2930
from .template import TemplateFabric
@@ -79,7 +80,9 @@ def do_work():
7980
DeviceGlobalCfgMgr(common_objs, "CONFIG_DB", swsscommon.CFG_BGP_DEVICE_GLOBAL_TABLE_NAME),
8081
# SRv6 Manager
8182
SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_SIDS"),
82-
SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_LOCATORS")
83+
SRv6Mgr(common_objs, "CONFIG_DB", "SRV6_MY_LOCATORS"),
84+
# Prefix List Manager
85+
PrefixListMgr(common_objs, "CONFIG_DB", "PREFIX_LIST")
8386
]
8487

8588
if device_info.is_chassis():
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from .manager import Manager
2+
from .log import log_debug, log_warn, log_info
3+
from swsscommon import swsscommon
4+
import netaddr
5+
6+
class PrefixListMgr(Manager):
7+
"""This class responds to changes in the PREFIX_LIST table"""
8+
9+
def __init__(self, common_objs, db, table):
10+
"""
11+
Initialize the object
12+
:param common_objs: common object dictionary
13+
:param db: name of the db
14+
:param table: name of the table in the db
15+
"""
16+
self.directory = common_objs['directory']
17+
self.cfg_mgr = common_objs['cfg_mgr']
18+
self.constants = common_objs['constants']
19+
self.templates = {
20+
"add_radian": common_objs['tf'].from_file("bgpd/radian/add_radian.conf.j2"),
21+
"del_radian": common_objs['tf'].from_file("bgpd/radian/del_radian.conf.j2"),
22+
}
23+
super(PrefixListMgr, self).__init__(
24+
common_objs,
25+
[],
26+
db,
27+
table,
28+
)
29+
30+
def generate_prefix_list_config(self, data, add):
31+
"""
32+
Generate the prefix list configuration from the template
33+
:param data: data from the PREFIX_LIST table
34+
:return: rendered configuration
35+
"""
36+
cmd = "\n"
37+
bgp_asn = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["bgp_asn"]
38+
localhost_type = self.directory.get_slot("CONFIG_DB", swsscommon.CFG_DEVICE_METADATA_TABLE_NAME)["localhost"]["type"]
39+
if data["prefix_list_name"] == "ANCHOR_PREFIX" and localhost_type == "SpineRouter":
40+
# Add the anchor prefix to the radian configuration`
41+
data["bgp_asn"] = bgp_asn
42+
if add:
43+
# add some way of getting this asn list from the database in the future
44+
cmd += self.templates["add_radian"].render(data=data)
45+
log_debug("PrefixListMgr:: Anchor prefix %s added to radian configuration" % data["prefix"])
46+
else:
47+
cmd += self.templates["del_radian"].render(data=data)
48+
log_debug("PrefixListMgr:: Anchor prefix %s removed from radian configuration" % data["prefix"])
49+
self.cfg_mgr.push(cmd)
50+
51+
52+
53+
def set_handler(self, key, data):
54+
log_debug("PrefixListMgr:: set handler")
55+
if '|' in key:
56+
prefix_list_name, prefix_str = key.split('|', 1)
57+
try:
58+
prefix = netaddr.IPNetwork(str(prefix_str))
59+
except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError):
60+
log_warn("PrefixListMgr:: Prefix '%s' format is wrong for prefix list '%s'" % (prefix_str, prefix_list_name))
61+
return True
62+
data["prefix_list_name"] = prefix_list_name
63+
data["prefix"] = str(prefix.cidr)
64+
data["ipv"] = self.get_ip_type(prefix)
65+
# Generate the prefix list configuration
66+
self.generate_prefix_list_config(data, add=True)
67+
log_info("PrefixListMgr:: %s %s configuration generated" % (prefix_list_name, data["prefix"]))
68+
69+
self.directory.put(self.db_name, self.table_name, key, data)
70+
log_info("PrefixListMgr:: set %s" % key)
71+
return True
72+
73+
def del_handler(self, key):
74+
log_debug("PrefixListMgr:: del handler")
75+
if '|' in key:
76+
prefix_list_name, prefix_str = key.split('|', 1)
77+
try:
78+
prefix = netaddr.IPNetwork(str(prefix_str))
79+
except (netaddr.NotRegisteredError, netaddr.AddrFormatError, netaddr.AddrConversionError):
80+
log_warn("PrefixListMgr:: Prefix '%s' format is wrong for prefix list '%s'" % (prefix_str, prefix_list_name))
81+
return True
82+
data = {}
83+
data["prefix_list_name"] = prefix_list_name
84+
data["prefix"] = str(prefix.cidr)
85+
data["ipv"] = self.get_ip_type(prefix)
86+
self.generate_prefix_list_config(data, add=False)
87+
log_info("PrefixListMgr:: %s %s configuration deleted" % (prefix_list_name, data["prefix"]))
88+
self.directory.remove(self.db_name, self.table_name, key)
89+
log_info("PrefixListMgr:: deleted %s" % key)
90+
# Implement deletion logic if necessary
91+
return True
92+
93+
def get_ip_type(self, prefix: netaddr.IPNetwork):
94+
"""
95+
Determine the IP type (IPv4 or IPv6) of a prefix.
96+
:param prefix: The prefix to check (e.g., "192.168.1.0/24" or "2001:db8::/32")
97+
:return: "ip" if the prefix is an IPv4 address, "ipv6" if it is an IPv6 address, None if invalid
98+
"""
99+
if prefix.version == 4:
100+
return "ip"
101+
elif prefix.version == 6:
102+
return "ipv6"
103+
else:
104+
return None

0 commit comments

Comments
 (0)