├── .github ├── FUNDING.yml └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── bgp ├── attributes.rs ├── capabilities.rs ├── community.rs ├── elem.rs ├── error.rs ├── mod.rs └── role.rs ├── err.rs ├── lib.rs ├── mrt ├── bgp4mp.rs ├── mod.rs └── tabledump.rs ├── network.rs └── prelude.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bgpkit 4 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - '**.md' 8 | pull_request: 9 | branches: [ main ] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Build 22 | run: cargo build --verbose 23 | - name: Run tests 24 | run: cargo test --verbose 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .idea 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bgp-models" 3 | version = "0.8.0" 4 | edition = "2018" 5 | authors = ["Mingwei Zhang "] 6 | readme = "README.md" 7 | license = "MIT" 8 | repository = "https://github.com/bgpkit/bgp-models" 9 | documentation = "https://docs.rs/bgp-models" 10 | description = """ 11 | Structs and other building blocks for BGP and MRT related Rust projects. 12 | """ 13 | keywords = ["bgp", "mrt"] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [package.metadata] 18 | msrv = "1.46.0" 19 | 20 | [dependencies] 21 | enum-primitive-derive = "0.2" 22 | num-traits = "0.2" 23 | log = "0.4" 24 | ipnetwork = {version="0.18", default-features=false} 25 | itertools = "0.10.1" 26 | serde={version="1", features=["derive"]} 27 | 28 | [dev-dependencies] 29 | serde_json = "1.0" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mingwei Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bgp-models 2 | 3 | [![Rust](https://github.com/bgpkit/bgp-models/actions/workflows/rust.yml/badge.svg)](https://github.com/bgpkit/bgp-models/actions/workflows/rust.yml) 4 | 5 | `bgp-models` is a library that defines the basic BGP and MRT message data structures. 6 | This library aims to provide building blocks for downstreams libraries working with BGP and MRT 7 | messages such as MRT parser or BGP table constructor. 8 | 9 | ## Minimum Supported Rust Version 10 | 11 | `1.46.0` 12 | 13 | ## Supported RFCs 14 | 15 | Most of the structs defined in this library are named after the formal definitions in a number of 16 | RFCs. Here is a list of them: 17 | 18 | ### BGP 19 | - [X] [RFC 2042](https://datatracker.ietf.org/doc/html/rfc2042): Registering New BGP Attribute Types 20 | - [X] [RFC 3392](https://datatracker.ietf.org/doc/html/rfc3392): Capabilities Advertisement with BGP-4 21 | - [X] [RFC 4271](https://datatracker.ietf.org/doc/html/rfc4271): A Border Gateway Protocol 4 (BGP-4) 22 | - [X] [RFC 5065](https://datatracker.ietf.org/doc/html/rfc5065): Autonomous System Confederations for BGP 23 | - [X] [RFC 6793](https://datatracker.ietf.org/doc/html/rfc6793): BGP Support for Four-Octet Autonomous System (AS) Number Space 24 | - [X] [RFC 7911](https://datatracker.ietf.org/doc/html/rfc7911): Advertisement of Multiple Paths in BGP (ADD-PATH) 25 | - [X] [RFC 9072](https://datatracker.ietf.org/doc/html/rfc9072): Extended Optional Parameters Length for BGP OPEN Message Updates 26 | - [X] [RFC 9234](https://datatracker.ietf.org/doc/html/rfc9234): Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages 27 | 28 | ### MRT 29 | 30 | - [X] [RFC 6396](https://datatracker.ietf.org/doc/html/rfc6396): Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format 31 | - [ ] [RFC 6397](https://datatracker.ietf.org/doc/html/rfc6397): Multi-Threaded Routing Toolkit (MRT) Border Gateway Protocol (BGP) Routing Information Export Format with Geo-Location Extensions 32 | - [X] [RFC 8050](https://datatracker.ietf.org/doc/html/rfc8050): Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format with BGP Additional Path Extensions 33 | 34 | ### Communities 35 | 36 | #### Communities 37 | 38 | - [X] [RFC 1977](https://datatracker.ietf.org/doc/html/rfc1977): BGP Communities Attribute 39 | - [X] [RFC 4360](https://datatracker.ietf.org/doc/html/rfc4360): BGP Extended Communities Attribute 40 | - [X] [RFC 5668](https://datatracker.ietf.org/doc/html/rfc5668): 4-Octet AS Specific BGP Extended Community 41 | - [X] [RFC 5701](https://datatracker.ietf.org/doc/html/rfc5701): IPv6 Address Specific BGP Extended Community Attribute 42 | - [X] [RFC 7153](https://datatracker.ietf.org/doc/html/rfc7153): IANA Registries for BGP Extended Communities Updates 4360, 5701 43 | - [X] [RFC 8097](https://datatracker.ietf.org/doc/html/rfc8097): BGP Prefix Origin Validation State Extended Community 44 | - [X] [RFC 8092](https://datatracker.ietf.org/doc/html/rfc8092): BGP Large Communities 45 | 46 | ## Used By 47 | 48 | - [bgpkit-parser](https://github.com/bgpkit/bgpkit-parser) 49 | - [ris-live-rs](https://github.com/bgpkit/ris-live-rs) 50 | 51 | ## Built with ❤️ by BGPKIT Team 52 | 53 | BGPKIT is a small-team start-up that focus on building the best tooling for BGP data in Rust. We have 10 years of 54 | experience working with BGP data and believe that our work can enable more companies to start keeping tracks of BGP data 55 | on their own turf. Learn more about what services we provide at https://bgpkit.com. 56 | 57 | https://bgpkit.com/favicon.ico 58 | -------------------------------------------------------------------------------- /src/bgp/attributes.rs: -------------------------------------------------------------------------------- 1 | //! BGP attribute structs 2 | use std::fmt::{Display, Formatter}; 3 | use std::net::IpAddr; 4 | use itertools::Itertools; 5 | use crate::network::*; 6 | use serde::{Serialize, Serializer}; 7 | use crate::bgp::{ExtendedCommunity, LargeCommunity, Community}; 8 | 9 | /// The high-order bit (bit 0) of the Attribute Flags octet is the 10 | /// Optional bit. It defines whether the attribute is optional (if 11 | /// set to 1) or well-known (if set to 0). 12 | /// 13 | /// The second high-order bit (bit 1) of the Attribute Flags octet 14 | /// is the Transitive bit. It defines whether an optional 15 | /// attribute is transitive (if set to 1) or non-transitive (if set 16 | /// to 0). 17 | /// 18 | /// For well-known attributes, the Transitive bit MUST be set to 1. 19 | /// (See Section 5 for a discussion of transitive attributes.) 20 | /// 21 | /// The third high-order bit (bit 2) of the Attribute Flags octet 22 | /// is the Partial bit. It defines whether the information 23 | /// contained in the optional transitive attribute is partial (if 24 | /// set to 1) or complete (if set to 0). For well-known attributes 25 | /// and for optional non-transitive attributes, the Partial bit 26 | /// MUST be set to 0. 27 | /// 28 | /// The fourth high-order bit (bit 3) of the Attribute Flags octet 29 | /// is the Extended Length bit. It defines whether the Attribute 30 | /// Length is one octet (if set to 0) or two octets (if set to 1). 31 | pub enum AttributeFlagsBit { 32 | /// 128 = 0b10000000 33 | OptionalBit = 0b10000000, 34 | /// 64 = 0b01000000 35 | TransitiveBit = 0b01000000, 36 | /// 32 = 0b00100000 37 | PartialBit = 0b00100000, 38 | /// 16 = 0b00010000 39 | ExtendedLengthBit = 0b00010000, 40 | } 41 | 42 | /// Attribute types. 43 | /// 44 | /// All attributes currently defined and not Unassigned or Deprecated are included here. 45 | /// To see the full list, check out IANA at: 46 | /// 47 | #[allow(non_camel_case_types)] 48 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 49 | pub enum AttrType { 50 | RESERVED = 0, 51 | ORIGIN = 1, 52 | AS_PATH = 2, 53 | NEXT_HOP = 3, 54 | MULTI_EXIT_DISCRIMINATOR = 4, 55 | LOCAL_PREFERENCE = 5, 56 | ATOMIC_AGGREGATE = 6, 57 | AGGREGATOR = 7, 58 | COMMUNITIES = 8, 59 | /// 60 | ORIGINATOR_ID = 9, 61 | CLUSTER_LIST = 10, 62 | /// 63 | CLUSTER_ID = 13, 64 | MP_REACHABLE_NLRI = 14, 65 | MP_UNREACHABLE_NLRI = 15, 66 | /// 67 | EXTENDED_COMMUNITIES = 16, 68 | AS4_PATH = 17, 69 | AS4_AGGREGATOR = 18, 70 | PMSI_TUNNEL = 22, 71 | TUNNEL_ENCAPSULATION = 23, 72 | TRAFFIC_ENGINEERING = 24, 73 | IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES = 25, 74 | AIGP = 26, 75 | PE_DISTINGUISHER_LABELS = 27, 76 | BGP_LS_ATTRIBUTE = 29, 77 | LARGE_COMMUNITIES = 32, 78 | BGPSEC_PATH = 33, 79 | SFP_ATTRIBUTE = 37, 80 | BFD_DISCRIMINATOR = 38, 81 | BGP_PREFIX_SID = 40, 82 | ATTR_SET = 128, 83 | /// 84 | DEVELOPMENT = 255, 85 | } 86 | 87 | #[allow(non_camel_case_types)] 88 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone)] 89 | pub enum Origin { 90 | IGP = 0, 91 | EGP = 1, 92 | INCOMPLETE = 2, 93 | } 94 | 95 | #[allow(non_camel_case_types)] 96 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone)] 97 | pub enum AtomicAggregate { 98 | NAG = 0, 99 | AG = 1, 100 | } 101 | 102 | /// BGP Attribute struct with attribute value and flag 103 | #[derive(Debug, PartialEq, Clone, Serialize, Eq)] 104 | pub struct Attribute { 105 | pub attr_type: AttrType, 106 | pub value: AttributeValue, 107 | pub flag: u8, 108 | } 109 | 110 | /// The `AttributeValue` enum represents different kinds of Attribute values. 111 | #[derive(Debug, PartialEq, Clone, Serialize, Eq)] 112 | pub enum AttributeValue { 113 | Origin(Origin), 114 | AsPath(AsPath), 115 | As4Path(AsPath), 116 | NextHop(IpAddr), 117 | MultiExitDiscriminator(u32), 118 | LocalPreference(u32), 119 | AtomicAggregate(AtomicAggregate), 120 | Aggregator(Asn, IpAddr), 121 | Communities(Vec), 122 | ExtendedCommunities(Vec), 123 | LargeCommunities(Vec), 124 | OriginatorId(IpAddr), 125 | Clusters(Vec), 126 | MpReachNlri(Nlri), 127 | MpUnreachNlri(Nlri), 128 | Development(Vec), 129 | } 130 | 131 | ///////////// 132 | // AS PATH // 133 | ///////////// 134 | 135 | /// Enum of AS path segment. 136 | #[derive(Debug, PartialEq, Clone, Eq)] 137 | pub enum AsPathSegment { 138 | AsSequence(Vec), 139 | AsSet(Vec), 140 | ConfedSequence(Vec), 141 | ConfedSet(Vec), 142 | } 143 | 144 | impl AsPathSegment { 145 | pub fn count_asns(&self) -> usize { 146 | match self { 147 | AsPathSegment::AsSequence(v) => { 148 | v.len() 149 | }, 150 | AsPathSegment::AsSet(_) => 1, 151 | AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_)=> 0, 152 | } 153 | } 154 | } 155 | 156 | #[derive(Debug, PartialEq, Clone, Eq)] 157 | pub struct AsPath { 158 | pub segments: Vec, 159 | } 160 | 161 | impl AsPath { 162 | pub fn new() -> AsPath { 163 | AsPath { segments: vec![] } 164 | } 165 | 166 | pub fn from_segments(segments: Vec) -> AsPath { 167 | AsPath { segments } 168 | } 169 | 170 | pub fn add_segment(&mut self, segment: AsPathSegment) { 171 | self.segments.push(segment); 172 | } 173 | 174 | pub fn segments(&self) -> &Vec { 175 | &self.segments 176 | } 177 | 178 | pub fn count_asns(&self) -> usize { 179 | self.segments.iter().map(AsPathSegment::count_asns).sum() 180 | } 181 | 182 | /// Construct AsPath from AS_PATH and AS4_PATH 183 | /// 184 | /// https://datatracker.ietf.org/doc/html/rfc6793#section-4.2.3 185 | /// If the number of AS numbers in the AS_PATH attribute is less than the 186 | /// number of AS numbers in the AS4_PATH attribute, then the AS4_PATH 187 | /// attribute SHALL be ignored, and the AS_PATH attribute SHALL be taken 188 | /// as the AS path information. 189 | /// 190 | /// If the number of AS numbers in the AS_PATH attribute is larger than 191 | /// or equal to the number of AS numbers in the AS4_PATH attribute, then 192 | /// the AS path information SHALL be constructed by taking as many AS 193 | /// numbers and path segments as necessary from the leading part of the 194 | /// AS_PATH attribute, and then prepending them to the AS4_PATH attribute 195 | /// so that the AS path information has a number of AS numbers identical 196 | /// to that of the AS_PATH attribute. Note that a valid 197 | /// AS_CONFED_SEQUENCE or AS_CONFED_SET path segment SHALL be prepended 198 | /// if it is either the leading path segment or is adjacent to a path 199 | /// segment that is prepended. 200 | pub fn merge_aspath_as4path(aspath: &AsPath, as4path: &AsPath) -> Option { 201 | if aspath.count_asns() < as4path.count_asns() { 202 | return Some(aspath.clone()) 203 | } 204 | 205 | let mut as4iter = as4path.segments.iter(); 206 | let mut as4seg = as4iter.next(); 207 | let mut new_segs: Vec = vec![]; 208 | if as4seg.is_none(){ 209 | new_segs.extend(aspath.segments.clone()); 210 | return Some(AsPath{ segments: new_segs }) 211 | } 212 | 213 | for seg in &aspath.segments { 214 | let as4seg_unwrapped = as4seg.unwrap(); 215 | if let (AsPathSegment::AsSequence(seq), AsPathSegment::AsSequence(seq4)) = (seg, as4seg_unwrapped) { 216 | let diff_len = seq.len() - seq4.len(); 217 | let mut new_seq: Vec = vec![]; 218 | new_seq.extend(seq.iter().take(diff_len)); 219 | new_seq.extend(seq4); 220 | new_segs.push(AsPathSegment::AsSequence(new_seq)); 221 | } else { 222 | new_segs.push(as4seg_unwrapped.clone()); 223 | } 224 | as4seg = as4iter.next(); 225 | } 226 | 227 | Some(AsPath{ segments: new_segs }) 228 | } 229 | 230 | pub fn get_origin(&self) -> Option> { 231 | if let Some(seg) = self.segments.last() { 232 | match seg { 233 | AsPathSegment::AsSequence(v) => { 234 | if let Some(n) = v.last() { 235 | Some(vec![n.clone()]) 236 | } else { 237 | None 238 | } 239 | } 240 | AsPathSegment::AsSet(v) => { Some(v.clone()) } 241 | AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_) => { None } 242 | } 243 | } else { 244 | None 245 | } 246 | } 247 | } 248 | 249 | ////////// 250 | // NLRI // 251 | ////////// 252 | 253 | #[derive(Debug, PartialEq, Clone, Serialize, Eq)] 254 | pub struct Nlri { 255 | pub afi: Afi, 256 | pub safi: Safi, 257 | pub next_hop: Option, 258 | pub prefixes: Vec, 259 | } 260 | 261 | #[derive(Debug, PartialEq, Clone, Serialize)] 262 | pub struct MpReachableNlri { 263 | afi: Afi, 264 | safi: Safi, 265 | next_hop: NextHopAddress, 266 | prefixes: Vec, 267 | } 268 | 269 | impl MpReachableNlri { 270 | pub fn new( 271 | afi: Afi, 272 | safi: Safi, 273 | next_hop: NextHopAddress, 274 | prefixes: Vec, 275 | ) -> MpReachableNlri { 276 | MpReachableNlri { 277 | afi, 278 | safi, 279 | next_hop, 280 | prefixes, 281 | } 282 | } 283 | } 284 | 285 | #[derive(Debug, PartialEq, Copy, Clone)] 286 | pub struct MpReachableNlriV2 { 287 | next_hop: NextHopAddress, 288 | } 289 | 290 | #[derive(Debug, PartialEq, Clone)] 291 | pub struct MpUnreachableNlri { 292 | afi: Afi, 293 | safi: Safi, 294 | prefixes: Vec, 295 | } 296 | 297 | impl MpUnreachableNlri { 298 | pub fn new(afi: Afi, safi: Safi, prefixes: Vec) -> MpUnreachableNlri { 299 | MpUnreachableNlri { 300 | afi, 301 | safi, 302 | prefixes, 303 | } 304 | } 305 | } 306 | 307 | /////////////////// 308 | // DISPLAY IMPLS // 309 | /////////////////// 310 | 311 | impl Display for Origin { 312 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 313 | let s = match self { 314 | Origin::IGP => {"IGP"} 315 | Origin::EGP => {"EGP"} 316 | Origin::INCOMPLETE => {"INCOMPLETE"} 317 | }; 318 | write!(f, "{}", s) 319 | } 320 | } 321 | 322 | impl Display for AtomicAggregate { 323 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 324 | write!(f, "{}", match self { 325 | AtomicAggregate::NAG => {"NAG"} 326 | AtomicAggregate::AG => {"AG"} 327 | }) 328 | } 329 | } 330 | 331 | 332 | impl Display for NextHopAddress { 333 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 334 | write!(f, "{}", 335 | match self { 336 | NextHopAddress::Ipv4(v) => {v.to_string()} 337 | NextHopAddress::Ipv6(v) => {v.to_string()} 338 | NextHopAddress::Ipv6LinkLocal(v1, _v2) => {v1.to_string()} 339 | } 340 | ) 341 | } 342 | } 343 | 344 | impl Display for AsPath { 345 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 346 | write!(f, "{}", 347 | self 348 | .segments() 349 | .iter() 350 | .map(|seg| match seg { 351 | AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => v 352 | .iter() 353 | .join(" "), 354 | AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => { 355 | format!( 356 | "{{{}}}", 357 | v.iter() 358 | .join(",") 359 | ) 360 | } 361 | }) 362 | .join(" ") 363 | ) 364 | } 365 | } 366 | 367 | /////////////// 368 | // SERIALIZE // 369 | /////////////// 370 | 371 | impl Serialize for AsPath { 372 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 373 | serializer.serialize_str(self.to_string().as_str()) 374 | } 375 | } 376 | 377 | impl Serialize for Origin { 378 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 379 | serializer.serialize_str(self.to_string().as_str()) 380 | } 381 | } 382 | 383 | impl Serialize for AtomicAggregate { 384 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 385 | serializer.serialize_str(self.to_string().as_str()) 386 | } 387 | } 388 | 389 | #[cfg(test)] 390 | mod tests { 391 | use crate::bgp::attributes::{AsPath, AsPathSegment}; 392 | 393 | #[test] 394 | fn test_aspath_as4path_merge() { 395 | let aspath = AsPath{ 396 | segments: vec![AsPathSegment::AsSequence([1,2,3,5].map(|i|{i.into()}).to_vec())] 397 | }; 398 | let as4path = AsPath{ 399 | segments: vec![AsPathSegment::AsSequence([2,3,7].map(|i|{i.into()}).to_vec())] 400 | }; 401 | let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path).unwrap(); 402 | assert_eq!(newpath.segments[0], AsPathSegment::AsSequence([1,2,3,7].map(|i|{i.into()}).to_vec())); 403 | } 404 | 405 | #[test] 406 | fn test_get_origin() { 407 | let aspath = AsPath{ 408 | segments: vec![ 409 | AsPathSegment::AsSequence([1,2,3,5].map(|i|{i.into()}).to_vec()), 410 | ] 411 | }; 412 | let origins = aspath.get_origin(); 413 | assert!(origins.is_some()); 414 | assert_eq!(origins.unwrap(), vec![5]); 415 | 416 | let aspath = AsPath{ 417 | segments: vec![ 418 | AsPathSegment::AsSequence([1,2,3,5].map(|i|{i.into()}).to_vec()), 419 | AsPathSegment::AsSet([7,8].map(|i|{i.into()}).to_vec()), 420 | ] 421 | }; 422 | let origins = aspath.get_origin(); 423 | assert!(origins.is_some()); 424 | assert_eq!(origins.unwrap(), vec![7,8]); 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /src/bgp/capabilities.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use num_traits::FromPrimitive; 3 | use std::error::Error; 4 | use std::fmt::{Display, Formatter}; 5 | 6 | /// BGP capability parsing error 7 | #[derive(Debug, PartialEq, Eq, Clone, Serialize)] 8 | pub enum BgpCapabilityParsingError { 9 | Unassigned(u8), 10 | DeprecatedCode(u8), 11 | ReservedCode(u8), 12 | ReservedExperimentalCode(u8), 13 | } 14 | 15 | impl Display for BgpCapabilityParsingError { 16 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 17 | match self { 18 | BgpCapabilityParsingError::Unassigned(v) => { 19 | write!(f, "unassigned BGP capability code: {}", v) 20 | } 21 | BgpCapabilityParsingError::DeprecatedCode(v) => { 22 | write!(f, "deprecated BGP capability code: {}", v) 23 | } 24 | BgpCapabilityParsingError::ReservedCode(v) => { 25 | write!(f, "reserved BGP capability code: {}", v) 26 | } 27 | BgpCapabilityParsingError::ReservedExperimentalCode(v) => { 28 | write!(f, "reserved BGP capability code for experimental use: {}", v) 29 | } 30 | } 31 | } 32 | } 33 | 34 | impl Error for BgpCapabilityParsingError{} 35 | 36 | #[allow(non_camel_case_types)] 37 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 38 | pub enum BgpCapabilityType { 39 | MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 = 1, 40 | ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 = 2, 41 | OUTBOUND_ROUTE_FILTERING_CAPABILITY = 3, 42 | EXTENDED_NEXT_HOP_ENCODING = 5, 43 | BGP_EXTENDED_MESSAGE = 6, 44 | BGPSEC_CAPABILITY = 7, 45 | MULTIPLE_LABELS_CAPABILITY = 8, 46 | BGP_ROLE = 9, 47 | GRACEFUL_RESTART_CAPABILITY = 64, 48 | SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY = 65, 49 | SUPPORT_FOR_DYNAMIC_CAPABILITY = 67, 50 | MULTISESSION_BGP_CAPABILITY = 68, 51 | ADD_PATH_CAPABILITY = 69, 52 | ENHANCED_ROUTE_REFRESH_CAPABILITY = 70, 53 | LONG_LIVED_GRACEFUL_RESTART_CAPABILITY = 71, 54 | ROUTING_POLICY_DISTRIBUTION = 72, 55 | FQDN_CAPABILITY = 73, 56 | } 57 | 58 | pub fn parse_capability(capability_code: &u8) -> Result { 59 | match BgpCapabilityType::from_u8(*capability_code) { 60 | Some(v) => { 61 | return Ok(v) 62 | } 63 | None => { 64 | if [4, 66, 128, 129, 130, 131, 184, 185].contains(capability_code) { 65 | Err(BgpCapabilityParsingError::DeprecatedCode(*capability_code)) 66 | } else if *capability_code == 0 || *capability_code == 255 { 67 | Err(BgpCapabilityParsingError::ReservedCode(*capability_code)) 68 | } else if *capability_code >= 239 && *capability_code <= 254 { 69 | Err(BgpCapabilityParsingError::ReservedExperimentalCode(*capability_code)) 70 | } else { 71 | Err(BgpCapabilityParsingError::Unassigned(*capability_code)) 72 | } 73 | } 74 | } 75 | } 76 | 77 | #[cfg(test)] 78 | mod tests { 79 | use super::*; 80 | 81 | #[test] 82 | fn test_parsing_capability() { 83 | let mut code; 84 | 85 | // reserved 86 | code = 0; 87 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::ReservedCode(code))); 88 | code = 255; 89 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::ReservedCode(code))); 90 | 91 | // deprecated 92 | for code in [4, 66, 128, 129, 130, 131, 184, 185] { 93 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::DeprecatedCode(code))); 94 | } 95 | 96 | // unassigned 97 | for code in 10..=63 { 98 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::Unassigned(code))); 99 | } 100 | for code in 74..=127 { 101 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::Unassigned(code))); 102 | } 103 | for code in 132..=183 { 104 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::Unassigned(code))); 105 | } 106 | for code in 186..=238 { 107 | assert_eq!(parse_capability(&code), Err(BgpCapabilityParsingError::Unassigned(code))); 108 | } 109 | 110 | // valid capabilities 111 | code = 1; 112 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4)); 113 | code = 2; 114 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4)); 115 | code = 3; 116 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::OUTBOUND_ROUTE_FILTERING_CAPABILITY)); 117 | code = 5; 118 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING)); 119 | code = 6; 120 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::BGP_EXTENDED_MESSAGE)); 121 | code = 7; 122 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::BGPSEC_CAPABILITY)); 123 | code = 8; 124 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::MULTIPLE_LABELS_CAPABILITY)); 125 | code = 9; 126 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::BGP_ROLE)); 127 | 128 | code = 64; 129 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY)); 130 | code = 65; 131 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY)); 132 | code = 67; 133 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::SUPPORT_FOR_DYNAMIC_CAPABILITY)); 134 | code = 68; 135 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::MULTISESSION_BGP_CAPABILITY)); 136 | code = 69; 137 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::ADD_PATH_CAPABILITY)); 138 | code = 70; 139 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::ENHANCED_ROUTE_REFRESH_CAPABILITY)); 140 | code = 71; 141 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::LONG_LIVED_GRACEFUL_RESTART_CAPABILITY)); 142 | code = 72; 143 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::ROUTING_POLICY_DISTRIBUTION)); 144 | code = 73; 145 | assert_eq!(parse_capability(&code), Ok(BgpCapabilityType::FQDN_CAPABILITY)); 146 | 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/bgp/community.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Formatter; 2 | use enum_primitive_derive::Primitive; 3 | use std::net::{Ipv4Addr, Ipv6Addr}; 4 | use serde::Serialize; 5 | use crate::network::Asn; 6 | 7 | #[derive(Debug, PartialEq, Copy, Clone, Eq)] 8 | pub enum MetaCommunity { 9 | Community(Community), 10 | ExtendedCommunity(ExtendedCommunity), 11 | LargeCommunity(LargeCommunity), 12 | } 13 | 14 | #[derive(Debug, PartialEq, Copy, Clone, Eq)] 15 | pub enum Community { 16 | NoExport, 17 | NoAdvertise, 18 | NoExportSubConfed, 19 | Custom(Asn, u16), 20 | } 21 | 22 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 23 | pub struct LargeCommunity { 24 | pub global_administrator: u32, 25 | pub local_data: [u32; 2], 26 | } 27 | 28 | impl LargeCommunity { 29 | pub fn new(global_administrator: u32, local_data: [u32; 2]) -> LargeCommunity { 30 | LargeCommunity { 31 | global_administrator, 32 | local_data, 33 | } 34 | } 35 | } 36 | 37 | /// Type definitions of extended communities 38 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone)] 39 | pub enum ExtendedCommunityType { 40 | // transitive types 41 | 42 | TransitiveTwoOctetAsSpecific = 0x00, 43 | TransitiveIpv4AddressSpecific = 0x01, 44 | TransitiveFourOctetAsSpecific = 0x02, 45 | TransitiveOpaque = 0x03, 46 | 47 | // non-transitive types 48 | 49 | NonTransitiveTwoOctetAsSpecific = 0x40, 50 | NonTransitiveIpv4AddressSpecific = 0x41, 51 | NonTransitiveFourOctetAsSpecific = 0x42, 52 | NonTransitiveOpaque = 0x43, 53 | 54 | // the rest are either draft or experimental 55 | } 56 | 57 | /// Extended Communities. 58 | /// 59 | /// It is a 8-octet data that has flexible definition based on the types: 60 | /// 61 | /// 62 | /// For more up-to-date definitions, see [IANA' website](https://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#e-tree-flags). 63 | /// 64 | /// ```text 65 | /// Each Extended Community is encoded as an 8-octet quantity, as 66 | /// follows: 67 | /// 68 | /// - Type Field : 1 or 2 octets 69 | /// - Value Field : Remaining octets 70 | /// 71 | /// 0 1 2 3 72 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 73 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | /// | Type high | Type low(*) | | 75 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Value | 76 | /// | | 77 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 78 | /// 79 | /// (*) Present for Extended types only, used for the Value field 80 | /// otherwise. 81 | /// ``` 82 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 83 | pub enum ExtendedCommunity { 84 | TransitiveTwoOctetAsSpecific(TwoOctetAsSpecific), 85 | TransitiveIpv4AddressSpecific(Ipv4AddressSpecific), 86 | TransitiveFourOctetAsSpecific(FourOctetAsSpecific), 87 | TransitiveOpaque(Opaque), 88 | NonTransitiveTwoOctetAsSpecific(TwoOctetAsSpecific), 89 | NonTransitiveIpv4AddressSpecific(Ipv4AddressSpecific), 90 | NonTransitiveFourOctetAsSpecific(FourOctetAsSpecific), 91 | NonTransitiveOpaque(Opaque), 92 | Ipv6AddressSpecific(Ipv6AddressSpecific), 93 | Raw([u8; 8]), 94 | } 95 | 96 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 97 | pub struct Ipv6AddressSpecific { 98 | pub ec_type: u8, 99 | pub ec_subtype: u8, 100 | // 16 octets 101 | pub global_administrator: Ipv6Addr, 102 | // 2 octets 103 | pub local_administrator: [u8; 2] 104 | } 105 | 106 | 107 | /// Two-Octet AS Specific Extended Community 108 | /// 109 | /// 110 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 111 | pub struct TwoOctetAsSpecific { 112 | pub ec_type: u8, 113 | pub ec_subtype: u8, 114 | // 2 octet 115 | pub global_administrator: Asn, 116 | // 4 octet 117 | pub local_administrator: [u8; 4], 118 | } 119 | 120 | /// Four-Octet AS Specific Extended Community 121 | /// 122 | /// 123 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 124 | pub struct FourOctetAsSpecific { 125 | pub ec_type: u8, 126 | pub ec_subtype: u8, 127 | // 4 octet 128 | pub global_administrator: Asn, 129 | // 2 octet 130 | pub local_administrator: [u8; 2], 131 | } 132 | 133 | /// IPv4 Address Specific Extended Community 134 | /// 135 | /// 136 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 137 | pub struct Ipv4AddressSpecific { 138 | pub ec_type: u8, 139 | pub ec_subtype: u8, 140 | // 4 octet 141 | pub global_administrator: Ipv4Addr, 142 | // 2 octet 143 | pub local_administrator: [u8; 2], 144 | } 145 | 146 | /// Opaque Extended Community 147 | /// 148 | /// 149 | #[derive(Debug, PartialEq, Clone, Copy, Eq)] 150 | pub struct Opaque { 151 | pub ec_type: u8, 152 | pub ec_subtype: u8, 153 | // 6 octet 154 | pub value: [u8; 6], 155 | } 156 | 157 | ///////////// 158 | // DISPLAY // 159 | ///////////// 160 | 161 | fn bytes_to_string(bytes: &[u8]) -> String { 162 | bytes.iter().map(|x| format!("{:02X}", x)).collect::>().join("") 163 | } 164 | 165 | 166 | impl std::fmt::Display for Community { 167 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 168 | write!(f, "{}", match self { 169 | Community::NoExport => { 170 | "no-export".to_string() 171 | } 172 | Community::NoAdvertise => { 173 | "no-advertise".to_string() 174 | } 175 | Community::NoExportSubConfed => { 176 | "no-export-sub-confed".to_string() 177 | } 178 | Community::Custom(asn, value) => { 179 | format!("{}:{}", asn, value) 180 | } 181 | } 182 | ) 183 | } 184 | } 185 | 186 | impl std::fmt::Display for LargeCommunity { 187 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 188 | write!(f, "lg:{}:{}:{}", self.global_administrator, self.local_data[0], self.local_data[1]) 189 | } 190 | } 191 | 192 | impl std::fmt::Display for ExtendedCommunity { 193 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 194 | write!(f, "{}", match self { 195 | ExtendedCommunity::TransitiveTwoOctetAsSpecific(ec) | ExtendedCommunity::NonTransitiveTwoOctetAsSpecific(ec) => { 196 | format!("ecas2:{}:{}:{}:{}", ec.ec_type, ec.ec_subtype, ec.global_administrator, bytes_to_string(&ec.local_administrator)) 197 | } 198 | ExtendedCommunity::TransitiveIpv4AddressSpecific(ec) | 199 | ExtendedCommunity::NonTransitiveIpv4AddressSpecific(ec) => { 200 | format!("ecv4:{}:{}:{}:{}", ec.ec_type, ec.ec_subtype, ec.global_administrator, bytes_to_string(&ec.local_administrator)) 201 | } 202 | ExtendedCommunity::TransitiveFourOctetAsSpecific(ec) | 203 | ExtendedCommunity::NonTransitiveFourOctetAsSpecific(ec) => { 204 | format!("ecas4:{}:{}:{}:{}", ec.ec_type, ec.ec_subtype, ec.global_administrator, bytes_to_string(&ec.local_administrator)) 205 | } 206 | ExtendedCommunity::TransitiveOpaque(ec) | 207 | ExtendedCommunity::NonTransitiveOpaque(ec) => { 208 | format!("ecop:{}:{}:{}", ec.ec_type, ec.ec_subtype, bytes_to_string(&ec.value)) 209 | } 210 | ExtendedCommunity::Ipv6AddressSpecific(ec) => { 211 | format!("ecv6:{}:{}:{}:{}", ec.ec_type, ec.ec_subtype, ec.global_administrator, bytes_to_string(&ec.local_administrator)) 212 | } 213 | ExtendedCommunity::Raw(ec) => { 214 | format!("ecraw:{}", bytes_to_string(ec)) 215 | } 216 | }) 217 | } 218 | } 219 | 220 | impl std::fmt::Display for MetaCommunity { 221 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 222 | write!(f, "{}", 223 | match self { 224 | MetaCommunity::Community(c) => {c.to_string()} 225 | MetaCommunity::ExtendedCommunity(c) => {c.to_string()} 226 | MetaCommunity::LargeCommunity(c) => {c.to_string()} 227 | } 228 | ) 229 | } 230 | } 231 | 232 | /////////////// 233 | // SERIALIZE // 234 | /////////////// 235 | 236 | macro_rules! impl_serialize { 237 | ($a:ident) => { 238 | impl Serialize for $a { 239 | fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { 240 | serializer.serialize_str(self.to_string().as_str()) 241 | } 242 | } 243 | } 244 | } 245 | 246 | impl_serialize!(Community); 247 | impl_serialize!(ExtendedCommunity); 248 | impl_serialize!(LargeCommunity); 249 | impl_serialize!(MetaCommunity); 250 | -------------------------------------------------------------------------------- /src/bgp/elem.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::fmt::{Display, Formatter}; 3 | use std::net::IpAddr; 4 | use std::str::FromStr; 5 | use itertools::Itertools; 6 | use crate::bgp::attributes::{AsPath, AtomicAggregate, Origin}; 7 | use crate::bgp::community::*; 8 | use crate::network::{Asn, NetworkPrefix}; 9 | use serde::{Serialize, Serializer}; 10 | 11 | /// Element type. 12 | /// 13 | /// - ANNOUNCE: announcement/reachable prefix 14 | /// - WITHDRAW: withdrawn/unreachable prefix 15 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 16 | pub enum ElemType { 17 | ANNOUNCE, 18 | WITHDRAW, 19 | } 20 | 21 | impl Serialize for ElemType { 22 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 23 | Ok(serializer.serialize_str(match self { 24 | ElemType::ANNOUNCE => {"announce"} 25 | ElemType::WITHDRAW => {"withdraw"} 26 | })?) 27 | } 28 | } 29 | 30 | /// BgpElem represents per-prefix BGP element. 31 | /// 32 | /// The information is for per announced/withdrawn prefix. 33 | /// 34 | /// Note: it consumes more memory to construct BGP elements due to duplicate information 35 | /// shared between multiple elements of one MRT record. 36 | #[derive(Debug, Clone, Serialize, PartialEq)] 37 | pub struct BgpElem { 38 | pub timestamp: f64, 39 | #[serde(rename="type")] 40 | pub elem_type: ElemType, 41 | pub peer_ip: IpAddr, 42 | pub peer_asn: Asn, 43 | pub prefix: NetworkPrefix, 44 | pub next_hop: Option, 45 | pub as_path: Option, 46 | pub origin_asns: Option>, 47 | pub origin: Option, 48 | pub local_pref: Option, 49 | pub med: Option, 50 | pub communities: Option>, 51 | pub atomic: Option, 52 | pub aggr_asn: Option, 53 | pub aggr_ip: Option, 54 | } 55 | 56 | impl Eq for BgpElem {} 57 | 58 | impl PartialOrd for BgpElem { 59 | fn partial_cmp(&self, other: &Self) -> Option { 60 | Some(self.cmp(other)) 61 | } 62 | } 63 | 64 | impl Ord for BgpElem { 65 | fn cmp(&self, other: &Self) -> Ordering { 66 | self.timestamp.partial_cmp(&other.timestamp).unwrap().then_with(||self.peer_ip.cmp(&other.peer_ip)) 67 | } 68 | } 69 | 70 | /// Reference version of the [BgpElem] struct. 71 | #[derive(Debug, Clone, Serialize)] 72 | pub struct BgpElemRef<'a> { 73 | pub timestamp: &'a f64, 74 | pub elem_type: &'a ElemType, 75 | pub peer_ip: &'a IpAddr, 76 | pub peer_asn: &'a Asn, 77 | pub prefix: &'a NetworkPrefix, 78 | pub next_hop: &'a Option, 79 | pub as_path: &'a Option, 80 | pub origin_asns: &'a Option>, 81 | pub origin: &'a Option, 82 | pub local_pref: &'a Option, 83 | pub med: &'a Option, 84 | pub communities: &'a Option>, 85 | pub atomic: &'a Option, 86 | pub aggr_asn: &'a Option, 87 | pub aggr_ip: &'a Option, 88 | } 89 | 90 | impl Default for BgpElem { 91 | fn default() -> Self { 92 | BgpElem { 93 | timestamp: 0.0, 94 | elem_type: ElemType::ANNOUNCE, 95 | peer_ip: IpAddr::from_str("0.0.0.0").unwrap(), 96 | peer_asn: 0.into(), 97 | prefix: NetworkPrefix::from_str("0.0.0.0/0").unwrap(), 98 | next_hop: None, 99 | as_path: None, 100 | origin_asns: None, 101 | origin: None, 102 | local_pref: None, 103 | med: None, 104 | communities: None, 105 | atomic: None, 106 | aggr_asn: None, 107 | aggr_ip: None 108 | } 109 | } 110 | } 111 | 112 | macro_rules! option_to_string{ 113 | ($a:expr) => { 114 | if let Some(v) = $a { 115 | v.to_string() 116 | } else { 117 | String::new() 118 | } 119 | } 120 | } 121 | 122 | #[inline(always)] 123 | pub fn option_to_string_communities(o: &Option>) -> String { 124 | if let Some(v) = o { 125 | v.iter() 126 | .join(" ") 127 | } else { 128 | String::new() 129 | } 130 | } 131 | 132 | impl Display for BgpElem { 133 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 134 | let t = match self.elem_type { 135 | ElemType::ANNOUNCE => "A", 136 | ElemType::WITHDRAW => "W", 137 | }; 138 | let format = format!( 139 | "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", 140 | t, &self.timestamp, 141 | &self.peer_ip, 142 | &self.peer_asn, 143 | &self.prefix, 144 | option_to_string!(&self.as_path), 145 | option_to_string!(&self.origin), 146 | option_to_string!(&self.next_hop), 147 | option_to_string!(&self.local_pref), 148 | option_to_string!(&self.med), 149 | option_to_string_communities(&self.communities), 150 | option_to_string!(&self.atomic), 151 | option_to_string!(&self.aggr_asn), 152 | option_to_string!(&self.aggr_ip), 153 | ); 154 | write!(f, "{}", format) 155 | } 156 | } 157 | 158 | #[cfg(test)] 159 | mod tests { 160 | use std::str::FromStr; 161 | use std::default::Default; 162 | use super::*; 163 | 164 | #[test] 165 | fn test_default() { 166 | let elem = BgpElem{ 167 | timestamp: 0.0, 168 | elem_type: ElemType::ANNOUNCE, 169 | peer_ip: IpAddr::from_str("192.168.1.1").unwrap(), 170 | peer_asn: 0.into(), 171 | prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(), 172 | ..Default::default() 173 | }; 174 | println!("{}",serde_json::json!(elem).to_string()); 175 | } 176 | 177 | #[test] 178 | fn test_sorting() { 179 | let elem1 = BgpElem{ 180 | timestamp: 1.1, 181 | elem_type: ElemType::ANNOUNCE, 182 | peer_ip: IpAddr::from_str("192.168.1.1").unwrap(), 183 | peer_asn: 0.into(), 184 | prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(), 185 | ..Default::default() 186 | }; 187 | let elem2 = BgpElem{ 188 | timestamp: 1.2, 189 | elem_type: ElemType::ANNOUNCE, 190 | peer_ip: IpAddr::from_str("192.168.1.1").unwrap(), 191 | peer_asn: 0.into(), 192 | prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(), 193 | ..Default::default() 194 | }; 195 | let elem3 = BgpElem{ 196 | timestamp: 1.2, 197 | elem_type: ElemType::ANNOUNCE, 198 | peer_ip: IpAddr::from_str("192.168.1.2").unwrap(), 199 | peer_asn: 0.into(), 200 | prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(), 201 | ..Default::default() 202 | }; 203 | 204 | assert_eq!(elem1. 5 | use std::error::Error; 6 | use std::fmt::{Display, Formatter}; 7 | use serde::Serialize; 8 | use num_traits::FromPrimitive; 9 | 10 | /// Error for parsing BGP error code 11 | #[derive(Debug, PartialEq, Eq, Clone, Serialize)] 12 | pub enum BgpErrorCodeParsingError { 13 | UnknownCode(u8), 14 | UnknownSubcode(u8), 15 | DeprecatedCode(u8), 16 | DeprecatedSubcode(u8), 17 | } 18 | 19 | impl Display for BgpErrorCodeParsingError { 20 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 21 | match self { 22 | BgpErrorCodeParsingError::UnknownCode(v) => { 23 | write!(f, "unknown BGP error code {}", v) 24 | } 25 | BgpErrorCodeParsingError::UnknownSubcode(v) => { 26 | write!(f, "unknown BGP error subcode {}", v) 27 | } 28 | BgpErrorCodeParsingError::DeprecatedCode(v) => { 29 | write!(f, "deprecated BGP error code {}", v) 30 | } 31 | BgpErrorCodeParsingError::DeprecatedSubcode(v) => { 32 | write!(f, "deprecated BGP error subcode {}", v) 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl Error for BgpErrorCodeParsingError{} 39 | 40 | /// Utility function to parse a pair of BGP error code and subcode (both u8) into a defined struct. 41 | pub fn parse_error_codes(error_code: &u8, error_subcode: &u8) -> Result { 42 | match error_code { 43 | 0 => { 44 | Ok(BgpError::Reserved) 45 | }, 46 | 1 => { 47 | match MessageHeaderErrorSubcode::from_u8(*error_subcode){ 48 | Some(v) => { 49 | Ok(BgpError::MessageHeaderError(v)) 50 | }, 51 | None => { 52 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 53 | } 54 | } 55 | }, 56 | 2 => { 57 | if [5,8,9,10].contains(error_subcode) { 58 | return Err(BgpErrorCodeParsingError::DeprecatedSubcode(*error_subcode)) 59 | } 60 | match OpenMessageErrorSubcode::from_u8(*error_subcode){ 61 | Some(v) => { 62 | Ok(BgpError::OpenMessageError(v)) 63 | }, 64 | None => { 65 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 66 | } 67 | } 68 | }, 69 | 3 => { 70 | if [7].contains(error_subcode) { 71 | return Err(BgpErrorCodeParsingError::DeprecatedSubcode(*error_subcode)) 72 | } 73 | match UpdateMessageErrorSubcode::from_u8(*error_subcode){ 74 | Some(v) => { 75 | Ok(BgpError::UpdateMessageError(v)) 76 | }, 77 | None => { 78 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 79 | } 80 | } 81 | }, 82 | 4 => { 83 | Ok(BgpError::HoldTimerExpired) 84 | }, 85 | 5 => { 86 | match BgpFiniteStateMachineErrorSubcode::from_u8(*error_subcode){ 87 | Some(v) => { 88 | Ok(BgpError::BgpFiniteStateMachineError(v)) 89 | }, 90 | None => { 91 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 92 | } 93 | } 94 | }, 95 | 6 => { 96 | match BgpCeaseNotificationMessageSubcode::from_u8(*error_subcode){ 97 | Some(v) => { 98 | Ok(BgpError::BgpCeaseNotification(v)) 99 | }, 100 | None => { 101 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 102 | } 103 | } 104 | }, 105 | 7 => { 106 | match BgpRouteRefreshMessageErrorSubcode::from_u8(*error_subcode){ 107 | Some(v) => { 108 | Ok(BgpError::BgpRouteFreshMessageError(v)) 109 | }, 110 | None => { 111 | Err(BgpErrorCodeParsingError::UnknownSubcode(*error_subcode)) 112 | } 113 | } 114 | }, 115 | v => Err(BgpErrorCodeParsingError::UnknownCode(*v)), 116 | } 117 | } 118 | 119 | /// BGP Error Subcode enum. 120 | /// 121 | /// 122 | #[allow(non_camel_case_types)] 123 | #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 124 | pub enum BgpError { 125 | Reserved, 126 | MessageHeaderError(MessageHeaderErrorSubcode), 127 | OpenMessageError(OpenMessageErrorSubcode), 128 | UpdateMessageError(UpdateMessageErrorSubcode), 129 | HoldTimerExpired, 130 | BgpFiniteStateMachineError(BgpFiniteStateMachineErrorSubcode), 131 | BgpCeaseNotification(BgpCeaseNotificationMessageSubcode), 132 | BgpRouteFreshMessageError(BgpRouteRefreshMessageErrorSubcode), 133 | } 134 | 135 | /// Message Header Error subcodes 136 | /// 137 | /// 138 | /// 139 | /// *See source code for number assignment* 140 | #[allow(non_camel_case_types)] 141 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 142 | pub enum MessageHeaderErrorSubcode { 143 | UNSPECIFIC = 0, 144 | CONNECTION_NOT_SYNCHRONIZED = 1, 145 | BAD_MESSAGE_LENGTH = 2, 146 | BAD_MESSAGE_TYPE = 3, 147 | // 4 - 255: unassigned 148 | } 149 | 150 | 151 | /// OPEN Message Error subcodes 152 | /// 153 | /// 154 | /// 155 | /// *See source code for number assignment* 156 | #[allow(non_camel_case_types)] 157 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 158 | pub enum OpenMessageErrorSubcode { 159 | UNSPECIFIC = 0, 160 | UNSUPPORTED_VERSION_NUMBER = 1, 161 | BAD_PEER_AS = 2, 162 | BAD_BGP_IDENTIFIER = 3, 163 | UNSUPPORTED_OPTIONAL_PARAMETER = 4, 164 | // 5 -- deprecated 165 | UNACCEPTABLE_HOLD_TIME = 6, 166 | UNSUPPORTED_CAPACITY = 7, 167 | // 8 -- deprecated 168 | // 9 -- deprecated 169 | // 10 -- deprecated 170 | ROLE_MISMATCH = 11, 171 | // 12 - 255: unassinged 172 | } 173 | 174 | /// UPDATE Message Error subcodes 175 | /// 176 | /// 177 | /// 178 | /// *See source code for number assignment* 179 | #[allow(non_camel_case_types)] 180 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 181 | pub enum UpdateMessageErrorSubcode { 182 | UNSPECIFIC = 0, 183 | MALFORMED_ATTRIBUTE_LIST = 1, 184 | UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2, 185 | MISSING_WELL_KNOWN_ATTRIBUTE = 3, 186 | ATTRIBUTE_FLAGS_ERROR = 4, 187 | ATTRIBUTE_LENGTH_ERROR = 5, 188 | INVALID_ORIGIN_ERROR = 6, 189 | // 7 - deprecated 190 | INVALID_NEXT_HOP_ATTRIBUTE = 8, 191 | OPTIONAL_ATTRIBUTE_ERROR = 9, 192 | INVALID_NETWORK_FIELD = 10, 193 | MALFORMED_AS_PATH = 11, 194 | // 12 - 255: unassigned 195 | } 196 | 197 | /// BGP Finite State Machine Error Subcodes 198 | /// 199 | /// 200 | /// 201 | /// *See source code for number assignment* 202 | #[allow(non_camel_case_types)] 203 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 204 | pub enum BgpFiniteStateMachineErrorSubcode { 205 | UNSPECIFIED = 0, 206 | RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_State = 1, 207 | RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE = 2, 208 | RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE = 3, 209 | // 4 - 255: unassigned 210 | } 211 | 212 | 213 | /// BGP Cease NOTIFICATION message subcodes 214 | /// 215 | /// 216 | /// 217 | /// *See source code for number assignment* 218 | #[allow(non_camel_case_types)] 219 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 220 | pub enum BgpCeaseNotificationMessageSubcode { 221 | RESERVED = 0, 222 | MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1, 223 | ADMINISTRATIVE_SHUTDOWN = 2, 224 | PEER_DE_CONFIGURED = 3, 225 | ADMINISTRATIVE_RESET = 4, 226 | CONNECTION_REJECTED = 5, 227 | OTHER_CONFIGURATION_CHANGE = 6, 228 | CONNECTION_COLLISION_RESOLUTION = 7, 229 | OUT_OF_RESOURCES = 8, 230 | HARD_RESET = 9, 231 | BFD_DOWN = 10, // TEMPORARY - registered 2022-02-23, expires 2023-02-23 232 | // 11 - 255: unassigned 233 | } 234 | 235 | /// BGP ROUTE-REFRESH Message Error subcodes 236 | /// 237 | /// 238 | /// 239 | /// *See source code for number assignment* 240 | #[allow(non_camel_case_types)] 241 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 242 | pub enum BgpRouteRefreshMessageErrorSubcode { 243 | RESERVED = 0, 244 | INVALID_MESSAGE_LENGTH = 1, 245 | // 2 - 255: unassigned 246 | } 247 | 248 | #[cfg(test)] 249 | mod tests { 250 | use super::*; 251 | 252 | #[test] 253 | fn test_parsing() { 254 | let mut error_code: u8; 255 | let mut error_subcode: u8; 256 | error_code = 0; 257 | error_subcode = 0; 258 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::Reserved)); 259 | 260 | error_code = 1; 261 | error_subcode = 0; 262 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::MessageHeaderError(MessageHeaderErrorSubcode::UNSPECIFIC))); 263 | error_subcode = 1; 264 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::MessageHeaderError(MessageHeaderErrorSubcode::CONNECTION_NOT_SYNCHRONIZED))); 265 | error_subcode = 2; 266 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::MessageHeaderError(MessageHeaderErrorSubcode::BAD_MESSAGE_LENGTH))); 267 | error_subcode = 3; 268 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::MessageHeaderError(MessageHeaderErrorSubcode::BAD_MESSAGE_TYPE))); 269 | error_subcode = 4; 270 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(4))); 271 | 272 | error_code = 2; 273 | error_subcode = 0; 274 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::UNSPECIFIC))); 275 | error_subcode = 1; 276 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::UNSUPPORTED_VERSION_NUMBER))); 277 | error_subcode = 2; 278 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::BAD_PEER_AS))); 279 | error_subcode = 3; 280 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::BAD_BGP_IDENTIFIER))); 281 | error_subcode = 4; 282 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::UNSUPPORTED_OPTIONAL_PARAMETER))); 283 | error_subcode = 6; 284 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::UNACCEPTABLE_HOLD_TIME))); 285 | error_subcode = 7; 286 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::UNSUPPORTED_CAPACITY))); 287 | error_subcode = 11; 288 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::OpenMessageError(OpenMessageErrorSubcode::ROLE_MISMATCH))); 289 | // deprecated subcodes 290 | for n in [5,8,9,10] { 291 | error_subcode = n as u8; 292 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::DeprecatedSubcode(error_subcode))); 293 | } 294 | error_subcode = 12; 295 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(12))); 296 | 297 | error_code = 3; 298 | error_subcode = 0; 299 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::UNSPECIFIC))); 300 | error_subcode = 1; 301 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::MALFORMED_ATTRIBUTE_LIST))); 302 | error_subcode = 2; 303 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE))); 304 | error_subcode = 3; 305 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::MISSING_WELL_KNOWN_ATTRIBUTE))); 306 | error_subcode = 4; 307 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::ATTRIBUTE_FLAGS_ERROR))); 308 | error_subcode = 5; 309 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::ATTRIBUTE_LENGTH_ERROR))); 310 | error_subcode = 6; 311 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::INVALID_ORIGIN_ERROR))); 312 | error_subcode = 8; 313 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::INVALID_NEXT_HOP_ATTRIBUTE))); 314 | error_subcode = 9; 315 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::OPTIONAL_ATTRIBUTE_ERROR))); 316 | error_subcode = 10; 317 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::INVALID_NETWORK_FIELD))); 318 | error_subcode = 11; 319 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::UpdateMessageError(UpdateMessageErrorSubcode::MALFORMED_AS_PATH))); 320 | // deprecated subcodes 321 | for n in [7] { 322 | error_subcode = n as u8; 323 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::DeprecatedSubcode(error_subcode))); 324 | } 325 | error_subcode = 12; 326 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(12))); 327 | 328 | error_code = 4; 329 | error_subcode = 0; 330 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::HoldTimerExpired)); 331 | // subcode should not matter here 332 | error_subcode = 1; 333 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::HoldTimerExpired)); 334 | 335 | error_code = 5; 336 | error_subcode = 0; 337 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpFiniteStateMachineError(BgpFiniteStateMachineErrorSubcode::UNSPECIFIED))); 338 | error_subcode = 1; 339 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpFiniteStateMachineError(BgpFiniteStateMachineErrorSubcode::RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_State))); 340 | error_subcode = 2; 341 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpFiniteStateMachineError(BgpFiniteStateMachineErrorSubcode::RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE))); 342 | error_subcode = 3; 343 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpFiniteStateMachineError(BgpFiniteStateMachineErrorSubcode::RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE))); 344 | error_subcode = 4; 345 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(4))); 346 | 347 | error_code = 6; 348 | error_subcode = 0; 349 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::RESERVED))); 350 | error_subcode = 1; 351 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::MAXIMUM_NUMBER_OF_PREFIXES_REACHED))); 352 | error_subcode = 2; 353 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::ADMINISTRATIVE_SHUTDOWN))); 354 | error_subcode = 3; 355 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::PEER_DE_CONFIGURED))); 356 | error_subcode = 4; 357 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::ADMINISTRATIVE_RESET))); 358 | error_subcode = 5; 359 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::CONNECTION_REJECTED))); 360 | error_subcode = 6; 361 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::OTHER_CONFIGURATION_CHANGE))); 362 | error_subcode = 7; 363 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::CONNECTION_COLLISION_RESOLUTION))); 364 | error_subcode = 8; 365 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::OUT_OF_RESOURCES))); 366 | error_subcode = 9; 367 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::HARD_RESET))); 368 | error_subcode = 10; 369 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpCeaseNotification(BgpCeaseNotificationMessageSubcode::BFD_DOWN))); 370 | error_subcode = 11; 371 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(11))); 372 | 373 | error_code = 7; 374 | error_subcode = 0; 375 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpRouteFreshMessageError(BgpRouteRefreshMessageErrorSubcode::RESERVED))); 376 | error_subcode = 1; 377 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Ok(BgpError::BgpRouteFreshMessageError(BgpRouteRefreshMessageErrorSubcode::INVALID_MESSAGE_LENGTH))); 378 | error_subcode = 2; 379 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownSubcode(2))); 380 | 381 | error_code = 8; 382 | assert_eq!(parse_error_codes(&error_code, &error_subcode), Err(BgpErrorCodeParsingError::UnknownCode(8))); 383 | 384 | } 385 | } -------------------------------------------------------------------------------- /src/bgp/mod.rs: -------------------------------------------------------------------------------- 1 | //! BGP messages and relevant structs. 2 | 3 | pub mod attributes; 4 | pub mod elem; 5 | pub mod community; 6 | pub mod error; 7 | pub mod capabilities; 8 | pub mod role; 9 | 10 | pub use crate::bgp::attributes::*; 11 | pub use crate::bgp::elem::*; 12 | pub use crate::bgp::community::*; 13 | pub use crate::bgp::error::*; 14 | pub use crate::bgp::capabilities::*; 15 | pub use crate::bgp::role::*; 16 | 17 | use serde::Serialize; 18 | use std::net::Ipv4Addr; 19 | use crate::bgp::capabilities::BgpCapabilityType; 20 | use crate::bgp::error::BgpError; 21 | use crate::network::*; 22 | 23 | #[derive(Debug, Primitive, Copy, Clone, Serialize, PartialEq)] 24 | pub enum BgpMessageType { 25 | OPEN = 1, 26 | UPDATE = 2, 27 | NOTIFICATION = 3, 28 | KEEPALIVE = 4, 29 | } 30 | 31 | // https://tools.ietf.org/html/rfc4271#section-4 32 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 33 | pub enum BgpMessage{ 34 | Open(BgpOpenMessage), 35 | Update(BgpUpdateMessage), 36 | Notification(BgpNotificationMessage), 37 | KeepAlive(BgpKeepAliveMessage), 38 | } 39 | 40 | /// BGP Open Message 41 | /// 42 | /// ```text 43 | /// 0 1 2 3 44 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 45 | /// +-+-+-+-+-+-+-+-+ 46 | /// | Version | 47 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | /// | My Autonomous System | 49 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 | /// | Hold Time | 51 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 | /// | BGP Identifier | 53 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 | /// | Opt Parm Len | 55 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 56 | /// | | 57 | /// | Optional Parameters (variable) | 58 | /// | | 59 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 60 | /// ``` 61 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 62 | pub struct BgpOpenMessage { 63 | pub version: u8, 64 | pub asn: Asn, 65 | pub hold_time: u16, 66 | pub sender_ip: Ipv4Addr, 67 | pub extended_length: bool, 68 | pub opt_params: Vec 69 | } 70 | 71 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 72 | pub struct OptParam { 73 | pub param_type: u8, 74 | pub param_len: u16, 75 | pub param_value: ParamValue, 76 | } 77 | 78 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 79 | pub enum ParamValue { 80 | Raw(Vec), 81 | Capability(Capability) 82 | } 83 | 84 | /// BGP Capability. 85 | /// 86 | /// - RFC3392: 87 | /// - Capability codes: 88 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 89 | pub struct Capability { 90 | pub code: u8, 91 | pub len: u8, 92 | pub value: Vec, 93 | pub capability_type: Option, 94 | } 95 | 96 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 97 | pub struct BgpUpdateMessage { 98 | pub withdrawn_prefixes: Vec, 99 | pub attributes: Vec, 100 | pub announced_prefixes: Vec, 101 | } 102 | 103 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 104 | pub struct BgpNotificationMessage { 105 | pub error_code: u8, 106 | pub error_subcode: u8, 107 | pub error_type: Option, 108 | pub data: Vec, 109 | } 110 | 111 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 112 | pub struct BgpKeepAliveMessage { 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/bgp/role.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use num_traits::FromPrimitive; 3 | 4 | /// BGP Role 5 | /// 6 | /// Defined in [RFC9234](https://www.iana.org/go/rfc9234). 7 | #[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)] 8 | pub enum BgpRole { 9 | Provider = 0, 10 | RouteServer = 1, 11 | RouteServerClient = 2, 12 | Customer = 3, 13 | Peer = 4, 14 | } 15 | 16 | pub fn parse_bgp_role_value(value: &u8) -> Option { 17 | BgpRole::from_u8(*value) 18 | } 19 | 20 | /// Validate the local-remote BGP Role pairs. 21 | /// 22 | /// This function checks the role correctness by following the description in [Section 4.2 of RFC9234](https://www.rfc-editor.org/rfc/rfc9234.html#section-4.2). 23 | /// 24 | /// The acceptable local-remote BGP pairs are: 25 | /// 26 | /// Local AS Role | Remote AS Role 27 | /// --- | --- 28 | /// Provider | Customer 29 | /// Customer | Provider 30 | /// RouteServer | RouterServer-Client 31 | /// RouterServer-Client | RouteServer 32 | /// Peer | Peer 33 | /// 34 | pub fn validate_role_pairs(local_role: &BgpRole, remote_role: &BgpRole) -> bool { 35 | match local_role { 36 | BgpRole::Provider => { 37 | if let BgpRole::Customer = remote_role { 38 | return true 39 | } 40 | return false 41 | } 42 | BgpRole::RouteServer => { 43 | if let BgpRole::RouteServerClient = remote_role { 44 | return true 45 | } 46 | return false 47 | } 48 | BgpRole::RouteServerClient => { 49 | if let BgpRole::RouteServer = remote_role { 50 | return true 51 | } 52 | return false 53 | } 54 | BgpRole::Customer => { 55 | if let BgpRole::Provider = remote_role { 56 | return true 57 | } 58 | return false 59 | } 60 | BgpRole::Peer => { 61 | if let BgpRole::Peer = remote_role { 62 | return true 63 | } 64 | return false 65 | } 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use crate::bgp::BgpRole::*; 72 | use super::*; 73 | 74 | #[test] 75 | fn test_bgp_role_validation() { 76 | let mut local: BgpRole; 77 | let mut remote: BgpRole; 78 | 79 | local = Provider; 80 | remote = Customer; 81 | assert_eq!(validate_role_pairs(&local, &remote), true); 82 | for remote in [ Provider, Peer, RouteServer, RouteServerClient] { 83 | assert_eq!(validate_role_pairs(&local, &remote), false); 84 | } 85 | 86 | local = Customer; 87 | remote = Provider; 88 | assert_eq!(validate_role_pairs(&local, &remote), true); 89 | for remote in [ Customer, Peer, RouteServer, RouteServerClient] { 90 | assert_eq!(validate_role_pairs(&local, &remote), false); 91 | } 92 | 93 | local = RouteServer; 94 | remote = RouteServerClient; 95 | assert_eq!(validate_role_pairs(&local, &remote), true); 96 | for remote in [ Provider, Customer, Peer, RouteServer] { 97 | assert_eq!(validate_role_pairs(&local, &remote), false); 98 | } 99 | 100 | local = RouteServerClient; 101 | remote = RouteServer; 102 | assert_eq!(validate_role_pairs(&local, &remote), true); 103 | for remote in [ Provider, Customer, Peer, RouteServerClient] { 104 | assert_eq!(validate_role_pairs(&local, &remote), false); 105 | } 106 | 107 | local = Peer; 108 | remote = Peer; 109 | assert_eq!(validate_role_pairs(&local, &remote), true); 110 | for remote in [ Provider, Customer, RouteServer, RouteServerClient] { 111 | assert_eq!(validate_role_pairs(&local, &remote), false); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /src/err.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt::{Display, Formatter}; 3 | use ipnetwork::IpNetworkError; 4 | 5 | #[derive(Debug)] 6 | pub enum BgpModelsError { 7 | PrefixParsingError(String), 8 | } 9 | 10 | impl Display for BgpModelsError { 11 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 12 | match self{ 13 | BgpModelsError::PrefixParsingError(msg) => { 14 | write!(f, "cannot convert str to IP prefix: {}", msg) 15 | } 16 | } 17 | } 18 | } 19 | 20 | impl Error for BgpModelsError{} 21 | 22 | impl From for BgpModelsError { 23 | fn from(err: IpNetworkError) -> Self { 24 | BgpModelsError::PrefixParsingError(err.to_string()) 25 | } 26 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | `bgp-models` is a library that defines the basic BGP and MRT message data structures. 3 | This library aims to provide building blocks for downstreams libraries working with BGP and MRT 4 | messages such as MRT parser or BGP table constructor. 5 | 6 | ## Supported RFCs 7 | 8 | Most of the structs defined in this library are named after the formal definitions in a number of 9 | RFCs. Here is a list of them: 10 | 11 | ### BGP 12 | - [X] [RFC 2042](https://datatracker.ietf.org/doc/html/rfc2042): Registering New BGP Attribute Types 13 | - [X] [RFC 3392](https://datatracker.ietf.org/doc/html/rfc3392): Capabilities Advertisement with BGP-4 14 | - [X] [RFC 4271](https://datatracker.ietf.org/doc/html/rfc4271): A Border Gateway Protocol 4 (BGP-4) 15 | - [X] [RFC 5065](https://datatracker.ietf.org/doc/html/rfc5065): Autonomous System Confederations for BGP 16 | - [X] [RFC 6793](https://datatracker.ietf.org/doc/html/rfc6793): BGP Support for Four-Octet Autonomous System (AS) Number Space 17 | - [X] [RFC 7911](https://datatracker.ietf.org/doc/html/rfc7911): Advertisement of Multiple Paths in BGP (ADD-PATH) 18 | - [X] [RFC 9072](https://datatracker.ietf.org/doc/html/rfc9072): Extended Optional Parameters Length for BGP OPEN Message Updates 19 | - [X] [RFC 9234](https://datatracker.ietf.org/doc/html/rfc9234): Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages 20 | 21 | ### MRT 22 | 23 | - [X] [RFC 6396](https://datatracker.ietf.org/doc/html/rfc6396): Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format 24 | - [ ] [RFC 6397](https://datatracker.ietf.org/doc/html/rfc6397): Multi-Threaded Routing Toolkit (MRT) Border Gateway Protocol (BGP) Routing Information Export Format with Geo-Location Extensions 25 | - [X] [RFC 8050](https://datatracker.ietf.org/doc/html/rfc8050): Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format with BGP Additional Path Extensions 26 | 27 | ### Communities 28 | 29 | #### Communities 30 | 31 | - [X] [RFC 1977](https://datatracker.ietf.org/doc/html/rfc1977): BGP Communities Attribute 32 | 33 | #### Extended Communities 34 | 35 | - [X] [RFC 4360](https://datatracker.ietf.org/doc/html/rfc4360): BGP Extended Communities Attribute 36 | - [X] [RFC 5668](https://datatracker.ietf.org/doc/html/rfc5668): 4-Octet AS Specific BGP Extended Community 37 | - [X] [RFC 5701](https://datatracker.ietf.org/doc/html/rfc5701): IPv6 Address Specific BGP Extended Community Attribute 38 | - [X] [RFC 7153](https://datatracker.ietf.org/doc/html/rfc7153): IANA Registries for BGP Extended Communities Updates 4360, 5701 39 | - [X] [RFC 8097](https://datatracker.ietf.org/doc/html/rfc8097): BGP Prefix Origin Validation State Extended Community 40 | 41 | #### Large Communities 42 | 43 | - [X] [RFC 8092](https://datatracker.ietf.org/doc/html/rfc8092): BGP Large Communities 44 | 45 | ### Other Informational 46 | 47 | - [RFC 4384](https://datatracker.ietf.org/doc/html/rfc4384): BGP Communities for Data Collection BCP 114 48 | - [RFC 8195](https://datatracker.ietf.org/doc/html/rfc8195): Use of BGP Large Communities (informational) 49 | - [RFC 8642](https://datatracker.ietf.org/doc/html/rfc8642): Policy Behavior for Well-Known BGP Communities 50 | 51 | */ 52 | 53 | #![allow(dead_code)] 54 | 55 | pub mod bgp; 56 | pub mod network; 57 | pub mod mrt; 58 | pub mod prelude; 59 | pub mod err; 60 | 61 | #[macro_use] 62 | extern crate enum_primitive_derive; -------------------------------------------------------------------------------- /src/mrt/bgp4mp.rs: -------------------------------------------------------------------------------- 1 | //! MRT BGP4MP structs 2 | use std::net::IpAddr; 3 | use serde::Serialize; 4 | use crate::bgp::BgpMessage; 5 | use crate::network::{Afi, Asn}; 6 | 7 | /// BGP states enum. 8 | #[derive(Debug, Primitive, Copy, Clone, Serialize, PartialEq, Eq)] 9 | pub enum BgpState { 10 | Idle = 1, 11 | Connect = 2, 12 | Active = 3, 13 | OpenSent = 4, 14 | OpenConfirm = 5, 15 | Established = 6, 16 | } 17 | 18 | /// BGP4MP message types. 19 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 20 | pub enum Bgp4Mp { 21 | Bgp4MpStateChange(Bgp4MpStateChange), 22 | Bgp4MpStateChangeAs4(Bgp4MpStateChange), 23 | Bgp4MpMessage(Bgp4MpMessage), 24 | Bgp4MpMessageLocal(Bgp4MpMessage), 25 | Bgp4MpMessageAs4(Bgp4MpMessage), 26 | Bgp4MpMessageAs4Local(Bgp4MpMessage), 27 | } 28 | 29 | /// BGP4MP message subtypes. 30 | #[derive(Debug, Primitive, Copy, Clone, Serialize, PartialEq, Eq)] 31 | pub enum Bgp4MpType { 32 | Bgp4MpStateChange = 0, 33 | Bgp4MpMessage = 1, 34 | Bgp4MpMessageAs4 = 4, 35 | Bgp4MpStateChangeAs4 = 5, 36 | Bgp4MpMessageLocal = 6, 37 | Bgp4MpMessageAs4Local = 7, 38 | Bgp4MpMessageAddpath = 8, 39 | Bgp4MpMessageAs4Addpath = 9, 40 | Bgp4MpMessageLocalAddpath = 10, 41 | Bgp4MpMessageLocalAs4Addpath = 11, 42 | } 43 | 44 | /// BGP4MP state change message. 45 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 46 | pub struct Bgp4MpStateChange { 47 | pub msg_type: Bgp4MpType, 48 | pub peer_asn: Asn, 49 | pub local_asn: Asn, 50 | pub interface_index: u16, 51 | pub address_family: Afi, 52 | pub peer_addr: IpAddr, 53 | pub local_addr: IpAddr, 54 | pub old_state: BgpState, 55 | pub new_state: BgpState, 56 | } 57 | 58 | /// BGP4MP message. 59 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 60 | pub struct Bgp4MpMessage { 61 | pub msg_type: Bgp4MpType, 62 | pub peer_asn: Asn, 63 | pub local_asn: Asn, 64 | pub interface_index: u16, 65 | pub afi: Afi, 66 | pub peer_ip: IpAddr, 67 | pub local_ip: IpAddr, 68 | pub bgp_message: BgpMessage 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/mrt/mod.rs: -------------------------------------------------------------------------------- 1 | //! MRT message and relevant structs. 2 | 3 | pub mod tabledump; 4 | pub mod bgp4mp; 5 | 6 | pub use crate::mrt::bgp4mp::*; 7 | pub use crate::mrt::tabledump::*; 8 | use serde::Serialize; 9 | 10 | /// MrtRecord is a wrapper struct that contains a header and a message. 11 | /// 12 | /// A MRT record is constructed as the following: 13 | /// ```text 14 | /// 0 1 2 3 15 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 16 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 17 | /// | Header... (variable) | 18 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 19 | /// | Message... (variable) 20 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 21 | /// ``` 22 | /// 23 | /// See [CommonHeader] for the content in header, and [MrtMessage] for the 24 | /// message format. 25 | #[derive(Debug, Serialize, PartialEq, Clone)] 26 | pub struct MrtRecord { 27 | pub common_header: CommonHeader, 28 | pub message: MrtMessage, 29 | } 30 | 31 | /// MRT common header. 32 | /// 33 | /// A CommonHeader ([RFC6396 section 2][header-link]) is constructed as the following: 34 | /// ```text 35 | /// 0 1 2 3 36 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 37 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | /// | Timestamp | 39 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | /// | Type | Subtype | 41 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | /// | Length | 43 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 | /// ``` 45 | /// 46 | /// Or with extended timestamp: 47 | /// ```text 48 | /// 0 1 2 3 49 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 50 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51 | /// | Timestamp | 52 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 53 | /// | Type | Subtype | 54 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 55 | /// | Length | 56 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 57 | /// | Microsecond Timestamp | 58 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 59 | /// ``` 60 | /// 61 | /// The headers include the following: 62 | /// - timestamp: 32 bits 63 | /// - entry_type: [EntryType] enum 64 | /// - entry_subtype: entry subtype 65 | /// - length: length of the message in octets 66 | /// - (`ET` type only) microsecond_timestamp: microsecond part of the timestamp. 67 | /// only applicable to the MRT message type with `_ET` suffix, such as 68 | /// `BGP4MP_ET` 69 | /// 70 | /// [header-link]: https://datatracker.ietf.org/doc/html/rfc6396#section-2 71 | #[derive(Debug, Copy, Clone, Serialize, PartialEq, Eq)] 72 | pub struct CommonHeader { 73 | pub timestamp: u32, 74 | pub microsecond_timestamp: Option, 75 | pub entry_type: EntryType, 76 | pub entry_subtype: u16, 77 | pub length: u32, 78 | } 79 | 80 | #[derive(Debug, Serialize, PartialEq, Eq, Clone)] 81 | pub enum MrtMessage { 82 | TableDumpMessage(TableDumpMessage), 83 | TableDumpV2Message(TableDumpV2Message), 84 | Bgp4Mp(Bgp4Mp), 85 | } 86 | 87 | /// MRT entry type. 88 | /// 89 | /// EntryType indicates the type of the current MRT record. Type 0 to 10 are deprecated. 90 | /// 91 | /// Excerpt from [RFC6396 section 4](https://datatracker.ietf.org/doc/html/rfc6396#section-4): 92 | /// ```text 93 | /// The following MRT Types are currently defined for the MRT format. 94 | /// The MRT Types that contain the "_ET" suffix in their names identify 95 | /// those types that use an Extended Timestamp MRT Header. The Subtype 96 | /// and Message fields in these types remain as defined for the MRT Types 97 | /// of the same name without the "_ET" suffix. 98 | /// 99 | /// 11 OSPFv2 100 | /// 12 TABLE_DUMP 101 | /// 13 TABLE_DUMP_V2 102 | /// 16 BGP4MP 103 | /// 17 BGP4MP_ET 104 | /// 32 ISIS 105 | /// 33 ISIS_ET 106 | /// 48 OSPFv3 107 | /// 49 OSPFv3_ET 108 | /// ``` 109 | #[derive(Debug, Primitive, Copy, Clone, Serialize, PartialEq, Eq)] 110 | #[allow(non_camel_case_types)] 111 | pub enum EntryType { 112 | // START DEPRECATED 113 | NULL = 0, 114 | START = 1, 115 | DIE = 2, 116 | I_AM_DEAD = 3, 117 | PEER_DOWN = 4, 118 | BGP = 5, 119 | RIP = 6, 120 | IDRP = 7, 121 | RIPNG = 8, 122 | BGP4PLUS = 9, 123 | BGP4PLUS_01 = 10, 124 | // END DEPRECATED 125 | OSPFv2 = 11, 126 | TABLE_DUMP = 12, 127 | TABLE_DUMP_V2 = 13, 128 | BGP4MP = 16, 129 | BGP4MP_ET = 17, 130 | ISIS = 32, 131 | ISIS_ET = 33, 132 | OSPFv3 = 48, 133 | OSPFv3_ET = 49, 134 | } 135 | 136 | -------------------------------------------------------------------------------- /src/mrt/tabledump.rs: -------------------------------------------------------------------------------- 1 | //! MRT table dump version 1 and 2 structs 2 | use std::net::{IpAddr, Ipv4Addr}; 3 | use std::collections::HashMap; 4 | use crate::network::{Afi, Asn, NetworkPrefix, Safi}; 5 | use serde::Serialize; 6 | use crate::bgp::Attribute; 7 | 8 | /// TableDump message version 1 9 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 10 | pub struct TableDumpMessage { 11 | pub view_number: u16, 12 | pub sequence_number: u16, 13 | pub prefix: NetworkPrefix, 14 | pub status: u8, 15 | pub originated_time: u64, 16 | pub peer_address: IpAddr, 17 | pub peer_asn: Asn, 18 | pub attributes: Vec, 19 | } 20 | 21 | /// TableDump message version 2 enum 22 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 23 | pub enum TableDumpV2Message { 24 | PeerIndexTable(PeerIndexTable), 25 | RibAfiEntries(RibAfiEntries), 26 | RibGenericEntries(RibGenericEntries), 27 | } 28 | 29 | /// TableDump version 2 subtypes. 30 | /// 31 | /// 32 | #[derive(Debug, Primitive, Copy, Clone, Serialize, PartialEq, Eq)] 33 | pub enum TableDumpV2Type{ 34 | PeerIndexTable = 1, 35 | RibIpv4Unicast = 2, 36 | RibIpv4Multicast = 3, 37 | RibIpv6Unicast = 4, 38 | RibIpv6Multicast = 5, 39 | RibGeneric = 6, 40 | GeoPeerTable = 7, 41 | RibIpv4UnicastAddPath = 8, 42 | RibIpv4MulticastAddPath = 9, 43 | RibIpv6UnicastAddPath = 10, 44 | RibIpv6MulticastAddPath = 11, 45 | RibGenericAddPath = 12, 46 | } 47 | 48 | 49 | /// AFI/SAFI-Specific RIB Subtypes. 50 | /// 51 | /// ```text 52 | /// The AFI/SAFI-specific RIB Subtypes consist of the RIB_IPV4_UNICAST, 53 | /// RIB_IPV4_MULTICAST, RIB_IPV6_UNICAST, and RIB_IPV6_MULTICAST 54 | /// Subtypes. These specific RIB table entries are given their own MRT 55 | /// TABLE_DUMP_V2 subtypes as they are the most common type of RIB table 56 | /// instances, and providing specific MRT subtypes for them permits more 57 | /// compact encodings. These subtypes permit a single MRT record to 58 | /// encode multiple RIB table entries for a single prefix. The Prefix 59 | /// Length and Prefix fields are encoded in the same manner as the BGP 60 | /// NLRI encoding for IPv4 and IPv6 prefixes. Namely, the Prefix field 61 | /// contains address prefixes followed by enough trailing bits to make 62 | /// the end of the field fall on an octet boundary. The value of 63 | /// trailing bits is irrelevant. 64 | /// 65 | /// 0 1 2 3 66 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 67 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 | /// | Sequence Number | 69 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 | /// | Prefix Length | 71 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 | /// | Prefix (variable) | 73 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | /// | Entry Count | RIB Entries (variable) 75 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 | /// ``` 77 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 78 | pub struct RibAfiEntries{ 79 | pub rib_type: TableDumpV2Type, 80 | pub sequence_number: u32, 81 | pub prefix: NetworkPrefix, 82 | pub rib_entries: Vec, 83 | } 84 | 85 | /// RIB generic entries subtype. 86 | /// 87 | /// ```text 88 | /// The RIB_GENERIC header is shown below. It is used to cover RIB 89 | /// entries that do not fall under the common case entries defined above. 90 | /// It consists of an AFI, Subsequent AFI (SAFI), and a single NLRI 91 | /// entry. The NLRI information is specific to the AFI and SAFI values. 92 | /// An implementation that does not recognize particular AFI and SAFI 93 | /// values SHOULD discard the remainder of the MRT record. 94 | /// 0 1 2 3 95 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 96 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 97 | /// | Sequence Number | 98 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 99 | /// | Address Family Identifier |Subsequent AFI | 100 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 101 | /// | Network Layer Reachability Information (variable) | 102 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 103 | /// | Entry Count | RIB Entries (variable) 104 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 105 | /// ``` 106 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 107 | pub struct RibGenericEntries{ 108 | pub sequence_number: u32, 109 | pub afi: Afi, 110 | pub safi: Safi, 111 | pub nlri: NetworkPrefix, 112 | pub rib_entries: Vec, 113 | } 114 | 115 | /// RIB entry. 116 | /// 117 | /// ```text 118 | /// The RIB Entries are repeated Entry Count times. These entries share 119 | /// a common format as shown below. They include a Peer Index from the 120 | /// PEER_INDEX_TABLE MRT record, an originated time for the RIB Entry, 121 | /// and the BGP path attribute length and attributes. All AS numbers in 122 | /// the AS_PATH attribute MUST be encoded as 4-byte AS numbers. 123 | /// 124 | /// 0 1 2 3 125 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 126 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 127 | /// | Peer Index | 128 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 129 | /// | Originated Time | 130 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 131 | /// | Attribute Length | 132 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 133 | /// | BGP Attributes... (variable) 134 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 135 | /// ``` 136 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 137 | pub struct RibEntry { 138 | pub peer_index: u16, 139 | pub originated_time: u32, 140 | pub attributes: Vec 141 | } 142 | 143 | /// peer index table. 144 | /// 145 | /// ```text 146 | /// An initial PEER_INDEX_TABLE MRT record provides the BGP ID of the 147 | /// collector, an OPTIONAL view name, and a list of indexed peers. 148 | /// Following the PEER_INDEX_TABLE MRT record, a series of MRT records is 149 | /// used to encode RIB table entries. This series of MRT records uses 150 | /// subtypes 2-6 and is separate from the PEER_INDEX_TABLE MRT record 151 | /// itself and includes full MRT record headers. The RIB entry MRT 152 | /// records MUST immediately follow the PEER_INDEX_TABLE MRT record. 153 | /// ``` 154 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 155 | pub struct PeerIndexTable{ 156 | pub collector_bgp_id: Ipv4Addr, 157 | pub view_name_length: u16, 158 | pub view_name: String, 159 | pub peer_count: u16, 160 | pub peers_map: HashMap 161 | } 162 | 163 | /// Peer struct. 164 | #[derive(Debug, Clone, Serialize, PartialEq, Eq)] 165 | pub struct Peer { 166 | pub peer_type: u8, 167 | pub peer_bgp_id: Ipv4Addr, 168 | pub peer_address: IpAddr, 169 | pub peer_asn: Asn, 170 | } -------------------------------------------------------------------------------- /src/network.rs: -------------------------------------------------------------------------------- 1 | //! Common network-related structs. 2 | 3 | use std::fmt::{Display, Formatter}; 4 | use std::net::{Ipv4Addr, Ipv6Addr}; 5 | use std::str::FromStr; 6 | use ipnetwork::IpNetwork; 7 | use serde::{Serialize, Serializer, Deserialize}; 8 | use crate::err::BgpModelsError; 9 | 10 | /// Meta information for an address/prefix. 11 | /// 12 | /// [AddrMeta] is a struct that used to save address family and as number length information 13 | /// when parsing [TableDumpMessage]. 14 | /// 15 | /// The meta information includes: 16 | /// 1. `afi`: address family ([Afi]): IPv4 or IPv6, 17 | /// 2. `asn_len`: AS number length ([AsnLength]): 16 or 32 bits. 18 | #[derive(Debug, Clone, Serialize, Copy)] 19 | pub struct AddrMeta { 20 | pub afi: Afi, 21 | pub asn_len: AsnLength, 22 | } 23 | 24 | /// AS number length: 16 or 32 bits. 25 | #[derive(Debug, Clone, Serialize, Copy, Deserialize, PartialEq, Eq)] 26 | pub enum AsnLength { 27 | Bits16, 28 | Bits32, 29 | } 30 | 31 | /// ASN -- Autonomous System Number 32 | #[derive(Debug, Clone, Copy, Eq)] 33 | pub struct Asn { 34 | pub asn: u32, 35 | pub len: AsnLength, 36 | } 37 | 38 | impl PartialEq for Asn { 39 | fn eq(&self, other: &Self) -> bool { 40 | self.asn==other.asn 41 | } 42 | } 43 | 44 | impl PartialEq for Asn { 45 | fn eq(&self, other: &i32) -> bool { 46 | self.asn as i32==*other 47 | } 48 | } 49 | 50 | impl PartialEq for Asn { 51 | fn eq(&self, other: &u32) -> bool { 52 | self.asn==*other 53 | } 54 | } 55 | 56 | impl From for Asn { 57 | fn from(v: u32) -> Self { 58 | Asn{asn:v, len: AsnLength::Bits32} 59 | } 60 | } 61 | 62 | impl From for Asn { 63 | fn from(v: i32) -> Self { 64 | Asn{asn:v as u32, len: AsnLength::Bits32} 65 | } 66 | } 67 | 68 | impl Into for Asn { 69 | fn into(self) -> i32 { 70 | self.asn as i32 71 | } 72 | } 73 | 74 | impl Into for Asn { 75 | fn into(self) -> u32 { 76 | self.asn 77 | } 78 | } 79 | 80 | impl Serialize for Asn { 81 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 82 | Ok( serializer.serialize_u32(self.asn)?) 83 | } 84 | } 85 | 86 | /// AFI -- Address Family Identifier 87 | /// 88 | /// https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml 89 | #[derive(Debug, PartialEq, Primitive, Clone, Copy, Serialize, Eq)] 90 | pub enum Afi { 91 | Ipv4 = 1, 92 | Ipv6 = 2, 93 | } 94 | 95 | /// SAFI -- Subsequent Address Family Identifier 96 | /// 97 | /// SAFI can be: Unicast, Multicast, or both. 98 | #[derive(Debug, PartialEq, Primitive, Clone, Copy, Serialize, Eq)] 99 | pub enum Safi { 100 | Unicast = 1, 101 | Multicast = 2, 102 | UnicastMulticast = 3, 103 | } 104 | 105 | /// enum that represents the type of the next hop address. 106 | /// 107 | /// [NextHopAddress] is used when parsing for next hops in [Nlri]. 108 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Eq)] 109 | pub enum NextHopAddress { 110 | Ipv4(Ipv4Addr), 111 | Ipv6(Ipv6Addr), 112 | Ipv6LinkLocal(Ipv6Addr, Ipv6Addr), 113 | } 114 | 115 | /// A representation of a IP prefix with optional path ID. 116 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 117 | pub struct NetworkPrefix { 118 | pub prefix: IpNetwork, 119 | pub path_id: u32, 120 | } 121 | 122 | impl Serialize for NetworkPrefix { 123 | fn serialize(&self, serializer: S) -> Result where S: Serializer { 124 | serializer.serialize_str(self.to_string().as_str()) 125 | } 126 | } 127 | 128 | impl FromStr for NetworkPrefix { 129 | type Err = BgpModelsError; 130 | 131 | fn from_str(s: &str) -> Result { 132 | let prefix = IpNetwork::from_str(s)?; 133 | Ok( 134 | NetworkPrefix{ 135 | prefix, 136 | path_id: 0, 137 | } 138 | ) 139 | } 140 | } 141 | 142 | impl NetworkPrefix { 143 | pub fn new(prefix: IpNetwork, path_id: u32) -> NetworkPrefix { 144 | NetworkPrefix { prefix, path_id } 145 | } 146 | } 147 | 148 | impl Display for NetworkPrefix { 149 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 150 | write!(f, "{}", self.prefix) 151 | } 152 | } 153 | 154 | impl Display for Asn { 155 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 156 | write!(f, "{}", self.asn) 157 | } 158 | } 159 | 160 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::bgp::*; 2 | pub use crate::mrt::*; 3 | pub use crate::network::*; 4 | pub use crate::err::BgpModelsError; --------------------------------------------------------------------------------