use core::any::Any;
use core::net::Ipv6Addr;
use core::ops::Div;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet, TransportChecksumContext};
use crate::protocols::ip::shared::{Dscp, Ecn, DSCP_SHIFT, ECN_MASK};
use super::constants::{IPV6_HEADER_LEN, IPV6_MAX_FLOW_LABEL};
use super::{display, layer_ipv6_next_header, parse_ipv6, payload_len_after, value_or_copy};
macro_rules! impl_layer_object {
($type:ty) => {
fn clone_layer(&self) -> Box<dyn Layer> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
};
}
macro_rules! impl_layer_div {
($type:ty) => {
impl<R> Div<R> for $type
where
R: IntoPacket,
{
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ipv6 {
pub(super) version: Field<u8>,
pub(super) traffic_class: Field<u8>,
pub(super) flow_label: Field<u32>,
pub(super) payload_length: Field<u16>,
pub(super) next_header: Field<u8>,
pub(super) hop_limit: Field<u8>,
pub(super) source: Field<Ipv6Addr>,
pub(super) destination: Field<Ipv6Addr>,
}
impl Ipv6 {
pub fn new() -> Self {
Self {
version: Field::defaulted(6),
traffic_class: Field::defaulted(0),
flow_label: Field::defaulted(0),
payload_length: Field::unset(),
next_header: Field::defaulted(0),
hop_limit: Field::defaulted(64),
source: Field::defaulted(Ipv6Addr::LOCALHOST),
destination: Field::defaulted(Ipv6Addr::LOCALHOST),
}
}
pub fn with_addresses(source: Ipv6Addr, destination: Ipv6Addr) -> Self {
Self::new().src(source).dst(destination)
}
pub fn version(mut self, version: u8) -> Self {
self.version.set_user(version);
self
}
pub fn traffic_class(mut self, traffic_class: u8) -> Self {
self.traffic_class.set_user(traffic_class);
self
}
pub fn tc(self, traffic_class: u8) -> Self {
self.traffic_class(traffic_class)
}
pub fn dscp(mut self, dscp: Dscp) -> Self {
let traffic_class = (dscp.value() << DSCP_SHIFT) | (self.traffic_class_value() & ECN_MASK);
self.traffic_class.set_user(traffic_class);
self
}
pub fn ecn(mut self, ecn: Ecn) -> Self {
let traffic_class = (self.traffic_class_value() & !ECN_MASK) | ecn.value();
self.traffic_class.set_user(traffic_class);
self
}
pub fn flow_label(mut self, flow_label: u32) -> Self {
self.flow_label.set_user(flow_label);
self
}
pub fn try_flow_label(mut self, flow_label: u32) -> Result<Self> {
validate_flow_label(flow_label)?;
self.flow_label.set_user(flow_label);
Ok(self)
}
pub fn fl(self, flow_label: u32) -> Self {
self.flow_label(flow_label)
}
pub fn payload_length(mut self, payload_length: u16) -> Self {
self.payload_length.set_user(payload_length);
self
}
pub fn plen(self, payload_length: u16) -> Self {
self.payload_length(payload_length)
}
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 hop_limit(mut self, hop_limit: u8) -> Self {
self.hop_limit.set_user(hop_limit);
self
}
pub fn hlim(self, hop_limit: u8) -> Self {
self.hop_limit(hop_limit)
}
pub fn src(mut self, source: Ipv6Addr) -> Self {
self.source.set_user(source);
self
}
pub fn src_str(self, source: &str) -> Result<Self> {
Ok(self.src(parse_ipv6(source)?))
}
pub fn dst(mut self, destination: Ipv6Addr) -> Self {
self.destination.set_user(destination);
self
}
pub fn dst_str(self, destination: &str) -> Result<Self> {
Ok(self.dst(parse_ipv6(destination)?))
}
pub fn version_value(&self) -> u8 {
value_or_copy(&self.version, 6)
}
pub fn traffic_class_value(&self) -> u8 {
value_or_copy(&self.traffic_class, 0)
}
pub fn dscp_value(&self) -> Dscp {
Dscp::from_u6(self.traffic_class_value() >> DSCP_SHIFT)
}
pub fn ecn_value(&self) -> Ecn {
Ecn::from_u2(self.traffic_class_value() & ECN_MASK)
}
pub fn flow_label_value(&self) -> u32 {
value_or_copy(&self.flow_label, 0)
}
pub fn payload_length_value(&self) -> Option<u16> {
self.payload_length.value().copied()
}
pub fn next_header_value(&self) -> u8 {
value_or_copy(&self.next_header, 0)
}
pub fn hop_limit_value(&self) -> u8 {
value_or_copy(&self.hop_limit, 64)
}
pub fn source(&self) -> Ipv6Addr {
value_or_copy(&self.source, Ipv6Addr::LOCALHOST)
}
pub fn destination(&self) -> Ipv6Addr {
value_or_copy(&self.destination, Ipv6Addr::LOCALHOST)
}
fn effective_payload_length(&self, payload_len: usize) -> Result<u16> {
if let Some(payload_length) = self.payload_length.value().copied() {
return Ok(payload_length);
}
u16::try_from(payload_len).map_err(|_| {
CrafterError::invalid_field_value(
"ipv6.payload_length",
"IPv6 payload length exceeds 65535 bytes",
)
})
}
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, payload_len: usize) -> Result<()> {
if self.version_value() != 6 {
return Err(CrafterError::invalid_field_value(
"ipv6.version",
"IPv6 layer version must be 6",
));
}
validate_flow_label(self.flow_label_value())?;
self.effective_payload_length(payload_len)?;
Ok(())
}
}
fn validate_flow_label(flow_label: u32) -> Result<()> {
if flow_label > IPV6_MAX_FLOW_LABEL {
return Err(CrafterError::invalid_field_value(
"ipv6.flow_label",
"flow label must fit in 20 bits",
));
}
Ok(())
}
impl Default for Ipv6 {
fn default() -> Self {
Self::new()
}
}
impl Layer for Ipv6 {
fn name(&self) -> &'static str {
"Ipv6"
}
fn summary(&self) -> String {
display::summary(self)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
display::inspection_fields(self)
}
fn encoded_len(&self) -> usize {
IPV6_HEADER_LEN
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let payload_len = payload_len_after(*ctx);
self.validate(payload_len)?;
let version_class_flow = ((self.version_value() as u32) << 28)
| ((self.traffic_class_value() as u32) << 20)
| self.flow_label_value();
out.extend_from_slice(&version_class_flow.to_be_bytes());
out.extend_from_slice(&self.effective_payload_length(payload_len)?.to_be_bytes());
out.push(self.effective_next_header(ctx.next()));
out.push(self.hop_limit_value());
out.extend_from_slice(&self.source().octets());
out.extend_from_slice(&self.destination().octets());
Ok(())
}
fn transport_checksum_context(
&self,
transport_protocol: u8,
) -> Option<TransportChecksumContext> {
Some(TransportChecksumContext::Ipv6 {
source: self.source(),
destination: self.destination(),
next_header: transport_protocol,
})
}
impl_layer_object!(Ipv6);
}
impl_layer_div!(Ipv6);