use core::net::Ipv4Addr;
use crate::field::Field;
use super::constants::{OSPFV3_DD_FLAG_I, OSPFV3_DD_FLAG_M, OSPFV3_DD_FLAG_MS};
use super::lsa::{encode_ospfv3_lsa_headers, Ospfv3LsaHeader, OSPFV3_LSA_HEADER_LEN};
const OSPFV3_DD_FIXED_LEN: usize = 12;
const OSPFV3_OPTIONS_MASK: u32 = 0x00ff_ffff;
const OSPFV3_LSR_ENTRY_LEN: usize = 12;
#[derive(Debug, Clone)]
pub struct Ospfv3DatabaseDescription {
reserved1: Field<u8>,
options: Field<u32>,
interface_mtu: Field<u16>,
reserved2: Field<u8>,
flags: Field<u8>,
dd_sequence_number: Field<u32>,
lsa_headers: Vec<Ospfv3LsaHeader>,
}
impl Ospfv3DatabaseDescription {
pub fn new() -> Self {
Self {
reserved1: Field::defaulted(0),
options: Field::defaulted(0),
interface_mtu: Field::defaulted(0),
reserved2: Field::defaulted(0),
flags: Field::defaulted(0),
dd_sequence_number: Field::defaulted(0),
lsa_headers: Vec::new(),
}
}
pub fn reserved1(mut self, reserved1: u8) -> Self {
self.reserved1.set_user(reserved1);
self
}
pub fn options(mut self, options: u32) -> Self {
self.options.set_user(options);
self
}
pub fn interface_mtu(mut self, interface_mtu: u16) -> Self {
self.interface_mtu.set_user(interface_mtu);
self
}
pub fn reserved2(mut self, reserved2: u8) -> Self {
self.reserved2.set_user(reserved2);
self
}
pub fn flags(mut self, flags: u8) -> Self {
self.flags.set_user(flags);
self
}
pub fn dd_sequence_number(mut self, dd_sequence_number: u32) -> Self {
self.dd_sequence_number.set_user(dd_sequence_number);
self
}
pub fn init(mut self, init: bool) -> Self {
self.set_flag_bit(OSPFV3_DD_FLAG_I, init);
self
}
pub fn more(mut self, more: bool) -> Self {
self.set_flag_bit(OSPFV3_DD_FLAG_M, more);
self
}
pub fn master(mut self, master: bool) -> Self {
self.set_flag_bit(OSPFV3_DD_FLAG_MS, master);
self
}
fn set_flag_bit(&mut self, bit: u8, set: bool) {
let mut flags = self.flags_value();
if set {
flags |= bit;
} else {
flags &= !bit;
}
self.flags.set_user(flags);
}
pub fn lsa_header(mut self, header: Ospfv3LsaHeader) -> Self {
self.lsa_headers.push(header);
self
}
pub fn lsa_headers<I>(mut self, headers: I) -> Self
where
I: IntoIterator<Item = Ospfv3LsaHeader>,
{
self.lsa_headers.extend(headers);
self
}
pub fn reserved1_value(&self) -> u8 {
self.reserved1.value().copied().unwrap_or(0)
}
pub fn options_value(&self) -> u32 {
self.options.value().copied().unwrap_or(0) & OSPFV3_OPTIONS_MASK
}
pub fn interface_mtu_value(&self) -> u16 {
self.interface_mtu.value().copied().unwrap_or(0)
}
pub fn reserved2_value(&self) -> u8 {
self.reserved2.value().copied().unwrap_or(0)
}
pub fn flags_value(&self) -> u8 {
self.flags.value().copied().unwrap_or(0)
}
pub fn is_init(&self) -> bool {
self.flags_value() & OSPFV3_DD_FLAG_I != 0
}
pub fn is_more(&self) -> bool {
self.flags_value() & OSPFV3_DD_FLAG_M != 0
}
pub fn is_master(&self) -> bool {
self.flags_value() & OSPFV3_DD_FLAG_MS != 0
}
pub fn dd_flags_summary(&self) -> String {
let flags = self.flags_value();
let mut labels: Vec<&str> = Vec::new();
if flags & OSPFV3_DD_FLAG_I != 0 {
labels.push("I");
}
if flags & OSPFV3_DD_FLAG_M != 0 {
labels.push("M");
}
if flags & OSPFV3_DD_FLAG_MS != 0 {
labels.push("MS");
}
labels.join("|")
}
pub fn dd_sequence_number_value(&self) -> u32 {
self.dd_sequence_number.value().copied().unwrap_or(0)
}
pub fn lsa_headers_value(&self) -> &[Ospfv3LsaHeader] {
&self.lsa_headers
}
pub(crate) fn encoded_len(&self) -> usize {
OSPFV3_DD_FIXED_LEN + self.lsa_headers.len() * OSPFV3_LSA_HEADER_LEN
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.push(self.reserved1_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);
out.extend_from_slice(&self.interface_mtu_value().to_be_bytes());
out.push(self.reserved2_value());
out.push(self.flags_value());
out.extend_from_slice(&self.dd_sequence_number_value().to_be_bytes());
encode_ospfv3_lsa_headers(&self.lsa_headers, out);
}
}
impl Default for Ospfv3DatabaseDescription {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ospfv3LinkStateRequestEntry {
ls_type: u16,
link_state_id: Ipv4Addr,
advertising_router: Ipv4Addr,
}
impl Ospfv3LinkStateRequestEntry {
pub fn new(
ls_type: u16,
link_state_id: impl Into<Ipv4Addr>,
advertising_router: impl Into<Ipv4Addr>,
) -> Self {
Self {
ls_type,
link_state_id: link_state_id.into(),
advertising_router: advertising_router.into(),
}
}
pub fn ls_type_value(&self) -> u16 {
self.ls_type
}
pub fn link_state_id_value(&self) -> Ipv4Addr {
self.link_state_id
}
pub fn advertising_router_value(&self) -> Ipv4Addr {
self.advertising_router
}
fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&0u16.to_be_bytes());
out.extend_from_slice(&self.ls_type.to_be_bytes());
out.extend_from_slice(&self.link_state_id.octets());
out.extend_from_slice(&self.advertising_router.octets());
}
}
#[derive(Debug, Clone)]
pub struct Ospfv3LinkStateRequest {
entries: Vec<Ospfv3LinkStateRequestEntry>,
}
impl Ospfv3LinkStateRequest {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn request(mut self, entry: Ospfv3LinkStateRequestEntry) -> Self {
self.entries.push(entry);
self
}
pub fn requests<I>(mut self, entries: I) -> Self
where
I: IntoIterator<Item = Ospfv3LinkStateRequestEntry>,
{
self.entries.extend(entries);
self
}
pub fn entries_value(&self) -> &[Ospfv3LinkStateRequestEntry] {
&self.entries
}
pub(crate) fn encoded_len(&self) -> usize {
self.entries.len() * OSPFV3_LSR_ENTRY_LEN
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
for entry in &self.entries {
entry.encode(out);
}
}
}
impl Default for Ospfv3LinkStateRequest {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Ospfv3LinkStateAck {
lsa_headers: Vec<Ospfv3LsaHeader>,
}
impl Ospfv3LinkStateAck {
pub fn new() -> Self {
Self {
lsa_headers: Vec::new(),
}
}
pub fn lsa_header(mut self, header: Ospfv3LsaHeader) -> Self {
self.lsa_headers.push(header);
self
}
pub fn lsa_headers<I>(mut self, headers: I) -> Self
where
I: IntoIterator<Item = Ospfv3LsaHeader>,
{
self.lsa_headers.extend(headers);
self
}
pub fn lsa_headers_value(&self) -> &[Ospfv3LsaHeader] {
&self.lsa_headers
}
pub(crate) fn encoded_len(&self) -> usize {
self.lsa_headers.len() * OSPFV3_LSA_HEADER_LEN
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
encode_ospfv3_lsa_headers(&self.lsa_headers, out);
}
}
impl Default for Ospfv3LinkStateAck {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::super::lsa::decode_ospfv3_lsa_headers;
use super::*;
use crate::packet::{NetworkLayer, Packet};
use crate::protocols::ip::v6::Ipv6;
use crate::protocols::ospf::{
Ospfv3, OSPFV3_HEADER_LEN, OSPFV3_TYPE_DATABASE_DESCRIPTION, OSPFV3_TYPE_LINK_STATE_ACK,
OSPFV3_TYPE_LINK_STATE_REQUEST,
};
use core::net::Ipv6Addr;
fn round_trip_v3_body(ospfv3: Ospfv3, type_code: u8, expected_body: &[u8]) -> Vec<u8> {
let src: Ipv6Addr = "2001:db8::1".parse().unwrap();
let dst: Ipv6Addr = "2001:db8::2".parse().unwrap();
let bytes = (Ipv6::new().src(src).dst(dst) / ospfv3)
.compile()
.expect("Ipv6 / Ospfv3 body compiles");
let body_start = bytes.as_bytes().len() - expected_body.len();
assert_eq!(&bytes.as_bytes()[body_start..], expected_body);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes.as_bytes())
.expect("the default registry decodes the OSPFv3 body over IPv6");
let layer = decoded
.layer::<Ospfv3>()
.expect("the decoded packet exposes a typed Ospfv3 layer");
assert_eq!(layer.packet_type_value(), type_code);
let ospfv3_start = bytes.as_bytes().len() - (OSPFV3_HEADER_LEN + expected_body.len());
assert_eq!(
&bytes.as_bytes()[ospfv3_start + OSPFV3_HEADER_LEN..],
expected_body
);
let recompiled = decoded
.compile()
.expect("the decoded OSPFv3 body re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
bytes.as_bytes().to_vec()
}
#[test]
fn ospfv3_database_description_body_compiles_and_round_trips() {
let dd = Ospfv3DatabaseDescription::new()
.interface_mtu(1500)
.options(0x00ff_0013)
.dd_sequence_number(0x0000_1a2b)
.more(true)
.master(true)
.lsa_header(
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),
)
.lsa_header(
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),
);
assert_eq!(dd.flags_value(), OSPFV3_DD_FLAG_M | OSPFV3_DD_FLAG_MS);
assert!(dd.is_more());
assert!(dd.is_master());
assert!(!dd.is_init());
assert_eq!(dd.options_value(), 0x00ff_0013);
let mut body = Vec::new();
dd.encode(&mut body);
assert_eq!(
dd.encoded_len(),
OSPFV3_DD_FIXED_LEN + 2 * OSPFV3_LSA_HEADER_LEN
);
assert_eq!(body.len(), dd.encoded_len());
assert_eq!(body[0], 0x00); assert_eq!(&body[1..4], &[0xff, 0x00, 0x13]); assert_eq!(&body[4..6], &1500u16.to_be_bytes()); assert_eq!(body[6], 0x00); assert_eq!(body[7], OSPFV3_DD_FLAG_M | OSPFV3_DD_FLAG_MS); assert_eq!(&body[8..12], &0x0000_1a2bu32.to_be_bytes());
let mut expected_headers = Vec::new();
encode_ospfv3_lsa_headers(dd.lsa_headers_value(), &mut expected_headers);
assert_eq!(&body[OSPFV3_DD_FIXED_LEN..], expected_headers.as_slice());
assert_eq!(expected_headers.len(), 2 * OSPFV3_LSA_HEADER_LEN);
let ospfv3 = Ospfv3::database_description()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_database_description(|d| *d = dd);
round_trip_v3_body(ospfv3, OSPFV3_TYPE_DATABASE_DESCRIPTION, &body);
}
#[test]
fn ospfv3_link_state_request_body_compiles_and_round_trips() {
let lsr = Ospfv3LinkStateRequest::new()
.request(Ospfv3LinkStateRequestEntry::new(
0x2001,
Ipv4Addr::new(192, 0, 2, 1),
Ipv4Addr::new(192, 0, 2, 1),
))
.request(Ospfv3LinkStateRequestEntry::new(
0x2002,
Ipv4Addr::new(192, 0, 2, 2),
Ipv4Addr::new(198, 51, 100, 7),
));
let mut body = Vec::new();
lsr.encode(&mut body);
assert_eq!(lsr.encoded_len(), 2 * OSPFV3_LSR_ENTRY_LEN);
assert_eq!(body.len(), lsr.encoded_len());
assert_eq!(body.len(), 24);
assert_eq!(&body[0..2], &0u16.to_be_bytes()); assert_eq!(&body[2..4], &0x2001u16.to_be_bytes()); assert_eq!(&body[4..8], &Ipv4Addr::new(192, 0, 2, 1).octets());
assert_eq!(&body[8..12], &Ipv4Addr::new(192, 0, 2, 1).octets());
assert_eq!(&body[12..14], &0u16.to_be_bytes()); assert_eq!(&body[14..16], &0x2002u16.to_be_bytes()); assert_eq!(&body[16..20], &Ipv4Addr::new(192, 0, 2, 2).octets());
assert_eq!(&body[20..24], &Ipv4Addr::new(198, 51, 100, 7).octets());
let ospfv3 = Ospfv3::link_state_request()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_request(|r| *r = lsr);
round_trip_v3_body(ospfv3, OSPFV3_TYPE_LINK_STATE_REQUEST, &body);
}
#[test]
fn ospfv3_link_state_ack_body_compiles_and_round_trips() {
let ack = Ospfv3LinkStateAck::new()
.lsa_header(
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),
)
.lsa_header(
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),
);
let mut body = Vec::new();
ack.encode(&mut body);
assert_eq!(ack.encoded_len(), 2 * OSPFV3_LSA_HEADER_LEN);
assert_eq!(body.len(), ack.encoded_len());
assert_eq!(body.len(), 40);
let mut expected_headers = Vec::new();
encode_ospfv3_lsa_headers(ack.lsa_headers_value(), &mut expected_headers);
assert_eq!(body, expected_headers);
assert_eq!(expected_headers.len(), 2 * OSPFV3_LSA_HEADER_LEN);
let ospfv3 = Ospfv3::link_state_ack()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_link_state_ack(|a| *a = ack);
round_trip_v3_body(ospfv3, OSPFV3_TYPE_LINK_STATE_ACK, &body);
}
#[test]
fn ospfv3_empty_bodies_build_and_round_trip() {
let mut dd_body = Vec::new();
Ospfv3DatabaseDescription::new().encode(&mut dd_body);
assert_eq!(dd_body.len(), OSPFV3_DD_FIXED_LEN);
let mut lsr_body = Vec::new();
Ospfv3LinkStateRequest::new().encode(&mut lsr_body);
assert!(lsr_body.is_empty());
let mut ack_body = Vec::new();
Ospfv3LinkStateAck::new().encode(&mut ack_body);
assert!(ack_body.is_empty());
round_trip_v3_body(
Ospfv3::database_description()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0]),
OSPFV3_TYPE_DATABASE_DESCRIPTION,
&dd_body,
);
round_trip_v3_body(
Ospfv3::link_state_request()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0]),
OSPFV3_TYPE_LINK_STATE_REQUEST,
&lsr_body,
);
round_trip_v3_body(
Ospfv3::link_state_ack()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0]),
OSPFV3_TYPE_LINK_STATE_ACK,
&ack_body,
);
}
#[test]
fn ospfv3_dd_lsa_header_list_parses_back() {
let dd = Ospfv3DatabaseDescription::new().lsa_header(
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),
);
let mut body = Vec::new();
dd.encode(&mut body);
let headers = decode_ospfv3_lsa_headers(&body[OSPFV3_DD_FIXED_LEN..])
.expect("the DD LSA-header list decodes");
assert_eq!(headers.len(), 1);
assert_eq!(headers[0].ls_type_value(), 0x2001);
assert_eq!(
headers[0].link_state_id_value(),
Ipv4Addr::new(192, 0, 2, 1)
);
}
}