Coverage for src/birdplan/bird_config/sections/protocols/bgp/__init__.py: 97%

430 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-25 07:38 +0000

1# 

2# SPDX-License-Identifier: GPL-3.0-or-later 

3# 

4# Copyright (c) 2019-2025, AllWorldIT 

5# 

6# This program is free software: you can redistribute it and/or modify 

7# it under the terms of the GNU General Public License as published by 

8# the Free Software Foundation, either version 3 of the License, or 

9# (at your option) any later version. 

10# 

11# This program is distributed in the hope that it will be useful, 

12# but WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14# GNU General Public License for more details. 

15# 

16# You should have received a copy of the GNU General Public License 

17# along with this program. If not, see <http://www.gnu.org/licenses/>. 

18 

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

20 

21from .....exceptions import BirdPlanError 

22from ....globals import BirdConfigGlobals 

23from ...bird_attributes import SectionBirdAttributes 

24from ...constants import SectionConstants 

25from ...functions import SectionFunctions 

26from ...tables import SectionTables 

27from ..base import SectionProtocolBase 

28from ..direct import ProtocolDirect 

29from ..pipe import ProtocolPipe, ProtocolPipeFilterType 

30from ..rpki import ProtocolRPKI, RPKISource 

31from .bgp_attributes import BGPAttributes, BGPPeertypeConstraints, BGPRoutePolicyAccept, BGPRoutePolicyImport 

32from .bgp_functions import BGPFunctions 

33from .bgp_types import BGPPeerConfig 

34from .peer import ProtocolBGPPeer 

35 

36__all__ = ["ProtocolBGP"] 

37 

38 

39BGPPeersConfig = dict[str, BGPPeerConfig] 

40BGPPeers = dict[str, ProtocolBGPPeer] 

41BGPOriginatedRoutes = dict[str, str] 

42 

43 

44class ProtocolBGP(SectionProtocolBase): # pylint: disable=too-many-public-methods,too-many-positional-arguments 

45 """BIRD BGP protocol configuration.""" 

46 

47 # BGP protocol attributes 

48 _bgp_attributes: BGPAttributes 

49 # BGP functions 

50 _bgp_functions: BGPFunctions 

51 

52 # BGP peers 

53 _peers: BGPPeers 

54 

55 # Internal config before configuration happens 

56 _originated_routes: BGPOriginatedRoutes 

57 

58 def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments 

59 self, 

60 birdconfig_globals: BirdConfigGlobals, 

61 birdattributes: SectionBirdAttributes, 

62 constants: SectionConstants, 

63 functions: SectionFunctions, 

64 tables: SectionTables, 

65 ) -> None: 

66 """Initialize the object.""" 

67 super().__init__(birdconfig_globals, birdattributes, constants, functions, tables) 

68 

69 # Set section name 

70 self._section = "BGP Protocol" 

71 

72 # BGP peers 

73 self._peers = {} 

74 

75 # Routes originated from BGP 

76 self._originated_routes = {} 

77 

78 # Setup BGP attributes 

79 self._bgp_attributes = BGPAttributes() 

80 # Setup BGP functions 

81 self._bgp_functions = BGPFunctions(self.birdconfig_globals, self.functions) 

82 

83 def configure(self) -> None: 

84 """Configure the BGP protocol.""" 

85 super().configure() 

86 

87 # Blank the BGP peer state 

88 if "bgp" not in self.birdconfig_globals.state: 

89 self.birdconfig_globals.state["bgp"] = {} 

90 self.birdconfig_globals.state["bgp"]["peers"] = {} 

91 

92 self._configure_constants_bgp() 

93 

94 # Check if we're adding RPKI ROA tables 

95 if self.rpki_source: 

96 # Configure RPKI protocol 

97 rpki_protocol = ProtocolRPKI(self.birdconfig_globals, self.birdattributes, self.tables, self.rpki_source) 

98 self.conf.add(rpki_protocol) 

99 

100 self._configure_birdattributes_bgp() 

101 

102 self.functions.conf.append(self.bgp_functions, deferred=True) 

103 

104 self.tables.conf.append("# BGP Tables") 

105 self.tables.conf.append("ipv4 table t_bgp4;") 

106 self.tables.conf.append("ipv6 table t_bgp6;") 

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

108 

109 # Setup BGP origination 

110 self._configure_originated_routes() 

111 

112 # BGP to master export filters 

113 self._setup_bgp_to_master_export_filter() 

114 

115 # BGP to master import filters 

