implement complete UpdateMessage

This commit is contained in:
Masato Imai
2025-02-25 18:01:56 +00:00
parent c66434a9ea
commit a8bc90eade
4 changed files with 205 additions and 4 deletions

View File

@ -27,3 +27,10 @@ pub struct ConvertbgpMessageToBytesError {
#[from]
source: anyhow::Error,
}
#[derive(Error, Debug)]
#[error(transparent)]
pub struct ConstructIpv4NetworkError {
#[from]
source: anyhow::Error,
}

View File

@ -5,6 +5,7 @@ use crate::bgp_type::AutonomousSystemNumber;
use crate::error::ConvertBytesToBgpMessageError;
use crate::path_attribute::{AsPath, Origin, PathAttribute};
use crate::routing::Ipv4Network;
use anyhow::Context;
use bytes::{BufMut, BytesMut};
use super::header::{Header, MessageType};
@ -90,7 +91,42 @@ impl TryFrom<BytesMut> for UpdateMessage {
type Error = ConvertBytesToBgpMessageError;
fn try_from(bytes: BytesMut) -> Result<Self, Self::Error> {
todo!();
let header = Header::try_from(BytesMut::from(&bytes[0..19]))?;
let withdrawn_routes_length: u16 = u16::from_be_bytes(bytes[19..21].try_into().context(
format!("cannot convert to withdrawn_routes_length: {:?}", &bytes),
)?);
let withdrawn_routes_end_index = 21 + withdrawn_routes_length as usize;
let withdrawn_routes_bytes = &bytes[21..withdrawn_routes_end_index];
let withdrawn_routes = Ipv4Network::from_u8_slice(withdrawn_routes_bytes)?;
let path_attributes_start_index = withdrawn_routes_end_index + 2;
let total_path_attribute_length = u16::from_be_bytes(
bytes[withdrawn_routes_end_index..path_attributes_start_index]
.try_into()
.context(format!(
"cannot convert to total_path_attribute_length: {:?}",
&bytes
))?,
);
let path_attributes_bytes = &bytes[path_attributes_start_index
..path_attributes_start_index + total_path_attribute_length as usize];
let path_attributes = Arc::new(PathAttribute::from_u8_slice(path_attributes_bytes)?);
let network_layer_reachability_information_start_index =
path_attributes_start_index + total_path_attribute_length as usize;
let network_layer_reachability_information = Ipv4Network::from_u8_slice(
&bytes[network_layer_reachability_information_start_index..],
)?;
Ok(Self {
header,
withdrawn_routes,
withdrawn_routes_length,
path_attributes,
path_attributes_length: total_path_attribute_length,
network_layer_reachability_information,
})
}
}
@ -114,7 +150,7 @@ mod tests {
let update_message = UpdateMessage::new(
update_message_path_attributes,
vec!["10.100.220.0.24".parse().unwrap()],
vec!["10.100.220.0/24".parse().unwrap()],
vec![],
);

View File

@ -1,6 +1,8 @@
use anyhow::Context;
use bytes::{BufMut, BytesMut};
use crate::bgp_type::AutonomousSystemNumber;
use crate::error::ConvertBytesToBgpMessageError;
use std::collections::BTreeSet;
use std::net::Ipv4Addr;
@ -61,6 +63,50 @@ impl From<&AsPath> for BytesMut {
}
}
impl TryFrom<u8> for Origin {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Origin::Igp),
1 => Ok(Origin::Egp),
2 => Ok(Origin::Incomplete),
_ => Err(anyhow::anyhow!("cannot convert to Origin: {:?}", value)),
}
}
}
impl TryFrom<&[u8]> for AsPath {
type Error = anyhow::Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value[0] {
1 => {
let mut ases = BTreeSet::new();
let mut i = 2;
while i < value.len() {
ases.insert(u16::from_be_bytes(value[i..i + 2].try_into()?).into());
i += 2;
}
Ok(AsPath::AsSet(ases))
}
2 => {
let mut ases = Vec::new();
let mut i = 2;
while i < value.len() {
ases.push(u16::from_be_bytes(value[i..i + 2].try_into()?).into());
i += 2;
}
Ok(AsPath::AsSequence(ases))
}
_ => Err(anyhow::anyhow!(format!(
"cannot convert to AsPath: {:?}",
value
))),
}
}
}
impl PathAttribute {
pub fn bytes_len(&self) -> usize {
let path_attribute_value_length = match self {
@ -78,6 +124,51 @@ impl PathAttribute {
length + 1
}
}
pub fn from_u8_slice(
bytes: &[u8],
) -> Result<Vec<PathAttribute>, ConvertBytesToBgpMessageError> {
let mut path_attributes = vec![];
let mut i = 0;
while bytes.len() > i {
let attribute_flag = bytes[i];
let attribute_length_octets = ((attribute_flag & 0b0001_0000) >> 4) + 1;
let attribute_type_code = bytes[i + 1];
let attribute_length = if attribute_length_octets == 1 {
bytes[i + 2] as usize
} else {
u16::from_be_bytes(
bytes[i + 2..i + 4]
.try_into()
.context("cannot convert to attribute_length")?,
) as usize
};
let attribute_start_index = i + 1 + attribute_length_octets as usize + 1;
let attribute_end_index = attribute_start_index + attribute_length;
let path_attribute = match attribute_type_code {
1 => PathAttribute::Origin(Origin::try_from(bytes[attribute_start_index])?),
2 => PathAttribute::AsPath(AsPath::try_from(
&bytes[attribute_start_index..attribute_end_index],
)?),
3 => {
let addr = Ipv4Addr::new(
bytes[attribute_start_index],
bytes[attribute_start_index + 1],
bytes[attribute_start_index + 2],
bytes[attribute_start_index + 3],
);
PathAttribute::NextHop(addr)
}
_ => PathAttribute::DontKnow(bytes[i..attribute_end_index].to_owned()),
};
path_attributes.push(path_attribute);
i = attribute_end_index;
}
Ok(path_attributes)
}
}
impl From<&PathAttribute> for BytesMut {

View File

@ -1,5 +1,5 @@
use std::{
net::IpAddr,
net::{IpAddr, Ipv4Addr},
ops::{Deref, DerefMut},
str::FromStr,
};
@ -10,7 +10,10 @@ use futures::TryStreamExt;
use rtnetlink::{new_connection, IpVersion};
use tracing::info;
use crate::{config::Config, error::ConfigParseError};
use crate::{
config::Config,
error::{ConfigParseError, ConstructIpv4NetworkError, ConvertBytesToBgpMessageError},
};
#[derive(Debug, PartialEq, Eq, Clone)]
struct LocRib;
@ -74,6 +77,70 @@ impl From<&Ipv4Network> for BytesMut {
}
impl Ipv4Network {
pub fn new(addr: Ipv4Addr, prefix: u8) -> Result<Self, ConstructIpv4NetworkError> {
let network = ipnetwork::Ipv4Network::new(addr, prefix).context(format!(
"cannot create Ipv4Network: {:?}, {:?}",
addr, prefix
))?;
Ok(Self(network))
}
pub fn from_u8_slice(bytes: &[u8]) -> Result<Vec<Self>, ConvertBytesToBgpMessageError> {
let mut networks = vec![];
let mut i = 0;
while bytes.len() > i {
let prefix = bytes[i];
i += 1;
match prefix {
0 => {
networks.push(Ipv4Network::new(Ipv4Addr::new(0, 0, 0, 0), prefix).context("")?);
}
1..=8 => {
networks.push(
Ipv4Network::new(Ipv4Addr::new(bytes[i], 0, 0, 0), prefix).context("")?,
);
i += 1;
}
9..=16 => {
networks.push(
Ipv4Network::new(Ipv4Addr::new(bytes[i], bytes[i + 1], 0, 0), prefix)
.context("")?,
);
i += 2;
}
17..=24 => {
networks.push(
Ipv4Network::new(
Ipv4Addr::new(bytes[i], bytes[i + 1], bytes[i + 2], 0),
prefix,
)
.context("")?,
);
i += 3;
}
25..=32 => {
networks.push(
Ipv4Network::new(
Ipv4Addr::new(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]),
prefix,
)
.context("")?,
);
i += 4;
}
_ => {
return Err(ConvertBytesToBgpMessageError::from(anyhow::anyhow!(
"Invalid prefix length: {:?}",
prefix
)));
}
}
}
Ok(networks)
}
pub fn bytes_len(&self) -> usize {
match self.prefix() {
0 => 1,