use std::net::{IpAddr, Ipv6Addr};
use netlink_packet_core::{
DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable,
};
use crate::ip::{emit_ip_addr, parse_ipv6_addr};
const SEG6_IPTUN_MODE_INLINE: u32 = 0;
const SEG6_IPTUN_MODE_ENCAP: u32 = 1;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
#[non_exhaustive]
pub enum Seg6Mode {
#[default]
Inline,
Encap,
Other(u32),
}
impl From<Seg6Mode> for u32 {
fn from(value: Seg6Mode) -> Self {
match value {
Seg6Mode::Inline => SEG6_IPTUN_MODE_INLINE,
Seg6Mode::Encap => SEG6_IPTUN_MODE_ENCAP,
Seg6Mode::Other(i) => i,
}
}
}
impl From<u32> for Seg6Mode {
fn from(value: u32) -> Self {
match value {
SEG6_IPTUN_MODE_INLINE => Seg6Mode::Inline,
SEG6_IPTUN_MODE_ENCAP => Seg6Mode::Encap,
v => Seg6Mode::Other(v),
}
}
}
const SEG6_IPTUNNEL_SRH: u16 = 1;
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum RouteSeg6IpTunnel {
Seg6(Seg6Header),
Other(DefaultNla),
}
impl Nla for RouteSeg6IpTunnel {
fn value_len(&self) -> usize {
match self {
RouteSeg6IpTunnel::Seg6(v) => v.value_len(),
RouteSeg6IpTunnel::Other(v) => v.value_len(),
}
}
fn kind(&self) -> u16 {
match self {
RouteSeg6IpTunnel::Seg6(v) => v.kind(),
RouteSeg6IpTunnel::Other(v) => v.kind(),
}
}
fn emit_value(&self, buffer: &mut [u8]) {
match self {
RouteSeg6IpTunnel::Seg6(v) => v.emit_value(buffer),
RouteSeg6IpTunnel::Other(v) => v.emit_value(buffer),
}
}
}
const SEG6_HEADER_LEN: usize = 12;
buffer!(Seg6MessageBuffer(SEG6_HEADER_LEN) {
mode: (u32, 0..4),
nexthdr: (u8, 4),
hdrlen: (u8, 5),
seg_type: (u8, 6),
segments_left: (u8, 7),
first_segment: (u8, 8),
flags: (u8, 9),
tag: (u16, 10..12),
segments: (slice, SEG6_HEADER_LEN..),
});
const SEG6_SEGMENT_LEN: usize = 16;
buffer!(Seg6SegmentBuffer(SEG6_SEGMENT_LEN) {
segment: (slice, 0..SEG6_SEGMENT_LEN),
rest: (slice, SEG6_SEGMENT_LEN..)
});
#[derive(Debug, PartialEq, Eq, Clone, Default)]
#[non_exhaustive]
pub struct Seg6Header {
pub mode: Seg6Mode,
pub segments: Vec<Ipv6Addr>,
}
impl Seg6Header {
fn push_segments(buf: &mut [u8], mut segments: Vec<Ipv6Addr>) {
if let Some(segment) = segments.pop() {
let mut segment_buffer = Seg6SegmentBuffer::new(buf);
emit_ip_addr(&IpAddr::V6(segment), segment_buffer.segment_mut());
Self::push_segments(segment_buffer.rest_mut(), segments);
}
}
fn get_segments(
buf: &[u8],
segments: &mut Vec<Ipv6Addr>,
) -> Result<(), DecodeError> {
if buf.len() >= SEG6_SEGMENT_LEN {
let segment_buffer = Seg6SegmentBuffer::new(buf);
let segment = parse_ipv6_addr(segment_buffer.segment())?;
segments.push(segment);
Self::get_segments(segment_buffer.rest(), segments)?;
}
Ok(())
}
}
impl Nla for Seg6Header {
fn value_len(&self) -> usize {
let segments = match self.mode {
Seg6Mode::Inline => self.segments.len() + 1,
Seg6Mode::Encap => self.segments.len(),
Seg6Mode::Other(_) => self.segments.len(),
};
12 + 16 * segments
}
fn kind(&self) -> u16 {
SEG6_IPTUNNEL_SRH
}
fn emit_value(&self, buffer: &mut [u8]) {
let mut seg6_header = Seg6MessageBuffer::new(buffer);
let mut number_segments = self.segments.len();
if matches!(self.mode, Seg6Mode::Inline) {
number_segments += 1 }
let srhlen = 8 + 16 * number_segments;
seg6_header.set_mode(self.mode.into());
seg6_header.set_nexthdr(0);
seg6_header.set_hdrlen(((srhlen >> 3) - 1) as u8);
seg6_header.set_seg_type(4);
seg6_header.set_segments_left((number_segments - 1) as u8);
seg6_header.set_first_segment((number_segments - 1) as u8);
seg6_header.set_flags(0);
seg6_header.set_tag(0);
let mut segments = self.segments.clone();
if matches!(self.mode, Seg6Mode::Inline) {
segments.push("::".parse().expect("Impossible error"))
}
Seg6Header::push_segments(seg6_header.segments_mut(), segments);
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
for RouteSeg6IpTunnel
{
fn parse(
buf: &NlaBuffer<&'a T>,
) -> Result<Self, netlink_packet_core::DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
SEG6_IPTUNNEL_SRH => {
let seg6_header = Seg6MessageBuffer::new(payload);
let mut segments: Vec<Ipv6Addr> = vec![];
Seg6Header::get_segments(
seg6_header.segments(),
&mut segments,
)?;
let mut segments: Vec<Ipv6Addr> =
segments.into_iter().rev().collect();
if matches!(seg6_header.mode().into(), Seg6Mode::Inline) {
segments.pop(); }
RouteSeg6IpTunnel::Seg6(Seg6Header {
mode: seg6_header.mode().into(),
segments,
})
}
_ => Self::Other(
DefaultNla::parse(buf)
.context("invalid NLA value (unknown type) value")?,
),
})
}
}