116 self._setup_bgp_to_master_import_filter() 

117 

118 # Configure pipe from BGP to the master routing table 

119 bgp_master_pipe = ProtocolPipe( 

120 birdconfig_globals=self.birdconfig_globals, 

121 table_from="bgp", 

122 table_to="master", 

123 export_filter_type=ProtocolPipeFilterType.UNVERSIONED, 

124 import_filter_type=ProtocolPipeFilterType.UNVERSIONED, 

125 ) 

126 self.conf.add(bgp_master_pipe) 

127 

128 # Check if we're importing connected routes, if we are, create the protocol and pipe 

129 if self.route_policy_import.connected: 

130 # Create an interface list to feed to our routing table 

131 interfaces: list[str] = [] 

132 if isinstance(self.route_policy_import.connected, list): 

133 interfaces = self.route_policy_import.connected 

134 # Add direct protocol for redistribution of connected routes 

135 bgp_direct_protocol = ProtocolDirect( 

136 self.birdconfig_globals, 

137 self.birdattributes, 

138 self.constants, 

139 self.functions, 

140 self.tables, 

141 name="bgp", 

142 interfaces=interfaces, 

143 ) 

144 self.conf.add(bgp_direct_protocol) 

145 # Add pipe 

146 self._setup_bgp_to_direct_import_filter() 

147 bgp_direct_pipe = ProtocolPipe( 

148 self.birdconfig_globals, 

149 name="bgp", 

150 table_from="bgp", 

151 table_to="direct", 

152 table_export="none", 

153 import_filter_type=ProtocolPipeFilterType.UNVERSIONED, 

154 ) 

155 self.conf.add(bgp_direct_pipe) 

156 

157 # Loop with BGP peers and configure them 

158 self.conf.add("") 

159 for peer in self.peers.values(): 

160 self.conf.add(peer) 

161 

162 def add_originated_route(self, route: str) -> None: 

163 """Add originated route.""" 

164 (prefix, route_info) = route.split(" ", 1) 

165 self.originated_routes[prefix] = route_info 

166 

167 def add_peer(self, peer_name: str, peer_config: BGPPeerConfig) -> None: 

168 """Add peer to BGP.""" 

169 

170 if peer_name in self.peers: 

171 raise BirdPlanError(f"BGP peer '{peer_name}' already exists") 

172 

173 # Create BGP peer object 

174 peer = ProtocolBGPPeer( 

175 self.birdconfig_globals, 

176 self.birdattributes, 

177 self.constants, 

178 self.functions, 

179 self.tables, 

180 self.bgp_attributes, 

181 self.bgp_functions, 

182 peer_name, 

183 peer_config, 

184 ) 

185 

186 # Add peer to our configured peer list 

187 self.peers[peer_name] = peer 

188 

189 def peer(self, name: str) -> ProtocolBGPPeer: 

190 """Return a BGP peer configuration object.""" 

191 if name not in self.peers: 

192 raise BirdPlanError(f"Peer '{name}' not found") 

193 return self.peers[name] 

194 

195 def constraints(self, peer_type: str) -> BGPPeertypeConstraints: 

196 """Return the prefix limits for a specific peer type.""" 

197 if peer_type not in self.bgp_attributes.peertype_constraints: 

198 raise BirdPlanError(f"Peer type '{peer_type}' has no implemented global prefix limits") 

199 return self.bgp_attributes.peertype_constraints[peer_type] 

200 

201 def _configure_birdattributes_bgp(self) -> None: 

202 """Configure BGP attributes.""" 

203 # NK: No attributes for now 

204 # self.birdattributes.conf.append_title("BGP Attributes") # noqa: ERA001 

205 

206 def _configure_constants_bgp(self) -> None: # noqa: PLR0915 

207 """Configure BGP constants.""" 

208 self.constants.conf.append_title("BGP Constants") 

209 

210 self.constants.conf.append("# Our BGP ASN") 

211 self.constants.conf.append(f"define BGP_ASN = {self.asn};") 

212 self.constants.conf.append("") 

213 

214 self.constants.conf.append("# Ref http://bgpfilterguide.nlnog.net/guides/bogon_asns/") 

215 self.constants.conf.append("define BOGON_ASNS = [") 

216 self.constants.conf.append(" 0, # RFC 7607") 

217 self.constants.conf.append(" 23456, # RFC 4893 AS_TRANS") 

218 self.constants.conf.append(" 64496..64511, # RFC 5398 and documentation/example ASNs") 

219 if self.birdconfig_globals.test_mode: 

