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

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/>. 

18 

19"""BIRD BGP protocol peer configuration.""" 

20 

21# pylint: disable=too-many-lines 

22 

23import fnmatch 

24import logging 

25import re 

26from typing import Any, Dict, List, Optional, Union 

27 

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) 

58 

59__all__ = ["ProtocolBGPPeer"] 

60 

61 

62class ProtocolBGPPeer(SectionProtocolBase): # pylint: disable=too-many-instance-attributes,too-many-public-methods 

63 """BIRD BGP protocol peer configuration.""" 

64 

65 _bgp_attributes: BGPAttributes 

66 _bgp_functions: BGPFunctions 

67 _peer_attributes: BGPPeerAttributes 

68 _state: Dict[str, Any] 

69 _prev_state: Optional[Dict[str, Any]] 

70 

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) 

84 

85 # Initialize our attributes 

86 self._bgp_attributes = bgp_attributes 

87 self._bgp_functions = bgp_functions 

88 self._peer_attributes = BGPPeerAttributes() 

89 self._state = {} 

90 

91 # Save our name and configuration 

92 self.name = peer_name 

93 

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] 

103 

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"] 

108 

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") 

126 

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") 

130 

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"] 

135 

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 

160 

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 ) 

168 

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"] 

182 

183 # INTERNAL: Dynamically set the section 

184 self._section = f"BGP Peer: {self.asn} - {self.name}" 

185 

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'") 

205 

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"] 

219 

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"] 

225 

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"] 

233 

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)) 

244 

245 # 

246 # bgp:peers:$PEER:outgoing_communities 

247 # 

248 

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 

316 

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 

408 

409 # 

410 # bgp:peers:$PEER:outgoing_large_communities 

411 # 

412 

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 

480 

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 

572 

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"] 

579 

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 

604 

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 

627 

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}'") 

727 

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}'") 

731 

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}'") 

735 

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 

743 

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 ) 

760 

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) 

777 

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) 

794 

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) 

806 

807 # 

808 # bgp:peers:$PPER:accept 

809 # 

810 

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 

818 

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 

823 

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) 

834 

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 ) 

870 

871 # 

872 # bgp:peers:$PEER:prepend 

873 # 

874 

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 

942 

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 

1036 

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)) 

1077 

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) 

1154 

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"] 

1160 

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"] 

1166 

1167 # 

1168 # NETWORK AND STATE (CACHE) RELATED QUERIES 

1169 # 

1170 

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] = {} 

1175 

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) 

1211 

1212 # Grab PeeringDB entries 

1213 peeringdb = PeeringDB() 

1214 peeringdb_info = peeringdb.get_prefix_limits(self.asn) 

1215 

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}'") 

1219 

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}'") 

1223 

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"] 

1253 

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"] 

1283 

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": []} 

1289 

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) 

1294 

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"] 

1307 

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"] 

1321 

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"] 

1335 

1336 else: 

1337 if not self.birdconfig_globals.suppress_info: 

1338 logging.info("[bgp:peer:%s] Retrieving IRR information for AS-SETs", self.name) 

1339 

1340 # Grab BGPQ3 object to use below 

1341 bgpq3 = BGPQ3() 

1342 

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}'") 

1348 

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}'") 

1356 

1357 # Add our ASN's onto the filter policy origin ASNs list 

1358 self.import_filter_policy.origin_asns_irr.extend(irr_asns) 

1359 

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"]) 

1389 

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"]) 

1419 

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] 

1434 

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] 

1449 

1450 def configure(self) -> None: # noqa: CFQ001 # pylint: disable=too-many-branches,too-many-statements 

1451 """Configure BGP peer.""" 

1452 

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) 

1455 

1456 super().configure() 

1457 

1458 # Save basic peer information 

1459 self.state["asn"] = self.asn 

1460 self.state["description"] = self.description 

1461 self.state["type"] = self.peer_type 

1462 

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") 

1469 

1470 self.state["import_filter"] = {} 

1471 self.state["import_filter"]["as_sets"] = self.import_filter_policy.as_sets 

1472 

1473 self.state["import_filter_deny"] = {} 

1474 

1475 self.state["export_filter"] = {} 

1476 

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 

1508 

1509 # Make sure we keep our graceful shutdown state 

1510 self.state["graceful_shutdown"] = self.graceful_shutdown 

1511 

1512 # Make sure we keep our quarantine state 

