use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::protocols::ip::shared::IPPROTO_TCP;
use super::super::constants::{IPV6_FRAGMENT_HEADER_LEN, IPV6_MAX_FRAGMENT_OFFSET};
use super::super::display::{ipv6_fragment_header_status_label, next_header_summary};
use super::super::{layer_ipv6_next_header, value_or_copy};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Ipv6FragmentHeaderStatus {
Atomic,
Initial,
NonInitial,
}
impl Ipv6FragmentHeaderStatus {
pub const fn is_atomic(self) -> bool {
matches!(self, Self::Atomic)
}
pub const fn is_initial(self) -> bool {
matches!(self, Self::Atomic | Self::Initial)
}
pub const fn is_non_initial(self) -> bool {
matches!(self, Self::NonInitial)
}
pub const fn label(self) -> &'static str {
ipv6_fragment_header_status_label(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ipv6FragmentHeader {
pub(in crate::protocols::ip::v6) next_header: Field<u8>,
pub(in crate::protocols::ip::v6) reserved: Field<u8>,
pub(in crate::protocols::ip::v6) fragment_offset: Field<u16>,
pub(in crate::protocols::ip::v6) res: Field<u8>,
pub(in crate::protocols::ip::v6) more_fragments: Field<bool>,
pub(in crate::protocols::ip::v6) identification: Field<u32>,
}
impl Ipv6FragmentHeader {
pub fn new() -> Self {
Self {
next_header: Field::defaulted(IPPROTO_TCP),
reserved: Field::defaulted(0),
fragment_offset: Field::defaulted(0),
res: Field::defaulted(0),
more_fragments: Field::defaulted(false),
identification: Field::defaulted(0),
}
}
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 reserved(mut self, reserved: u8) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn fragment_offset(mut self, fragment_offset: u16) -> Self {
self.fragment_offset.set_user(fragment_offset);
self
}
pub fn offset(self, fragment_offset: u16) -> Self {
self.fragment_offset(fragment_offset)
}
pub fn frag(self, fragment_offset: u16) -> Self {
self.fragment_offset(fragment_offset)
}
pub fn res(mut self, res: u8) -> Self {
self.res.set_user(res);
self
}
pub fn more_fragments(mut self, more_fragments: bool) -> Self {
self.more_fragments.set_user(more_fragments);
self
}
pub fn mflag(self, more_fragments: bool) -> Self {
self.more_fragments(more_fragments)
}
pub fn identification(mut self, identification: u32) -> Self {
self.identification.set_user(identification);
self
}
pub fn id(self, identification: u32) -> Self {
self.identification(identification)
}
pub fn next_header_value(&self) -> u8 {
value_or_copy(&self.next_header, IPPROTO_TCP)
}
pub fn reserved_value(&self) -> u8 {
value_or_copy(&self.reserved, 0)
}
pub fn fragment_offset_value(&self) -> u16 {
value_or_copy(&self.fragment_offset, 0)
}
pub fn offset_value(&self) -> u16 {
self.fragment_offset_value()
}
pub fn fragment_offset_units(&self) -> u16 {
self.fragment_offset_value()
}
pub fn fragment_offset_bytes(&self) -> u32 {
u32::from(self.fragment_offset_value()) * 8
}
pub fn res_value(&self) -> u8 {
value_or_copy(&self.res, 0)
}
pub fn reserved_byte_value(&self) -> u8 {
self.reserved_value()
}
pub fn reserved_bits_value(&self) -> u8 {
self.res_value()
}
pub fn reserved_bits_are_zero(&self) -> bool {
self.reserved_bits_value() == 0
}
pub fn reserved_fields_are_zero(&self) -> bool {
self.reserved_byte_value() == 0 && self.reserved_bits_are_zero()
}
pub fn has_more_fragments(&self) -> bool {
value_or_copy(&self.more_fragments, false)
}
pub fn more_fragments_value(&self) -> bool {
self.has_more_fragments()
}
pub fn mflag_value(&self) -> bool {
self.has_more_fragments()
}
pub fn is_last_fragment(&self) -> bool {
!self.has_more_fragments()
}
pub fn identification_value(&self) -> u32 {
value_or_copy(&self.identification, 0)
}
pub fn id_value(&self) -> u32 {
self.identification_value()
}
pub fn fragment_status(&self) -> Ipv6FragmentHeaderStatus {
match (self.fragment_offset_value(), self.has_more_fragments()) {
(0, false) => Ipv6FragmentHeaderStatus::Atomic,
(0, true) => Ipv6FragmentHeaderStatus::Initial,
_ => Ipv6FragmentHeaderStatus::NonInitial,
}
}
pub fn status(&self) -> Ipv6FragmentHeaderStatus {
self.fragment_status()
}
pub fn fragment_status_label(&self) -> &'static str {
self.fragment_status().label()
}
pub fn is_atomic_fragment(&self) -> bool {
self.fragment_status().is_atomic()
}
pub fn is_initial_fragment(&self) -> bool {
self.fragment_status().is_initial()
}
pub fn is_non_initial_fragment(&self) -> bool {
self.fragment_status().is_non_initial()
}
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(IPPROTO_TCP)
}
fn validate(&self) -> Result<()> {
if self.fragment_offset_value() > IPV6_MAX_FRAGMENT_OFFSET {
return Err(CrafterError::invalid_field_value(
"ipv6.fragment.fragment_offset",
"fragment offset must fit in 13 bits",
));
}
if self.res_value() > 0x03 {
return Err(CrafterError::invalid_field_value(
"ipv6.fragment.res",
"fragment reserved bits must fit in two bits",
));
}
Ok(())
}
}
impl Default for Ipv6FragmentHeader {
fn default() -> Self {
Self::new()
}
}
impl Layer for Ipv6FragmentHeader {
fn name(&self) -> &'static str {
"Ipv6FragmentHeader"
}
fn summary(&self) -> String {
format!(
"Ipv6FragmentHeader(offset={}, m={}, next={})",
self.fragment_offset_value(),
self.has_more_fragments(),
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())),
("reserved", format!("0x{:02x}", self.reserved_value())),
("fragment_offset", self.fragment_offset_value().to_string()),
(
"fragment_offset_bytes",
self.fragment_offset_bytes().to_string(),
),
("fragment_status", self.fragment_status_label().to_string()),
("res", self.res_value().to_string()),
(
"reserved_bits",
format!("0b{:02b}", self.reserved_bits_value()),
),
(
"reserved_fields_zero",
self.reserved_fields_are_zero().to_string(),
),
("more_fragments", self.has_more_fragments().to_string()),
(
"identification",
format!("0x{:08x}", self.identification_value()),
),
]
}
fn encoded_len(&self) -> usize {
IPV6_FRAGMENT_HEADER_LEN
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
self.validate()?;
let fragment_field = (self.fragment_offset_value() << 3)
| ((self.res_value() as u16) << 1)
| u16::from(self.has_more_fragments());
out.push(self.effective_next_header(ctx.next()));
out.push(self.reserved_value());
out.extend_from_slice(&fragment_field.to_be_bytes());
out.extend_from_slice(&self.identification_value().to_be_bytes());
Ok(())
}
impl_ipv6_extension_layer_object!(Ipv6FragmentHeader);
}
impl_ipv6_extension_layer_div!(Ipv6FragmentHeader);