220 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 64512..65534, # RFC 6996 Private ASNs") 

221 else: 

222 self.constants.conf.append(" 64512..65534, # RFC 6996 Private ASNs") 

223 self.constants.conf.append(" 65535, # RFC 7300 Last 16 bit ASN") 

224 self.constants.conf.append(" 65536..65551, # RFC 5398 and documentation/example ASNs") 

225 self.constants.conf.append(" 65552..131071, # RFC IANA reserved ASNs") 

226 if self.birdconfig_globals.test_mode: 

227 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 4200000000..4294967294, # RFC 6996 Private ASNs") 

228 self.constants.conf.append(" 4200000000..4294900000, # RFC 6996 Private ASNs - ADJUSTED FOR TESTING") 

229 else: 

230 self.constants.conf.append(" 4200000000..4294967294, # RFC 6996 Private ASNs") 

231 self.constants.conf.append(" 4294967295 # RFC 7300 Last 32 bit ASN") 

232 self.constants.conf.append("];") 

233 self.constants.conf.append("") 

234 

235 self.constants.conf.append("define PRIVATE_ASNS = [") 

236 if self.birdconfig_globals.test_mode: 

237 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 64512..65534, # RFC 6996 Private ASNs") 

238 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: 4200000000..4294967294, # RFC 6996 Private ASNs") 

239 self.constants.conf.append(" 4200000000..4294900000 # RFC 6996 Private ASNs - ADJUSTED FOR TESTING") 

240 else: 

241 self.constants.conf.append(" 64512..65534, # RFC 6996 Private ASNs") 

242 self.constants.conf.append(" 4200000000..4294967294 # RFC 6996 Private ASNs") 

243 self.constants.conf.append("];") 

244 self.constants.conf.append("") 

245 

246 self.constants.conf.append("# Ref http://bgpfilterguide.nlnog.net/guides/no_transit_leaks") 

247 self.constants.conf.append("define BGP_ASNS_TRANSIT = [") 

248 self.constants.conf.append(" 174, # Cogent") 

249 self.constants.conf.append(" 209, # Qwest (HE carries this on IXPs IPv6 (Jul 12 2018))") 

250 self.constants.conf.append(" 701, # UUNET") 

251 self.constants.conf.append(" 702, # UUNET") 

252 self.constants.conf.append(" 1239, # Sprint") 

253 self.constants.conf.append(" 1299, # Telia") 

254 self.constants.conf.append(" 2914, # NTT Communications") 

255 self.constants.conf.append(" 3257, # GTT Backbone") 

256 self.constants.conf.append(" 3320, # Deutsche Telekom AG (DTAG)") 

257 self.constants.conf.append(" 3356, # Level3") 

258 self.constants.conf.append(" 3491, # PCCW") 

259 self.constants.conf.append(" 3549, # Level3") 

260 self.constants.conf.append(" 3561, # Savvis / CenturyLink") 

261 self.constants.conf.append(" 4134, # Chinanet") 

262 self.constants.conf.append(" 5511, # Chinanet") 

263 self.constants.conf.append(" 5511, # Orange opentransit") 

264 self.constants.conf.append(" 6453, # Tata Communications") 

265 self.constants.conf.append(" 6461, # Zayo Bandwidth") 

266 self.constants.conf.append(" 6762, # Seabone / Telecom Italia") 

267 self.constants.conf.append(" 6830, # Liberty Global") 

268 self.constants.conf.append(" 7018 # AT&T") 

269 self.constants.conf.append("];") 

270 self.constants.conf.append("") 

271 

272 # NK: IMPORTANT IF THE ABOVE CHANGES UPDATE THE BELOW 

273 self.constants.conf.append("# Community stripping") 

274 self.constants.conf.append("define BGP_COMMUNITY_STRIP = [ ") 

275 self.constants.conf.append(" (23456, *),") 

276 self.constants.conf.append(" (64496..64511, *)") # Documentation 

277 self.constants.conf.append("];") 

278 

279 # This is used for stripping large communities from customers mostly 

280 self.constants.conf.append("define BGP_LC_STRIP = [ ") 

281 self.constants.conf.append(" (23456, *, *),") 

282 self.constants.conf.append(" (64496..64511, *, *),") # Documentation 

283 self.constants.conf.append(" (65552..131071, *, *),") # Reserved 

284 self.constants.conf.append(" (BGP_ASN, 1..3, *),") # Strip route learned functions 

285 # Allow client traffic engineering: 4, 5, 6, 7, 8 

