use core::net::Ipv6Addr;
use crate::endian::read_u32_be;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use super::super::constants::{
IPV6_EXTENSION_MIN_LEN, IPV6_MOBILE_ROUTING_HEADER_EXT_LEN, IPV6_MOBILE_ROUTING_LEN,
IPV6_MOBILE_ROUTING_RESERVED, IPV6_MOBILE_ROUTING_SEGMENTS_LEFT, IPV6_ROUTING_TYPE_MOBILE,
};
use super::super::display::{
ipv6_routing_type_label, ipv6_routing_type_status, next_header_summary, routing_type_summary,
};
use super::super::{copy_array_16, layer_ipv6_next_header, parse_ipv6, value_or_copy};
use super::{header_ext_len_from_total, validate_extension_total_len};
use super::routing::Ipv6RoutingTypeStatus;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Ipv6MobileRoutingHeaderStatus {
Valid,
InvalidRoutingType,
InvalidHeaderExtLen,
InvalidSegmentsLeft,
NonzeroReserved,
}
impl Ipv6MobileRoutingHeaderStatus {
pub const fn is_valid(self) -> bool {
matches!(self, Self::Valid)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ipv6MobileRoutingHeader {
next_header: Field<u8>,
header_ext_len: Field<u8>,
routing_type: Field<u8>,
segments_left: Field<u8>,
reserved: Field<u32>,
home_address: Field<Ipv6Addr>,
}
impl Ipv6MobileRoutingHeader {
pub fn new() -> Self {
Self {
next_header: Field::defaulted(0),
header_ext_len: Field::unset(),
routing_type: Field::defaulted(IPV6_ROUTING_TYPE_MOBILE),
segments_left: Field::defaulted(IPV6_MOBILE_ROUTING_SEGMENTS_LEFT),
reserved: Field::defaulted(IPV6_MOBILE_ROUTING_RESERVED),
home_address: Field::defaulted(Ipv6Addr::LOCALHOST),
}
}
pub fn next_header(mut self, next_header: u8) -> Self {
self.next_header.set_user(next_header);
self
}
pub fn nh(self, next_header: u8) -> Self {
self.next_header(next_header)
}
pub fn header_ext_len(mut self, header_ext_len: u8) -> Self {
self.header_ext_len.set_user(header_ext_len);
self
}
pub fn routing_type(mut self, routing_type: u8) -> Self {
self.routing_type.set_user(routing_type);
self
}
pub fn segments_left(mut self, segments_left: u8) -> Self {
self.segments_left.set_user(segments_left);
self
}
pub fn segleft(self, segments_left: u8) -> Self {
self.segments_left(segments_left)
}
pub fn reserved(mut self, reserved: u32) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn home_address(mut self, home_address: Ipv6Addr) -> Self {
self.home_address.set_user(home_address);
self
}
pub fn home(self, home_address: Ipv6Addr) -> Self {
self.home_address(home_address)
}
pub fn home_address_str(self, home_address: &str) -> Result<Self> {
Ok(self.home_address(parse_ipv6(home_address)?))
}
pub fn home_str(self, home_address: &str) -> Result<Self> {
self.home_address_str(home_address)
}
pub fn next_header_value(&self) -> u8 {
value_or_copy(&self.next_header, 0)
}
pub fn header_ext_len_value(&self) -> Option<u8> {
self.header_ext_len.value().copied()
}
pub fn effective_header_ext_len_value(&self) -> u8 {
self.header_ext_len
.value()
.copied()
.unwrap_or(IPV6_MOBILE_ROUTING_HEADER_EXT_LEN)
}
pub fn header_ext_len_status(&self) -> Ipv6MobileRoutingHeaderStatus {
if self.effective_header_ext_len_value() == IPV6_MOBILE_ROUTING_HEADER_EXT_LEN {
Ipv6MobileRoutingHeaderStatus::Valid
} else {
Ipv6MobileRoutingHeaderStatus::InvalidHeaderExtLen
}
}
pub fn routing_type_value(&self) -> u8 {
value_or_copy(&self.routing_type, IPV6_ROUTING_TYPE_MOBILE)
}
pub fn routing_type_label(&self) -> &'static str {
ipv6_routing_type_label(self.routing_type_value())
}
pub fn routing_type_status(&self) -> Ipv6RoutingTypeStatus {
ipv6_routing_type_status(self.routing_type_value())
}
pub fn segments_left_value(&self) -> u8 {
value_or_copy(&self.segments_left, IPV6_MOBILE_ROUTING_SEGMENTS_LEFT)
}
pub fn segments_left_is_defaulted(&self) -> bool {
!self.segments_left.is_user_set()
&& self.segments_left_value() == IPV6_MOBILE_ROUTING_SEGMENTS_LEFT
}
pub fn segments_left_status(&self) -> Ipv6MobileRoutingHeaderStatus {
if self.segments_left_value() == IPV6_MOBILE_ROUTING_SEGMENTS_LEFT {
Ipv6MobileRoutingHeaderStatus::Valid
} else {
Ipv6MobileRoutingHeaderStatus::InvalidSegmentsLeft
}
}
pub fn reserved_value(&self) -> u32 {
value_or_copy(&self.reserved, IPV6_MOBILE_ROUTING_RESERVED)
}
pub fn reserved_is_zero(&self) -> bool {
self.reserved_value() == IPV6_MOBILE_ROUTING_RESERVED
}
pub fn reserved_status(&self) -> Ipv6MobileRoutingHeaderStatus {
if self.reserved_is_zero() {
Ipv6MobileRoutingHeaderStatus::Valid
} else {
Ipv6MobileRoutingHeaderStatus::NonzeroReserved
}
}
pub fn home_address_value(&self) -> Ipv6Addr {
value_or_copy(&self.home_address, Ipv6Addr::LOCALHOST)
}
pub fn home_address_bytes(&self) -> [u8; 16] {
self.home_address_value().octets()
}
pub fn validity_status(&self) -> Ipv6MobileRoutingHeaderStatus {
if self.routing_type_value() != IPV6_ROUTING_TYPE_MOBILE {
return Ipv6MobileRoutingHeaderStatus::InvalidRoutingType;
}
if self.effective_header_ext_len_value() != IPV6_MOBILE_ROUTING_HEADER_EXT_LEN {
return Ipv6MobileRoutingHeaderStatus::InvalidHeaderExtLen;
}
if self.segments_left_value() != IPV6_MOBILE_ROUTING_SEGMENTS_LEFT {
return Ipv6MobileRoutingHeaderStatus::InvalidSegmentsLeft;
}
if self.reserved_value() != IPV6_MOBILE_ROUTING_RESERVED {
return Ipv6MobileRoutingHeaderStatus::NonzeroReserved;
}
Ipv6MobileRoutingHeaderStatus::Valid
}
fn effective_total_len(&self) -> usize {
self.header_ext_len
.value()
.map(|value| IPV6_EXTENSION_MIN_LEN + *value as usize * 8)
.unwrap_or(IPV6_MOBILE_ROUTING_LEN)
}
fn effective_header_ext_len(&self) -> Result<u8> {
header_ext_len_from_total("ipv6.mobile.header_ext_len", self.effective_total_len())
}
fn effective_next_header(&self, next: Option<&dyn Layer>) -> u8 {
if self.next_header.is_user_set() {
return self.next_header_value();
}
next.and_then(layer_ipv6_next_header)
.or_else(|| self.next_header.value().copied())
.unwrap_or(0)
}
fn validate(&self) -> Result<()> {
validate_extension_total_len("ipv6.mobile.header_ext_len", self.effective_total_len())?;
if self.effective_total_len() < IPV6_MOBILE_ROUTING_LEN {
return Err(CrafterError::invalid_field_value(
"ipv6.mobile.header_ext_len",
"mobile routing header must be at least 24 bytes",
));
}
Ok(())
}
}
impl Default for Ipv6MobileRoutingHeader {
fn default() -> Self {
Self::new()
}
}
impl Layer for Ipv6MobileRoutingHeader {
fn name(&self) -> &'static str {
"Ipv6MobileRoutingHeader"
}
fn summary(&self) -> String {
format!(
"Ipv6MobileRoutingHeader(type={}, home={}, next={})",
routing_type_summary(self.routing_type_value()),
self.home_address_value(),
next_header_summary(self.next_header_value())
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("next_header", next_header_summary(self.next_header_value())),
(
"header_ext_len",
self.header_ext_len_value()
.map(|value| value.to_string())
.unwrap_or_else(|| "auto".to_string()),
),
(
"routing_type",
routing_type_summary(self.routing_type_value()),
),
(
"routing_type_status",
format!("{:?}", self.routing_type_status()),
),
("validity_status", format!("{:?}", self.validity_status())),
("segments_left", self.segments_left_value().to_string()),
(
"segments_left_status",
format!("{:?}", self.segments_left_status()),
),
("reserved", format!("0x{:08x}", self.reserved_value())),
("reserved_status", format!("{:?}", self.reserved_status())),
("home_address", self.home_address_value().to_string()),
]
}
fn encoded_len(&self) -> usize {
self.effective_total_len()
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
self.validate()?;
let start = out.len();
let total_len = self.effective_total_len();
out.push(self.effective_next_header(ctx.next()));
out.push(self.effective_header_ext_len()?);
out.push(self.routing_type_value());
out.push(self.segments_left_value());
out.extend_from_slice(&self.reserved_value().to_be_bytes());
out.extend_from_slice(&self.home_address_value().octets());
out.resize(start + total_len, 0);
Ok(())
}
impl_ipv6_extension_layer_object!(Ipv6MobileRoutingHeader);
}
impl_ipv6_extension_layer_div!(Ipv6MobileRoutingHeader);
pub(in crate::protocols::ip::v6) fn decode_mobile_routing_header(
bytes: &[u8],
total_len: usize,
) -> Result<Ipv6MobileRoutingHeader> {
if total_len < IPV6_MOBILE_ROUTING_LEN {
return Err(CrafterError::invalid_field_value(
"ipv6.mobile.header_ext_len",
"mobile routing header must be at least 24 bytes",
));
}
Ok(Ipv6MobileRoutingHeader {
next_header: Field::user(bytes[0]),
header_ext_len: Field::user(bytes[1]),
routing_type: Field::user(bytes[2]),
segments_left: Field::user(bytes[3]),
reserved: Field::user(read_u32_be(&bytes[4..8])?),
home_address: Field::user(Ipv6Addr::from(copy_array_16(&bytes[8..24]))),
})
}