1513 self.state["quarantine"] = self.quarantine 

1514 

1515 self.conf.add("") 

1516 self.conf.add(f"# Peer type: {self.peer_type}") 

1517 self.conf.add("") 

1518 

1519 # Setup routing tables 

1520 self._setup_peer_tables() 

1521 

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() 

1527 

1528 # Setup allowed prefixes 

1529 self._setup_peer_import_prefix_filter() 

1530 self._setup_peer_export_prefix_filter() 

1531 

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() 

1536 

1537 # BGP peer to main table 

1538 self._setup_peer_to_bgp_filters() 

1539 

1540 # BGP peer filters 

1541 self._setup_peer_filters() 

1542 

1543 # BGP peer protocols 

1544 self._setup_peer_protocols() 

1545 

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) 

1557 

1558 # End of peer 

1559 self.conf.add("") 

1560 

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"] = {} 

1566 

1567 # Save our configuration 

1568 self.birdconfig_globals.state["bgp"]["peers"][self.name] = self.state 

1569 

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}" 

1573 

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" 

1577 

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" 

1582 

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" 

1587 

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" 

1592 

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" 

1597 

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" 

1601 

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" 

1605 

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" 

1609 

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" 

1613 

1614 def _setup_peer_tables(self) -> None: 

1615 """Peering routing table setup.""" 

1616 

1617 # Start with no tables as we can have IPv4 and/or IPv6 tables below 

1618 state_tables = {} 

1619 

1620 self.tables.conf.append(f"# BGP Peer Tables: {self.asn} - {self.name}") 

1621 

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") 

1626 

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") 

1631 

1632 self.tables.conf.append("") 

1633 

1634 # Store our BGP table names 

1635 self.state["tables"] = state_tables 

1636 

1637 def _setup_import_aspath_asns_filter(self) -> None: # pylint: disable=too-many-branches 

1638 """AS-PATH ASN import list setup.""" 

1639 

1640 # Short circuit and exit if we have none 

1641 if not self.has_import_aspath_asn_filter: 

1642 return 

1643 

1644 state = {} 

1645 

1646 aspath_asns = [] 

1647 calculated_aspath_asns = [] 

1648 

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}") 

1653 

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 

1658 

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) 

1668 

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) 

1684 

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) 

1698 

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("") 

1708 

1709 # Store calculated aspath filter info in our state 

1710 state["calculated"] = calculated_aspath_asns 

1711 

1712 # Save state 

1713 if "import_filter" not in self.state: 

1714 self.state["import_filter"] = {} 

1715 self.state["import_filter"]["aspath_asns"] = state 

1716 

1717 def _setup_import_origin_asns_filter(self) -> None: 

1718 """Origin ASN import list setup.""" 

1719 

1720 # Short circuit and exit if we have none 

1721 if not self.has_import_origin_asn_filter: 

1722 return 

1723 

1724 state = {} 

1725 

1726 origin_asns = [] 

1727 

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 

1732 

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) 

1741 

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 

1746 

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) 

1756 

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("") 

1766 

1767 # Save state 

1768 if "import_filter" not in self.state: 

1769 self.state["import_filter"] = {} 

1770 self.state["import_filter"]["origin_asns"] = state 

1771 

1772 def _setup_import_aspath_asns_deny_filter(self) -> None: # pylint: disable=too-many-branches 

1773 """AS-PATH ASN import deny list setup.""" 

1774 

1775 # Short circuit and exit if we have none 

1776 if not self.has_import_aspath_asn_deny_filter: 

1777 return 

1778 

1779 aspath_asns: List[str] = [] 

1780 

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) 

1790 

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("") 

1800 

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 

1805 

1806 def _setup_import_origin_asns_deny_filter(self) -> None: 

1807 """Origin ASN import list setup.""" 

1808 

1809 # Short circuit and exit if we have none 

1810 if not self.has_import_origin_asn_deny_filter: 

1811 return 

1812 

1813 origin_asns = [] 

1814 

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) 

1825 

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("") 

1835 

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 

1840 

1841 def _setup_export_origin_asns_filter(self) -> None: 

1842 """Origin ASN export list setup.""" 

1843 

1844 # Short circuit and exit if we have none 

1845 if not self.has_export_origin_asn_filter: 

1846 return 

1847 

1848 state = {} 

1849 

1850 origin_asns = [] 

1851 

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 

1856 

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) 

1865 

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("") 