286 self.constants.conf.append(" (BGP_ASN, 9..60, *),") # Strip unused 

287 self.constants.conf.append(" (BGP_ASN, 64..70, *),") # Strip unused 

288 self.constants.conf.append(" (BGP_ASN, 74..665, *),") # Strip unsed 

289 self.constants.conf.append(" (BGP_ASN, 667..4294967295, *),") # Strip unsed + rest (incl. 1000 - info, 1101 - filter) 

290 # These functions should never be used on our own ASN 

291 self.constants.conf.append(" (BGP_ASN, 4, BGP_ASN),") 

292 self.constants.conf.append(" (BGP_ASN, 6, BGP_ASN),") 

293 self.constants.conf.append(" (BGP_ASN, 61, BGP_ASN),") 

294 self.constants.conf.append(" (BGP_ASN, 62, BGP_ASN),") 

295 self.constants.conf.append(" (BGP_ASN, 63, BGP_ASN),") 

296 self.constants.conf.append(" (BGP_ASN, 666, BGP_ASN)") 

297 self.constants.conf.append("];") 

298 

299 # Strip communities mostly for peers and transit providers 

300 self.constants.conf.append("define BGP_COMMUNITY_STRIP_ALL = [") 

301 # This is first because of the , we need 

302 if self.asn and self.asn < 65535: # noqa: PLR2004 

303 self.constants.conf.append(" (BGP_ASN, *),") 

304 else: 

305 self.constants.conf.append(" # (BGP_ASN, *), # Not stripping due to 4-byte ASN") 

306 self.constants.conf.append(" (23456, *),") 

307 self.constants.conf.append(" (64496..64511, *)") # Documentation 

308 self.constants.conf.append("];") 

309 # This is used for stripping large communities from peers and transit providers 

310 self.constants.conf.append("define BGP_LC_STRIP_ALL = [") 

311 self.constants.conf.append(" (23456, *, *),") 

312 self.constants.conf.append(" (64496..64511, *, *),") # Documentation 

313 self.constants.conf.append(" (65552..131071, *, *),") # Reserved 

314 self.constants.conf.append(" (BGP_ASN, *, *)") 

315 self.constants.conf.append("];") 

316 

317 # Stripping private communities 

318 self.constants.conf.append("define BGP_COMMUNITY_STRIP_PRIVATE = [") 

319 if self.birdconfig_globals.test_mode: 

320 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (64512..65534, *)") # Private 

321 else: 

322 self.constants.conf.append(" (64512..65534, *)") # Private 

323 self.constants.conf.append("];") 

324 

325 self.constants.conf.append("define BGP_LC_STRIP_PRIVATE = [") 

326 # Don't strip the lower private ASN range during testing 

327 if self.birdconfig_globals.test_mode: 

328 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (64512..65534, *, *)") # Private 

329 self.constants.conf.append(" # EXCLUDING DUE TO TESTING: (4200000000..4294967294, *, *)") 

330 self.constants.conf.append(" (4200000000..4294900000, *, *) # ADJUSTED FOR TESTING") 

331 else: 

332 self.constants.conf.append(" (64512..65534, *, *),") # Private 

333 self.constants.conf.append(" (4200000000..4294967294, *, *)") 

334 self.constants.conf.append("];") 

335 

336 self.constants.conf.append("# BGP Route Preferences") 

337 self.constants.conf.append("define BGP_PREF_OWN = 950;") # -20 = Originate, -10 = static, -5 = kernel 

338 self.constants.conf.append("define BGP_PREF_CUSTOMER = 750;") 

339 self.constants.conf.append("define BGP_PREF_PEER = 470;") 

340 self.constants.conf.append("define BGP_PREF_ROUTESERVER = 450;") 

341 self.constants.conf.append("define BGP_PREF_TRANSIT = 150;") 

342 self.constants.conf.append("") 

343 

344 self.constants.conf.append("# Well known communities") 

345 self.constants.conf.append("define BGP_COMMUNITY_GRACEFUL_SHUTDOWN = (65535, 0);") 

346 self.constants.conf.append("define BGP_COMMUNITY_BLACKHOLE = (65535, 666);") 

347 self.constants.conf.append("define BGP_COMMUNITY_NOEXPORT = (65535, 65281);") 

348 self.constants.conf.append("define BGP_COMMUNITY_NOADVERTISE = (65535, 65282);") 

349 self.constants.conf.append("") 

350 

351 self.constants.conf.append("# Well known extended communities") 

