use crate::field::Field;
use crate::protocols::ospf::lsa::OspfLsa;
const OSPF_LSU_COUNT_LEN: usize = 4;
#[derive(Debug, Clone)]
pub struct OspfLinkStateUpdate {
num_lsas: Field<u32>,
lsas: Vec<OspfLsa>,
}
impl OspfLinkStateUpdate {
pub fn new() -> Self {
Self {
num_lsas: Field::unset(),
lsas: Vec::new(),
}
}
#[allow(dead_code)]
pub(crate) fn from_decoded_parts(num_lsas: u32, lsas: Vec<OspfLsa>) -> Self {
Self {
num_lsas: Field::user(num_lsas),
lsas,
}
}
pub fn lsa(mut self, lsa: OspfLsa) -> Self {
self.lsas.push(lsa);
self
}
pub fn lsas<I>(mut self, lsas: I) -> Self
where
I: IntoIterator<Item = OspfLsa>,
{
self.lsas.extend(lsas);
self
}
pub fn num_lsas(mut self, num_lsas: u32) -> Self {
self.num_lsas.set_user(num_lsas);
self
}
pub fn lsas_value(&self) -> &[OspfLsa] {
&self.lsas
}
pub fn num_lsas_value(&self) -> u32 {
self.num_lsas
.value()
.copied()
.unwrap_or(self.lsas.len() as u32)
}
pub(crate) fn encoded_len(&self) -> usize {
OSPF_LSU_COUNT_LEN + self.lsas.iter().map(OspfLsa::encoded_len).sum::<usize>()
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.num_lsas_value().to_be_bytes());
for lsa in &self.lsas {
lsa.encode(out);
}
}
}
impl Default for OspfLinkStateUpdate {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::fletcher16_valid;
use crate::packet::{Layer, Packet};
use crate::protocols::ospf::lsa::{
OspfLsaBody, OspfLsaHeader, OSPF_LSA_HEADER_LEN, OSPF_LSA_ROUTER,
};
use crate::protocols::ospf::{Ospfv2, OSPF_HEADER_LEN, OSPF_TYPE_LINK_STATE_UPDATE};
use core::net::Ipv4Addr;
#[test]
fn ospf_link_state_update_body_compiles_with_one_raw_lsa() {
let lsa_body = [0xde, 0xad, 0xbe, 0xef, 0x01, 0x02];
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_ROUTER)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Raw(lsa_body.to_vec()),
);
let lsu = OspfLinkStateUpdate::new().lsa(lsa);
assert_eq!(lsu.lsas_value().len(), 1);
assert_eq!(lsu.num_lsas_value(), 1);
let mut body = Vec::new();
lsu.encode(&mut body);
assert_eq!(body.len(), lsu.encoded_len());
assert_eq!(&body[0..4], &1u32.to_be_bytes());
let lsa_bytes = &body[OSPF_LSU_COUNT_LEN..];
assert_eq!(lsa_bytes.len(), OSPF_LSA_HEADER_LEN + lsa_body.len());
let expected_lsa_len = (OSPF_LSA_HEADER_LEN + lsa_body.len()) as u16;
assert_eq!(&lsa_bytes[18..20], &expected_lsa_len.to_be_bytes());
assert!(
fletcher16_valid(lsa_bytes),
"auto-filled Fletcher checksum should validate over the LSA"
);
let ospf = Ospfv2::link_state_update().with_link_state_update(|u| {
*u = lsu.clone();
});
let bytes = Packet::from_layer(ospf)
.compile()
.expect("LinkStateUpdate compiles");
let total = OSPF_HEADER_LEN + body.len();
assert_eq!(bytes.len(), total);
assert_eq!(&bytes[2..4], &(total as u16).to_be_bytes());
assert_eq!(bytes[1], OSPF_TYPE_LINK_STATE_UPDATE);
assert_eq!(&bytes[OSPF_HEADER_LEN..], body.as_slice());
let layer = Ospfv2::link_state_update().with_link_state_update(|u| *u = lsu);
assert_eq!(layer.encoded_len(), total);
}
#[test]
fn ospf_link_state_update_user_count_survives_encode() {
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_ROUTER)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1)),
OspfLsaBody::Raw(vec![0x00, 0x01]),
);
let lsu = OspfLinkStateUpdate::new().lsa(lsa).num_lsas(5);
assert_eq!(lsu.num_lsas_value(), 5);
let mut body = Vec::new();
lsu.encode(&mut body);
assert_eq!(&body[0..4], &5u32.to_be_bytes());
}
}