Cleanup and implementation of Update / Notification parser / serializers.
This commit is contained in:
1 parent
dfe066c9ab
commit
5ae8f6e249
22 files changed
+1559
-51
No files matched your search
Generated
+578
-5
@@ -17,6 +17,71 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
@@ -50,6 +115,7 @@ dependencies = [
|
||||
name = "bgp-packet"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"bgp-primitives",
|
||||
"bitfield",
|
||||
"bytes",
|
||||
"eyre",
|
||||
@@ -57,7 +123,35 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bgp-primitives"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"bitfield",
|
||||
"bytes",
|
||||
"eyre",
|
||||
"nom",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bgp-server"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"eyre",
|
||||
"prometheus",
|
||||
"protobuf",
|
||||
"protobuf-codegen",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -98,6 +192,74 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
@@ -108,30 +270,103 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@@ -142,6 +377,21 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -165,7 +415,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -177,6 +427,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
@@ -192,6 +451,12 @@ version = "1.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
@@ -230,6 +495,72 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fnv",
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"parking_lot",
|
||||
"protobuf",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"protobuf-support",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
"protobuf",
|
||||
"protobuf-parse",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-parse"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"log",
|
||||
"protobuf",
|
||||
"protobuf-support",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-support"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
@@ -239,6 +570,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.10"
|
||||
@@ -248,12 +585,67 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
@@ -297,6 +689,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
@@ -319,9 +720,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.1"
|
||||
@@ -355,13 +762,46 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.1.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -375,6 +815,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.2"
|
||||
@@ -390,7 +839,7 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -404,18 +853,118 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@@ -425,6 +974,24 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@@ -488,3 +1055,9 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
@@ -16,6 +16,8 @@ version = "0.0.2"
|
||||
[workspace.dependencies]
|
||||
# --- Our crates ---
|
||||
bgp-packet = { path = "crates/packet" }
|
||||
bgp-primitives = { path = "crates/primitives" }
|
||||
bgp-server = { path = "crates/server" }
|
||||
|
||||
# --- General ---
|
||||
bitfield = "0.19.0"
|
||||
@@ -28,3 +30,9 @@ serde_repr = "0.1.20"
|
||||
thiserror = "2.0.12"
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||
prometheus = "0.14.0"
|
||||
protobuf = { package = "protobuf", version = "3.7.2" }
|
||||
protobuf-codegen = { package = "protobuf-codegen", version = "3.7.2" }
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
@@ -14,13 +14,14 @@ The project is currently being rewritten from scratch to be more modular and mai
|
||||
|
||||
We aim for the 1.0 release to be functional and stable to operate peering sessions, and to install routes in the Linux Kernel.
|
||||
|
||||
* ☐ Implement basic parsers for the fundamental types required
|
||||
* ✅ Path Attributes from RFC4271
|
||||
* ✅ Open message
|
||||
* ✅ Capabilities from RFC4271
|
||||
* ✅ Update message
|
||||
* ☐ Notification message
|
||||
* ☐ Implement connection logic to establish sessions with peers
|
||||
* ☐ Implement an efficient data structure for storing prefixes (e.g. Poptrie)
|
||||
* ☐ Filtering for peer RIB in / out
|
||||
* ☐ Main / auxillary RIBs for exchanging routes between peers
|
||||
- ☐ Implement basic parsers for the fundamental types required
|
||||
- ✅ Path Attributes from RFC4271
|
||||
- ✅ Open message
|
||||
- ✅ Capabilities from RFC4271
|
||||
- ✅ Update message
|
||||
- ☐ Notification message
|
||||
- ☐ Implement scaffolding required to start and configure a server
|
||||
- ☐ Implement connection logic to establish sessions with peers
|
||||
- ☐ Implement an efficient data structure for storing prefixes (e.g. Poptrie)
|
||||
- ☐ Filtering for peer RIB in / out
|
||||
- ☐ Main / auxillary RIBs for exchanging routes between peers
|
||||
@@ -17,3 +17,4 @@ serde.workspace = true
|
||||
serde_repr.workspace = true
|
||||
thiserror.workspace = true
|
||||
strum.workspace = true
|
||||
bgp-primitives.workspace = true
|
||||
+201
-7
@@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum BgpError {
|
||||
#[error("Message header error")]
|
||||
@@ -28,7 +29,36 @@ pub enum BgpError {
|
||||
SendHoldTimer = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl BgpError {
|
||||
pub fn from_code_subcode(code: u8, subcode: u8) -> Result<Self, ()> {
|
||||
match code {
|
||||
1 => Ok(Self::MsgHeader(MsgHeaderSubcode::try_from(subcode)?)),
|
||||
2 => Ok(Self::OpenMsg(OpenMsgSubcode::try_from(subcode)?)),
|
||||
3 => Ok(Self::UpdateMsg(UpdateMsgSubcode::try_from(subcode)?)),
|
||||
4 => Ok(Self::HoldTimer),
|
||||
5 => Ok(Self::FsmError(FsmSubcode::try_from(subcode)?)),
|
||||
6 => Ok(Self::Cease(CeaseSubcode::try_from(subcode)?)),
|
||||
7 => Ok(Self::RouteRefresh(RouteRefreshSubcode::try_from(subcode)?)),
|
||||
8 => Ok(Self::SendHoldTimer),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_subcode(&self) -> (u8, u8) {
|
||||
match self {
|
||||
Self::MsgHeader(s) => (1, u8::from(s)),
|
||||
Self::OpenMsg(s) => (2, u8::from(s)),
|
||||
Self::UpdateMsg(s) => (3, u8::from(s)),
|
||||
Self::HoldTimer => (4, 0),
|
||||
Self::FsmError(s) => (5, u8::from(s)),
|
||||
Self::Cease(s) => (6, u8::from(s)),
|
||||
Self::RouteRefresh(s) => (7, u8::from(s)),
|
||||
Self::SendHoldTimer => (8, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum MsgHeaderSubcode {
|
||||
#[error("Connection not synchronized")]
|
||||
ConnNotSynchronized = 1,
|
||||
@@ -40,7 +70,29 @@ pub enum MsgHeaderSubcode {
|
||||
BadMessagType = 3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl TryFrom<u8> for MsgHeaderSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::ConnNotSynchronized),
|
||||
2 => Ok(Self::BadMessageLength),
|
||||
3 => Ok(Self::BadMessagType),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&MsgHeaderSubcode> for u8 {
|
||||
fn from(s: &MsgHeaderSubcode) -> u8 {
|
||||
match s {
|
||||
MsgHeaderSubcode::ConnNotSynchronized => 1,
|
||||
MsgHeaderSubcode::BadMessageLength => 2,
|
||||
MsgHeaderSubcode::BadMessagType => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum OpenMsgSubcode {
|
||||
#[error("Unsupported BGP version number")]
|
||||
UnsupportedVersion = 1,
|
||||
@@ -64,7 +116,37 @@ pub enum OpenMsgSubcode {
|
||||
RoleMismatch = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl TryFrom<u8> for OpenMsgSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::UnsupportedVersion),
|
||||
2 => Ok(Self::BadPeerAs),
|
||||
3 => Ok(Self::BadBgpId),
|
||||
4 => Ok(Self::UnsupportedOptionalParam),
|
||||
6 => Ok(Self::UnacceptableHoldTime),
|
||||
7 => Ok(Self::UnsupportedCapability),
|
||||
8 => Ok(Self::RoleMismatch),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&OpenMsgSubcode> for u8 {
|
||||
fn from(s: &OpenMsgSubcode) -> u8 {
|
||||
match s {
|
||||
OpenMsgSubcode::UnsupportedVersion => 1,
|
||||
OpenMsgSubcode::BadPeerAs => 2,
|
||||
OpenMsgSubcode::BadBgpId => 3,
|
||||
OpenMsgSubcode::UnsupportedOptionalParam => 4,
|
||||
OpenMsgSubcode::UnacceptableHoldTime => 6,
|
||||
OpenMsgSubcode::UnsupportedCapability => 7,
|
||||
OpenMsgSubcode::RoleMismatch => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum UpdateMsgSubcode {
|
||||
#[error("Malformed attribute list")]
|
||||
MalforedAttrs = 1,
|
||||
@@ -97,7 +179,43 @@ pub enum UpdateMsgSubcode {
|
||||
MalformedAsPath = 11,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl TryFrom<u8> for UpdateMsgSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::MalforedAttrs),
|
||||
2 => Ok(Self::UnrecognizedWellKnownAttr),
|
||||
3 => Ok(Self::MissingWellKnown),
|
||||
4 => Ok(Self::AttributeFlags),
|
||||
5 => Ok(Self::AttributeLength),
|
||||
6 => Ok(Self::InvalidOrigin),
|
||||
8 => Ok(Self::InvalidNextHop),
|
||||
9 => Ok(Self::OptionalAttribute),
|
||||
10 => Ok(Self::InvalidNetworkField),
|
||||
11 => Ok(Self::MalformedAsPath),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&UpdateMsgSubcode> for u8 {
|
||||
fn from(s: &UpdateMsgSubcode) -> u8 {
|
||||
match s {
|
||||
UpdateMsgSubcode::MalforedAttrs => 1,
|
||||
UpdateMsgSubcode::UnrecognizedWellKnownAttr => 2,
|
||||
UpdateMsgSubcode::MissingWellKnown => 3,
|
||||
UpdateMsgSubcode::AttributeFlags => 4,
|
||||
UpdateMsgSubcode::AttributeLength => 5,
|
||||
UpdateMsgSubcode::InvalidOrigin => 6,
|
||||
UpdateMsgSubcode::InvalidNextHop => 8,
|
||||
UpdateMsgSubcode::OptionalAttribute => 9,
|
||||
UpdateMsgSubcode::InvalidNetworkField => 10,
|
||||
UpdateMsgSubcode::MalformedAsPath => 11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum FsmSubcode {
|
||||
#[error("Received an unexpected message in OpenSent state")]
|
||||
UnexpectedOpenSent = 1,
|
||||
@@ -109,7 +227,29 @@ pub enum FsmSubcode {
|
||||
UnexpectedEstablished = 3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl TryFrom<u8> for FsmSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::UnexpectedOpenSent),
|
||||
2 => Ok(Self::UnexpectedOpenConfirm),
|
||||
3 => Ok(Self::UnexpectedEstablished),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FsmSubcode> for u8 {
|
||||
fn from(s: &FsmSubcode) -> u8 {
|
||||
match s {
|
||||
FsmSubcode::UnexpectedOpenSent => 1,
|
||||
FsmSubcode::UnexpectedOpenConfirm => 2,
|
||||
FsmSubcode::UnexpectedEstablished => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum CeaseSubcode {
|
||||
#[error("Maximum number of prefixes reached")]
|
||||
MaxPrefixes = 1,
|
||||
@@ -142,8 +282,62 @@ pub enum CeaseSubcode {
|
||||
BfdDown = 10,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
impl TryFrom<u8> for CeaseSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::MaxPrefixes),
|
||||
2 => Ok(Self::AdminShutdown),
|
||||
3 => Ok(Self::PeerDeconf),
|
||||
4 => Ok(Self::AdminReset),
|
||||
5 => Ok(Self::ConnRejected),
|
||||
6 => Ok(Self::ConfChange),
|
||||
7 => Ok(Self::ConnCollisionResolution),
|
||||
8 => Ok(Self::OutOfResources),
|
||||
9 => Ok(Self::HardReset),
|
||||
10 => Ok(Self::BfdDown),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CeaseSubcode> for u8 {
|
||||
fn from(s: &CeaseSubcode) -> u8 {
|
||||
match s {
|
||||
CeaseSubcode::MaxPrefixes => 1,
|
||||
CeaseSubcode::AdminShutdown => 2,
|
||||
CeaseSubcode::PeerDeconf => 3,
|
||||
CeaseSubcode::AdminReset => 4,
|
||||
CeaseSubcode::ConnRejected => 5,
|
||||
CeaseSubcode::ConfChange => 6,
|
||||
CeaseSubcode::ConnCollisionResolution => 7,
|
||||
CeaseSubcode::OutOfResources => 8,
|
||||
CeaseSubcode::HardReset => 9,
|
||||
CeaseSubcode::BfdDown => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||
pub enum RouteRefreshSubcode {
|
||||
#[error("Invalid message length")]
|
||||
InvalidLength = 1,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for RouteRefreshSubcode {
|
||||
type Error = ();
|
||||
fn try_from(v: u8) -> Result<Self, ()> {
|
||||
match v {
|
||||
1 => Ok(Self::InvalidLength),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&RouteRefreshSubcode> for u8 {
|
||||
fn from(s: &RouteRefreshSubcode) -> u8 {
|
||||
match s {
|
||||
RouteRefreshSubcode::InvalidLength => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
pub mod constants;
|
||||
pub mod errors;
|
||||
pub mod ip_prefix;
|
||||
pub mod message;
|
||||
pub mod open;
|
||||
pub mod parser;
|
||||
+421
-23
@@ -18,12 +18,14 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use strum::EnumDiscriminants;
|
||||
|
||||
use crate::constants::AddressFamilyId;
|
||||
use crate::constants::SubsequentAfi;
|
||||
use crate::ip_prefix::IpPrefix;
|
||||
use bgp_primitives::constants::AddressFamilyId;
|
||||
use bgp_primitives::constants::SubsequentAfi;
|
||||
use bgp_primitives::ip_prefix::IpPrefix;
|
||||
use bgp_primitives::parser::BgpParserError;
|
||||
use bgp_primitives::parser::ParserContext;
|
||||
|
||||
use crate::errors::BgpError;
|
||||
use crate::open::OpenMessage;
|
||||
use crate::parser::BgpParserError;
|
||||
use crate::parser::ParserContext;
|
||||
|
||||
/// A sentinel AS number used to denote that the actual AS number is provided as
|
||||
/// a 4 byte value in the capabilities instead.
|
||||
@@ -47,7 +49,7 @@ pub enum Message {
|
||||
impl Message {}
|
||||
|
||||
/// Represents a BGP Update message.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateMessage {
|
||||
pub withdrawn_routes: Vec<IpPrefix>,
|
||||
pub path_attributes: Vec<PathAttribute>,
|
||||
@@ -55,20 +57,118 @@ pub struct UpdateMessage {
|
||||
}
|
||||
|
||||
impl UpdateMessage {
|
||||
/// Parses a BGP UPDATE message body per RFC 4271 §4.3. The buffer passed in
|
||||
/// must contain exactly the UPDATE payload (i.e. without the 19 byte BGP
|
||||
/// message header). The NLRI section has no explicit length and is inferred
|
||||
/// as the remainder of the buffer, so passing extra trailing bytes will
|
||||
/// cause them to be (mis-)parsed as NLRI.
|
||||
pub fn from_wire<'a>(
|
||||
_ctx: &ParserContext,
|
||||
_buf: &'a [u8],
|
||||
ctx: &ParserContext,
|
||||
buf: &'a [u8],
|
||||
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
|
||||
todo!();
|
||||
// Withdrawn routes. Per RFC 4271 these are always IPv4 prefixes; other
|
||||
// address families are carried in the MP_UNREACH_NLRI path attribute.
|
||||
let (buf, withdrawn_len) = be_u16(buf)?;
|
||||
let (buf, withdrawn_buf) = nom::bytes::take(withdrawn_len as usize).parse(buf)?;
|
||||
let (withdrawn_rem, withdrawn_routes) =
|
||||
nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv4, buf))
|
||||
.parse(withdrawn_buf)?;
|
||||
if !withdrawn_rem.is_empty() {
|
||||
return Err(Failure(BgpParserError::Eyre(eyre!(
|
||||
"Leftover bytes {:x?} after parsing withdrawn routes",
|
||||
withdrawn_rem
|
||||
))));
|
||||
}
|
||||
|
||||
pub fn to_wire(&self, _ctx: &ParserContext, _out: &mut BytesMut) -> Result<()> {
|
||||
todo!()
|
||||
// Path attributes.
|
||||
let (buf, total_pa_len) = be_u16(buf)?;
|
||||
let (buf, pa_buf) = nom::bytes::take(total_pa_len as usize).parse(buf)?;
|
||||
let (pa_rem, path_attributes) =
|
||||
nom::multi::many0(|buf| PathAttribute::from_wire(ctx, buf)).parse(pa_buf)?;
|
||||
if !pa_rem.is_empty() {
|
||||
return Err(Failure(BgpParserError::Eyre(eyre!(
|
||||
"Leftover bytes {:x?} after parsing path attributes",
|
||||
pa_rem
|
||||
))));
|
||||
}
|
||||
|
||||
pub fn wire_len(&self, _ctx: &ParserContext) -> Result<u16> {
|
||||
todo!()
|
||||
// NLRI. As with withdrawn routes, these are IPv4 prefixes per RFC 4271.
|
||||
// They occupy the remainder of the UPDATE body; if the caller has
|
||||
// delimited `buf` correctly then `many0` will consume it entirely.
|
||||
let (buf, announced_routes) =
|
||||
nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv4, buf)).parse(buf)?;
|
||||
if !buf.is_empty() {
|
||||
return Err(Failure(BgpParserError::Eyre(eyre!(
|
||||
"Leftover bytes {:x?} after parsing NLRI",
|
||||
buf
|
||||
))));
|
||||
}
|
||||
|
||||
Ok((
|
||||
buf,
|
||||
Self {
|
||||
withdrawn_routes,
|
||||
path_attributes,
|
||||
announced_routes,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
|
||||
// Withdrawn routes.
|
||||
let withdrawn_len = prefix_list_wire_len(&self.withdrawn_routes)
|
||||
.wrap_err("Withdrawn routes length exceeds u16::MAX")?;
|
||||
out.put_u16(withdrawn_len);
|
||||
for prefix in &self.withdrawn_routes {
|
||||
out.put_u8(prefix.length);
|
||||
out.put(prefix.prefix.as_slice());
|
||||
}
|
||||
|
||||
// Path attributes.
|
||||
let pa_total: u32 = self
|
||||
.path_attributes
|
||||
.iter()
|
||||
.map(|p| p.wire_len(ctx).map(|v| v as u32))
|
||||
.sum::<Result<u32>>()?;
|
||||
let pa_total = u16::try_from(pa_total)
|
||||
.map_err(|_| eyre!("Total path attribute length exceeds u16::MAX"))?;
|
||||
out.put_u16(pa_total);
|
||||
for pa in &self.path_attributes {
|
||||
pa.to_wire(ctx, out)?;
|
||||
}
|
||||
|
||||
// NLRI (consumes the remainder of the message).
|
||||
for prefix in &self.announced_routes {
|
||||
out.put_u8(prefix.length);
|
||||
out.put(prefix.prefix.as_slice());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
|
||||
// 2 bytes for the withdrawn length field + 2 bytes for the total path
|
||||
// attribute length field.
|
||||
let mut total: u32 = 4;
|
||||
for prefix in &self.withdrawn_routes {
|
||||
total += 1 + prefix.prefix.len() as u32;
|
||||
}
|
||||
for pa in &self.path_attributes {
|
||||
total += pa.wire_len(ctx)? as u32;
|
||||
}
|
||||
for prefix in &self.announced_routes {
|
||||
total += 1 + prefix.prefix.len() as u32;
|
||||
}
|
||||
u16::try_from(total).map_err(|_| eyre!("UPDATE message length exceeds u16::MAX"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the total wire length of a list of IPv4/IPv6 NLRI prefixes as they
|
||||
/// appear in the withdrawn or NLRI sections of an UPDATE message (each prefix
|
||||
/// is encoded as a 1-byte length followed by `ceil(length/8)` prefix bytes).
|
||||
fn prefix_list_wire_len(prefixes: &[IpPrefix]) -> Result<u16> {
|
||||
let total: u32 = prefixes.iter().map(|p| 1 + p.prefix.len() as u32).sum();
|
||||
u16::try_from(total).map_err(|_| eyre!("Prefix list length exceeds u16::MAX"))
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
@@ -207,6 +307,20 @@ impl PathAttribute {
|
||||
};
|
||||
|
||||
let discriminant = PathAttributeDiscriminants::from(type_code);
|
||||
if matches!(
|
||||
discriminant,
|
||||
PathAttributeDiscriminants::UnknownPathAttribute
|
||||
) {
|
||||
let (buf, pa_buf) = nom::bytes::take(length).parse(buf)?;
|
||||
return Ok((
|
||||
buf,
|
||||
PathAttribute::UnknownPathAttribute {
|
||||
flags: attr_flags,
|
||||
type_code,
|
||||
payload: pa_buf.to_vec(),
|
||||
},
|
||||
));
|
||||
}
|
||||
Self::parse_known_path_attribute(ctx, buf, discriminant, length)
|
||||
}
|
||||
|
||||
@@ -332,18 +446,48 @@ impl PathAttribute {
|
||||
payload,
|
||||
} => {
|
||||
out.put_u8(flags.0);
|
||||
out.put_u8(*type_code);
|
||||
if flags.extended_length() {
|
||||
out.put_u16(payload.len() as u16);
|
||||
} else {
|
||||
out.put_u8(payload.len() as u8);
|
||||
}
|
||||
out.put_u8(*type_code);
|
||||
out.put(payload.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the total on-wire length of this path attribute, including the
|
||||
/// attribute header (flags + type + length) and the payload.
|
||||
pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
|
||||
let payload_len: u16 = match self {
|
||||
PathAttribute::Origin(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::AsPath(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::NextHop(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::MultiExitDisc(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::LocalPref(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::AtomicAggregate(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::Aggregator(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::Communitites(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::MpReachNlri(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::MpUnreachNlri(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::ExtendedCommunities(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::LargeCommunities(a) => a.wire_len(ctx)?,
|
||||
PathAttribute::UnknownPathAttribute { flags, payload, .. } => {
|
||||
// An unknown attribute preserves its original extended_length
|
||||
// flag on re-serialization, so use it verbatim here rather than
|
||||
// re-deriving it from the payload length.
|
||||
let payload_len = u16::try_from(payload.len())
|
||||
.map_err(|_| eyre!("UnknownPathAttribute payload exceeds u16::MAX"))?;
|
||||
let header_len: u16 = if flags.extended_length() { 4 } else { 3 };
|
||||
return Ok(header_len + payload_len);
|
||||
}
|
||||
};
|
||||
let header_len: u16 = if payload_len > u8::MAX as u16 { 4 } else { 3 };
|
||||
Ok(header_len + payload_len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin path attribute is a mandatory attribute defined in RFC4271.
|
||||
@@ -535,7 +679,7 @@ impl NextHopPathAttribute {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn wire_len(&self, _: &ParserContext) -> Result<u32> {
|
||||
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
@@ -1168,8 +1312,47 @@ impl MpUnreachNlriPathAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NotificationMessage {}
|
||||
/// Represents a BGP NOTIFICATION message per RFC 4271 §4.5.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct NotificationMessage {
|
||||
pub error: BgpError,
|
||||
/// Optional diagnostic data; may be empty.
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl NotificationMessage {
|
||||
/// Parses a NOTIFICATION message body (without the 19-byte BGP header).
|
||||
/// Consumes the entire buffer; any remaining bytes become `data`.
|
||||
pub fn from_wire<'a>(
|
||||
_ctx: &ParserContext,
|
||||
buf: &'a [u8],
|
||||
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
|
||||
let (buf, error_code) = be_u8(buf)?;
|
||||
let (buf, error_subcode) = be_u8(buf)?;
|
||||
|
||||
let error = BgpError::from_code_subcode(error_code, error_subcode).map_err(|_| {
|
||||
Failure(BgpParserError::Eyre(eyre!(
|
||||
"Unknown NOTIFICATION error code {} subcode {}",
|
||||
error_code,
|
||||
error_subcode
|
||||
)))
|
||||
})?;
|
||||
|
||||
Ok((&[], Self { error, data: buf.to_vec() }))
|
||||
}
|
||||
|
||||
pub fn to_wire(&self, _ctx: &ParserContext, buf: &mut BytesMut) -> Result<()> {
|
||||
let (code, subcode) = self.error.code_subcode();
|
||||
buf.put_u8(code);
|
||||
buf.put_u8(subcode);
|
||||
buf.put_slice(&self.data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn wire_len(&self) -> u16 {
|
||||
2 + self.data.len() as u16
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -1181,16 +1364,18 @@ mod tests {
|
||||
use bytes::BytesMut;
|
||||
use eyre::{Result, eyre};
|
||||
|
||||
use crate::{
|
||||
constants::{AddressFamilyId, SubsequentAfi},
|
||||
ip_prefix::IpPrefix,
|
||||
message::{
|
||||
use crate::message::{
|
||||
AggregatorPathAttribute, AsPathAttribute, AsPathSegment, AtomicAggregatePathAttribute,
|
||||
CommunitiesPathAttribute, ExtendedCommunitiesPathAttribute, ExtendedCommunity,
|
||||
LargeCommunitiesPathAttribute, LargeCommunity, LocalPrefPathAttribute,
|
||||
MpReachNlriPathAttribute, MpUnreachNlriPathAttribute, MultiExitDiscPathAttribute,
|
||||
NextHopPathAttribute, NlriNextHop, OriginPathAttribute, PathAttribute,
|
||||
},
|
||||
NextHopPathAttribute, NlriNextHop, NotificationMessage, OriginPathAttribute, PathAttribute,
|
||||
PathAttributeFlags, UpdateMessage,
|
||||
};
|
||||
|
||||
use bgp_primitives::{
|
||||
constants::{AddressFamilyId, SubsequentAfi},
|
||||
ip_prefix::IpPrefix,
|
||||
parser::ParserContext,
|
||||
};
|
||||
|
||||
@@ -1415,4 +1600,217 @@ mod tests {
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
// Unknown Path Attribute. Ensures we both parse an unrecognized type code
|
||||
// into the catch-all variant and re-serialize it with the correct field
|
||||
// ordering (flags, type, length, payload) per RFC 4271 §4.3.
|
||||
test_path_attribute_roundtrip!(
|
||||
test_unknown_path_attribute,
|
||||
&[0x80, 0x63, 0x04, 0xde, 0xad, 0xbe, 0xef],
|
||||
PathAttribute::UnknownPathAttribute {
|
||||
flags: PathAttributeFlags::new(
|
||||
/*optional=*/ true, /*transitive=*/ false, /*partial=*/ false,
|
||||
/*extended_length=*/ false,
|
||||
),
|
||||
type_code: 0x63,
|
||||
payload: vec![0xde, 0xad, 0xbe, 0xef],
|
||||
}
|
||||
);
|
||||
|
||||
// -- UPDATE message roundtrip tests --
|
||||
|
||||
/// Asserts that an UPDATE message roundtrips byte-for-byte and that
|
||||
/// wire_len agrees with the length of the encoded output.
|
||||
macro_rules! test_update_roundtrip {
|
||||
($name:ident, $input_bytes:expr, $expected:expr) => {
|
||||
#[test]
|
||||
fn $name() -> Result<()> {
|
||||
let ctx = &default_v6_context();
|
||||
let (rem, parsed) = UpdateMessage::from_wire(ctx, $input_bytes)?;
|
||||
assert!(rem.is_empty(), "Expected no trailing bytes, got {:x?}", rem);
|
||||
assert_eq!(parsed, $expected);
|
||||
|
||||
let mut out = BytesMut::with_capacity(u16::MAX as usize);
|
||||
parsed.to_wire(ctx, &mut out)?;
|
||||
assert_eq!(out.to_vec(), $input_bytes);
|
||||
assert_eq!(parsed.wire_len(ctx)?, $input_bytes.len() as u16);
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Wrapper to generate a IPv4 prefix used in tests below.
|
||||
fn v4_prefix(length: u8, bytes: &[u8]) -> IpPrefix {
|
||||
IpPrefix {
|
||||
address_family: AddressFamilyId::Ipv4,
|
||||
prefix: bytes.to_vec(),
|
||||
length,
|
||||
}
|
||||
}
|
||||
|
||||
/// A small set of path attributes that commonly accompany an IPv4 unicast
|
||||
/// announcement: Origin=IGP, AS_PATH=[65000], NEXT_HOP=192.0.2.1.
|
||||
fn sample_ipv4_path_attributes() -> Vec<PathAttribute> {
|
||||
vec![
|
||||
PathAttribute::Origin(OriginPathAttribute::IGP),
|
||||
PathAttribute::AsPath(AsPathAttribute {
|
||||
segments: vec![AsPathSegment {
|
||||
ordered: true,
|
||||
path: vec![65000],
|
||||
}],
|
||||
}),
|
||||
PathAttribute::NextHop(NextHopPathAttribute(Ipv4Addr::new(192, 0, 2, 1))),
|
||||
]
|
||||
}
|
||||
|
||||
// Empty UPDATE (equivalent to a keepalive-shaped body): no withdrawn
|
||||
// routes, no path attributes, no NLRI.
|
||||
test_update_roundtrip!(
|
||||
test_update_empty,
|
||||
&[0x00, 0x00, 0x00, 0x00],
|
||||
UpdateMessage {
|
||||
withdrawn_routes: vec![],
|
||||
path_attributes: vec![],
|
||||
announced_routes: vec![],
|
||||
}
|
||||
);
|
||||
|
||||
// Pure announcement: no withdrawn routes, standard v4 path attributes, one
|
||||
// NLRI prefix (10.0.0.0/8).
|
||||
test_update_roundtrip!(
|
||||
test_update_announcement,
|
||||
&[
|
||||
// Withdrawn routes length = 0.
|
||||
0x00, 0x00, // Total path attribute length = 20 bytes.
|
||||
0x00, 0x14, // ORIGIN = IGP.
|
||||
0x40, 0x01, 0x01, 0x00, // AS_PATH = AS_SEQUENCE [65000] (4-octet ASNs).
|
||||
0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0xfd, 0xe8,
|
||||
// NEXT_HOP = 192.0.2.1.
|
||||
0x40, 0x03, 0x04, 0xc0, 0x00, 0x02, 0x01, // NLRI: 10.0.0.0/8.
|
||||
0x08, 0x0a,
|
||||
],
|
||||
UpdateMessage {
|
||||
withdrawn_routes: vec![],
|
||||
path_attributes: sample_ipv4_path_attributes(),
|
||||
announced_routes: vec![v4_prefix(8, &[0x0a])],
|
||||
}
|
||||
);
|
||||
|
||||
// Pure withdrawal: one withdrawn route, no path attributes, no NLRI. RFC
|
||||
// 4271 allows (and indeed requires) this shape for unreach updates.
|
||||
test_update_roundtrip!(
|
||||
test_update_withdrawal,
|
||||
&[
|
||||
// Withdrawn routes length = 2.
|
||||
0x00, 0x02, // 10.0.0.0/8.
|
||||
0x08, 0x0a, // Total path attribute length = 0.
|
||||
0x00, 0x00,
|
||||
],
|
||||
UpdateMessage {
|
||||
withdrawn_routes: vec![v4_prefix(8, &[0x0a])],
|
||||
path_attributes: vec![],
|
||||
announced_routes: vec![],
|
||||
}
|
||||
);
|
||||
|
||||
// Mixed withdrawal + announcement with multiple NLRI entries at different
|
||||
// prefix lengths, exercising the variable-length NLRI parser tail.
|
||||
test_update_roundtrip!(
|
||||
test_update_mixed,
|
||||
&[
|
||||
// Withdrawn routes length = 2.
|
||||
0x00, 0x02, // 10.0.0.0/8 withdrawn.
|
||||
0x08, 0x0a, // Total path attribute length = 20.
|
||||
0x00, 0x14, // ORIGIN = IGP.
|
||||
0x40, 0x01, 0x01, 0x00, // AS_PATH = AS_SEQUENCE [65000].
|
||||
0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0xfd, 0xe8,
|
||||
// NEXT_HOP = 192.0.2.1.
|
||||
0x40, 0x03, 0x04, 0xc0, 0x00, 0x02, 0x01, // NLRI: 172.16.0.0/16.
|
||||
0x10, 0xac, 0x10, // NLRI: 192.168.1.0/24.
|
||||
0x18, 0xc0, 0xa8, 0x01,
|
||||
],
|
||||
UpdateMessage {
|
||||
withdrawn_routes: vec![v4_prefix(8, &[0x0a])],
|
||||
path_attributes: sample_ipv4_path_attributes(),
|
||||
announced_routes: vec![
|
||||
v4_prefix(16, &[0xac, 0x10]),
|
||||
v4_prefix(24, &[0xc0, 0xa8, 0x01]),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// An UPDATE carrying an unrecognized path attribute.
|
||||
test_update_roundtrip!(
|
||||
test_update_with_unknown_path_attribute,
|
||||
&[
|
||||
// Withdrawn routes length = 0.
|
||||
0x00, 0x00, // Total path attribute length = 7.
|
||||
0x00, 0x07, // Unknown attribute: flags=0x80 (optional), type=0x63, len=4.
|
||||
0x80, 0x63, 0x04, 0xde, 0xad, 0xbe, 0xef,
|
||||
],
|
||||
UpdateMessage {
|
||||
withdrawn_routes: vec![],
|
||||
path_attributes: vec![PathAttribute::UnknownPathAttribute {
|
||||
flags: PathAttributeFlags::new(true, false, false, false),
|
||||
type_code: 0x63,
|
||||
payload: vec![0xde, 0xad, 0xbe, 0xef],
|
||||
}],
|
||||
announced_routes: vec![],
|
||||
}
|
||||
);
|
||||
|
||||
// Rejects trailing garbage after the NLRI section (a partial prefix).
|
||||
#[test]
|
||||
fn test_update_rejects_trailing_garbage() {
|
||||
let ctx = default_v6_context();
|
||||
// Well-formed withdrawn_len=0, pa_len=0, followed by a single byte that
|
||||
// declares a prefix length of 8 but provides no prefix bytes.
|
||||
let buf = &[0x00, 0x00, 0x00, 0x00, 0x08];
|
||||
let result = UpdateMessage::from_wire(&ctx, buf);
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Expected a parse error for trailing garbage, got {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notification_roundtrip() -> Result<()> {
|
||||
use crate::errors::{BgpError, CeaseSubcode};
|
||||
|
||||
let ctx = &default_v6_context();
|
||||
let msg = NotificationMessage {
|
||||
error: BgpError::Cease(CeaseSubcode::AdminShutdown),
|
||||
data: vec![0xde, 0xad],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
msg.to_wire(ctx, &mut buf)?;
|
||||
assert_eq!(buf.as_ref(), &[6, 2, 0xde, 0xad]);
|
||||
|
||||
let (rem, parsed) = NotificationMessage::from_wire(ctx, &buf).unwrap();
|
||||
assert!(rem.is_empty());
|
||||
assert_eq!(parsed, msg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notification_no_data() -> Result<()> {
|
||||
use crate::errors::{BgpError, FsmSubcode};
|
||||
|
||||
let ctx = &default_v6_context();
|
||||
let msg = NotificationMessage {
|
||||
error: BgpError::FsmError(FsmSubcode::UnexpectedEstablished),
|
||||
data: vec![],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
msg.to_wire(ctx, &mut buf)?;
|
||||
assert_eq!(buf.as_ref(), &[5, 3]);
|
||||
|
||||
let (rem, parsed) = NotificationMessage::from_wire(ctx, &buf).unwrap();
|
||||
assert!(rem.is_empty());
|
||||
assert_eq!(parsed, msg);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ use nom::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::EnumDiscriminants;
|
||||
|
||||
use crate::{
|
||||
use bgp_primitives::{
|
||||
constants::{AddressFamilyId, SubsequentAfi},
|
||||
parser::{BgpParserError, ParserContext, ProtocolErrorValues},
|
||||
};
|
||||
@@ -814,9 +814,10 @@ mod tests {
|
||||
use bytes::BytesMut;
|
||||
use eyre::Result;
|
||||
|
||||
use crate::{
|
||||
use crate::open::{Capability, ExtendedNextHop, GracefulRestartEntry, OpenMessage, OpenOption};
|
||||
|
||||
use bgp_primitives::{
|
||||
constants::{AddressFamilyId, SubsequentAfi},
|
||||
open::{Capability, ExtendedNextHop, GracefulRestartEntry, OpenMessage, OpenOption},
|
||||
parser::ParserContext,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "bgp-primitives"
|
||||
|
||||
description = "Primitive types for working with routing information"
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitfield.workspace = true
|
||||
bytes.workspace = true
|
||||
eyre.workspace = true
|
||||
nom.workspace = true
|
||||
serde.workspace = true
|
||||
serde_repr.workspace = true
|
||||
thiserror.workspace = true
|
||||
strum.workspace = true
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
||||
pub mod constants;
|
||||
pub mod ip_prefix;
|
||||
pub mod parser;
|
||||
pub mod poptrie;
|
||||
File renamed without changes.
@@ -0,0 +1,176 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Poptrie<T> {
|
||||
pub inners: Vec<Inner>,
|
||||
pub leaves: Vec<Leaf<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Inner {
|
||||
/// A bit vector that describes the child internal nodes.
|
||||
pub internal_v: u64,
|
||||
/// A bit vector that describes the child leaf nodes.
|
||||
pub leaf_v: u64,
|
||||
/// Starting offset where the child interior nodes of this node begins.
|
||||
pub inner_offset: u64,
|
||||
/// Starting offset where the child leaf nodes of this node begins.
|
||||
pub leaf_offset: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Leaf<T> {
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn extract(width: u8, offset: u8, v: u32, bits: u8) -> u8 {
|
||||
let shift = bits.saturating_sub(width * (offset + 1));
|
||||
let mask = 0b111111 << shift;
|
||||
((v & mask) >> shift) as u8
|
||||
}
|
||||
|
||||
fn extract_32(width: u8, offset: u8, v: u32) -> u8 {
|
||||
extract(width, offset, v, 32)
|
||||
}
|
||||
|
||||
fn extract_128(width: u8, offset: u8, v: u32) -> u8 {
|
||||
extract(width, offset, v, 128)
|
||||
}
|
||||
|
||||
impl<T: Clone> Poptrie<T> {
|
||||
pub fn match_u32(&self, addr: u32) -> Option<&T> {
|
||||
let mut i = 0_u64;
|
||||
let mut v = self.inners.get(i as usize)?.internal_v;
|
||||
let mut offset = 0;
|
||||
let mut n = extract(6, offset, addr, 32);
|
||||
|
||||
let mut result = None;
|
||||
|
||||
while (v & (1 << n)) != 0 {
|
||||
let base = self.inners.get(i as usize)?.inner_offset;
|
||||
let arg = v & ((2 << n) - 1);
|
||||
let bc = arg.count_ones() as u64;
|
||||
i = base + bc - 1;
|
||||
v = self.inners.get(i as usize)?.internal_v;
|
||||
|
||||
offset += 1;
|
||||
n = extract(6, offset, addr, 32);
|
||||
|
||||
let base = self.inners.get(i as usize)?.leaf_offset;
|
||||
let v = self.inners.get(i as usize)?.leaf_v;
|
||||
if (v & (1 << n)) != 0 {
|
||||
let i = base - 1;
|
||||
result = Some(&self.leaves.get(i as usize)?.data);
|
||||
}
|
||||
}
|
||||
|
||||
let base = self.inners.get(i as usize)?.leaf_offset;
|
||||
let v = self.inners.get(i as usize)?.leaf_v;
|
||||
if (v & (1 << n)) != 0 {
|
||||
i = base - 1;
|
||||
result = Some(&self.leaves.get(i as usize)?.data);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn new(&mut self, rt: BTreeMap<(u32, u8), T>) {
|
||||
const DEPTH: u8 = 6;
|
||||
let mut forest = vec![(0, rt)];
|
||||
|
||||
let mut i_offset = 1;
|
||||
for _ in 0..DEPTH {
|
||||
let mut subforest = Vec::<(u8, BTreeMap<(u32, u8), T>)>::new();
|
||||
let mut children = 0;
|
||||
let mut siblings = 0;
|
||||
for (_, tree) in &forest {
|
||||
let mut iv = 0_u64;
|
||||
let mut lv = 0_u64;
|
||||
|
||||
let mut subsubforest = Vec::<(u8, BTreeMap<(u32, u8), T>)>::new();
|
||||
for (r, e) in tree {
|
||||
let k = extract(6, DEPTH, r.0, 32);
|
||||
let consumed = core::cmp::min((DEPTH + 1) * 6, 32);
|
||||
if r.1 < consumed {
|
||||
if ((1 << k) & iv) == 0 {
|
||||
lv |= 1 << k;
|
||||
self.leaves.push(Leaf { data: e.clone() });
|
||||
}
|
||||
|
||||
// If the prefix of the route entry is less than but not equal
|
||||
// to the consumed number of bits, we need to add those bits
|
||||
// to the bitvec.
|
||||
if r.1 != consumed {
|
||||
// Shift the extra bits and add to the bitvec for this
|
||||
// internal node.
|
||||
let extra = 1_u32 << (consumed - r.1);
|
||||
for i in 1..(extra).min(64 - k as u32) {
|
||||
lv |= 1_u64 << (k as u32 + i);
|
||||
self.leaves.push(Leaf { data: e.clone() });
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
iv |= 1 << k;
|
||||
match subsubforest.iter_mut().find(|x| x.0 == k) {
|
||||
Some(ref mut entry) => {
|
||||
entry.1.insert(*r, e.clone());
|
||||
}
|
||||
None => {
|
||||
let mut tbl = BTreeMap::new();
|
||||
tbl.insert(*r, e.clone());
|
||||
subsubforest.push((k, tbl));
|
||||
if iv > 0 {
|
||||
children += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if iv > 0 || lv > 0 {
|
||||
self.inners.push(Inner {
|
||||
internal_v: iv,
|
||||
leaf_v: lv,
|
||||
inner_offset: if iv > 0 { i_offset + siblings } else { 0 },
|
||||
leaf_offset: self.leaves.len() as u64,
|
||||
});
|
||||
if iv > 0 {
|
||||
siblings += 1;
|
||||
}
|
||||
}
|
||||
|
||||
subforest.extend_from_slice(subsubforest.as_slice());
|
||||
}
|
||||
i_offset += children;
|
||||
forest = subforest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::BTreeMap, net::Ipv4Addr};
|
||||
|
||||
use crate::poptrie::Poptrie;
|
||||
|
||||
#[test]
|
||||
fn test_poptrie() {
|
||||
let mut map: BTreeMap<(u32, u8), Ipv4Addr> = Default::default();
|
||||
map.insert(
|
||||
(Ipv4Addr::new(127, 0, 0, 0).into(), 8),
|
||||
Ipv4Addr::new(10, 0, 0, 1),
|
||||
);
|
||||
|
||||
let mut poptrie: Poptrie<Ipv4Addr> = Poptrie {
|
||||
inners: vec![],
|
||||
leaves: vec![],
|
||||
};
|
||||
|
||||
poptrie.new(map);
|
||||
println!("Success");
|
||||
|
||||
dbg!(poptrie);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "bgp-server"
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap.workspace = true
|
||||
eyre.workspace = true
|
||||
prometheus.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
protobuf.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen.workspace = true
|
||||
@@ -0,0 +1,9 @@
|
||||
use protobuf_codegen::Codegen;
|
||||
|
||||
fn main() {
|
||||
Codegen::new()
|
||||
.input("proto/config.proto")
|
||||
.includes(&["proto"])
|
||||
.cargo_out_dir("protos")
|
||||
.run_from_script();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package bgp.server;
|
||||
|
||||
// Top level BGP Server configuration.
|
||||
message BgpConfig {
|
||||
// The IPv4 address used as the router ID in BGP.
|
||||
string router_id = 1;
|
||||
// The local AS Number to use when communicating with peers.
|
||||
uint32 local_asn = 2;
|
||||
|
||||
// Endpoints to listen on.
|
||||
repeated TcpEndpoint listen_endpoints = 3;
|
||||
|
||||
// Peers configured on this server.
|
||||
repeated Peer peers = 4;
|
||||
}
|
||||
|
||||
// Represents an IP address and port that can be used for listening or connecting.
|
||||
message TcpEndpoint {
|
||||
// Address to listen on
|
||||
string address = 1;
|
||||
// Port to listen on.
|
||||
uint32 port = 2;
|
||||
}
|
||||
|
||||
// Configuration of a BGP peer.
|
||||
message Peer {
|
||||
string name = 1;
|
||||
TcpEndpoint address = 2;
|
||||
uint32 remote_asn = 3;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod tracing;
|
||||
|
||||
pub mod proto {
|
||||
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use bgp_server::proto;
|
||||
use clap::Parser;
|
||||
use eyre::{Context, Result};
|
||||
use tracing::info;
|
||||
|
||||
use crate::proto::config::BgpConfig;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct ServerArgs {
|
||||
/// Path to the configuration file.
|
||||
config: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
info!("Starting bgp server");
|
||||
|
||||
let args = ServerArgs::parse();
|
||||
let config_string =
|
||||
std::fs::read_to_string(args.config).wrap_err("Failed to open config file")?;
|
||||
let config = protobuf::text_format::parse_from_str::<BgpConfig>(&config_string)?;
|
||||
|
||||
info!("Read config file: {}", config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use clap::Args;
|
||||
use eyre::Result;
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
/// Default targets that we enable logging for.
|
||||
const DEFAULT_TARGETS: [&str; 1] = ["bgp_server"];
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct TracingArgs {
|
||||
/// Set the minimum log level.
|
||||
#[arg(short, long, global = true, default_value_t = Level::TRACE, help_heading = "Tracing")]
|
||||
level: Level,
|
||||
/// Disable logging to stdout.
|
||||
#[arg(long, global = true, default_value_t = false, help_heading = "Tracing")]
|
||||
quiet: bool,
|
||||
}
|
||||
|
||||
impl TracingArgs {
|
||||
pub fn install(&self) -> Result<()> {
|
||||
let filter = DEFAULT_TARGETS.into_iter().try_fold(
|
||||
EnvFilter::builder().from_env_lossy(),
|
||||
|filter, target| -> Result<_> {
|
||||
Ok(filter.add_directive(format!("{}={}", target, self.level).parse()?))
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# RFCs
|
||||
|
||||
This document refers to the relevant RFCs for BGP and associated networking protocol semantics that we implement in this project.
|
||||
|
||||
## RFC 4271 - Border Gateway protocol
|
||||
|
||||
https://datatracker.ietf.org/doc/html/rfc4271
|
||||
|
||||
This is the main specification document for the Border Gateway Protocol, and defines the core protocol semantics and wire format.
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
router_id: "10.0.0.1"
|
||||
local_asn: 65000
|
||||
listen_endpoints {
|
||||
address: "127.0.0.1"
|
||||
port: 179
|
||||
}
|
||||
listen_endpoints {
|
||||
address: "[::1]"
|
||||
port: 179
|
||||
}
|
||||
Reference in new issue
Block a user