352 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_VALID = (unknown 0x4300, 0, 0);") 

353 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_NOTFOUND = (unknown 0x4300, 0, 1);") 

354 self.constants.conf.append("define BGP_EXT_COMMUNITY_RPKI_INVALID = (unknown 0x4300, 0, 2);") 

355 self.constants.conf.append("") 

356 

357 self.constants.conf.append("# Large community functions") 

358 # NK: IMPORTANT IF YOU CHANGE THE BELOW, UPDATE BGP_LC_STRIP 

359 self.constants.conf.append("define BGP_LC_FUNCTION_LOCATION_ISO3166 = 1;") 

360 self.constants.conf.append("define BGP_LC_FUNCTION_LOCATION_UNM49 = 2;") 

361 self.constants.conf.append("define BGP_LC_FUNCTION_RELATION = 3;") 

362 self.constants.conf.append("define BGP_LC_FUNCTION_NOEXPORT = 4;") 

363 self.constants.conf.append("define BGP_LC_FUNCTION_NOEXPORT_LOCATION = 5;") 

364 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_ONE = 6;") 

365 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_ONE_2 = 61;") 

366 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_TWO = 62;") 

367 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_THREE = 63;") 

368 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_ONE = 7;") 

369 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_ONE_2 = 71;") 

370 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_TWO = 72;") 

371 self.constants.conf.append("define BGP_LC_FUNCTION_PREPEND_LOCATION_THREE = 73;") 

372 self.constants.conf.append("define BGP_LC_FUNCTION_LOCALPREF = 8;") 

373 self.constants.conf.append("define BGP_LC_FUNCTION_INFORMATION = 1000;") 

374 self.constants.conf.append("define BGP_LC_FUNCTION_FILTERED = 1101;") 

375 self.constants.conf.append("define BGP_LC_FUNCTION_ACTION = 1200;") 

376 self.constants.conf.append("") 

377 

378 self.constants.conf.append("# Large community noexport") 

379 self.constants.conf.append("define BGP_LC_EXPORT_NOTRANSIT = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65412);") 

380 self.constants.conf.append("define BGP_LC_EXPORT_NOPEER = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65413);") 

381 self.constants.conf.append("define BGP_LC_EXPORT_NOCUSTOMER = (BGP_ASN, BGP_LC_FUNCTION_NOEXPORT, 65414);") 

382 self.constants.conf.append("") 

383 

384 self.constants.conf.append("# Large community relations") 

385 self.constants.conf.append("define BGP_LC_RELATION = [(BGP_ASN, BGP_LC_FUNCTION_RELATION, 1..5)];") 

386 self.constants.conf.append("define BGP_LC_RELATION_OWN = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 1);") 

387 self.constants.conf.append("define BGP_LC_RELATION_CUSTOMER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 2);") 

388 self.constants.conf.append("define BGP_LC_RELATION_PEER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 3);") 

389 self.constants.conf.append("define BGP_LC_RELATION_TRANSIT = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 4);") 

390 self.constants.conf.append("define BGP_LC_RELATION_ROUTESERVER = (BGP_ASN, BGP_LC_FUNCTION_RELATION, 5);") 

391 self.constants.conf.append("") 

392 

393 self.constants.conf.append("# Large communities for LOCAL_PREF attribute manipulation") 

394 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_ONE = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 1);") 

395 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_TWO = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 2);") 

396 self.constants.conf.append("define BGP_LC_LOCALPREF_MINUS_THREE = (BGP_ASN, BGP_LC_FUNCTION_LOCALPREF, 3);") 

397 self.constants.conf.append("") 

398 

399 self.constants.conf.append("# Large community information") 

400 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_COMMUNITY = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 1);") 

401 self.constants.conf.append( 

402 "define BGP_LC_INFORMATION_STRIPPED_COMMUNITY_PRIVATE = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 2);" 

403 ) 

404 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_LC = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 3);") 

405 self.constants.conf.append("define BGP_LC_INFORMATION_STRIPPED_LC_PRIVATE = (BGP_ASN, BGP_LC_FUNCTION_INFORMATION, 4);") 

406 self.constants.conf.append("") 

407 

408 self.constants.conf.append("# Large community filtered") 

409 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_LEN_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 1);") 

410 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_LEN_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 2);") 

411 self.constants.conf.append("define BGP_LC_FILTERED_BOGON = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 3);") 

412 self.constants.conf.append("define BGP_LC_FILTERED_BOGON_ASN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 4);") 

413 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 5);") 

