diff --git a/Cargo.toml b/Cargo.toml index f06c84c..663fc08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,70 @@ [workspace] members = [ - "bgpd", + "bin", + "crates/bgp_packet", + "crates/route_client", + "crates/server", # Tests "tests/integration_tests", ] resolver = "2" + +[workspace.package] +authors = ["Rayhaan Jaufeerally "] +edition = "2021" +homepage = "https://github.com/net-control-plane/bgp" +license = "Apache 2" +repository = "https://github.com/net-control-plane/bgp" +rust-version = "1.76" +version = "0.1.1" + +# [[bin]] +# name = "bgp_server" +# path = "src/main.rs" + +# [[bin]] +# name = "route_client" +# path = "src/route_client/main.rs" + +# [[bin]] +# name = "streamer_cli" +# path = "src/streamer_cli/main.rs" + +[workspace.lints] +rust.unused_must_use = "deny" + +[workspace.dependencies] +anyhow = "1.0.71" +async-trait = "0.1.80" +byteorder = "1.4.3" +bytes = "1.*" +clap = { version = "3.2.8", features = ["cargo", "derive"] } +eyre = "0.6.12" +futures = "0.3" +ip_network_table-deps-treebitmap = "0.5.0" +ipnet = "2.3.0" +libc = "0.2.126" +log = "0.4" +netlink = "0.1.1" +netlink-packet-route = "0.19.0" +netlink-packet-utils = "0.5.2" +nom = "7.1" +prost = "0.8" +rtnetlink = "0.14.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.64" +stderrlog = "0.5.1" +tokio = { version = "1.13.0", features = ["full"] } +tokio-stream = { version = "0.1.7", features = ["net"] } +tokio-util = { version = "0.6.7", features = ["codec"] } +tonic = { version = "0.5", features = ["compression"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +warp = "0.3.5" + +# -- Local crates -- +bgp_packet = { path = "crates/bgp_packet" } +bgp_server = { path = "crates/server" } +bin = { path = "bin" } +route_client = { path = "crates/route_client" } diff --git a/bgpd/Cargo.toml b/bgpd/Cargo.toml deleted file mode 100644 index bdc19e7..0000000 --- a/bgpd/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -authors = ["Rayhaan Jaufeerally "] -edition = "2021" -name = "bgpd" -version = "0.1.0" - -[[bin]] -name = "bgp_server" -path = "src/main.rs" - -[[bin]] -name = "route_client" -path = "src/route_client/main.rs" - -[[bin]] -name = "streamer_cli" -path = "src/streamer_cli/main.rs" - -[dependencies] -anyhow = "1.0.71" -async-trait = "0.1.57" -byteorder = "1.4.3" -bytes = "1.*" -clap = { version = "3.2.8", features = ["cargo", "derive"] } -futures = "0.3" -ip_network_table-deps-treebitmap = "0.5.0" -ipnet = "2.3.0" -libc = "0.2.126" -log = "0.4" -netlink = "0.1.1" -netlink-packet-route = "0.19.0" -netlink-packet-utils = "0.5.2" -nom = "7.1" -prost = "0.8" -rtnetlink = "0.14.1" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.64" -signal-hook = { version = "0.3.17", features = ["extended-siginfo"] } -signal-hook-tokio = "0.3.0" -stderrlog = "0.5.1" -tokio = { version = "1.13.0", features = ["full"] } -tokio-stream = { version = "0.1.7", features = ["net"] } -tokio-util = { version = "0.6.7", features = ["codec"] } -tonic = { version = "0.5", features = ["compression"] } -tracing = "0.1" -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } -warp = "0.3.5" - -[build-dependencies] -tonic-build = { version = "0.5.1", features = ["compression", "prost"] } diff --git a/bgpd/rustfmt.toml b/bgpd/rustfmt.toml deleted file mode 100644 index c51666e..0000000 --- a/bgpd/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -edition = "2018" \ No newline at end of file diff --git a/bgpd/src/lib.rs b/bgpd/src/lib.rs deleted file mode 100644 index a393cbc..0000000 --- a/bgpd/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod bgp_packet; -pub mod route_client; -pub mod server; diff --git a/bin/Cargo.toml b/bin/Cargo.toml new file mode 100644 index 0000000..6ebe8ba --- /dev/null +++ b/bin/Cargo.toml @@ -0,0 +1,30 @@ +[package] +edition.workspace = true +homepage.workspace = true +license.workspace = true +name = "bgpd_bin" +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +bgp_server.workspace = true +clap.workspace = true +eyre.workspace = true +libc.workspace = true +log.workspace = true +route_client.workspace = true +serde_json.workspace = true +signal-hook = { version = "0.3.17", features = ["extended-siginfo"] } +signal-hook-tokio = "0.3.0" +tokio.workspace = true +tracing-subscriber.workspace = true +tracing.workspace = true + +[[bin]] +name = "bgp_server" +path = "src/bgp_server/main.rs" + +[[bin]] +name = "client" +path = "src/client/main.rs" diff --git a/bgpd/src/main.rs b/bin/src/bgp_server/main.rs similarity index 97% rename from bgpd/src/main.rs rename to bin/src/bgp_server/main.rs index 67a5ad5..754509b 100644 --- a/bgpd/src/main.rs +++ b/bin/src/bgp_server/main.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bgpd::server::bgp_server::Server; -use bgpd::server::config::ServerConfig; +use bgp_server::bgp_server::Server; +use bgp_server::config::ServerConfig; use clap::{App, Arg}; use core::sync::atomic::AtomicBool; use libc::SIGUSR1; diff --git a/bin/src/client/main.rs b/bin/src/client/main.rs new file mode 100644 index 0000000..d660dd3 --- /dev/null +++ b/bin/src/client/main.rs @@ -0,0 +1,77 @@ +use clap::Parser; +use eyre::Result; +use tracing::instrument::WithSubscriber; +use tracing::log::LevelFilter; +use tracing::{info, warn}; +use tracing_subscriber::filter; +use tracing_subscriber::prelude::*; + +use route_client::netlink::NetlinkConnector; +use route_client::{run_connector_v4, run_connector_v6}; + +#[derive(Parser)] +#[clap( + author = "Rayhaan Jaufeerally ", + version = "0.1", + about = "Installs routes from a BGP speaker via streaming RPC to the forwarding plane" +)] +struct Cli { + #[clap(long = "route_server")] + route_server: String, + #[clap(long = "rt_table")] + rt_table: Option, + dry_run: bool, +} + +#[tokio::main] +async fn main() -> Result<()> { + let args = Cli::parse(); + + tracing_subscriber::fmt().pretty().init(); + + info!("Starting route client"); + + let rt_table = match args.rt_table { + Some(table) => table, + None => 201, + }; + + let v4_joinhandle = { + let server_addr = args.route_server.clone(); + tokio::task::spawn(async move { + run_connector_v4::( + server_addr.clone(), + rt_table, + args.dry_run, + NetlinkConnector::new(Some(rt_table)).await.unwrap(), + ) + .await + .unwrap(); + }) + }; + + let v6_joinhandle = { + let server_addr = args.route_server.clone(); + tokio::task::spawn(async move { + run_connector_v6::( + server_addr, + rt_table, + args.dry_run, + NetlinkConnector::new(Some(rt_table)).await.unwrap(), + ) + .await + .unwrap(); + }) + }; + + tokio::select! { + _ = v4_joinhandle => { + warn!("Unexpected exit of IPv4 connector"); + }, + _ = v6_joinhandle => { + warn!("Unexpected exit of IPv6 connector"); + } + } + + Ok(()) +} diff --git a/bgpd/src/streamer_cli/main.rs b/bin/src/streamer_cli/main.rs similarity index 98% rename from bgpd/src/streamer_cli/main.rs rename to bin/src/streamer_cli/main.rs index e8a3475..7209c69 100644 --- a/bgpd/src/streamer_cli/main.rs +++ b/bin/src/streamer_cli/main.rs @@ -31,7 +31,7 @@ extern crate clap; #[clap( author = "Rayhaan Jaufeerally ", version = "0.1", - about = "A program to install routes from BGP into the Linux control plane" + about = "A utility for dumping routes from the bgp_server." )] struct Cli { server_address: String, diff --git a/crates/bgp_packet/Cargo.toml b/crates/bgp_packet/Cargo.toml new file mode 100644 index 0000000..5761180 --- /dev/null +++ b/crates/bgp_packet/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bgp_packet" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lints] +workspace = true + +[dependencies] +byteorder = "1.4.3" +bytes.workspace = true +nom = "7.1" +serde.workspace = true +tokio-util = { version = "0.7.10", features = ["codec"] } diff --git a/bgpd/src/bgp_packet/capabilities.rs b/crates/bgp_packet/src/capabilities.rs similarity index 98% rename from bgpd/src/bgp_packet/capabilities.rs rename to crates/bgp_packet/src/capabilities.rs index 05ab4c1..11e5249 100644 --- a/bgpd/src/bgp_packet/capabilities.rs +++ b/crates/bgp_packet/src/capabilities.rs @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier; -use crate::bgp_packet::traits::BGPParserError; -use crate::bgp_packet::traits::ParserContext; -use crate::bgp_packet::traits::ReadablePacket; -use crate::bgp_packet::traits::WritablePacket; +use crate::constants::AddressFamilyIdentifier; +use crate::constants::SubsequentAddressFamilyIdentifier; +use crate::traits::BGPParserError; +use crate::traits::ParserContext; +use crate::traits::ReadablePacket; +use crate::traits::WritablePacket; use byteorder::{ByteOrder, NetworkEndian}; use nom::number::complete::{be_u16, be_u8}; use nom::Err::Failure; @@ -694,9 +694,9 @@ mod tests { use super::ExtendedNextHopEncodingCapability; use super::FourByteASNCapability; use super::OpenOption; - use crate::bgp_packet::constants::AddressFamilyIdentifier::Ipv6; - use crate::bgp_packet::traits::ParserContext; - use crate::bgp_packet::traits::ReadablePacket; + use crate::constants::AddressFamilyIdentifier::Ipv6; + use crate::traits::ParserContext; + use crate::traits::ReadablePacket; #[test] fn test_four_byte_asn_capability() { diff --git a/bgpd/src/bgp_packet/constants.rs b/crates/bgp_packet/src/constants.rs similarity index 100% rename from bgpd/src/bgp_packet/constants.rs rename to crates/bgp_packet/src/constants.rs diff --git a/bgpd/src/bgp_packet/mod.rs b/crates/bgp_packet/src/lib.rs similarity index 100% rename from bgpd/src/bgp_packet/mod.rs rename to crates/bgp_packet/src/lib.rs diff --git a/bgpd/src/bgp_packet/messages.rs b/crates/bgp_packet/src/messages.rs similarity index 97% rename from bgpd/src/bgp_packet/messages.rs rename to crates/bgp_packet/src/messages.rs index 0dc5910..323403a 100644 --- a/bgpd/src/bgp_packet/messages.rs +++ b/crates/bgp_packet/src/messages.rs @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::capabilities::OpenOption; -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier; -use crate::bgp_packet::nlri::NLRI; -use crate::bgp_packet::path_attributes::PathAttribute; -use crate::bgp_packet::traits::BGPParserError; -use crate::bgp_packet::traits::ParserContext; -use crate::bgp_packet::traits::ReadablePacket; -use crate::bgp_packet::traits::WritablePacket; +use crate::capabilities::OpenOption; +use crate::constants::AddressFamilyIdentifier; +use crate::constants::SubsequentAddressFamilyIdentifier; +use crate::nlri::NLRI; +use crate::path_attributes::PathAttribute; +use crate::traits::BGPParserError; +use crate::traits::ParserContext; +use crate::traits::ReadablePacket; +use crate::traits::WritablePacket; use byteorder::{ByteOrder, NetworkEndian}; use bytes::Buf; use bytes::BufMut; @@ -545,11 +545,11 @@ impl Display for UpdateMessage { mod tests { use super::BGPMessage; use super::Codec; - use crate::bgp_packet::constants::AddressFamilyIdentifier::Ipv6; - use crate::bgp_packet::messages::AddressFamilyIdentifier::Ipv4; - use crate::bgp_packet::traits::ParserContext; - use crate::bgp_packet::traits::ReadablePacket; - use crate::bgp_packet::traits::WritablePacket; + use crate::constants::AddressFamilyIdentifier::Ipv6; + use crate::messages::AddressFamilyIdentifier::Ipv4; + use crate::traits::ParserContext; + use crate::traits::ReadablePacket; + use crate::traits::WritablePacket; use bytes::BufMut; use tokio_util::codec::{Decoder, Encoder}; diff --git a/bgpd/src/bgp_packet/nlri.rs b/crates/bgp_packet/src/nlri.rs similarity index 96% rename from bgpd/src/bgp_packet/nlri.rs rename to crates/bgp_packet/src/nlri.rs index 065ccbf..6e9b150 100644 --- a/bgpd/src/bgp_packet/nlri.rs +++ b/crates/bgp_packet/src/nlri.rs @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::bgp_packet::traits::BGPParserError; -use crate::bgp_packet::traits::ParserContext; -use crate::bgp_packet::traits::ReadablePacket; -use crate::bgp_packet::traits::WritablePacket; +use crate::constants::AddressFamilyIdentifier; +use crate::traits::BGPParserError; +use crate::traits::ParserContext; +use crate::traits::ReadablePacket; +use crate::traits::WritablePacket; use nom::bytes::complete::take; use nom::number::complete::be_u8; use nom::Err::Failure; @@ -254,10 +254,10 @@ mod tests { use std::convert::TryFrom; use super::NLRI; - use crate::bgp_packet::constants::AddressFamilyIdentifier::{Ipv4, Ipv6}; - use crate::bgp_packet::traits::ParserContext; - use crate::bgp_packet::traits::ReadablePacket; - use crate::bgp_packet::traits::WritablePacket; + use crate::constants::AddressFamilyIdentifier::{Ipv4, Ipv6}; + use crate::traits::ParserContext; + use crate::traits::ReadablePacket; + use crate::traits::WritablePacket; #[test] fn test_basic_nlri_v6() { diff --git a/bgpd/src/bgp_packet/path_attributes.rs b/crates/bgp_packet/src/path_attributes.rs similarity index 98% rename from bgpd/src/bgp_packet/path_attributes.rs rename to crates/bgp_packet/src/path_attributes.rs index 2870295..0036b90 100644 --- a/bgpd/src/bgp_packet/path_attributes.rs +++ b/crates/bgp_packet/src/path_attributes.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier; -use crate::bgp_packet::nlri::NLRI; -use crate::bgp_packet::traits::BGPParserError; -use crate::bgp_packet::traits::ParserContext; -use crate::bgp_packet::traits::ReadablePacket; -use crate::bgp_packet::traits::WritablePacket; +use crate::constants::AddressFamilyIdentifier; +use crate::constants::SubsequentAddressFamilyIdentifier; +use crate::nlri::NLRI; +use crate::traits::BGPParserError; +use crate::traits::ParserContext; +use crate::traits::ReadablePacket; +use crate::traits::WritablePacket; use byteorder::ByteOrder; use byteorder::NetworkEndian; use nom::number::complete::{be_u16, be_u32, be_u8}; @@ -976,11 +976,11 @@ impl fmt::Display for MPUnreachNLRIPathAttribute { mod tests { use std::net::Ipv4Addr; - use crate::bgp_packet::constants::AddressFamilyIdentifier::Ipv6; - use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier::Unicast; - use crate::bgp_packet::traits::ParserContext; - use crate::bgp_packet::traits::ReadablePacket; - use crate::bgp_packet::traits::WritablePacket; + use crate::constants::AddressFamilyIdentifier::Ipv6; + use crate::constants::SubsequentAddressFamilyIdentifier::Unicast; + use crate::traits::ParserContext; + use crate::traits::ReadablePacket; + use crate::traits::WritablePacket; use super::ASPathAttribute; use super::CommunitiesPathAttribute; diff --git a/bgpd/src/bgp_packet/traits.rs b/crates/bgp_packet/src/traits.rs similarity index 97% rename from bgpd/src/bgp_packet/traits.rs rename to crates/bgp_packet/src/traits.rs index f1a31ff..7a5dec2 100644 --- a/bgpd/src/bgp_packet/traits.rs +++ b/crates/bgp_packet/src/traits.rs @@ -14,7 +14,7 @@ //! Implements high level abstractions for use in the BGP parser. -use crate::bgp_packet::constants::AddressFamilyIdentifier; +use crate::constants::AddressFamilyIdentifier; use nom::error::ErrorKind; use nom::error::ParseError; use nom::IResult; diff --git a/crates/route_client/Cargo.toml b/crates/route_client/Cargo.toml new file mode 100644 index 0000000..80d315d --- /dev/null +++ b/crates/route_client/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "route_client" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lints] +workspace = true + +[dependencies] +async-trait.workspace = true +bgp_packet.workspace = true +byteorder = "1.4.3" +bytes.workspace = true +eyre.workspace = true +futures.workspace = true +ip_network_table-deps-treebitmap.workspace = true +log.workspace = true +netlink-packet-route.workspace = true +netlink-packet-utils.workspace = true +nom = "7.1" +prost.workspace = true +rtnetlink.workspace = true +serde.workspace = true +tokio-stream = "0.1.14" +tokio-util = { version = "0.7.10", features = ["codec"] } +tokio.workspace = true +tonic.workspace = true +tracing.workspace = true +warp.workspace = true + +[build-dependencies] +tonic-build = { version = "0.5.1", features = ["compression", "prost"] } diff --git a/bgpd/build.rs b/crates/route_client/build.rs similarity index 100% rename from bgpd/build.rs rename to crates/route_client/build.rs diff --git a/bgpd/proto/route_service.proto b/crates/route_client/proto/route_service.proto similarity index 100% rename from bgpd/proto/route_service.proto rename to crates/route_client/proto/route_service.proto diff --git a/bgpd/src/route_client/fib_state.rs b/crates/route_client/src/fib_state.rs similarity index 97% rename from bgpd/src/route_client/fib_state.rs rename to crates/route_client/src/fib_state.rs index 7d41f9b..a29d011 100644 --- a/bgpd/src/route_client/fib_state.rs +++ b/crates/route_client/src/fib_state.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::bgp_packet::nlri::NLRI; -use crate::route_client::southbound_interface::SouthboundInterface; use futures::lock::Mutex; use ip_network_table_deps_treebitmap::address::Address; use ip_network_table_deps_treebitmap::IpLookupTable; @@ -25,6 +22,11 @@ use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; use tracing::{trace, warn}; +use bgp_packet::constants::AddressFamilyIdentifier; +use bgp_packet::nlri::NLRI; + +use crate::southbound_interface::SouthboundInterface; + /// fib_state implements the logic to maintain forwarding routes in the FIB. /// This for now means the Linux Kernel via Netlink, but in the future can /// be extended to include other targets such as OpenFlow or even program diff --git a/bgpd/src/route_client/main.rs b/crates/route_client/src/lib.rs similarity index 73% rename from bgpd/src/route_client/main.rs rename to crates/route_client/src/lib.rs index 808458f..0dd95b4 100644 --- a/bgpd/src/route_client/main.rs +++ b/crates/route_client/src/lib.rs @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bgpd::route_client::netlink::NetlinkConnector; -use bgpd::route_client::southbound_interface::SouthboundInterface; -use clap::Parser; +pub mod fib_state; +pub mod netlink; +pub mod southbound_interface; + use log::trace; use std::convert::TryInto; use std::net::IpAddr; @@ -23,27 +24,28 @@ use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::str::FromStr; use std::time::Duration; -use tonic::transport::Uri; -use bgpd::bgp_packet::constants::AddressFamilyIdentifier; -use bgpd::bgp_packet::nlri::NLRI; -use bgpd::route_client::fib_state::FibState; +use bgp_packet::constants::AddressFamilyIdentifier; +use bgp_packet::nlri::NLRI; +use eyre::{anyhow, Result}; use ip_network_table_deps_treebitmap::IpLookupTable; use tonic::transport::Endpoint; +use tonic::transport::Uri; use tracing::{info, warn}; -use anyhow::{anyhow, Result}; - +use crate::fib_state::FibState; +use crate::netlink::NetlinkConnector; use crate::proto::route_service_client::RouteServiceClient; +use crate::southbound_interface::SouthboundInterface; pub mod proto { tonic::include_proto!("bgpd.grpc"); } -fn vec_to_array(v: Vec) -> Result<[T; N], anyhow::Error> { +fn vec_to_array(v: Vec) -> Result<[T; N]> { v.try_into() - .map_err(|_| anyhow::Error::msg("Wrong size of Vec".to_string())) + .map_err(|_| eyre::Error::msg("Wrong size of Vec".to_string())) } /// Temporary hack to select the route to install to the FIB. @@ -62,12 +64,12 @@ fn select_best_route(ps: &proto::PathSet) -> Option { selected } -async fn run_connector_v4( +pub async fn run_connector_v4( route_server: String, rt_table: u32, dry_run: bool, southbound: S, -) -> Result<(), anyhow::Error> { +) -> Result<()> { // Create netlink socket. let mut fib_state = FibState:: { fib: IpLookupTable::new(), @@ -132,7 +134,7 @@ async fn run_connector_v4( unreachable!() } -async fn run_connector_v6( +pub async fn run_connector_v6( route_server: String, rt_table: u32, dry_run: bool, @@ -201,72 +203,3 @@ async fn run_connector_v6( unreachable!() } - -#[derive(Parser)] -#[clap( - author = "Rayhaan Jaufeerally ", - version = "0.1", - about = "Installs routes from a BGP speaker via streaming RPC to the forwarding plane" -)] -struct Cli { - #[clap(long = "route_server")] - route_server: String, - #[clap(long = "rt_table")] - rt_table: Option, - dry_run: bool, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Cli::parse(); - - let _init_log = stderrlog::new() - .verbosity(2) // Shows info level. - .show_module_names(true) - .init(); - info!("Starting route client"); - - let rt_table = match args.rt_table { - Some(table) => table, - None => 201, - }; - - let v4_joinhandle = { - let server_addr = args.route_server.clone(); - tokio::task::spawn(async move { - run_connector_v4::( - server_addr.clone(), - rt_table, - args.dry_run, - NetlinkConnector::new(Some(rt_table)).await.unwrap(), - ) - .await - .unwrap(); - }) - }; - - let v6_joinhandle = { - let server_addr = args.route_server.clone(); - tokio::task::spawn(async move { - run_connector_v6::( - server_addr, - rt_table, - args.dry_run, - NetlinkConnector::new(Some(rt_table)).await.unwrap(), - ) - .await - .unwrap(); - }) - }; - - tokio::select! { - _ = v4_joinhandle => { - warn!("Unexpected exit of IPv4 connector"); - }, - _ = v6_joinhandle => { - warn!("Unexpected exit of IPv6 connector"); - } - } - - Ok(()) -} diff --git a/bgpd/src/route_client/netlink.rs b/crates/route_client/src/netlink.rs similarity index 89% rename from bgpd/src/route_client/netlink.rs rename to crates/route_client/src/netlink.rs index 500e9f3..b2b08f0 100644 --- a/bgpd/src/route_client/netlink.rs +++ b/crates/route_client/src/netlink.rs @@ -1,6 +1,6 @@ -use crate::bgp_packet::{constants::AddressFamilyIdentifier, nlri::NLRI}; -use anyhow::{anyhow, Result}; use async_trait::async_trait; +use bgp_packet::{constants::AddressFamilyIdentifier, nlri::NLRI}; +use eyre::{eyre, Result}; use futures::TryStreamExt; use netlink_packet_route::route::RouteAddress; use netlink_packet_route::route::RouteAttribute; @@ -37,7 +37,7 @@ impl SouthboundInterface for NetlinkConnector { let addr: Ipv6Addr = match prefix.try_into()? { IpAddr::V6(addr) => addr, _ => { - return Err(anyhow::Error::from(std::io::Error::new( + return Err(eyre::Error::from(std::io::Error::new( ErrorKind::InvalidInput, "Got non-IPv6 address from NLRI", ))) @@ -46,7 +46,7 @@ impl SouthboundInterface for NetlinkConnector { let gw_addr: Ipv6Addr = match nexthop.clone().try_into()? { IpAddr::V6(addr) => addr, _ => { - return Err(anyhow::Error::from(std::io::Error::new( + return Err(eyre::Error::from(std::io::Error::new( ErrorKind::InvalidInput, "Got non-IPv6 gateway for IPv6 NLRI", ))) @@ -60,14 +60,14 @@ impl SouthboundInterface for NetlinkConnector { if let Some(table_id) = self.table { mutation = mutation.table(table_id.try_into().unwrap()); } - mutation.execute().await.map_err(|e| anyhow::Error::from(e)) + mutation.execute().await.map_err(|e| eyre::Error::from(e)) } AddressFamilyIdentifier::Ipv4 => { let prefix_len = prefix.prefixlen; let addr: Ipv4Addr = match prefix.clone().try_into()? { IpAddr::V4(addr) => addr, _ => { - return Err(anyhow::Error::from(std::io::Error::new( + return Err(eyre::Error::from(std::io::Error::new( ErrorKind::InvalidInput, "Got non-IPv4 address from NLRI", ))) @@ -76,7 +76,7 @@ impl SouthboundInterface for NetlinkConnector { let gw_addr = match nexthop.clone().try_into()? { IpAddr::V4(addr) => addr, _ => { - return Err(anyhow::Error::from(std::io::Error::new( + return Err(eyre::Error::from(std::io::Error::new( ErrorKind::InvalidInput, "Got non-IPv4 gateway for IPv4 NLRI", ))) @@ -90,7 +90,7 @@ impl SouthboundInterface for NetlinkConnector { if let Some(table_id) = self.table { mutation = mutation.table(table_id.try_into().unwrap()); } - mutation.execute().await.map_err(|e| anyhow::Error::from(e)) + mutation.execute().await.map_err(|e| eyre::Error::from(e)) } } } @@ -99,10 +99,10 @@ impl SouthboundInterface for NetlinkConnector { let rt_handle = self.handle.route(); let destination = match prefix.afi { AddressFamilyIdentifier::Ipv4 => { - RouteAddress::Inet(prefix.clone().try_into().map_err(|e: String| anyhow!(e))?) + RouteAddress::Inet(prefix.clone().try_into().map_err(|e: String| eyre!(e))?) } AddressFamilyIdentifier::Ipv6 => { - RouteAddress::Inet6(prefix.clone().try_into().map_err(|e: String| anyhow!(e))?) + RouteAddress::Inet6(prefix.clone().try_into().map_err(|e: String| eyre!(e))?) } }; let nexthop = match nexthop { @@ -130,7 +130,7 @@ impl SouthboundInterface for NetlinkConnector { .del(rt_msg) .execute() .await - .map_err(|e| anyhow::Error::from(e)) + .map_err(|e| eyre::Error::from(e)) } } diff --git a/bgpd/src/route_client/southbound_interface.rs b/crates/route_client/src/southbound_interface.rs similarity index 93% rename from bgpd/src/route_client/southbound_interface.rs rename to crates/route_client/src/southbound_interface.rs index c362914..157a54b 100644 --- a/bgpd/src/route_client/southbound_interface.rs +++ b/crates/route_client/src/southbound_interface.rs @@ -14,11 +14,12 @@ use std::{collections::HashMap, net::IpAddr}; -use crate::bgp_packet::{constants::AddressFamilyIdentifier, nlri::NLRI}; -use anyhow::{anyhow, Result}; use async_trait::async_trait; +use eyre::{eyre, Result}; use log::info; +use bgp_packet::{constants::AddressFamilyIdentifier, nlri::NLRI}; + /// SouthboundInterface provides a uniform API to network forwarding elements /// These are devices or targets that perform packet routing and are the end /// consumers of packet routing data. @@ -60,7 +61,7 @@ impl SouthboundInterface for DummyVerifier { // Check that the route is not already present. match self.route_state.get(&prefix) { Some(value) => { - return Err(anyhow!( + return Err(eyre!( "Prefix {} with nexthop {} already contained in route_state! when trying to add {} -> {}", prefix, value, prefix, nexthop, )); @@ -80,7 +81,7 @@ impl SouthboundInterface for DummyVerifier { match self.route_state.remove(&prefix) { Some(entry) => { if entry != nexthop { - return Err(anyhow!( + return Err(eyre!( "Removed entry's nexthop did not match: {} vs requested {}", entry, nexthop @@ -88,7 +89,7 @@ impl SouthboundInterface for DummyVerifier { } } None => { - return Err(anyhow!( + return Err(eyre!( "Requested removal of route {} that was not in route_state", prefix )); diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml new file mode 100644 index 0000000..23f95ac --- /dev/null +++ b/crates/server/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "bgp_server" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lints] +workspace = true + +[dependencies] +bgp_packet.workspace = true +byteorder = "1.4.3" +bytes.workspace = true +ip_network_table-deps-treebitmap.workspace = true +log.workspace = true +nom = "7.1" +prost.workspace = true +serde.workspace = true +tokio-stream = "0.1.14" +tokio-util = { version = "0.7.10", features = ["codec"] } +tokio.workspace = true +tonic.workspace = true +tracing.workspace = true +warp.workspace = true + +[build-dependencies] +tonic-build = { version = "0.5.1", features = ["compression", "prost"] } diff --git a/bgpd/src/route_client/mod.rs b/crates/server/build.rs similarity index 82% rename from bgpd/src/route_client/mod.rs rename to crates/server/build.rs index 1b6de0b..9945377 100644 --- a/bgpd/src/route_client/mod.rs +++ b/crates/server/build.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod fib_state; -pub mod netlink; -pub mod southbound_interface; +fn main() { + tonic_build::configure() + .compile(&["proto/route_service.proto"], &["proto"]) + .unwrap(); +} diff --git a/crates/server/proto/route_service.proto b/crates/server/proto/route_service.proto new file mode 100644 index 0000000..5f4606c --- /dev/null +++ b/crates/server/proto/route_service.proto @@ -0,0 +1,83 @@ +// Copyright 2021 Rayhaan Jaufeerally. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package bgpd.grpc; + +enum AddressFamily { + UNKNOWN = 0; + IPv4 = 1; + IPv6 = 2; +} + +message Prefix { + bytes ip_prefix = 1; + int32 prefix_len = 2; + AddressFamily address_family = 3; +} + +// Path represents the metadata associated with the route to a particular +// prefix. +message Path { + bytes nexthop = 1; + string peer_name = 2; + uint32 local_pref = 3; + uint32 med = 4; + repeated uint32 as_path = 5; + // TODO: Path attributes. Not yet supported because we need to generate proto + // definitions for all of them. +} + +message PathSet { + uint64 epoch = 1; + Prefix prefix = 2; + repeated Path paths = 3; +} + +message StreamPathsRequest { AddressFamily address_family = 1; } + +message DumpPathsRequest { AddressFamily address_family = 1; }; + +message DumpPathsResponse { + uint64 epoch = 1; + repeated PathSet path_sets = 2; +}; + +service RouteService { + // DumpPaths returns all the paths currently in the RIB. + rpc DumpPaths(DumpPathsRequest) returns (DumpPathsResponse); + // StreamPaths dumps the existing routes and starts streaming updates to the + // RIB. + rpc StreamPaths(StreamPathsRequest) returns (stream PathSet); +} + +message PeerStatusRequest {} + +message PeerStatus { + string peer_name = 1; + string state = 2; + uint64 session_established_time = 3; + uint64 last_messaage_time = 4; + uint64 route_updates_in = 5; + uint64 route_updates_out = 6; +} + +message PeerStatusResponse { repeated PeerStatus peer_status = 1; } + +// BGPServerAdminService implements an administrative interface to +// view the status and control the operation of this BGP server. +service BGPServerAdminService { + rpc PeerStatus(PeerStatusRequest) returns (PeerStatusResponse); +} \ No newline at end of file diff --git a/bgpd/src/server/bgp_server.rs b/crates/server/src/bgp_server.rs similarity index 97% rename from bgpd/src/server/bgp_server.rs rename to crates/server/src/bgp_server.rs index 3a5bd98..84e95ee 100644 --- a/bgpd/src/server/bgp_server.rs +++ b/crates/server/src/bgp_server.rs @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::server::config::PeerConfig; -use crate::server::config::ServerConfig; -use crate::server::peer::PeerCommands; -use crate::server::peer::PeerStateMachine; -use crate::server::rib_manager::RibManager; -use crate::server::rib_manager::RibSnapshot; -use crate::server::rib_manager::RouteManagerCommands; -use crate::server::route_server; -use crate::server::route_server::route_server::route_service_server::RouteServiceServer; +use crate::config::PeerConfig; +use crate::config::ServerConfig; +use crate::peer::PeerCommands; +use crate::peer::PeerStateMachine; +use crate::rib_manager::RibManager; +use crate::rib_manager::RibSnapshot; +use crate::rib_manager::RouteManagerCommands; +use crate::route_server; +use crate::route_server::route_server::route_service_server::RouteServiceServer; +use bgp_packet::constants::AddressFamilyIdentifier; use std::collections::HashMap; use std::net::Ipv4Addr; use std::net::Ipv6Addr; diff --git a/bgpd/src/server/config.rs b/crates/server/src/config.rs similarity index 95% rename from bgpd/src/server/config.rs rename to crates/server/src/config.rs index 648d548..f51ca00 100644 --- a/bgpd/src/server/config.rs +++ b/crates/server/src/config.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier}; +use bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier}; use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; diff --git a/bgpd/src/server/data_structures.rs b/crates/server/src/data_structures.rs similarity index 95% rename from bgpd/src/server/data_structures.rs rename to crates/server/src/data_structures.rs index dff7d0b..a684c8a 100644 --- a/bgpd/src/server/data_structures.rs +++ b/crates/server/src/data_structures.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::nlri::NLRI; -use crate::bgp_packet::path_attributes::PathAttribute; +use bgp_packet::nlri::NLRI; +use bgp_packet::path_attributes::PathAttribute; use std::time::SystemTime; /// RouteInfo encapsulates information received about a particular BGP route. diff --git a/bgpd/src/server/mod.rs b/crates/server/src/lib.rs similarity index 100% rename from bgpd/src/server/mod.rs rename to crates/server/src/lib.rs diff --git a/bgpd/src/server/peer.rs b/crates/server/src/peer.rs similarity index 97% rename from bgpd/src/server/peer.rs rename to crates/server/src/peer.rs index e2dabb7..e8eeac8 100644 --- a/bgpd/src/server/peer.rs +++ b/crates/server/src/peer.rs @@ -12,39 +12,37 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::capabilities::{ +use crate::config::PrefixAnnouncement; +use crate::config::{PeerConfig, ServerConfig}; +use crate::data_structures::RouteAnnounce; +use crate::data_structures::RouteWithdraw; +use crate::data_structures::{RouteInfo, RouteUpdate}; +use crate::rib_manager::RouteManagerCommands; +use bgp_packet::capabilities::{ BGPCapability, BGPCapabilityTypeValues, BGPCapabilityValue, BGPOpenOptionTypeValues, FourByteASNCapability, MultiprotocolCapability, OpenOption, OpenOptionCapabilities, OpenOptions, }; -use crate::bgp_packet::constants::{ - AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier, AS_TRANS, -}; -use crate::bgp_packet::messages::BGPMessage; -use crate::bgp_packet::messages::BGPMessageTypeValues; -use crate::bgp_packet::messages::BGPMessageTypeValues::OPEN_MESSAGE; -use crate::bgp_packet::messages::BGPMessageTypeValues::UPDATE_MESSAGE; -use crate::bgp_packet::messages::BGPSubmessage; -use crate::bgp_packet::messages::Codec; -use crate::bgp_packet::messages::KeepaliveMessage; -use crate::bgp_packet::messages::NotificationMessage; -use crate::bgp_packet::messages::OpenMessage; -use crate::bgp_packet::messages::UpdateMessage; -use crate::bgp_packet::nlri::NLRI; -use crate::bgp_packet::path_attributes::ASPathAttribute; -use crate::bgp_packet::path_attributes::NextHopPathAttribute; -use crate::bgp_packet::path_attributes::OriginPathAttribute; -use crate::bgp_packet::path_attributes::PathAttribute; -use crate::bgp_packet::path_attributes::{ +use bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier, AS_TRANS}; +use bgp_packet::messages::BGPMessage; +use bgp_packet::messages::BGPMessageTypeValues; +use bgp_packet::messages::BGPMessageTypeValues::OPEN_MESSAGE; +use bgp_packet::messages::BGPMessageTypeValues::UPDATE_MESSAGE; +use bgp_packet::messages::BGPSubmessage; +use bgp_packet::messages::Codec; +use bgp_packet::messages::KeepaliveMessage; +use bgp_packet::messages::NotificationMessage; +use bgp_packet::messages::OpenMessage; +use bgp_packet::messages::UpdateMessage; +use bgp_packet::nlri::NLRI; +use bgp_packet::path_attributes::ASPathAttribute; +use bgp_packet::path_attributes::NextHopPathAttribute; +use bgp_packet::path_attributes::OriginPathAttribute; +use bgp_packet::path_attributes::PathAttribute; +use bgp_packet::path_attributes::{ LargeCommunitiesPathAttribute, LargeCommunitiesPayload, MPReachNLRIPathAttribute, }; -use crate::bgp_packet::traits::ParserContext; -use crate::server::config::PrefixAnnouncement; -use crate::server::config::{PeerConfig, ServerConfig}; -use crate::server::data_structures::RouteAnnounce; -use crate::server::data_structures::RouteWithdraw; -use crate::server::data_structures::{RouteInfo, RouteUpdate}; -use crate::server::rib_manager::RouteManagerCommands; +use bgp_packet::traits::ParserContext; use bytes::BytesMut; use ip_network_table_deps_treebitmap::address::Address; use ip_network_table_deps_treebitmap::IpLookupTable; diff --git a/bgpd/src/server/rib_manager.rs b/crates/server/src/rib_manager.rs similarity index 95% rename from bgpd/src/server/rib_manager.rs rename to crates/server/src/rib_manager.rs index c4c8ea2..448cf7f 100644 --- a/bgpd/src/server/rib_manager.rs +++ b/crates/server/src/rib_manager.rs @@ -12,28 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::nlri::NLRI; -use crate::server::data_structures::RouteAnnounce; -use std::collections::BTreeMap; - -use crate::bgp_packet::path_attributes::PathAttribute; -use crate::server::config::PeerConfig; -use crate::server::data_structures::RouteUpdate; -use crate::server::peer::PeerCommands; - -use tokio_util::sync::CancellationToken; -use tracing::{info, trace, warn}; +use crate::config::PeerConfig; +use crate::data_structures::RouteAnnounce; +use crate::data_structures::RouteUpdate; +use crate::peer::PeerCommands; use std::cmp::Eq; +use std::collections::BTreeMap; use std::collections::HashMap; use std::convert::TryInto; +use std::sync::Mutex; +use bgp_packet::nlri::NLRI; +use bgp_packet::path_attributes::PathAttribute; use ip_network_table_deps_treebitmap::address::Address; use serde::Serialize; -use std::sync::Mutex; use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; +use tokio_util::sync::CancellationToken; +use tracing::{info, trace, warn}; use super::data_structures::RouteWithdraw; @@ -330,12 +328,13 @@ where #[cfg(test)] mod tests { - use crate::bgp_packet::constants::AddressFamilyIdentifier; - use crate::bgp_packet::nlri::NLRI; - use crate::server::rib_manager::RibManager; - use crate::server::rib_manager::RouteAnnounce; - use crate::server::rib_manager::RouteManagerCommands; - use crate::server::rib_manager::RouteUpdate; + use crate::rib_manager::RibManager; + use crate::rib_manager::RouteAnnounce; + use crate::rib_manager::RouteManagerCommands; + use crate::rib_manager::RouteUpdate; + + use bgp_packet::constants::AddressFamilyIdentifier; + use bgp_packet::nlri::NLRI; use std::net::Ipv6Addr; use std::str::FromStr; diff --git a/bgpd/src/server/route_server.rs b/crates/server/src/route_server.rs similarity index 94% rename from bgpd/src/server/route_server.rs rename to crates/server/src/route_server.rs index 9bc1b5f..4e4531e 100644 --- a/bgpd/src/server/route_server.rs +++ b/crates/server/src/route_server.rs @@ -12,19 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bgp_packet::constants::AddressFamilyIdentifier; -use crate::server::peer::PeerCommands; -use crate::server::rib_manager; -use crate::server::rib_manager::RibSnapshot; -use crate::server::rib_manager::RouteManagerCommands; -use crate::server::route_server::route_server::route_service_server::RouteService; -use crate::server::route_server::route_server::AddressFamily; -use crate::server::route_server::route_server::DumpPathsRequest; -use crate::server::route_server::route_server::DumpPathsResponse; -use crate::server::route_server::route_server::Path; -use crate::server::route_server::route_server::PathSet; -use crate::server::route_server::route_server::Prefix; -use crate::server::route_server::route_server::StreamPathsRequest; +use crate::peer::PeerCommands; +use crate::rib_manager; +use crate::rib_manager::RibSnapshot; +use crate::rib_manager::RouteManagerCommands; +use crate::route_server::route_server::route_service_server::RouteService; +use crate::route_server::route_server::AddressFamily; +use crate::route_server::route_server::DumpPathsRequest; +use crate::route_server::route_server::DumpPathsResponse; +use crate::route_server::route_server::Path; +use crate::route_server::route_server::PathSet; +use crate::route_server::route_server::Prefix; +use crate::route_server::route_server::StreamPathsRequest; +use bgp_packet::constants::AddressFamilyIdentifier; use log::warn; use std::collections::HashMap; use std::net::Ipv4Addr; diff --git a/tests/integration_tests/Cargo.toml b/tests/integration_tests/Cargo.toml index 97e06fd..9a0abf5 100644 --- a/tests/integration_tests/Cargo.toml +++ b/tests/integration_tests/Cargo.toml @@ -1,21 +1,19 @@ [package] -name = "integration_tests" -version = "0.1.0" authors = ["Rayhaan Jaufeerally "] edition = "2018" license = "Apache-2.0" +name = "integration_tests" +version = "0.1.0" [dependencies] -bgpd = { path = "../../bgpd" } -bytes = "1.*" -tokio = { version = "1.6.1", features = ["full"] } -tokio-util = { version = "0.6.7", features = ["codec"] } -tracing = "0.1" -tracing-subscriber = "0.2" -libc = "0.2.126" +bgp_packet.workspace = true +bgp_server.workspace = true +bytes = "1.*" +libc = "0.2.126" +tokio = { version = "1.6.1", features = ["full"] } +tokio-util = { version = "0.7.10", features = ["codec"] } +tracing = "0.1" +tracing-subscriber = "0.2" [dev-dependencies] serial_test = "0.5.1" - -[unstable] -thread_id_value = true \ No newline at end of file diff --git a/tests/integration_tests/tests/basic_startup.rs b/tests/integration_tests/tests/basic_startup.rs index c42f880..408cad9 100644 --- a/tests/integration_tests/tests/basic_startup.rs +++ b/tests/integration_tests/tests/basic_startup.rs @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bgpd::bgp_packet; -use bgpd::bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier}; -use bgpd::bgp_packet::messages::BGPSubmessage; -use bgpd::bgp_packet::traits::ParserContext; -use bgpd::server::bgp_server::Server; -use bgpd::server::config::{PeerConfig, ServerConfig}; +use bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier}; +use bgp_packet::messages::BGPSubmessage; +use bgp_packet::traits::ParserContext; +use bgp_server::bgp_server::Server; +use bgp_server::config::{PeerConfig, ServerConfig}; use std::io::{Read, Write}; use std::mem::size_of; use std::net::Ipv4Addr; @@ -27,6 +26,7 @@ use std::net::TcpStream; use std::net::{IpAddr, SocketAddrV6}; use std::os::unix::io::AsRawFd; use std::time::Duration; +use tokio::io::AsyncReadExt; use tokio_util::codec::Decoder; use tracing::info;