Coverage for birdplan/bird_config/sections/protocols/bgp/peer/__init__.py: 95%
1969 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-23 03:27 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-23 03:27 +0000
1#
2# SPDX-License-Identifier: GPL-3.0-or-later
3#
4# Copyright (c) 2019-2024, 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 peer configuration."""
21# pylint: disable=too-many-lines
23import fnmatch
24import logging
25import re
26from typing import Any, Dict, List, Optional, Union
28from ......bgpq3 import BGPQ3
29from ......console.colors import colored
30from ......exceptions import BirdPlanError
31from ......peeringdb import PeeringDB
32from ..... import util
33from .....globals import BirdConfigGlobals
34from ....constants import SectionConstants
35from ....functions import BirdVariable, SectionFunctions
36from ....tables import SectionTables
37from ...base import SectionProtocolBase
38from ...pipe import ProtocolPipe, ProtocolPipeFilterType
39from ..bgp_attributes import BGPAttributes, BGPPeertypeConstraints
40from ..bgp_functions import BGPFunctions
41from ..bgp_types import BGPPeerConfig
42from .peer_attributes import (
43 BGPPeerAttributes,
44 BGPPeerCommunities,
45 BGPPeerConstraints,
46 BGPPeerExportFilterPolicy,
47 BGPPeerFilterItem,
48 BGPPeerImportFilterDenyPolicy,
49 BGPPeerImportFilterPolicy,
50 BGPPeerImportPrefixLimitAction,
51 BGPPeerLargeCommunities,
52 BGPPeerLocation,
53 BGPPeerPrefixLimit,
54 BGPPeerPrepend,
55 BGPPeerRoutePolicyAccept,
56 BGPPeerRoutePolicyRedistribute,
57)
59__all__ = ["ProtocolBGPPeer"]
62class ProtocolBGPPeer(SectionProtocolBase): # pylint: disable=too-many-instance-attributes,too-many-public-methods
63 """BIRD BGP protocol peer configuration."""
65 _bgp_attributes: BGPAttributes
66 _bgp_functions: BGPFunctions
67 _peer_attributes: BGPPeerAttributes
68 _state: Dict[str, Any]
69 _prev_state: Optional[Dict[str, Any]]
71 def __init__( # noqa: CFQ001,CFQ002 # pylint: disable=too-many-branches,too-many-statements,too-many-arguments,too-many-locals
72 self,
73 birdconfig_globals: BirdConfigGlobals,
74 constants: SectionConstants,
75 functions: SectionFunctions,
76 tables: SectionTables,
77 bgp_attributes: BGPAttributes,
78 bgp_functions: BGPFunctions,
79 peer_name: str,
80 peer_config: BGPPeerConfig,
81 ):
82 """Initialize the object."""
83 super().__init__(birdconfig_globals, constants, functions, tables)
85 # Initialize our attributes
86 self._bgp_attributes = bgp_attributes
87 self._bgp_functions = bgp_functions
88 self._peer_attributes = BGPPeerAttributes()
89 self._state = {}
91 # Save our name and configuration
92 self.name = peer_name
94 # Check if we have a previous state for this peer
95 self._prev_state = None
96 if (
97 "bgp" in self.birdconfig_globals.state
98 and "peers" in self.birdconfig_globals.state["bgp"]
99 and self.name in self.birdconfig_globals.state["bgp"]["peers"]
100 ):
101 # If we do set our attribute for it
102 self._prev_state = self.birdconfig_globals.state["bgp"]["peers"][self.name]
104 # Check if we have a peer description
105 if "description" not in peer_config:
106 raise BirdPlanError(f"BGP peer '{self.name}' need a 'description' field")
107 self.description = peer_config["description"]
109 # Check if we have a peer type
110 if "type" not in peer_config:
111 raise BirdPlanError(f"BGP peer '{self.name}' need a 'type' field")
112 self.peer_type = peer_config["type"]
113 # Make sure it is valid
114 if self.peer_type not in (
115 "customer",
116 "internal",
117 "peer",
118 "routecollector",
119 "routeserver",
120 "rrclient",
121 "rrserver",
122 "rrserver-rrserver",
123 "transit",
124 ):
125 raise BirdPlanError(f"The BGP peer type '{self.peer_type}' is not supported")
127 # First of all check if we have a route reflector cluster ID, we need one to have a rrclient
128 if self.peer_type in ("rrclient", "rrserver-rrserver") and not self.bgp_attributes.rr_cluster_id:
129 raise BirdPlanError(f"The BGP peer type '{self.peer_type}' requires a BGP 'cluster_id' options to be specified")
131 # Check if we have a peer asn
132 if "asn" not in peer_config:
133 raise BirdPlanError(f"BGP peer '{self.name}' need a 'asn' field")
134 self.asn = peer_config["asn"]
136 # Check if we're replacing the ASN in the AS-PATH
137 if "replace_aspath" in peer_config:
138 # Do a sanity check on replacing the ASN
139 if self.peer_type not in ("customer", "internal"):
140 raise BirdPlanError(
141 f"Having 'replace_aspath' set for peer '{self.name}' with type '{self.peer_type}' makes no sense"
142 )
143 # Make sure the ASN is private and not public
144 if self.birdconfig_globals.test_mode:
145 # In test mode the only private ASN range is 4294900001 to 4294967294
146 if not (self.asn >= 4200000000 and self.asn <= 4294900000):
147 raise BirdPlanError(
148 f"Having 'replace_aspath' set for peer '{self.name}' with a non-private ASN {self.asn} makes no sense"
149 )
150 # We're not in test mode...
151 else:
152 # Make sure we're within the full set of private ASN ranges
153 if not ((self.asn >= 64512 and self.asn <= 65534) or (self.asn >= 4200000000 and self.asn <= 4294967294)):
154 raise BirdPlanError(
155 f"Having 'replace_aspath' set for peer '{self.name}' with a non-private ASN {self.asn} makes no sense"
156 )
157 # Check if we're actually replacing it?
158 if peer_config["replace_aspath"]:
159 self.replace_aspath = True
161 # If the peer type is of internal nature, but doesn't match our peer type, throw an exception
162 if self.peer_type in ("internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
163 if self.asn != self.bgp_attributes.asn and not (self.peer_type == "internal" and self.replace_aspath):
164 raise BirdPlanError(
165 f"BGP peer '{self.name}' ({self.asn}) is of internal nature, "
166 f"but has a different ASN ({self.bgp_attributes.asn})"
167 )
169 # Setup the peer location
170 if "location" in peer_config:
171 # Make sure the peer type configuration is valid for the specification of the peer location
172 if self.peer_type not in ("customer", "peer", "routeserver", "routecollector", "transit"):
173 raise BirdPlanError(
174 f"BGP peer '{self.name}' has 'location' configuration but it makes no sense for peer type '{self.peer_type}'"
175 )
176 # Check for our ISO-3166 location
177 if "iso3166" in peer_config["location"]:
178 self.location.iso3166 = peer_config["location"]["iso3166"]
179 # Check for our UN.M49 location
180 if "unm49" in peer_config["location"]:
181 self.location.unm49 = peer_config["location"]["unm49"]
183 # INTERNAL: Dynamically set the section
184 self._section = f"BGP Peer: {self.asn} - {self.name}"
186 # Check for neighbor addresses
187 if "neighbor4" in peer_config:
188 self.neighbor4 = peer_config["neighbor4"]
189 if "neighbor6" in peer_config:
190 self.neighbor6 = peer_config["neighbor6"]
191 # Check if we have a source address
192 if "source_address4" in peer_config:
193 self.source_address4 = peer_config["source_address4"]
194 if "source_address6" in peer_config:
195 self.source_address6 = peer_config["source_address6"]
196 # Sanity test the neighbor and source addresses
197 if self.neighbor4 and not self.source_address4:
198 raise BirdPlanError(f"BGP peer '{self.name}' has 'neighbor4' specified but no 'source_address4'")
199 if self.neighbor6 and not self.source_address6:
200 raise BirdPlanError(f"BGP peer '{self.name}' has 'neighbor6' specified but no 'source_address6'")
201 if self.source_address4 and not self.neighbor4:
202 raise BirdPlanError(f"BGP peer '{self.name}' has 'source_address4' specified but no 'neighbor4'")
203 if self.source_address6 and not self.neighbor6:
204 raise BirdPlanError(f"BGP peer '{self.name}' has 'source_address6' specified but no 'neighbor6'")
206 # Check additional options we may have
207 if "connect_delay_time" in peer_config:
208 self.connect_delay_time = peer_config["connect_delay_time"]
209 if "connect_retry_time" in peer_config:
210 self.connect_retry_time = peer_config["connect_retry_time"]
211 if "error_wait_time" in peer_config:
212 self.error_wait_time = peer_config["error_wait_time"]
213 if "multihop" in peer_config:
214 self.multihop = peer_config["multihop"]
215 if "password" in peer_config:
216 self.password = peer_config["password"]
217 if "ttl_security" in peer_config:
218 self.ttl_security = peer_config["ttl_security"]
220 if "cost" in peer_config:
221 # Raise an exception if peer cost does not make sense for a specific peer type
222 if self.peer_type not in ("customer", "peer", "routeserver", "transit"):
223 raise BirdPlanError(f"Having 'cost' specified for peer '{self.name}' with type '{self.peer_type}' makes no sense")
224 self.cost = peer_config["cost"]
226 if "add_paths" in peer_config:
227 # Raise an exception if add paths does not make sense for a specific peer type
228 if self.peer_type not in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
229 raise BirdPlanError(
230 f"Having 'add_paths' specified for peer '{self.name}' with type '{self.peer_type}' makes no" " sense"
231 )
232 self.add_paths = peer_config["add_paths"]
234 # Check if we are adding a large community to outgoing routes
235 if "incoming_large_communities" in peer_config:
236 # Raise an exception if incoming large communities makes no sense for this peer type
237 if self.peer_type == "routecollector":
238 raise BirdPlanError(
239 f"Having 'incoming_large_communities' set for peer '{self.name}' with type '{self.peer_type}' makes no sense"
240 )
241 # Add incoming large communities
242 for large_community in sorted(peer_config["incoming_large_communities"]):
243 self.large_communities.incoming.append(util.sanitize_community(large_community))
245 #
246 # bgp:peers:$PEER:outgoing_communities
247 #
249 # Check for outgoing_communities we need to setup
250 if "outgoing_communities" in peer_config:
251 # Add outgoing_communities configuration
252 if isinstance(peer_config["outgoing_communities"], dict):
253 # Check if we're adding large communities to blackhole routes
254 if "blackhole" in peer_config["outgoing_communities"]:
255 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["blackhole"])
256 self.communities.outgoing.kernel_blackhole = community_option
257 self.communities.outgoing.static_blackhole = community_option
258 self.communities.outgoing.bgp_customer_blackhole = community_option
259 self.communities.outgoing.bgp_own_blackhole = community_option
260 # Check if we're adding large communities to default routes
261 if "default" in peer_config["outgoing_communities"]:
262 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["default"])
263 self.communities.outgoing.kernel_default = community_option
264 self.communities.outgoing.originated_default = community_option
265 self.communities.outgoing.static_default = community_option
266 self.communities.outgoing.bgp_own_default = community_option
267 self.communities.outgoing.bgp_transit_default = community_option
268 # Check if we're adding outgoing large communitiesing to all BGP routes
269 if "bgp" in peer_config["outgoing_communities"]:
270 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp"])
271 self.communities.outgoing.bgp_customer = community_option
272 self.communities.outgoing.bgp_customer_blackhole = community_option
273 self.communities.outgoing.bgp_own = community_option
274 self.communities.outgoing.bgp_own_blackhole = community_option
275 self.communities.outgoing.bgp_own_default = community_option
276 self.communities.outgoing.bgp_peering = community_option
277 self.communities.outgoing.bgp_transit = community_option
278 self.communities.outgoing.bgp_transit_default = community_option
279 # Check if we're adding outgoing large communities to all BGP blackhole routes
280 if "bgp_blackhole" in peer_config["outgoing_communities"]:
281 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp_blackhole"])
282 self.communities.outgoing.bgp_customer_blackhole = community_option
283 self.communities.outgoing.bgp_own_blackhole = community_option
284 # Check if we're adding outgoing large communities to all BGP default routes
285 if "bgp_default" in peer_config["outgoing_communities"]:
286 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp_default"])
287 self.communities.outgoing.bgp_own_default = community_option
288 self.communities.outgoing.bgp_transit_default = community_option
289 # Check if we're adding outgoing large communities to all customer BGP routes
290 if "bgp_customer" in peer_config["outgoing_communities"]:
291 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp_customer"])
292 self.communities.outgoing.bgp_customer_blackhole = community_option
293 # Check if we're adding outgoing large communities to all our own BGP routes
294 if "bgp_own" in peer_config["outgoing_communities"]:
295 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp_own"])
296 self.communities.outgoing.bgp_own_blackhole = community_option
297 self.communities.outgoing.bgp_own_default = community_option
298 # Check if we're adding outgoing large communities to all transit BGP routes
299 if "bgp_transit" in peer_config["outgoing_communities"]:
300 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["bgp_transit"])
301 self.communities.outgoing.bgp_transit_default = community_option
302 # Check if we're adding outgoing large communities to all kernel routes
303 if "kernel" in peer_config["outgoing_communities"]:
304 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["kernel"])
305 self.communities.outgoing.kernel_blackhole = community_option
306 self.communities.outgoing.kernel_default = community_option
307 # Check if we're adding outgoing large communities to all originated routes
308 if "originated" in peer_config["outgoing_communities"]:
309 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["originated"])
310 self.communities.outgoing.originated_default = community_option
311 # Check if we're adding outgoing large communities to all static routes
312 if "static" in peer_config["outgoing_communities"]:
313 community_option = util.sanitize_community_list(peer_config["outgoing_communities"]["static"])
314 self.communities.outgoing.static_blackhole = community_option
315 self.communities.outgoing.static_default = community_option
317 for community_type, community_config in peer_config["outgoing_communities"].items():
318 if community_type not in (
319 "bgp",
320 "bgp_blackhole",
321 "bgp_customer_blackhole",
322 "bgp_customer",
323 "bgp_default",
324 "bgp_own_blackhole",
325 "bgp_own_default",
326 "bgp_own",
327 "bgp_peering",
328 "bgp_transit_default",
329 "bgp_transit",
330 "blackhole",
331 "connected",
332 "default",
333 "kernel",
334 "kernel_blackhole",
335 "kernel_default",
336 "originated",
337 "originated_default",
338 "static",
339 "static_blackhole",
340 "static_default",
341 ):
342 raise BirdPlanError(
343 f"BGP peer 'outgoing_communities' configuration '{community_type}' for peer '{self.name}' with type "
344 f"'{self.peer_type}' is invalid"
345 )
346 # Check that we're not doing something stupid
347 if self.peer_type in ("peer", "routecollector", "routeserver", "transit"): # noqa: SIM102
348 if community_type in (
349 "bgp_default",
350 "bgp_own_default",
351 "bgp_peering",
352 "bgp_transit",
353 "bgp_transit_default",
354 "default",
355 "kernel_default",
356 "originated_default",
357 "static_default",
358 ):
359 raise BirdPlanError(
360 f"Having 'outgoing_communities:{community_type}' specified for peer '{self.name}' "
361 f"with type '{self.peer_type}' makes no sense"
362 )
363 if self.peer_type not in ( # noqa: SIM102
364 "internal",
365 "routeserver",
366 "routecollector",
367 "rrclient",
368 "rrserver",
369 "rrserver-rrserver",
370 "transit",
371 ):
372 if community_type in (
373 "bgp_blackhole",
374 "bgp_customer_blackhole",
375 "bgp_own_blackhole",
376 "blackhole",
377 "kernel_blackhole",
378 "static_blackhole",
379 ):
380 raise BirdPlanError(
381 f"Having 'outgoing_communities:{community_type}' specified for peer '{self.name}' "
382 f"with type '{self.peer_type}' makes no sense"
383 )
384 # Exclude virtual options "bgp" and "default" from being set
385 if community_type not in ("bgp", "bgp_blackhole", "bgp_default", "blackhole", "default"):
386 # Set the community list
387 setattr(self.communities.outgoing, community_type, util.sanitize_community_list(community_config))
388 # If its just a number set the count
389 else:
390 community_option = util.sanitize_community_list(peer_config["outgoing_communities"])
391 self.communities.outgoing.connected = community_option
392 self.communities.outgoing.kernel = community_option
393 self.communities.outgoing.kernel_default = community_option
394 self.communities.outgoing.kernel_blackhole = community_option
395 self.communities.outgoing.originated = community_option
396 self.communities.outgoing.originated_default = community_option
397 self.communities.outgoing.static = community_option
398 self.communities.outgoing.static_blackhole = community_option
399 self.communities.outgoing.static_default = community_option
400 self.communities.outgoing.bgp_customer = community_option
401 self.communities.outgoing.bgp_customer_blackhole = community_option
402 self.communities.outgoing.bgp_own = community_option
403 self.communities.outgoing.bgp_own_blackhole = community_option
404 self.communities.outgoing.bgp_own_default = community_option
405 self.communities.outgoing.bgp_peering = community_option
406 self.communities.outgoing.bgp_transit = community_option
407 self.communities.outgoing.bgp_transit_default = community_option
409 #
410 # bgp:peers:$PEER:outgoing_large_communities
411 #
413 # Check for outgoing_large_communities we need to setup
414 if "outgoing_large_communities" in peer_config:
415 # Add outgoing_large_communities configuration
416 if isinstance(peer_config["outgoing_large_communities"], dict):
417 # Check if we're adding large communities to blackhole routes
418 if "blackhole" in peer_config["outgoing_large_communities"]:
419 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["blackhole"])
420 self.large_communities.outgoing.kernel_blackhole = lc_option
421 self.large_communities.outgoing.static_blackhole = lc_option
422 self.large_communities.outgoing.bgp_customer_blackhole = lc_option
423 self.large_communities.outgoing.bgp_own_blackhole = lc_option
424 # Check if we're adding large communities to default routes
425 if "default" in peer_config["outgoing_large_communities"]:
426 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["default"])
427 self.large_communities.outgoing.kernel_default = lc_option
428 self.large_communities.outgoing.originated_default = lc_option
429 self.large_communities.outgoing.static_default = lc_option
430 self.large_communities.outgoing.bgp_own_default = lc_option
431 self.large_communities.outgoing.bgp_transit_default = lc_option
432 # Check if we're adding outgoing large communitiesing to all BGP routes
433 if "bgp" in peer_config["outgoing_large_communities"]:
434 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp"])
435 self.large_communities.outgoing.bgp_customer = lc_option
436 self.large_communities.outgoing.bgp_customer_blackhole = lc_option
437 self.large_communities.outgoing.bgp_own = lc_option
438 self.large_communities.outgoing.bgp_own_blackhole = lc_option
439 self.large_communities.outgoing.bgp_own_default = lc_option
440 self.large_communities.outgoing.bgp_peering = lc_option
441 self.large_communities.outgoing.bgp_transit = lc_option
442 self.large_communities.outgoing.bgp_transit_default = lc_option
443 # Check if we're adding outgoing large communities to all BGP blackhole routes
444 if "bgp_blackhole" in peer_config["outgoing_large_communities"]:
445 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp_blackhole"])
446 self.large_communities.outgoing.bgp_customer_blackhole = lc_option
447 self.large_communities.outgoing.bgp_own_blackhole = lc_option
448 # Check if we're adding outgoing large communities to all BGP default routes
449 if "bgp_default" in peer_config["outgoing_large_communities"]:
450 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp_default"])
451 self.large_communities.outgoing.bgp_own_default = lc_option
452 self.large_communities.outgoing.bgp_transit_default = lc_option
453 # Check if we're adding outgoing large communities to all customer BGP routes
454 if "bgp_customer" in peer_config["outgoing_large_communities"]:
455 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp_customer"])
456 self.large_communities.outgoing.bgp_customer_blackhole = lc_option
457 # Check if we're adding outgoing large communities to all our own BGP routes
458 if "bgp_own" in peer_config["outgoing_large_communities"]:
459 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp_own"])
460 self.large_communities.outgoing.bgp_own_blackhole = lc_option
461 self.large_communities.outgoing.bgp_own_default = lc_option
462 # Check if we're adding outgoing large communities to all transit BGP routes
463 if "bgp_transit" in peer_config["outgoing_large_communities"]:
464 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["bgp_transit"])
465 self.large_communities.outgoing.bgp_transit_default = lc_option
466 # Check if we're adding outgoing large communities to all kernel routes
467 if "kernel" in peer_config["outgoing_large_communities"]:
468 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["kernel"])
469 self.large_communities.outgoing.kernel_blackhole = lc_option
470 self.large_communities.outgoing.kernel_default = lc_option
471 # Check if we're adding outgoing large communities to all originated routes
472 if "originated" in peer_config["outgoing_large_communities"]:
473 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["originated"])
474 self.large_communities.outgoing.originated_default = lc_option
475 # Check if we're adding outgoing large communities to all static routes
476 if "static" in peer_config["outgoing_large_communities"]:
477 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"]["static"])
478 self.large_communities.outgoing.static_blackhole = lc_option
479 self.large_communities.outgoing.static_default = lc_option
481 for lc_type, lc_config in peer_config["outgoing_large_communities"].items():
482 if lc_type not in (
483 "bgp",
484 "bgp_blackhole",
485 "bgp_customer_blackhole",
486 "bgp_customer",
487 "bgp_default",
488 "bgp_own_blackhole",
489 "bgp_own_default",
490 "bgp_own",
491 "bgp_peering",
492 "bgp_transit_default",
493 "bgp_transit",
494 "blackhole",
495 "connected",
496 "default",
497 "kernel",
498 "kernel_blackhole",
499 "kernel_default",
500 "originated",
501 "originated_default",
502 "static",
503 "static_blackhole",
504 "static_default",
505 ):
506 raise BirdPlanError(
507 f"BGP peer 'outgoing_large_communities' configuration '{lc_type}' for peer '{self.name}' with type "
508 f"'{self.peer_type}' is invalid"
509 )
510 # Check that we're not doing something stupid
511 if self.peer_type in ("peer", "routecollector", "routeserver", "transit"): # noqa: SIM102
512 if lc_type in (
513 "bgp_default",
514 "bgp_own_default",
515 "bgp_peering",
516 "bgp_transit",
517 "bgp_transit_default",
518 "default",
519 "kernel_default",
520 "originated_default",
521 "static_default",
522 ):
523 raise BirdPlanError(
524 f"Having 'outgoing_large_communities:{lc_type}' specified for peer '{self.name}' "
525 f"with type '{self.peer_type}' makes no sense"
526 )
527 if self.peer_type not in ( # noqa: SIM102
528 "internal",
529 "routeserver",
530 "routecollector",
531 "rrclient",
532 "rrserver",
533 "rrserver-rrserver",
534 "transit",
535 ):
536 if lc_type in (
537 "bgp_blackhole",
538 "bgp_customer_blackhole",
539 "bgp_own_blackhole",
540 "blackhole",
541 "kernel_blackhole",
542 "static_blackhole",
543 ):
544 raise BirdPlanError(
545 f"Having 'outgoing_large_communities:{lc_type}' specified for peer '{self.name}' "
546 f"with type '{self.peer_type}' makes no sense"
547 )
548 # Exclude virtual options "bgp" and "default" from being set
549 if lc_type not in ("bgp", "bgp_blackhole", "bgp_default", "blackhole", "default"):
550 # Set the community list
551 setattr(self.large_communities.outgoing, lc_type, util.sanitize_community_list(lc_config))
552 # If its just a number set the count
553 else:
554 lc_option = util.sanitize_community_list(peer_config["outgoing_large_communities"])
555 self.large_communities.outgoing.connected = lc_option
556 self.large_communities.outgoing.kernel = lc_option
557 self.large_communities.outgoing.kernel_default = lc_option
558 self.large_communities.outgoing.kernel_blackhole = lc_option
559 self.large_communities.outgoing.originated = lc_option
560 self.large_communities.outgoing.originated_default = lc_option
561 self.large_communities.outgoing.static = lc_option
562 self.large_communities.outgoing.static_blackhole = lc_option
563 self.large_communities.outgoing.static_default = lc_option
564 self.large_communities.outgoing.bgp_customer = lc_option
565 self.large_communities.outgoing.bgp_customer_blackhole = lc_option
566 self.large_communities.outgoing.bgp_own = lc_option
567 self.large_communities.outgoing.bgp_own_blackhole = lc_option
568 self.large_communities.outgoing.bgp_own_default = lc_option
569 self.large_communities.outgoing.bgp_peering = lc_option
570 self.large_communities.outgoing.bgp_transit = lc_option
571 self.large_communities.outgoing.bgp_transit_default = lc_option
573 # Turn on passive mode for route reflectors and customers
574 if self.peer_type in ("customer", "rrclient"):
575 self.passive = True
576 # But allow it to be set manually
577 if "passive" in peer_config:
578 self.passive = peer_config["passive"]
580 # Default redistribution settings based on peer type
581 if self.peer_type in (
582 "customer",
583 "internal",
584 "routecollector",
585 "routeserver",
586 "rrclient",
587 "rrserver",
588 "rrserver-rrserver",
589 "peer",
590 "transit",
591 ):
592 self.route_policy_redistribute.bgp_own = True
593 self.route_policy_redistribute.bgp_customer = True
594 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"):
595 self.route_policy_redistribute.bgp_peering = True
596 self.route_policy_redistribute.bgp_transit = True
597 # Default redistribution settings based on route type
598 if self.peer_type in ("internal", "routecollector", "routeserver", "rrclient", "rrserver", "rrserver-rrserver", "transit"):
599 self.route_policy_redistribute.bgp_customer_blackhole = True
600 self.route_policy_redistribute.bgp_own_blackhole = True
601 if self.peer_type == "rrserver-rrserver":
602 self.route_policy_redistribute.bgp_own_default = True
603 self.route_policy_redistribute.bgp_transit_default = True
605 # Work out what we're going to be redistributing
606 if "redistribute" in peer_config:
607 # Check if we're disabling BGP redistribution
608 if "bgp" in peer_config["redistribute"] and not peer_config["redistribute"]["bgp"]:
609 self.route_policy_redistribute.bgp_customer = False
610 self.route_policy_redistribute.bgp_own = False
611 self.route_policy_redistribute.bgp_peering = False
612 self.route_policy_redistribute.bgp_transit = False
613 self.route_policy_redistribute.bgp_customer_blackhole = False
614 self.route_policy_redistribute.bgp_own_blackhole = False
615 self.route_policy_redistribute.bgp_own_default = False
616 self.route_policy_redistribute.bgp_transit_default = False
617 # Disable customer blackhole routes by default if we're not redistributing customer BGP routes
618 if "bgp_customer" in peer_config["redistribute"] and not peer_config["redistribute"]["bgp_customer"]:
619 self.route_policy_redistribute.bgp_customer_blackhole = False
620 # Disable own blackhole and default routes by default if we're not redistributing our own BGP routes
621 if "bgp_own" in peer_config["redistribute"] and not peer_config["redistribute"]["bgp_own"]:
622 self.route_policy_redistribute.bgp_own_blackhole = False
623 self.route_policy_redistribute.bgp_own_default = False
624 # Disable transit default routes by default if we're not redistributing transit BGP routes
625 if "bgp_transit" in peer_config["redistribute"] and not peer_config["redistribute"]["bgp_transit"]:
626 self.route_policy_redistribute.bgp_transit_default = False
628 # Process each option individually now...
629 for redistribute_type, redistribute_config in peer_config["redistribute"].items():
630 if redistribute_type not in (
631 "bgp",
632 "bgp_customer",
633 "bgp_customer_blackhole",
634 "bgp_own",
635 "bgp_own_blackhole",
636 "bgp_own_default",
637 "bgp_peering",
638 "bgp_transit",
639 "bgp_transit_default",
640 "connected",
641 "kernel",
642 "kernel_blackhole",
643 "kernel_default",
644 "originated",
645 "originated_default",
646 "static",
647 "static_blackhole",
648 "static_default",
649 ):
650 raise BirdPlanError(f"The BGP redistribute type '{redistribute_type}' is not known")
651 # "bgp" is set above as defaults
652 if redistribute_type != "bgp":
653 setattr(self.route_policy_redistribute, redistribute_type, redistribute_config)
654 # Do a sanity check on our redistribution
655 if self.peer_type not in (
656 "internal",
657 "routeserver",
658 "routecollector",
659 "rrclient",
660 "rrserver",
661 "rrserver-rrserver",
662 "transit",
663 ):
664 # We should not redistribute blackhole routes to peers types customer and peer
665 if self.route_policy_redistribute.kernel_blackhole:
666 raise BirdPlanError(
667 f"Having 'redistribute:kernel_blackhole' set to True for peer '{self.name}' with type '{self.peer_type}' "
668 "makes no sense"
669 )
670 if self.route_policy_redistribute.static_blackhole:
671 raise BirdPlanError(
672 f"Having 'redistribute:static_blackhole' set to True for peer '{self.name}' with type '{self.peer_type}' "
673 "makes no sense"
674 )
675 if self.route_policy_redistribute.bgp_customer_blackhole:
676 raise BirdPlanError(
677 f"Having 'redistribute:bgp_customer_blackhole' set to True for peer '{self.name}' "
678 f"with type '{self.peer_type}' makes no sense"
679 )
680 if self.route_policy_redistribute.bgp_own_blackhole:
681 raise BirdPlanError(
682 f"Having 'redistribute:bgp_own_blackhole' set to True for peer '{self.name}' "
683 f"with type '{self.peer_type}' makes no sense"
684 )
685 if self.peer_type in ("peer", "routecollector", "routeserver", "transit"):
686 # We should not be distributing default routes to non-customer eBGP peers
687 if self.route_policy_redistribute.kernel_default:
688 raise BirdPlanError(
689 f"Having 'redistribute:kernel_default' set to True for peer '{self.name}' with type '{self.peer_type}' makes "
690 "no sense"
691 )
692 if self.route_policy_redistribute.originated_default:
693 raise BirdPlanError(
694 f"Having 'redistribute:originated_default' set to True for peer '{self.name}' with type '{self.peer_type}' "
695 "makes no sense"
696 )
697 if self.route_policy_redistribute.static_default:
698 raise BirdPlanError(
699 f"Having 'redistribute:static_default' set to True for peer '{self.name}' with type '{self.peer_type}' makes "
700 "no sense"
701 )
702 # We should not be redistributing peering routes or transit routes to non-customer eBGP peers
703 if self.route_policy_redistribute.bgp_peering:
704 raise BirdPlanError(
705 f"Having 'redistribute:bgp_peering' set to True for peer '{self.name}' "
706 f"with type '{self.peer_type}' makes no sense"
707 )
708 if self.route_policy_redistribute.bgp_transit:
709 raise BirdPlanError(
710 f"Having 'redistribute:bgp_transit' set to True for peer '{self.name}' "
711 f"with type '{self.peer_type}' makes no sense"
712 )
713 # We should not redistribute default routes to non customer eBGP peer types
714 if self.route_policy_redistribute.bgp_own_default:
715 raise BirdPlanError(
716 f"Having 'redistribute:bgp_own_default' set to True for peer '{self.name}' "
717 f"with type '{self.peer_type}' makes no sense"
718 )
719 if self.route_policy_redistribute.bgp_transit_default:
720 raise BirdPlanError(
721 f"Having 'redistribute:bgp_transit_default' set to True for peer '{self.name}' "
722 f"with type '{self.peer_type}' makes no sense"
723 )
724 # Check that we have static routes imported first
725 if self.route_policy_redistribute.connected and not self.bgp_attributes.route_policy_import.connected:
726 raise BirdPlanError(f"BGP needs connected routes to be imported before they can be redistributed to peer '{self.name}'")
728 # Check that we have static routes imported first
729 if self.route_policy_redistribute.kernel and not self.bgp_attributes.route_policy_import.kernel:
730 raise BirdPlanError(f"BGP needs kernel routes to be imported before they can be redistributed to peer '{self.name}'")
732 # Check that we have static routes imported first
733 if self.route_policy_redistribute.static and not self.bgp_attributes.route_policy_import.static:
734 raise BirdPlanError(f"BGP needs static routes to be imported before they can be redistributed to peer '{self.name}'")
736 # Check if we have the prefix limit action defined for this peer
737 if "prefix_limit_action" in peer_config:
738 prefix_limit_action = peer_config["prefix_limit_action"]
739 try:
740 self.prefix_limit_action = BGPPeerImportPrefixLimitAction(prefix_limit_action)
741 except ValueError:
742 raise BirdPlanError(f"The BGP peer prefix limit action '{prefix_limit_action}' is invalid") from None
744 # If the peer is a customer or peer, check if we have a prefix limit, if not add it from peeringdb
745 if self.peer_type in ("customer", "peer"):
746 if self.has_ipv4:
747 self.prefix_limit4 = peer_config.get("prefix_limit4", "peeringdb")
748 if self.has_ipv6:
749 self.prefix_limit6 = peer_config.get("prefix_limit6", "peeringdb")
750 # Having a prefix limit set for anything other than the above peer types makes no sense
751 else:
752 if "prefix_limit4" in peer_config:
753 raise BirdPlanError(
754 f"Having 'prefix_limit4' set for peer '{self.name}' with type '{self.peer_type}' makes no sense"
755 )
756 if "prefix_limit6" in peer_config:
757 raise BirdPlanError(
758 f"Having 'prefix_limit6' set for peer '{self.name}' with type '{self.peer_type}' makes no sense"
759 )
761 # Check for filters we need to setup
762 if "import_filter" in peer_config:
763 # Raise an exception if filters makes no sense for this peer type
764 if self.peer_type == "routecollector":
765 raise BirdPlanError(
766 f"Having 'import_filter' specified for peer '{self.name}' with type '{self.peer_type}' makes no sense"
767 )
768 # Add filters
769 for filter_type, filter_config in peer_config["import_filter"].items():
770 if filter_type not in ("as_sets", "aspath_asns", "origin_asns", "peer_asns", "prefixes"):
771 raise BirdPlanError(
772 f"BGP peer 'import_filter' configuration '{filter_type}' for peer '{self.name}' with type "
773 f"'{self.peer_type}' is invalid"
774 )
775 # Set filter policy
776 setattr(self.import_filter_policy, filter_type, filter_config)
778 # Check for filters we need to setup to out right deny
779 if "import_filter_deny" in peer_config:
780 # Raise an exception if filters makes no sense for this peer type
781 if self.peer_type == "routecollector":
782 raise BirdPlanError(
783 f"Having 'import_filter_deny' specified for peer '{self.name}' with type '{self.peer_type}' makes no sense"
784 )
785 # Add filters
786 for filter_type, filter_config in peer_config["import_filter_deny"].items():
787 if filter_type not in ("aspath_asns", "origin_asns", "prefixes"):
788 raise BirdPlanError(
789 f"BGP peer 'import_filter_deny' configuration '{filter_type}' for peer '{self.name}' with type "
790 f"'{self.peer_type}' is invalid"
791 )
792 # Set filter policy
793 setattr(self.import_filter_deny_policy, filter_type, filter_config)
795 # Check for filters we need to setup
796 if "export_filter" in peer_config:
797 # Add filters
798 for filter_type, filter_config in peer_config["export_filter"].items():
799 if filter_type not in ("origin_asns", "prefixes"):
800 raise BirdPlanError(
801 f"BGP peer 'export_filter' configuration '{filter_type}' for peer '{self.name}' with type "
802 f"'{self.peer_type}' is invalid"
803 )
804 # Set filter policy
805 setattr(self.export_filter_policy, filter_type, filter_config)
807 #
808 # bgp:peers:$PPER:accept
809 #
811 # Check if we should accept our own blackhole routes
812 if self.peer_type in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
813 self.route_policy_accept.bgp_own_blackhole = True
814 self.route_policy_accept.bgp_customer_blackhole = True
815 # Check if we should be accepting customer blackhole routes
816 if self.peer_type == "customer" and self.has_import_prefix_filter:
817 self.route_policy_accept.bgp_customer_blackhole = True
819 # If this is a rrserver to rrserver peer, we need to by default redistribute the default route (if we have one)
820 if self.peer_type == "rrserver-rrserver":
821 self.route_policy_accept.bgp_own_default = True
822 self.route_policy_accept.bgp_transit_default = True
824 # Get peer configuration and set our attributes
825 if "accept" in peer_config:
826 for accept_type, accept_config in peer_config["accept"].items():
827 if accept_type not in ("bgp_customer_blackhole", "bgp_own_blackhole", "bgp_own_default", "bgp_transit_default"):
828 raise BirdPlanError(
829 f"BGP peer 'accept' configuration '{accept_type}' for peer '{self.name}' with type '{self.peer_type}'"
830 " is invalid"
831 )
832 # Set route policy accept
833 setattr(self.route_policy_accept, accept_type, accept_config)
835 # Check bgp:accept:bgp_customer_blackhole
836 if self.peer_type not in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
837 if self.route_policy_accept.bgp_customer_blackhole:
838 raise BirdPlanError(
839 f"Having 'accept:bgp_customer_blackhole' set to True for peer '{self.name}' with type '{self.peer_type}' "
840 "makes no sense"
841 )
842 # Internal peer type checks
843 if self.peer_type not in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
844 # Check bgp:accept:bgp_own_blackhole
845 if self.route_policy_accept.bgp_own_blackhole:
846 raise BirdPlanError(
847 f"Having 'accept:bgp_own_blackhole' set to True for peer '{self.name}' with type '{self.peer_type}' "
848 "makes no sense"
849 )
850 # Check bgp:accept:bgp_own_blackhole
851 if self.route_policy_accept.bgp_own_default:
852 raise BirdPlanError(
853 f"Having 'accept:bgp_own_default' set to True for peer '{self.name}' with type '{self.peer_type}' "
854 "makes no sense"
855 )
856 # Check bgp:accept:bgp_transit_default
857 if self.peer_type not in ("internal", "rrclient", "rrserver", "rrserver-rrserver", "transit"): # noqa: SIM102
858 if self.route_policy_accept.bgp_transit_default:
859 raise BirdPlanError(
860 f"Having 'accept:bgp_transit_default' set to True for peer '{self.name}' with type '{self.peer_type}' "
861 "makes no sense"
862 )
863 # Check if this is a customer with blackholing and without a prefix filter set
864 if self.peer_type == "customer": # noqa: SIM102
865 if self.route_policy_accept.bgp_customer_blackhole and not self.has_import_prefix_filter:
866 raise BirdPlanError(
867 f"Having 'accept:bgp_customer_blackhole' set to True for peer '{self.name}' with type '{self.peer_type}' "
868 "and without 'filter:prefixes' or 'filter:as_sets' set makes no sense"
869 )
871 #
872 # bgp:peers:$PEER:prepend
873 #
875 # Check for prepending we need to setup
876 if "prepend" in peer_config:
877 # Add prepending configuration
878 if isinstance(peer_config["prepend"], dict):
879 # Check if we're prepending blackhole routes
880 if "blackhole" in peer_config["prepend"]:
881 prepend_option = peer_config["prepend"]["blackhole"]
882 self.prepend.kernel_blackhole.own_asn = prepend_option
883 self.prepend.static_blackhole.own_asn = prepend_option
884 self.prepend.bgp_customer_blackhole.own_asn = prepend_option
885 self.prepend.bgp_own_blackhole.own_asn = prepend_option
886 # Check if we're prepending default routes
887 if "default" in peer_config["prepend"]:
888 prepend_option = peer_config["prepend"]["default"]
889 self.prepend.kernel_default.own_asn = prepend_option
890 self.prepend.originated_default.own_asn = prepend_option
891 self.prepend.static_default.own_asn = prepend_option
892 self.prepend.bgp_own_default.own_asn = prepend_option
893 self.prepend.bgp_transit_default.own_asn = prepend_option
894 # Check if we're prepending all BGP routes
895 if "bgp" in peer_config["prepend"]:
896 prepend_option = peer_config["prepend"]["bgp"]
897 self.prepend.bgp_customer.own_asn = prepend_option
898 self.prepend.bgp_customer_blackhole.own_asn = prepend_option
899 self.prepend.bgp_own.own_asn = prepend_option
900 self.prepend.bgp_own_blackhole.own_asn = prepend_option
901 self.prepend.bgp_own_default.own_asn = prepend_option
902 self.prepend.bgp_peering.own_asn = prepend_option
903 self.prepend.bgp_transit.own_asn = prepend_option
904 self.prepend.bgp_transit_default.own_asn = prepend_option
905 # Check if we're prepending all BGP blackhole routes
906 if "bgp_blackhole" in peer_config["prepend"]:
907 prepend_option = peer_config["prepend"]["bgp_blackhole"]
908 self.prepend.bgp_customer_blackhole.own_asn = prepend_option
909 self.prepend.bgp_own_blackhole.own_asn = prepend_option
910 # Check if we're prepending all BGP default routes
911 if "bgp_default" in peer_config["prepend"]:
912 prepend_option = peer_config["prepend"]["bgp_default"]
913 self.prepend.bgp_transit_default.own_asn = prepend_option
914 self.prepend.bgp_own_default.own_asn = prepend_option
915 # Check if we're prepending all customer BGP routes
916 if "bgp_customer" in peer_config["prepend"]:
917 prepend_option = peer_config["prepend"]["bgp_customer"]
918 self.prepend.bgp_customer_blackhole.own_asn = prepend_option
919 # Check if we're prepending all our own BGP routes
920 if "bgp_own" in peer_config["prepend"]:
921 prepend_option = peer_config["prepend"]["bgp_own"]
922 self.prepend.bgp_own_blackhole.own_asn = prepend_option
923 self.prepend.bgp_own_default.own_asn = prepend_option
924 # Check if we're prepending all transit BGP routes
925 if "bgp_transit" in peer_config["prepend"]:
926 prepend_option = peer_config["prepend"]["bgp_transit"]
927 self.prepend.bgp_transit_default.own_asn = prepend_option
928 # Check if we're prepending all kernel routes
929 if "kernel" in peer_config["prepend"]:
930 prepend_option = peer_config["prepend"]["kernel"]
931 self.prepend.kernel_blackhole.own_asn = prepend_option
932 self.prepend.kernel_default.own_asn = prepend_option
933 # Check if we're prepending all originated routes
934 if "originated" in peer_config["prepend"]:
935 prepend_option = peer_config["prepend"]["originated"]
936 self.prepend.originated_default.own_asn = prepend_option
937 # Check if we're prepending all static routes
938 if "static" in peer_config["prepend"]:
939 prepend_option = peer_config["prepend"]["static"]
940 self.prepend.static_blackhole.own_asn = prepend_option
941 self.prepend.static_default.own_asn = prepend_option
943 for prepend_type, prepend_config in peer_config["prepend"].items():
944 if prepend_type not in (
945 "bgp",
946 "bgp_blackhole",
947 "bgp_customer",
948 "bgp_customer_blackhole",
949 "bgp_default",
950 "bgp_own",
951 "bgp_own_blackhole",
952 "bgp_own_default",
953 "bgp_peering",
954 "bgp_transit",
955 "bgp_transit_default",
956 "blackhole",
957 "connected",
958 "default",
959 "kernel",
960 "kernel_blackhole",
961 "kernel_default",
962 "originated",
963 "originated_default",
964 "static",
965 "static_blackhole",
966 "static_default",
967 ):
968 raise BirdPlanError(
969 f"BGP peer 'prepend' configuration '{prepend_type}' for peer '{self.name}' with type "
970 f"'{self.peer_type}' is invalid"
971 )
972 # Check that we're not doing something stupid
973 if self.peer_type in ("peer", "routecollector", "routeserver", "transit"): # noqa: SIM102
974 if prepend_type in (
975 "bgp_default",
976 "default",
977 "kernel_default",
978 "static_default",
979 "originated_default",
980 "bgp_own_default",
981 "bgp_peering",
982 "bgp_transit",
983 "bgp_transit_default",
984 ):
985 raise BirdPlanError(
986 f"Having 'prepend:{prepend_type}' specified for peer '{self.name}' with type '{self.peer_type}' "
987 "makes no sense"
988 )
989 if self.peer_type not in ( # noqa: SIM102
990 "internal",
991 "routeserver",
992 "routecollector",
993 "rrclient",
994 "rrserver",
995 "rrserver-rrserver",
996 "transit",
997 ):
998 if prepend_type in (
999 "bgp_blackhole",
1000 "blackhole",
1001 "kernel_blackhole",
1002 "static_blackhole",
1003 "bgp_customer_blackhole",
1004 "bgp_own_blackhole",
1005 ):
1006 raise BirdPlanError(
1007 f"Having 'prepend:{prepend_type}' specified for peer '{self.name}' with type '{self.peer_type}' "
1008 "makes no sense"
1009 )
1010 # Exclude virtual options "bgp" and "default" from being set
1011 if prepend_type not in ("bgp", "bgp_blackhole", "bgp_default", "blackhole", "default"):
1012 # Grab the prepend attribute
1013 prepend_attr = getattr(self.prepend, prepend_type)
1014 # Set prepend count
1015 setattr(prepend_attr, "own_asn", prepend_config) # noqa: B010
1016 # If its just a number set the count
1017 else:
1018 prepend_option = peer_config["prepend"]
1019 self.prepend.bgp_customer.own_asn = prepend_option
1020 self.prepend.bgp_customer_blackhole.own_asn = prepend_option
1021 self.prepend.bgp_own.own_asn = prepend_option
1022 self.prepend.bgp_own_blackhole.own_asn = prepend_option
1023 self.prepend.bgp_own_default.own_asn = prepend_option
1024 self.prepend.bgp_peering.own_asn = prepend_option
1025 self.prepend.bgp_transit.own_asn = prepend_option
1026 self.prepend.bgp_transit_default.own_asn = prepend_option
1027 self.prepend.connected.own_asn = prepend_option
1028 self.prepend.kernel.own_asn = prepend_option
1029 self.prepend.kernel_blackhole.own_asn = prepend_option
1030 self.prepend.kernel_default.own_asn = prepend_option
1031 self.prepend.originated.own_asn = prepend_option
1032 self.prepend.originated_default.own_asn = prepend_option
1033 self.prepend.static.own_asn = prepend_option
1034 self.prepend.static_blackhole.own_asn = prepend_option
1035 self.prepend.static_default.own_asn = prepend_option
1037 # Check if we have a blackhole community
1038 if "blackhole_community" in peer_config:
1039 if self.peer_type not in ("routeserver", "routecollector", "transit"):
1040 raise BirdPlanError(
1041 f"Having 'blackhole_community' specified for peer '{self.name}' with type '{self.peer_type}' makes no sense"
1042 )
1043 # List of communities to add...
1044 communities = []
1045 # Check if this is a plain string community
1046 if isinstance(peer_config["blackhole_community"], str):
1047 communities.append(peer_config["blackhole_community"])
1048 # Check if this is a list of communities
1049 elif isinstance(peer_config["blackhole_community"], list):
1050 communities.extend(peer_config["blackhole_community"])
1051 # Finally if this is not a boolean, then its unsupported
1052 elif isinstance(peer_config["blackhole_community"], bool):
1053 self.blackhole_community = peer_config["blackhole_community"]
1054 else:
1055 raise BirdPlanError(
1056 f"Option 'blackhole_community' specified for peer '{self.name}' with type '{self.peer_type}' "
1057 f"has an invalid type"
1058 )
1059 # If we have communities, this can be a list or a single item in the list
1060 if communities:
1061 # Initialize our configuration list
1062 self.blackhole_community = []
1063 # Loop with each community
1064 for community in communities:
1065 # Check how many :'s we have
1066 component_count = community.count(":")
1067 if component_count < 1 or component_count > 2:
1068 raise BirdPlanError(
1069 f"Option 'blackhole_community' specified for peer '{self.name}' with type '{self.peer_type}' "
1070 f"has an invalid value '{community}'"
1071 )
1072 # NK: make linting happy
1073 if not isinstance(self.blackhole_community, list):
1074 pass
1075 # Add to our configuration
1076 self.blackhole_community.append(util.sanitize_community(community))
1078 # Setup our constraint overrides
1079 if "constraints" in peer_config:
1080 for constraint_name, constraint_value in peer_config["constraints"].items():
1081 if constraint_name not in (
1082 "blackhole_import_maxlen4",
1083 "blackhole_import_minlen4",
1084 "blackhole_export_maxlen4",
1085 "blackhole_export_minlen4",
1086 "blackhole_import_maxlen6",
1087 "blackhole_import_minlen6",
1088 "blackhole_export_maxlen6",
1089 "blackhole_export_minlen6",
1090 "import_maxlen4",
1091 "import_minlen4",
1092 "export_maxlen4",
1093 "export_minlen4",
1094 "import_maxlen6",
1095 "import_minlen6",
1096 "export_maxlen6",
1097 "export_minlen6",
1098 "aspath_import_maxlen",
1099 "aspath_import_minlen",
1100 "community_import_maxlen",
1101 "extended_community_import_maxlen",
1102 "large_community_import_maxlen",
1103 ):
1104 raise BirdPlanError(
1105 f"BGP peer 'constraints' configuration '{constraint_name}' for peer '{self.name}' "
1106 f"with type '{self.peer_type}' is invalid"
1107 )
1108 # Make sure this peer supports blackhole imports
1109 if constraint_name.startswith("blackhole_import_"): # noqa: SIM102
1110 if self.peer_type not in (
1111 "customer",
1112 "internal",
1113 "rrclient",
1114 "rrserver",
1115 "rrserver-rrserver",
1116 ):
1117 raise BirdPlanError(
1118 f"Having '{constraint_name}' specified for peer '{self.name}' "
1119 f"with type '{self.peer_type}' makes no sense"
1120 )
1121 # Make sure this peer accepts blackhole exports
1122 if constraint_name.startswith("blackhole_export_"): # noqa: SIM102
1123 if self.peer_type not in (
1124 "internal",
1125 "routeserver",
1126 "routecollector",
1127 "rrclient",
1128 "rrserver",
1129 "rrserver-rrserver",
1130 "transit",
1131 ):
1132 raise BirdPlanError(
1133 f"Having '{constraint_name}' specified for peer '{self.name}' "
1134 f"with type '{self.peer_type}' makes no sense"
1135 )
1136 # Make sure this peer supports imports
1137 if "import" in constraint_name: # noqa: SIM102
1138 if self.peer_type not in (
1139 "customer",
1140 "internal",
1141 "peer",
1142 "routeserver",
1143 "rrclient",
1144 "rrserver",
1145 "rrserver-rrserver",
1146 "transit",
1147 ):
1148 raise BirdPlanError(
1149 f"Having '{constraint_name}' specified for peer '{self.name}' "
1150 f"with type '{self.peer_type}' makes no sense"
1151 )
1152 # Set constraint
1153 setattr(self.constraints, constraint_name, constraint_value)
1155 # Check if we're in graceful_shutdown mode
1156 if self.bgp_attributes.graceful_shutdown:
1157 self.graceful_shutdown = True
1158 if "graceful_shutdown" in peer_config:
1159 self.graceful_shutdown = peer_config["graceful_shutdown"]
1161 # Check if we're quarantined
1162 if self.bgp_attributes.quarantine:
1163 self.quarantine = True
1164 if "quarantine" in peer_config:
1165 self.quarantine = peer_config["quarantine"]
1167 #
1168 # NETWORK AND STATE (CACHE) RELATED QUERIES
1169 #
1171 # Work out the prefix limits...
1172 if self.prefix_limit4 == "peeringdb" or self.prefix_limit6 == "peeringdb":
1173 # Setup our peeringdb info
1174 peeringdb_info: Dict[str, Any] = {}
1176 # Check if we're using cached values or not
1177 if self.birdconfig_globals.use_cached:
1178 if not self.birdconfig_globals.suppress_info:
1179 logging.info("[bgp:peer:%s] Using cached PeeringDB information for prefix limits", self.name)
1180 # Check if we're pulling the IPv4 limits out our cache
1181 if self.prefix_limit4 == "peeringdb":
1182 if not (
1183 self.prev_state
1184 and "prefix_limit" in self.prev_state
1185 and "peeringdb" in self.prev_state["prefix_limit"]
1186 and "ipv4" in self.prev_state["prefix_limit"]["peeringdb"]
1187 ):
1188 raise BirdPlanError(
1189 f"No PeeringDB information in cache for peer '{self.name}' "
1190 f"with type '{self.peer_type}' for IPv4 prefix limit"
1191 )
1192 # Pull entry from cache
1193 peeringdb_info["info_prefixes4"] = self.prev_state["prefix_limit"]["peeringdb"]["ipv4"]
1194 # Check if we're pulling the IPv6 limits out our cache
1195 if self.prefix_limit6 == "peeringdb":
1196 if not (
1197 self.prev_state
1198 and "prefix_limit" in self.prev_state
1199 and "peeringdb" in self.prev_state["prefix_limit"]
1200 and "ipv6" in self.prev_state["prefix_limit"]["peeringdb"]
1201 ):
1202 raise BirdPlanError(
1203 f"No PeeringDB information in cache for peer '{self.name}' "
1204 f"with type '{self.peer_type}' for IPv6 prefix limit"
1205 )
1206 # Pull entry from cache
1207 peeringdb_info["info_prefixes6"] = self.prev_state["prefix_limit"]["peeringdb"]["ipv6"]
1208 else:
1209 if not self.birdconfig_globals.suppress_info:
1210 logging.info("[bgp:peer:%s] Retrieving prefix limits from PeeringDB", self.name)
1212 # Grab PeeringDB entries
1213 peeringdb = PeeringDB()
1214 peeringdb_info = peeringdb.get_prefix_limits(self.asn)
1216 # Make sure we got IPv4 limits back from PeeringDB
1217 if not peeringdb_info["info_prefixes4"]:
1218 raise BirdPlanError(f"No IPv4 PeeringDB information found for peer '{self.name}' with type '{self.peer_type}'")
1220 # Make sure we got IPv6 limits back from PeeringDB
1221 if not peeringdb_info["info_prefixes6"]:
1222 raise BirdPlanError(f"No IPv6 PeeringDB information found for peer '{self.name}' with type '{self.peer_type}'")
1224 # Check if we're setting our IPv4 prefix limit from peeringdb
1225 if self.prefix_limit4 == "peeringdb":
1226 # Sanity checks
1227 if (
1228 not self.birdconfig_globals.ignore_peeringdb_changes
1229 and self.prev_state
1230 and "prefix_limit" in self.prev_state
1231 and "peeringdb" in self.prev_state["prefix_limit"]
1232 and "ipv4" in self.prev_state["prefix_limit"]["peeringdb"]
1233 ):
1234 # Check if there was a substantial reduction in number of prefixes allowed
1235 if peeringdb_info["info_prefixes4"] * 2 < self.prev_state["prefix_limit"]["peeringdb"]["ipv4"]:
1236 raise BirdPlanError(
1237 f"PeeringDB IPv4 prefix limit for peer '{self.name}' with type '{self.peer_type}' "
1238 "decreased substantially from previous run: "
1239 "last=%s, now=%s"
1240 % (self.prev_state["prefix_limit"]["peeringdb"]["ipv4"], peeringdb_info["info_prefixes4"])
1241 )
1242 # Check if there was a substantial increase in number of prefixes allowed
1243 if peeringdb_info["info_prefixes4"] / 2 > self.prev_state["prefix_limit"]["peeringdb"]["ipv4"]:
1244 raise BirdPlanError(
1245 f"PeeringDB IPv4 prefix limit for peer '{self.name}' with type '{self.peer_type}' "
1246 "increased substantially from previous run: "
1247 "last=%s, now=%s"
1248 % (self.prev_state["prefix_limit"]["peeringdb"]["ipv4"], peeringdb_info["info_prefixes4"])
1249 )
1250 # Set the limits
1251 self.prefix_limit4 = None
1252 self.prefix_limit4_peeringdb = peeringdb_info["info_prefixes4"]
1254 # Check if we're setting our IPv6 prefix limit from peeringdb
1255 if self.prefix_limit6 == "peeringdb":
1256 # Sanity checks
1257 if (
1258 not self.birdconfig_globals.ignore_peeringdb_changes
1259 and self.prev_state
1260 and "prefix_limit" in self.prev_state
1261 and "peeringdb" in self.prev_state["prefix_limit"]
1262 and "ipv6" in self.prev_state["prefix_limit"]["peeringdb"]
1263 ):
1264 # Check if there was a substantial reduction in number of prefixes allowed
1265 if peeringdb_info["info_prefixes6"] * 2 < self.prev_state["prefix_limit"]["peeringdb"]["ipv6"]:
1266 raise BirdPlanError(
1267 f"PeeringDB IPv6 prefix limit for peer '{self.name}' with type '{self.peer_type}' "
1268 "decreased substantially from previous run: "
1269 "last=%s, now=%s"
1270 % (self.prev_state["prefix_limit"]["peeringdb"]["ipv6"], peeringdb_info["info_prefixes6"])
1271 )
1272 # Check if there was a substantial increase in number of prefixes allowed
1273 if peeringdb_info["info_prefixes6"] / 2 > self.prev_state["prefix_limit"]["peeringdb"]["ipv6"]:
1274 raise BirdPlanError(
1275 f"PeeringDB IPv6 prefix limit for peer '{self.name}' with type '{self.peer_type}' "
1276 "increased substantially from previous run: "
1277 "last=%s, now=%s"
1278 % (self.prev_state["prefix_limit"]["peeringdb"]["ipv6"], peeringdb_info["info_prefixes6"])
1279 )
1280 # Set the limits
1281 self.prefix_limit6 = None
1282 self.prefix_limit6_peeringdb = peeringdb_info["info_prefixes6"]
1284 # Check if we're going to be pulling in AS-SET information
1285 if self.import_filter_policy.as_sets:
1286 # Setup our IRR info
1287 irr_asns: List[str] = []
1288 irr_prefixes: Dict[str, Any] = {"ipv4": [], "ipv6": []}
1290 # Check if we're using cached values or not
1291 if self.birdconfig_globals.use_cached:
1292 if not self.birdconfig_globals.suppress_info:
1293 logging.info("[bgp:peer:%s] Using cached IRR information for AS-SETs", self.name)
1295 # Grab IRR ASNs from previous state
1296 if not (
1297 self.prev_state
1298 and "import_filter" in self.prev_state
1299 and "origin_asns" in self.prev_state["import_filter"]
1300 and "irr" in self.prev_state["import_filter"]["origin_asns"]
1301 ):
1302 raise BirdPlanError(
1303 f"No IRR information in cache for peer '{self.name}' " f"with type '{self.peer_type}' for IRR origin ASNs"
1304 )
1305 # Populate irr_asns
1306 irr_asns = self.prev_state["import_filter"]["origin_asns"]["irr"]
1308 # Grab IRR prefixes for IPv4 from previous state
1309 if not (
1310 self.prev_state
1311 and "import_filter" in self.prev_state
1312 and "prefixes" in self.prev_state["import_filter"]
1313 and "irr" in self.prev_state["import_filter"]["prefixes"]
1314 and "ipv4" in self.prev_state["import_filter"]["prefixes"]["irr"]
1315 ):
1316 raise BirdPlanError(
1317 f"No IRR information in cache for peer '{self.name}' " f"with type '{self.peer_type}' for IRR IPv4 prefixes"
1318 )
1319 # Populate IRR IPv4 prefixes
1320 irr_prefixes["ipv4"] = self.prev_state["import_filter"]["prefixes"]["irr"]["ipv4"]
1322 # Grab IRR prefixes for IPv6 from previous state
1323 if not (
1324 self.prev_state
1325 and "import_filter" in self.prev_state
1326 and "prefixes" in self.prev_state["import_filter"]
1327 and "irr" in self.prev_state["import_filter"]["prefixes"]
1328 and "ipv6" in self.prev_state["import_filter"]["prefixes"]["irr"]
1329 ):
1330 raise BirdPlanError(
1331 f"No IRR information in cache for peer '{self.name}' " f"with type '{self.peer_type}' for IRR IPv6 prefixes"
1332 )
1333 # Populate IRR IPv6 prefixes
1334 irr_prefixes["ipv6"] = self.prev_state["import_filter"]["prefixes"]["irr"]["ipv6"]
1336 else:
1337 if not self.birdconfig_globals.suppress_info:
1338 logging.info("[bgp:peer:%s] Retrieving IRR information for AS-SETs", self.name)
1340 # Grab BGPQ3 object to use below
1341 bgpq3 = BGPQ3()
1343 # Grab ASNs from IRR
1344 irr_asns = bgpq3.get_asns(self.import_filter_policy.as_sets)
1345 # Make sure we got IRR ASNs back from BGPQ3
1346 if not irr_asns:
1347 raise BirdPlanError(f"No IRR ASNs found for peer '{self.name}' with type '{self.peer_type}'")
1349 # Grab IRR prefixes
1350 irr_prefixes = bgpq3.get_prefixes(self.import_filter_policy.as_sets)
1351 # Make sure we got IRR prefixes back from BGPQ3
1352 if ("ipv4" not in irr_prefixes or not irr_prefixes["ipv4"]) and (
1353 "ipv6" not in irr_prefixes or not irr_prefixes["ipv6"]
1354 ):
1355 raise BirdPlanError(f"No IRR prefixes found for peer '{self.name}' with type '{self.peer_type}'")
1357 # Add our ASN's onto the filter policy origin ASNs list
1358 self.import_filter_policy.origin_asns_irr.extend(irr_asns)
1360 # Lets work out what to do with the IPv4 prefixes
1361 if irr_prefixes["ipv4"]:
1362 # Sanity checks for IPv4 network count
1363 if (
1364 not self.birdconfig_globals.ignore_irr_changes # pylint: disable=too-many-boolean-expressions
1365 and self.prev_state
1366 and "import_filter" in self.prev_state
1367 and "prefixes" in self.prev_state["import_filter"]
1368 and "irr" in self.prev_state["import_filter"]["prefixes"]
1369 and "ipv4" in self.prev_state["import_filter"]["prefixes"]["irr"]
1370 ):
1371 # Check if there was a substantial reduction in number of prefixes allowed
1372 new_network_count = util.network_count(irr_prefixes["ipv4"])
1373 old_network_count = util.network_count(self.prev_state["import_filter"]["prefixes"]["irr"]["ipv4"])
1374 if new_network_count * 2 < old_network_count:
1375 raise BirdPlanError(
1376 f"IRR IPv4 network count for peer '{self.name}' with type '{self.peer_type}' "
1377 "decreased substantially from previous run: "
1378 "last=%s, now=%s" % (old_network_count, new_network_count)
1379 )
1380 # Check if there was a substantial increase in number of prefixes allowed
1381 if new_network_count / 2 > old_network_count:
1382 raise BirdPlanError(
1383 f"IRR IPv4 network count for peer '{self.name}' with type '{self.peer_type}' "
1384 "increased substantially from previous run: "
1385 "last=%s, now=%s" % (old_network_count, new_network_count)
1386 )
1387 # All looks good, add them
1388 self.import_filter_policy.prefixes_irr.extend(irr_prefixes["ipv4"])
1390 # Lets work out what to do with the IPv6 prefixes
1391 if irr_prefixes["ipv6"]:
1392 # Sanity checks for IPv6 network count
1393 if (
1394 not self.birdconfig_globals.ignore_irr_changes # pylint: disable=too-many-boolean-expressions
1395 and self.prev_state
1396 and "import_filter" in self.prev_state
1397 and "prefixes" in self.prev_state["import_filter"]
1398 and "irr" in self.prev_state["import_filter"]["prefixes"]
1399 and "ipv6" in self.prev_state["import_filter"]["prefixes"]["irr"]
1400 ):
1401 # Check if there was a substantial reduction in number of prefixes allowed
1402 new_network_count = util.network_count(irr_prefixes["ipv6"])
1403 old_network_count = util.network_count(self.prev_state["import_filter"]["prefixes"]["irr"]["ipv6"])
1404 if new_network_count * 2 < old_network_count:
1405 raise BirdPlanError(
1406 f"IRR IPv6 network count for peer '{self.name}' with type '{self.peer_type}' "
1407 "decreased substantially from previous run: "
1408 "last=%s, now=%s" % (old_network_count, new_network_count)
1409 )
1410 # Check if there was a substantial increase in number of prefixes allowed
1411 if new_network_count / 2 > old_network_count:
1412 raise BirdPlanError(
1413 f"IRR IPv6 network count for peer '{self.name}' with type '{self.peer_type}' "
1414 "increased substantially from previous run: "
1415 "last=%s, now=%s" % (old_network_count, new_network_count)
1416 )
1417 # All looks good, add them
1418 self.import_filter_policy.prefixes_irr.extend(irr_prefixes["ipv6"])
1420 # Check if we have a graceful shutdown override
1421 if ("bgp" in self.birdconfig_globals.state) and ("+graceful_shutdown" in self.birdconfig_globals.state["bgp"]):
1422 # Then check if we have an explicit setting
1423 if self.name in self.birdconfig_globals.state["bgp"]["+graceful_shutdown"]:
1424 self.graceful_shutdown = self.birdconfig_globals.state["bgp"]["+graceful_shutdown"][self.name]
1425 # If not we process the patterns
1426 else:
1427 for item in sorted(self.birdconfig_globals.state["bgp"]["+graceful_shutdown"]):
1428 # Skip non patterns
1429 if "*" not in item:
1430 continue
1431 # If pattern matches peer name, set the value for graceful shutdown
1432 if fnmatch.fnmatch(self.name, item):
1433 self.graceful_shutdown = self.birdconfig_globals.state["bgp"]["+graceful_shutdown"][item]
1435 # Check if we have a quarantine override
1436 if ("bgp" in self.birdconfig_globals.state) and ("+quarantine" in self.birdconfig_globals.state["bgp"]):
1437 # Then check if we have an explicit setting
1438 if self.name in self.birdconfig_globals.state["bgp"]["+quarantine"]:
1439 self.quarantine = self.birdconfig_globals.state["bgp"]["+quarantine"][self.name]
1440 # If not we process the patterns
1441 else:
1442 for item in sorted(self.birdconfig_globals.state["bgp"]["+quarantine"]):
1443 # Skip non patterns
1444 if "*" not in item:
1445 continue
1446 # If pattern matches peer name, set the value for quarantine
1447 if fnmatch.fnmatch(self.name, item):
1448 self.quarantine = self.birdconfig_globals.state["bgp"]["+quarantine"][item]
1450 def configure(self) -> None: # noqa: CFQ001 # pylint: disable=too-many-branches,too-many-statements
1451 """Configure BGP peer."""
1453 if not self.birdconfig_globals.suppress_info:
1454 logging.info(colored("[bgp:peer:%s] Configuring peer: asn=%s, type=%s", "blue"), self.name, self.asn, self.peer_type)
1456 super().configure()
1458 # Save basic peer information
1459 self.state["asn"] = self.asn
1460 self.state["description"] = self.description
1461 self.state["type"] = self.peer_type
1463 # Work out what security settings are in play
1464 self.state["security"] = []
1465 if self.password:
1466 self.state["security"].append("password")
1467 if self.ttl_security:
1468 self.state["security"].append("ttl-security")
1470 self.state["import_filter"] = {}
1471 self.state["import_filter"]["as_sets"] = self.import_filter_policy.as_sets
1473 self.state["import_filter_deny"] = {}
1475 self.state["export_filter"] = {}
1477 # Check for some config options we also need to save
1478 if self.prefix_limit4:
1479 if "prefix_limit" not in self.state:
1480 self.state["prefix_limit"] = {}
1481 if self.prefix_limit4_peeringdb:
1482 # Make sure we have a peeringdb attribute
1483 if "peeringdb" not in self.state["prefix_limit"]:
1484 self.state["prefix_limit"]["peeringdb"] = {}
1485 # Add our peeringdb IPv4 limit
1486 self.state["prefix_limit"]["peeringdb"]["ipv4"] = self.prefix_limit4_peeringdb
1487 else:
1488 # Make sure we have a static attribute
1489 if "static" not in self.state["prefix_limit"]:
1490 self.state["prefix_limit"]["static"] = {}
1491 # Add our static IPv4 limit
1492 self.state["prefix_limit"]["static"]["ipv4"] = self.prefix_limit4
1493 if self.prefix_limit6:
1494 if "prefix_limit" not in self.state:
1495 self.state["prefix_limit"] = {}
1496 if self.prefix_limit6_peeringdb:
1497 # Make sure we have a peeringdb attribute
1498 if "peeringdb" not in self.state["prefix_limit"]:
1499 self.state["prefix_limit"]["peeringdb"] = {}
1500 # Add our peeringdb IPv6 limit
1501 self.state["prefix_limit"]["peeringdb"]["ipv6"] = self.prefix_limit6_peeringdb
1502 else:
1503 # Make sure we have a static attribute
1504 if "static" not in self.state["prefix_limit"]:
1505 self.state["prefix_limit"]["static"] = {}
1506 # Add our static IPv6 limit
1507 self.state["prefix_limit"]["static"]["ipv6"] = self.prefix_limit6
1509 # Make sure we keep our graceful shutdown state
1510 self.state["graceful_shutdown"] = self.graceful_shutdown
1512 # Make sure we keep our quarantine state
1513 self.state["quarantine"] = self.quarantine
1515 self.conf.add("")
1516 self.conf.add(f"# Peer type: {self.peer_type}")
1517 self.conf.add("")
1519 # Setup routing tables
1520 self._setup_peer_tables()
1522 # Setup filters
1523 self._setup_import_aspath_asns_filter()
1524 self._setup_import_origin_asns_filter()
1525 self._setup_export_origin_asns_filter()
1526 self._setup_import_peer_asns_filter()
1528 # Setup allowed prefixes
1529 self._setup_peer_import_prefix_filter()
1530 self._setup_peer_export_prefix_filter()
1532 # Setup deny filters
1533 self._setup_import_aspath_asns_deny_filter()
1534 self._setup_import_origin_asns_deny_filter()
1535 self._setup_peer_import_prefix_deny_filter()
1537 # BGP peer to main table
1538 self._setup_peer_to_bgp_filters()
1540 # BGP peer filters
1541 self._setup_peer_filters()
1543 # BGP peer protocols
1544 self._setup_peer_protocols()
1546 # Configure pipe from the BGP peer table to the main BGP table
1547 bgp_peer_pipe = ProtocolPipe(
1548 birdconfig_globals=self.birdconfig_globals,
1549 table_from=self.bgp_table_name,
1550 table_to="bgp",
1551 export_filter_type=ProtocolPipeFilterType.UNVERSIONED,
1552 import_filter_type=ProtocolPipeFilterType.UNVERSIONED,
1553 has_ipv4=self.has_ipv4,
1554 has_ipv6=self.has_ipv6,
1555 )
1556 self.conf.add(bgp_peer_pipe)
1558 # End of peer
1559 self.conf.add("")
1561 # Make sure our state exists
1562 if "bgp" not in self.birdconfig_globals.state:
1563 self.birdconfig_globals.state["bgp"] = {}
1564 if "peers" not in self.birdconfig_globals.state["bgp"]:
1565 self.birdconfig_globals.state["bgp"]["peers"] = {}
1567 # Save our configuration
1568 self.birdconfig_globals.state["bgp"]["peers"][self.name] = self.state
1570 def protocol_name(self, ipv: str) -> str:
1571 """Return the IP versioned protocol name."""
1572 return f"bgp{ipv}_AS{self.asn}_{self.name}"
1574 def bgp_table_name(self, ipv: str) -> str:
1575 """Return the IP versioned BGP table name."""
1576 return f"t_bgp{ipv}_AS{self.asn}_{self.name}_peer"
1578 @property
1579 def filter_name_export(self) -> str:
1580 """Return the IP versioned peer export filter name."""
1581 return f"f_bgp_AS{self.asn}_{self.name}_peer_export"
1583 @property
1584 def filter_name_import(self) -> str:
1585 """Return the IP versioned peer import filter name."""
1586 return f"f_bgp_AS{self.asn}_{self.name}_peer_import"
1588 @property
1589 def filter_name_export_bgp(self) -> str:
1590 """Return the IP versioned BGP export filter name."""
1591 return f"f_bgp_AS{self.asn}_{self.name}_peer_bgp_export"
1593 @property
1594 def filter_name_import_bgp(self) -> str:
1595 """Return the IP versioned BGP import filter name."""
1596 return f"f_bgp_AS{self.asn}_{self.name}_peer_bgp_import"
1598 def export_prefix_list_name(self, ipv: str) -> str:
1599 """Return our export prefix list name."""
1600 return f"bgp{ipv}_AS{self.asn}_{self.name}_prefixes_export"
1602 def import_prefix_list_name(self, ipv: str) -> str:
1603 """Return our import prefix list name."""
1604 return f"bgp{ipv}_AS{self.asn}_{self.name}_prefixes_import"
1606 def import_prefix_deny_list_name(self, ipv: str) -> str:
1607 """Return our import prefix list name."""
1608 return f"bgp{ipv}_AS{self.asn}_{self.name}_prefixes_deny_import"
1610 def import_blackhole_prefix_list_name(self, ipv: str) -> str:
1611 """Return our import blackhole prefix list name."""
1612 return f"bgp{ipv}_AS{self.asn}_{self.name}_blackhole_prefixes_import"
1614 def _setup_peer_tables(self) -> None:
1615 """Peering routing table setup."""
1617 # Start with no tables as we can have IPv4 and/or IPv6 tables below
1618 state_tables = {}
1620 self.tables.conf.append(f"# BGP Peer Tables: {self.asn} - {self.name}")
1622 # Only create an IPv4 table if we have IPv4 configuration
1623 if self.has_ipv4:
1624 self.tables.conf.append(f"ipv4 table {self.bgp_table_name('4')};")
1625 state_tables["ipv4"] = self.bgp_table_name("4")
1627 # Only create an IPv6 table if we have IPv6 configuration
1628 if self.has_ipv6:
1629 self.tables.conf.append(f"ipv6 table {self.bgp_table_name('6')};")
1630 state_tables["ipv6"] = self.bgp_table_name("6")
1632 self.tables.conf.append("")
1634 # Store our BGP table names
1635 self.state["tables"] = state_tables
1637 def _setup_import_aspath_asns_filter(self) -> None: # pylint: disable=too-many-branches
1638 """AS-PATH ASN import list setup."""
1640 # Short circuit and exit if we have none
1641 if not self.has_import_aspath_asn_filter:
1642 return
1644 state = {}
1646 aspath_asns = []
1647 calculated_aspath_asns = []
1649 # If we're a "customer" or "peer", make sure the aspath_asns list has our own ASN
1650 if self.peer_type in ("customer", "peer"):
1651 aspath_asns.append("# Peer ASN automatically added")
1652 aspath_asns.append(f"{self.asn}")
1654 # Populate AS-PATH ASN list
1655 if self.import_filter_policy.aspath_asns:
1656 # Store static filter info in our state
1657 state["static"] = self.import_filter_policy.aspath_asns
1659 extra_aspath_asns = []
1660 # Loop with ASNs specified in configuration
1661 for asn in self.import_filter_policy.aspath_asns:
1662 if asn not in aspath_asns and asn not in extra_aspath_asns:
1663 extra_aspath_asns.append(f"{asn}")
1664 if asn not in calculated_aspath_asns:
1665 calculated_aspath_asns.append(asn)
1666 aspath_asns.insert(0, f"# Explicitly defined {len(extra_aspath_asns)} items (import_filer:aspath_asns)")
1667 aspath_asns.extend(extra_aspath_asns)
1669 # If we're a "customer" or "peer", pull in the origin_asns and irr_asns lists
1670 if self.peer_type in ("customer", "peer"):
1671 # Check if we can add our ORIGIN ASNs
1672 if self.import_filter_policy.origin_asns:
1673 extra_aspath_asns = []
1674 # Loop with ASNs specified in configuration
1675 for asn in self.import_filter_policy.origin_asns:
1676 # Make sure we don't add duplicates
1677 if asn not in aspath_asns and asn not in extra_aspath_asns:
1678 extra_aspath_asns.append(f"{asn}")
1679 if asn not in calculated_aspath_asns:
1680 calculated_aspath_asns.append(asn)
1681 # If we're a "customer" or "peer", pull in the origin_asns into our aspath_asns list
1682 aspath_asns.append(f"# Explicitly defined {len(extra_aspath_asns)} items (import_filter:aspath_asns)")
1683 aspath_asns.extend(extra_aspath_asns)
1685 # Check if we got results, if so add the IRR ASNs to the aspath_asns list
1686 if self.import_filter_policy.origin_asns_irr:
1687 extra_aspath_asns = []
1688 # Loop with ASNs retrieved from IRR records
1689 for asn in self.import_filter_policy.origin_asns_irr:
1690 if asn not in aspath_asns and asn not in extra_aspath_asns:
1691 extra_aspath_asns.append(f"{asn}")
1692 if asn not in calculated_aspath_asns:
1693 calculated_aspath_asns.append(asn)
1694 aspath_asns.append(
1695 f"# Retrieved {len(extra_aspath_asns)} items from IRR with object '{self.import_filter_policy.as_sets}'"
1696 )
1697 aspath_asns.extend(extra_aspath_asns)
1699 self.conf.add(f"define {self.import_aspath_asn_list_name} = [")
1700 # Loop with each line and add commas where needed
1701 for count, asn in enumerate(aspath_asns):
1702 asn_str = f" {asn}"
1703 if not asn.startswith("#") and count < len(aspath_asns) - 1:
1704 asn_str += ","
1705 self.conf.add(asn_str)
1706 self.conf.add("];")
1707 self.conf.add("")
1709 # Store calculated aspath filter info in our state
1710 state["calculated"] = calculated_aspath_asns
1712 # Save state
1713 if "import_filter" not in self.state:
1714 self.state["import_filter"] = {}
1715 self.state["import_filter"]["aspath_asns"] = state
1717 def _setup_import_origin_asns_filter(self) -> None:
1718 """Origin ASN import list setup."""
1720 # Short circuit and exit if we have none
1721 if not self.has_import_origin_asn_filter:
1722 return
1724 state = {}
1726 origin_asns = []
1728 # Populate our origin ASN list from configuration
1729 if self.import_filter_policy.origin_asns:
1730 # Store filter info in our state
1731 state["static"] = self.import_filter_policy.origin_asns
1733 extra_origin_asns = []
1734 # Loop with ASNs specified in configuration
1735 for asn in self.import_filter_policy.origin_asns:
1736 # Make sure we don't add duplicates
1737 if asn not in origin_asns and asn not in extra_origin_asns:
1738 extra_origin_asns.append(f"{asn}")
1739 origin_asns.append(f"# Explicitly defined {len(extra_origin_asns)} items (import_filter:origin_asns)")
1740 origin_asns.extend(extra_origin_asns)
1742 # Grab IRR ASNs
1743 if self.import_filter_policy.origin_asns_irr:
1744 # Store filter info in our state
1745 state["irr"] = self.import_filter_policy.origin_asns_irr
1747 extra_origin_asns = []
1748 # Loop with ASNs retrieved from IRR records
1749 for asn in self.import_filter_policy.origin_asns_irr:
1750 if asn not in origin_asns and asn not in extra_origin_asns:
1751 extra_origin_asns.append(f"{asn}")
1752 origin_asns.append(
1753 f"# Retrieved {len(extra_origin_asns)} items from IRR with object '{self.import_filter_policy.as_sets}'"
1754 )
1755 origin_asns.extend(extra_origin_asns)
1757 self.conf.add(f"define {self.import_origin_asn_list_name} = [")
1758 # Loop with each line and add commas where needed
1759 for count, asn in enumerate(origin_asns):
1760 asn_str = f" {asn}"
1761 if not asn.startswith("#") and count < len(origin_asns) - 1:
1762 asn_str += ","
1763 self.conf.add(asn_str)
1764 self.conf.add("];")
1765 self.conf.add("")
1767 # Save state
1768 if "import_filter" not in self.state:
1769 self.state["import_filter"] = {}
1770 self.state["import_filter"]["origin_asns"] = state
1772 def _setup_import_aspath_asns_deny_filter(self) -> None: # pylint: disable=too-many-branches
1773 """AS-PATH ASN import deny list setup."""
1775 # Short circuit and exit if we have none
1776 if not self.has_import_aspath_asn_deny_filter:
1777 return
1779 aspath_asns: List[str] = []
1781 # Populate AS-PATH ASN list
1782 if self.import_filter_deny_policy.aspath_asns:
1783 extra_aspath_asns = []
1784 # Loop with ASNs specified in configuration
1785 for asn in self.import_filter_deny_policy.aspath_asns:
1786 if asn not in aspath_asns and asn not in extra_aspath_asns:
1787 extra_aspath_asns.append(f"{asn}")
1788 aspath_asns.insert(0, f"# Explicitly defined {len(extra_aspath_asns)} items (import_filer_deny:aspath_asns)")
1789 aspath_asns.extend(extra_aspath_asns)
1791 self.conf.add(f"define {self.import_aspath_asn_deny_list_name} = [")
1792 # Loop with each line and add commas where needed
1793 for count, asn in enumerate(aspath_asns):
1794 asn_str = f" {asn}"
1795 if not asn.startswith("#") and count < len(aspath_asns) - 1:
1796 asn_str += ","
1797 self.conf.add(asn_str)
1798 self.conf.add("];")
1799 self.conf.add("")
1801 # Save state
1802 if "import_filter_deny" not in self.state:
1803 self.state["import_filter_deny"] = {}
1804 self.state["import_filter_deny"]["aspath_asns"] = aspath_asns
1806 def _setup_import_origin_asns_deny_filter(self) -> None:
1807 """Origin ASN import list setup."""
1809 # Short circuit and exit if we have none
1810 if not self.has_import_origin_asn_deny_filter:
1811 return
1813 origin_asns = []
1815 # Populate our origin ASN list from configuration
1816 if self.import_filter_deny_policy.origin_asns:
1817 extra_origin_asns = []
1818 # Loop with ASNs specified in configuration
1819 for asn in self.import_filter_deny_policy.origin_asns:
1820 # Make sure we don't add duplicates
1821 if asn not in origin_asns and asn not in extra_origin_asns:
1822 extra_origin_asns.append(f"{asn}")
1823 origin_asns.append(f"# Explicitly defined {len(extra_origin_asns)} items (import_filter_deny:origin_asns)")
1824 origin_asns.extend(extra_origin_asns)
1826 self.conf.add(f"define {self.import_origin_asn_deny_list_name} = [")
1827 # Loop with each line and add commas where needed
1828 for count, asn in enumerate(origin_asns):
1829 asn_str = f" {asn}"
1830 if not asn.startswith("#") and count < len(origin_asns) - 1:
1831 asn_str += ","
1832 self.conf.add(asn_str)
1833 self.conf.add("];")
1834 self.conf.add("")
1836 # Save state
1837 if "import_filter_deny" not in self.state:
1838 self.state["import_filter_deny"] = {}
1839 self.state["import_filter_deny"]["origin_asns"] = origin_asns
1841 def _setup_export_origin_asns_filter(self) -> None:
1842 """Origin ASN export list setup."""
1844 # Short circuit and exit if we have none
1845 if not self.has_export_origin_asn_filter:
1846 return
1848 state = {}
1850 origin_asns = []
1852 # Populate our origin ASN list from configuration
1853 if self.export_filter_policy.origin_asns:
1854 # Store filter info in our state
1855 state["static"] = self.export_filter_policy.origin_asns
1857 extra_origin_asns = []
1858 # Loop with ASNs specified in configuration
1859 for asn in self.export_filter_policy.origin_asns:
1860 # Make sure we don't add duplicates
1861 if asn not in origin_asns and asn not in extra_origin_asns:
1862 extra_origin_asns.append(f"{asn}")
1863 origin_asns.append(f"# Explicitly defined {len(extra_origin_asns)} items (export_filter:origin_asns)")
1864 origin_asns.extend(extra_origin_asns)
1866 self.conf.add(f"define {self.export_origin_asn_list_name} = [")
1867 # Loop with each line and add commas where needed
1868 for count, asn in enumerate(origin_asns):
1869 asn_str = f" {asn}"
1870 if not asn.startswith("#") and count < len(origin_asns) - 1:
1871 asn_str += ","
1872 self.conf.add(asn_str)
1873 self.conf.add("];")
1874 self.conf.add("")
1876 # Save state
1877 if "export_filter" not in self.state:
1878 self.state["export_filter"] = {}
1879 self.state["export_filter"]["origin_asns"] = state
1881 def _setup_import_peer_asns_filter(self) -> None:
1882 """Peer ASN import list setup."""
1884 # Short circuit and exit if we have none
1885 if not self.has_import_peer_asn_filter:
1886 return
1888 state = {}
1890 peer_asns = []
1892 # Add ASN list with comments
1893 if self.import_filter_policy.peer_asns:
1894 # Save our peer ASN list in our state
1895 state["static"] = self.import_filter_policy.peer_asns
1896 peer_asns.append(f"# Explicitly defined {len(self.import_filter_policy.peer_asns)} items (import_filter:peer_asns)")
1897 for asn in self.import_filter_policy.peer_asns:
1898 peer_asns.append(f"{asn}")
1900 self.conf.add(f"define {self.import_peer_asn_list_name} = [")
1901 # Loop with each line and add commas where needed
1902 for count, asn in enumerate(peer_asns):
1903 asn_str = f" {asn}"
1904 if not asn.startswith("#") and count < len(peer_asns) - 1:
1905 asn_str += ","
1906 self.conf.add(asn_str)
1907 self.conf.add("];")
1908 self.conf.add("")
1910 # Save state
1911 if "import_filter" not in self.state:
1912 self.state["import_filter"] = {}
1913 self.state["import_filter"]["peer_asns"] = state
1915 def _setup_peer_import_prefix_filter( # noqa: CFQ001 # pylint: disable=too-many-locals,too-many-branches,too-many-statements
1916 self,
1917 ) -> None:
1918 """Prefix import filter setup."""
1920 # Short circuit and exit if we have none
1921 if not self.has_import_prefix_filter:
1922 return
1924 state: Dict[str, Dict[str, Any]] = {}
1926 # Work out prefixes
1927 import_prefix_lists: Dict[str, List[str]] = {"4": [], "6": []}
1928 for prefix in sorted(self.import_filter_policy.prefixes):
1929 if ":" in prefix:
1930 import_prefix_lists["6"].append(prefix)
1931 else:
1932 import_prefix_lists["4"].append(prefix)
1933 import_prefix_lists_irr: Dict[str, List[str]] = {"4": [], "6": []}
1934 for prefix in sorted(self.import_filter_policy.prefixes_irr):
1935 if ":" in prefix:
1936 import_prefix_lists_irr["6"].append(prefix)
1937 else:
1938 import_prefix_lists_irr["4"].append(prefix)
1940 # Output prefix definitions
1941 for ipv in ["4", "6"]:
1942 import_prefix_list = import_prefix_lists[ipv]
1943 import_prefix_list_irr = import_prefix_lists_irr[ipv]
1945 import_prefixes = []
1946 import_blackholes = []
1948 # Add statically defined prefix list
1949 if import_prefix_list:
1950 # Save prefix list in our state
1951 if "static" not in state:
1952 state["static"] = {}
1953 state["static"][f"ipv{ipv}"] = import_prefix_list
1955 for prefix in import_prefix_list:
1956 import_prefixes.append(prefix)
1957 # Add blackhole
1958 blackhole = re.split(r"[{+]", prefix, maxsplit=1)[0] + "+"
1959 import_blackholes.append(blackhole)
1960 # Sort and unique our results
1961 import_prefixes = sorted(set(import_prefixes))
1962 import_blackholes = sorted(set(import_blackholes))
1963 # Add title for this section
1964 import_prefixes.insert(0, f"# {len(import_prefix_list)} explicitly defined")
1965 import_blackholes.insert(0, f"# {len(import_prefix_list)} explicitly defined")
1967 # Add prefix list from IRR
1968 if import_prefix_list_irr:
1969 # Save prefix list in our state
1970 if "irr" not in state:
1971 state["irr"] = {}
1972 state["irr"][f"ipv{ipv}"] = import_prefix_list_irr
1974 import_prefixes_irr = []
1975 import_blackholes_irr = []
1977 # Loop with each prefix we got from IRR
1978 for prefix in import_prefix_list_irr:
1979 # Make sure we're not making duplicates
1980 if prefix not in import_prefixes:
1981 import_prefixes_irr.append(prefix)
1982 # Add blackhole, and make sure we're not duplicating here either
1983 blackhole = prefix.split("{", 1)[0] + "+"
1984 if blackhole not in import_blackholes:
1985 import_blackholes_irr.append(blackhole)
1987 # Add title to top of prefixes retrieved via IRR
1988 import_prefixes.append(
1989 f"# Retrieved {len(import_prefixes_irr)} items from IRR with object '{self.import_filter_policy.as_sets}'"
1990 )
1991 import_blackholes.append(
1992 f"# Retrieved {len(import_blackholes_irr)} items from IRR with object '{self.import_filter_policy.as_sets}'"
1993 )
1994 # Extend our lists
1995 import_prefixes.extend(import_prefixes_irr)
1996 import_blackholes.extend(import_blackholes_irr)
1998 self.conf.add(f"define {self.import_prefix_list_name(ipv)} = [")
1999 # Loop with each line and add commas where needed
2000 for count, prefix in enumerate(import_prefixes):
2001 prefix_str = f" {prefix}"
2002 if not prefix.startswith("#") and count < len(import_prefixes) - 1:
2003 prefix_str += ","
2004 self.conf.add(prefix_str)
2005 self.conf.add("];")
2006 self.conf.add("")
2008 # We only need to output the blackhole list if the peer is a peertype that we support receiving blackhole prefixes from
2009 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"):
2010 self.conf.add(f"define {self.import_blackhole_prefix_list_name(ipv)} = [")
2011 # Loop with each line and add commas where needed
2012 for count, blackhole in enumerate(import_blackholes):
2013 blackhole_str = f" {blackhole}"
2014 if not blackhole.startswith("#") and count < len(import_blackholes) - 1:
2015 blackhole_str += ","
2016 self.conf.add(blackhole_str)
2017 self.conf.add("];")
2018 self.conf.add("")
2020 # Save state
2021 if "import_filter" not in self.state:
2022 self.state["import_filter"] = {}
2023 self.state["import_filter"]["prefixes"] = state
2025 def _setup_peer_import_prefix_deny_filter( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
2026 self,
2027 ) -> None:
2028 """Prefix import deny filter setup."""
2030 # Short circuit and exit if we have none
2031 if not self.has_import_prefix_deny_filter:
2032 return
2034 state: Dict[str, List[str]] = {}
2036 # Work out prefixes
2037 import_prefix_lists: Dict[str, List[str]] = {"4": [], "6": []}
2038 for prefix in sorted(self.import_filter_deny_policy.prefixes):
2039 if ":" in prefix:
2040 import_prefix_lists["6"].append(prefix)
2041 else:
2042 import_prefix_lists["4"].append(prefix)
2044 # Output prefix definitions
2045 for ipv in ["4", "6"]:
2046 import_prefix_list = import_prefix_lists[ipv]
2048 import_prefixes = []
2050 # Add statically defined prefix list
2051 if import_prefix_list:
2052 state[f"ipv{ipv}"] = import_prefix_list
2053 for prefix in import_prefix_list:
2054 import_prefixes.append(prefix)
2056 # Sort and unique our results
2057 import_prefixes = sorted(set(import_prefixes))
2058 # Add title for this section
2059 import_prefixes.insert(0, f"# {len(import_prefix_list)} explicitly defined")
2061 self.conf.add(f"define {self.import_prefix_deny_list_name(ipv)} = [")
2062 # Loop with each line and add commas where needed
2063 for count, prefix in enumerate(import_prefixes):
2064 prefix_str = f" {prefix}"
2065 if not prefix.startswith("#") and count < len(import_prefixes) - 1:
2066 prefix_str += ","
2067 self.conf.add(prefix_str)
2068 self.conf.add("];")
2069 self.conf.add("")
2071 # Save state
2072 if "import_filter_deny" not in self.state:
2073 self.state["import_filter_deny"] = {}
2074 self.state["import_filter_deny"]["prefixes"] = state
2076 def _setup_peer_export_prefix_filter(self) -> None: # pylint: disable=too-many-locals,too-many-branches,too-many-statements
2077 """Prefix export filter setup."""
2079 # Short circuit and exit if we have none
2080 if not self.has_export_prefix_filter:
2081 return
2083 state: Dict[str, Dict[str, Any]] = {}
2085 # Work out prefixes
2086 export_prefix_lists: Dict[str, List[str]] = {"4": [], "6": []}
2087 for prefix in sorted(self.export_filter_policy.prefixes):
2088 if ":" in prefix:
2089 export_prefix_lists["6"].append(prefix)
2090 else:
2091 export_prefix_lists["4"].append(prefix)
2093 # Output prefix definitions
2094 for ipv in ["4", "6"]:
2095 export_prefix_list = export_prefix_lists[ipv]
2097 export_prefixes = []
2099 # Add statically defined prefix list
2100 if export_prefix_list:
2101 # Save prefix list in our state
2102 if "static" not in state:
2103 state["static"] = {}
2104 state["static"][f"ipv{ipv}"] = export_prefix_list
2106 for prefix in export_prefix_list:
2107 export_prefixes.append(prefix)
2109 # Sort and unique our results
2110 export_prefixes = sorted(set(export_prefixes))
2112 # Add title for this section
2113 export_prefixes.insert(0, f"# {len(export_prefix_list)} explicitly defined")
2115 self.conf.add(f"define {self.export_prefix_list_name(ipv)} = [")
2116 # Loop with each line and add commas where needed
2117 for count, prefix in enumerate(export_prefixes):
2118 prefix_str = f" {prefix}"
2119 if not prefix.startswith("#") and count < len(export_prefixes) - 1:
2120 prefix_str += ","
2121 self.conf.add(prefix_str)
2122 self.conf.add("];")
2123 self.conf.add("")
2125 # Save state
2126 if "export_filter" not in self.state:
2127 self.state["export_filter"] = {}
2128 self.state["export_filter"]["prefixes"] = state
2130 def _peer_to_bgp_export_filter(self) -> None:
2131 """Export filters into our main BGP routing table from the BGP peer table."""
2133 # Set our filter name
2134 filter_name = self.filter_name_export_bgp
2136 # Configure export filter to our main BGP table
2137 self.conf.add("# Export filter TO the main BGP table from the BGP peer table")
2138 self.conf.add(f"filter {filter_name}")
2139 self.conf.add("string filter_name;")
2140 self.conf.add("{")
2141 self.conf.add(f' filter_name = "{filter_name}";')
2142 # If this is a filtered route, reject it
2143 self.conf.add(f" {self.bgp_functions.peer_reject_filtered()};")
2144 # Enable blackholing for customers and internal peers
2145 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"):
2146 self.conf.add(f" {self.bgp_functions.peer_accept_blackhole()};")
2147 # Enable blackhole large community origination for internal peers
2148 if self.peer_type in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
2149 self.conf.add(f" {self.bgp_functions.peer_accept_blackhole_originated()};")
2150 # Finally accept the route
2151 self.conf.add(f" {self.bgp_functions.peer_accept()};")
2152 self.conf.add("};")
2153 self.conf.add("")
2155 def _peer_to_bgp_import_filter( # noqa: CFQ001 # pylint: disable=too-many-branches,too-many-statements,too-many-locals
2156 self,
2157 ) -> None:
2158 """Import filter FROM the main BGP table to the BGP peer table."""
2160 # Set our filter name
2161 filter_name = self.filter_name_import_bgp
2163 # Configure import filter from our main BGP table
2164 self.conf.add("# Import filter FROM the main BGP table to the BGP peer table")
2165 self.conf.add(f"filter {filter_name}")
2166 self.conf.add("string filter_name;")
2167 self.conf.add("bool accept_route;")
2168 self.conf.add("{")
2169 self.conf.add(f' filter_name = "{filter_name}";')
2170 self.conf.add(" accept_route = false;")
2171 self.conf.add("")
2173 # Reject NOADVERTISE
2174 self.conf.add(f" {self.bgp_functions.peer_reject_noadvertise()};")
2176 # Do not export blackhole routes to customers or peers
2177 if self.peer_type in ("customer", "peer"):
2178 self.conf.add(f" {self.bgp_functions.peer_reject_blackholes()};")
2179 # Check if we allow blackholes
2180 blackhole_function_arg = None
2181 if self.peer_type in ("routeserver", "routecollector"):
2182 blackhole_function_arg = 65413
2183 elif self.peer_type == "transit":
2184 blackhole_function_arg = 65412
2185 # For eBGP peers we need to verify that the blackhole is targetted
2186 if blackhole_function_arg:
2187 # If the peer accepts blackhole communites, check if we're going to be exporting this blackhole
2188 if self.blackhole_community:
2189 self.conf.add(" # Peer is blackhole community capable")
2190 # Grab BIRD function to reject non targetted blackhole routes
2191 peer_reject_non_targetted_blackhole = self.bgp_functions.peer_reject_non_targetted_blackhole(
2192 True,
2193 self.route_policy_redistribute.kernel_blackhole,
2194 self.route_policy_redistribute.static_blackhole,
2195 self.asn,
2196 blackhole_function_arg,
2197 )
2198 self.conf.add(f" {peer_reject_non_targetted_blackhole};")
2199 else:
2200 self.conf.add(" # Peer is not blackhole community capable")
2201 # Grab BIRD function to reject non targetted blackhole routes
2202 peer_reject_non_targetted_blackhole = self.bgp_functions.peer_reject_non_targetted_blackhole(
2203 False, False, False, self.asn, blackhole_function_arg
2204 )
2205 self.conf.add(f" {peer_reject_non_targetted_blackhole};")
2207 # IMPORTANT: This must be after peer_reject_non_targetted_blackhole as peer_reject_non_targetted_blackhole removes
2208 # the NOEXPORT community
2209 # Reject NOEXPORT and NOEXPORT ASN
2210 if self.peer_type not in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
2211 self.conf.add(f" {self.bgp_functions.peer_reject_noexport()};")
2212 self.conf.add(f" {self.bgp_functions.peer_reject_noexport_asn(self.asn)};")
2214 # Check for peer types we're not exporting to
2215 if self.peer_type == "customer":
2216 self.conf.add(f" {self.bgp_functions.peer_reject_noexport_customer()};")
2217 if self.peer_type in ("peer", "routecollector", "routeserver"):
2218 self.conf.add(f" {self.bgp_functions.peer_reject_noexport_peer()};")
2219 if self.peer_type == "transit":
2220 self.conf.add(f" {self.bgp_functions.peer_reject_noexport_transit()};")
2222 # Rejections based on location-based large communities
2223 if self.peer_type in ("customer", "peer", "routeserver", "routecollector", "transit"): # noqa: SIM102
2224 # Check if we have a ISO-3166 country code
2225 if self.location.iso3166:
2226 self.conf.add(" # Check if we're not exporting this route based on the ISO-3166 location")
2227 self.conf.add(f" {self.bgp_functions.peer_reject_noexport_location(self.location.iso3166)};")
2229 # Check for connected route redistribution
2230 if self.route_policy_redistribute.connected:
2231 self.conf.add(f" if {self.bgp_functions.peer_redistribute_connected(True)} then accept_route = true;")
2232 else:
2233 self.conf.add(f" {self.bgp_functions.peer_redistribute_connected(False)};")
2234 # Check for kernel route redistribution
2235 if self.route_policy_redistribute.kernel:
2236 self.conf.add(f" if {self.bgp_functions.peer_redistribute_kernel(True)} then accept_route = true;")
2237 else:
2238 self.conf.add(f" {self.bgp_functions.peer_redistribute_kernel(False)};")
2239 # Check for kernel blackhole route redistribution
2240 if self.route_policy_redistribute.kernel_blackhole:
2241 self.conf.add(f" if {self.bgp_functions.peer_redistribute_kernel_blackhole(True)} then accept_route = true;")
2242 else:
2243 self.conf.add(f" {self.bgp_functions.peer_redistribute_kernel_blackhole(False)};")
2244 # Check for kernel default route redistribution
2245 if self.route_policy_redistribute.kernel_default:
2246 self.conf.add(f" if {self.bgp_functions.peer_redistribute_kernel_default(True)} then accept_route = true;")
2247 else:
2248 self.conf.add(f" {self.bgp_functions.peer_redistribute_kernel_default(False)};")
2249 # Check for static route redistribution
2250 if self.route_policy_redistribute.static:
2251 self.conf.add(f" if {self.bgp_functions.peer_redistribute_static(True)} then accept_route = true;")
2252 else:
2253 self.conf.add(f" {self.bgp_functions.peer_redistribute_static(False)};")
2254 # Check for static blackhole route redistribution
2255 if self.route_policy_redistribute.static_blackhole:
2256 self.conf.add(f" if {self.bgp_functions.peer_redistribute_static_blackhole(True)} then accept_route = true;")
2257 else:
2258 self.conf.add(f" {self.bgp_functions.peer_redistribute_static_blackhole(False)};")
2259 # Check for static default route redistribution
2260 if self.route_policy_redistribute.static_default:
2261 self.conf.add(f" if {self.bgp_functions.peer_redistribute_static_default(True)} then accept_route = true;")
2262 else:
2263 self.conf.add(f" {self.bgp_functions.peer_redistribute_static_default(False)};")
2264 # Check for originated route redistribution
2265 if self.route_policy_redistribute.originated:
2266 self.conf.add(f" if {self.bgp_functions.peer_redistribute_originated(True)} then accept_route = true;")
2267 else:
2268 self.conf.add(f" {self.bgp_functions.peer_redistribute_originated(False)};")
2269 # Check for originated default route redistribution
2270 if self.route_policy_redistribute.originated_default:
2271 self.conf.add(f" if {self.bgp_functions.peer_redistribute_originated_default(True)} then accept_route = true;")
2272 else:
2273 self.conf.add(f" {self.bgp_functions.peer_redistribute_originated_default(False)};")
2275 # Check redistribution BGP routes originating from the different peer types
2276 # BGP routes originating from customers
2277 if self.route_policy_redistribute.bgp_customer:
2278 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_customer(True)} then accept_route = true;")
2279 else:
2280 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_customer(False)};")
2282 if self.route_policy_redistribute.bgp_customer_blackhole:
2283 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_customer_blackhole(True)} then accept_route = true;")
2284 else:
2285 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_customer_blackhole(False)};")
2287 # BGP routes originating from within our federation
2288 if self.route_policy_redistribute.bgp_own:
2289 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_own(True)} then accept_route = true;")
2290 else:
2291 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_own(False)};")
2293 if self.route_policy_redistribute.bgp_own_blackhole:
2294 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_own_blackhole(True)} then accept_route = true;")
2295 else:
2296 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_own_blackhole(False)};")
2298 if self.route_policy_redistribute.bgp_own_default:
2299 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_own_default(True)} then accept_route = true;")
2300 else:
2301 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_own_default(False)};")
2303 # BGP routes originating from peering
2304 if self.route_policy_redistribute.bgp_peering:
2305 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_peering(True)} then accept_route = true;")
2306 else:
2307 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_peering(False)};")
2309 # BGP routes originating from transit
2310 if self.route_policy_redistribute.bgp_transit:
2311 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_transit(True)} then accept_route = true;")
2312 else:
2313 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_transit(False)};")
2315 if self.route_policy_redistribute.bgp_transit_default:
2316 self.conf.add(f" if {self.bgp_functions.peer_redistribute_bgp_transit_default(True)} then accept_route = true;")
2317 else:
2318 self.conf.add(f" {self.bgp_functions.peer_redistribute_bgp_transit_default(False)};")
2320 # Check if the route is exportable
2321 self.conf.add(" # BGP exportable checks")
2322 self.conf.add(f" if !{self.functions.is_default()} then {{")
2323 # Reject bogons for eBGP peer types
2324 if self.peer_type not in ("internal", "rrclient", "rrserver", "rrserver-server"):
2325 self.conf.add(f" {self.bgp_functions.peer_reject_bogons()};")
2326 self.conf.add(f" if {self.bgp_functions.is_blackhole()} then {{")
2327 self.conf.add(f" {self._peer_reject_non_exportable_blackhole()};")
2328 self.conf.add(" } else {")
2329 self.conf.add(f" {self._peer_reject_non_exportable()};")
2330 self.conf.add(" }")
2332 # Filter BGP routes we don't want
2333 # Check if we're filtering allowed origin ASNs
2334 if self.has_export_origin_asn_filter:
2335 self.conf.add(" # Filter exported prefixes on origin ASN")
2336 self.conf.add(
2337 f" if {self.bgp_functions.export_filter_origin_asns(BirdVariable(self.export_origin_asn_list_name))}"
2338 " then accept_route = false;"
2339 )
2340 # Check if we're filtering allowed prefixes
2341 if self.has_export_prefix_filter:
2342 self.conf.add(" # Filter exported prefixes")
2343 # Check if we have IPv4 support and output the filter
2344 export_filter_prefixes = self.bgp_functions.export_filter_prefixes(
2345 BirdVariable(self.export_prefix_list_name("4")), BirdVariable(self.export_prefix_list_name("6"))
2346 )
2347 self.conf.add(f" if {export_filter_prefixes} then accept_route = false;")
2349 # End of BGP type tests
2350 self.conf.add(" }")
2352 # Check if we're accepting the route...
2353 self.conf.add(" if (accept_route) then {")
2354 # Check if we are adding a community to outgoing routes
2355 if self.communities.outgoing.connected:
2356 for community in self.communities.outgoing.connected:
2357 self.conf.add(f" {self.bgp_functions.peer_community_add_connected(BirdVariable(community))};")
2358 if self.communities.outgoing.kernel:
2359 for community in self.communities.outgoing.kernel:
2360 self.conf.add(f" {self.bgp_functions.peer_community_add_kernel(BirdVariable(community))};")
2361 if self.communities.outgoing.kernel_blackhole:
2362 for community in self.communities.outgoing.kernel_blackhole:
2363 self.conf.add(f" {self.bgp_functions.peer_community_add_kernel_blackhole(BirdVariable(community))};")
2364 if self.communities.outgoing.kernel_default:
2365 for community in self.communities.outgoing.kernel_default:
2366 self.conf.add(f" {self.bgp_functions.peer_community_add_kernel_default(BirdVariable(community))};")
2367 if self.communities.outgoing.originated:
2368 for community in self.communities.outgoing.originated:
2369 self.conf.add(f" {self.bgp_functions.peer_community_add_originated(BirdVariable(community))};")
2370 if self.communities.outgoing.originated_default:
2371 for community in self.communities.outgoing.originated_default:
2372 self.conf.add(f" {self.bgp_functions.peer_community_add_originated_default(BirdVariable(community))};")
2373 if self.communities.outgoing.static:
2374 for community in self.communities.outgoing.static:
2375 self.conf.add(f" {self.bgp_functions.peer_community_add_static(BirdVariable(community))};")
2376 if self.communities.outgoing.static_blackhole:
2377 for community in self.communities.outgoing.static_blackhole:
2378 self.conf.add(f" {self.bgp_functions.peer_community_add_static_blackhole(BirdVariable(community))};")
2379 if self.communities.outgoing.static_default:
2380 for community in self.communities.outgoing.static_default:
2381 self.conf.add(f" {self.bgp_functions.peer_community_add_static_default(BirdVariable(community))};")
2382 if self.communities.outgoing.bgp_own:
2383 for community in self.communities.outgoing.bgp_own:
2384 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_own(BirdVariable(community))};")
2385 if self.communities.outgoing.bgp_own_blackhole:
2386 for community in self.communities.outgoing.bgp_own_blackhole:
2387 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_own_blackhole(BirdVariable(community))};")
2388 if self.communities.outgoing.bgp_own_default:
2389 for community in self.communities.outgoing.bgp_own_default:
2390 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_own_default(BirdVariable(community))};")
2391 if self.communities.outgoing.bgp_customer:
2392 for community in self.communities.outgoing.bgp_customer:
2393 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_customer(BirdVariable(community))};")
2394 if self.communities.outgoing.bgp_customer_blackhole:
2395 for community in self.communities.outgoing.bgp_customer_blackhole:
2396 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_customer_blackhole(BirdVariable(community))};")
2397 if self.communities.outgoing.bgp_peering:
2398 for community in self.communities.outgoing.bgp_peering:
2399 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_peering(BirdVariable(community))};")
2400 if self.communities.outgoing.bgp_transit:
2401 for community in self.communities.outgoing.bgp_transit:
2402 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_transit(BirdVariable(community))};")
2403 if self.communities.outgoing.bgp_transit_default:
2404 for community in self.communities.outgoing.bgp_transit_default:
2405 self.conf.add(f" {self.bgp_functions.peer_community_add_bgp_transit_default(BirdVariable(community))};")
2406 # Check if we are adding a large community to outgoing routes
2407 if self.large_communities.outgoing.connected:
2408 for large_community in self.large_communities.outgoing.connected:
2409 self.conf.add(f" {self.bgp_functions.peer_lc_add_connected(BirdVariable(large_community))};")
2410 if self.large_communities.outgoing.kernel:
2411 for large_community in self.large_communities.outgoing.kernel:
2412 self.conf.add(f" {self.bgp_functions.peer_lc_add_kernel(BirdVariable(large_community))};")
2413 if self.large_communities.outgoing.kernel_blackhole:
2414 for large_community in self.large_communities.outgoing.kernel_blackhole:
2415 self.conf.add(f" {self.bgp_functions.peer_lc_add_kernel_blackhole(BirdVariable(large_community))};")
2416 if self.large_communities.outgoing.kernel_default:
2417 for large_community in self.large_communities.outgoing.kernel_default:
2418 self.conf.add(f" {self.bgp_functions.peer_lc_add_kernel_default(BirdVariable(large_community))};")
2419 if self.large_communities.outgoing.originated:
2420 for large_community in self.large_communities.outgoing.originated:
2421 self.conf.add(f" {self.bgp_functions.peer_lc_add_originated(BirdVariable(large_community))};")
2422 if self.large_communities.outgoing.originated_default:
2423 for large_community in self.large_communities.outgoing.originated_default:
2424 self.conf.add(f" {self.bgp_functions.peer_lc_add_originated_default(BirdVariable(large_community))};")
2425 if self.large_communities.outgoing.static:
2426 for large_community in self.large_communities.outgoing.static:
2427 self.conf.add(f" {self.bgp_functions.peer_lc_add_static(BirdVariable(large_community))};")
2428 if self.large_communities.outgoing.static_blackhole:
2429 for large_community in self.large_communities.outgoing.static_blackhole:
2430 self.conf.add(f" {self.bgp_functions.peer_lc_add_static_blackhole(BirdVariable(large_community))};")
2431 if self.large_communities.outgoing.static_default:
2432 for large_community in self.large_communities.outgoing.static_default:
2433 self.conf.add(f" {self.bgp_functions.peer_lc_add_static_default(BirdVariable(large_community))};")
2434 if self.large_communities.outgoing.bgp_own:
2435 for large_community in self.large_communities.outgoing.bgp_own:
2436 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_own(BirdVariable(large_community))};")
2437 if self.large_communities.outgoing.bgp_own_blackhole:
2438 for large_community in self.large_communities.outgoing.bgp_own_blackhole:
2439 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_own_blackhole(BirdVariable(large_community))};")
2440 if self.large_communities.outgoing.bgp_own_default:
2441 for large_community in self.large_communities.outgoing.bgp_own_default:
2442 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_own_default(BirdVariable(large_community))};")
2443 if self.large_communities.outgoing.bgp_customer:
2444 for large_community in self.large_communities.outgoing.bgp_customer:
2445 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_customer(BirdVariable(large_community))};")
2446 if self.large_communities.outgoing.bgp_customer_blackhole:
2447 for large_community in self.large_communities.outgoing.bgp_customer_blackhole:
2448 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_customer_blackhole(BirdVariable(large_community))};")
2449 if self.large_communities.outgoing.bgp_peering:
2450 for large_community in self.large_communities.outgoing.bgp_peering:
2451 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_peering(BirdVariable(large_community))};")
2452 if self.large_communities.outgoing.bgp_transit:
2453 for large_community in self.large_communities.outgoing.bgp_transit:
2454 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_transit(BirdVariable(large_community))};")
2455 if self.large_communities.outgoing.bgp_transit_default:
2456 for large_community in self.large_communities.outgoing.bgp_transit_default:
2457 self.conf.add(f" {self.bgp_functions.peer_lc_add_bgp_transit_default(BirdVariable(large_community))};")
2459 # For eBGP peer types, make sure we replace AS-PATHs with the LC action set
2460 if self.peer_type in ("customer", "peer", "routecollector", "routeserver", "transit"):
2461 self.conf.add(f" {self.bgp_functions.peer_replace_aspath()};")
2462 self.conf.add(f" {self.bgp_functions.peer_remove_lc_private()};")
2464 # Check if we're doing AS-PATH prepending...
2465 if self.prepend.connected.own_asn:
2466 self.conf.add(
2467 f" {self.bgp_functions.peer_prepend_connected(BirdVariable('BGP_ASN'), self.prepend.connected.own_asn)};"
2468 )
2470 if self.prepend.kernel.own_asn:
2471 self.conf.add(f" {self.bgp_functions.peer_prepend_kernel(BirdVariable('BGP_ASN'), self.prepend.kernel.own_asn)};")
2473 if self.peer_type in ( # noqa: SIM102
2474 "internal",
2475 "routecollector",
2476 "routeserver",
2477 "rrclient",
2478 "rrserver",
2479 "rrserver-rrserver",
2480 "transit",
2481 ):
2482 if self.prepend.kernel_blackhole.own_asn:
2483 peer_prepend_kernel_blackhole = self.bgp_functions.peer_prepend_kernel_blackhole(
2484 BirdVariable("BGP_ASN"), self.prepend.kernel_blackhole.own_asn
2485 )
2486 self.conf.add(f" {peer_prepend_kernel_blackhole};")
2488 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2489 if self.prepend.kernel_default.own_asn:
2490 peer_prepend_kernel_default = self.bgp_functions.peer_prepend_kernel_default(
2491 BirdVariable("BGP_ASN"), self.prepend.kernel_default.own_asn
2492 )
2493 self.conf.add(f" {peer_prepend_kernel_default};")
2495 if self.prepend.originated.own_asn:
2496 self.conf.add(
2497 f" {self.bgp_functions.peer_prepend_originated(BirdVariable('BGP_ASN'), self.prepend.originated.own_asn)};"
2498 )
2500 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2501 if self.prepend.originated_default.own_asn:
2502 peer_prepend_originated_default = self.bgp_functions.peer_prepend_originated_default(
2503 BirdVariable("BGP_ASN"), self.prepend.originated_default.own_asn
2504 )
2505 self.conf.add(f" {peer_prepend_originated_default};")
2507 if self.prepend.static.own_asn:
2508 self.conf.add(f" {self.bgp_functions.peer_prepend_static(BirdVariable('BGP_ASN'), self.prepend.static.own_asn)};")
2510 if self.peer_type in ( # noqa: SIM102
2511 "internal",
2512 "routecollector",
2513 "routeserver",
2514 "rrclient",
2515 "rrserver",
2516 "rrserver-rrserver",
2517 "transit",
2518 ):
2519 if self.prepend.static_blackhole.own_asn:
2520 peer_prepend_static_blackhole = self.bgp_functions.peer_prepend_static_blackhole(
2521 BirdVariable("BGP_ASN"), self.prepend.static_blackhole.own_asn
2522 )
2523 self.conf.add(f" {peer_prepend_static_blackhole};")
2525 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2526 if self.prepend.static_default.own_asn:
2527 peer_prepend_static_default = self.bgp_functions.peer_prepend_static_default(
2528 BirdVariable("BGP_ASN"), self.prepend.static_default.own_asn
2529 )
2530 self.conf.add(f" {peer_prepend_static_default};")
2532 if self.prepend.bgp_own.own_asn:
2533 self.conf.add(f" {self.bgp_functions.peer_prepend_bgp_own(BirdVariable('BGP_ASN'), self.prepend.bgp_own.own_asn)};")
2535 if self.peer_type in ( # noqa: SIM102
2536 "internal",
2537 "routecollector",
2538 "routeserver",
2539 "rrclient",
2540 "rrserver",
2541 "rrserver-rrserver",
2542 "transit",
2543 ):
2544 if self.prepend.bgp_own_blackhole.own_asn:
2545 peer_prepend_bgp_own_blackhole = self.bgp_functions.peer_prepend_bgp_own_blackhole(
2546 BirdVariable("BGP_ASN"), self.prepend.bgp_own_blackhole.own_asn
2547 )
2548 self.conf.add(f" {peer_prepend_bgp_own_blackhole};")
2550 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2551 if self.prepend.bgp_own_default.own_asn:
2552 peer_prepend_bgp_own_default = self.bgp_functions.peer_prepend_bgp_own_default(
2553 BirdVariable("BGP_ASN"), self.prepend.bgp_own_default.own_asn
2554 )
2555 self.conf.add(f" {peer_prepend_bgp_own_default};")
2557 if self.prepend.bgp_customer.own_asn:
2558 self.conf.add(
2559 f" {self.bgp_functions.peer_prepend_bgp_customer(BirdVariable('BGP_ASN'), self.prepend.bgp_customer.own_asn)};"
2560 )
2562 if self.peer_type in ( # noqa: SIM102
2563 "internal",
2564 "routecollector",
2565 "routeserver",
2566 "rrclient",
2567 "rrserver",
2568 "rrserver-rrserver",
2569 "transit",
2570 ):
2571 if self.prepend.bgp_customer_blackhole.own_asn:
2572 peer_prepend_bgp_customer_blackhole = self.bgp_functions.peer_prepend_bgp_customer_blackhole(
2573 BirdVariable("BGP_ASN"), self.prepend.bgp_customer_blackhole.own_asn
2574 )
2575 self.conf.add(f" {peer_prepend_bgp_customer_blackhole};")
2577 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2578 if self.prepend.bgp_peering.own_asn:
2579 self.conf.add(
2580 f" {self.bgp_functions.peer_prepend_bgp_peering(BirdVariable('BGP_ASN'), self.prepend.bgp_peering.own_asn)};"
2581 )
2583 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2584 if self.prepend.bgp_transit.own_asn:
2585 self.conf.add(
2586 f" {self.bgp_functions.peer_prepend_bgp_transit(BirdVariable('BGP_ASN'), self.prepend.bgp_transit.own_asn)};"
2587 )
2589 if self.peer_type in ("customer", "internal", "rrclient", "rrserver", "rrserver-rrserver"): # noqa: SIM102
2590 if self.prepend.bgp_transit_default.own_asn:
2591 peer_prepend_bgp_transit_default = self.bgp_functions.peer_prepend_bgp_transit_default(
2592 BirdVariable("BGP_ASN"), self.prepend.bgp_transit_default.own_asn
2593 )
2594 self.conf.add(f" {peer_prepend_bgp_transit_default};")
2596 # Do large community prepending if the peer is a customer, peer, routeserver or transit
2597 if self.peer_type in ("customer", "peer", "routeserver", "routecollector", "transit"):
2598 # Check if we're doing prepending
2599 self.conf.add(" # Do large community based prepending")
2600 self.conf.add(f" {self.bgp_functions.peer_prepend_lc(self.asn)};")
2601 # Check if we have a ISO-3166 country code
2602 if self.location.iso3166:
2603 self.conf.add(" # Do prepending based on ISO-3166 location;")
2604 self.conf.add(f" {self.bgp_functions.peer_prepend_lc_location(self.location.iso3166)};")
2605 # If we have graceful_shutdown set, add the community
2606 if self.graceful_shutdown:
2607 self.conf.add(f" {self.bgp_functions.peer_graceful_shutdown()};")
2608 # Check if we need to do any blackhole manipulation
2609 if self.blackhole_community and isinstance(self.blackhole_community, list):
2610 self.conf.add(" # If this is a blackhole route, then add the communities")
2611 self.conf.add(f" if {self.bgp_functions.is_blackhole()} then {{")
2612 self.conf.add(" bgp_community.delete([BGP_COMMUNITY_BLACKHOLE]);")
2613 # Loop with the communities we have to add
2614 for community in self.blackhole_community:
2615 # Get community component count so we can see if its a normal or large community
2616 component_count = community.count(",")
2617 if component_count == 1:
2618 self.conf.add(f" {self.bgp_functions.peer_community_add_blackhole(BirdVariable(community))};")
2619 elif component_count == 2:
2620 self.conf.add(f" {self.bgp_functions.peer_lc_add_blackhole(BirdVariable(community))};")
2621 self.conf.add(" }")
2623 # Finally accept
2624 self.conf.add(" # Finally accept")
2625 self.conf.add(" if DEBUG then")
2626 self.conf.add(
2627 f' print "[{filter_name}] Accepting ", {self.functions.route_info()}, " from t_bgp to t_bgp_peer (fallthrough)";'
2628 )
2629 self.conf.add(" accept;")
2630 self.conf.add(" }")
2632 # By default reject all routes
2633 self.conf.add(" # Reject by default")
2634 self.conf.add(" if DEBUG then")
2635 self.conf.add(
2636 f' print "[{filter_name}] Rejecting ", {self.functions.route_info()}, " from t_bgp to t_bgp_peer (fallthrough)";'
2637 )
2638 self.conf.add(" reject;")
2639 self.conf.add("};")
2640 self.conf.add("")
2642 def _peer_export_filter(self) -> None:
2643 """Peer export filter setup from peer table to peer."""
2645 filter_name = self.filter_name_export
2647 # Configure export filter to the BGP peer
2648 self.conf.add("# Export filter TO the BGP peer from the peer BGP table")
2649 self.conf.add(f"filter {filter_name} {{")
2650 # Check if we're quarantined, if we are reject routes to the peer
2651 if self.quarantine:
2652 self.conf.add(" # Peer is quarantined so reject exporting of routes")
2653 self.conf.add(" if DEBUG then")
2654 self.conf.add(
2655 f' print "[{filter_name}] Rejecting ", {self.functions.route_info()}, " from t_bgp_peer to peer (qurantined)";'
2656 )
2657 self.conf.add(" reject;")
2658 # If we're not quarantined, then export routes
2659 else:
2660 self.conf.add(" # We accept all routes going to the peer that are in the peer BGP table")
2661 self.conf.add(f' if (proto != "{self.protocol_name("4")}" && proto != "{self.protocol_name("6")}") then accept;')
2662 self.conf.add("};")
2663 self.conf.add("")
2665 def _peer_import_filter(self) -> None: # noqa: CFQ001 # pylint: disable=too-many-branches,too-many-statements
2666 """Peer import filter setup from peer to peer table."""
2668 # Set our filter name
2669 filter_name = self.filter_name_import
2671 # Configure import filter from the BGP peer
2672 self.conf.add("# Import filter FROM the BGP peer TO the peer BGP table")
2673 self.conf.add(f"filter {filter_name}")
2674 self.conf.add("string filter_name;")
2675 self.conf.add("{")
2676 self.conf.add(f' filter_name = "{filter_name}";')
2677 self.conf.add(" # Only process routes from our peer, accept everything else")
2678 self.conf.add(f' if (proto != "{self.protocol_name("4")}" && proto != "{self.protocol_name("6")}") then accept;')
2680 # Clients
2681 if self.peer_type == "customer":
2682 self.conf.add(f" {self.bgp_functions.peer_communities_strip_internal()};")
2683 self.conf.add(f" {self.bgp_functions.peer_import_customer(self.asn, self.cost)};")
2684 self.conf.add(f" {self.bgp_functions.import_filter_default()};")
2685 self.conf.add(f" {self.bgp_functions.import_filter_bogons()};")
2686 # The bgp_filter_prefix size test will exclude blackholes from checks
2687 self.conf.add(f" {self._bgp_filter_prefix_size()};")
2688 # Check if we're going to accept a blackhole route
2689 if self.route_policy_accept.bgp_customer_blackhole:
2690 self.conf.add(f" {self._bgp_filter_blackhole_size()};")
2691 else:
2692 self.conf.add(f" {self.bgp_functions.import_filter_blackhole()};")
2693 # If we're replacing the ASN we only allow private ASN's in the AS-PATH
2694 if self.replace_aspath:
2695 self.conf.add(f" {self.bgp_functions.import_filter_asn_private(BirdVariable(f'[{self.asn}]'))};")
2696 # Else, filter bogon ASN's
2697 else:
2698 self.conf.add(f" {self.bgp_functions.import_filter_asn_bogons()};")
2699 self.conf.add(
2700 f" {self.bgp_functions.import_filter_aspath_length(self.aspath_import_maxlen, self.aspath_import_minlen)};"
2701 )
2702 self.conf.add(f" {self.bgp_functions.import_filter_asn_invalid(self.asn)};")
2703 self.conf.add(f" {self.bgp_functions.import_filter_asn_transit()};")
2704 self.conf.add(f" {self.bgp_functions.import_filter_nexthop_not_peerip()};")
2706 # Peers
2707 elif self.peer_type == "peer":
2708 self.conf.add(f" {self.bgp_functions.peer_communities_strip_all()};")
2709 self.conf.add(f" {self.bgp_functions.peer_import_peer(self.asn, self.cost)};")
2710 self.conf.add(f" {self.bgp_functions.import_filter_default()};")
2711 self.conf.add(f" {self.bgp_functions.import_filter_blackhole()};")
2712 self.conf.add(f" {self.bgp_functions.import_filter_bogons()};")
2713 self.conf.add(f" {self._bgp_filter_prefix_size()};")
2714 self.conf.add(f" {self.bgp_functions.import_filter_asn_bogons()};")
2715 self.conf.add(
2716 f" {self.bgp_functions.import_filter_aspath_length(self.aspath_import_maxlen, self.aspath_import_minlen)};"
2717 )
2718 self.conf.add(f" {self.bgp_functions.import_filter_nexthop_not_peerip()};")
2719 self.conf.add(f" {self.bgp_functions.import_filter_asn_invalid(self.asn)};")
2720 self.conf.add(f" {self.bgp_functions.import_filter_asn_transit()};")
2722 # Routecollector
2723 elif self.peer_type == "routecollector":
2724 self.conf.add(f" {self.bgp_functions.peer_communities_strip_all()};")
2725 self.conf.add(f" {self.bgp_functions.import_filter_routecollector_all()};")
2726 # Routeserver
2727 elif self.peer_type == "routeserver":
2728 self.conf.add(f" {self.bgp_functions.peer_communities_strip_all()};")
2729 self.conf.add(f" {self.bgp_functions.peer_import_routeserver(self.asn, self.cost)};")
2730 self.conf.add(f" {self.bgp_functions.import_filter_default()};")
2731 self.conf.add(f" {self.bgp_functions.import_filter_blackhole()};")
2732 self.conf.add(f" {self.bgp_functions.import_filter_bogons()};")
2733 self.conf.add(f" {self._bgp_filter_prefix_size()};")
2734 self.conf.add(f" {self.bgp_functions.import_filter_asn_bogons()};")
2735 self.conf.add(
2736 f" {self.bgp_functions.import_filter_aspath_length(self.aspath_import_maxlen, self.aspath_import_minlen)};"
2737 )
2738 self.conf.add(f" {self.bgp_functions.import_filter_asn_transit()};")
2740 # Internal router peer types
2741 elif self.peer_type in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
2742 self.conf.add(f" {self.bgp_functions.import_filter_lc_no_relation()};")
2744 # If we are not accepting any default routes, just filter them all out
2745 if not self.route_policy_accept.bgp_own_default and not self.route_policy_accept.bgp_transit_default:
2746 self.conf.add(f" {self.bgp_functions.import_filter_default()};")
2747 else:
2748 # Filter own default routes if we're not accepting them
2749 if not self.route_policy_accept.bgp_own_default:
2750 self.conf.add(f" {self.bgp_functions.import_filter_own_default()};")
2751 # Filter transit default routes if we're not accepting them
2752 if not self.route_policy_accept.bgp_transit_default:
2753 self.conf.add(f" {self.bgp_functions.import_filter_transit_default()};")
2754 # Filter any non-own and non-transit default routes (invalid default routes)
2755 self.conf.add(f" {self.bgp_functions.import_filter_invalid_default()};")
2757 # If we're not accepting any blackhole routes, just filter them all out
2758 if not self.route_policy_accept.bgp_customer_blackhole and not self.route_policy_accept.bgp_own_blackhole:
2759 self.conf.add(f" {self.bgp_functions.import_filter_blackhole()};")
2760 else:
2761 # Filter customer blackhole routes if we're not accepting them
2762 if not self.route_policy_accept.bgp_customer_blackhole:
2763 self.conf.add(f" {self.bgp_functions.import_filter_customer_blackhole()};")
2764 # Filter own blackhole routes if we're not accepting them
2765 if not self.route_policy_accept.bgp_own_blackhole:
2766 self.conf.add(f" {self.bgp_functions.import_filter_own_blackhole()};")
2767 # Filter any non-own and non-transit blackhole routes (invalid blackhole routes)
2768 self.conf.add(f" {self.bgp_functions.import_filter_invalid_blackhole()};")
2769 self.conf.add(f" {self._bgp_filter_blackhole_size()};")
2771 self.conf.add(" # Bypass prefix size filters for the default route")
2772 self.conf.add(f" if !{self.functions.is_default()} then {self._bgp_filter_prefix_size()};")
2774 # Check if we're replacing the AS-PATH
2775 if self.peer_type == "internal" and self.replace_aspath:
2776 self.conf.add(f" {self.bgp_functions.import_filter_asn_private(BirdVariable('PRIVATE_ASNS'))};")
2778 # NK: Should we be filtering AS-PATH lengths for internal systems?
2779 # - probably safer to do so?
2780 self.conf.add(
2781 f" {self.bgp_functions.import_filter_aspath_length(self.aspath_import_maxlen, self.aspath_import_minlen)};"
2782 )
2784 # Transit providers
2785 elif self.peer_type == "transit":
2786 self.conf.add(f" {self.bgp_functions.peer_communities_strip_all()};")
2787 self.conf.add(f" {self.bgp_functions.peer_import_transit(self.asn, self.cost)};")
2788 # Check if we need to filter out the default route
2789 if not self.route_policy_accept.bgp_transit_default:
2790 self.conf.add(f" {self.bgp_functions.import_filter_default()};")
2792 self.conf.add(f" {self.bgp_functions.import_filter_blackhole()};")
2793 self.conf.add(" # Bypass bogon filters for the default route")
2794 self.conf.add(f" if !{self.functions.is_default()} then {self.bgp_functions.import_filter_bogons()};")
2795 self.conf.add(" # Bypass prefix size filters for the default route")
2796 self.conf.add(f" if !{self.functions.is_default()} then {self._bgp_filter_prefix_size()};")
2798 self.conf.add(f" {self.bgp_functions.import_filter_asn_bogons()};")
2799 self.conf.add(
2800 f" {self.bgp_functions.import_filter_aspath_length(self.aspath_import_maxlen, self.aspath_import_minlen)};"
2801 )
2802 self.conf.add(f" {self.bgp_functions.import_filter_asn_invalid(self.asn)};")
2803 self.conf.add(f" {self.bgp_functions.import_filter_nexthop_not_peerip()};")
2805 # With exception of routecollector (which is not supposed to be sending us routes), check the community lengths
2806 if self.peer_type != "routecollector":
2807 self.conf.add(f" {self._bgp_filter_community_lengths()};")
2809 # Flip around the meaning of filters depending on peer type
2810 # For customer and peer, it is an ALLOW list
2811 if self.peer_type in ("customer", "peer"):
2812 # Check if we're filtering allowed origin ASNs
2813 if self.has_import_origin_asn_filter:
2814 self.conf.add(
2815 f" {self.bgp_functions.import_filter_origin_asns_allow(BirdVariable(self.import_origin_asn_list_name))};"
2816 )
2817 # Check if we're filtering allowed peer ASNs
2818 if self.has_import_peer_asn_filter:
2819 self.conf.add(f" {self.bgp_functions.import_filter_asns_allow(BirdVariable(self.import_peer_asn_list_name))};")
2820 # Check if we're filtering allowed AS-PATH ASNs
2821 if self.has_import_aspath_asn_filter:
2822 self.conf.add(f" {self.bgp_functions.import_filter_aspath_allow(BirdVariable(self.import_aspath_asn_list_name))};")
2823 # Check if we're filtering allowed prefixes
2824 if self.has_import_prefix_filter:
2825 self.conf.add(" # Filter on the allowed prefixes")
2826 # Check if we have IPv4 support and output the filter
2827 import_filter_prefixes_allow = self.bgp_functions.import_filter_prefixes_allow(
2828 BirdVariable(self.import_prefix_list_name("4")), BirdVariable(self.import_prefix_list_name("6"))
2829 )
2830 self.conf.add(f" {import_filter_prefixes_allow};")
2832 # For peer types that support blackholes, add the blackhole filter
2833 if self.peer_type == "customer":
2834 import_filter_blackholes_allow = self.bgp_functions.import_filter_prefixes_blackhole_allow(
2835 BirdVariable(self.import_blackhole_prefix_list_name("4")),
2836 BirdVariable(self.import_blackhole_prefix_list_name("6")),
2837 )
2838 self.conf.add(f" {import_filter_blackholes_allow};")
2840 # For everything else it is a DENY list
2841 elif self.peer_type != "routecollector":
2842 # Check if we're filtering denied origin ASNs
2843 if self.has_import_origin_asn_filter:
2844 self.conf.add(
2845 f" {self.bgp_functions.import_filter_origin_asns_deny(BirdVariable(self.import_origin_asn_list_name))};"
2846 )
2847 # Check if we're filtering denied peer ASNs
2848 if self.has_import_peer_asn_filter:
2849 self.conf.add(f" {self.bgp_functions.import_filter_asns_deny(BirdVariable(self.import_peer_asn_list_name))};")
2850 # Check if we're filtering allowed AS-PATH ASNs
2851 if self.has_import_aspath_asn_filter:
2852 self.conf.add(f" {self.bgp_functions.import_filter_aspath_deny(BirdVariable(self.import_aspath_asn_list_name))};")
2853 # Check if we're filtering denied prefixes
2854 if self.has_import_prefix_filter:
2855 import_filter_prefixes_deny = self.bgp_functions.import_filter_prefixes_deny(
2856 BirdVariable(self.import_prefix_list_name("4")), BirdVariable(self.import_prefix_list_name("6"))
2857 )
2858 self.conf.add(f" {import_filter_prefixes_deny};")
2860 # For peer types that support blackholes, add the blackhole filter
2861 if self.peer_type in ("internal", "rrclient", "rrserver", "rrserver-rrserver"):
2862 import_filter_blackholes_deny = self.bgp_functions.import_filter_prefixes_blackhole_deny(
2863 BirdVariable(self.import_blackhole_prefix_list_name("4")),
2864 BirdVariable(self.import_blackhole_prefix_list_name("6")),
2865 )
2866 self.conf.add(f" {import_filter_blackholes_deny};")
2868 # Implementation of the denies from import_filter_deny
2869 # Deny origin AS
2870 if self.has_import_origin_asn_deny_filter:
2871 self.conf.add(
2872 f" {self.bgp_functions.import_filter_deny_origin_asns(BirdVariable(self.import_origin_asn_deny_list_name))};"
2873 )
2874 # Deny AS in path
2875 if self.has_import_aspath_asn_deny_filter:
2876 self.conf.add(f" {self.bgp_functions.import_filter_deny_aspath(BirdVariable(self.import_aspath_asn_deny_list_name))};")
2877 # Deny prefix
2878 if self.has_import_prefix_deny_filter:
2879 import_filter_prefixes_deny = self.bgp_functions.import_filter_deny_prefixes(
2880 BirdVariable(self.import_prefix_deny_list_name("4")), BirdVariable(self.import_prefix_deny_list_name("6"))
2881 )
2882 self.conf.add(f" {import_filter_prefixes_deny};")
2884 # Quarantine mode...
2885 # NK: We don't quarantine route collectors as they are automagically filtered
2886 if self.quarantine and self.peer_type != "routecollector":
2887 # Quarantine prefixes
2888 self.conf.add(" # Quarantine all prefixes received")
2889 self.conf.add(f" {self.bgp_functions.peer_quarantine()};")
2891 # Add location-based large communities
2892 if self.peer_type in ("customer", "peer", "routeserver", "transit"):
2893 # Check if we have a ISO-3166 country code
2894 if self.location.iso3166:
2895 self.conf.add(f" {self.bgp_functions.peer_import_location_iso3166(self.location.iso3166)};")
2896 # Check if we have a UN.M49 country code
2897 if self.location.unm49:
2898 self.conf.add(f" {self.bgp_functions.peer_import_location_unm49(self.location.unm49)};")
2900 # If this is a customer or internal peer type, check if we're doing replacement of the AS-PATH and add the action community
2901 if self.peer_type in ("customer", "internal"): # noqa: SIM102
2902 if self.replace_aspath:
2903 # Lastly add the large community to replace the ASN
2904 self.conf.add(
2905 f' print "[{filter_name}] Adding LC action BGP_LC_ACTION_REPLACE_ASPATH to ", {self.functions.route_info()};'
2906 )
2907 self.conf.add(" bgp_large_community.add(BGP_LC_ACTION_REPLACE_ASPATH);")
2909 # Check if we are adding a large community to incoming routes
2910 if self.large_communities.incoming:
2911 # Loop with large communities and add to the prefix
2912 for large_community in sorted(self.large_communities.incoming):
2913 if self.birdconfig_globals.debug:
2914 self.conf.add(f' print "[{filter_name}] Adding LC {large_community} to ", {self.functions.route_info()};')
2915 self.conf.add(f" bgp_large_community.add({large_community});")
2917 # Support for changing incoming local_pref
2918 if self.peer_type == "customer":
2919 self.conf.add(f" {self.bgp_functions.peer_import_localpref()};")
2921 # Enable graceful_shutdown for this prefix
2922 if self.graceful_shutdown:
2923 self.conf.add(f" {self.bgp_functions.peer_graceful_shutdown()};")
2924 # Set local_pref to 0 (GRACEFUL_SHUTDOWN) for the peer in graceful_shutdown
2925 self.conf.add(f" {self.bgp_functions.peer_import_graceful_shutdown()};")
2927 self.conf.add(" accept;")
2928 self.conf.add("};")
2929 self.conf.add("")
2931 def _setup_peer_protocol(self, ipv: str) -> None: # pylint: disable=too-many-statements,too-many-branches
2932 """Peer protocol setup for a single protocol."""
2934 protocol_state = {
2935 # Save the current protocol name
2936 "name": self.protocol_name(ipv)
2937 }
2939 # Get our source and neighbor addresses
2940 source_address = getattr(self, f"source_address{ipv}")
2941 neighbor = getattr(self, f"neighbor{ipv}")
2942 # Save IP address info
2943 protocol_state["source_address"] = source_address
2944 protocol_state["neighbor"] = neighbor
2946 self.conf.add(f"protocol bgp {self.protocol_name(ipv)} {{")
2947 self.conf.add(f' description "AS{self.asn} {self.name} - {self.description}";')
2948 self.conf.add("")
2949 self.conf.add(f" vrf {self.birdconfig_globals.vrf};")
2950 self.conf.add("")
2951 self.conf.add(" local as BGP_ASN;")
2952 self.conf.add(f" source address {source_address};")
2953 self.conf.add(" strict bind;")
2954 self.conf.add(f" neighbor {neighbor} as {self.asn};")
2955 # Check if this is a passive peer
2956 if self.passive:
2957 protocol_state["mode"] = "passive"
2958 self.conf.add(" passive;")
2959 else:
2960 protocol_state["mode"] = "active"
2962 # Add various tunables
2963 if self.connect_delay_time:
2964 self.conf.add(f" connect delay time {self.connect_delay_time};")
2965 if self.connect_retry_time:
2966 self.conf.add(f" connect retry time {self.connect_retry_time};")
2967 if self.error_wait_time:
2968 self.conf.add(f" error wait time {self.error_wait_time};")
2969 if self.multihop:
2970 self.conf.add(f" multihop {self.multihop};")
2971 if self.password:
2972 self.conf.add(f' password "{self.password}";')
2973 if self.ttl_security:
2974 self.conf.add(" ttl security on;")
2976 # Handle route reflector clients
2977 if self.peer_type in ("rrclient", "rrserver-rrserver"):
2978 # Set this peer as a route reflector client
2979 self.conf.add(" rr client;")
2980 self.conf.add(f" rr cluster id {self.bgp_attributes.rr_cluster_id};")
2982 # Setup peer table
2983 self.conf.add(f" ipv{ipv} {{")
2984 self.conf.add(f" table {self.bgp_table_name(ipv)};")
2985 self.conf.add(f" igp table master{ipv};")
2986 # Set the nexthop to ourselves for external peers
2987 if self.peer_type in ("customer", "peer", "transit", "routecollector", "routeserver"):
2988 self.conf.add(" next hop self;")
2989 # Now, if we're peering with a route reflector, or internal server
2990 elif self.peer_type in ("rrserver", "internal"):
2991 self.conf.add(" next hop self ebgp;")
2992 # Decide if we're adding all BGP paths
2993 if self.add_paths:
2994 self.conf.add(f" add paths {self.add_paths};")
2995 # Setup import and export table so we can do soft reconfiguration
2996 self.conf.add(" import table;")
2997 self.conf.add(" export table;")
2998 # Setup prefix limit
2999 prefix_limit = getattr(self, f"prefix_limit{ipv}")
3000 if prefix_limit:
3001 self.conf.add(f" import limit {prefix_limit} action {self.prefix_limit_action.value};")
3002 protocol_state["prefix_limit"] = prefix_limit
3003 # Setup filters
3004 self.conf.add(f" import filter {self.filter_name_import};")
3005 self.conf.add(f" export filter {self.filter_name_export};")
3006 self.conf.add(" };")
3007 self.conf.add("}")
3008 self.conf.add("")
3010 # Make sure we have protocols in our state
3011 if "protocols" not in self.state:
3012 self.state["protocols"] = {}
3014 # Save protocol state
3015 self.state["protocols"][f"ipv{ipv}"] = protocol_state
3017 def _setup_peer_protocols(self) -> None:
3018 """Peer protocols setup."""
3019 if self.has_ipv4:
3020 self._setup_peer_protocol("4")
3021 if self.has_ipv6:
3022 self._setup_peer_protocol("6")
3024 def _setup_peer_to_bgp_filters(self) -> None:
3025 """Peer filters to the main BGP table."""
3026 self._peer_to_bgp_export_filter()
3027 self._peer_to_bgp_import_filter()
3029 def _setup_peer_filters(self) -> None:
3030 """Peer filter setup."""
3031 self._peer_export_filter()
3032 self._peer_import_filter()
3034 def _peer_reject_non_exportable(self) -> str:
3035 """Generate the function call to peer_reject_non_exportable."""
3036 return self.bgp_functions.peer_reject_non_exportable(
3037 self.export_maxlen4, self.export_minlen4, self.export_maxlen6, self.export_minlen6
3038 )
3040 def _peer_reject_non_exportable_blackhole(self) -> str:
3041 """Generate the function call to peer_reject_non_exportable_blackhole."""
3042 return self.bgp_functions.peer_reject_non_exportable_blackhole(
3043 self.blackhole_export_maxlen4,
3044 self.blackhole_export_minlen4,
3045 self.blackhole_export_maxlen6,
3046 self.blackhole_export_minlen6,
3047 )
3049 def _bgp_filter_prefix_size(self) -> str:
3050 """Return a BGP prefix size filter for a specific IP version."""
3051 return self.bgp_functions.import_filter_prefix_size(
3052 self.import_maxlen4, self.import_minlen4, self.import_maxlen6, self.import_minlen6
3053 )
3055 def _bgp_filter_blackhole_size(self) -> str:
3056 """Return a BGP blackhole size filter for a specific IP version."""
3057 return self.bgp_functions.import_filter_blackhole_size(
3058 self.blackhole_import_maxlen4,
3059 self.blackhole_import_minlen4,
3060 self.blackhole_import_maxlen6,
3061 self.blackhole_import_minlen6,
3062 )
3064 def _bgp_filter_community_lengths(self) -> str:
3065 """Generate the BGP filter function for the community lengths."""
3066 maxlen = self.community_import_maxlen
3067 maxlen_extended = self.extended_community_import_maxlen
3068 maxlen_large = self.large_community_import_maxlen
3069 return self.bgp_functions.import_filter_community_lengths(maxlen, maxlen_extended, maxlen_large)
3071 @property
3072 def bgp_attributes(self) -> BGPAttributes:
3073 """Return the BGP protocol attributes."""
3074 return self._bgp_attributes
3076 @property
3077 def bgp_functions(self) -> BGPFunctions:
3078 """Return the BGP functions."""
3079 return self._bgp_functions
3081 @property
3082 def peer_attributes(self) -> BGPPeerAttributes:
3083 """Return our attributes."""
3084 return self._peer_attributes
3086 @property
3087 def state(self) -> Dict[str, Any]:
3088 """Return our state info."""
3089 return self._state
3091 @state.setter
3092 def state(self, state: Dict[str, Any]) -> None:
3093 """Set our state."""
3094 self.state = state
3096 @property
3097 def prev_state(self) -> Optional[Dict[str, Any]]:
3098 """Previous state info."""
3099 return self._prev_state
3101 @prev_state.setter
3102 def prev_state(self, prev_state: Dict[str, Any]) -> None:
3103 """Set previous state info."""
3104 self.prev_state = prev_state
3106 @property
3107 def name(self) -> str:
3108 """Return our name."""
3109 return self.peer_attributes.name
3111 @name.setter
3112 def name(self, name: str) -> None:
3113 """Set our name."""
3114 self.peer_attributes.name = name
3116 @property
3117 def description(self) -> str:
3118 """Return our description."""
3119 return self.peer_attributes.description
3121 @description.setter
3122 def description(self, description: str) -> None:
3123 """Set our description."""
3124 self.peer_attributes.description = description
3126 @property
3127 def location(self) -> BGPPeerLocation:
3128 """Return our location."""
3129 return self.peer_attributes.location
3131 @property
3132 def peer_type(self) -> str:
3133 """Return our type."""
3134 return self.peer_attributes.peer_type
3136 @peer_type.setter
3137 def peer_type(self, peer_type: str) -> None:
3138 """Set our peer_type."""
3139 self.peer_attributes.peer_type = peer_type
3141 @property
3142 def asn(self) -> int:
3143 """Return our ASN."""
3144 return self.peer_attributes.asn
3146 @asn.setter
3147 def asn(self, asn: int) -> None:
3148 """Set our asn."""
3149 self.peer_attributes.asn = asn
3151 @property
3152 def neighbor4(self) -> Optional[str]:
3153 """Return our IPv4 neighbor address."""
3154 return self.peer_attributes.neighbor4
3156 @neighbor4.setter
3157 def neighbor4(self, neighbor4: str) -> None:
3158 """Set our IPv4 neighbor address."""
3159 self.peer_attributes.neighbor4 = neighbor4
3161 @property
3162 def neighbor6(self) -> Optional[str]:
3163 """Return our IPv4 neighbor address."""
3164 return self.peer_attributes.neighbor6
3166 @neighbor6.setter
3167 def neighbor6(self, neighbor6: str) -> None:
3168 """Set our IPv4 neighbor address."""
3169 self.peer_attributes.neighbor6 = neighbor6
3171 @property
3172 def source_address4(self) -> Optional[str]:
3173 """Return our IPv4 source_address4 address."""
3174 return self.peer_attributes.source_address4
3176 @source_address4.setter
3177 def source_address4(self, source_address4: str) -> None:
3178 """Set our IPv4 source_address4 address."""
3179 self.peer_attributes.source_address4 = source_address4
3181 @property
3182 def source_address6(self) -> Optional[str]:
3183 """Return our IPv4 source_address6 address."""
3184 return self.peer_attributes.source_address6
3186 @source_address6.setter
3187 def source_address6(self, source_address6: str) -> None:
3188 """Set our IPv4 source_address6 address."""
3189 self.peer_attributes.source_address6 = source_address6
3191 @property
3192 def connect_delay_time(self) -> Optional[str]:
3193 """Return the value of our connect_delay_time option."""
3194 return self.peer_attributes.connect_delay_time
3196 @connect_delay_time.setter
3197 def connect_delay_time(self, connect_delay_time: str) -> None:
3198 """Set the value of our connect_delay_time option."""
3199 self.peer_attributes.connect_delay_time = connect_delay_time
3201 @property
3202 def connect_retry_time(self) -> Optional[str]:
3203 """Return the value of our connect_retry_time option."""
3204 return self.peer_attributes.connect_retry_time
3206 @connect_retry_time.setter
3207 def connect_retry_time(self, connect_retry_time: str) -> None:
3208 """Set the value of our connect_retry_time option."""
3209 self.peer_attributes.connect_retry_time = connect_retry_time
3211 @property
3212 def error_wait_time(self) -> Optional[str]:
3213 """Return the value of our error_wait_time option."""
3214 return self.peer_attributes.error_wait_time
3216 @error_wait_time.setter
3217 def error_wait_time(self, error_wait_time: str) -> None:
3218 """Set the value of our error_wait_time option."""
3219 self.peer_attributes.error_wait_time = error_wait_time
3221 @property
3222 def multihop(self) -> Optional[str]:
3223 """Return the value of our multihop option."""
3224 return self.peer_attributes.multihop
3226 @multihop.setter
3227 def multihop(self, multihop: str) -> None:
3228 """Set the value of our multihop option."""
3229 self.peer_attributes.multihop = multihop
3231 @property
3232 def password(self) -> Optional[str]:
3233 """Return the value of our password option."""
3234 return self.peer_attributes.password
3236 @password.setter
3237 def password(self, password: str) -> None:
3238 """Set the value of our password option."""
3239 self.peer_attributes.password = password
3241 @property
3242 def ttl_security(self) -> bool:
3243 """Return the value of our ttl_security option."""
3244 return self.peer_attributes.ttl_security
3246 @ttl_security.setter
3247 def ttl_security(self, ttl_security: bool) -> None:
3248 """Set the value of our ttl_security option."""
3249 self.peer_attributes.ttl_security = ttl_security
3251 @property
3252 def cost(self) -> int:
3253 """Return our prefix cost."""
3254 return self.peer_attributes.cost
3256 @cost.setter
3257 def cost(self, cost: int) -> None:
3258 """Set our prefix cost."""
3259 self.peer_attributes.cost = cost
3261 @property
3262 def add_paths(self) -> Optional[str]:
3263 """Return the setting for adding all BGP paths."""
3264 return self.peer_attributes.add_paths
3266 @add_paths.setter
3267 def add_paths(self, add_paths: str) -> None:
3268 """Set our preference for adding all BGP paths."""
3269 self.peer_attributes.add_paths = add_paths
3271 @property
3272 def graceful_shutdown(self) -> bool:
3273 """Peer graceful_shutdown property."""
3274 return self.peer_attributes.graceful_shutdown
3276 @graceful_shutdown.setter
3277 def graceful_shutdown(self, graceful_shutdown: bool) -> None:
3278 """Set the graceful_shutdown state of the peer."""
3279 self.peer_attributes.graceful_shutdown = graceful_shutdown
3281 @property
3282 def communities(self) -> BGPPeerCommunities:
3283 """Return our communities."""
3284 return self.peer_attributes.communities
3286 @property
3287 def large_communities(self) -> BGPPeerLargeCommunities:
3288 """Return our large communities."""
3289 return self.peer_attributes.large_communities
3291 @property
3292 def prepend(self) -> BGPPeerPrepend:
3293 """Return our prepending."""
3294 return self.peer_attributes.prepend
3296 @property
3297 def passive(self) -> bool:
3298 """Return if we only accept connections, not make them."""
3299 return self.peer_attributes.passive
3301 @passive.setter
3302 def passive(self, passive: bool) -> None:
3303 """Set our passive mode."""
3304 self.peer_attributes.passive = passive
3306 @property
3307 def route_policy_redistribute(self) -> BGPPeerRoutePolicyRedistribute:
3308 """Return our route redistribute policy."""
3309 return self.peer_attributes.route_policy_redistribute
3311 @property
3312 def route_policy_accept(self) -> BGPPeerRoutePolicyAccept:
3313 """Return if we're accepting the default route or not."""
3314 return self.peer_attributes.route_policy_accept
3316 @property
3317 def import_filter_policy(self) -> BGPPeerImportFilterPolicy:
3318 """Return the our import filter policy."""
3319 return self.peer_attributes.import_filter_policy
3321 @property
3322 def import_filter_deny_policy(self) -> BGPPeerImportFilterDenyPolicy:
3323 """Return the our import filter deny policy."""
3324 return self.peer_attributes.import_filter_deny_policy
3326 @property
3327 def export_filter_policy(self) -> BGPPeerExportFilterPolicy:
3328 """Return the our export filter policy."""
3329 return self.peer_attributes.export_filter_policy
3331 @property
3332 def prefix_limit_action(self) -> BGPPeerImportPrefixLimitAction:
3333 """Return our prefix limit action."""
3334 return self.peer_attributes.prefix_limit_action
3336 @prefix_limit_action.setter
3337 def prefix_limit_action(self, prefix_limit_action: BGPPeerImportPrefixLimitAction) -> None:
3338 """Set our prefix limit action."""
3339 self.peer_attributes.prefix_limit_action = prefix_limit_action
3341 @property
3342 def prefix_limit4(self) -> BGPPeerPrefixLimit:
3343 """Return our IPv4 prefix limit."""
3344 return self.peer_attributes.prefix_limit4 or self.peer_attributes.prefix_limit4_peeringdb
3346 @prefix_limit4.setter
3347 def prefix_limit4(self, prefix_limit4: Optional[str]) -> None:
3348 """Set our IPv4 prefix limit."""
3349 self.peer_attributes.prefix_limit4 = prefix_limit4
3351 @property
3352 def prefix_limit6(self) -> BGPPeerPrefixLimit:
3353 """Return our IPv4 prefix limit."""
3354 return self.peer_attributes.prefix_limit6 or self.peer_attributes.prefix_limit6_peeringdb
3356 @prefix_limit6.setter
3357 def prefix_limit6(self, prefix_limit6: Optional[str]) -> None:
3358 """Set our IPv6 prefix limit."""
3359 self.peer_attributes.prefix_limit6 = prefix_limit6
3361 @property
3362 def prefix_limit4_peeringdb(self) -> BGPPeerPrefixLimit:
3363 """Return our IPv4 prefix limit from PeeringDB."""
3364 return self.peer_attributes.prefix_limit4_peeringdb
3366 @prefix_limit4_peeringdb.setter
3367 def prefix_limit4_peeringdb(self, prefix_limit4: Optional[str]) -> None:
3368 """Set our IPv4 prefix limit from PeeringDB."""
3369 self.peer_attributes.prefix_limit4_peeringdb = prefix_limit4
3371 @property
3372 def prefix_limit6_peeringdb(self) -> BGPPeerPrefixLimit:
3373 """Return our IPv6 prefix limit from PeeringDB."""
3374 return self.peer_attributes.prefix_limit6_peeringdb
3376 @prefix_limit6_peeringdb.setter
3377 def prefix_limit6_peeringdb(self, prefix_limit6: Optional[str]) -> None:
3378 """Set our IPv6 prefix limit from PeeringDB."""
3379 self.peer_attributes.prefix_limit6_peeringdb = prefix_limit6
3381 @property
3382 def quarantine(self) -> bool:
3383 """Peer quarantine property."""
3384 return self.peer_attributes.quarantine
3386 @quarantine.setter
3387 def quarantine(self, quarantine: bool) -> None:
3388 """Set the quarantine state of the peer."""
3389 self.peer_attributes.quarantine = quarantine
3391 @property
3392 def replace_aspath(self) -> bool:
3393 """Return the ASN which replaces the AS-PATH."""
3394 return self.peer_attributes.replace_aspath
3396 @replace_aspath.setter
3397 def replace_aspath(self, replace_aspath: bool) -> None:
3398 """Set the ASN which will replace the AS-PATH."""
3399 self.peer_attributes.replace_aspath = replace_aspath
3401 @property
3402 def blackhole_community(self) -> Optional[Union[bool, List[str]]]:
3403 """Return the current value of blackhole_community."""
3404 return self.peer_attributes.blackhole_community
3406 @blackhole_community.setter
3407 def blackhole_community(self, blackhole_community: Union[List[str], bool]) -> None:
3408 """Set the blackhole community."""
3409 self.peer_attributes.blackhole_community = blackhole_community
3411 @property
3412 def constraints(self) -> BGPPeerConstraints:
3413 """Return our own BGP constraint overrides."""
3414 return self.peer_attributes.constraints
3416 @property
3417 def global_constraints(self) -> BGPPeertypeConstraints:
3418 """Return the global constraints for our peer type."""
3419 # Work out the peer type we're going to use for the global constraints
3420 peer_type = self.peer_type
3421 if peer_type == "customer" and self.replace_aspath:
3422 peer_type = "customer.private"
3424 return self.bgp_attributes.peertype_constraints[peer_type]
3426 # IPV4 BLACKHOLE IMPORT PREFIX LENGTHS
3428 @property
3429 def blackhole_import_maxlen4(self) -> int:
3430 """Return the current value of blackhole_import_maxlen4."""
3431 return self.constraints.blackhole_import_maxlen4 or self.global_constraints.blackhole_import_maxlen4
3433 @blackhole_import_maxlen4.setter
3434 def blackhole_import_maxlen4(self, blackhole_import_maxlen4: int) -> None:
3435 """Setter for blackhole_import_maxlen4."""
3436 self.constraints.blackhole_import_maxlen4 = blackhole_import_maxlen4
3438 @property
3439 def blackhole_import_minlen4(self) -> int:
3440 """Return the current value of blackhole_import_minlen4."""
3441 return self.constraints.blackhole_import_minlen4 or self.global_constraints.blackhole_import_minlen4
3443 @blackhole_import_minlen4.setter
3444 def blackhole_import_minlen4(self, blackhole_import_minlen4: int) -> None:
3445 """Setter for blackhole_import_minlen4."""
3446 self.constraints.blackhole_import_minlen4 = blackhole_import_minlen4
3448 # IPV4 BLACKHOLE EXPORT PREFIX LENGTHS
3450 @property
3451 def blackhole_export_maxlen4(self) -> int:
3452 """Return the current value of blackhole_export_maxlen4."""
3453 return self.constraints.blackhole_export_maxlen4 or self.global_constraints.blackhole_export_maxlen4
3455 @blackhole_export_maxlen4.setter
3456 def blackhole_export_maxlen4(self, blackhole_export_maxlen4: int) -> None:
3457 """Setter for blackhole_export_maxlen4."""
3458 self.constraints.blackhole_export_maxlen4 = blackhole_export_maxlen4
3460 @property
3461 def blackhole_export_minlen4(self) -> int:
3462 """Return the current value of blackhole_export_minlen4."""
3463 return self.constraints.blackhole_export_minlen4 or self.global_constraints.blackhole_export_minlen4
3465 @blackhole_export_minlen4.setter
3466 def blackhole_export_minlen4(self, blackhole_export_minlen4: int) -> None:
3467 """Setter for blackhole_export_minlen4."""
3468 self.constraints.blackhole_export_minlen4 = blackhole_export_minlen4
3470 # IPV6 BLACKHOLE IMPORT PREFIX LENGTHS
3472 @property
3473 def blackhole_import_maxlen6(self) -> int:
3474 """Return the current value of blackhole_import_maxlen6."""
3475 return self.constraints.blackhole_import_maxlen6 or self.global_constraints.blackhole_import_maxlen6
3477 @blackhole_import_maxlen6.setter
3478 def blackhole_import_maxlen6(self, blackhole_import_maxlen6: int) -> None:
3479 """Setter for blackhole_import_maxlen6."""
3480 self.constraints.blackhole_import_maxlen6 = blackhole_import_maxlen6
3482 @property
3483 def blackhole_import_minlen6(self) -> int:
3484 """Return the current value of blackhole_import_minlen6."""
3485 return self.constraints.blackhole_import_minlen6 or self.global_constraints.blackhole_import_minlen6
3487 @blackhole_import_minlen6.setter
3488 def blackhole_import_minlen6(self, blackhole_import_minlen6: int) -> None:
3489 """Setter for blackhole_import_minlen6."""
3490 self.constraints.blackhole_import_minlen6 = blackhole_import_minlen6
3492 # IPV6 BLACKHOLE EXPORT PREFIX LENGTHS
3494 @property
3495 def blackhole_export_maxlen6(self) -> int:
3496 """Return the current value of blackhole_export_maxlen6."""
3497 return self.constraints.blackhole_export_maxlen6 or self.global_constraints.blackhole_export_maxlen6
3499 @blackhole_export_maxlen6.setter
3500 def blackhole_export_maxlen6(self, blackhole_export_maxlen6: int) -> None:
3501 """Setter for blackhole_export_maxlen6."""
3502 self.constraints.blackhole_export_maxlen6 = blackhole_export_maxlen6
3504 @property
3505 def blackhole_export_minlen6(self) -> int:
3506 """Return the current value of blackhole_export_minlen6."""
3507 return self.constraints.blackhole_export_minlen6 or self.global_constraints.blackhole_export_minlen6
3509 @blackhole_export_minlen6.setter
3510 def blackhole_export_minlen6(self, blackhole_export_minlen6: int) -> None:
3511 """Setter for blackhole_export_minlen6."""
3512 self.constraints.blackhole_export_minlen6 = blackhole_export_minlen6
3514 # IPV4 IMPORT PREFIX LENGTHS
3516 @property
3517 def import_maxlen4(self) -> int:
3518 """Return the current value of import_maxlen4."""
3519 return self.constraints.import_maxlen4 or self.global_constraints.import_maxlen4
3521 @import_maxlen4.setter
3522 def import_maxlen4(self, import_maxlen4: int) -> None:
3523 """Setter for import_maxlen4."""
3524 self.constraints.import_maxlen4 = import_maxlen4
3526 @property
3527 def import_minlen4(self) -> int:
3528 """Return the current value of import_minlen4."""
3529 return self.constraints.import_minlen4 or self.global_constraints.import_minlen4
3531 @import_minlen4.setter
3532 def import_minlen4(self, import_minlen4: int) -> None:
3533 """Setter for import_minlen4."""
3534 self.constraints.import_minlen4 = import_minlen4
3536 # IPV4 EXPORT PREFIX LENGHTS
3538 @property
3539 def export_maxlen4(self) -> int:
3540 """Return the current value of export_maxlen4."""
3541 return self.constraints.export_maxlen4 or self.global_constraints.export_maxlen4
3543 @export_maxlen4.setter
3544 def export_maxlen4(self, export_maxlen4: int) -> None:
3545 """Setter for export_maxlen4."""
3546 self.constraints.export_maxlen4 = export_maxlen4
3548 @property
3549 def export_minlen4(self) -> int:
3550 """Return the current value of export_minlen4."""
3551 return self.constraints.export_minlen4 or self.global_constraints.export_minlen4
3553 @export_minlen4.setter
3554 def export_minlen4(self, export_minlen4: int) -> None:
3555 """Setter for export_minlen4."""
3556 self.constraints.export_minlen4 = export_minlen4
3558 # IPV6 IMPORT LENGTHS
3560 @property
3561 def import_maxlen6(self) -> int:
3562 """Return the current value of import_maxlen6."""
3563 return self.constraints.import_maxlen6 or self.global_constraints.import_maxlen6
3565 @import_maxlen6.setter
3566 def import_maxlen6(self, import_maxlen6: int) -> None:
3567 """Setter for import_maxlen6."""
3568 self.constraints.import_maxlen6 = import_maxlen6
3570 @property
3571 def import_minlen6(self) -> int:
3572 """Return the current value of import_minlen6."""
3573 return self.constraints.import_minlen6 or self.global_constraints.import_minlen6
3575 @import_minlen6.setter
3576 def import_minlen6(self, import_minlen6: int) -> None:
3577 """Setter for import_minlen6."""
3578 self.constraints.import_minlen6 = import_minlen6
3580 # IPV6 EXPORT LENGTHS
3582 @property
3583 def export_minlen6(self) -> int:
3584 """Return the current value of export_minlen6."""
3585 return self.constraints.export_minlen6 or self.global_constraints.export_minlen6
3587 @export_minlen6.setter
3588 def export_minlen6(self, export_minlen6: int) -> None:
3589 """Setter for export_minlen6."""
3590 self.constraints.export_minlen6 = export_minlen6
3592 @property
3593 def export_maxlen6(self) -> int:
3594 """Return the current value of export_maxlen6."""
3595 return self.constraints.export_maxlen6 or self.global_constraints.export_maxlen6
3597 @export_maxlen6.setter
3598 def export_maxlen6(self, export_maxlen6: int) -> None:
3599 """Setter for export_maxlen6."""
3600 self.constraints.export_maxlen6 = export_maxlen6
3602 # AS PATH LENGTHS
3604 @property
3605 def aspath_import_minlen(self) -> int:
3606 """Return the current value of aspath_import_minlen."""
3607 return self.constraints.aspath_import_minlen or self.global_constraints.aspath_import_minlen
3609 @aspath_import_minlen.setter
3610 def aspath_import_minlen(self, aspath_import_minlen: int) -> None:
3611 """Set the AS path minlen."""
3612 self.constraints.aspath_import_minlen = aspath_import_minlen
3614 @property
3615 def aspath_import_maxlen(self) -> int:
3616 """Return the current value of aspath_import_maxlen."""
3617 return self.constraints.aspath_import_maxlen or self.global_constraints.aspath_import_maxlen
3619 @aspath_import_maxlen.setter
3620 def aspath_import_maxlen(self, aspath_import_maxlen: int) -> None:
3621 """Set the AS path maxlen."""
3622 self.constraints.aspath_import_maxlen = aspath_import_maxlen
3624 # COMMUNITY LENGTHS
3626 @property
3627 def community_import_maxlen(self) -> int:
3628 """Return the current value of community_import_maxlen."""
3629 return self.constraints.community_import_maxlen or self.global_constraints.community_import_maxlen
3631 @community_import_maxlen.setter
3632 def community_import_maxlen(self, community_import_maxlen: int) -> None:
3633 """Set the value of community_import_maxlen."""
3634 self.constraints.community_import_maxlen = community_import_maxlen
3636 @property
3637 def extended_community_import_maxlen(self) -> int:
3638 """Return the current value of extended_community_import_maxlen."""
3639 return self.constraints.extended_community_import_maxlen or self.global_constraints.extended_community_import_maxlen
3641 @extended_community_import_maxlen.setter
3642 def extended_community_import_maxlen(self, extended_community_import_maxlen: int) -> None:
3643 """Set the value of extended_community_import_maxlen."""
3644 self.constraints.extended_community_import_maxlen = extended_community_import_maxlen
3646 @property
3647 def large_community_import_maxlen(self) -> int:
3648 """Return the current value of large_community_import_maxlen."""
3649 return self.constraints.large_community_import_maxlen or self.global_constraints.large_community_import_maxlen
3651 @large_community_import_maxlen.setter
3652 def large_community_import_maxlen(self, large_community_import_maxlen: int) -> None:
3653 """Set the value of large_community_import_maxlen."""
3654 self.constraints.large_community_import_maxlen = large_community_import_maxlen
3656 #
3657 # Helper properties
3658 #
3660 @property
3661 def import_aspath_asn_list_name(self) -> str:
3662 """Return our AS-PATH ASN list name."""
3663 return f"bgp_AS{self.asn}_{self.name}_aspath_asns_import"
3665 @property
3666 def import_aspath_asn_deny_list_name(self) -> str:
3667 """Return our AS-PATH ASN deny list name."""
3668 return f"bgp_AS{self.asn}_{self.name}_aspath_asns_deny_import"
3670 @property
3671 def import_origin_asn_list_name(self) -> str:
3672 """Return our origin ASN list name."""
3673 return f"bgp_AS{self.asn}_{self.name}_origin_asns_import"
3675 @property
3676 def import_origin_asn_deny_list_name(self) -> str:
3677 """Return our origin ASN deny list name."""
3678 return f"bgp_AS{self.asn}_{self.name}_origin_asns_deny_import"
3680 @property
3681 def export_origin_asn_list_name(self) -> str:
3682 """Return our origin ASN list name."""
3683 return f"bgp_AS{self.asn}_{self.name}_origin_asns_export"
3685 @property
3686 def import_peer_asn_list_name(self) -> str:
3687 """Return our peer ASN list name."""
3688 return f"bgp_AS{self.asn}_{self.name}_peer_asns_import"
3690 @property
3691 def has_import_aspath_asn_filter(self) -> BGPPeerFilterItem:
3692 """Return if we filter on ASNs in the AS-PATH."""
3694 # We pull in "origin_asns" here to populate the aspath_asns for better filtering
3695 if self.peer_type in ("customer", "peer"):
3696 return (
3697 self.import_filter_policy.aspath_asns or self.import_filter_policy.origin_asns or self.import_filter_policy.as_sets
3698 )
3700 return self.import_filter_policy.aspath_asns
3702 @property
3703 def has_import_aspath_asn_deny_filter(self) -> BGPPeerFilterItem:
3704 """Return if we deny filter on ASNs in the AS-PATH."""
3706 return self.import_filter_deny_policy.aspath_asns
3708 @property
3709 def has_import_origin_asn_filter(self) -> BGPPeerFilterItem:
3710 """Return if we filter import origin ASNs."""
3711 return self.import_filter_policy.origin_asns or self.import_filter_policy.as_sets
3713 @property
3714 def has_import_origin_asn_deny_filter(self) -> BGPPeerFilterItem:
3715 """Return if we deny filter import origin ASNs."""
3716 return self.import_filter_deny_policy.origin_asns
3718 @property
3719 def has_export_origin_asn_filter(self) -> BGPPeerFilterItem:
3720 """Return if we filter export origin ASNs."""
3721 return self.export_filter_policy.origin_asns
3723 @property
3724 def has_import_peer_asn_filter(self) -> BGPPeerFilterItem:
3725 """Return if we filter on peer ASNs."""
3726 return self.import_filter_policy.peer_asns
3728 @property
3729 def has_ipv4(self) -> bool:
3730 """Return if we have IPv4."""
3731 return self.neighbor4 is not None
3733 @property
3734 def has_ipv6(self) -> bool:
3735 """Peer is configured with IPv6."""
3736 return self.neighbor6 is not None
3738 @property
3739 def has_import_prefix_filter(self) -> BGPPeerFilterItem:
3740 """Peer has a import prefix filter."""
3741 return self.import_filter_policy.prefixes or self.import_filter_policy.as_sets
3743 @property
3744 def has_import_prefix_deny_filter(self) -> BGPPeerFilterItem:
3745 """Peer has a import prefix deny filter."""
3746 return self.import_filter_deny_policy.prefixes
3748 @property
3749 def has_export_prefix_filter(self) -> BGPPeerFilterItem:
3750 """Peer has a export prefix filter."""
3751 return self.export_filter_policy.prefixes