414 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 6);") 

415 self.constants.conf.append("define BGP_LC_FILTERED_FIRST_AS_NOT_PEER_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 7);") 

416 self.constants.conf.append("define BGP_LC_FILTERED_NEXT_HOP_NOT_PEER_IP = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 8);") 

417 self.constants.conf.append("define BGP_LC_FILTERED_PREFIX_FILTERED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 9);") 

418 self.constants.conf.append("define BGP_LC_FILTERED_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 10);") 

419 # self.constants.conf.append('define BGP_LC_FILTERED_PREFIX_NOT_IN_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 11);') # noqa: E501,ERA001 

420 self.constants.conf.append("define BGP_LC_FILTERED_DEFAULT_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 12);") 

421 self.constants.conf.append("define BGP_LC_FILTERED_RPKI_UNKNOWN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 13);") 

422 self.constants.conf.append("define BGP_LC_FILTERED_RPKI_INVALID = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 14);") 

423 self.constants.conf.append("define BGP_LC_FILTERED_TRANSIT_FREE_ASN = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 15);") 

424 self.constants.conf.append("define BGP_LC_FILTERED_TOO_MANY_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 16);") 

425 self.constants.conf.append("define BGP_LC_FILTERED_ROUTECOLLECTOR = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 17);") 

426 self.constants.conf.append("define BGP_LC_FILTERED_QUARANTINED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 18);") 

427 self.constants.conf.append( 

428 "define BGP_LC_FILTERED_TOO_MANY_EXTENDED_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 19);" 

429 ) 

430 self.constants.conf.append("define BGP_LC_FILTERED_TOO_MANY_LARGE_COMMUNITIES = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 20);") 

431 self.constants.conf.append("define BGP_LC_FILTERED_PEER_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 21);") 

432 self.constants.conf.append("define BGP_LC_FILTERED_ASPATH_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 22);") 

433 self.constants.conf.append("define BGP_LC_FILTERED_NO_RELATION_LC = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 23);") 

434 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_LEN_TOO_LONG = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 24);") 

435 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_LEN_TOO_SHORT = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 25);") 

436 self.constants.conf.append("define BGP_LC_FILTERED_BLACKHOLE_NOT_ALLOWED = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 26);") 

437 self.constants.conf.append("define BGP_LC_FILTERED_DENY_ORIGIN_AS = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 27);") 

438 self.constants.conf.append("define BGP_LC_FILTERED_DENY_ASPATH = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 28);") 

439 self.constants.conf.append("define BGP_LC_FILTERED_DENY_PREFIX = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 29);") 

440 self.constants.conf.append("define BGP_LC_FILTERED_ACTION = (BGP_ASN, BGP_LC_FUNCTION_FILTERED, 30);") 

441 self.constants.conf.append("") 

442 self.constants.conf.append("# Large community actions") 

443 self.constants.conf.append("define BGP_LC_ACTION_REPLACE_ASPATH = (BGP_ASN, BGP_LC_FUNCTION_ACTION, 1);") 

444 self.constants.conf.append("define BGP_LC_ACTION_BLACKHOLE_ORIGINATE = (BGP_ASN, BGP_LC_FUNCTION_ACTION, 2);") 

445 self.constants.conf.append("") 

446 

447 def _configure_originated_routes(self) -> None: 

448 # Work out static v4 and v6 routes 

449 routes: dict[str, list[str]] = {"4": [], "6": []} 

450 for prefix in sorted(self.originated_routes.keys()): 

451 info = self.originated_routes[prefix] 

452 if "." in prefix: 

453 routes["4"].append(f"{prefix} {info}") 

454 elif ":" in prefix: 

455 routes["6"].append(f"{prefix} {info}") 

456 else: 

457 raise BirdPlanError(f"The BGP originate route '{prefix}' is odd") 

458 

459 self.tables.conf.append("# BGP Origination Tables") 

460 

461 filter_name = "f_bgp_originate_import" 

462 self.conf.add(f"filter {filter_name}") 

463 self.conf.add("string filter_name;") 

464 self.conf.add("{") 

465 self.conf.add(f' filter_name = "{filter_name}";') 

466 self.conf.add(" # Origination import") 

467 self.conf.add(f" {self.bgp_functions.import_own(20)};") 

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

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

470 self.conf.add("") 

471 

472 # Loop with IPv4 and IPv6 

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

474 self.tables.conf.append(f"ipv{ipv} table t_bgp_originate{ipv};") 

475 

