use core::net::Ipv4Addr;
use crate::checksum::internet_checksum_chunks;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::Packet;
use super::constants::{
OSPF_AUTH_LEN, OSPF_AUTYPE_CRYPTOGRAPHIC, OSPF_HEADER_LEN, OSPF_TYPE_DATABASE_DESCRIPTION,
OSPF_TYPE_HELLO, OSPF_TYPE_LINK_STATE_ACK, OSPF_TYPE_LINK_STATE_REQUEST,
OSPF_TYPE_LINK_STATE_UPDATE,
};
use super::lsa::{
decode_lsa_headers, OspfAsExternalLsa, OspfExternalTos, OspfLsa, OspfLsaBody, OspfLsaHeader,
OspfNetworkLsa, OspfNssaLsa, OspfOpaqueLsa, OspfOpaqueTlv, OspfRouterLink, OspfRouterLinkTos,
OspfRouterLsa, OspfSummaryLsa, OspfSummaryTos, OSPF_AS_EXTERNAL_FLAG_E, OSPF_LSA_AS_EXTERNAL,
OSPF_LSA_HEADER_LEN, OSPF_LSA_NETWORK, OSPF_LSA_NSSA, OSPF_LSA_OPAQUE_AREA, OSPF_LSA_OPAQUE_AS,
OSPF_LSA_OPAQUE_LINK_LOCAL, OSPF_LSA_ROUTER, OSPF_LSA_SUMMARY_ASBR, OSPF_LSA_SUMMARY_IP,
};
use super::packet::{
OspfDatabaseDescription, OspfHello, OspfLinkStateAck, OspfLinkStateRequest,
OspfLinkStateRequestEntry, OspfLinkStateUpdate,
};
use super::{OspfBody, OspfChecksumStatus, Ospfv2};
const OSPF_HELLO_FIXED_LEN: usize = 20;
const OSPF_DD_FIXED_LEN: usize = 8;
const OSPF_LSR_ENTRY_LEN: usize = 12;
const OSPF_LSU_COUNT_LEN: usize = 4;
const OSPF_ROUTER_LSA_FIXED_LEN: usize = 4;
const OSPF_ROUTER_LINK_FIXED_LEN: usize = 12;
const OSPF_ROUTER_LINK_TOS_LEN: usize = 4;
const OSPF_NETWORK_LSA_MASK_LEN: usize = 4;
const OSPF_NETWORK_LSA_ROUTER_LEN: usize = 4;
const OSPF_SUMMARY_LSA_MASK_LEN: usize = 4;
const OSPF_SUMMARY_LSA_TOS_LEN: usize = 4;
const OSPF_AS_EXTERNAL_LSA_MASK_LEN: usize = 4;
const OSPF_AS_EXTERNAL_LSA_ENTRY_LEN: usize = 12;
const OSPF_NSSA_LSA_MASK_LEN: usize = 4;
const OSPF_NSSA_LSA_ENTRY_LEN: usize = 12;
#[allow(dead_code)]
pub(crate) fn append_ospf_packet(packet: Packet, bytes: &[u8]) -> Result<Packet> {
append_ospf_packet_with_checksum_validation(packet, bytes, true)
}
#[allow(dead_code)]
pub(crate) fn append_ospf_packet_with_checksum_validation(
mut packet: Packet,
bytes: &[u8],
validate_checksum: bool,
) -> Result<Packet> {
if bytes.len() < OSPF_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"ospf header",
OSPF_HEADER_LEN,
bytes.len(),
));
}
let version = bytes[0];
let packet_type = bytes[1];
let packet_length = u16::from_be_bytes([bytes[2], bytes[3]]);
let router_id = Ipv4Addr::new(bytes[4], bytes[5], bytes[6], bytes[7]);
let area_id = Ipv4Addr::new(bytes[8], bytes[9], bytes[10], bytes[11]);
let checksum = u16::from_be_bytes([bytes[12], bytes[13]]);
let autype = u16::from_be_bytes([bytes[14], bytes[15]]);
let mut authentication = [0u8; OSPF_AUTH_LEN];
authentication.copy_from_slice(&bytes[16..OSPF_HEADER_LEN]);
let declared = packet_length as usize;
let body_end = if (OSPF_HEADER_LEN..=bytes.len()).contains(&declared) {
declared
} else {
bytes.len()
};
let body_bytes = &bytes[OSPF_HEADER_LEN..body_end];
let body = match packet_type {
OSPF_TYPE_HELLO => OspfBody::Hello(decode_hello_body(body_bytes)?),
OSPF_TYPE_DATABASE_DESCRIPTION => {
OspfBody::DatabaseDescription(decode_database_description_body(body_bytes)?)
}
OSPF_TYPE_LINK_STATE_REQUEST => {
OspfBody::LinkStateRequest(decode_link_state_request_body(body_bytes)?)
}
OSPF_TYPE_LINK_STATE_UPDATE => {
OspfBody::LinkStateUpdate(decode_link_state_update_body(body_bytes)?)
}
OSPF_TYPE_LINK_STATE_ACK => OspfBody::LinkStateAck(decode_link_state_ack_body(body_bytes)?),
other => OspfBody::Unknown {
type_code: other,
body: body_bytes.to_vec(),
},
};
let checksum_status = if validate_checksum && autype != OSPF_AUTYPE_CRYPTOGRAPHIC {
let zero_checksum = [0u8; 2];
let computed = internet_checksum_chunks([
&bytes[..12],
&zero_checksum[..],
&bytes[14..16],
&bytes[OSPF_HEADER_LEN..body_end],
]);
if computed == checksum {
OspfChecksumStatus::Valid
} else {
OspfChecksumStatus::Invalid
}
} else {
OspfChecksumStatus::NotChecked
};
let auth_trailer = if autype == OSPF_AUTYPE_CRYPTOGRAPHIC && body_end < bytes.len() {
let trailing = &bytes[body_end..];
let auth_data_len = usize::from(authentication[3]);
if trailing.len() >= auth_data_len {
trailing.to_vec()
} else {
Vec::new()
}
} else {
Vec::new()
};
let ospf = Ospfv2 {
version: Field::user(version),
packet_type: Field::user(packet_type),
packet_length: Field::user(packet_length),
router_id: Field::user(router_id),
area_id: Field::user(area_id),
checksum: Field::user(checksum),
autype: Field::user(autype),
authentication: Field::user(authentication),
checksum_status,
crypto_auth: None,
auth_trailer,
body,
};
packet = packet.push(ospf);
Ok(packet)
}
fn decode_hello_body(body: &[u8]) -> Result<OspfHello> {
if body.len() < OSPF_HELLO_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ospf hello",
OSPF_HELLO_FIXED_LEN,
body.len(),
));
}
let network_mask = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
let hello_interval = u16::from_be_bytes([body[4], body[5]]);
let options = body[6];
let router_priority = body[7];
let router_dead_interval = u32::from_be_bytes([body[8], body[9], body[10], body[11]]);
let designated_router = Ipv4Addr::new(body[12], body[13], body[14], body[15]);
let backup_designated_router = Ipv4Addr::new(body[16], body[17], body[18], body[19]);
let neighbor_region = &body[OSPF_HELLO_FIXED_LEN..];
if neighbor_region.len() % 4 != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.hello.neighbors",
"neighbor list length must be a multiple of 4",
));
}
let neighbors = neighbor_region
.chunks_exact(4)
.map(|chunk| Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]))
.collect();
Ok(OspfHello::from_decoded_parts(
network_mask,
hello_interval,
options,
router_priority,
router_dead_interval,
designated_router,
backup_designated_router,
neighbors,
))
}
fn decode_database_description_body(body: &[u8]) -> Result<OspfDatabaseDescription> {
if body.len() < OSPF_DD_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ospf database description",
OSPF_DD_FIXED_LEN,
body.len(),
));
}
let interface_mtu = u16::from_be_bytes([body[0], body[1]]);
let options = body[2];
let flags = body[3];
let dd_sequence_number = u32::from_be_bytes([body[4], body[5], body[6], body[7]]);
let lsa_headers = decode_lsa_headers(&body[OSPF_DD_FIXED_LEN..])?;
Ok(OspfDatabaseDescription::from_decoded_parts(
interface_mtu,
options,
flags,
dd_sequence_number,
lsa_headers,
))
}
fn decode_link_state_request_body(body: &[u8]) -> Result<OspfLinkStateRequest> {
if body.len() % OSPF_LSR_ENTRY_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.link_state_request.entries",
"request list length must be a multiple of 12",
));
}
let entries = body
.chunks_exact(OSPF_LSR_ENTRY_LEN)
.map(|chunk| {
let ls_type = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let link_state_id = Ipv4Addr::new(chunk[4], chunk[5], chunk[6], chunk[7]);
let advertising_router = Ipv4Addr::new(chunk[8], chunk[9], chunk[10], chunk[11]);
OspfLinkStateRequestEntry::new(ls_type, link_state_id, advertising_router)
})
.collect();
Ok(OspfLinkStateRequest::from_decoded_parts(entries))
}
fn decode_link_state_update_body(body: &[u8]) -> Result<OspfLinkStateUpdate> {
if body.len() < OSPF_LSU_COUNT_LEN {
return Err(CrafterError::buffer_too_short(
"ospf link state update",
OSPF_LSU_COUNT_LEN,
body.len(),
));
}
let num_lsas = u32::from_be_bytes([body[0], body[1], body[2], body[3]]);
let mut rest = &body[OSPF_LSU_COUNT_LEN..];
let mut lsas = Vec::with_capacity(num_lsas as usize);
let mut parsed: u64 = 0;
while !rest.is_empty() && parsed < u64::from(num_lsas) {
let (header, length) = OspfLsaHeader::decode(rest)?;
if length < OSPF_LSA_HEADER_LEN {
return Err(CrafterError::invalid_field_value(
"ospf.lsa.length",
"LSA length is below the 20-octet header minimum",
));
}
if length > rest.len() {
return Err(CrafterError::buffer_too_short(
"ospf lsa",
length,
rest.len(),
));
}
let lsa_body = &rest[OSPF_LSA_HEADER_LEN..length];
let body = decode_lsa_body(header.ls_type_value(), lsa_body)?;
lsas.push(OspfLsa::new(header, body));
rest = &rest[length..];
parsed += 1;
}
Ok(OspfLinkStateUpdate::from_decoded_parts(num_lsas, lsas))
}
fn decode_lsa_body(ls_type: u8, body: &[u8]) -> Result<OspfLsaBody> {
match ls_type {
OSPF_LSA_ROUTER => Ok(OspfLsaBody::Router(decode_router_lsa_body(body)?)),
OSPF_LSA_NETWORK => Ok(OspfLsaBody::Network(decode_network_lsa_body(body)?)),
OSPF_LSA_SUMMARY_IP | OSPF_LSA_SUMMARY_ASBR => {
Ok(OspfLsaBody::Summary(decode_summary_lsa_body(body)?))
}
OSPF_LSA_AS_EXTERNAL => Ok(OspfLsaBody::AsExternal(decode_as_external_lsa_body(body)?)),
OSPF_LSA_NSSA => Ok(OspfLsaBody::Nssa(decode_nssa_lsa_body(body)?)),
OSPF_LSA_OPAQUE_LINK_LOCAL | OSPF_LSA_OPAQUE_AREA | OSPF_LSA_OPAQUE_AS => {
Ok(OspfLsaBody::Opaque(decode_opaque_lsa_body(body)?))
}
_ => Ok(OspfLsaBody::Raw(body.to_vec())),
}
}
fn decode_opaque_lsa_body(body: &[u8]) -> Result<OspfOpaqueLsa> {
let mut rest = body;
let mut tlvs = Vec::new();
while !rest.is_empty() {
let (tlv, consumed) = OspfOpaqueTlv::decode(rest)?;
tlvs.push(tlv);
rest = &rest[consumed..];
}
Ok(OspfOpaqueLsa::from_decoded_parts(tlvs))
}
fn decode_router_lsa_body(body: &[u8]) -> Result<OspfRouterLsa> {
if body.len() < OSPF_ROUTER_LSA_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ospf router-lsa",
OSPF_ROUTER_LSA_FIXED_LEN,
body.len(),
));
}
let flags = body[0];
let num_links = u16::from_be_bytes([body[2], body[3]]);
let mut rest = &body[OSPF_ROUTER_LSA_FIXED_LEN..];
let mut links = Vec::with_capacity(num_links as usize);
for _ in 0..num_links {
if rest.len() < OSPF_ROUTER_LINK_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ospf router-lsa link",
OSPF_ROUTER_LINK_FIXED_LEN,
rest.len(),
));
}
let link_id = Ipv4Addr::new(rest[0], rest[1], rest[2], rest[3]);
let link_data = Ipv4Addr::new(rest[4], rest[5], rest[6], rest[7]);
let link_type = rest[8];
let num_tos = rest[9] as usize;
let metric = u16::from_be_bytes([rest[10], rest[11]]);
let link_len = OSPF_ROUTER_LINK_FIXED_LEN + num_tos * OSPF_ROUTER_LINK_TOS_LEN;
if rest.len() < link_len {
return Err(CrafterError::buffer_too_short(
"ospf router-lsa link",
link_len,
rest.len(),
));
}
let mut tos_entries = Vec::with_capacity(num_tos);
let mut tos_bytes = &rest[OSPF_ROUTER_LINK_FIXED_LEN..link_len];
for _ in 0..num_tos {
let tos = tos_bytes[0];
let tos_metric = u16::from_be_bytes([tos_bytes[2], tos_bytes[3]]);
tos_entries.push(OspfRouterLinkTos::new(tos, tos_metric));
tos_bytes = &tos_bytes[OSPF_ROUTER_LINK_TOS_LEN..];
}
links.push(
OspfRouterLink::new(link_id, link_data, link_type, metric).tos_entries(tos_entries),
);
rest = &rest[link_len..];
}
Ok(OspfRouterLsa::from_decoded_parts(flags, num_links, links))
}
fn decode_network_lsa_body(body: &[u8]) -> Result<OspfNetworkLsa> {
if body.len() < OSPF_NETWORK_LSA_MASK_LEN {
return Err(CrafterError::buffer_too_short(
"ospf network-lsa",
OSPF_NETWORK_LSA_MASK_LEN,
body.len(),
));
}
let network_mask = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
let router_region = &body[OSPF_NETWORK_LSA_MASK_LEN..];
if router_region.len() % OSPF_NETWORK_LSA_ROUTER_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.network_lsa.attached_routers",
"attached router list length must be a multiple of 4",
));
}
let attached_routers = router_region
.chunks_exact(OSPF_NETWORK_LSA_ROUTER_LEN)
.map(|chunk| Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]))
.collect();
Ok(OspfNetworkLsa::from_decoded_parts(
network_mask,
attached_routers,
))
}
fn decode_summary_lsa_body(body: &[u8]) -> Result<OspfSummaryLsa> {
if body.len() < OSPF_SUMMARY_LSA_MASK_LEN {
return Err(CrafterError::buffer_too_short(
"ospf summary-lsa",
OSPF_SUMMARY_LSA_MASK_LEN,
body.len(),
));
}
let network_mask = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
let tos_region = &body[OSPF_SUMMARY_LSA_MASK_LEN..];
if tos_region.len() % OSPF_SUMMARY_LSA_TOS_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.summary_lsa.entries",
"TOS metric list length must be a multiple of 4",
));
}
let entries = tos_region
.chunks_exact(OSPF_SUMMARY_LSA_TOS_LEN)
.map(|chunk| {
let tos = chunk[0];
let metric =
(u32::from(chunk[1]) << 16) | (u32::from(chunk[2]) << 8) | u32::from(chunk[3]);
OspfSummaryTos::new(tos, metric)
})
.collect();
Ok(OspfSummaryLsa::from_decoded_parts(network_mask, entries))
}
fn decode_as_external_lsa_body(body: &[u8]) -> Result<OspfAsExternalLsa> {
if body.len() < OSPF_AS_EXTERNAL_LSA_MASK_LEN {
return Err(CrafterError::buffer_too_short(
"ospf as-external-lsa",
OSPF_AS_EXTERNAL_LSA_MASK_LEN,
body.len(),
));
}
let network_mask = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
let entry_region = &body[OSPF_AS_EXTERNAL_LSA_MASK_LEN..];
if entry_region.len() % OSPF_AS_EXTERNAL_LSA_ENTRY_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.as_external_lsa.entries",
"external metric list length must be a multiple of 12",
));
}
let entries = entry_region
.chunks_exact(OSPF_AS_EXTERNAL_LSA_ENTRY_LEN)
.map(|chunk| {
let e_bit = chunk[0] & OSPF_AS_EXTERNAL_FLAG_E != 0;
let tos = chunk[0] & 0x7f;
let metric =
(u32::from(chunk[1]) << 16) | (u32::from(chunk[2]) << 8) | u32::from(chunk[3]);
let forwarding_address = Ipv4Addr::new(chunk[4], chunk[5], chunk[6], chunk[7]);
let external_route_tag = u32::from_be_bytes([chunk[8], chunk[9], chunk[10], chunk[11]]);
OspfExternalTos::new(e_bit, tos, metric, forwarding_address, external_route_tag)
})
.collect();
Ok(OspfAsExternalLsa::from_decoded_parts(network_mask, entries))
}
fn decode_nssa_lsa_body(body: &[u8]) -> Result<OspfNssaLsa> {
if body.len() < OSPF_NSSA_LSA_MASK_LEN {
return Err(CrafterError::buffer_too_short(
"ospf nssa-lsa",
OSPF_NSSA_LSA_MASK_LEN,
body.len(),
));
}
let network_mask = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
let entry_region = &body[OSPF_NSSA_LSA_MASK_LEN..];
if entry_region.len() % OSPF_NSSA_LSA_ENTRY_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ospf.nssa_lsa.entries",
"external metric list length must be a multiple of 12",
));
}
let entries = entry_region
.chunks_exact(OSPF_NSSA_LSA_ENTRY_LEN)
.map(|chunk| {
let e_bit = chunk[0] & OSPF_AS_EXTERNAL_FLAG_E != 0;
let tos = chunk[0] & 0x7f;
let metric =
(u32::from(chunk[1]) << 16) | (u32::from(chunk[2]) << 8) | u32::from(chunk[3]);
let forwarding_address = Ipv4Addr::new(chunk[4], chunk[5], chunk[6], chunk[7]);
let external_route_tag = u32::from_be_bytes([chunk[8], chunk[9], chunk[10], chunk[11]]);
OspfExternalTos::new(e_bit, tos, metric, forwarding_address, external_route_tag)
})
.collect();
Ok(OspfNssaLsa::from_decoded_parts(network_mask, entries))
}
fn decode_link_state_ack_body(body: &[u8]) -> Result<OspfLinkStateAck> {
let lsa_headers = decode_lsa_headers(body)?;
Ok(OspfLinkStateAck::from_decoded_parts(lsa_headers))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::CrafterError;
use crate::protocols::ospf::constants::{
OSPF_TYPE_HELLO, OSPF_TYPE_LINK_STATE_ACK, OSPF_VERSION_2,
};
use crate::protocols::ospf::Ospfv2;
#[test]
fn ospf_decode_short_header_is_truncation_error() {
let bytes = [0u8; OSPF_HEADER_LEN - 1];
let err = append_ospf_packet(Packet::new(), &bytes)
.expect_err("a 23-byte buffer is too short for the OSPF header");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf header");
assert_eq!(required, OSPF_HEADER_LEN);
assert_eq!(available, bytes.len());
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_header_only_round_trips() {
let bytes = Packet::from_layer(
Ospfv2::new()
.packet_type(OSPF_TYPE_LINK_STATE_ACK)
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0]),
)
.compile()
.expect("header-only OSPF packet compiles");
assert_eq!(bytes.len(), OSPF_HEADER_LEN);
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("a full common header decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.version_value(), OSPF_VERSION_2);
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_ACK);
assert_eq!(ospf.router_id_value(), Ipv4Addr::new(192, 0, 2, 1));
assert_eq!(ospf.area_id_value(), Ipv4Addr::new(0, 0, 0, 0));
let recompiled = decoded.compile().expect("decoded OSPF re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_hello_with_three_neighbors_round_trips() {
use crate::protocols::ospf::OspfBody;
let dr = Ipv4Addr::new(192, 0, 2, 1);
let neighbors = [
Ipv4Addr::new(192, 0, 2, 10),
Ipv4Addr::new(192, 0, 2, 11),
Ipv4Addr::new(192, 0, 2, 12),
];
let bytes = Packet::from_layer(
Ospfv2::hello()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_hello(|h| {
*h = h
.clone()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.options(0x02)
.router_priority(1)
.designated_router(dr)
.backup_designated_router(Ipv4Addr::new(192, 0, 2, 2))
.neighbors(neighbors);
}),
)
.compile()
.expect("a Hello with three neighbors compiles");
let decoded =
append_ospf_packet(Packet::new(), bytes.as_bytes()).expect("the Hello decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_HELLO);
let hello = match &ospf.body {
OspfBody::Hello(hello) => hello,
other => panic!("expected a typed Hello body, got {other:?}"),
};
assert_eq!(hello.neighbors_value(), neighbors.as_slice());
assert_eq!(hello.designated_router_value(), dr);
let recompiled = decoded.compile().expect("decoded Hello re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_hello_body_truncated_is_truncation_error() {
let err = decode_hello_body(&[0u8; OSPF_HELLO_FIXED_LEN - 1])
.expect_err("a 19-octet Hello body is too short for the fixed fields");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf hello");
assert_eq!(required, OSPF_HELLO_FIXED_LEN);
assert_eq!(available, OSPF_HELLO_FIXED_LEN - 1);
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_hello_misaligned_neighbors_is_invalid_field() {
let body = [0u8; OSPF_HELLO_FIXED_LEN + 6];
let err =
decode_hello_body(&body).expect_err("a 6-octet neighbor region is not a multiple of 4");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.hello.neighbors");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_database_description_with_two_lsa_headers_round_trips() {
use crate::protocols::ospf::lsa::{OspfLsaHeader, OSPF_LSA_NETWORK, OSPF_LSA_ROUTER};
use crate::protocols::ospf::packet::database_description::{
OSPF_DD_FLAG_I, OSPF_DD_FLAG_M, OSPF_DD_FLAG_MS,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_DATABASE_DESCRIPTION;
let bytes = Packet::from_layer(
Ospfv2::database_description()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_database_description(|d| {
*d = d
.clone()
.interface_mtu(1500)
.options(0x02)
.dd_sequence_number(0x0000_1a2b)
.init(true)
.more(true)
.master(true)
.lsa_header(
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),
)
.lsa_header(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_NETWORK)
.link_state_id(Ipv4Addr::new(192, 0, 2, 2))
.advertising_router(Ipv4Addr::new(198, 51, 100, 7))
.ls_sequence_number(0x8000_0002),
);
}),
)
.compile()
.expect("a Database Description with two LSA headers compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Database Description decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_DATABASE_DESCRIPTION);
let dd = match &ospf.body {
OspfBody::DatabaseDescription(dd) => dd,
other => panic!("expected a typed Database Description body, got {other:?}"),
};
assert_eq!(dd.interface_mtu_value(), 1500);
assert_eq!(dd.dd_sequence_number_value(), 0x0000_1a2b);
assert_eq!(
dd.flags_value(),
OSPF_DD_FLAG_I | OSPF_DD_FLAG_M | OSPF_DD_FLAG_MS
);
assert!(dd.init_value());
assert!(dd.more_value());
assert!(dd.master_value());
let headers = dd.lsa_headers_value();
assert_eq!(headers.len(), 2);
assert_eq!(headers[0].ls_type_value(), OSPF_LSA_ROUTER);
assert_eq!(
headers[0].link_state_id_value(),
Ipv4Addr::new(192, 0, 2, 1)
);
assert_eq!(
headers[0].advertising_router_value(),
Ipv4Addr::new(192, 0, 2, 1)
);
assert_eq!(headers[0].ls_sequence_number_value(), 0x8000_0001);
assert_eq!(headers[1].ls_type_value(), OSPF_LSA_NETWORK);
assert_eq!(
headers[1].link_state_id_value(),
Ipv4Addr::new(192, 0, 2, 2)
);
assert_eq!(
headers[1].advertising_router_value(),
Ipv4Addr::new(198, 51, 100, 7)
);
assert_eq!(headers[1].ls_sequence_number_value(), 0x8000_0002);
let recompiled = decoded
.compile()
.expect("decoded Database Description re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_database_description_body_truncated_is_truncation_error() {
let err = decode_database_description_body(&[0u8; OSPF_DD_FIXED_LEN - 1])
.expect_err("a 7-octet Database Description body is too short for the fixed fields");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf database description");
assert_eq!(required, OSPF_DD_FIXED_LEN);
assert_eq!(available, OSPF_DD_FIXED_LEN - 1);
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_request_with_three_entries_round_trips() {
use crate::protocols::ospf::lsa::{OSPF_LSA_NETWORK, OSPF_LSA_ROUTER, OSPF_LSA_SUMMARY_IP};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::{OspfLinkStateRequestEntry, OSPF_TYPE_LINK_STATE_REQUEST};
let entries = [
OspfLinkStateRequestEntry::new(
u32::from(OSPF_LSA_ROUTER),
Ipv4Addr::new(192, 0, 2, 1),
Ipv4Addr::new(192, 0, 2, 1),
),
OspfLinkStateRequestEntry::new(
u32::from(OSPF_LSA_NETWORK),
Ipv4Addr::new(192, 0, 2, 2),
Ipv4Addr::new(198, 51, 100, 7),
),
OspfLinkStateRequestEntry::new(
u32::from(OSPF_LSA_SUMMARY_IP),
Ipv4Addr::new(198, 51, 100, 0),
Ipv4Addr::new(192, 0, 2, 3),
),
];
let bytes = Packet::from_layer(
Ospfv2::link_state_request()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_request(|r| {
*r = r.clone().requests(entries.iter().cloned());
}),
)
.compile()
.expect("a Link State Request with three entries compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Request decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_REQUEST);
let lsr = match &ospf.body {
OspfBody::LinkStateRequest(lsr) => lsr,
other => panic!("expected a typed Link State Request body, got {other:?}"),
};
let decoded_entries = lsr.entries_value();
assert_eq!(decoded_entries.len(), 3);
for (decoded_entry, expected) in decoded_entries.iter().zip(entries.iter()) {
assert_eq!(decoded_entry, expected);
}
let recompiled = decoded
.compile()
.expect("decoded Link State Request re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_link_state_request_misaligned_entries_is_invalid_field() {
let err = decode_link_state_request_body(&[0u8; 10])
.expect_err("a 10-octet request body is not a multiple of 12");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.link_state_request.entries");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_ack_with_three_lsa_headers_round_trips() {
use crate::protocols::ospf::lsa::{
OspfLsaHeader, OSPF_LSA_NETWORK, OSPF_LSA_ROUTER, OSPF_LSA_SUMMARY_IP,
};
use crate::protocols::ospf::OspfBody;
let headers = [
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),
OspfLsaHeader::new()
.ls_type(OSPF_LSA_NETWORK)
.link_state_id(Ipv4Addr::new(192, 0, 2, 2))
.advertising_router(Ipv4Addr::new(198, 51, 100, 7))
.ls_sequence_number(0x8000_0002),
OspfLsaHeader::new()
.ls_type(OSPF_LSA_SUMMARY_IP)
.link_state_id(Ipv4Addr::new(198, 51, 100, 0))
.advertising_router(Ipv4Addr::new(192, 0, 2, 3))
.ls_sequence_number(0x8000_0003),
];
let bytes = Packet::from_layer(
Ospfv2::link_state_ack()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_ack(|a| {
*a = a.clone().lsa_headers(headers.iter().cloned());
}),
)
.compile()
.expect("a Link State Acknowledgment with three LSA headers compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Acknowledgment decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_ACK);
let ack = match &ospf.body {
OspfBody::LinkStateAck(ack) => ack,
other => panic!("expected a typed Link State Acknowledgment body, got {other:?}"),
};
let decoded_headers = ack.lsa_headers_value();
assert_eq!(decoded_headers.len(), 3);
for (decoded_header, expected) in decoded_headers.iter().zip(headers.iter()) {
assert_eq!(decoded_header.ls_type_value(), expected.ls_type_value());
assert_eq!(
decoded_header.link_state_id_value(),
expected.link_state_id_value()
);
assert_eq!(
decoded_header.advertising_router_value(),
expected.advertising_router_value()
);
assert_eq!(
decoded_header.ls_sequence_number_value(),
expected.ls_sequence_number_value()
);
}
let recompiled = decoded
.compile()
.expect("decoded Link State Acknowledgment re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_link_state_ack_partial_trailing_header_is_truncation_error() {
use crate::protocols::ospf::lsa::OSPF_LSA_HEADER_LEN;
let body = [0u8; OSPF_LSA_HEADER_LEN + 10];
let err = decode_link_state_ack_body(&body)
.expect_err("a 30-octet ack body has a partial trailing LSA header");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf lsa header");
assert_eq!(required, OSPF_LSA_HEADER_LEN);
assert_eq!(available, 10);
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_with_two_raw_lsas_round_trips() {
use crate::protocols::ospf::lsa::{OspfLsa, OspfLsaBody, OspfLsaHeader};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
const OSPF_LSA_UNKNOWN_TYPE_A: u8 = 99;
const OSPF_LSA_UNKNOWN_TYPE_B: u8 = 98;
let first_body = [0xde, 0xad, 0xbe, 0xef, 0x01, 0x02];
let second_body = [0x00, 0x11, 0x22, 0x33];
let lsas = [
OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_UNKNOWN_TYPE_A)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Raw(first_body.to_vec()),
),
OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_UNKNOWN_TYPE_B)
.link_state_id(Ipv4Addr::new(192, 0, 2, 2))
.advertising_router(Ipv4Addr::new(198, 51, 100, 7))
.ls_sequence_number(0x8000_0002),
OspfLsaBody::Raw(second_body.to_vec()),
),
];
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 = u.clone().lsas(lsas.iter().cloned());
}),
)
.compile()
.expect("a Link State Update with two raw LSAs compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
assert_eq!(lsu.num_lsas_value(), 2);
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 2);
assert_eq!(
decoded_lsas[0].header.ls_type_value(),
OSPF_LSA_UNKNOWN_TYPE_A
);
assert_eq!(
decoded_lsas[1].header.ls_type_value(),
OSPF_LSA_UNKNOWN_TYPE_B
);
match &decoded_lsas[0].body {
OspfLsaBody::Raw(raw) => assert_eq!(raw.as_slice(), first_body.as_slice()),
other => panic!("expected a raw LSA body, got {other:?}"),
}
match &decoded_lsas[1].body {
OspfLsaBody::Raw(raw) => assert_eq!(raw.as_slice(), second_body.as_slice()),
other => panic!("expected a raw LSA body, got {other:?}"),
}
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_link_state_update_lsa_length_below_minimum_is_invalid_field() {
let mut body = Vec::new();
body.extend_from_slice(&1u32.to_be_bytes());
body.extend_from_slice(&[0u8; OSPF_LSA_HEADER_LEN]);
let length_offset = OSPF_LSU_COUNT_LEN + (OSPF_LSA_HEADER_LEN - 2);
body[length_offset..length_offset + 2].copy_from_slice(&10u16.to_be_bytes());
let err = decode_link_state_update_body(&body)
.expect_err("an LSA length of 10 is below the 20-octet header minimum");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.lsa.length");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_lsa_length_beyond_buffer_is_truncation_error() {
let mut body = Vec::new();
body.extend_from_slice(&1u32.to_be_bytes());
body.extend_from_slice(&[0u8; OSPF_LSA_HEADER_LEN]);
let length_offset = OSPF_LSU_COUNT_LEN + (OSPF_LSA_HEADER_LEN - 2);
body[length_offset..length_offset + 2].copy_from_slice(&40u16.to_be_bytes());
let err = decode_link_state_update_body(&body)
.expect_err("a declared LSA length of 40 exceeds the 20 remaining octets");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf lsa");
assert_eq!(required, 40);
assert_eq!(available, OSPF_LSA_HEADER_LEN);
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_router_lsa_two_links_round_trips() {
use crate::protocols::ospf::lsa::{
OspfLsa, OspfLsaBody, OspfLsaHeader, OspfRouterLink, OspfRouterLinkTos, OspfRouterLsa,
OSPF_LSA_ROUTER, OSPF_ROUTER_LINK_POINT_TO_POINT, OSPF_ROUTER_LINK_STUB,
OSPF_ROUTER_LSA_FLAG_B,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
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)),
);
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 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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with a Router-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(decoded_lsas[0].header.ls_type_value(), OSPF_LSA_ROUTER);
let router = match &decoded_lsas[0].body {
OspfLsaBody::Router(router) => router,
other => panic!("expected a typed Router-LSA body, got {other:?}"),
};
assert_eq!(router.flags_value(), OSPF_ROUTER_LSA_FLAG_B);
assert_eq!(router.num_links_value(), 2);
let links = router.links_value();
assert_eq!(links.len(), 2);
assert_eq!(links[0].link_id_value(), Ipv4Addr::new(192, 0, 2, 2));
assert_eq!(links[0].link_data_value(), Ipv4Addr::new(198, 51, 100, 1));
assert_eq!(links[0].link_type_value(), OSPF_ROUTER_LINK_POINT_TO_POINT);
assert_eq!(links[0].metric_value(), 10);
assert!(links[0].tos_value().is_empty());
assert_eq!(links[1].link_id_value(), Ipv4Addr::new(198, 51, 100, 0));
assert_eq!(links[1].link_data_value(), Ipv4Addr::new(255, 255, 255, 0));
assert_eq!(links[1].link_type_value(), OSPF_ROUTER_LINK_STUB);
assert_eq!(links[1].metric_value(), 20);
assert_eq!(links[1].tos_value().len(), 1);
assert_eq!(links[1].tos_value()[0].tos_value(), 2);
assert_eq!(links[1].tos_value()[0].metric_value(), 30);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_link_state_update_router_lsa_too_many_links_is_truncation_error() {
use crate::protocols::ospf::lsa::{OSPF_LSA_ROUTER, OSPF_ROUTER_LINK_POINT_TO_POINT};
let mut router_body = Vec::new();
router_body.extend_from_slice(&[0x00, 0x00]); router_body.extend_from_slice(&2u16.to_be_bytes()); router_body.extend_from_slice(&[192, 0, 2, 2]); router_body.extend_from_slice(&[198, 51, 100, 1]); router_body.push(OSPF_ROUTER_LINK_POINT_TO_POINT); router_body.push(0); router_body.extend_from_slice(&10u16.to_be_bytes());
let lsa_len = OSPF_LSA_HEADER_LEN + router_body.len();
let mut body = Vec::new();
body.extend_from_slice(&1u32.to_be_bytes());
let mut header = vec![0u8; OSPF_LSA_HEADER_LEN];
header[3] = OSPF_LSA_ROUTER; header[18..20].copy_from_slice(&(lsa_len as u16).to_be_bytes());
body.extend_from_slice(&header);
body.extend_from_slice(&router_body);
let err = decode_link_state_update_body(&body)
.expect_err("a Router-LSA declaring 2 links but carrying 1 must error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf router-lsa link");
assert_eq!(required, OSPF_ROUTER_LINK_FIXED_LEN);
assert_eq!(available, 0);
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_network_lsa_two_attached_routers_round_trips() {
use crate::protocols::ospf::lsa::{
OspfLsa, OspfLsaBody, OspfLsaHeader, OspfNetworkLsa, OSPF_LSA_NETWORK,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
let network = OspfNetworkLsa::new()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.attached_router(Ipv4Addr::new(192, 0, 2, 1))
.attached_router(Ipv4Addr::new(192, 0, 2, 2));
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_NETWORK)
.link_state_id(Ipv4Addr::new(192, 0, 2, 1))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Network(network),
);
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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with a Network-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(decoded_lsas[0].header.ls_type_value(), OSPF_LSA_NETWORK);
let network = match &decoded_lsas[0].body {
OspfLsaBody::Network(network) => network,
other => panic!("expected a typed Network-LSA body, got {other:?}"),
};
assert_eq!(
network.network_mask_value(),
Ipv4Addr::new(255, 255, 255, 0)
);
assert_eq!(
network.attached_routers_value(),
&[Ipv4Addr::new(192, 0, 2, 1), Ipv4Addr::new(192, 0, 2, 2)]
);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_network_lsa_misaligned_attached_routers_is_invalid_field() {
let body = [0u8; OSPF_NETWORK_LSA_MASK_LEN + 6];
let err = decode_network_lsa_body(&body)
.expect_err("a 6-octet attached-router region is not a multiple of 4");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.network_lsa.attached_routers");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_summary_lsa_two_entries_round_trips() {
use crate::protocols::ospf::lsa::{
OspfLsa, OspfLsaBody, OspfLsaHeader, OspfSummaryLsa, OSPF_LSA_SUMMARY_IP,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
let summary = OspfSummaryLsa::new()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.metric(10)
.tos_entry(2, 0x000a_0b0c);
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_SUMMARY_IP)
.link_state_id(Ipv4Addr::new(198, 51, 100, 0))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Summary(summary),
);
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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with a Summary-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(decoded_lsas[0].header.ls_type_value(), OSPF_LSA_SUMMARY_IP);
let summary = match &decoded_lsas[0].body {
OspfLsaBody::Summary(summary) => summary,
other => panic!("expected a typed Summary-LSA body, got {other:?}"),
};
assert_eq!(
summary.network_mask_value(),
Ipv4Addr::new(255, 255, 255, 0)
);
let entries = summary.entries_value();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].tos_value(), 0);
assert_eq!(entries[0].metric_value(), 10);
assert_eq!(entries[1].tos_value(), 2);
assert_eq!(entries[1].metric_value(), 0x000a_0b0c);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_link_state_update_summary_asbr_lsa_round_trips() {
use crate::protocols::ospf::lsa::{
OspfLsa, OspfLsaBody, OspfLsaHeader, OspfSummaryLsa, OSPF_LSA_SUMMARY_ASBR,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
let summary = OspfSummaryLsa::new()
.network_mask(Ipv4Addr::UNSPECIFIED)
.metric(42);
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_SUMMARY_ASBR)
.link_state_id(Ipv4Addr::new(192, 0, 2, 9))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Summary(summary),
);
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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with a type 4 Summary-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(
decoded_lsas[0].header.ls_type_value(),
OSPF_LSA_SUMMARY_ASBR
);
let summary = match &decoded_lsas[0].body {
OspfLsaBody::Summary(summary) => summary,
other => panic!("expected a typed Summary-LSA body, got {other:?}"),
};
assert_eq!(summary.network_mask_value(), Ipv4Addr::UNSPECIFIED);
let entries = summary.entries_value();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].tos_value(), 0);
assert_eq!(entries[0].metric_value(), 42);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_summary_lsa_trailing_partial_entry_is_invalid_field() {
let body = [0u8; OSPF_SUMMARY_LSA_MASK_LEN + 6];
let err = decode_summary_lsa_body(&body)
.expect_err("a 6-octet TOS/metric region is not a multiple of 4");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.summary_lsa.entries");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_as_external_lsa_round_trips() {
use crate::protocols::ospf::lsa::{
OspfAsExternalLsa, OspfLsa, OspfLsaBody, OspfLsaHeader, OSPF_LSA_AS_EXTERNAL,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
let external = OspfAsExternalLsa::new()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.external_entry(
true,
5,
0x000a_0b0c,
Ipv4Addr::new(192, 0, 2, 9),
0x1234_5678,
);
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_AS_EXTERNAL)
.link_state_id(Ipv4Addr::new(198, 51, 100, 0))
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::AsExternal(external),
);
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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with an AS-External-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(decoded_lsas[0].header.ls_type_value(), OSPF_LSA_AS_EXTERNAL);
let external = match &decoded_lsas[0].body {
OspfLsaBody::AsExternal(external) => external,
other => panic!("expected a typed AS-External-LSA body, got {other:?}"),
};
assert_eq!(
external.network_mask_value(),
Ipv4Addr::new(255, 255, 255, 0)
);
let entries = external.entries_value();
assert_eq!(entries.len(), 2);
assert!(!entries[0].e_bit_value());
assert_eq!(entries[0].tos_value(), 0);
assert_eq!(entries[0].metric_value(), 0);
assert_eq!(entries[0].forwarding_address_value(), Ipv4Addr::UNSPECIFIED);
assert_eq!(entries[0].external_route_tag_value(), 0);
assert!(entries[1].e_bit_value());
assert_eq!(entries[1].tos_value(), 5);
assert_eq!(entries[1].metric_value(), 0x000a_0b0c);
assert_eq!(
entries[1].forwarding_address_value(),
Ipv4Addr::new(192, 0, 2, 9)
);
assert_eq!(entries[1].external_route_tag_value(), 0x1234_5678);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_as_external_lsa_trailing_partial_entry_is_invalid_field() {
let body = [0u8; OSPF_AS_EXTERNAL_LSA_MASK_LEN + 14];
let err = decode_as_external_lsa_body(&body)
.expect_err("a 14-octet external-metric region is not a multiple of 12");
match err {
CrafterError::InvalidFieldValue { field, .. } => {
assert_eq!(field, "ospf.as_external_lsa.entries");
}
other => panic!("expected invalid_field_value, got {other:?}"),
}
}
#[test]
fn ospf_decode_link_state_update_opaque_lsa_area_scope_round_trips() {
use crate::protocols::ospf::lsa::{
opaque_id, opaque_type, OspfLsa, OspfLsaBody, OspfLsaHeader, OspfOpaqueLsa,
OspfOpaqueTlv, OSPF_LSA_OPAQUE_AREA,
};
use crate::protocols::ospf::OspfBody;
use crate::protocols::ospf::OSPF_TYPE_LINK_STATE_UPDATE;
let tlv_aligned = OspfOpaqueTlv::new(0x0001, vec![0xaa, 0xbb, 0xcc, 0xdd]);
let tlv_padded = OspfOpaqueTlv::new(0x0002, vec![0x11, 0x22, 0x33, 0x44, 0x55]);
let opaque = OspfOpaqueLsa::new()
.tlv(tlv_aligned.clone())
.tlv(tlv_padded.clone());
let opaque_type_code = 10u8;
let opaque_id_value = 0x0001_0203u32;
let lsa = OspfLsa::new(
OspfLsaHeader::new()
.ls_type(OSPF_LSA_OPAQUE_AREA)
.opaque_link_state_id(opaque_type_code, opaque_id_value)
.advertising_router(Ipv4Addr::new(192, 0, 2, 1))
.ls_sequence_number(0x8000_0001),
OspfLsaBody::Opaque(opaque),
);
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 = u.clone().lsa(lsa.clone());
}),
)
.compile()
.expect("a Link State Update with an area-scope Opaque-LSA compiles");
let decoded = append_ospf_packet(Packet::new(), bytes.as_bytes())
.expect("the Link State Update decodes");
let ospf = decoded
.layer::<Ospfv2>()
.expect("the decoded packet exposes a typed Ospfv2 layer");
assert_eq!(ospf.packet_type_value(), OSPF_TYPE_LINK_STATE_UPDATE);
let lsu = match &ospf.body {
OspfBody::LinkStateUpdate(lsu) => lsu,
other => panic!("expected a typed Link State Update body, got {other:?}"),
};
let decoded_lsas = lsu.lsas_value();
assert_eq!(decoded_lsas.len(), 1);
assert_eq!(decoded_lsas[0].header.ls_type_value(), OSPF_LSA_OPAQUE_AREA);
assert_eq!(opaque_type(&decoded_lsas[0].header), opaque_type_code);
assert_eq!(opaque_id(&decoded_lsas[0].header), opaque_id_value);
let opaque = match &decoded_lsas[0].body {
OspfLsaBody::Opaque(opaque) => opaque,
other => panic!("expected a typed Opaque-LSA body, got {other:?}"),
};
let tlvs = opaque.tlvs_value();
assert_eq!(tlvs.len(), 2);
assert_eq!(tlvs[0].tlv_type(), 0x0001);
assert_eq!(tlvs[0].value(), &[0xaa, 0xbb, 0xcc, 0xdd]);
assert_eq!(tlvs[1].tlv_type(), 0x0002);
assert_eq!(tlvs[1].value(), &[0x11, 0x22, 0x33, 0x44, 0x55]);
assert_eq!(tlvs[0], tlv_aligned);
assert_eq!(tlvs[1], tlv_padded);
let recompiled = decoded
.compile()
.expect("decoded Link State Update re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
#[test]
fn ospf_decode_opaque_lsa_truncated_tlv_value_is_truncation_error() {
let body = [0x00, 0x02, 0x00, 0x05, 0x11, 0x22];
let err = decode_opaque_lsa_body(&body)
.expect_err("a TLV value running past the body is a truncation error");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert_eq!(context, "ospf opaque tlv value");
assert_eq!(required, 12);
assert_eq!(available, body.len());
}
other => panic!("expected buffer_too_short, got {other:?}"),
}
}
#[test]
fn ospf_decode_opaque_lsa_unknown_tlv_type_is_preserved() {
use crate::protocols::ospf::lsa::OspfOpaqueTlv;
let unknown = OspfOpaqueTlv::new(0xbeef, vec![0xde, 0xad, 0xbe]);
let mut body = Vec::new();
unknown.encode(&mut body);
let opaque = decode_opaque_lsa_body(&body).expect("an unknown-type TLV body decodes");
let tlvs = opaque.tlvs_value();
assert_eq!(tlvs.len(), 1);
assert_eq!(tlvs[0].tlv_type(), 0xbeef);
assert_eq!(tlvs[0].value(), &[0xde, 0xad, 0xbe]);
assert_eq!(tlvs[0], unknown);
}
}