Cleanup clippy warnings.
Some checks failed
Rust / build (push) Has been cancelled

This commit is contained in:
Rayhaan Jaufeerally
2024-12-08 22:09:00 +00:00
parent cc3842b384
commit b0f2995ed8
18 changed files with 1547 additions and 189 deletions

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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 =

View File

@ -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,
}
}
}

View File

@ -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]

View File

@ -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);

View File

@ -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), \

View File

@ -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

View File

@ -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 {}

View 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(())
}
}

View File

@ -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;

View File

@ -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!();
}

View File

@ -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 {}

View File

@ -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;

View File

@ -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(

View File

@ -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,

View File

@ -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();