476 self.conf.add(f"protocol static bgp_originate{ipv} {{") 

477 self.conf.add(f' description "BGP route origination for IPv{ipv}";') 

478 self.conf.add("") 

479 self.conf.add(f" vrf {self.birdconfig_globals.vrf};") 

480 self.conf.add("") 

481 self.conf.add(f" ipv{ipv} {{") 

482 self.conf.add(f" table t_bgp_originate{ipv};") 

483 self.conf.add(" preference 195;") 

484 self.conf.add(" export none;") 

485 self.conf.add(f" import filter {filter_name};") 

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

487 # If we have IPv4 routes 

488 if routes[ipv]: 

489 self.conf.add("") 

490 # Output the routes 

491 for route in routes[ipv]: 

492 self.conf.add(f" route {route};") 

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

494 self.conf.add("") 

495 

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

497 

498 # Configure BGP origination route pipe to the bgp table 

499 originate_pipe = ProtocolPipe( 

500 birdconfig_globals=self.birdconfig_globals, 

501 table_from="bgp_originate", 

502 table_to="bgp", 

503 table_export="all", 

504 table_import="none", 

505 ) 

506 self.conf.add(originate_pipe) 

507 

508 def _setup_bgp_to_master_export_filter(self) -> None: 

509 """BGP main table to master export filters setup.""" 

510 

511 # Configure export filter to master 

512 filter_name = "f_bgp_master_export" 

513 self.conf.add("# Export filter FROM BGP table TO master table") 

514 self.conf.add(f"filter {filter_name}") 

515 self.conf.add("string filter_name;") 

516 self.conf.add("{") 

517 self.conf.add(f' filter_name = "{filter_name}";') 

518 # Accept BGP routes into the master routing table 

519 self.conf.add(f" {self.bgp_functions.accept_bgp()};") 

520 # Check if we accept customer blackhole routes, if not block it 

521 if self.route_policy_accept.bgp_customer_blackhole: 

522 self.conf.add(f" {self.bgp_functions.accept_customer_blackhole()};") 

523 # Check if we accept our own blackhole routes, if not block it 

524 if self.route_policy_accept.bgp_own_blackhole: 

525 self.conf.add(f" {self.bgp_functions.accept_own_blackhole()};") 

526 # Check if we accept default routes originated from within our federation, if not block it 

527 if self.route_policy_accept.bgp_own_default: 

528 self.conf.add(f" {self.bgp_functions.accept_bgp_own_default()};") 

529 # Check if we accept default routes originated from transit peers, if not block it 

530 if self.route_policy_accept.bgp_transit_default: 

531 self.conf.add(f" {self.bgp_functions.accept_bgp_transit_default()};") 

532 # Check if we accept originated routes, if not block it 

533 if self.route_policy_accept.originated: 

534 self.conf.add(f" {self.bgp_functions.accept_originated()};") 

535 # Check if we accept originated routes, if not block it 

536 if self.route_policy_accept.originated_default: 

537 self.conf.add(f" {self.bgp_functions.accept_originated_default()};") 

538 # Default to reject 

539 self.conf.add(" if DEBUG then") 

540 self.conf.add(f' print "[{filter_name}] Rejecting ", net, " from t_bgp to master (fallthrough)";') 

541 self.conf.add(" reject;") 

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

543 self.conf.add("") 

544 

545 def _setup_bgp_to_master_import_filter(self) -> None: 

546 """BGP main table to master import filters setup.""" 

547 # Configure import filter to master 

548 filter_name = "f_bgp_master_import" 

549 self.conf.add("# Import filter FROM master table TO BGP table") 

550 self.conf.add(f"filter {filter_name}") 

551 self.conf.add("string filter_name;") 

552 self.conf.add("{") 

553 self.conf.add(f' filter_name = "{filter_name}";') 

554 # BGP importation of kernel routes 

555 if self.route_policy_import.kernel: 

556 self.conf.add(f" {self.bgp_functions.import_kernel()};") 

557 # BGP importation of kernel blackhole routes 

558 if self.route_policy_import.kernel_blackhole: 

559 self.conf.add(f" {self.bgp_functions.import_kernel_blackhole()};") 

560 # BGP importation of kernel default routes 

561 if self.route_policy_import.kernel_default: 

562 self.conf.add(f" {self.bgp_functions.import_kernel_default()};") 

563 # BGP importation of static routes 

564 if self.route_policy_import.static: 

565 self.conf.add(f" {self.bgp_functions.import_static()};") 

566 # BGP importation of static blackhole routes 

