use core::net::Ipv4Addr;
use crate::field::Field;
pub const OSPF_ROUTER_LSA_FLAG_B: u8 = 0x01;
pub const OSPF_ROUTER_LSA_FLAG_E: u8 = 0x02;
pub const OSPF_ROUTER_LSA_FLAG_V: u8 = 0x04;
pub const OSPF_ROUTER_LINK_POINT_TO_POINT: u8 = 1;
pub const OSPF_ROUTER_LINK_TRANSIT: u8 = 2;
pub const OSPF_ROUTER_LINK_STUB: u8 = 3;
pub const OSPF_ROUTER_LINK_VIRTUAL: u8 = 4;
pub fn ospf_router_link_type_name(link_type: u8) -> &'static str {
match link_type {
OSPF_ROUTER_LINK_POINT_TO_POINT => "PointToPoint",
OSPF_ROUTER_LINK_TRANSIT => "Transit",
OSPF_ROUTER_LINK_STUB => "Stub",
OSPF_ROUTER_LINK_VIRTUAL => "Virtual",
_ => "Unknown",
}
}
const OSPF_ROUTER_LSA_FIXED_LEN: usize = 4;
const OSPF_ROUTER_LINK_FIXED_LEN: usize = 12;
const OSPF_ROUTER_LINK_TOS_LEN: usize = 4;
#[derive(Debug, Clone)]
pub struct OspfRouterLinkTos {
tos: u8,
metric: u16,
}
impl OspfRouterLinkTos {
pub fn new(tos: u8, metric: u16) -> Self {
Self { tos, metric }
}
pub fn tos_value(&self) -> u8 {
self.tos
}
pub fn metric_value(&self) -> u16 {
self.metric
}
fn encode(&self, out: &mut Vec<u8>) {
out.push(self.tos);
out.push(0);
out.extend_from_slice(&self.metric.to_be_bytes());
}
}
#[derive(Debug, Clone)]
pub struct OspfRouterLink {
link_id: Ipv4Addr,
link_data: Ipv4Addr,
link_type: u8,
metric: u16,
tos: Vec<OspfRouterLinkTos>,
}
impl OspfRouterLink {
pub fn new(
link_id: impl Into<Ipv4Addr>,
link_data: impl Into<Ipv4Addr>,
link_type: u8,
metric: u16,
) -> Self {
Self {
link_id: link_id.into(),
link_data: link_data.into(),
link_type,
metric,
tos: Vec::new(),
}
}
pub fn tos(mut self, tos: OspfRouterLinkTos) -> Self {
self.tos.push(tos);
self
}
pub fn tos_entries<I>(mut self, entries: I) -> Self
where
I: IntoIterator<Item = OspfRouterLinkTos>,
{
self.tos.extend(entries);
self
}
pub fn link_id_value(&self) -> Ipv4Addr {
self.link_id
}
pub fn link_data_value(&self) -> Ipv4Addr {
self.link_data
}
pub fn link_type_value(&self) -> u8 {
self.link_type
}
pub fn metric_value(&self) -> u16 {
self.metric
}
pub fn tos_value(&self) -> &[OspfRouterLinkTos] {
&self.tos
}
pub fn summary(&self) -> String {
format!(
"type={}, id={}, data={}, metric={}",
ospf_router_link_type_name(self.link_type),
self.link_id,
self.link_data,
self.metric,
)
}
fn encoded_len(&self) -> usize {
OSPF_ROUTER_LINK_FIXED_LEN + self.tos.len() * OSPF_ROUTER_LINK_TOS_LEN
}
fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.link_id.octets());
out.extend_from_slice(&self.link_data.octets());
out.push(self.link_type);
out.push(self.tos.len() as u8);
out.extend_from_slice(&self.metric.to_be_bytes());
for tos in &self.tos {
tos.encode(out);
}
}
}
#[derive(Debug, Clone)]
pub struct OspfRouterLsa {
flags: Field<u8>,
num_links: Field<u16>,
links: Vec<OspfRouterLink>,
}
impl OspfRouterLsa {
pub fn new() -> Self {
Self {
flags: Field::defaulted(0),
num_links: Field::unset(),
links: Vec::new(),
}
}
pub(crate) fn from_decoded_parts(
flags: u8,
num_links: u16,
links: Vec<OspfRouterLink>,
) -> Self {
Self {
flags: Field::user(flags),
num_links: Field::user(num_links),
links,
}
}
pub fn virtual_link(mut self) -> Self {
let flags = self.flags_value() | OSPF_ROUTER_LSA_FLAG_V;
self.flags.set_user(flags);
self
}
pub fn external(mut self) -> Self {
let flags = self.flags_value() | OSPF_ROUTER_LSA_FLAG_E;
self.flags.set_user(flags);
self
}
pub fn border(mut self) -> Self {
let flags = self.flags_value() | OSPF_ROUTER_LSA_FLAG_B;
self.flags.set_user(flags);
self
}
pub fn flags(mut self, flags: u8) -> Self {
self.flags.set_user(flags);
self
}
pub fn link(mut self, link: OspfRouterLink) -> Self {
self.links.push(link);
self
}
pub fn links<I>(mut self, links: I) -> Self
where
I: IntoIterator<Item = OspfRouterLink>,
{
self.links.extend(links);
self
}
pub fn num_links(mut self, num_links: u16) -> Self {
self.num_links.set_user(num_links);
self
}
pub fn flags_value(&self) -> u8 {
self.flags.value().copied().unwrap_or(0)
}
pub fn is_virtual(&self) -> bool {
self.flags_value() & OSPF_ROUTER_LSA_FLAG_V != 0
}
pub fn is_external(&self) -> bool {
self.flags_value() & OSPF_ROUTER_LSA_FLAG_E != 0
}
pub fn is_border(&self) -> bool {
self.flags_value() & OSPF_ROUTER_LSA_FLAG_B != 0
}
pub fn router_flags_summary(&self) -> String {
let flags = self.flags_value();
let mut labels: Vec<&str> = Vec::new();
if flags & OSPF_ROUTER_LSA_FLAG_V != 0 {
labels.push("V");
}
if flags & OSPF_ROUTER_LSA_FLAG_E != 0 {
labels.push("E");
}
if flags & OSPF_ROUTER_LSA_FLAG_B != 0 {
labels.push("B");
}
labels.join("|")
}
pub fn num_links_value(&self) -> u16 {
self.num_links
.value()
.copied()
.unwrap_or(self.links.len() as u16)
}
pub fn links_value(&self) -> &[OspfRouterLink] {
&self.links
}
pub fn summary(&self) -> String {
let labels = self.router_flags_summary();
let labels = if labels.is_empty() {
"-".to_string()
} else {
labels
};
format!("flags={} links={}", labels, self.num_links_value())
}
pub(crate) fn encoded_len(&self) -> usize {
OSPF_ROUTER_LSA_FIXED_LEN
+ self
.links
.iter()
.map(OspfRouterLink::encoded_len)
.sum::<usize>()
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.push(self.flags_value());
out.push(0);
out.extend_from_slice(&self.num_links_value().to_be_bytes());
for link in &self.links {
link.encode(out);
}
}
}
impl Default for OspfRouterLsa {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::fletcher16_valid;
use crate::protocols::ospf::lsa::{
OspfLsa, OspfLsaBody, OspfLsaHeader, OSPF_LSA_HEADER_LEN, OSPF_LSA_ROUTER,
};
use crate::protocols::ospf::packet::link_state_update::OspfLinkStateUpdate;
#[test]
fn ospf_router_lsa_two_links_with_tos_round_trips_in_lsu() {
let router = OspfRouterLsa::new()
.border()
.link(OspfRouterLink::new(
Ipv4Addr::new(192, 0, 2, 2),
Ipv4Addr::new(198, 51, 100, 1),
OSPF_ROUTER_LINK_POINT_TO_POINT,
10,
))
.link(
OspfRouterLink::new(
Ipv4Addr::new(198, 51, 100, 0),
Ipv4Addr::new(255, 255, 255, 0),
OSPF_ROUTER_LINK_STUB,
20,
)
.tos(OspfRouterLinkTos::new(2, 30)),
);
assert_eq!(router.flags_value(), OSPF_ROUTER_LSA_FLAG_B);
assert_eq!(router.num_links_value(), 2);
assert_eq!(router.links_value().len(), 2);
let mut body = Vec::new();
router.encode(&mut body);
assert_eq!(body.len(), router.encoded_len());
let expected: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x02, 192, 0, 2, 2, 198, 51, 100, 1, 0x01, 0x00, 0x00, 0x0a,
198, 51, 100, 0, 255, 255, 255, 0, 0x03, 0x01, 0x00, 0x14, 0x02, 0x00, 0x00, 0x1e,
];
assert_eq!(body, expected);
assert_eq!(&body[2..4], &2u16.to_be_bytes());
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::Router(router),
);
let lsu = OspfLinkStateUpdate::new().lsa(lsa);
let mut update = Vec::new();
lsu.encode(&mut update);
assert_eq!(&update[0..4], &1u32.to_be_bytes());
let lsa_bytes = &update[4..];
assert_eq!(lsa_bytes.len(), OSPF_LSA_HEADER_LEN + expected.len());
let expected_lsa_len = (OSPF_LSA_HEADER_LEN + expected.len()) as u16;
assert_eq!(&lsa_bytes[18..20], &expected_lsa_len.to_be_bytes());
assert_eq!(&lsa_bytes[OSPF_LSA_HEADER_LEN..], expected.as_slice());
assert!(
fletcher16_valid(lsa_bytes),
"auto-filled Fletcher checksum should validate over the Router-LSA"
);
}
#[test]
fn ospf_router_lsa_inspection_describes_flags_and_links() {
use crate::packet::Layer;
use crate::protocols::ospf::Ospfv2;
let router = OspfRouterLsa::new()
.border()
.external()
.link(OspfRouterLink::new(
Ipv4Addr::new(192, 0, 2, 2),
Ipv4Addr::new(198, 51, 100, 1),
OSPF_ROUTER_LINK_POINT_TO_POINT,
10,
))
.link(OspfRouterLink::new(
Ipv4Addr::new(198, 51, 100, 0),
Ipv4Addr::new(255, 255, 255, 0),
OSPF_ROUTER_LINK_STUB,
20,
));
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::Router(router),
);
let ospf = Ospfv2::link_state_update()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_update(|u| {
*u = OspfLinkStateUpdate::new().lsa(lsa);
});
let fields = ospf.inspection_fields();
let router_lsa = fields
.iter()
.find(|(field, _)| *field == "router_lsa")
.map(|(_, value)| value.as_str())
.expect("inspection output should carry a router_lsa pair");
assert!(
router_lsa.contains("links=2"),
"router_lsa missing link count: {router_lsa}"
);
assert!(
router_lsa.contains("flags=E|B"),
"router_lsa missing flags: {router_lsa}"
);
let router_links: Vec<&str> = fields
.iter()
.filter(|(field, _)| *field == "router_link")
.map(|(_, value)| value.as_str())
.collect();
assert_eq!(router_links.len(), 2);
assert!(
router_links[0].contains("type=PointToPoint"),
"first router_link missing the link type name: {}",
router_links[0]
);
assert!(
router_links[1].contains("type=Stub"),
"second router_link missing the link type name: {}",
router_links[1]
);
}
#[test]
fn ospf_router_flags_summary_and_accessors_agree_for_external_border() {
let router = OspfRouterLsa::new().external().border();
assert_eq!(
router.flags_value(),
OSPF_ROUTER_LSA_FLAG_E | OSPF_ROUTER_LSA_FLAG_B
);
assert!(!router.is_virtual());
assert!(router.is_external());
assert!(router.is_border());
assert_eq!(
router.is_virtual(),
router.flags_value() & OSPF_ROUTER_LSA_FLAG_V != 0
);
assert_eq!(
router.is_external(),
router.flags_value() & OSPF_ROUTER_LSA_FLAG_E != 0
);
assert_eq!(
router.is_border(),
router.flags_value() & OSPF_ROUTER_LSA_FLAG_B != 0
);
assert_eq!(router.router_flags_summary(), "E|B");
assert_eq!(OspfRouterLsa::new().router_flags_summary(), "");
}
}