use std::net::Ipv6Addr;
use crate::MacAddr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct NdpMessage {
pub kind: NdpKind,
pub target: Ipv6Addr,
pub lladdr: Option<MacAddr>,
pub router_flag: bool,
pub solicited_flag: bool,
pub override_flag: bool,
}
impl NdpMessage {
#[inline]
pub fn is_unsolicited_override(&self) -> bool {
matches!(self.kind, NdpKind::Advertisement) && self.override_flag && !self.solicited_flag
}
#[inline]
pub fn is_likely_spoof(&self) -> bool {
self.is_unsolicited_override() && self.lladdr.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
#[non_exhaustive]
pub enum NdpKind {
Solicitation,
Advertisement,
}
impl NdpKind {
pub fn as_str(&self) -> &'static str {
match self {
NdpKind::Solicitation => "solicitation",
NdpKind::Advertisement => "advertisement",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn na(
target: [u8; 16],
lladdr: Option<[u8; 6]>,
router: bool,
solicited: bool,
ov: bool,
) -> NdpMessage {
NdpMessage {
kind: NdpKind::Advertisement,
target: Ipv6Addr::from(target),
lladdr: lladdr.map(MacAddr),
router_flag: router,
solicited_flag: solicited,
override_flag: ov,
}
}
#[test]
fn ndp_kind_slugs() {
assert_eq!(NdpKind::Solicitation.as_str(), "solicitation");
assert_eq!(NdpKind::Advertisement.as_str(), "advertisement");
}
#[test]
fn unsolicited_override_classification() {
let m = na([0; 16], Some([0x11; 6]), false, false, true);
assert!(m.is_unsolicited_override());
let m = na([0; 16], Some([0x11; 6]), false, true, true);
assert!(!m.is_unsolicited_override());
let m = na([0; 16], Some([0x11; 6]), false, false, false);
assert!(!m.is_unsolicited_override());
}
#[test]
fn solicitation_never_unsolicited_override() {
let m = NdpMessage {
kind: NdpKind::Solicitation,
target: Ipv6Addr::UNSPECIFIED,
lladdr: Some(MacAddr([0; 6])),
router_flag: false,
solicited_flag: false,
override_flag: true,
};
assert!(!m.is_unsolicited_override());
}
#[test]
fn likely_spoof_requires_lladdr() {
let m = na([0; 16], None, false, false, true);
assert!(m.is_unsolicited_override());
assert!(!m.is_likely_spoof());
let m = na([0; 16], Some([0x11; 6]), false, false, true);
assert!(m.is_likely_spoof());
}
}