567 if self.route_policy_import.static_blackhole: 

568 self.conf.add(f" {self.bgp_functions.import_static_blackhole()};") 

569 # BGP importation of static default routes 

570 if self.route_policy_import.static_default: 

571 self.conf.add(f" {self.bgp_functions.import_static_default()};") 

572 # Else reject 

573 self.conf.add(" if DEBUG then") 

574 self.conf.add(f' print "[{filter_name}] Rejecting ", net, " from master to t_bgp (fallthrough)";') 

575 self.conf.add(" reject;") 

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

577 self.conf.add("") 

578 

579 def _setup_bgp_to_direct_import_filter(self) -> None: 

580 """BGP main table to direct import filters setup.""" 

581 

582 filter_name = "f_bgp_direct_import" 

583 self.conf.add("# Import filter FROM master table TO BGP table") 

584 self.conf.add(f"filter {filter_name}") 

585 self.conf.add("string filter_name;") 

586 self.conf.add("{") 

587 self.conf.add(f' filter_name = "{filter_name}";') 

588 self.conf.add(" # Import connected routes") 

589 self.conf.add(f" {self.bgp_functions.import_own(10)};") 

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

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

592 self.conf.add("") 

593 

594 # PROPERTIES 

595 

596 @property 

597 def bgp_attributes(self) -> BGPAttributes: 

598 """Return our BGP protocol attributes.""" 

599 return self._bgp_attributes 

600 

601 @property 

602 def bgp_functions(self) -> BGPFunctions: 

603 """Return our BGP protocol functions.""" 

604 return self._bgp_functions 

605 

606 @property 

607 def asn(self) -> int | None: 

608 """Return our ASN.""" 

609 return self.bgp_attributes.asn 

610 

611 @asn.setter 

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

613 """Set our ASN.""" 

614 self.bgp_attributes.asn = asn 

615 # Enable bogon constants 

616 self.constants.need_bogons = True 

617 

618 @property 

619 def peertype_constraints(self) -> dict[str, BGPPeertypeConstraints]: 

620 """Return our peertype constraints.""" 

621 return self.bgp_attributes.peertype_constraints 

622 

623 @property 

624 def rpki_source(self) -> RPKISource | None: 

625 """Return the RPKI source to use for validation.""" 

626 return self.bgp_attributes.rpki_source 

627 

628 @rpki_source.setter 

629 def rpki_source(self, rpki_source: RPKISource) -> None: 

630 """Set the RPKI source to use for validation.""" 

631 self.bgp_attributes.rpki_source = rpki_source 

632 

633 @property 

634 def graceful_shutdown(self) -> bool: 

635 """Return our the value of graceful_shutdown.""" 

636 return self.bgp_attributes.graceful_shutdown 

637 

638 @graceful_shutdown.setter 

639 def graceful_shutdown(self, graceful_shutdown: bool) -> None: 

640 """Set the value of graceful_shutdown.""" 

641 self.bgp_attributes.graceful_shutdown = graceful_shutdown 

642 

643 @property 

644 def quarantine(self) -> bool: 

645 """Global BGP peer quarantine state.""" 

646 return self.bgp_attributes.quarantine 

647 

648 @quarantine.setter 

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

650 """Global BGP peer quarantine state.""" 

651 self.bgp_attributes.quarantine = quarantine 

652 

653 @property 

654 def rr_cluster_id(self) -> str | None: 

655 """Return route reflector cluster ID.""" 

656 return self.bgp_attributes.rr_cluster_id 

657 

658 @rr_cluster_id.setter 

659 def rr_cluster_id(self, rr_cluster_id: str) -> None: 

660 """Set our route reflector cluster ID.""" 

661 self.bgp_attributes.rr_cluster_id = rr_cluster_id 

662 

663 @property 

664 def route_policy_accept(self) -> BGPRoutePolicyAccept: 

665 """Return our route policy for accepting of routes from peers into the main BGP table.""" 

666 return self.bgp_attributes.route_policy_accept 

667 

668 @property 

669 def route_policy_import(self) -> BGPRoutePolicyImport: 

670 """Return our route policy for importing of routes from internal tables.""" 

671 return self.bgp_attributes.route_policy_import 

672 

673 @property 

674 def peers(self) -> BGPPeers: 

675 """BGP peers.""" 

676 return self._peers 

677 

678 @property 

679 def originated_routes(self) -> BGPOriginatedRoutes: 

680 """Return our originated routes.""" 

681 return self._originated_routes