use std::net::Ipv6Addr;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use super::constants::{RIPNG_METRIC_INFINITY, RIPNG_NEXT_HOP_METRIC, RIPNG_RTE_LEN};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RipngRte {
pub prefix: Field<Ipv6Addr>,
pub route_tag: Field<u16>,
pub prefix_len: Field<u8>,
pub metric: Field<u8>,
}
impl RipngRte {
pub fn new() -> Self {
Self {
prefix: Field::defaulted(Ipv6Addr::UNSPECIFIED),
route_tag: Field::defaulted(0),
prefix_len: Field::defaulted(0),
metric: Field::defaulted(0),
}
}
pub fn route(prefix: Ipv6Addr, prefix_len: u8, metric: u8) -> Self {
let mut rte = Self::new();
rte.prefix.set_user(prefix);
rte.prefix_len.set_user(prefix_len);
rte.metric.set_user(metric);
rte
}
pub fn next_hop(address: Ipv6Addr) -> Self {
let mut rte = Self::new();
rte.prefix.set_user(address);
rte.route_tag.set_user(0);
rte.prefix_len.set_user(0);
rte.metric.set_user(RIPNG_NEXT_HOP_METRIC);
rte
}
pub fn is_next_hop(&self) -> bool {
self.metric_value() == RIPNG_NEXT_HOP_METRIC
}
pub fn next_hop_address(&self) -> Option<Ipv6Addr> {
if self.is_next_hop() {
Some(self.prefix_value())
} else {
None
}
}
pub fn whole_table_request() -> Self {
let mut rte = Self::new();
rte.prefix.set_user(Ipv6Addr::UNSPECIFIED);
rte.route_tag.set_user(0);
rte.prefix_len.set_user(0);
rte.metric.set_user(RIPNG_METRIC_INFINITY);
rte
}
pub fn is_whole_table_request(&self) -> bool {
self.prefix_value() == Ipv6Addr::UNSPECIFIED
&& self.prefix_len_value() == 0
&& self.metric_value() == RIPNG_METRIC_INFINITY
}
pub fn prefix(mut self, value: Ipv6Addr) -> Self {
self.prefix.set_user(value);
self
}
pub fn route_tag(mut self, value: u16) -> Self {
self.route_tag.set_user(value);
self
}
pub fn prefix_len(mut self, value: u8) -> Self {
self.prefix_len.set_user(value);
self
}
pub fn metric(mut self, value: u8) -> Self {
self.metric.set_user(value);
self
}
pub fn prefix_value(&self) -> Ipv6Addr {
self.prefix
.value()
.copied()
.unwrap_or(Ipv6Addr::UNSPECIFIED)
}
pub fn route_tag_value(&self) -> u16 {
self.route_tag.value().copied().unwrap_or(0)
}
pub fn prefix_len_value(&self) -> u8 {
self.prefix_len.value().copied().unwrap_or(0)
}
pub fn metric_value(&self) -> u8 {
self.metric.value().copied().unwrap_or(0)
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.prefix_value().octets());
out.extend_from_slice(&self.route_tag_value().to_be_bytes());
out.push(self.prefix_len_value());
out.push(self.metric_value());
}
pub const fn encoded_len(&self) -> usize {
RIPNG_RTE_LEN
}
#[allow(dead_code)]
pub(crate) fn decode(bytes: &[u8]) -> Result<RipngRte> {
if bytes.len() < RIPNG_RTE_LEN {
return Err(CrafterError::buffer_too_short(
"RIPng route table entry",
RIPNG_RTE_LEN,
bytes.len(),
));
}
let mut prefix_octets = [0u8; 16];
prefix_octets.copy_from_slice(&bytes[0..16]);
let prefix = Ipv6Addr::from(prefix_octets);
let route_tag = u16::from_be_bytes([bytes[16], bytes[17]]);
let prefix_len = bytes[18];
let metric = bytes[19];
let mut rte = RipngRte::new();
rte.prefix.set_user(prefix);
rte.route_tag.set_user(route_tag);
rte.prefix_len.set_user(prefix_len);
rte.metric.set_user(metric);
Ok(rte)
}
}
impl Default for RipngRte {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod ripng_rte_builder_sets_fields {
use super::*;
#[test]
fn builders_set_and_read_back_each_field() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::new()
.prefix(prefix)
.route_tag(0xABCD)
.prefix_len(64)
.metric(7);
assert_eq!(rte.prefix_value(), prefix);
assert_eq!(rte.route_tag_value(), 0xABCD);
assert_eq!(rte.prefix_len_value(), 64);
assert_eq!(rte.metric_value(), 7);
assert!(rte.prefix.is_user_set());
assert!(rte.route_tag.is_user_set());
assert!(rte.prefix_len.is_user_set());
assert!(rte.metric.is_user_set());
}
#[test]
fn defaults_are_present_but_not_user_set() {
let rte = RipngRte::new();
assert_eq!(rte.prefix_value(), Ipv6Addr::UNSPECIFIED);
assert_eq!(rte.route_tag_value(), 0);
assert_eq!(rte.prefix_len_value(), 0);
assert_eq!(rte.metric_value(), 0);
assert!(rte.prefix.is_defaulted());
assert!(rte.route_tag.is_defaulted());
assert!(rte.prefix_len.is_defaulted());
assert!(rte.metric.is_defaulted());
}
#[test]
fn route_constructor_sets_prefix_len_metric_user() {
let prefix = "2001:db8:1::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::route(prefix, 48, 3);
assert_eq!(rte.prefix_value(), prefix);
assert_eq!(rte.prefix_len_value(), 48);
assert_eq!(rte.metric_value(), 3);
assert_eq!(rte.route_tag_value(), 0);
assert!(rte.prefix.is_user_set());
assert!(rte.prefix_len.is_user_set());
assert!(rte.metric.is_user_set());
assert!(rte.route_tag.is_defaulted());
}
}
#[cfg(test)]
mod ripng_rte_encodes_20_octets_be {
use super::*;
#[test]
fn encodes_fields_in_big_endian_order() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::route(prefix, 32, 3).route_tag(0x1234);
let mut out = Vec::new();
rte.encode(&mut out);
let mut expected = Vec::new();
expected.extend_from_slice(&prefix.octets()); expected.extend_from_slice(&[0x12, 0x34]); expected.push(0x20); expected.push(0x03);
assert_eq!(out, expected);
assert_eq!(out.len(), RIPNG_RTE_LEN);
assert_eq!(rte.encoded_len(), RIPNG_RTE_LEN);
}
}
#[cfg(test)]
mod ripng_rte_preserves_user_values {
use super::*;
#[test]
fn out_of_range_prefix_len_serializes_exactly() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::route(prefix, 200, 3);
let mut out = Vec::new();
rte.encode(&mut out);
assert_eq!(out[18], 0xC8);
assert_eq!(out.len(), RIPNG_RTE_LEN);
}
}
#[cfg(test)]
mod ripng_rte_decode_roundtrip {
use super::*;
#[test]
fn decode_reproduces_fields_and_reencodes_identically() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::route(prefix, 32, 3).route_tag(0x1234);
let mut bytes = Vec::new();
rte.encode(&mut bytes);
let decoded = RipngRte::decode(&bytes).expect("20 octets decode");
assert_eq!(decoded.prefix_value(), rte.prefix_value());
assert_eq!(decoded.route_tag_value(), rte.route_tag_value());
assert_eq!(decoded.prefix_len_value(), rte.prefix_len_value());
assert_eq!(decoded.metric_value(), rte.metric_value());
let mut reencoded = Vec::new();
decoded.encode(&mut reencoded);
assert_eq!(reencoded, bytes);
}
}
#[cfg(test)]
mod ripng_rte_decode_truncated_is_error {
use super::*;
#[test]
fn short_slice_returns_structured_error_without_panic() {
let short = [0u8; 12];
let err = RipngRte::decode(&short).expect_err("12 octets is too short");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert!(
context.contains("RIPng"),
"context should mention the RIPng RTE, got {context:?}"
);
assert_eq!(required, RIPNG_RTE_LEN);
assert_eq!(available, short.len());
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
}
#[cfg(test)]
mod ripng_next_hop_rte {
use super::*;
#[test]
fn next_hop_encodes_marker_round_trips_and_classifies() {
let address = "2001:db8::1".parse::<Ipv6Addr>().expect("valid address");
let rte = RipngRte::next_hop(address);
let mut bytes = Vec::new();
rte.encode(&mut bytes);
assert_eq!(bytes.len(), RIPNG_RTE_LEN);
assert_eq!(bytes[19], 0xFF, "metric octet marks a next-hop RTE");
assert!(rte.is_next_hop());
assert_eq!(rte.next_hop_address(), Some(address));
assert_eq!(rte.prefix_value(), address);
assert_eq!(rte.route_tag_value(), 0);
assert_eq!(rte.prefix_len_value(), 0);
assert_eq!(rte.metric_value(), RIPNG_NEXT_HOP_METRIC);
let decoded = RipngRte::decode(&bytes).expect("20 octets decode");
assert!(decoded.is_next_hop());
assert_eq!(decoded.next_hop_address(), Some(address));
let mut reencoded = Vec::new();
decoded.encode(&mut reencoded);
assert_eq!(reencoded, bytes);
}
#[test]
fn normal_route_is_not_a_next_hop() {
let prefix = "2001:db8:1::".parse::<Ipv6Addr>().expect("valid prefix");
let rte = RipngRte::route(prefix, 48, 3);
assert!(!rte.is_next_hop());
assert_eq!(rte.next_hop_address(), None);
}
}