1875 

1876 # Save state 

1877 if "export_filter" not in self.state: 

1878 self.state["export_filter"] = {} 

1879 self.state["export_filter"]["origin_asns"] = state 

1880 

1881 def _setup_import_peer_asns_filter(self) -> None: 

1882 """Peer ASN import list setup.""" 

1883 

1884 # Short circuit and exit if we have none 

1885 if not self.has_import_peer_asn_filter: 

1886 return 

1887 

1888 state = {} 

1889 

1890 peer_asns = [] 

1891 

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}") 

1899 

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("") 

1909 

1910 # Save state 

1911 if "import_filter" not in self.state: 

1912 self.state["import_filter"] = {} 

1913 self.state["import_filter"]["peer_asns"] = state 

1914 

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.""" 

1919 

1920 # Short circuit and exit if we have none 

1921 if not self.has_import_prefix_filter: 

1922 return 

1923 

1924 state: Dict[str, Dict[str, Any]] = {} 

1925 

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) 

1939 

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] 

1944 

1945 import_prefixes = [] 

1946 import_blackholes = [] 

1947 

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 

1954 

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") 

1966 

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 

1973 

1974 import_prefixes_irr = [] 

1975 import_blackholes_irr = [] 

1976 

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) 

1986 

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) 

1997 

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("") 

2007 

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("") 

2019 

2020 # Save state 

2021 if "import_filter" not in self.state: 

2022 self.state["import_filter"] = {} 

2023 self.state["import_filter"]["prefixes"] = state 

2024 

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.""" 

2029 

2030 # Short circuit and exit if we have none 

2031 if not self.has_import_prefix_deny_filter: 

2032 return 

2033 

2034 state: Dict[str, List[str]] = {} 

2035 

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) 

2043 

2044 # Output prefix definitions 

2045 for ipv in ["4", "6"]: 

2046 import_prefix_list = import_prefix_lists[ipv] 

2047 

2048 import_prefixes = [] 

2049 

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) 

2055 

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") 

2060 

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("") 

2070 

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 

2075 

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.""" 

2078 

2079 # Short circuit and exit if we have none 

2080 if not self.has_export_prefix_filter: 

2081 return 

2082 

2083 state: Dict[str, Dict[str, Any]] = {} 

2084 

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) 

2092 

2093 # Output prefix definitions 

2094 for ipv in ["4", "6"]: 

2095 export_prefix_list = export_prefix_lists[ipv] 

2096 

2097 export_prefixes = [] 

2098 

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 

2105 

2106 for prefix in export_prefix_list: 

2107 export_prefixes.append(prefix) 

2108 

2109 # Sort and unique our results 

2110 export_prefixes = sorted(set(export_prefixes)) 

2111 

2112 # Add title for this section 

2113 export_prefixes.insert(0, f"# {len(export_prefix_list)} explicitly defined") 

2114 

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("") 

2124 

2125 # Save state 

2126 if "export_filter" not in self.state: 

2127 self.state["export_filter"] = {} 

2128 self.state["export_filter"]["prefixes"] = state 

2129 

2130 def _peer_to_bgp_export_filter(self) -> None: 

2131 """Export filters into our main BGP routing table from the BGP peer table.""" 

2132 

2133 # Set our filter name 

2134 filter_name = self.filter_name_export_bgp 

2135 

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("") 

2154 

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.""" 

2159 

2160 # Set our filter name 

2161 filter_name = self.filter_name_import_bgp 

2162 

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("") 

2172 

2173 # Reject NOADVERTISE 

2174 self.conf.add(f" {self.bgp_functions.peer_reject_noadvertise()};") 

2175 

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};") 

2206 

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)};") 

2213 

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()};") 

2221 

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)};") 

2228 

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)};") 

2274 

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)};") 

2281 

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)};") 

2286 

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)};") 

2292 

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)};") 

2297 

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)};") 

2302 

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)};") 

2308 

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)};") 

2314 

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)};") 

2319 

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(" }") 

2331 

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;") 

2348 

2349 # End of BGP type tests 

2350 self.conf.add(" }") 

2351 

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))};") 

2458 

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()};") 

2463 

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 ) 

2469 

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)};") 

2472 

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};") 

2487 

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};") 

2494 

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 ) 

2499 

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};") 

2506 

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)};") 

2509 

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};") 

2524 

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};") 

2531 

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)};") 

2534 

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};") 

