use core::net::Ipv4Addr;
use super::{OspfLsa, OspfLsaBody, OspfLsaHeader, OSPF_LSA_OPAQUE_AREA};
use crate::{CrafterError, Result};
const OSPF_OPAQUE_TLV_HEADER_LEN: usize = 4;
const OSPF_OPAQUE_TLV_ALIGNMENT: usize = 4;
fn padded_value_len(len: usize) -> usize {
len.div_ceil(OSPF_OPAQUE_TLV_ALIGNMENT) * OSPF_OPAQUE_TLV_ALIGNMENT
}
pub const OSPF_TE_OPAQUE_TYPE: u8 = 1;
pub const OSPF_TE_TLV_ROUTER_ADDRESS: u16 = 1;
pub const OSPF_TE_TLV_LINK: u16 = 2;
pub const OSPF_TE_SUBTLV_LINK_TYPE: u16 = 1;
pub const OSPF_TE_SUBTLV_LINK_ID: u16 = 2;
pub const OSPF_TE_SUBTLV_LOCAL_INTERFACE_IP: u16 = 3;
pub const OSPF_TE_SUBTLV_REMOTE_INTERFACE_IP: u16 = 4;
pub const OSPF_TE_SUBTLV_TE_METRIC: u16 = 5;
pub const OSPF_TE_SUBTLV_MAX_BANDWIDTH: u16 = 6;
pub const OSPF_TE_LINK_TYPE_POINT_TO_POINT: u8 = 1;
pub const OSPF_TE_LINK_TYPE_MULTI_ACCESS: u8 = 2;
pub const OSPF_OPAQUE_TYPE_ROUTER_INFORMATION: u8 = 4;
pub const OSPF_RI_TLV_ROUTER_FUNCTIONAL_CAPABILITIES: u16 = 1;
pub const OSPF_RI_TLV_ROUTER_INFORMATIONAL_CAPABILITIES: u16 = 2;
pub fn opaque_type(header: &OspfLsaHeader) -> u8 {
header.link_state_id_value().octets()[0]
}
pub fn opaque_id(header: &OspfLsaHeader) -> u32 {
let octets = header.link_state_id_value().octets();
u32::from_be_bytes([0, octets[1], octets[2], octets[3]])
}
pub fn opaque_link_state_id(opaque_type: u8, opaque_id: u32) -> Ipv4Addr {
let id = opaque_id.to_be_bytes();
Ipv4Addr::new(opaque_type, id[1], id[2], id[3])
}
impl OspfLsaHeader {
pub fn opaque_link_state_id(self, opaque_type: u8, opaque_id: u32) -> Self {
self.link_state_id(opaque_link_state_id(opaque_type, opaque_id))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OspfOpaqueTlv {
tlv_type: u16,
value: Vec<u8>,
}
impl OspfOpaqueTlv {
pub fn new(tlv_type: u16, value: impl Into<Vec<u8>>) -> Self {
Self {
tlv_type,
value: value.into(),
}
}
pub fn te_router_address(router_address: impl Into<Ipv4Addr>) -> Self {
Self::new(
OSPF_TE_TLV_ROUTER_ADDRESS,
router_address.into().octets().to_vec(),
)
}
pub fn ri_router_functional_capabilities(capabilities: u32) -> Self {
Self::new(
OSPF_RI_TLV_ROUTER_FUNCTIONAL_CAPABILITIES,
capabilities.to_be_bytes().to_vec(),
)
}
pub fn tlv_type(&self) -> u16 {
self.tlv_type
}
pub fn value(&self) -> &[u8] {
&self.value
}
pub(crate) fn encoded_len(&self) -> usize {
OSPF_OPAQUE_TLV_HEADER_LEN + padded_value_len(self.value.len())
}
pub fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.tlv_type.to_be_bytes());
out.extend_from_slice(&(self.value.len() as u16).to_be_bytes());
out.extend_from_slice(&self.value);
let pad = padded_value_len(self.value.len()) - self.value.len();
out.extend(core::iter::repeat(0u8).take(pad));
}
pub fn decode(bytes: &[u8]) -> Result<(OspfOpaqueTlv, usize)> {
if bytes.len() < OSPF_OPAQUE_TLV_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"ospf opaque tlv",
OSPF_OPAQUE_TLV_HEADER_LEN,
bytes.len(),
));
}
let tlv_type = u16::from_be_bytes([bytes[0], bytes[1]]);
let value_len = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
let padded = padded_value_len(value_len);
let consumed = OSPF_OPAQUE_TLV_HEADER_LEN + padded;
if bytes.len() < consumed {
return Err(CrafterError::buffer_too_short(
"ospf opaque tlv value",
consumed,
bytes.len(),
));
}
let value =
bytes[OSPF_OPAQUE_TLV_HEADER_LEN..OSPF_OPAQUE_TLV_HEADER_LEN + value_len].to_vec();
Ok((OspfOpaqueTlv::new(tlv_type, value), consumed))
}
}
#[derive(Debug, Clone, Default)]
pub struct OspfTeLinkTlv {
sub_tlvs: Vec<OspfOpaqueTlv>,
}
impl OspfTeLinkTlv {
pub fn new() -> Self {
Self {
sub_tlvs: Vec::new(),
}
}
pub fn sub_tlv(mut self, sub_tlv: OspfOpaqueTlv) -> Self {
self.sub_tlvs.push(sub_tlv);
self
}
pub fn link_type(self, link_type: u8) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_LINK_TYPE,
vec![link_type],
))
}
pub fn link_id(self, link_id: impl Into<Ipv4Addr>) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_LINK_ID,
link_id.into().octets().to_vec(),
))
}
pub fn local_interface_ip(self, address: impl Into<Ipv4Addr>) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_LOCAL_INTERFACE_IP,
address.into().octets().to_vec(),
))
}
pub fn remote_interface_ip(self, address: impl Into<Ipv4Addr>) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_REMOTE_INTERFACE_IP,
address.into().octets().to_vec(),
))
}
pub fn te_metric(self, metric: u32) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_TE_METRIC,
metric.to_be_bytes().to_vec(),
))
}
pub fn max_bandwidth(self, bytes_per_second: f32) -> Self {
self.sub_tlv(OspfOpaqueTlv::new(
OSPF_TE_SUBTLV_MAX_BANDWIDTH,
bytes_per_second.to_be_bytes().to_vec(),
))
}
pub fn build(&self) -> OspfOpaqueTlv {
let mut value = Vec::new();
for sub_tlv in &self.sub_tlvs {
sub_tlv.encode(&mut value);
}
OspfOpaqueTlv::new(OSPF_TE_TLV_LINK, value)
}
}
#[derive(Debug, Clone, Default)]
pub struct OspfOpaqueLsa {
tlvs: Vec<OspfOpaqueTlv>,
}
impl OspfOpaqueLsa {
pub fn new() -> Self {
Self { tlvs: Vec::new() }
}
pub(crate) fn from_decoded_parts(tlvs: Vec<OspfOpaqueTlv>) -> Self {
Self { tlvs }
}
pub fn tlv(mut self, tlv: OspfOpaqueTlv) -> Self {
self.tlvs.push(tlv);
self
}
pub fn tlvs_value(&self) -> &[OspfOpaqueTlv] {
&self.tlvs
}
pub fn summary(&self) -> String {
format!("tlvs={}", self.tlvs.len())
}
pub(crate) fn encoded_len(&self) -> usize {
self.tlvs.iter().map(OspfOpaqueTlv::encoded_len).sum()
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
for tlv in &self.tlvs {
tlv.encode(out);
}
}
pub fn te_area_lsa(
advertising_router: impl Into<Ipv4Addr>,
opaque_id: u32,
tlvs: impl IntoIterator<Item = OspfOpaqueTlv>,
) -> OspfLsa {
let mut opaque = OspfOpaqueLsa::new();
for tlv in tlvs {
opaque = opaque.tlv(tlv);
}
let header = OspfLsaHeader::new()
.ls_type(OSPF_LSA_OPAQUE_AREA)
.opaque_link_state_id(OSPF_TE_OPAQUE_TYPE, opaque_id)
.advertising_router(advertising_router.into());
OspfLsa::new(header, OspfLsaBody::Opaque(opaque))
}
pub fn ri_lsa(
ls_type: u8,
advertising_router: impl Into<Ipv4Addr>,
opaque_id: u32,
tlvs: impl IntoIterator<Item = OspfOpaqueTlv>,
) -> OspfLsa {
let mut opaque = OspfOpaqueLsa::new();
for tlv in tlvs {
opaque = opaque.tlv(tlv);
}
let header = OspfLsaHeader::new()
.ls_type(ls_type)
.opaque_link_state_id(OSPF_OPAQUE_TYPE_ROUTER_INFORMATION, opaque_id)
.advertising_router(advertising_router.into());
OspfLsa::new(header, OspfLsaBody::Opaque(opaque))
}
pub fn ri_area_lsa(
advertising_router: impl Into<Ipv4Addr>,
opaque_id: u32,
tlvs: impl IntoIterator<Item = OspfOpaqueTlv>,
) -> OspfLsa {
Self::ri_lsa(OSPF_LSA_OPAQUE_AREA, advertising_router, opaque_id, tlvs)
}
}
#[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_OPAQUE_AREA,
};
use crate::protocols::ospf::packet::link_state_update::OspfLinkStateUpdate;
#[test]
fn ospf_opaque_lsa_area_scope_two_tlvs_round_trips_in_lsu() {
let tlv_aligned = OspfOpaqueTlv::new(0x0001, vec![0xaa, 0xbb, 0xcc, 0xdd]);
let tlv_padded = OspfOpaqueTlv::new(0x0002, vec![0x11, 0x22, 0x33, 0x44, 0x55]);
assert_eq!(tlv_aligned.encoded_len(), OSPF_OPAQUE_TLV_HEADER_LEN + 4);
assert_eq!(tlv_padded.encoded_len(), OSPF_OPAQUE_TLV_HEADER_LEN + 8);
let opaque = OspfOpaqueLsa::new()
.tlv(tlv_aligned.clone())
.tlv(tlv_padded.clone());
assert_eq!(opaque.tlvs_value().len(), 2);
let mut body = Vec::new();
opaque.encode(&mut body);
assert_eq!(body.len(), opaque.encoded_len());
let expected: Vec<u8> = vec![
0x00, 0x01, 0x00, 0x04, 0xaa, 0xbb, 0xcc, 0xdd, 0x00, 0x02, 0x00, 0x05, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x00,
];
assert_eq!(body, expected);
assert_eq!(&body[12..17], &[0x11, 0x22, 0x33, 0x44, 0x55]);
assert_eq!(&body[17..20], &[0x00, 0x00, 0x00]);
assert_eq!(body.len(), 20);
let (decoded_aligned, consumed_aligned) =
OspfOpaqueTlv::decode(&body).expect("the first TLV decodes");
assert_eq!(consumed_aligned, tlv_aligned.encoded_len());
assert_eq!(decoded_aligned, tlv_aligned);
let (decoded_padded, consumed_padded) =
OspfOpaqueTlv::decode(&body[consumed_aligned..]).expect("the second TLV decodes");
assert_eq!(consumed_padded, tlv_padded.encoded_len());
assert_eq!(decoded_padded, tlv_padded);
let opaque_type = 10u8;
let opaque_id = 0x0001_0203u32;
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_OPAQUE_AREA)
.opaque_link_state_id(opaque_type, opaque_id)
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Opaque(opaque),
);
assert_eq!(opaque_type_of(&lsa.header), opaque_type);
assert_eq!(opaque_id_of(&lsa.header), opaque_id);
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());
assert_eq!(lsa_bytes[3], OSPF_LSA_OPAQUE_AREA);
assert_eq!(lsa_bytes[4], opaque_type);
assert_eq!(&lsa_bytes[5..8], &[0x01, 0x02, 0x03]);
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 Opaque-LSA"
);
}
#[test]
fn ospf_te_area_opaque_lsa_round_trips_through_generic_decode() {
use crate::protocols::ospf::decode::append_ospf_packet;
use crate::protocols::ospf::lsa::opaque_type as lsa_opaque_type;
use crate::protocols::ospf::packet::link_state_update::OspfLinkStateUpdate;
use crate::protocols::ospf::{OspfBody, Ospfv2};
use crate::Packet;
let router_address = Ipv4Addr::new(192, 0, 2, 1);
let router_tlv = OspfOpaqueTlv::te_router_address(router_address);
assert_eq!(router_tlv.tlv_type(), OSPF_TE_TLV_ROUTER_ADDRESS);
assert_eq!(router_tlv.value(), &[192, 0, 2, 1]);
let link_id = Ipv4Addr::new(198, 51, 100, 7);
let link_tlv = OspfTeLinkTlv::new()
.link_type(OSPF_TE_LINK_TYPE_POINT_TO_POINT)
.link_id(link_id)
.build();
assert_eq!(link_tlv.tlv_type(), OSPF_TE_TLV_LINK);
let expected_link_value: Vec<u8> = vec![
0x00,
0x01,
0x00,
0x01, OSPF_TE_LINK_TYPE_POINT_TO_POINT,
0x00,
0x00,
0x00, 0x00,
0x02,
0x00,
0x04, 198,
51,
100,
7,
];
assert_eq!(link_tlv.value(), expected_link_value.as_slice());
let (link_type_sub, consumed) =
OspfOpaqueTlv::decode(link_tlv.value()).expect("the Link Type sub-TLV decodes");
assert_eq!(link_type_sub.tlv_type(), OSPF_TE_SUBTLV_LINK_TYPE);
assert_eq!(link_type_sub.value(), &[OSPF_TE_LINK_TYPE_POINT_TO_POINT]);
let (link_id_sub, _) = OspfOpaqueTlv::decode(&link_tlv.value()[consumed..])
.expect("the Link ID sub-TLV decodes");
assert_eq!(link_id_sub.tlv_type(), OSPF_TE_SUBTLV_LINK_ID);
assert_eq!(link_id_sub.value(), &link_id.octets());
let opaque_id = 0x0000_002au32;
let lsa = OspfOpaqueLsa::te_area_lsa(
router_address,
opaque_id,
[router_tlv.clone(), link_tlv.clone()],
);
assert_eq!(lsa.header.ls_type_value(), OSPF_LSA_OPAQUE_AREA);
assert_eq!(opaque_type_of(&lsa.header), OSPF_TE_OPAQUE_TYPE);
assert_eq!(opaque_id_of(&lsa.header), opaque_id);
let bytes = Packet::from_layer(
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.clone());
}),
)
.compile()
.expect("a Link State Update with a TE Opaque-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the TE Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
let decoded_lsa = &decoded_lsas[0];
assert_eq!(decoded_lsa.header.ls_type_value(), OSPF_LSA_OPAQUE_AREA);
assert_eq!(lsa_opaque_type(&decoded_lsa.header), OSPF_TE_OPAQUE_TYPE);
let opaque = match &decoded_lsa.body {
OspfLsaBody::Opaque(opaque) => opaque,
other => panic!("expected an Opaque-LSA body, got {other:?}"),
};
let tlvs = opaque.tlvs_value();
assert_eq!(tlvs.len(), 2);
assert_eq!(tlvs[0], router_tlv);
assert_eq!(tlvs[1], link_tlv);
let (decoded_link_type_sub, consumed) =
OspfOpaqueTlv::decode(tlvs[1].value()).expect("decoded Link Type sub-TLV re-parses");
assert_eq!(decoded_link_type_sub, link_type_sub);
let (decoded_link_id_sub, _) = OspfOpaqueTlv::decode(&tlvs[1].value()[consumed..])
.expect("decoded Link ID sub-TLV re-parses");
assert_eq!(decoded_link_id_sub, link_id_sub);
let recompiled = decoded
.compile()
.expect("the decoded TE Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_ri_area_opaque_lsa_round_trips_through_generic_decode() {
use crate::protocols::ospf::decode::append_ospf_packet;
use crate::protocols::ospf::lsa::opaque_type as lsa_opaque_type;
use crate::protocols::ospf::packet::link_state_update::OspfLinkStateUpdate;
use crate::protocols::ospf::{OspfBody, Ospfv2};
use crate::Packet;
let capabilities = 0x0000_002au32;
let caps_tlv = OspfOpaqueTlv::ri_router_functional_capabilities(capabilities);
assert_eq!(
caps_tlv.tlv_type(),
OSPF_RI_TLV_ROUTER_FUNCTIONAL_CAPABILITIES
);
assert_eq!(caps_tlv.value(), &capabilities.to_be_bytes());
let advertising_router = Ipv4Addr::new(192, 0, 2, 1);
let opaque_id = 0x0000_0007u32;
let lsa = OspfOpaqueLsa::ri_area_lsa(advertising_router, opaque_id, [caps_tlv.clone()]);
assert_eq!(lsa.header.ls_type_value(), OSPF_LSA_OPAQUE_AREA);
assert_eq!(
opaque_type_of(&lsa.header),
OSPF_OPAQUE_TYPE_ROUTER_INFORMATION
);
assert_eq!(opaque_id_of(&lsa.header), opaque_id);
let bytes = Packet::from_layer(
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.clone());
}),
)
.compile()
.expect("a Link State Update with an RI Opaque-LSA compiles");
let lsu_payload = &bytes.as_bytes()[crate::protocols::ospf::OSPF_HEADER_LEN..];
let lsa_bytes = &lsu_payload[4..];
assert_eq!(lsa_bytes[3], OSPF_LSA_OPAQUE_AREA);
assert_eq!(lsa_bytes[4], OSPF_OPAQUE_TYPE_ROUTER_INFORMATION);
assert_eq!(&lsa_bytes[5..8], &[0x00, 0x00, 0x07]);
assert!(
fletcher16_valid(lsa_bytes),
"auto-filled Fletcher checksum should validate over the RI Opaque-LSA"
);
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the RI Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
let decoded_lsa = &decoded_lsas[0];
assert_eq!(decoded_lsa.header.ls_type_value(), OSPF_LSA_OPAQUE_AREA);
assert_eq!(
lsa_opaque_type(&decoded_lsa.header),
OSPF_OPAQUE_TYPE_ROUTER_INFORMATION
);
let opaque = match &decoded_lsa.body {
OspfLsaBody::Opaque(opaque) => opaque,
other => panic!("expected an Opaque-LSA body, got {other:?}"),
};
let tlvs = opaque.tlvs_value();
assert_eq!(tlvs.len(), 1);
assert_eq!(tlvs[0], caps_tlv);
let recompiled = decoded
.compile()
.expect("the decoded RI Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
fn opaque_type_of(header: &OspfLsaHeader) -> u8 {
super::opaque_type(header)
}
fn opaque_id_of(header: &OspfLsaHeader) -> u32 {
super::opaque_id(header)
}
#[test]
fn ospf_opaque_tlv_decode_rejects_short_buffers() {
let short_prefix = [0x00, 0x01, 0x00];
let err = OspfOpaqueTlv::decode(&short_prefix).expect_err("a short prefix must error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf opaque tlv");
assert_eq!(required, OSPF_OPAQUE_TLV_HEADER_LEN);
assert_eq!(available, 3);
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
let short_value = [0x00, 0x02, 0x00, 0x05, 0x11, 0x22];
let err = OspfOpaqueTlv::decode(&short_value).expect_err("a short value must error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf opaque tlv value");
assert_eq!(required, OSPF_OPAQUE_TLV_HEADER_LEN + 8);
assert_eq!(available, 6);
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
}