Cleanup and implementation of Update / Notification parser / serializers.
This commit is contained in:
1 parent
dfe066c9ab
commit
5ae8f6e249
22 files changed
+1573
-65
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -50,6 +115,7 @@ dependencies = [
|
|||||||
name = "bgp-packet"
|
name = "bgp-packet"
|
||||||
version = "0.0.2"
|
version = "0.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bgp-primitives",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"bytes",
|
"bytes",
|
||||||
"eyre",
|
"eyre",
|
||||||
@@ -57,7 +123,35 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"strum",
|
"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]]
|
[[package]]
|
||||||
@@ -98,6 +192,74 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
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]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.12"
|
version = "0.6.12"
|
||||||
@@ -108,30 +270,103 @@ dependencies = [
|
|||||||
"once_cell",
|
"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]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
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]]
|
[[package]]
|
||||||
name = "indenter"
|
name = "indenter"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
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]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.171"
|
version = "0.2.171"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
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]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -142,6 +377,21 @@ dependencies = [
|
|||||||
"scopeguard",
|
"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]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@@ -165,7 +415,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -177,6 +427,15 @@ dependencies = [
|
|||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@@ -192,6 +451,12 @@ version = "1.21.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -230,6 +495,72 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@@ -239,6 +570,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.10"
|
version = "0.5.10"
|
||||||
@@ -248,12 +585,67 @@ dependencies = [
|
|||||||
"bitflags",
|
"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]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
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]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
@@ -297,6 +689,15 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@@ -319,9 +720,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
@@ -355,13 +762,46 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -375,6 +815,15 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.44.2"
|
version = "1.44.2"
|
||||||
@@ -390,7 +839,7 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -404,18 +853,118 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
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]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -425,6 +974,24 @@ dependencies = [
|
|||||||
"windows-targets",
|
"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]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -488,3 +1055,9 @@ name = "windows_x86_64_msvc"
|
|||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
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]
|
[workspace.dependencies]
|
||||||
# --- Our crates ---
|
# --- Our crates ---
|
||||||
bgp-packet = { path = "crates/packet" }
|
bgp-packet = { path = "crates/packet" }
|
||||||
|
bgp-primitives = { path = "crates/primitives" }
|
||||||
|
bgp-server = { path = "crates/server" }
|
||||||
|
|
||||||
# --- General ---
|
# --- General ---
|
||||||
bitfield = "0.19.0"
|
bitfield = "0.19.0"
|
||||||
@@ -28,3 +30,9 @@ serde_repr = "0.1.20"
|
|||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
tokio = { version = "1.44.2", features = ["full"] }
|
tokio = { version = "1.44.2", features = ["full"] }
|
||||||
strum = { version = "0.27.1", features = ["derive"] }
|
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.
|
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
|
- ☐ Implement basic parsers for the fundamental types required
|
||||||
* ✅ Path Attributes from RFC4271
|
- ✅ Path Attributes from RFC4271
|
||||||
* ✅ Open message
|
- ✅ Open message
|
||||||
* ✅ Capabilities from RFC4271
|
- ✅ Capabilities from RFC4271
|
||||||
* ✅ Update message
|
- ✅ Update message
|
||||||
* ☐ Notification message
|
- ☐ Notification message
|
||||||
* ☐ Implement connection logic to establish sessions with peers
|
- ☐ Implement scaffolding required to start and configure a server
|
||||||
* ☐ Implement an efficient data structure for storing prefixes (e.g. Poptrie)
|
- ☐ Implement connection logic to establish sessions with peers
|
||||||
* ☐ Filtering for peer RIB in / out
|
- ☐ Implement an efficient data structure for storing prefixes (e.g. Poptrie)
|
||||||
* ☐ Main / auxillary RIBs for exchanging routes between peers
|
- ☐ Filtering for peer RIB in / out
|
||||||
|
- ☐ Main / auxillary RIBs for exchanging routes between peers
|
||||||
+13
-12
@@ -2,18 +2,19 @@
|
|||||||
name = "bgp-packet"
|
name = "bgp-packet"
|
||||||
|
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitfield.workspace = true
|
bitfield.workspace = true
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
eyre.workspace = true
|
eyre.workspace = true
|
||||||
nom.workspace = true
|
nom.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_repr.workspace = true
|
serde_repr.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
|
bgp-primitives.workspace = true
|
||||||
+201
-7
@@ -1,6 +1,7 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum BgpError {
|
pub enum BgpError {
|
||||||
#[error("Message header error")]
|
#[error("Message header error")]
|
||||||
@@ -28,7 +29,36 @@ pub enum BgpError {
|
|||||||
SendHoldTimer = 8,
|
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 {
|
pub enum MsgHeaderSubcode {
|
||||||
#[error("Connection not synchronized")]
|
#[error("Connection not synchronized")]
|
||||||
ConnNotSynchronized = 1,
|
ConnNotSynchronized = 1,
|
||||||
@@ -40,7 +70,29 @@ pub enum MsgHeaderSubcode {
|
|||||||
BadMessagType = 3,
|
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 {
|
pub enum OpenMsgSubcode {
|
||||||
#[error("Unsupported BGP version number")]
|
#[error("Unsupported BGP version number")]
|
||||||
UnsupportedVersion = 1,
|
UnsupportedVersion = 1,
|
||||||
@@ -64,7 +116,37 @@ pub enum OpenMsgSubcode {
|
|||||||
RoleMismatch = 8,
|
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 {
|
pub enum UpdateMsgSubcode {
|
||||||
#[error("Malformed attribute list")]
|
#[error("Malformed attribute list")]
|
||||||
MalforedAttrs = 1,
|
MalforedAttrs = 1,
|
||||||
@@ -97,7 +179,43 @@ pub enum UpdateMsgSubcode {
|
|||||||
MalformedAsPath = 11,
|
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 {
|
pub enum FsmSubcode {
|
||||||
#[error("Received an unexpected message in OpenSent state")]
|
#[error("Received an unexpected message in OpenSent state")]
|
||||||
UnexpectedOpenSent = 1,
|
UnexpectedOpenSent = 1,
|
||||||
@@ -109,7 +227,29 @@ pub enum FsmSubcode {
|
|||||||
UnexpectedEstablished = 3,
|
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 {
|
pub enum CeaseSubcode {
|
||||||
#[error("Maximum number of prefixes reached")]
|
#[error("Maximum number of prefixes reached")]
|
||||||
MaxPrefixes = 1,
|
MaxPrefixes = 1,
|
||||||
@@ -142,8 +282,62 @@ pub enum CeaseSubcode {
|
|||||||
BfdDown = 10,
|
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 {
|
pub enum RouteRefreshSubcode {
|
||||||
#[error("Invalid message length")]
|
#[error("Invalid message length")]
|
||||||
InvalidLength = 1,
|
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 errors;
|
||||||
pub mod ip_prefix;
|
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod open;
|
pub mod open;
|
||||||
pub mod parser;
|
|
||||||
+423
-25
@@ -18,12 +18,14 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use strum::EnumDiscriminants;
|
use strum::EnumDiscriminants;
|
||||||
|
|
||||||
use crate::constants::AddressFamilyId;
|
use bgp_primitives::constants::AddressFamilyId;
|
||||||
use crate::constants::SubsequentAfi;
|
use bgp_primitives::constants::SubsequentAfi;
|
||||||
use crate::ip_prefix::IpPrefix;
|
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::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 sentinel AS number used to denote that the actual AS number is provided as
|
||||||
/// a 4 byte value in the capabilities instead.
|
/// a 4 byte value in the capabilities instead.
|
||||||
@@ -47,7 +49,7 @@ pub enum Message {
|
|||||||
impl Message {}
|
impl Message {}
|
||||||
|
|
||||||
/// Represents a BGP Update message.
|
/// Represents a BGP Update message.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct UpdateMessage {
|
pub struct UpdateMessage {
|
||||||
pub withdrawn_routes: Vec<IpPrefix>,
|
pub withdrawn_routes: Vec<IpPrefix>,
|
||||||
pub path_attributes: Vec<PathAttribute>,
|
pub path_attributes: Vec<PathAttribute>,
|
||||||
@@ -55,22 +57,120 @@ pub struct UpdateMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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>(
|
pub fn from_wire<'a>(
|
||||||
_ctx: &ParserContext,
|
ctx: &ParserContext,
|
||||||
_buf: &'a [u8],
|
buf: &'a [u8],
|
||||||
) -> IResult<&'a [u8], Self, BgpParserError<&'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
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<()> {
|
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
|
||||||
todo!()
|
// 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> {
|
pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
|
||||||
todo!()
|
// 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! {
|
bitfield! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PathAttributeFlags(u8);
|
pub struct PathAttributeFlags(u8);
|
||||||
@@ -207,6 +307,20 @@ impl PathAttribute {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let discriminant = PathAttributeDiscriminants::from(type_code);
|
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)
|
Self::parse_known_path_attribute(ctx, buf, discriminant, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,18 +446,48 @@ impl PathAttribute {
|
|||||||
payload,
|
payload,
|
||||||
} => {
|
} => {
|
||||||
out.put_u8(flags.0);
|
out.put_u8(flags.0);
|
||||||
|
out.put_u8(*type_code);
|
||||||
if flags.extended_length() {
|
if flags.extended_length() {
|
||||||
out.put_u16(payload.len() as u16);
|
out.put_u16(payload.len() as u16);
|
||||||
} else {
|
} else {
|
||||||
out.put_u8(payload.len() as u8);
|
out.put_u8(payload.len() as u8);
|
||||||
}
|
}
|
||||||
out.put_u8(*type_code);
|
|
||||||
out.put(payload.as_slice());
|
out.put(payload.as_slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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.
|
/// Origin path attribute is a mandatory attribute defined in RFC4271.
|
||||||
@@ -535,7 +679,7 @@ impl NextHopPathAttribute {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wire_len(&self, _: &ParserContext) -> Result<u32> {
|
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
|
||||||
Ok(4)
|
Ok(4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1168,8 +1312,47 @@ impl MpUnreachNlriPathAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
/// Represents a BGP NOTIFICATION message per RFC 4271 §4.5.
|
||||||
pub struct NotificationMessage {}
|
#[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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -1181,16 +1364,18 @@ mod tests {
|
|||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use eyre::{Result, eyre};
|
use eyre::{Result, eyre};
|
||||||
|
|
||||||
use crate::{
|
use crate::message::{
|
||||||
|
AggregatorPathAttribute, AsPathAttribute, AsPathSegment, AtomicAggregatePathAttribute,
|
||||||
|
CommunitiesPathAttribute, ExtendedCommunitiesPathAttribute, ExtendedCommunity,
|
||||||
|
LargeCommunitiesPathAttribute, LargeCommunity, LocalPrefPathAttribute,
|
||||||
|
MpReachNlriPathAttribute, MpUnreachNlriPathAttribute, MultiExitDiscPathAttribute,
|
||||||
|
NextHopPathAttribute, NlriNextHop, NotificationMessage, OriginPathAttribute, PathAttribute,
|
||||||
|
PathAttributeFlags, UpdateMessage,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bgp_primitives::{
|
||||||
constants::{AddressFamilyId, SubsequentAfi},
|
constants::{AddressFamilyId, SubsequentAfi},
|
||||||
ip_prefix::IpPrefix,
|
ip_prefix::IpPrefix,
|
||||||
message::{
|
|
||||||
AggregatorPathAttribute, AsPathAttribute, AsPathSegment, AtomicAggregatePathAttribute,
|
|
||||||
CommunitiesPathAttribute, ExtendedCommunitiesPathAttribute, ExtendedCommunity,
|
|
||||||
LargeCommunitiesPathAttribute, LargeCommunity, LocalPrefPathAttribute,
|
|
||||||
MpReachNlriPathAttribute, MpUnreachNlriPathAttribute, MultiExitDiscPathAttribute,
|
|
||||||
NextHopPathAttribute, NlriNextHop, OriginPathAttribute, PathAttribute,
|
|
||||||
},
|
|
||||||
parser::ParserContext,
|
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 serde::{Deserialize, Serialize};
|
||||||
use strum::EnumDiscriminants;
|
use strum::EnumDiscriminants;
|
||||||
|
|
||||||
use crate::{
|
use bgp_primitives::{
|
||||||
constants::{AddressFamilyId, SubsequentAfi},
|
constants::{AddressFamilyId, SubsequentAfi},
|
||||||
parser::{BgpParserError, ParserContext, ProtocolErrorValues},
|
parser::{BgpParserError, ParserContext, ProtocolErrorValues},
|
||||||
};
|
};
|
||||||
@@ -814,9 +814,10 @@ mod tests {
|
|||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::open::{Capability, ExtendedNextHop, GracefulRestartEntry, OpenMessage, OpenOption};
|
||||||
|
|
||||||
|
use bgp_primitives::{
|
||||||
constants::{AddressFamilyId, SubsequentAfi},
|
constants::{AddressFamilyId, SubsequentAfi},
|
||||||
open::{Capability, ExtendedNextHop, GracefulRestartEntry, OpenMessage, OpenOption},
|
|
||||||
parser::ParserContext,
|
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