use core::net::Ipv4Addr;
use crate::checksum::fletcher16_checkbytes;
use crate::field::Field;
use crate::{CrafterError, Result};
pub const OSPFV3_LSA_HEADER_LEN: usize = 20;
const OSPFV3_LSA_INITIAL_SEQUENCE_NUMBER: u32 = 0x8000_0001;
const OSPFV3_LSA_CHECKSUM_OFFSET: usize = 16;
const OSPFV3_LSA_CHECKSUM_START: usize = 2;
#[derive(Debug, Clone)]
pub struct Ospfv3LsaHeader {
ls_age: Field<u16>,
ls_type: Field<u16>,
link_state_id: Field<Ipv4Addr>,
advertising_router: Field<Ipv4Addr>,
ls_sequence_number: Field<u32>,
ls_checksum: Field<u16>,
length: Field<u16>,
}
impl Ospfv3LsaHeader {
pub fn new() -> Self {
Self {
ls_age: Field::defaulted(0),
ls_type: Field::unset(),
link_state_id: Field::unset(),
advertising_router: Field::unset(),
ls_sequence_number: Field::defaulted(OSPFV3_LSA_INITIAL_SEQUENCE_NUMBER),
ls_checksum: Field::unset(),
length: Field::unset(),
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_decoded_parts(
ls_age: u16,
ls_type: u16,
link_state_id: Ipv4Addr,
advertising_router: Ipv4Addr,
ls_sequence_number: u32,
ls_checksum: u16,
length: u16,
) -> Self {
Self {
ls_age: Field::user(ls_age),
ls_type: Field::user(ls_type),
link_state_id: Field::user(link_state_id),
advertising_router: Field::user(advertising_router),
ls_sequence_number: Field::user(ls_sequence_number),
ls_checksum: Field::user(ls_checksum),
length: Field::user(length),
}
}
pub fn ls_age(mut self, ls_age: u16) -> Self {
self.ls_age.set_user(ls_age);
self
}
pub fn ls_type(mut self, ls_type: u16) -> Self {
self.ls_type.set_user(ls_type);
self
}
pub fn link_state_id(mut self, link_state_id: impl Into<Ipv4Addr>) -> Self {
self.link_state_id.set_user(link_state_id.into());
self
}
pub fn advertising_router(mut self, advertising_router: impl Into<Ipv4Addr>) -> Self {
self.advertising_router.set_user(advertising_router.into());
self
}
pub fn ls_sequence_number(mut self, ls_sequence_number: u32) -> Self {
self.ls_sequence_number.set_user(ls_sequence_number);
self
}
pub fn ls_checksum(mut self, ls_checksum: u16) -> Self {
self.ls_checksum.set_user(ls_checksum);
self
}
pub fn length(mut self, length: u16) -> Self {
self.length.set_user(length);
self
}
pub fn ls_age_value(&self) -> u16 {
self.ls_age.value().copied().unwrap_or(0)
}
pub fn ls_type_value(&self) -> u16 {
self.ls_type.value().copied().unwrap_or(0)
}
pub fn link_state_id_value(&self) -> Ipv4Addr {
self.link_state_id
.value()
.copied()
.unwrap_or(Ipv4Addr::UNSPECIFIED)
}
pub fn advertising_router_value(&self) -> Ipv4Addr {
self.advertising_router
.value()
.copied()
.unwrap_or(Ipv4Addr::UNSPECIFIED)
}
pub fn ls_sequence_number_value(&self) -> u32 {
self.ls_sequence_number
.value()
.copied()
.unwrap_or(OSPFV3_LSA_INITIAL_SEQUENCE_NUMBER)
}
pub fn ls_checksum_value(&self) -> Option<u16> {
self.ls_checksum.value().copied()
}
pub fn length_value(&self) -> Option<u16> {
self.length.value().copied()
}
pub fn summary(&self) -> String {
let length = match self.length_value() {
Some(length) => length.to_string(),
None => "auto".to_string(),
};
format!(
"LSAv3(type=0x{:04x}, id={}, adv={}, seq=0x{:08x}, age={}, len={})",
self.ls_type_value(),
self.link_state_id_value(),
self.advertising_router_value(),
self.ls_sequence_number_value(),
self.ls_age_value(),
length,
)
}
pub fn encode_with_body(&self, body: &[u8], out: &mut Vec<u8>) {
let start = out.len();
out.extend_from_slice(&self.ls_age_value().to_be_bytes());
out.extend_from_slice(&self.ls_type_value().to_be_bytes());
out.extend_from_slice(&self.link_state_id_value().octets());
out.extend_from_slice(&self.advertising_router_value().octets());
out.extend_from_slice(&self.ls_sequence_number_value().to_be_bytes());
let pinned_checksum = self.ls_checksum.value().copied();
out.extend_from_slice(&pinned_checksum.unwrap_or(0).to_be_bytes());
let length = self
.length
.value()
.copied()
.unwrap_or((OSPFV3_LSA_HEADER_LEN + body.len()) as u16);
out.extend_from_slice(&length.to_be_bytes());
out.extend_from_slice(body);
if pinned_checksum.is_none() {
let protected = &out[start + OSPFV3_LSA_CHECKSUM_START..];
let checkbytes = fletcher16_checkbytes(
protected,
OSPFV3_LSA_CHECKSUM_OFFSET - OSPFV3_LSA_CHECKSUM_START,
);
out[start + OSPFV3_LSA_CHECKSUM_OFFSET..start + OSPFV3_LSA_CHECKSUM_OFFSET + 2]
.copy_from_slice(&checkbytes);
}
}
pub fn decode(bytes: &[u8]) -> Result<(Ospfv3LsaHeader, usize)> {
if bytes.len() < OSPFV3_LSA_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"ospfv3 lsa header",
OSPFV3_LSA_HEADER_LEN,
bytes.len(),
));
}
let ls_age = u16::from_be_bytes([bytes[0], bytes[1]]);
let ls_type = u16::from_be_bytes([bytes[2], bytes[3]]);
let link_state_id = Ipv4Addr::new(bytes[4], bytes[5], bytes[6], bytes[7]);
let advertising_router = Ipv4Addr::new(bytes[8], bytes[9], bytes[10], bytes[11]);
let ls_sequence_number = u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
let ls_checksum = u16::from_be_bytes([bytes[16], bytes[17]]);
let length = u16::from_be_bytes([bytes[18], bytes[19]]);
let header = Ospfv3LsaHeader::from_decoded_parts(
ls_age,
ls_type,
link_state_id,
advertising_router,
ls_sequence_number,
ls_checksum,
length,
);
Ok((header, length as usize))
}
}
impl Default for Ospfv3LsaHeader {
fn default() -> Self {
Self::new()
}
}
pub(crate) fn decode_ospfv3_lsa_headers(mut bytes: &[u8]) -> Result<Vec<Ospfv3LsaHeader>> {
let mut headers = Vec::new();
while !bytes.is_empty() {
if bytes.len() < OSPFV3_LSA_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"ospfv3 lsa header",
OSPFV3_LSA_HEADER_LEN,
bytes.len(),
));
}
let (header, _declared_len) = Ospfv3LsaHeader::decode(bytes)?;
headers.push(header);
bytes = &bytes[OSPFV3_LSA_HEADER_LEN..];
}
Ok(headers)
}
pub(crate) fn encode_ospfv3_lsa_headers(headers: &[Ospfv3LsaHeader], out: &mut Vec<u8>) {
for header in headers {
header.encode_with_body(&[], out);
}
}
const OSPFV3_LSA_OPTIONS_MASK: u32 = 0x00ff_ffff;
const OSPFV3_ROUTER_LSA_INTERFACE_LEN: usize = 16;
const OSPFV3_ROUTER_LSA_FIXED_LEN: usize = 4;
const OSPFV3_NETWORK_LSA_FIXED_LEN: usize = 4;
const OSPFV3_NETWORK_LSA_ROUTER_LEN: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ospfv3RouterInterface {
if_type: u8,
reserved: u8,
metric: u16,
interface_id: u32,
neighbor_interface_id: u32,
neighbor_router_id: Ipv4Addr,
}
impl Ospfv3RouterInterface {
pub fn new(
if_type: u8,
metric: u16,
interface_id: u32,
neighbor_interface_id: u32,
neighbor_router_id: impl Into<Ipv4Addr>,
) -> Self {
Self {
if_type,
reserved: 0,
metric,
interface_id,
neighbor_interface_id,
neighbor_router_id: neighbor_router_id.into(),
}
}
pub fn reserved(mut self, reserved: u8) -> Self {
self.reserved = reserved;
self
}
pub fn if_type_value(&self) -> u8 {
self.if_type
}
pub fn reserved_value(&self) -> u8 {
self.reserved
}
pub fn metric_value(&self) -> u16 {
self.metric
}
pub fn interface_id_value(&self) -> u32 {
self.interface_id
}
pub fn neighbor_interface_id_value(&self) -> u32 {
self.neighbor_interface_id
}
pub fn neighbor_router_id_value(&self) -> Ipv4Addr {
self.neighbor_router_id
}
fn encode(&self, out: &mut Vec<u8>) {
out.push(self.if_type);
out.push(self.reserved);
out.extend_from_slice(&self.metric.to_be_bytes());
out.extend_from_slice(&self.interface_id.to_be_bytes());
out.extend_from_slice(&self.neighbor_interface_id.to_be_bytes());
out.extend_from_slice(&self.neighbor_router_id.octets());
}
}
#[derive(Debug, Clone)]
pub struct Ospfv3RouterLsa {
flags: Field<u8>,
options: Field<u32>,
interfaces: Vec<Ospfv3RouterInterface>,
}
impl Ospfv3RouterLsa {
pub fn new() -> Self {
Self {
flags: Field::defaulted(0),
options: Field::defaulted(0),
interfaces: Vec::new(),
}
}
pub fn flags(mut self, flags: u8) -> Self {
self.flags.set_user(flags);
self
}
pub fn options(mut self, options: u32) -> Self {
self.options.set_user(options);
self
}
pub fn interface(mut self, interface: Ospfv3RouterInterface) -> Self {
self.interfaces.push(interface);
self
}
pub fn interfaces<I>(mut self, interfaces: I) -> Self
where
I: IntoIterator<Item = Ospfv3RouterInterface>,
{
self.interfaces.extend(interfaces);
self
}
pub fn flags_value(&self) -> u8 {
self.flags.value().copied().unwrap_or(0)
}
pub fn options_value(&self) -> u32 {
self.options.value().copied().unwrap_or(0) & OSPFV3_LSA_OPTIONS_MASK
}
pub fn interfaces_value(&self) -> &[Ospfv3RouterInterface] {
&self.interfaces
}
pub(crate) fn encoded_len(&self) -> usize {
OSPFV3_ROUTER_LSA_FIXED_LEN + self.interfaces.len() * OSPFV3_ROUTER_LSA_INTERFACE_LEN
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.push(self.flags_value());
let options = self.options_value();
out.push(((options >> 16) & 0xff) as u8);
out.push(((options >> 8) & 0xff) as u8);
out.push((options & 0xff) as u8);
for interface in &self.interfaces {
interface.encode(out);
}
}
}
impl Default for Ospfv3RouterLsa {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Ospfv3NetworkLsa {
reserved: Field<u8>,
options: Field<u32>,
attached_routers: Vec<Ipv4Addr>,
}
impl Ospfv3NetworkLsa {
pub fn new() -> Self {
Self {
reserved: Field::defaulted(0),
options: Field::defaulted(0),
attached_routers: Vec::new(),
}
}
pub fn reserved(mut self, reserved: u8) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn options(mut self, options: u32) -> Self {
self.options.set_user(options);
self
}
pub fn attached_router(mut self, router_id: impl Into<Ipv4Addr>) -> Self {
self.attached_routers.push(router_id.into());
self
}
pub fn attached_routers<I>(mut self, routers: I) -> Self
where
I: IntoIterator<Item = Ipv4Addr>,
{
self.attached_routers.extend(routers);
self
}
pub fn reserved_value(&self) -> u8 {
self.reserved.value().copied().unwrap_or(0)
}
pub fn options_value(&self) -> u32 {
self.options.value().copied().unwrap_or(0) & OSPFV3_LSA_OPTIONS_MASK
}
pub fn attached_routers_value(&self) -> &[Ipv4Addr] {
&self.attached_routers
}
pub(crate) fn encoded_len(&self) -> usize {
OSPFV3_NETWORK_LSA_FIXED_LEN + self.attached_routers.len() * OSPFV3_NETWORK_LSA_ROUTER_LEN
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.push(self.reserved_value());
let options = self.options_value();
out.push(((options >> 16) & 0xff) as u8);
out.push(((options >> 8) & 0xff) as u8);
out.push((options & 0xff) as u8);
for router in &self.attached_routers {
out.extend_from_slice(&router.octets());
}
}
}
impl Default for Ospfv3NetworkLsa {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum Ospfv3LsaBody {
Raw(Vec<u8>),
Router(Ospfv3RouterLsa),
Network(Ospfv3NetworkLsa),
}
impl Ospfv3LsaBody {
pub(crate) fn encoded_len(&self) -> usize {
match self {
Ospfv3LsaBody::Raw(body) => body.len(),
Ospfv3LsaBody::Router(router) => router.encoded_len(),
Ospfv3LsaBody::Network(network) => network.encoded_len(),
}
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
match self {
Ospfv3LsaBody::Raw(body) => out.extend_from_slice(body),
Ospfv3LsaBody::Router(router) => router.encode(out),
Ospfv3LsaBody::Network(network) => network.encode(out),
}
}
}
#[derive(Debug, Clone)]
pub struct Ospfv3Lsa {
pub header: Ospfv3LsaHeader,
pub body: Ospfv3LsaBody,
}
impl Ospfv3Lsa {
pub fn new(header: Ospfv3LsaHeader, body: Ospfv3LsaBody) -> Self {
Self { header, body }
}
pub(crate) fn encoded_len(&self) -> usize {
OSPFV3_LSA_HEADER_LEN + self.body.encoded_len()
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
let mut body_bytes = Vec::with_capacity(self.body.encoded_len());
self.body.encode(&mut body_bytes);
self.header.encode_with_body(&body_bytes, out);
}
}
const OSPFV3_LSU_COUNT_LEN: usize = 4;
#[derive(Debug, Clone)]
pub struct Ospfv3LinkStateUpdate {
num_lsas: Field<u32>,
lsas: Vec<Ospfv3Lsa>,
}
impl Ospfv3LinkStateUpdate {
pub fn new() -> Self {
Self {
num_lsas: Field::unset(),
lsas: Vec::new(),
}
}
pub fn lsa(mut self, lsa: Ospfv3Lsa) -> Self {
self.lsas.push(lsa);
self
}
pub fn lsas<I>(mut self, lsas: I) -> Self
where
I: IntoIterator<Item = Ospfv3Lsa>,
{
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) -> &[Ospfv3Lsa] {
&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 {
OSPFV3_LSU_COUNT_LEN + self.lsas.iter().map(Ospfv3Lsa::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 Ospfv3LinkStateUpdate {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::fletcher16_valid;
#[test]
fn ospfv3_lsa_header_round_trips_with_auto_length_and_checksum() {
let body = [0xde, 0xad, 0xbe, 0xef, 0x01, 0x02];
let header = Ospfv3LsaHeader::new()
.ls_age(0)
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001);
let mut out = Vec::new();
header.encode_with_body(&body, &mut out);
assert_eq!(out.len(), OSPFV3_LSA_HEADER_LEN + body.len());
assert_eq!(&out[2..4], &0x2001u16.to_be_bytes());
let expected_len = (OSPFV3_LSA_HEADER_LEN + body.len()) as u16;
assert_eq!(&out[18..20], &expected_len.to_be_bytes());
assert!(
fletcher16_valid(&out),
"auto-filled Fletcher checksum should validate over the OSPFv3 LSA"
);
let (decoded, declared_len) =
Ospfv3LsaHeader::decode(&out).expect("OSPFv3 LSA header decodes");
assert_eq!(declared_len, OSPFV3_LSA_HEADER_LEN + body.len());
assert_eq!(decoded.ls_age_value(), 0);
assert_eq!(decoded.ls_type_value(), 0x2001);
assert_eq!(decoded.link_state_id_value(), Ipv4Addr::new(192, 0, 2, 1));
assert_eq!(
decoded.advertising_router_value(),
Ipv4Addr::new(192, 0, 2, 1)
);
assert_eq!(decoded.ls_sequence_number_value(), 0x8000_0001);
assert_eq!(decoded.length_value(), Some(expected_len));
let mut reencoded = Vec::new();
decoded.encode_with_body(&body, &mut reencoded);
assert_eq!(reencoded, out);
}
#[test]
fn ospfv3_lsa_header_user_checksum_survives_encode() {
let body = [0x00, 0x01, 0x02, 0x03];
let header = Ospfv3LsaHeader::new()
.ls_type(0x2002)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 2))
.ls_checksum(0xBEEF);
let mut out = Vec::new();
header.encode_with_body(&body, &mut out);
assert_eq!(&out[16..18], &0xBEEFu16.to_be_bytes());
assert_eq!(
&out[18..20],
&((OSPFV3_LSA_HEADER_LEN + body.len()) as u16).to_be_bytes()
);
}
#[test]
fn ospfv3_lsa_header_decode_rejects_short_buffer() {
let short = [0u8; OSPFV3_LSA_HEADER_LEN - 1];
let err = Ospfv3LsaHeader::decode(&short).expect_err("a short buffer must error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospfv3 lsa header");
assert_eq!(required, OSPFV3_LSA_HEADER_LEN);
assert_eq!(available, OSPFV3_LSA_HEADER_LEN - 1);
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
#[test]
fn ospfv3_lsa_headers_list_round_trips_and_rejects_partial_trailer() {
let headers = [
Ospfv3LsaHeader::new()
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
Ospfv3LsaHeader::new()
.ls_type(0x2002)
.link_state_id(Ipv4Addr::new(192, 0, 2, 2))
.advertising_router(Ipv4Addr::new(198, 51, 100, 7))
.ls_sequence_number(0x8000_0002),
Ospfv3LsaHeader::new()
.ls_type(0x2003)
.link_state_id(Ipv4Addr::new(198, 51, 100, 0))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0003),
];
let mut out = Vec::new();
encode_ospfv3_lsa_headers(&headers, &mut out);
assert_eq!(out.len(), 3 * OSPFV3_LSA_HEADER_LEN);
let decoded = decode_ospfv3_lsa_headers(&out).expect("OSPFv3 LSA header list decodes");
assert_eq!(decoded.len(), 3);
for (original, parsed) in headers.iter().zip(decoded.iter()) {
assert_eq!(parsed.ls_age_value(), original.ls_age_value());
assert_eq!(parsed.ls_type_value(), original.ls_type_value());
assert_eq!(parsed.link_state_id_value(), original.link_state_id_value());
assert_eq!(
parsed.advertising_router_value(),
original.advertising_router_value()
);
assert_eq!(
parsed.ls_sequence_number_value(),
original.ls_sequence_number_value()
);
assert_eq!(parsed.length_value(), Some(OSPFV3_LSA_HEADER_LEN as u16));
}
let partial = &out[..2 * OSPFV3_LSA_HEADER_LEN + 10];
assert_eq!(partial.len(), 50);
let err =
decode_ospfv3_lsa_headers(partial).expect_err("a partial trailing header must error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospfv3 lsa header");
assert_eq!(required, OSPFV3_LSA_HEADER_LEN);
assert_eq!(available, 10);
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
#[test]
fn ospfv3_router_lsa_one_interface_layout_and_checksum() {
let router =
Ospfv3RouterLsa::new()
.options(0x00ff_0013)
.interface(Ospfv3RouterInterface::new(
1, 10,
0x0000_0005,
0x0000_0006,
Ipv4Addr::new(192, 0, 2, 2),
));
assert_eq!(router.options_value(), 0x00ff_0013);
assert_eq!(router.interfaces_value().len(), 1);
let mut body = Vec::new();
router.encode(&mut body);
assert_eq!(body.len(), router.encoded_len());
assert_eq!(body.len(), 20);
assert_eq!(body[0], 0x00); assert_eq!(&body[1..4], &[0xff, 0x00, 0x13]);
assert_eq!(body[4], 1); assert_eq!(body[5], 0); assert_eq!(&body[6..8], &10u16.to_be_bytes()); assert_eq!(&body[8..12], &0x0000_0005u32.to_be_bytes()); assert_eq!(&body[12..16], &0x0000_0006u32.to_be_bytes()); assert_eq!(&body[16..20], &Ipv4Addr::new(192, 0, 2, 2).octets());
let lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
Ospfv3LsaBody::Router(router),
);
assert_eq!(lsa.encoded_len(), OSPFV3_LSA_HEADER_LEN + body.len());
let mut lsa_bytes = Vec::new();
lsa.encode(&mut lsa_bytes);
assert_eq!(lsa_bytes.len(), OSPFV3_LSA_HEADER_LEN + body.len());
assert_eq!(
&lsa_bytes[18..20],
&((OSPFV3_LSA_HEADER_LEN + body.len()) as u16).to_be_bytes()
);
assert_eq!(&lsa_bytes[OSPFV3_LSA_HEADER_LEN..], body.as_slice());
assert!(
fletcher16_valid(&lsa_bytes),
"auto-filled Fletcher checksum should validate over the OSPFv3 Router-LSA"
);
}
#[test]
fn ospfv3_network_lsa_two_attached_routers_layout_and_checksum() {
let network = Ospfv3NetworkLsa::new()
.options(0x0000_0013)
.attached_router(Ipv4Addr::new(192, 0, 2, 1))
.attached_router(Ipv4Addr::new(192, 0, 2, 2));
assert_eq!(network.options_value(), 0x0000_0013);
assert_eq!(network.attached_routers_value().len(), 2);
let mut body = Vec::new();
network.encode(&mut body);
assert_eq!(body.len(), network.encoded_len());
assert_eq!(body.len(), 12);
assert_eq!(body[0], 0x00); assert_eq!(&body[1..4], &[0x00, 0x00, 0x13]); assert_eq!(&body[4..8], &Ipv4Addr::new(192, 0, 2, 1).octets());
assert_eq!(&body[8..12], &Ipv4Addr::new(192, 0, 2, 2).octets());
let lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2002)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0002),
Ospfv3LsaBody::Network(network),
);
assert_eq!(lsa.encoded_len(), OSPFV3_LSA_HEADER_LEN + body.len());
let mut lsa_bytes = Vec::new();
lsa.encode(&mut lsa_bytes);
assert_eq!(
&lsa_bytes[18..20],
&((OSPFV3_LSA_HEADER_LEN + body.len()) as u16).to_be_bytes()
);
assert_eq!(&lsa_bytes[OSPFV3_LSA_HEADER_LEN..], body.as_slice());
assert!(
fletcher16_valid(&lsa_bytes),
"auto-filled Fletcher checksum should validate over the OSPFv3 Network-LSA"
);
}
#[test]
fn ospfv3_link_state_update_body_carries_router_and_network_lsas() {
let router_lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
Ospfv3LsaBody::Router(Ospfv3RouterLsa::new().interface(Ospfv3RouterInterface::new(
1,
10,
0x0000_0005,
0x0000_0006,
Ipv4Addr::new(192, 0, 2, 2),
))),
);
let network_lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2002)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0002),
Ospfv3LsaBody::Network(
Ospfv3NetworkLsa::new()
.attached_router(Ipv4Addr::new(192, 0, 2, 1))
.attached_router(Ipv4Addr::new(192, 0, 2, 2)),
),
);
let mut router_bytes = Vec::new();
router_lsa.encode(&mut router_bytes);
let mut network_bytes = Vec::new();
network_lsa.encode(&mut network_bytes);
let lsu = Ospfv3LinkStateUpdate::new()
.lsa(router_lsa)
.lsa(network_lsa);
assert_eq!(lsu.lsas_value().len(), 2);
assert_eq!(lsu.num_lsas_value(), 2);
let mut body = Vec::new();
lsu.encode(&mut body);
assert_eq!(body.len(), lsu.encoded_len());
assert_eq!(&body[0..4], &2u32.to_be_bytes());
assert_eq!(
&body[OSPFV3_LSU_COUNT_LEN..OSPFV3_LSU_COUNT_LEN + router_bytes.len()],
router_bytes.as_slice()
);
assert_eq!(
&body[OSPFV3_LSU_COUNT_LEN + router_bytes.len()..],
network_bytes.as_slice()
);
assert!(fletcher16_valid(&router_bytes));
assert!(fletcher16_valid(&network_bytes));
}
#[test]
fn ospfv3_link_state_update_user_count_survives_encode() {
let lsu = Ospfv3LinkStateUpdate::new()
.lsa(Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1)),
Ospfv3LsaBody::Raw(vec![0x00, 0x01]),
))
.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());
}
#[test]
fn ospfv3_link_state_update_over_ipv6_round_trips() {
use crate::packet::{NetworkLayer, Packet};
use crate::protocols::ip::v6::Ipv6;
use crate::protocols::ospf::{Ospfv3, OSPFV3_HEADER_LEN, OSPFV3_TYPE_LINK_STATE_UPDATE};
use core::net::Ipv6Addr;
let router_lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2001)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
Ospfv3LsaBody::Router(Ospfv3RouterLsa::new().interface(Ospfv3RouterInterface::new(
1,
10,
0x0000_0005,
0x0000_0006,
Ipv4Addr::new(192, 0, 2, 2),
))),
);
let network_lsa = Ospfv3Lsa::new(
Ospfv3LsaHeader::new()
.ls_type(0x2002)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0002),
Ospfv3LsaBody::Network(
Ospfv3NetworkLsa::new()
.attached_router(Ipv4Addr::new(192, 0, 2, 1))
.attached_router(Ipv4Addr::new(192, 0, 2, 2)),
),
);
let lsu = Ospfv3LinkStateUpdate::new()
.lsa(router_lsa)
.lsa(network_lsa);
let mut body = Vec::new();
lsu.encode(&mut body);
let src: Ipv6Addr = "2001:db8::1".parse().unwrap();
let dst: Ipv6Addr = "2001:db8::2".parse().unwrap();
let ospfv3 = Ospfv3::link_state_update()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_update(|u| *u = lsu);
let bytes = (Ipv6::new().src(src).dst(dst) / ospfv3)
.compile()
.expect("Ipv6 / Ospfv3 LSU compiles");
let body_start = bytes.as_bytes().len() - body.len();
assert_eq!(&bytes.as_bytes()[body_start..], body.as_slice());
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes.as_bytes())
.expect("the default registry decodes the OSPFv3 LSU over IPv6");
let layer = decoded
.layer::<Ospfv3>()
.expect("the decoded packet exposes a typed Ospfv3 layer");
assert_eq!(layer.packet_type_value(), OSPFV3_TYPE_LINK_STATE_UPDATE);
let ospfv3_start = bytes.as_bytes().len() - (OSPFV3_HEADER_LEN + body.len());
assert_eq!(
&bytes.as_bytes()[ospfv3_start + OSPFV3_HEADER_LEN..],
body.as_slice()
);
let recompiled = decoded
.compile()
.expect("the decoded OSPFv3 LSU re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
}