2549 

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};") 

2556 

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 ) 

2561 

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};") 

2576 

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 ) 

2582 

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 ) 

2588 

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};") 

2595 

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(" }") 

2622 

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(" }") 

2631 

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("") 

2641 

2642 def _peer_export_filter(self) -> None: 

2643 """Peer export filter setup from peer table to peer.""" 

2644 

2645 filter_name = self.filter_name_export 

2646 

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("") 

2664 

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.""" 

2667 

2668 # Set our filter name 

2669 filter_name = self.filter_name_import 

2670 

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;') 

2679 

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()};") 

2705 

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()};") 

2721 

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()};") 

2739 

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()};") 

2743 

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()};") 

2756 

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()};") 

2770 

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()};") 

2773 

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'))};") 

2777 

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 ) 

2783 

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()};") 

2791 

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()};") 

2797 

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()};") 

2804 

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()};") 

2808 

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};") 

2831 

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};") 

2839 

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};") 

2859 

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};") 

2867 

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};") 

2883 

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()};") 

2890 

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)};") 

2899 

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);") 

2908 

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});") 

2916 

2917 # Support for changing incoming local_pref 

2918 if self.peer_type == "customer": 

2919 self.conf.add(f" {self.bgp_functions.peer_import_localpref()};") 

2920 

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()};") 

2926 

2927 self.conf.add(" accept;") 

2928 self.conf.add("};") 

2929 self.conf.add("") 

2930 

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.""" 

2933 

2934 protocol_state = { 

2935 # Save the current protocol name 

2936 "name": self.protocol_name(ipv) 

2937 } 

2938 

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 

2945 

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" 

2961 

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;") 

2975 

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};") 

2981 

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("") 

3009 

3010 # Make sure we have protocols in our state 

3011 if "protocols" not in self.state: 

3012 self.state["protocols"] = {} 

3013 

3014 # Save protocol state 

3015 self.state["protocols"][f"ipv{ipv}"] = protocol_state 

3016 

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") 

3023 

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() 

3028 

3029 def _setup_peer_filters(self) -> None: 

3030 """Peer filter setup.""" 

3031 self._peer_export_filter() 

3032 self._peer_import_filter() 

3033 

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 ) 

3039 

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 ) 

3048 

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 ) 

3054 

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 ) 

3063 

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) 

3070 

3071 @property 

3072 def bgp_attributes(self) -> BGPAttributes: 

3073 """Return the BGP protocol attributes.""" 

3074 return self._bgp_attributes 

3075 

3076 @property 

3077 def bgp_functions(self) -> BGPFunctions: 

3078 """Return the BGP functions.""" 

3079 return self._bgp_functions 

3080 

3081 @property 

3082 def peer_attributes(self) -> BGPPeerAttributes: 

3083 """Return our attributes.""" 

3084 return self._peer_attributes 

3085 

3086 @property 

3087 def state(self) -> Dict[str, Any]: 

3088 """Return our state info.""" 

3089 return self._state 

3090 

3091 @state.setter 

3092 def state(self, state: Dict[str, Any]) -> None: 

3093 """Set our state.""" 

3094 self.state = state 

3095 

3096 @property 

3097 def prev_state(self) -> Optional[Dict[str, Any]]: 

3098 """Previous state info.""" 

3099 return self._prev_state 

3100 

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 

3105 

3106 @property 

3107 def name(self) -> str: 

3108 """Return our name.""" 

3109 return self.peer_attributes.name 

3110 

3111 @name.setter 

3112 def name(self, name: str) -> None: 

3113 """Set our name.""" 

3114 self.peer_attributes.name = name 

3115 

3116 @property 

3117 def description(self) -> str: 

3118 """Return our description.""" 

3119 return self.peer_attributes.description 

3120 

3121 @description.setter 

3122 def description(self, description: str) -> None: 

3123 """Set our description.""" 

3124 self.peer_attributes.description = description 

3125 

3126 @property 

3127 def location(self) -> BGPPeerLocation: 

3128 """Return our location.""" 

3129 return self.peer_attributes.location 

3130 

3131 @property 

3132 def peer_type(self) -> str: 

3133 """Return our type.""" 

3134 return self.peer_attributes.peer_type 

3135 

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 

3140 

3141 @property 

3142 def asn(self) -> int: 

3143 """Return our ASN.""" 

3144 return self.peer_attributes.asn 

3145 

3146 @asn.setter 

