use core::net::Ipv4Addr;
use crate::field::Field;
use crate::protocols::ospf::constants::{OSPF_OPTIONS_E, OSPF_OPTIONS_O};
const OSPF_HELLO_FIXED_LEN: usize = 20;
const OSPF_HELLO_DEFAULT_HELLO_INTERVAL: u16 = 10;
const OSPF_HELLO_DEFAULT_DEAD_INTERVAL: u32 = 40;
#[derive(Debug, Clone)]
pub struct OspfHello {
network_mask: Field<Ipv4Addr>,
hello_interval: Field<u16>,
options: Field<u8>,
router_priority: Field<u8>,
router_dead_interval: Field<u32>,
designated_router: Field<Ipv4Addr>,
backup_designated_router: Field<Ipv4Addr>,
neighbors: Vec<Ipv4Addr>,
}
impl OspfHello {
pub fn new() -> Self {
Self {
network_mask: Field::unset(),
hello_interval: Field::defaulted(OSPF_HELLO_DEFAULT_HELLO_INTERVAL),
options: Field::defaulted(0),
router_priority: Field::defaulted(0),
router_dead_interval: Field::defaulted(OSPF_HELLO_DEFAULT_DEAD_INTERVAL),
designated_router: Field::unset(),
backup_designated_router: Field::unset(),
neighbors: Vec::new(),
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_decoded_parts(
network_mask: Ipv4Addr,
hello_interval: u16,
options: u8,
router_priority: u8,
router_dead_interval: u32,
designated_router: Ipv4Addr,
backup_designated_router: Ipv4Addr,
neighbors: Vec<Ipv4Addr>,
) -> Self {
Self {
network_mask: Field::user(network_mask),
hello_interval: Field::user(hello_interval),
options: Field::user(options),
router_priority: Field::user(router_priority),
router_dead_interval: Field::user(router_dead_interval),
designated_router: Field::user(designated_router),
backup_designated_router: Field::user(backup_designated_router),
neighbors,
}
}
pub fn network_mask(mut self, network_mask: impl Into<Ipv4Addr>) -> Self {
self.network_mask.set_user(network_mask.into());
self
}
pub fn hello_interval(mut self, hello_interval: u16) -> Self {
self.hello_interval.set_user(hello_interval);
self
}
pub fn options(mut self, options: u8) -> Self {
self.options.set_user(options);
self
}
pub fn external_capable(mut self, external_capable: bool) -> Self {
self.set_options_bit(OSPF_OPTIONS_E, external_capable);
self
}
pub fn opaque_capable(mut self, opaque_capable: bool) -> Self {
self.set_options_bit(OSPF_OPTIONS_O, opaque_capable);
self
}
fn set_options_bit(&mut self, bit: u8, set: bool) {
let mut options = self.options_value();
if set {
options |= bit;
} else {
options &= !bit;
}
self.options.set_user(options);
}
pub fn router_priority(mut self, router_priority: u8) -> Self {
self.router_priority.set_user(router_priority);
self
}
pub fn router_dead_interval(mut self, router_dead_interval: u32) -> Self {
self.router_dead_interval.set_user(router_dead_interval);
self
}
pub fn designated_router(mut self, designated_router: impl Into<Ipv4Addr>) -> Self {
self.designated_router.set_user(designated_router.into());
self
}
pub fn backup_designated_router(
mut self,
backup_designated_router: impl Into<Ipv4Addr>,
) -> Self {
self.backup_designated_router
.set_user(backup_designated_router.into());
self
}
pub fn neighbor(mut self, neighbor: impl Into<Ipv4Addr>) -> Self {
self.neighbors.push(neighbor.into());
self
}
pub fn neighbors<I, A>(mut self, neighbors: I) -> Self
where
I: IntoIterator<Item = A>,
A: Into<Ipv4Addr>,
{
self.neighbors.extend(neighbors.into_iter().map(Into::into));
self
}
pub fn network_mask_value(&self) -> Ipv4Addr {
self.network_mask
.value()
.copied()
.unwrap_or(Ipv4Addr::UNSPECIFIED)
}
pub fn hello_interval_value(&self) -> u16 {
self.hello_interval
.value()
.copied()
.unwrap_or(OSPF_HELLO_DEFAULT_HELLO_INTERVAL)
}
pub fn options_value(&self) -> u8 {
self.options.value().copied().unwrap_or(0)
}
pub fn router_priority_value(&self) -> u8 {
self.router_priority.value().copied().unwrap_or(0)
}
pub fn router_dead_interval_value(&self) -> u32 {
self.router_dead_interval
.value()
.copied()
.unwrap_or(OSPF_HELLO_DEFAULT_DEAD_INTERVAL)
}
pub fn designated_router_value(&self) -> Ipv4Addr {
self.designated_router
.value()
.copied()
.unwrap_or(Ipv4Addr::UNSPECIFIED)
}
pub fn backup_designated_router_value(&self) -> Ipv4Addr {
self.backup_designated_router
.value()
.copied()
.unwrap_or(Ipv4Addr::UNSPECIFIED)
}
pub fn neighbors_value(&self) -> &[Ipv4Addr] {
&self.neighbors
}
pub(crate) fn encoded_len(&self) -> usize {
OSPF_HELLO_FIXED_LEN + self.neighbors.len() * 4
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.network_mask_value().octets());
out.extend_from_slice(&self.hello_interval_value().to_be_bytes());
out.push(self.options_value());
out.push(self.router_priority_value());
out.extend_from_slice(&self.router_dead_interval_value().to_be_bytes());
out.extend_from_slice(&self.designated_router_value().octets());
out.extend_from_slice(&self.backup_designated_router_value().octets());
for neighbor in &self.neighbors {
out.extend_from_slice(&neighbor.octets());
}
}
}
impl Default for OspfHello {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ospf_hello_body_compiles_with_two_neighbors() {
use crate::packet::{Layer, Packet};
use crate::protocols::ospf::{Ospfv2, OSPF_HEADER_LEN};
let hello = OspfHello::new()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.hello_interval(10)
.options(0x02)
.router_priority(1)
.router_dead_interval(40)
.designated_router(Ipv4Addr::new(192, 0, 2, 1))
.backup_designated_router(Ipv4Addr::new(192, 0, 2, 2))
.neighbor(Ipv4Addr::new(192, 0, 2, 3))
.neighbor(Ipv4Addr::new(192, 0, 2, 4));
let mut body = Vec::new();
hello.encode(&mut body);
assert_eq!(hello.encoded_len(), OSPF_HELLO_FIXED_LEN + 2 * 4);
assert_eq!(body.len(), hello.encoded_len());
let expected: Vec<u8> = vec![
0xff, 0xff, 0xff, 0x00, 0x00, 0x0a, 0x02, 0x01, 0x00, 0x00, 0x00, 0x28, 192, 0, 2, 1, 192, 0, 2, 2, 192, 0, 2, 3, 192, 0, 2, 4,
];
assert_eq!(body, expected);
let ospf = Ospfv2::hello().with_hello(|h| {
*h = hello.clone();
});
let bytes = Packet::from_layer(ospf).compile().expect("Hello compiles");
let total = OSPF_HEADER_LEN + body.len();
assert_eq!(bytes.len(), total);
assert_eq!(&bytes[2..4], &(total as u16).to_be_bytes());
assert_eq!(bytes[1], crate::protocols::ospf::OSPF_TYPE_HELLO);
assert_eq!(&bytes[OSPF_HEADER_LEN..], expected.as_slice());
let layer = Ospfv2::hello().with_hello(|h| *h = hello);
assert_eq!(layer.encoded_len(), total);
}
#[test]
fn ospf_hello_external_capable_sets_options_bit_and_round_trips() {
use crate::packet::Packet;
use crate::protocols::ospf::constants::{OSPF_OPTIONS_E, OSPF_OPTIONS_O};
use crate::protocols::ospf::decode::append_ospf_packet;
use crate::protocols::ospf::{OspfBody, Ospfv2, OSPF_HEADER_LEN};
let hello = OspfHello::new().external_capable(true).opaque_capable(true);
assert_eq!(
hello.options_value(),
OSPF_OPTIONS_E | OSPF_OPTIONS_O,
"both convenience setters compose without clobbering each other"
);
let cleared = hello.clone().external_capable(false);
assert_eq!(cleared.options_value(), OSPF_OPTIONS_O);
let hello = OspfHello::new()
.network_mask(Ipv4Addr::new(255, 255, 255, 0))
.external_capable(true)
.neighbor(Ipv4Addr::new(192, 0, 2, 3));
assert_eq!(hello.options_value() & OSPF_OPTIONS_E, OSPF_OPTIONS_E);
let bytes = Packet::from_layer(
Ospfv2::hello()
.router_id([192, 0, 2, 1])
.area_id([0, 0, 0, 0])
.with_hello(|h| *h = hello.clone()),
)
.compile()
.expect("a Hello with external_capable compiles");
let options_octet = bytes.as_bytes()[OSPF_HEADER_LEN + 6];
assert_eq!(options_octet & OSPF_OPTIONS_E, OSPF_OPTIONS_E);
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");
let decoded_hello = match &ospf.body {
OspfBody::Hello(hello) => hello,
other => panic!("expected a typed Hello body, got {other:?}"),
};
assert_eq!(
decoded_hello.options_value() & OSPF_OPTIONS_E,
OSPF_OPTIONS_E
);
let recompiled = decoded.compile().expect("the decoded Hello re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
}
}