This commit is contained in:
@ -8,6 +8,7 @@ rust-version.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
axum = "0.7.7"
|
||||
bgp_packet.workspace = true
|
||||
bgp_server.workspace = true
|
||||
clap.workspace = true
|
||||
@ -33,3 +34,7 @@ path = "src/client/main.rs"
|
||||
[[bin]]
|
||||
name = "util"
|
||||
path = "src/util/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "api_server"
|
||||
path = "src/api_server/main.rs"
|
||||
|
||||
124
bin/src/api_server/main.rs
Normal file
124
bin/src/api_server/main.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::post;
|
||||
use axum::Router;
|
||||
use bgp_packet::nlri::NLRI;
|
||||
use clap::{Parser, Subcommand};
|
||||
use eyre::{bail, Result};
|
||||
use route_client::connector::Connector;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::info;
|
||||
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(
|
||||
author = "Rayhaan Jaufeerally <rayhaan@rayhaan.ch>",
|
||||
version = "0.1",
|
||||
about = "API Server"
|
||||
)]
|
||||
struct Cli {
|
||||
#[clap(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Run runs the API server connected to a given gRPC backend.
|
||||
Run {
|
||||
/// The route server gRPC backend.
|
||||
backend: String,
|
||||
},
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
pub connector: Connector,
|
||||
}
|
||||
|
||||
async fn handle_announce(
|
||||
State(state): State<Arc<Mutex<AppState>>>,
|
||||
Path((prefix, prefixlen)): Path<(String, u8)>,
|
||||
) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
state
|
||||
.lock()
|
||||
.await
|
||||
.connector
|
||||
.send_announce(
|
||||
"pr01_rue_rayhaan_net".to_owned(),
|
||||
NLRI::try_from(format!("{}/{}", prefix, prefixlen).as_str()).map_err(|e| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("failed to parse NLRI: {}", e),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("failed to make RPC to backend: {}", e),
|
||||
)
|
||||
})?;
|
||||
Ok((StatusCode::OK, "Success".to_owned()))
|
||||
}
|
||||
|
||||
async fn handle_withdraw(
|
||||
State(state): State<Arc<Mutex<AppState>>>,
|
||||
Path((prefix, prefixlen)): Path<(String, u8)>,
|
||||
) -> Result<(StatusCode, String), (StatusCode, String)> {
|
||||
state
|
||||
.lock()
|
||||
.await
|
||||
.connector
|
||||
.send_withdraw(
|
||||
"pr01_rue_rayhaan_net".to_owned(),
|
||||
NLRI::try_from(format!("{}/{}", prefix, prefixlen).as_str()).map_err(|e| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("failed to parse NLRI: {}", e),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("failed to make RPC to backend: {}", e),
|
||||
)
|
||||
})?;
|
||||
Ok((StatusCode::OK, "Success".to_owned()))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
info!("Starting API Server");
|
||||
|
||||
match args.command {
|
||||
Some(Commands::Run { backend }) => {
|
||||
let connector = Connector::new(backend).await?;
|
||||
let app = Router::new()
|
||||
.route("/announce/:prefix/:prefixlen", post(handle_announce))
|
||||
.route("/withdraw/:prefix/:prefixlen", post(handle_withdraw))
|
||||
.with_state(Arc::new(Mutex::new(AppState { connector })));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("localhost:8179")
|
||||
.await
|
||||
.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
None => bail!("A subcommand must be specified."),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1000
configs/bgplab.json
Normal file
1000
configs/bgplab.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ use nom::number::complete::{be_u16, be_u8};
|
||||
use nom::Err::Failure;
|
||||
use nom::IResult;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
|
||||
/// BGPOpenOptionType represents the option types in the Open message.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash)]
|
||||
@ -34,9 +35,9 @@ impl BGPOpenOptionType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for BGPOpenOptionType {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
impl From<BGPOpenOptionType> for u8 {
|
||||
fn from(val: BGPOpenOptionType) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,14 +96,12 @@ impl WritablePacket for OpenOption {
|
||||
}
|
||||
fn wire_len(&self, ctx: &ParserContext) -> Result<u16, &'static str> {
|
||||
match &self.oval {
|
||||
OpenOptions::Capabilities(c) => {
|
||||
return Ok(2 + c.wire_len(ctx)?);
|
||||
}
|
||||
OpenOptions::Capabilities(c) => Ok(2 + c.wire_len(ctx)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OpenOption {
|
||||
impl Display for OpenOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "OpenOption: {}", self.oval)
|
||||
}
|
||||
@ -113,11 +112,11 @@ pub enum OpenOptions {
|
||||
Capabilities(OpenOptionCapabilities),
|
||||
}
|
||||
|
||||
impl fmt::Display for OpenOptions {
|
||||
impl Display for OpenOptions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
return match &self {
|
||||
match &self {
|
||||
OpenOptions::Capabilities(c) => write!(f, "Capabilities: {}", c),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +136,7 @@ impl ReadablePacket for OpenOptionCapabilities {
|
||||
be_u8,
|
||||
nom::multi::many0(|i| BGPCapability::from_wire(ctx, i)),
|
||||
)(buf)?;
|
||||
return IResult::Ok((buf, OpenOptionCapabilities { caps }));
|
||||
IResult::Ok((buf, OpenOptionCapabilities { caps }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +160,7 @@ impl WritablePacket for OpenOptionCapabilities {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OpenOptionCapabilities {
|
||||
impl Display for OpenOptionCapabilities {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Capabilities: [")?;
|
||||
for cap in &self.caps {
|
||||
@ -181,9 +180,9 @@ impl BGPCapabilityType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for BGPCapabilityType {
|
||||
fn into(self) -> u8 {
|
||||
return self.0;
|
||||
impl From<BGPCapabilityType> for u8 {
|
||||
fn from(val: BGPCapabilityType) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,17 +305,17 @@ impl WritablePacket for BGPCapability {
|
||||
}
|
||||
fn wire_len(&self, ctx: &ParserContext) -> Result<u16, &'static str> {
|
||||
// BGPCapabilityType(u8) + cap_len(u8) + val
|
||||
return match &self.val {
|
||||
match &self.val {
|
||||
BGPCapabilityValue::FourByteASN(v) => Ok(2 + v.wire_len(ctx)?),
|
||||
BGPCapabilityValue::Multiprotocol(v) => Ok(2 + v.wire_len(ctx)?),
|
||||
BGPCapabilityValue::RouteRefresh(v) => Ok(2 + v.wire_len(ctx)?),
|
||||
BGPCapabilityValue::GracefulRestart(v) => Ok(2 + v.wire_len(ctx)?),
|
||||
BGPCapabilityValue::UnknownCapability(v) => Ok(2 + v.wire_len(ctx)?),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BGPCapability {
|
||||
impl Display for BGPCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
std::fmt::Display::fmt(&self.val, f)
|
||||
}
|
||||
@ -331,7 +330,7 @@ pub enum BGPCapabilityValue {
|
||||
UnknownCapability(UnknownCapability),
|
||||
}
|
||||
|
||||
impl fmt::Display for BGPCapabilityValue {
|
||||
impl Display for BGPCapabilityValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self {
|
||||
BGPCapabilityValue::FourByteASN(v) => std::fmt::Display::fmt(v, f),
|
||||
@ -379,7 +378,7 @@ impl WritablePacket for UnknownCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UnknownCapability {
|
||||
impl Display for UnknownCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "UnknownCapability type: {}", self.cap_code)
|
||||
}
|
||||
@ -403,7 +402,7 @@ impl ReadablePacket for FourByteASNCapability {
|
||||
buf: &'a [u8],
|
||||
) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> {
|
||||
let (buf, asn) = nom::combinator::complete(nom::number::complete::be_u32)(buf)?;
|
||||
return IResult::Ok((buf, FourByteASNCapability::new(asn)));
|
||||
IResult::Ok((buf, FourByteASNCapability::new(asn)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,7 +417,7 @@ impl WritablePacket for FourByteASNCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FourByteASNCapability {
|
||||
impl Display for FourByteASNCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "FourByteASN: asn: {}", self.asn)
|
||||
}
|
||||
@ -445,17 +444,12 @@ impl ReadablePacket for MultiprotocolCapability {
|
||||
ctx: &ParserContext,
|
||||
buf: &'a [u8],
|
||||
) -> IResult<&'a [u8], MultiprotocolCapability, BGPParserError<&'a [u8]>> {
|
||||
let (buf, (afi_raw, _, safi_raw)) = nom::combinator::complete(nom::sequence::tuple((
|
||||
let (buf, (afi, _, safi)) = nom::combinator::complete(nom::sequence::tuple((
|
||||
|i| AddressFamilyIdentifier::from_wire(ctx, i),
|
||||
nom::bytes::complete::take(1u8),
|
||||
|i| SubsequentAddressFamilyIdentifier::from_wire(ctx, i),
|
||||
)))(buf)?;
|
||||
|
||||
let afi = AddressFamilyIdentifier::try_from(afi_raw)
|
||||
.map_err(|e| nom::Err::Error(BGPParserError::CustomText(e.to_string())))?;
|
||||
let safi = SubsequentAddressFamilyIdentifier::try_from(safi_raw)
|
||||
.map_err(|e| nom::Err::Error(BGPParserError::CustomText(e.to_string())))?;
|
||||
|
||||
IResult::Ok((buf, MultiprotocolCapability::new(afi, safi)))
|
||||
}
|
||||
}
|
||||
@ -473,7 +467,7 @@ impl WritablePacket for MultiprotocolCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MultiprotocolCapability {
|
||||
impl Display for MultiprotocolCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "MultiprotocolCapbility: [ {} {} ]", self.afi, self.safi,)
|
||||
}
|
||||
@ -501,7 +495,7 @@ impl ReadablePacket for RouteRefreshCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RouteRefreshCapability {
|
||||
impl Display for RouteRefreshCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "RouteRefreshCapability")
|
||||
}
|
||||
@ -548,7 +542,7 @@ impl WritablePacket for GracefulRestartPayload {
|
||||
fn to_wire(&self, _: &ParserContext) -> Result<Vec<u8>, &'static str> {
|
||||
let afi: u16 = self.afi.into();
|
||||
let mut res = vec![0u8; 2];
|
||||
byteorder::NetworkEndian::write_u16(res.as_mut(), afi.into());
|
||||
byteorder::NetworkEndian::write_u16(res.as_mut(), afi);
|
||||
res.push(self.safi.into());
|
||||
res.push(if self.af_flags { 0x80 } else { 0 });
|
||||
Ok(res)
|
||||
@ -558,7 +552,7 @@ impl WritablePacket for GracefulRestartPayload {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GracefulRestartPayload {
|
||||
impl Display for GracefulRestartPayload {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
@ -604,7 +598,7 @@ impl WritablePacket for GracefulRestartCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GracefulRestartCapability {
|
||||
impl Display for GracefulRestartCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "GracefulRestartCapability: [")?;
|
||||
for value in &self.payloads {
|
||||
@ -630,14 +624,13 @@ impl WritablePacket for ExtendedNextHopEncodingCapability {
|
||||
Ok(self
|
||||
.afi_safi_nhafi
|
||||
.iter()
|
||||
.map(|e| {
|
||||
.flat_map(|e| {
|
||||
Into::<Vec<u8>>::into(e.0)
|
||||
.into_iter()
|
||||
.chain(vec![0x00, Into::<u8>::into(e.1)].into_iter())
|
||||
.chain(Into::<Vec<u8>>::into(e.2).into_iter())
|
||||
.chain(vec![0x00, Into::<u8>::into(e.1)])
|
||||
.chain(Into::<Vec<u8>>::into(e.2))
|
||||
.collect::<Vec<u8>>()
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<u8>>())
|
||||
}
|
||||
|
||||
@ -672,7 +665,7 @@ impl ReadablePacket for ExtendedNextHopEncodingCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExtendedNextHopEncodingCapability {
|
||||
impl Display for ExtendedNextHopEncodingCapability {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ExtendednextHopEncodingCapability [")?;
|
||||
for entry in &self.afi_safi_nhafi {
|
||||
@ -698,7 +691,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_four_byte_asn_capability() {
|
||||
let bytes: &[u8] = &[0x41, 0x04, 0x00, 0x00, 0x00, 0x2a];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let (buf, result) = BGPCapability::from_wire(ctx, bytes).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -716,7 +711,9 @@ mod tests {
|
||||
0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x80, 0x00, 0x02, 0x02,
|
||||
0x02, 0x00, 0x02, 0x02, 0x46, 0x00, 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x00, 0x2a,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let (_buf, result) =
|
||||
nom::multi::many0(|buf: &'a [u8]| OpenOption::from_wire(ctx, buf))(option_bytes)
|
||||
.unwrap();
|
||||
@ -728,7 +725,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_extended_next_hop_encoding_capability() {
|
||||
let bytes: Vec<u8> = vec![0x00, 0x01, 0x00, 0x01, 0x00, 0x02];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let (_, cap) = ExtendedNextHopEncodingCapability::from_wire(ctx, &bytes).unwrap();
|
||||
|
||||
let expected_str =
|
||||
|
||||
@ -27,11 +27,11 @@ pub enum AddressFamilyIdentifier {
|
||||
Ipv6,
|
||||
}
|
||||
|
||||
impl Into<u16> for AddressFamilyIdentifier {
|
||||
fn into(self) -> u16 {
|
||||
match self {
|
||||
Self::Ipv4 => 1,
|
||||
Self::Ipv6 => 2,
|
||||
impl From<AddressFamilyIdentifier> for u16 {
|
||||
fn from(val: AddressFamilyIdentifier) -> Self {
|
||||
match val {
|
||||
AddressFamilyIdentifier::Ipv4 => 1,
|
||||
AddressFamilyIdentifier::Ipv6 => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,28 +50,28 @@ impl TryFrom<u16> for AddressFamilyIdentifier {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for AddressFamilyIdentifier {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Ipv4 => 1_u16.to_be_bytes().to_vec(),
|
||||
Self::Ipv6 => 2_u16.to_be_bytes().to_vec(),
|
||||
impl From<AddressFamilyIdentifier> for Vec<u8> {
|
||||
fn from(val: AddressFamilyIdentifier) -> Self {
|
||||
match val {
|
||||
AddressFamilyIdentifier::Ipv4 => 1_u16.to_be_bytes().to_vec(),
|
||||
AddressFamilyIdentifier::Ipv6 => 2_u16.to_be_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience functions to convert AddressFamilyIdentifier into those used by netlink.
|
||||
impl Into<netlink_packet_route::AddressFamily> for AddressFamilyIdentifier {
|
||||
fn into(self) -> netlink_packet_route::AddressFamily {
|
||||
match self {
|
||||
impl From<AddressFamilyIdentifier> for netlink_packet_route::AddressFamily {
|
||||
fn from(val: AddressFamilyIdentifier) -> Self {
|
||||
match val {
|
||||
AddressFamilyIdentifier::Ipv4 => netlink_packet_route::AddressFamily::Inet,
|
||||
AddressFamilyIdentifier::Ipv6 => netlink_packet_route::AddressFamily::Inet6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<rtnetlink::IpVersion> for AddressFamilyIdentifier {
|
||||
fn into(self) -> rtnetlink::IpVersion {
|
||||
match self {
|
||||
impl From<AddressFamilyIdentifier> for rtnetlink::IpVersion {
|
||||
fn from(val: AddressFamilyIdentifier) -> Self {
|
||||
match val {
|
||||
AddressFamilyIdentifier::Ipv4 => rtnetlink::IpVersion::V4,
|
||||
AddressFamilyIdentifier::Ipv6 => rtnetlink::IpVersion::V6,
|
||||
}
|
||||
@ -113,14 +113,14 @@ pub enum SubsequentAddressFamilyIdentifier {
|
||||
MulticastMplsVpn,
|
||||
}
|
||||
|
||||
impl Into<u8> for SubsequentAddressFamilyIdentifier {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Self::Unicast => 1,
|
||||
Self::Multicast => 2,
|
||||
Self::NlriWithMpls => 4,
|
||||
Self::MplsLabeledVPN => 128,
|
||||
Self::MulticastMplsVpn => 129,
|
||||
impl From<SubsequentAddressFamilyIdentifier> for u8 {
|
||||
fn from(val: SubsequentAddressFamilyIdentifier) -> Self {
|
||||
match val {
|
||||
SubsequentAddressFamilyIdentifier::Unicast => 1,
|
||||
SubsequentAddressFamilyIdentifier::Multicast => 2,
|
||||
SubsequentAddressFamilyIdentifier::NlriWithMpls => 4,
|
||||
SubsequentAddressFamilyIdentifier::MplsLabeledVPN => 128,
|
||||
SubsequentAddressFamilyIdentifier::MulticastMplsVpn => 129,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,9 +45,9 @@ impl BGPMessageType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for BGPMessageType {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
impl From<BGPMessageType> for u8 {
|
||||
fn from(val: BGPMessageType) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
impl From<u8> for BGPMessageType {
|
||||
@ -264,7 +264,7 @@ impl Decoder for Codec {
|
||||
let len: u16 = byteorder::BigEndian::read_u16(&buf[16..18]);
|
||||
if buf.len() < len.into() {
|
||||
// Not enough data to read this frame.
|
||||
return Ok(None);
|
||||
Ok(None)
|
||||
} else if buf.len() == len as usize {
|
||||
// Exactly one message here, parse and clear buf.
|
||||
let parse_result = BGPMessage::from_wire(&self.ctx, buf.as_ref());
|
||||
@ -306,7 +306,7 @@ impl WritablePacket for BGPMessage {
|
||||
{
|
||||
let mut tmp: [u8; 2] = [0u8; 2];
|
||||
NetworkEndian::write_u16(&mut tmp, self.wire_len(ctx)?);
|
||||
buf.extend_from_slice(&mut tmp);
|
||||
buf.extend_from_slice(&tmp);
|
||||
}
|
||||
// Type
|
||||
buf.push(self.msg_type.into());
|
||||
@ -428,9 +428,9 @@ impl WritablePacket for OpenMessage {
|
||||
for opt in &self.options {
|
||||
count += (*opt).to_wire(ctx)?.len();
|
||||
}
|
||||
Ok(count
|
||||
count
|
||||
.try_into()
|
||||
.map_err(|_| "overflow in wire_len in OpenMessage")?)
|
||||
.map_err(|_| "overflow in wire_len in OpenMessage")
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,12 +485,12 @@ impl ReadablePacket for UpdateMessage {
|
||||
impl WritablePacket for UpdateMessage {
|
||||
fn to_wire(&self, ctx: &ParserContext) -> Result<Vec<u8>, &'static str> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
let mut tmp: &mut [u8] = &mut [0u8; 2];
|
||||
let tmp: &mut [u8] = &mut [0u8; 2];
|
||||
let mut wd_len: u16 = 0;
|
||||
for wd in &self.withdrawn_nlri {
|
||||
wd_len += wd.wire_len(ctx)?;
|
||||
}
|
||||
NetworkEndian::write_u16(&mut tmp, wd_len);
|
||||
NetworkEndian::write_u16(tmp, wd_len);
|
||||
buf.append(&mut tmp.to_vec());
|
||||
for wd in &self.withdrawn_nlri {
|
||||
buf.extend(wd.to_wire(ctx)?);
|
||||
@ -499,7 +499,7 @@ impl WritablePacket for UpdateMessage {
|
||||
for pattr in &self.path_attributes {
|
||||
pattr_len += pattr.wire_len(ctx)?;
|
||||
}
|
||||
NetworkEndian::write_u16(&mut tmp, pattr_len);
|
||||
NetworkEndian::write_u16(tmp, pattr_len);
|
||||
buf.extend(tmp.to_vec());
|
||||
for pattr in &self.path_attributes {
|
||||
buf.extend(pattr.to_wire(ctx)?);
|
||||
@ -566,7 +566,9 @@ mod tests {
|
||||
0x02, 0x02, 0x00, 0x02, 0x02, 0x46, 0x00, 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x00,
|
||||
0x2a,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv4);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv4);
|
||||
let (buf, result) = BGPMessage::from_wire(ctx, open_msg_bytes).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
|
||||
@ -585,7 +587,9 @@ mod tests {
|
||||
0x18, 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02,
|
||||
0x02, 0x80, 0x00, 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv4);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv4);
|
||||
let (buf, result) = BGPMessage::from_wire(ctx, open_msg_bytes).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
|
||||
@ -609,14 +613,16 @@ mod tests {
|
||||
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x00,
|
||||
0x14, 0x00, 0x00, 0x00, 0x0a, 0x18, 0xcb, 0x01, 0x4e,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv4);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv4);
|
||||
let (buf, result) = BGPMessage::from_wire(ctx, update_msg_bytes).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
|
||||
let want_str = "UpdateMessage [ withdrawn: announced: 203.1.78.0/24 path attributes: OriginPathAttribute::INCOMPLETEAS Path: { Segment [ Type: AS_SEGMENT 39540 57118 29691 1299 4739 ]] }NextHop: 185.95.219.36Communities: [ 1299:35000, 29691:4000, 29691:4021, 39540:4000, 39540:4010, 57118:2000, 57118:2010, ] LargeCommunities: [ 57118:20:0, 57118:20:10, ] ]";
|
||||
assert_eq!(format!("{}", result), want_str);
|
||||
|
||||
let reencoded = result.to_wire(&ctx).unwrap();
|
||||
let reencoded = result.to_wire(ctx).unwrap();
|
||||
assert_eq!(&reencoded, update_msg_bytes);
|
||||
}
|
||||
|
||||
@ -664,7 +670,7 @@ mod tests {
|
||||
for b in &buf {
|
||||
print!("0x{:02x}, ", b);
|
||||
}
|
||||
assert_eq!(buf.as_ref(), update_msg_bytes.as_ref());
|
||||
assert_eq!(buf.as_ref(), update_msg_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -72,11 +72,9 @@ impl ReadablePacket for NLRI {
|
||||
let (buf, prefix) = take(octet_len)(buf)?;
|
||||
|
||||
match ctx.nlri_mode {
|
||||
None => {
|
||||
return Err(Failure(BGPParserError::CustomText(
|
||||
None => Err(Failure(BGPParserError::CustomText(
|
||||
"nlri_mode not set in the context for NLRI::from_wire".to_string(),
|
||||
)));
|
||||
}
|
||||
))),
|
||||
Some(afi) => Ok((
|
||||
buf,
|
||||
NLRI {
|
||||
@ -115,8 +113,7 @@ impl<'de> Deserialize<'de> for NLRI {
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Self::try_from(String::deserialize(deserializer)?.as_str())
|
||||
.map_err(|e| de::Error::custom(e))
|
||||
Self::try_from(String::deserialize(deserializer)?.as_str()).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +204,7 @@ impl TryInto<IpAddr> for NLRI {
|
||||
impl TryFrom<&str> for NLRI {
|
||||
type Error = eyre::ErrReport;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let parts: Vec<&str> = value.split("/").collect();
|
||||
let parts: Vec<&str> = value.split('/').collect();
|
||||
if parts.len() != 2 {
|
||||
bail!("Expected ip_addr/prefixlen but got: {}", value);
|
||||
}
|
||||
@ -217,11 +214,11 @@ impl TryFrom<&str> for NLRI {
|
||||
let mut octets: Vec<u8>;
|
||||
let afi: AddressFamilyIdentifier;
|
||||
|
||||
if parts[0].contains(":") {
|
||||
if parts[0].contains(':') {
|
||||
afi = AddressFamilyIdentifier::Ipv6;
|
||||
let addr: Ipv6Addr = Ipv6Addr::from_str(parts[0]).map_err(|e| eyre!(e))?;
|
||||
octets = addr.octets().to_vec();
|
||||
} else if parts[0].contains(".") {
|
||||
} else if parts[0].contains('.') {
|
||||
afi = AddressFamilyIdentifier::Ipv4;
|
||||
let addr: Ipv4Addr = Ipv4Addr::from_str(parts[0]).map_err(|e| eyre!(e))?;
|
||||
octets = addr.octets().to_vec();
|
||||
@ -237,7 +234,7 @@ impl TryFrom<&str> for NLRI {
|
||||
let num_bytes = (prefixlen / 8) + 1;
|
||||
let mask = u8::MAX << (8 - (prefixlen % 8));
|
||||
octets.truncate(num_bytes.into());
|
||||
if octets.len() > 0 {
|
||||
if !octets.is_empty() {
|
||||
let last_pos = octets.len() - 1;
|
||||
octets[last_pos] &= mask;
|
||||
}
|
||||
@ -282,7 +279,7 @@ mod tests {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use super::NLRI;
|
||||
use crate::constants::AddressFamilyIdentifier::{self, Ipv4, Ipv6};
|
||||
use crate::constants::AddressFamilyIdentifier::{Ipv4, Ipv6};
|
||||
use crate::traits::ParserContext;
|
||||
use crate::traits::ReadablePacket;
|
||||
use crate::traits::WritablePacket;
|
||||
@ -290,7 +287,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic_nlri_v6() {
|
||||
let nlri_bytes: &[u8] = &[0x20, 0x20, 0x01, 0xdb, 0x8];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let nlri_res: (&[u8], NLRI) = NLRI::from_wire(ctx, nlri_bytes).unwrap();
|
||||
assert_eq!(nlri_res.1.afi, Ipv6);
|
||||
assert_eq!(nlri_res.1.prefixlen, 32);
|
||||
@ -305,7 +304,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic_nlri_v4() {
|
||||
let nlri_bytes: &[u8] = &[0x18, 192, 168, 1];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv4);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv4);
|
||||
let nlri_res: (&[u8], NLRI) = NLRI::from_wire(ctx, nlri_bytes).unwrap();
|
||||
assert_eq!(nlri_res.1.afi, Ipv4);
|
||||
assert_eq!(nlri_res.1.prefixlen, 24);
|
||||
|
||||
@ -472,10 +472,10 @@ impl ReadablePacket for NextHopPathAttribute {
|
||||
|
||||
impl WritablePacket for NextHopPathAttribute {
|
||||
fn to_wire(&self, _: &ParserContext) -> Result<Vec<u8>, &'static str> {
|
||||
return Ok(self.0.octets().to_vec());
|
||||
Ok(self.0.octets().to_vec())
|
||||
}
|
||||
fn wire_len(&self, _: &ParserContext) -> Result<u16, &'static str> {
|
||||
return Ok(4);
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ impl ReadablePacket for AggregatorPathAttribute {
|
||||
ctx: &ParserContext,
|
||||
buf: &'a [u8],
|
||||
) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> {
|
||||
if !ctx.four_octet_asn.is_some() {
|
||||
if ctx.four_octet_asn.is_none() {
|
||||
return Err(Failure(BGPParserError::CustomText(
|
||||
"Non four byte ASN not supported (AggregatorPathAttribute from_wire)".to_string(),
|
||||
)));
|
||||
@ -604,7 +604,8 @@ impl ReadablePacket for AggregatorPathAttribute {
|
||||
|
||||
impl WritablePacket for AggregatorPathAttribute {
|
||||
fn to_wire(&self, ctx: &ParserContext) -> Result<Vec<u8>, &'static str> {
|
||||
if !ctx.four_octet_asn.is_some() {
|
||||
if ctx.four_octet_asn.is_none() {
|
||||
// TODO: This should be gracefully handled, not panic!
|
||||
panic!("Non four byte ASN not supported (AggregatorPathAttribute from_wire)");
|
||||
}
|
||||
let mut buf: Vec<u8> = vec![0u8; 4];
|
||||
@ -613,7 +614,8 @@ impl WritablePacket for AggregatorPathAttribute {
|
||||
Ok(buf)
|
||||
}
|
||||
fn wire_len(&self, ctx: &ParserContext) -> Result<u16, &'static str> {
|
||||
if !ctx.four_octet_asn.is_some() {
|
||||
if ctx.four_octet_asn.is_none() {
|
||||
// TODO: This should be gracefully handled, not panic!
|
||||
panic!("Non four byte ASN not supported (AggregatorPathAttribute from_wire)");
|
||||
}
|
||||
Ok(8)
|
||||
@ -733,7 +735,7 @@ impl WritablePacket for ExtendedCommunitiesPathAttribute {
|
||||
if !self.value.len() == 7 {
|
||||
return Err("ExtendedCommunitiesPathAttribute value length != 7");
|
||||
}
|
||||
Ok(vec![vec![self.t_high], self.value.to_owned()].concat())
|
||||
Ok([vec![self.t_high], self.value.to_owned()].concat())
|
||||
}
|
||||
fn wire_len(&self, _: &ParserContext) -> Result<u16, &'static str> {
|
||||
Ok(8)
|
||||
@ -847,7 +849,7 @@ impl MPReachNLRIPathAttribute {
|
||||
// https://datatracker.ietf.org/doc/html/rfc2545 describes what the nexthop
|
||||
// field can contain. Returns a tuple of (global_nh, linklocal_nh)
|
||||
pub fn nexthop_to_v6(self) -> Option<(Ipv6Addr, Option<Ipv6Addr>)> {
|
||||
return match self.nexthop.len() {
|
||||
match self.nexthop.len() {
|
||||
16 => {
|
||||
let nh_bytes: [u8; 16] = self.nexthop.try_into().unwrap();
|
||||
Some((Ipv6Addr::from(nh_bytes), None))
|
||||
@ -861,7 +863,7 @@ impl MPReachNLRIPathAttribute {
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1010,7 +1012,9 @@ mod tests {
|
||||
0x02, 0x04, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x20, 0x1a,
|
||||
0x00, 0x00, 0x78, 0xfc,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = &ASPathAttribute::from_wire(ctx, as_path_bytes).unwrap();
|
||||
|
||||
let expected_aspath: Vec<u32> = vec![39540, 57118, 8218, 30972];
|
||||
@ -1029,7 +1033,9 @@ mod tests {
|
||||
0x02, 0x04, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x20, 0x1a,
|
||||
0x00, 0x00, 0x78, 0xfc, 0x01, 0x02, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = &ASPathAttribute::from_wire(ctx, as_path_bytes).unwrap();
|
||||
|
||||
let expected_aspath: Vec<u32> = vec![39540, 57118, 8218, 30972];
|
||||
@ -1048,7 +1054,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_next_hop_path_attribute() {
|
||||
let nh_bytes: &[u8] = &[192, 168, 1, 1];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = NextHopPathAttribute::from_wire(ctx, nh_bytes).unwrap();
|
||||
|
||||
assert_eq!(result.1 .0, "192.168.1.1".parse::<Ipv4Addr>().unwrap());
|
||||
@ -1060,7 +1068,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_multi_exit_discriminator_path_attribute() {
|
||||
let med_bytes: &[u8] = &[0xca, 0x00, 0x00, 0xbe];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = MultiExitDiscPathAttribute::from_wire(ctx, med_bytes).unwrap();
|
||||
|
||||
assert_eq!(result.1 .0, 3388997822);
|
||||
@ -1072,7 +1082,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_local_pref_path_attribute() {
|
||||
let local_pref_bytes: &[u8] = &[0xca, 0x00, 0x00, 0xbe];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = LocalPrefPathAttribute::from_wire(ctx, local_pref_bytes).unwrap();
|
||||
|
||||
assert_eq!(result.1 .0, 3388997822);
|
||||
@ -1090,7 +1102,9 @@ mod tests {
|
||||
0xff, 0xf1, 0x73, 0xfb, 0x0f, 0xa0, 0x73, 0xfb, 0x0f, 0xc8, 0x9a, 0x74, 0x0f, 0xa0,
|
||||
0x9a, 0x74, 0x0f, 0xb4, 0xdf, 0x1e, 0x07, 0xd0, 0xdf, 0x1e, 0x07, 0xe4,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = CommunitiesPathAttribute::from_wire(ctx, communities_bytes).unwrap();
|
||||
let expected_communities: Vec<(u16, u16)> = vec![
|
||||
(0, 0x32bd),
|
||||
@ -1127,7 +1141,9 @@ mod tests {
|
||||
0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xdf, 0x1e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14,
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result = LargeCommunitiesPathAttribute::from_wire(ctx, large_community_bytes).unwrap();
|
||||
assert_eq!(result.1.values.len(), 2);
|
||||
assert_eq!(result.1.values[0].global_admin, 57118);
|
||||
@ -1154,7 +1170,9 @@ mod tests {
|
||||
0x20, 0x20, 0x01, 0x0d, 0xb8, // NLRI 1
|
||||
0x10, 0xfe, 0x80, // NLRI 2
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result: (&[u8], MPReachNLRIPathAttribute) =
|
||||
MPReachNLRIPathAttribute::from_wire(ctx, mp_reach_bytes).unwrap();
|
||||
assert_eq!(result.1.afi, Ipv6);
|
||||
@ -1184,7 +1202,9 @@ mod tests {
|
||||
0x20, 0x20, 0x01, 0x0d, 0xb8, // NLRI 1
|
||||
0x10, 0xfe, 0x80, // NLRI 2
|
||||
];
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let result: (&[u8], MPUnreachNLRIPathAttribute) =
|
||||
MPUnreachNLRIPathAttribute::from_wire(ctx, mp_unreach_bytes).unwrap();
|
||||
assert_eq!(result.1.afi, Ipv6);
|
||||
@ -1201,7 +1221,7 @@ mod tests {
|
||||
|
||||
// Tests the high level dispatching of the path attribute parser
|
||||
#[test]
|
||||
fn test_path_attribute_parsing<'a>() {
|
||||
fn test_path_attribute_parsing() {
|
||||
let path_attr_bytes: &[u8] = &[
|
||||
0x40, 0x01, 0x01, 0x00, 0x50, 0x02, 0x00, 0x1a, 0x02, 0x06, 0x00, 0x00, 0x9a, 0x74,
|
||||
0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x0b, 0x62, 0x00, 0x00, 0x19, 0x35, 0x00, 0x00,
|
||||
@ -1212,9 +1232,13 @@ mod tests {
|
||||
0x9a, 0x74, 0x0f, 0xbe,
|
||||
];
|
||||
|
||||
let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6);
|
||||
let ctx = &ParserContext::default()
|
||||
.four_octet_asn(true)
|
||||
.nlri_mode(Ipv6);
|
||||
let (buf, res): (_, Vec<PathAttribute>) =
|
||||
nom::multi::many0(|buf: &'a [u8]| PathAttribute::from_wire(ctx, buf))(path_attr_bytes)
|
||||
nom::multi::many0(|buf: &'static [u8]| PathAttribute::from_wire(ctx, buf))(
|
||||
path_attr_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
let expected_str = "[OriginPathAttribute(IGP), \
|
||||
|
||||
@ -21,7 +21,7 @@ use nom::IResult;
|
||||
|
||||
// ParserContext contains information pertinent to configurations which affect
|
||||
// how message parsing is to be handled.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct ParserContext {
|
||||
// Whether the peer is RFC6793 compliant.
|
||||
pub four_octet_asn: Option<bool>,
|
||||
@ -30,13 +30,6 @@ pub struct ParserContext {
|
||||
}
|
||||
|
||||
impl ParserContext {
|
||||
pub fn new() -> ParserContext {
|
||||
ParserContext {
|
||||
four_octet_asn: None,
|
||||
nlri_mode: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn four_octet_asn(mut self, v: bool) -> Self {
|
||||
self.four_octet_asn = Some(v);
|
||||
self
|
||||
|
||||
@ -81,6 +81,8 @@ message AnnouncementRequest {
|
||||
string peer_name = 1;
|
||||
Prefix prefix = 2;
|
||||
repeated string large_communities = 3;
|
||||
// When set to true inserts the route, when set to false withdraws the route.
|
||||
bool add = 4;
|
||||
}
|
||||
|
||||
message AnnouncementResponse {}
|
||||
|
||||
66
crates/route_client/src/connector.rs
Normal file
66
crates/route_client/src/connector.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use bgp_packet::nlri::NLRI;
|
||||
use eyre::Result;
|
||||
use tonic::transport::{Channel, Endpoint, Uri};
|
||||
|
||||
use crate::proto::bgp_server_admin_service_client::BgpServerAdminServiceClient;
|
||||
use crate::proto::{AnnouncementRequest, Prefix};
|
||||
|
||||
pub struct Connector {
|
||||
client: BgpServerAdminServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl Connector {
|
||||
pub async fn new(addr: String) -> Result<Self> {
|
||||
let uri = Uri::from_str(addr.as_str()).unwrap();
|
||||
let endpoint = Endpoint::from(uri).keep_alive_timeout(Duration::from_secs(10));
|
||||
let client = BgpServerAdminServiceClient::connect(endpoint).await?;
|
||||
Ok(Self { client })
|
||||
}
|
||||
|
||||
pub async fn send_announce(&mut self, peer_name: String, prefix: NLRI) -> Result<()> {
|
||||
let request = AnnouncementRequest {
|
||||
peer_name,
|
||||
prefix: Some(Prefix {
|
||||
ip_prefix: prefix.prefix,
|
||||
prefix_len: prefix.prefixlen as i32,
|
||||
address_family: match prefix.afi {
|
||||
bgp_packet::constants::AddressFamilyIdentifier::Ipv4 => {
|
||||
crate::proto::AddressFamily::IPv4.into()
|
||||
}
|
||||
bgp_packet::constants::AddressFamilyIdentifier::Ipv6 => {
|
||||
crate::proto::AddressFamily::IPv6.into()
|
||||
}
|
||||
},
|
||||
}),
|
||||
large_communities: vec![],
|
||||
add: true,
|
||||
};
|
||||
self.client.announce_to_peer(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_withdraw(&mut self, peer_name: String, prefix: NLRI) -> Result<()> {
|
||||
let request = AnnouncementRequest {
|
||||
peer_name,
|
||||
prefix: Some(Prefix {
|
||||
ip_prefix: prefix.prefix,
|
||||
prefix_len: prefix.prefixlen as i32,
|
||||
address_family: match prefix.afi {
|
||||
bgp_packet::constants::AddressFamilyIdentifier::Ipv4 => {
|
||||
crate::proto::AddressFamily::IPv4.into()
|
||||
}
|
||||
bgp_packet::constants::AddressFamilyIdentifier::Ipv6 => {
|
||||
crate::proto::AddressFamily::IPv6.into()
|
||||
}
|
||||
},
|
||||
}),
|
||||
large_communities: vec![],
|
||||
add: false,
|
||||
};
|
||||
self.client.announce_to_peer(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod connector;
|
||||
pub mod fib_state;
|
||||
pub mod netlink;
|
||||
pub mod southbound_interface;
|
||||
|
||||
@ -43,24 +43,16 @@ pub trait SouthboundInterface {
|
||||
|
||||
/// DummyVerifier is a SouthboundInterface that checks that routes are not added more than
|
||||
/// once and not removed when there are none.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DummyVerifier {
|
||||
route_state: HashMap<NLRI, IpAddr>,
|
||||
}
|
||||
|
||||
impl std::default::Default for DummyVerifier {
|
||||
fn default() -> DummyVerifier {
|
||||
DummyVerifier {
|
||||
route_state: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SouthboundInterface for DummyVerifier {
|
||||
async fn get_all_routes(
|
||||
&mut self,
|
||||
address_family: AddressFamilyIdentifier,
|
||||
_address_family: AddressFamilyIdentifier,
|
||||
) -> Result<Vec<(NLRI, IpAddr)>> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
@ -81,6 +81,8 @@ message AnnouncementRequest {
|
||||
string peer_name = 1;
|
||||
Prefix prefix = 2;
|
||||
repeated string large_communities = 3;
|
||||
// When set to true inserts the route, when set to false withdraws the route.
|
||||
bool add = 4;
|
||||
}
|
||||
|
||||
message AnnouncementResponse {}
|
||||
|
||||
@ -20,8 +20,8 @@ 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::bgp_server_admin_service_server::BgpServerAdminServiceServer;
|
||||
use crate::route_server::route_server::route_service_server::RouteServiceServer;
|
||||
use crate::route_server::proto::bgp_server_admin_service_server::BgpServerAdminServiceServer;
|
||||
use crate::route_server::proto::route_service_server::RouteServiceServer;
|
||||
use bgp_packet::constants::AddressFamilyIdentifier;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::filter_eval::FilterEvaluator;
|
||||
use crate::path::path_data::PathData;
|
||||
use crate::path::path_set::PathSource;
|
||||
use crate::rib_manager::RouteManagerCommands;
|
||||
use crate::route_server::route_server::PeerStatus;
|
||||
use crate::route_server::proto::PeerStatus;
|
||||
|
||||
use bgp_packet::capabilities::{
|
||||
BGPCapability, BGPCapabilityTypeValues, BGPCapabilityValue, BGPOpenOptionTypeValues,
|
||||
@ -38,10 +38,10 @@ 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::{ASPathAttribute, MPUnreachNLRIPathAttribute};
|
||||
use bgp_packet::path_attributes::{
|
||||
LargeCommunitiesPathAttribute, LargeCommunitiesPayload, MPReachNLRIPathAttribute,
|
||||
};
|
||||
@ -485,7 +485,7 @@ where
|
||||
};
|
||||
let mut buf = BytesMut::new();
|
||||
self.codec.lock().await.encode(bgp_message, &mut buf)?;
|
||||
conn.write(&buf).await?;
|
||||
conn.write_all(&buf).await?;
|
||||
|
||||
// Update state
|
||||
self.state = BGPState::OpenSent;
|
||||
@ -530,8 +530,104 @@ where
|
||||
}
|
||||
|
||||
PeerCommands::Announce(route_update) => match route_update {
|
||||
RouteUpdate::Announce(announcement) => todo!(),
|
||||
RouteUpdate::Withdraw(withdrawal) => todo!(),
|
||||
RouteUpdate::Announce(announcement) => {
|
||||
// nexthop is not populated in announcement so we set it ourselves here.
|
||||
let nexthop = if let Some(stream) = &self.tcp_stream {
|
||||
stream.local_addr()?
|
||||
} else {
|
||||
bail!("Cannot send route announcement since tcp stream is closed");
|
||||
};
|
||||
let nexthop_attribute: PathAttribute = match nexthop.ip() {
|
||||
IpAddr::V4(ipv4_addr) => {
|
||||
// TODO: In the IPv4 case we also need to add the NLRI to the update msg.
|
||||
PathAttribute::NextHopPathAttribute(NextHopPathAttribute(ipv4_addr))
|
||||
}
|
||||
IpAddr::V6(ipv6_addr) => {
|
||||
PathAttribute::MPReachNLRIPathAttribute(MPReachNLRIPathAttribute {
|
||||
afi: AddressFamilyIdentifier::Ipv6,
|
||||
safi: SubsequentAddressFamilyIdentifier::Unicast,
|
||||
nexthop: ipv6_addr.octets().to_vec(),
|
||||
nlris: announcement.0.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mut path_attributes = vec![
|
||||
PathAttribute::OriginPathAttribute(announcement.1.origin),
|
||||
ASPathAttribute::from_asns(announcement.1.as_path.clone()),
|
||||
nexthop_attribute,
|
||||
];
|
||||
|
||||
for nlri in &announcement.0 {
|
||||
if !self.filter_evaluator.evaluate_out(
|
||||
&mut path_attributes,
|
||||
&announcement.1.as_path,
|
||||
&nlri,
|
||||
) {
|
||||
bail!("Filter rejected NLRI: {}", nlri);
|
||||
}
|
||||
}
|
||||
|
||||
let update_message = UpdateMessage {
|
||||
path_attributes,
|
||||
withdrawn_nlri: Vec::default(),
|
||||
announced_nlri: Vec::default(),
|
||||
};
|
||||
|
||||
let bgp_message = BGPMessage {
|
||||
msg_type: UPDATE_MESSAGE,
|
||||
payload: BGPSubmessage::UpdateMessage(update_message),
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
self.codec
|
||||
.lock()
|
||||
.await
|
||||
.encode(bgp_message, &mut buf)
|
||||
.map_err(|e| eyre!("failed to encode BGP message: {}", e))?;
|
||||
|
||||
if let Some(stream) = self.tcp_stream.as_mut() {
|
||||
stream
|
||||
.write(&buf)
|
||||
.await
|
||||
.map_err(|e| eyre!("Failed to write msg to peer: {}", e))?;
|
||||
}
|
||||
}
|
||||
RouteUpdate::Withdraw(withdrawal) => {
|
||||
// Only IPv6 NLRIs are supported for now.
|
||||
let path_attributes = vec![PathAttribute::MPUnreachNLRIPathAttribute(
|
||||
MPUnreachNLRIPathAttribute {
|
||||
afi: AddressFamilyIdentifier::Ipv6,
|
||||
safi: SubsequentAddressFamilyIdentifier::Unicast,
|
||||
nlris: withdrawal.prefixes,
|
||||
},
|
||||
)];
|
||||
|
||||
let update_message = UpdateMessage {
|
||||
path_attributes,
|
||||
withdrawn_nlri: vec![],
|
||||
announced_nlri: vec![],
|
||||
};
|
||||
|
||||
let bgp_message = BGPMessage {
|
||||
msg_type: UPDATE_MESSAGE,
|
||||
payload: BGPSubmessage::UpdateMessage(update_message),
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
self.codec
|
||||
.lock()
|
||||
.await
|
||||
.encode(bgp_message, &mut buf)
|
||||
.map_err(|e| eyre!("failed to encode BGP message: {}", e))?;
|
||||
|
||||
if let Some(stream) = self.tcp_stream.as_mut() {
|
||||
stream
|
||||
.write(&buf)
|
||||
.await
|
||||
.map_err(|e| eyre!("Failed to write msg to peer: {}", e))?;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
PeerCommands::MessageFromPeer(msg) => match self.handle_msg(msg).await {
|
||||
@ -666,7 +762,7 @@ where
|
||||
self.codec.lock().await.encode(bgp_msg, &mut buf)?;
|
||||
match self.tcp_stream.as_mut() {
|
||||
Some(stream) => {
|
||||
stream.write(&buf).await?;
|
||||
stream.write_all(&buf).await?;
|
||||
}
|
||||
None => warn!("Dropped notification message to peer"),
|
||||
}
|
||||
@ -927,7 +1023,7 @@ where
|
||||
};
|
||||
let mut buf = BytesMut::new();
|
||||
self.codec.lock().await.encode(keepalive, &mut buf)?;
|
||||
conn.write(buf.as_ref()).await?;
|
||||
conn.write_all(buf.as_ref()).await?;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(std::io::Error::new(
|
||||
|
||||
@ -12,29 +12,36 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::data_structures::RouteUpdate;
|
||||
use crate::data_structures::RouteWithdraw;
|
||||
use crate::path::path_data::PathData;
|
||||
use crate::path::path_set::PathSet;
|
||||
use crate::path::path_set::PathSource;
|
||||
use crate::peer::PeerCommands;
|
||||
use crate::rib_manager::RibSnapshot;
|
||||
use crate::rib_manager::RouteManagerCommands;
|
||||
use crate::route_server::route_server::bgp_server_admin_service_server::BgpServerAdminService;
|
||||
use crate::route_server::route_server::route_service_server::RouteService;
|
||||
use crate::route_server::route_server::AddressFamily;
|
||||
use crate::route_server::route_server::AnnouncementRequest;
|
||||
use crate::route_server::route_server::AnnouncementResponse;
|
||||
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::Prefix;
|
||||
use crate::route_server::route_server::StreamPathsRequest;
|
||||
use crate::route_server::proto::bgp_server_admin_service_server::BgpServerAdminService;
|
||||
use crate::route_server::proto::route_service_server::RouteService;
|
||||
use crate::route_server::proto::AddressFamily;
|
||||
use crate::route_server::proto::AnnouncementRequest;
|
||||
use crate::route_server::proto::AnnouncementResponse;
|
||||
use crate::route_server::proto::DumpPathsRequest;
|
||||
use crate::route_server::proto::DumpPathsResponse;
|
||||
use crate::route_server::proto::Path;
|
||||
use crate::route_server::proto::Prefix;
|
||||
use crate::route_server::proto::StreamPathsRequest;
|
||||
|
||||
use bgp_packet::constants::AddressFamilyIdentifier;
|
||||
use route_server::PeerStatusRequest;
|
||||
use route_server::PeerStatusResponse;
|
||||
use bgp_packet::nlri::NLRI;
|
||||
use bgp_packet::path_attributes::OriginPathAttribute;
|
||||
use chrono::Utc;
|
||||
use proto::PeerStatusRequest;
|
||||
use proto::PeerStatusResponse;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
@ -44,7 +51,7 @@ use tonic::Response;
|
||||
use tonic::Status;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub mod route_server {
|
||||
pub mod proto {
|
||||
tonic::include_proto!("bgpd.grpc");
|
||||
}
|
||||
|
||||
@ -78,11 +85,8 @@ impl RouteServer {
|
||||
|
||||
/// Converts a rib_manager::PathSet into the proto format PathSet using the
|
||||
/// appropriate address family.
|
||||
fn transform_pathset<A>(
|
||||
mgr_ps: (u64, PathSet<A>),
|
||||
address_family: i32,
|
||||
) -> route_server::PathSet {
|
||||
let mut proto_pathset = route_server::PathSet {
|
||||
fn transform_pathset<A>(mgr_ps: (u64, PathSet<A>), address_family: i32) -> proto::PathSet {
|
||||
let mut proto_pathset = proto::PathSet {
|
||||
epoch: mgr_ps.0,
|
||||
prefix: Some(Prefix {
|
||||
ip_prefix: mgr_ps.1.nlri().prefix.clone(),
|
||||
@ -151,7 +155,50 @@ impl BgpServerAdminService for RouteServer {
|
||||
let request = request.get_ref();
|
||||
|
||||
if let Some(peer) = self.peer_state_machines.get(&request.peer_name) {
|
||||
info!("Would make announcement to peer: {}", &request.peer_name);
|
||||
let prefix = request
|
||||
.prefix
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("Missing prefix"))?;
|
||||
let nlri = NLRI {
|
||||
afi: AddressFamilyIdentifier::Ipv6,
|
||||
prefix: prefix.ip_prefix.clone(),
|
||||
prefixlen: prefix.prefix_len as u8,
|
||||
};
|
||||
|
||||
if request.add {
|
||||
let path_data = PathData {
|
||||
origin: OriginPathAttribute::IGP,
|
||||
nexthop: Vec::default(),
|
||||
path_source: PathSource::LocallyConfigured,
|
||||
local_pref: 100,
|
||||
med: 100,
|
||||
as_path: vec![210036],
|
||||
path_attributes: Vec::default(),
|
||||
learn_time: Utc::now(),
|
||||
};
|
||||
if let Err(e) = peer.send(PeerCommands::Announce(RouteUpdate::Announce((
|
||||
vec![nlri],
|
||||
Arc::new(path_data),
|
||||
)))) {
|
||||
warn!("Failed to send announcement to peer: {}", e);
|
||||
return Err(Status::internal(format!(
|
||||
"Failed to send message to PeerStateMachine: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
let update = PeerCommands::Announce(RouteUpdate::Withdraw(RouteWithdraw {
|
||||
peer_id: Ipv4Addr::new(0, 0, 0, 0),
|
||||
prefixes: vec![nlri],
|
||||
}));
|
||||
if let Err(e) = peer.send(update) {
|
||||
warn!("Failed to send withdrawal to peer: {}", e);
|
||||
return Err(Status::internal(format!(
|
||||
"Failed to send message to PeerStateMachine: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(Status::invalid_argument(format!(
|
||||
"No such peer: {}",
|
||||
@ -189,7 +236,7 @@ impl RouteService for RouteServer {
|
||||
Ok(result) => {
|
||||
response.epoch = result.epoch;
|
||||
for pathset in result.routes {
|
||||
let mut proto_pathset = route_server::PathSet {
|
||||
let mut proto_pathset = proto::PathSet {
|
||||
epoch: result.epoch,
|
||||
prefix: Some(Prefix {
|
||||
ip_prefix: pathset.nlri().prefix.clone(),
|
||||
@ -236,7 +283,7 @@ impl RouteService for RouteServer {
|
||||
Ok(result) => {
|
||||
response.epoch = result.epoch;
|
||||
for pathset in result.routes {
|
||||
let mut proto_pathset = route_server::PathSet {
|
||||
let mut proto_pathset = proto::PathSet {
|
||||
epoch: result.epoch,
|
||||
prefix: Some(Prefix {
|
||||
ip_prefix: pathset.nlri().prefix.clone(),
|
||||
@ -274,7 +321,7 @@ impl RouteService for RouteServer {
|
||||
}
|
||||
}
|
||||
|
||||
type StreamPathsStream = ReceiverStream<Result<route_server::PathSet, Status>>;
|
||||
type StreamPathsStream = ReceiverStream<Result<proto::PathSet, Status>>;
|
||||
|
||||
async fn stream_paths(
|
||||
&self,
|
||||
|
||||
@ -29,8 +29,8 @@ use bgp_packet::messages::BGPSubmessage;
|
||||
use bgp_packet::traits::ParserContext;
|
||||
use bgp_server::bgp_server::Server;
|
||||
use bgp_server::config::{PeerConfig, PrefixAnnouncement, ServerConfig};
|
||||
use bgp_server::route_server::route_server::bgp_server_admin_service_client::BgpServerAdminServiceClient;
|
||||
use bgp_server::route_server::route_server::PeerStatusRequest;
|
||||
use bgp_server::route_server::proto::bgp_server_admin_service_client::BgpServerAdminServiceClient;
|
||||
use bgp_server::route_server::proto::PeerStatusRequest;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serial_test;
|
||||
@ -151,7 +151,7 @@ async fn test_bgp_listener_known_peer() {
|
||||
assert!(conn.write_all(open_msg_bytes).is_ok());
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -168,14 +168,14 @@ async fn test_bgp_listener_known_peer() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the server sends a keepalive after the open message.
|
||||
|
||||
let mut ka_buf = vec![0u8; 65536];
|
||||
conn.read(&mut ka_buf).unwrap();
|
||||
let _ = conn.read(&mut ka_buf).unwrap();
|
||||
let response_ka_message = codec
|
||||
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
|
||||
.unwrap();
|
||||
@ -183,7 +183,7 @@ async fn test_bgp_listener_known_peer() {
|
||||
match response_ka_message.unwrap().payload {
|
||||
BGPSubmessage::KeepaliveMessage(_ka) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ async fn test_bgp_peer_statemachine_outbound_conn() {
|
||||
assert!(conn.write_all(open_msg_bytes).is_ok());
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -257,14 +257,14 @@ async fn test_bgp_peer_statemachine_outbound_conn() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the server sends a keepalive after the open message.
|
||||
|
||||
let mut ka_buf = vec![0u8; 65536];
|
||||
conn.read(&mut ka_buf).unwrap();
|
||||
let _ = conn.read(&mut ka_buf).unwrap();
|
||||
let response_ka_message = codec
|
||||
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
|
||||
.unwrap();
|
||||
@ -272,7 +272,7 @@ async fn test_bgp_peer_statemachine_outbound_conn() {
|
||||
match response_ka_message.unwrap().payload {
|
||||
BGPSubmessage::KeepaliveMessage(_ka) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,7 +330,7 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
assert!(conn.write_all(open_msg_bytes).is_ok());
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -347,14 +347,14 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the server sends a keepalive after the open message.
|
||||
|
||||
let mut ka_buf = vec![0u8; 65536];
|
||||
conn.read(&mut ka_buf).unwrap();
|
||||
let _ = conn.read(&mut ka_buf).unwrap();
|
||||
let response_ka_message = codec
|
||||
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
|
||||
.unwrap();
|
||||
@ -362,7 +362,7 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
match response_ka_message.unwrap().payload {
|
||||
BGPSubmessage::KeepaliveMessage(_ka) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,7 +383,7 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
assert!(conn.write_all(open_msg_bytes).is_ok());
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -400,14 +400,14 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the server sends a keepalive after the open message.
|
||||
|
||||
let mut ka_buf = vec![0u8; 65536];
|
||||
conn.read(&mut ka_buf).unwrap();
|
||||
let _ = conn.read(&mut ka_buf).unwrap();
|
||||
let response_ka_message = codec
|
||||
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
|
||||
.unwrap();
|
||||
@ -415,7 +415,7 @@ async fn test_bgp_peer_statemachine_outbound_reconnection() {
|
||||
match response_ka_message.unwrap().payload {
|
||||
BGPSubmessage::KeepaliveMessage(_ka) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,7 +487,7 @@ async fn test_bgp_listener_known_peer_inbound_reconnection() {
|
||||
assert!(conn.write_all(open_msg_bytes).is_ok());
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -504,14 +504,14 @@ async fn test_bgp_listener_known_peer_inbound_reconnection() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the server sends a keepalive after the open message.
|
||||
|
||||
let mut ka_buf = vec![0u8; 65536];
|
||||
conn.read(&mut ka_buf).unwrap();
|
||||
let _ = conn.read(&mut ka_buf).unwrap();
|
||||
let response_ka_message = codec
|
||||
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
|
||||
.unwrap();
|
||||
@ -519,7 +519,7 @@ async fn test_bgp_listener_known_peer_inbound_reconnection() {
|
||||
match response_ka_message.unwrap().payload {
|
||||
BGPSubmessage::KeepaliveMessage(_ka) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,7 +540,7 @@ async fn test_bgp_listener_known_peer_inbound_reconnection() {
|
||||
|
||||
let mut open_buf = vec![0u8; 65536];
|
||||
conn.set_read_timeout(Some(Duration::from_secs(3))).unwrap();
|
||||
conn.read(&mut open_buf).unwrap();
|
||||
let _ = conn.read(&mut open_buf).unwrap();
|
||||
|
||||
let mut codec = bgp_packet::messages::Codec {
|
||||
ctx: ParserContext {
|
||||
@ -557,7 +557,7 @@ async fn test_bgp_listener_known_peer_inbound_reconnection() {
|
||||
match response_open_msg.unwrap().payload {
|
||||
BGPSubmessage::OpenMessage(_open) => {}
|
||||
_ => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -607,7 +607,7 @@ async fn test_multi_instance_announce() {
|
||||
config_b.grpc_addr = Some("[::]:9182".to_owned());
|
||||
config_b.peers[0].asn = 65535;
|
||||
config_b.peers[0].port = Some(9179);
|
||||
config_b.peers[0].name = "config-a-peer".to_owned();
|
||||
config_b.peers[0].name = "config-a-peer".to_string();
|
||||
|
||||
let mut server_a = Server::new(config_a);
|
||||
server_a.start(true).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user