3147 def asn(self, asn: int) -> None: 

3148 """Set our asn.""" 

3149 self.peer_attributes.asn = asn 

3150 

3151 @property 

3152 def neighbor4(self) -> Optional[str]: 

3153 """Return our IPv4 neighbor address.""" 

3154 return self.peer_attributes.neighbor4 

3155 

3156 @neighbor4.setter 

3157 def neighbor4(self, neighbor4: str) -> None: 

3158 """Set our IPv4 neighbor address.""" 

3159 self.peer_attributes.neighbor4 = neighbor4 

3160 

3161 @property 

3162 def neighbor6(self) -> Optional[str]: 

3163 """Return our IPv4 neighbor address.""" 

3164 return self.peer_attributes.neighbor6 

3165 

3166 @neighbor6.setter 

3167 def neighbor6(self, neighbor6: str) -> None: 

3168 """Set our IPv4 neighbor address.""" 

3169 self.peer_attributes.neighbor6 = neighbor6 

3170 

3171 @property 

3172 def source_address4(self) -> Optional[str]: 

3173 """Return our IPv4 source_address4 address.""" 

3174 return self.peer_attributes.source_address4 

3175 

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 

3180 

3181 @property 

3182 def source_address6(self) -> Optional[str]: 

3183 """Return our IPv4 source_address6 address.""" 

3184 return self.peer_attributes.source_address6 

3185 

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 

3190 

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 

3195 

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 

3200 

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 

3205 

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 

3210 

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 

3215 

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 

3220 

3221 @property 

3222 def multihop(self) -> Optional[str]: 

3223 """Return the value of our multihop option.""" 

3224 return self.peer_attributes.multihop 

3225 

3226 @multihop.setter 

3227 def multihop(self, multihop: str) -> None: 

3228 """Set the value of our multihop option.""" 

3229 self.peer_attributes.multihop = multihop 

3230 

3231 @property 

3232 def password(self) -> Optional[str]: 

3233 """Return the value of our password option.""" 

3234 return self.peer_attributes.password 

3235 

3236 @password.setter 

3237 def password(self, password: str) -> None: 

3238 """Set the value of our password option.""" 

3239 self.peer_attributes.password = password 

3240 

3241 @property 

3242 def ttl_security(self) -> bool: 

3243 """Return the value of our ttl_security option.""" 

3244 return self.peer_attributes.ttl_security 

3245 

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 

3250 

3251 @property 

3252 def cost(self) -> int: 

3253 """Return our prefix cost.""" 

3254 return self.peer_attributes.cost 

3255 

3256 @cost.setter 

3257 def cost(self, cost: int) -> None: 

3258 """Set our prefix cost.""" 

3259 self.peer_attributes.cost = cost 

3260 

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 

3265 

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 

3270 

3271 @property 

3272 def graceful_shutdown(self) -> bool: 

3273 """Peer graceful_shutdown property.""" 

3274 return self.peer_attributes.graceful_shutdown 

3275 

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 

3280 

3281 @property 

3282 def communities(self) -> BGPPeerCommunities: 

3283 """Return our communities.""" 

3284 return self.peer_attributes.communities 

3285 

3286 @property 

3287 def large_communities(self) -> BGPPeerLargeCommunities: 

3288 """Return our large communities.""" 

3289 return self.peer_attributes.large_communities 

3290 

3291 @property 

3292 def prepend(self) -> BGPPeerPrepend: 

3293 """Return our prepending.""" 

3294 return self.peer_attributes.prepend 

3295 

3296 @property 

3297 def passive(self) -> bool: 

3298 """Return if we only accept connections, not make them.""" 

3299 return self.peer_attributes.passive 

3300 

3301 @passive.setter 

3302 def passive(self, passive: bool) -> None: 

3303 """Set our passive mode.""" 

3304 self.peer_attributes.passive = passive 

3305 

3306 @property 

3307 def route_policy_redistribute(self) -> BGPPeerRoutePolicyRedistribute: 

3308 """Return our route redistribute policy.""" 

3309 return self.peer_attributes.route_policy_redistribute 

3310 

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 

3315 

3316 @property 

3317 def import_filter_policy(self) -> BGPPeerImportFilterPolicy: 

3318 """Return the our import filter policy.""" 

3319 return self.peer_attributes.import_filter_policy 

3320 

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 

3325 

3326 @property 

