mirror of
https://github.com/mii443/miibgpd.git
synced 2025-08-22 15:55:26 +00:00
implement complete UpdateMessage
This commit is contained in:
@ -27,3 +27,10 @@ pub struct ConvertbgpMessageToBytesError {
|
||||
#[from]
|
||||
source: anyhow::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct ConstructIpv4NetworkError {
|
||||
#[from]
|
||||
source: anyhow::Error,
|
||||
}
|
||||
|
@ -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![],
|
||||
);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user