Coverage for src/birdplan/bird_config/sections/protocols/bgp/__init__.py: 97%
430 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-25 07:38 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-25 07:38 +0000
1#
2# SPDX-License-Identifier: GPL-3.0-or-later
3#
4# Copyright (c) 2019-2025, AllWorldIT
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
19"""BIRD BGP protocol configuration."""
21from .....exceptions import BirdPlanError
22from ....globals import BirdConfigGlobals
23from ...bird_attributes import SectionBirdAttributes
24from ...constants import SectionConstants
25from ...functions import SectionFunctions
26from ...tables import SectionTables
27from ..base import SectionProtocolBase
28from ..direct import ProtocolDirect
29from ..pipe import ProtocolPipe, ProtocolPipeFilterType
30from ..rpki import ProtocolRPKI, RPKISource
31from .bgp_attributes import BGPAttributes, BGPPeertypeConstraints, BGPRoutePolicyAccept, BGPRoutePolicyImport
32from .bgp_functions import BGPFunctions
33from .bgp_types import BGPPeerConfig
34from .peer import ProtocolBGPPeer
36__all__ = ["ProtocolBGP"]
39BGPPeersConfig = dict[str, BGPPeerConfig]
40BGPPeers = dict[str, ProtocolBGPPeer]
41BGPOriginatedRoutes = dict[str, str]
44class ProtocolBGP(SectionProtocolBase): # pylint: disable=too-many-public-methods,too-many-positional-arguments
45 """BIRD BGP protocol configuration."""
47 # BGP protocol attributes
48 _bgp_attributes: BGPAttributes
49 # BGP functions
50 _bgp_functions: BGPFunctions
52 # BGP peers
53 _peers: BGPPeers
55 # Internal config before configuration happens
56 _originated_routes: BGPOriginatedRoutes
58 def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
59 self,
60 birdconfig_globals: BirdConfigGlobals,
61 birdattributes: SectionBirdAttributes,
62 constants: SectionConstants,
63 functions: SectionFunctions,
64 tables: SectionTables,
65 ) -> None:
66 """Initialize the object."""
67 super().__init__(birdconfig_globals, birdattributes, constants, functions, tables)
69 # Set section name
70 self._section = "BGP Protocol"
72 # BGP peers
73 self._peers = {}
75 # Routes originated from BGP
76 self._originated_routes = {}
78 # Setup BGP attributes
79 self._bgp_attributes = BGPAttributes()
80 # Setup BGP functions
81 self._bgp_functions = BGPFunctions(self.birdconfig_globals, self.functions)
83 def configure(self) -> None:
84 """Configure the BGP protocol."""
85 super().configure()
87 # Blank the BGP peer state
88 if "bgp" not in self.birdconfig_globals.state:
89 self.birdconfig_globals.state["bgp"] = {}
90 self.birdconfig_globals.state["bgp"]["peers"] = {}
92 self._configure_constants_bgp()
94 # Check if we're adding RPKI ROA tables
95 if self.rpki_source:
96 # Configure RPKI protocol
97 rpki_protocol = ProtocolRPKI(self.birdconfig_globals, self.birdattributes, self.tables, self.rpki_source)
98 self.conf.add(rpki_protocol)
100 self._configure_birdattributes_bgp()
102 self.functions.conf.append(self.bgp_functions, deferred=True)
104 self.tables.conf.append("# BGP Tables")
105 self.tables.conf.append("ipv4 table t_bgp4;")
106 self.tables.conf.append("ipv6 table t_bgp6;")
107 self.tables.conf.append("")
109 # Setup BGP origination
110 self._configure_originated_routes()
112 # BGP to master export filters
113 self._setup_bgp_to_master_export_filter()
115 # BGP to master import filters
116 self._setup_bgp_to_master_import_filter()
118 # Configure pipe from BGP to the master routing table
119 bgp_master_pipe = ProtocolPipe(
120 birdconfig_globals=self.birdconfig_globals,
121 table_from="bgp",
122 table_to="master",
123 export_filter_type=ProtocolPipeFilterType.UNVERSIONED,
124 import_filter_type=ProtocolPipeFilterType.UNVERSIONED,
125 )
126 self.conf.add(bgp_master_pipe)
128 # Check if we're importing connected routes, if we are, create the protocol and pipe
129 if self.route_policy_import.connected:
130 # Create an interface list to feed to our routing table
131 interfaces: list[str] = []
132 if isinstance(self.route_policy_import.connected, list):
133 interfaces = self.route_policy_import.connected
134 # Add direct protocol for redistribution of connected routes
135 bgp_direct_protocol = ProtocolDirect(
136 self.birdconfig_globals,
137 self.birdattributes,
138 self.constants,
139 self.functions,
140 self.tables,
141 name="bgp",
142 interfaces=interfaces,
143 )
144 self.conf.add(bgp_direct_protocol)
145 # Add pipe
146 self._setup_bgp_to_direct_import_filter()
147 bgp_direct_pipe = ProtocolPipe(
148 self.birdconfig_globals,
149 name="bgp",
150 table_from="bgp",
151 table_to="direct",
152 table_export="none",
153 import_filter_type=ProtocolPipeFilterType.UNVERSIONED,
154 )
155 self.conf.add(bgp_direct_pipe)
157 # Loop with BGP peers and configure them
158 self.conf.add("")
159 for peer in self.peers.values():
160 self.conf.add(peer)
162 def add_originated_route(self, route: str) -> None:
163 """Add originated route."""
164 (prefix, route_info) = route.split(" ", 1)
165 self.originated_routes[prefix] = route_info
167 def add_peer(self, peer_name: str, peer_config: BGPPeerConfig) -> None:
168 """Add peer to BGP."""
170 if peer_name in self.peers:
171 raise BirdPlanError(f"BGP peer '{peer_name}' already exists")
173 # Create BGP peer object
174 peer = ProtocolBGPPeer(
175 self.birdconfig_globals,
176 self.birdattributes,
177 self.constants,
178 self.functions,
179 self.tables,
180 self.bgp_attributes,
181 self.bgp_functions,
182 peer_name,
183 peer_config,
184 )
186 # Add peer to our configured peer list
187 self.peers[peer_name] = peer
189 def peer(self, name: str) -> ProtocolBGPPeer:
190 """Return a BGP peer configuration object."""
191 if name not in self.peers:
192 raise BirdPlanError(f"Peer '{name}' not found")
193 return self.peers[name]
195 def constraints(self, peer_type: str) -> BGPPeertypeConstraints:
196 """Return the prefix limits for a specific peer type."""
197 if peer_type not in self.bgp_attributes.peertype_constraints:
198 raise BirdPlanError(f"Peer type '{peer_type}' has no implemented global prefix limits")
199 return self.bgp_attributes.peertype_constraints[peer_type]
201 def _configure_birdattributes_bgp(self) -> None:
202 """Configure BGP attributes."""
203 # NK: No attributes for now
204 # self.birdattributes.conf.append_title("BGP Attributes") # noqa: ERA001
206 def _configure_constants_bgp(self) -> None: # noqa: PLR0915
207 """Configure BGP constants."""
208 self.constants.conf.append_title("BGP Constants")
210 self.constants.conf.append("# Our BGP ASN")
211 self.constants.conf.append(f"define BGP_ASN = {self.asn};")
212 self.constants.conf.append("")
214 self.constants.conf.append("# Ref http://bgpfilterguide.nlnog.net/guides/bogon_asns/")
215 self.constants.conf.append("define BOGON_ASNS = [")
216 self.constants.conf.append(" 0, # RFC 7607")
217 self.constants.conf.append(" 23456, # RFC 4893 AS_TRANS")
218 self.constants.conf.append(" 64496..64511, # RFC 5398 and documentation/example ASNs")
219 if self.birdconfig_globals.test_mode:
220 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 64512..65534, # RFC 6996 Private ASNs")
221 else:
222 self.constants.conf.append(" 64512..65534, # RFC 6996 Private ASNs")
223 self.constants.conf.append(" 65535, # RFC 7300 Last 16 bit ASN")
224 self.constants.conf.append(" 65536..65551, # RFC 5398 and documentation/example ASNs")
225 self.constants.conf.append(" 65552..131071, # RFC IANA reserved ASNs")
226 if self.birdconfig_globals.test_mode:
227 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 4200000000..4294967294, # RFC 6996 Private ASNs")
228 self.constants.conf.append(" 4200000000..4294900000, # RFC 6996 Private ASNs - ADJUSTED FOR TESTING")
229 else:
230 self.constants.conf.append(" 4200000000..4294967294, # RFC 6996 Private ASNs")
231 self.constants.conf.append(" 4294967295 # RFC 7300 Last 32 bit ASN")
232 self.constants.conf.append("];")
233 self.constants.conf.append("")
235 self.constants.conf.append("define PRIVATE_ASNS = [")
236 if self.birdconfig_globals.test_mode:
237 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 64512..65534, # RFC 6996 Private ASNs")
238 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 4200000000..4294967294, # RFC 6996 Private ASNs")
239 self.constants.conf.append(" 4200000000..4294900000 # RFC 6996 Private ASNs - ADJUSTED FOR TESTING")
240 else:
241 self.constants.conf.append(" 64512..65534, # RFC 6996 Private ASNs")
242 self.constants.conf.append(" 4200000000..4294967294 # RFC 6996 Private ASNs")
243 self.constants.conf.append("];")
244 self.constants.conf.append("")
246 self.constants.conf.append("# Ref http://bgpfilterguide.nlnog.net/guides/no_transit_leaks")
247 self.constants.conf.append("define BGP_ASNS_TRANSIT = [")
248 self.constants.conf.append(" 174, # Cogent")
249 self.constants.conf.append(" 209, # Qwest (HE carries this on IXPs IPv6 (Jul 12 2018))")
250 self.constants.conf.append(" 701, # UUNET")
251 self.constants.conf.append(" 702, # UUNET")
252 self.constants.conf.append(" 1239, # Sprint")
253 self.constants.conf.append(" 1299, # Telia")
254 self.constants.conf.append(" 2914, # NTT Communications")
255 self.constants.conf.append(" 3257, # GTT Backbone")
256 self.constants.conf.append(" 3320, # Deutsche Telekom AG (DTAG)")
257 self.constants.conf.append(" 3356, # Level3")
258 self.constants.conf.append(" 3491, # PCCW")
259 self.constants.conf.append(" 3549, # Level3")
260 self.constants.conf.append(" 3561, # Savvis / CenturyLink")
261 self.constants.conf.append(" 4134, # Chinanet")
262 self.constants.conf.append(" 5511, # Chinanet")
263 self.constants.conf.append(" 5511, # Orange opentransit")
264 self.constants.conf.append(" 6453, # Tata Communications")
265 self.constants.conf.append(" 6461, # Zayo Bandwidth")
266 self.constants.conf.append(" 6762, # Seabone / Telecom Italia")
267 self.constants.conf.append(" 6830, # Liberty Global")
268 self.constants.conf.append(" 7018 # AT&T")
269 self.constants.conf.append("];")
270 self.constants.conf.append("")
272 # NK: IMPORTANT IF THE ABOVE CHANGES UPDATE THE BELOW
273 self.constants.conf.append("# Community stripping")
274 self.constants.conf.append("define BGP_COMMUNITY_STRIP = [ ")
275 self.constants.conf.append(" (23456, *),")
276 self.constants.conf.append(" (64496..64511, *)") # Documentation
277 self.constants.conf.append("];")
279 # This is used for stripping large communities from customers mostly
280 self.constants.conf.append("define BGP_LC_STRIP = [ ")
281 self.constants.conf.append(" (23456, *, *),")
282 self.constants.conf.append(" (64496..64511, *, *),") # Documentation
283 self.constants.conf.append(" (65552..131071, *, *),") # Reserved
284 self.constants.conf.append(" (BGP_ASN, 1..3, *),") # Strip route learned functions
285 # Allow client traffic engineering: 4, 5, 6, 7, 8
286 self.constants.conf.append(" (BGP_ASN, 9..60, *),") # Strip unused
287 self.constants.conf.append(" (BGP_ASN, 64..70, *),") # Strip unused
288 self.constants.conf.append(" (BGP_ASN, 74..665, *),") # Strip unsed
289 self.constants.conf.append(" (BGP_ASN, 667..4294967295, *),") # Strip unsed + rest (incl. 1000 - info, 1101 - filter)
290 # These functions should never be used on our own ASN
291 self.constants.conf.append(" (BGP_ASN, 4, BGP_ASN),")
292 self.constants.conf.append(" (BGP_ASN, 6, BGP_ASN),")
293 self.constants.conf.append(" (BGP_ASN, 61, BGP_ASN),")
294 self.constants.conf.append(" (BGP_ASN, 62, BGP_ASN),")
295 self.constants.conf.append(" (BGP_ASN, 63, BGP_ASN),")
296 self.constants.conf.append(" (BGP_ASN, 666, BGP_ASN)")
297 self.constants.conf.append("];")
299 # Strip communities mostly for peers and transit providers
300 self.constants.conf.append("define BGP_COMMUNITY_STRIP_ALL = [")
301 # This is first because of the , we need
302 if self.asn and self.asn < 65535: # noqa: PLR2004
303 self.constants.conf.append(" (BGP_ASN, *),")
304 else:
305 self.constants.conf.append(" # (BGP_ASN, *), # Not stripping due to 4-byte ASN")
306 self.constants.conf.append(" (23456, *),")
307 self.constants.conf.append(" (64496..64511, *)") # Documentation
308 self.constants.conf.append("];")
309 # This is used for stripping large communities from peers and transit providers
310 self.constants.conf.append("define BGP_LC_STRIP_ALL = [")
311 self.constants.conf.append(" (23456, *, *),")
312 self.constants.conf.append(" (64496..64511, *, *),") # Documentation
313 self.constants.conf.append(" (65552..131071, *, *),") # Reserved
314 self.constants.conf.append(" (BGP_ASN, *, *)")
315 self.constants.conf.append("];")
317 # Stripping private communities
318 self.constants.conf.append("define BGP_COMMUNITY_STRIP_PRIVATE = [")
319 if self.birdconfig_globals.test_mode:
320 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (64512..65534, *)") # Private
321 else:
322 self.constants.conf.append(" (64512..65534, *)") # Private
323 self.constants.conf.append("];")
325 self.constants.conf.append("define BGP_LC_STRIP_PRIVATE = [")
326 # Don't strip the lower private ASN range during testing
327 if self.birdconfig_globals.test_mode:
328 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (64512..65534, *, *)") # Private
329 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (4200000000..4294967294, *, *)")
330 self.constants.conf.append(" (4200000000..4294900000, *, *) # ADJUSTED FOR TESTING")
331 else:
332 self.constants.conf.append(" (64512..65534, *, *),") # Private
333 self.constants.conf.append(" (4200000000..4294967294, *, *)")
334 self.constants.conf.append("];")
336 self.constants.conf.append("# BGP Route Preferences")
337 self.constants.conf.append("define BGP_PREF_OWN = 950;") # -20 = Originate, -10 = static, -5 = kernel
338 self.constants.conf.append("define BGP_PREF_CUSTOMER = 750;")
339 self.constants.conf.append("define BGP_PREF_PEER = 470;")
340 self.constants.conf.append("define BGP_PREF_ROUTESERVER = 450;")
341 self.constants.conf.append("define BGP_PREF_TRANSIT = 150;")
342 self.constants.conf.append("")
344 self.constants.conf.append("# Well known communities")
345 self.constants.conf.append("define BGP_COMMUNITY_GRACEFUL_SHUTDOWN = (65535, 0);")
346 self.constants.conf.append("define BGP_COMMUNITY_BLACKHOLE = (65535, 666);")
347 self.constants.conf.append("define BGP_COMMUNITY_NOEXPORT = (65535, 65281);")
348 self.constants.conf.append("define BGP_COMMUNITY_NOADVERTISE = (65535, 65282);")
349 self.constants.conf.append("")
351 self.constants.conf.append("# Well known extended communities")
352 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_VALID = (unknown 0x4300, 0, 0);")
353 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_NOTFOUND = (unknown 0x4300, 0, 1);")
354 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_INVALID = (unknown 0x4300, 0, 2);")
355 self.constants.conf.append("")
357 self.constants.conf.append("# Large community functions")
358 # NK: IMPORTANT IF YOU CHANGE THE BELOW, UPDATE BGP_LC_STRIP
359 self.constants.conf.append("define BGP_LC_FUNCTION_LOCATION_ISO3166 = 1;")
360 self.constants.conf.append("define BGP_LC_FUNCTION_LOCATION_UNM49 = 2;")
361 self.constants.conf.append("define BGP_LC_FUNCTION_RELATION = 3;")
362 self.constants.conf.append("define BGP_LC_FUNCTION_NOEXPORT = 4;")
363 self.constants.conf.append("define BGP_LC_FUNCTION_NOEXPORT_LOCATION = 5;")
364 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_ONE = 6;")
365 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_ONE_2 = 61;")
366 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_TWO = 62;")
367 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_THREE = 63;")
368 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_ONE = 7;")
369 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_ONE_2 = 71;")
370 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_TWO = 72;")
371 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_THREE = 73;")
372 self.constants.conf.append("define BGP_LC_FUNCTION_LOCALPREF = 8;")
373 self.constants.conf.append("define BGP_LC_FUNCTION_INFORMATION = 1000;")
374 self.constants.conf.append("define BGP_LC_FUNCTION_FILTERED = 1101;")
375 self.constants.conf.append("define BGP_LC_FUNCTION_ACTION = 1200;")
376 self.constants.conf.append("")
378 self.constants.conf.append("# Large community noexport")
379 self.constants.conf.append("define BGP_LC_EXPORT_NOTRANSIT = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65412);")
380 self.constants.conf.append("define BGP_LC_EXPORT_NOPEER = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65413);")
381 self.constants.conf.append("define BGP_LC_EXPORT_NOCUSTOMER = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65414);")
382 self.constants.conf.append("")
384 self.constants.conf.append("# Large community relations")
385 self.constants.conf.append("define BGP_LC_RELATION = [(BGP_ASN, BGP_LC_FUNCTION_RELATION, 1..5)];")
386 self.constants.conf.append("define BGP_LC_RELATION_OWN = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 1);")
387 self.constants.conf.append("define BGP_LC_RELATION_CUSTOMER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 2);")
388 self.constants.conf.append("define BGP_LC_RELATION_PEER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 3);")
389 self.constants.conf.append("define BGP_LC_RELATION_TRANSIT = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 4);")
390 self.constants.conf.append("define BGP_LC_RELATION_ROUTESERVER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 5);")
391 self.constants.conf.append("")
393 self.constants.conf.append("# Large communities for LOCAL_PREF attribute manipulation")
394 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_ONE = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 1);")
395 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_TWO = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 2);")
396 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_THREE = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 3);")
397 self.constants.conf.append("")
399 self.constants.conf.append("# Large community information")
400 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_COMMUNITY = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 1);")
401 self.constants.conf.append(
402 "define BGP_LC_INFORMATION_STRIPPED_COMMUNITY_PRIVATE = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 2);"
403 )
404 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_LC = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 3);")
405 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_LC_PRIVATE = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 4);")
406 self.constants.conf.append("")
408 self.constants.conf.append("# Large community filtered")
409 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_LEN_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 1);")
410 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 2);")
411 self.constants.conf.append("define BGP_LC_FILTERED_BOGON = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 3);")
412 self.constants.conf.append("define BGP_LC_FILTERED_BOGON_ASN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 4);")
413 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 5);")
414 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 6);")
415 self.constants.conf.append("define BGP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 7);")
416 self.constants.conf.append("define BGP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 8);")
417 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_FILTERED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 9);")
418 self.constants.conf.append("define BGP_LC_FILTERED_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 10);")
419 # self.constants.conf.append('define BGP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 11);') # noqa: E501,ERA001
420 self.constants.conf.append("define BGP_LC_FILTERED_DEFAULT_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 12);")
421 self.constants.conf.append("define BGP_LC_FILTERED_RPKI_UNKNOWN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 13);")
422 self.constants.conf.append("define BGP_LC_FILTERED_RPKI_INVALID = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 14);")
423 self.constants.conf.append("define BGP_LC_FILTERED_TRANSIT_FREE_ASN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 15);")
424 self.constants.conf.append("define BGP_LC_FILTERED_TOO_MANY_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 16);")
425 self.constants.conf.append("define BGP_LC_FILTERED_ROUTECOLLECTOR = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 17);")
426 self.constants.conf.append("define BGP_LC_FILTERED_QUARANTINED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 18);")
427 self.constants.conf.append(
428 "define BGP_LC_FILTERED_TOO_MANY_EXTENDED_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 19);"
429 )
430 self.constants.conf.append("define BGP_LC_FILTERED_TOO_MANY_LARGE_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 20);")
431 self.constants.conf.append("define BGP_LC_FILTERED_PEER_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 21);")
432 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 22);")
433 self.constants.conf.append("define BGP_LC_FILTERED_NO_RELATION_LC = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 23);")
434 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_LEN_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 24);")
435 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_LEN_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 25);")
436 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 26);")
437 self.constants.conf.append("define BGP_LC_FILTERED_DENY_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 27);")
438 self.constants.conf.append("define BGP_LC_FILTERED_DENY_ASPATH = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 28);")
439 self.constants.conf.append("define BGP_LC_FILTERED_DENY_PREFIX = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 29);")
440 self.constants.conf.append("define BGP_LC_FILTERED_ACTION = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 30);")
441 self.constants.conf.append("")
442 self.constants.conf.append("# Large community actions")
443 self.constants.conf.append("define BGP_LC_ACTION_REPLACE_ASPATH = (BGP_ASN, BGP_LC_FUNCTION_ACTION, 1);")
444 self.constants.conf.append("define BGP_LC_ACTION_BLACKHOLE_ORIGINATE = (BGP_ASN, BGP_LC_FUNCTION_ACTION, 2);")
445 self.constants.conf.append("")
447 def _configure_originated_routes(self) -> None:
448 # Work out static v4 and v6 routes
449 routes: dict[str, list[str]] = {"4": [], "6": []}
450 for prefix in sorted(self.originated_routes.keys()):
451 info = self.originated_routes[prefix]
452 if "." in prefix:
453 routes["4"].append(f"{prefix} {info}")
454 elif ":" in prefix:
455 routes["6"].append(f"{prefix} {info}")
456 else:
457 raise BirdPlanError(f"The BGP originate route '{prefix}' is odd")
459 self.tables.conf.append("# BGP Origination Tables")
461 filter_name = "f_bgp_originate_import"
462 self.conf.add(f"filter {filter_name}")
463 self.conf.add("string filter_name;")
464 self.conf.add("{")
465 self.conf.add(f' filter_name = "{filter_name}";')
466 self.conf.add(" # Origination import")
467 self.conf.add(f" {self.bgp_functions.import_own(20)};")
468 self.conf.add(" accept;")
469 self.conf.add("};")
470 self.conf.add("")
472 # Loop with IPv4 and IPv6
473 for ipv in ["4", "6"]:
474 self.tables.conf.append(f"ipv{ipv} table t_bgp_originate{ipv};")
476 self.conf.add(f"protocol static bgp_originate{ipv} {{")
477 self.conf.add(f' description "BGP route origination for IPv{ipv}";')
478 self.conf.add("")
479 self.conf.add(f" vrf {self.birdconfig_globals.vrf};")
480 self.conf.add("")
481 self.conf.add(f" ipv{ipv} {{")
482 self.conf.add(f" table t_bgp_originate{ipv};")
483 self.conf.add(" preference 195;")
484 self.conf.add(" export none;")
485 self.conf.add(f" import filter {filter_name};")
486 self.conf.add(" };")
487 # If we have IPv4 routes
488 if routes[ipv]:
489 self.conf.add("")
490 # Output the routes
491 for route in routes[ipv]:
492 self.conf.add(f" route {route};")
493 self.conf.add("};")
494 self.conf.add("")
496 self.tables.conf.append("")
498 # Configure BGP origination route pipe to the bgp table
499 originate_pipe = ProtocolPipe(
500 birdconfig_globals=self.birdconfig_globals,
501 table_from="bgp_originate",
502 table_to="bgp",
503 table_export="all",
504 table_import="none",
505 )
506 self.conf.add(originate_pipe)
508 def _setup_bgp_to_master_export_filter(self) -> None:
509 """BGP main table to master export filters setup."""
511 # Configure export filter to master
512 filter_name = "f_bgp_master_export"
513 self.conf.add("# Export filter FROM BGP table TO master table")
514 self.conf.add(f"filter {filter_name}")
515 self.conf.add("string filter_name;")
516 self.conf.add("{")
517 self.conf.add(f' filter_name = "{filter_name}";')
518 # Accept BGP routes into the master routing table
519 self.conf.add(f" {self.bgp_functions.accept_bgp()};")
520 # Check if we accept customer blackhole routes, if not block it
521 if self.route_policy_accept.bgp_customer_blackhole:
522 self.conf.add(f" {self.bgp_functions.accept_customer_blackhole()};")
523 # Check if we accept our own blackhole routes, if not block it
524 if self.route_policy_accept.bgp_own_blackhole:
525 self.conf.add(f" {self.bgp_functions.accept_own_blackhole()};")
526 # Check if we accept default routes originated from within our federation, if not block it
527 if self.route_policy_accept.bgp_own_default:
528 self.conf.add(f" {self.bgp_functions.accept_bgp_own_default()};")
529 # Check if we accept default routes originated from transit peers, if not block it
530 if self.route_policy_accept.bgp_transit_default:
531 self.conf.add(f" {self.bgp_functions.accept_bgp_transit_default()};")
532 # Check if we accept originated routes, if not block it
533 if self.route_policy_accept.originated:
534 self.conf.add(f" {self.bgp_functions.accept_originated()};")
535 # Check if we accept originated routes, if not block it
536 if self.route_policy_accept.originated_default:
537 self.conf.add(f" {self.bgp_functions.accept_originated_default()};")
538 # Default to reject
539 self.conf.add(" if DEBUG then")
540 self.conf.add(f' print "[{filter_name}] Rejecting ", net, " from t_bgp to master (fallthrough)";')
541 self.conf.add(" reject;")
542 self.conf.add("};")
543 self.conf.add("")
545 def _setup_bgp_to_master_import_filter(self) -> None:
546 """BGP main table to master import filters setup."""
547 # Configure import filter to master
548 filter_name = "f_bgp_master_import"
549 self.conf.add("# Import filter FROM master table TO BGP table")
550 self.conf.add(f"filter {filter_name}")
551 self.conf.add("string filter_name;")
552 self.conf.add("{")
553 self.conf.add(f' filter_name = "{filter_name}";')
554 # BGP importation of kernel routes
555 if self.route_policy_import.kernel:
556 self.conf.add(f" {self.bgp_functions.import_kernel()};")
557 # BGP importation of kernel blackhole routes
558 if self.route_policy_import.kernel_blackhole:
559 self.conf.add(f" {self.bgp_functions.import_kernel_blackhole()};")
560 # BGP importation of kernel default routes
561 if self.route_policy_import.kernel_default:
562 self.conf.add(f" {self.bgp_functions.import_kernel_default()};")
563 # BGP importation of static routes
564 if self.route_policy_import.static:
565 self.conf.add(f" {self.bgp_functions.import_static()};")
566 # BGP importation of static blackhole routes
567 if self.route_policy_import.static_blackhole:
568 self.conf.add(f" {self.bgp_functions.import_static_blackhole()};")
569 # BGP importation of static default routes
570 if self.route_policy_import.static_default:
571 self.conf.add(f" {self.bgp_functions.import_static_default()};")
572 # Else reject
573 self.conf.add(" if DEBUG then")
574 self.conf.add(f' print "[{filter_name}] Rejecting ", net, " from master to t_bgp (fallthrough)";')
575 self.conf.add(" reject;")
576 self.conf.add("};")
577 self.conf.add("")
579 def _setup_bgp_to_direct_import_filter(self) -> None:
580 """BGP main table to direct import filters setup."""
582 filter_name = "f_bgp_direct_import"
583 self.conf.add("# Import filter FROM master table TO BGP table")
584 self.conf.add(f"filter {filter_name}")
585 self.conf.add("string filter_name;")
586 self.conf.add("{")
587 self.conf.add(f' filter_name = "{filter_name}";')
588 self.conf.add(" # Import connected routes")
589 self.conf.add(f" {self.bgp_functions.import_own(10)};")
590 self.conf.add(" accept;")
591 self.conf.add("};")
592 self.conf.add("")
594 # PROPERTIES
596 @property
597 def bgp_attributes(self) -> BGPAttributes:
598 """Return our BGP protocol attributes."""
599 return self._bgp_attributes
601 @property
602 def bgp_functions(self) -> BGPFunctions:
603 """Return our BGP protocol functions."""
604 return self._bgp_functions
606 @property
607 def asn(self) -> int | None:
608 """Return our ASN."""
609 return self.bgp_attributes.asn
611 @asn.setter
612 def asn(self, asn: int) -> None:
613 """Set our ASN."""
614 self.bgp_attributes.asn = asn
615 # Enable bogon constants
616 self.constants.need_bogons = True
618 @property
619 def peertype_constraints(self) -> dict[str, BGPPeertypeConstraints]:
620 """Return our peertype constraints."""
621 return self.bgp_attributes.peertype_constraints
623 @property
624 def rpki_source(self) -> RPKISource | None:
625 """Return the RPKI source to use for validation."""
626 return self.bgp_attributes.rpki_source
628 @rpki_source.setter
629 def rpki_source(self, rpki_source: RPKISource) -> None:
630 """Set the RPKI source to use for validation."""
631 self.bgp_attributes.rpki_source = rpki_source
633 @property
634 def graceful_shutdown(self) -> bool:
635 """Return our the value of graceful_shutdown."""
636 return self.bgp_attributes.graceful_shutdown
638 @graceful_shutdown.setter
639 def graceful_shutdown(self, graceful_shutdown: bool) -> None:
640 """Set the value of graceful_shutdown."""
641 self.bgp_attributes.graceful_shutdown = graceful_shutdown
643 @property
644 def quarantine(self) -> bool:
645 """Global BGP peer quarantine state."""
646 return self.bgp_attributes.quarantine
648 @quarantine.setter
649 def quarantine(self, quarantine: bool) -> None:
650 """Global BGP peer quarantine state."""
651 self.bgp_attributes.quarantine = quarantine
653 @property
654 def rr_cluster_id(self) -> str | None:
655 """Return route reflector cluster ID."""
656 return self.bgp_attributes.rr_cluster_id
658 @rr_cluster_id.setter
659 def rr_cluster_id(self, rr_cluster_id: str) -> None:
660 """Set our route reflector cluster ID."""
661 self.bgp_attributes.rr_cluster_id = rr_cluster_id
663 @property
664 def route_policy_accept(self) -> BGPRoutePolicyAccept:
665 """Return our route policy for accepting of routes from peers into the main BGP table."""
666 return self.bgp_attributes.route_policy_accept
668 @property
669 def route_policy_import(self) -> BGPRoutePolicyImport:
670 """Return our route policy for importing of routes from internal tables."""
671 return self.bgp_attributes.route_policy_import
673 @property
674 def peers(self) -> BGPPeers:
675 """BGP peers."""
676 return self._peers
678 @property
679 def originated_routes(self) -> BGPOriginatedRoutes:
680 """Return our originated routes."""
681 return self._originated_routes