3327 def export_filter_policy(self) -> BGPPeerExportFilterPolicy: 

3328 """Return the our export filter policy.""" 

3329 return self.peer_attributes.export_filter_policy 

3330 

3331 @property 

3332 def prefix_limit_action(self) -> BGPPeerImportPrefixLimitAction: 

3333 """Return our prefix limit action.""" 

3334 return self.peer_attributes.prefix_limit_action 

3335 

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 

3340 

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 

3345 

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 

3350 

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 

3355 

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 

3360 

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 

3365 

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 

3370 

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 

3375 

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 

3380 

3381 @property 

3382 def quarantine(self) -> bool: 

3383 """Peer quarantine property.""" 

3384 return self.peer_attributes.quarantine 

3385 

3386 @quarantine.setter 

3387 def quarantine(self, quarantine: bool) -> None: 

3388 """Set the quarantine state of the peer.""" 

3389 self.peer_attributes.quarantine = quarantine 

3390 

3391 @property 

3392 def replace_aspath(self) -> bool: 

3393 """Return the ASN which replaces the AS-PATH.""" 

3394 return self.peer_attributes.replace_aspath 

3395 

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 

3400 

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 

3405 

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 

3410 

3411 @property 

3412 def constraints(self) -> BGPPeerConstraints: 

3413 """Return our own BGP constraint overrides.""" 

3414 return self.peer_attributes.constraints 

3415 

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" 

3423 

3424 return self.bgp_attributes.peertype_constraints[peer_type] 

3425 

3426 # IPV4 BLACKHOLE IMPORT PREFIX LENGTHS 

3427 

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 

3432 

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 

3437 

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 

3442 

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 

3447 

3448 # IPV4 BLACKHOLE EXPORT PREFIX LENGTHS 

3449 

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 

3454 

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 

3459 

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 

3464 

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 

3469 

3470 # IPV6 BLACKHOLE IMPORT PREFIX LENGTHS 

3471 

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 

3476 

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 

3481 

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 

3486 

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 

3491 

3492 # IPV6 BLACKHOLE EXPORT PREFIX LENGTHS 

3493 

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 

3498 

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 

3503 

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 

3508 

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 

3513 

3514 # IPV4 IMPORT PREFIX LENGTHS 

3515 

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 

3520 

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 

3525 

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 

3530 

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 

3535 

3536 # IPV4 EXPORT PREFIX LENGHTS 

3537 

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 

3542 

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 

3547 

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 

3552 

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 

3557 

3558 # IPV6 IMPORT LENGTHS 

3559 

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 

3564 

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 

3569 

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 

3574 

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 

3579 

3580 # IPV6 EXPORT LENGTHS 

3581 

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 

3586 

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 

3591 

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 

3596 

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 

3601 

3602 # AS PATH LENGTHS 

3603 

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 

3608 

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 

3613 

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 

3618 

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 

3623 

3624 # COMMUNITY LENGTHS 

3625 

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 

3630 

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 

3635 

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 

3640 

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 

3645 

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 

3650 

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 

3655 

3656 # 

3657 # Helper properties 

3658 # 

3659 

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" 

3664 

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" 

3669 

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" 

3674 

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" 

3679 

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" 

3684 

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" 

3689 

3690 @property 

3691 def has_import_aspath_asn_filter(self) -> BGPPeerFilterItem: 

3692 """Return if we filter on ASNs in the AS-PATH.""" 

3693 

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 ) 

3699 

3700 return self.import_filter_policy.aspath_asns 

3701 

3702 @property 

3703 def has_import_aspath_asn_deny_filter(self) -> BGPPeerFilterItem: 

3704 """Return if we deny filter on ASNs in the AS-PATH.""" 

3705 

3706 return self.import_filter_deny_policy.aspath_asns 

3707 

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 

3712 

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 

3717 

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 

3722 

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 

3727 

3728 @property 

3729 def has_ipv4(self) -> bool: 

3730 """Return if we have IPv4.""" 

3731 return self.neighbor4 is not None 

3732 

3733 @property 

3734 def has_ipv6(self) -> bool: 

3735 """Peer is configured with IPv6.""" 

3736 return self.neighbor6 is not None 

3737 

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 

3742 

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 

3747 

3748 @property 

3749 def has_export_prefix_filter(self) -> BGPPeerFilterItem: 

3750 """Peer has a export prefix filter.""" 

3751 return self.export_filter_policy.prefixes