pub mod constants;
pub mod rte;
pub use constants::*;
pub use rte::RipngRte;
use core::any::Any;
use core::ops::Div;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet};
use crate::protocols::rip::message::RipCommand;
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)]
pub struct Ripng {
pub command: Field<u8>,
pub version: Field<u8>,
pub reserved: Field<u16>,
pub rtes: Vec<RipngRte>,
}
impl Ripng {
pub fn new() -> Self {
Self {
command: Field::defaulted(RIPNG_COMMAND_RESPONSE),
version: Field::defaulted(RIPNG_VERSION_1),
reserved: Field::defaulted(0),
rtes: Vec::new(),
}
}
pub fn request() -> Self {
Self::new()
.with_command(RipCommand::Request)
.version(RIPNG_VERSION_1)
}
pub fn response() -> Self {
Self::new()
.with_command(RipCommand::Response)
.version(RIPNG_VERSION_1)
}
pub fn with_command(mut self, command: RipCommand) -> Self {
self.command.set_user(command.code());
self
}
pub fn command_code(mut self, code: u8) -> Self {
self.command.set_user(code);
self
}
pub fn version(mut self, version: u8) -> Self {
self.version.set_user(version);
self
}
pub fn reserved(mut self, reserved: u16) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn rte(mut self, rte: RipngRte) -> Self {
self.rtes.push(rte);
self
}
pub fn with_rtes(mut self, rtes: impl Into<Vec<RipngRte>>) -> Self {
self.rtes = rtes.into();
self
}
pub fn command_value(&self) -> u8 {
self.command
.value()
.copied()
.unwrap_or(RIPNG_COMMAND_RESPONSE)
}
pub fn command(&self) -> RipCommand {
RipCommand::from_code(self.command_value())
}
pub fn version_value(&self) -> u8 {
self.version.value().copied().unwrap_or(RIPNG_VERSION_1)
}
pub fn reserved_value(&self) -> u16 {
self.reserved.value().copied().unwrap_or(0)
}
pub fn rtes(&self) -> &[RipngRte] {
&self.rtes
}
}
impl Default for Ripng {
fn default() -> Self {
Self::new()
}
}
impl Layer for Ripng {
fn name(&self) -> &'static str {
"Ripng"
}
fn encoded_len(&self) -> usize {
RIPNG_HEADER_LEN + self.rtes.len() * RIPNG_RTE_LEN
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
out.reserve(self.encoded_len());
out.push(self.command_value());
out.push(self.version_value());
out.extend_from_slice(&self.reserved_value().to_be_bytes());
for rte in &self.rtes {
rte.encode(out);
}
Ok(())
}
fn summary(&self) -> String {
let count = self.rtes.len();
let plural = if count == 1 { "RTE" } else { "RTEs" };
format!(
"Ripng v{} {} ({} {})",
self.version_value(),
self.command().name(),
count,
plural
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("command", self.command().name().to_string()),
("version", self.version_value().to_string()),
("reserved", self.reserved_value().to_string()),
("rtes", self.rtes.len().to_string()),
]
}
impl_layer_object!(Ripng);
}
impl_layer_div!(Ripng);
pub fn decode(bytes: &[u8]) -> Result<Ripng> {
if bytes.len() < RIPNG_HEADER_LEN {
return Err(CrafterError::buffer_too_short(
"RIPng header",
RIPNG_HEADER_LEN,
bytes.len(),
));
}
let command = bytes[0];
let version = bytes[1];
let reserved = u16::from_be_bytes([bytes[2], bytes[3]]);
let mut ripng = Ripng::new();
ripng.command.set_user(command);
ripng.version.set_user(version);
ripng.reserved.set_user(reserved);
let mut rest = &bytes[RIPNG_HEADER_LEN..];
while !rest.is_empty() {
let rte = RipngRte::decode(rest)?;
ripng.rtes.push(rte);
rest = &rest[RIPNG_RTE_LEN..];
}
Ok(ripng)
}
pub(crate) fn append_ripng_packet(packet: Packet, bytes: &[u8]) -> Result<Packet> {
Ok(packet.push(decode(bytes)?))
}
pub(crate) fn looks_like_ripng_payload(bytes: &[u8]) -> bool {
if bytes.len() < RIPNG_HEADER_LEN {
return false;
}
let command = bytes[0];
let version = bytes[1];
matches!(command, RIPNG_COMMAND_REQUEST | RIPNG_COMMAND_RESPONSE)
&& version == RIPNG_VERSION_1
&& (bytes.len() - RIPNG_HEADER_LEN) % RIPNG_RTE_LEN == 0
}
pub fn ripng_whole_table_request(source: std::net::Ipv6Addr) -> Packet {
use crate::protocols::ip::v6::Ipv6;
use crate::protocols::transport::Udp;
Ipv6::new().src(source).dst(RIPNG_MULTICAST)
/ Udp::new().sport(RIPNG_UDP_PORT).dport(RIPNG_UDP_PORT)
/ Ripng::request().rte(RipngRte::whole_table_request())
}
#[cfg(test)]
mod ripng_decode_roundtrips_response {
use super::*;
use crate::packet::LayerContext;
use std::net::Ipv6Addr;
#[test]
fn ripng_decode_roundtrips_response() {
let first = RipngRte::route(
"2001:db8::".parse::<Ipv6Addr>().expect("valid prefix"),
64,
1,
);
let second = RipngRte::route(
"2001:db8:1::".parse::<Ipv6Addr>().expect("valid prefix"),
48,
3,
)
.route_tag(0x1234);
let ripng = Ripng::response().with_rtes(vec![first.clone(), second.clone()]);
let packet = Packet::from_layer(ripng.clone());
let ctx = LayerContext::new(&packet, 0);
let mut bytes = Vec::new();
ripng.compile(&ctx, &mut bytes).expect("ripng compiles");
let decoded = decode(&bytes).expect("two-RTE response decodes");
assert_eq!(decoded.command(), RipCommand::Response);
assert_eq!(decoded.command_value(), ripng.command_value());
assert_eq!(decoded.version_value(), ripng.version_value());
assert_eq!(decoded.rtes().len(), 2);
for (got, want) in decoded.rtes().iter().zip([&first, &second]) {
assert_eq!(got.prefix_value(), want.prefix_value());
assert_eq!(got.route_tag_value(), want.route_tag_value());
assert_eq!(got.prefix_len_value(), want.prefix_len_value());
assert_eq!(got.metric_value(), want.metric_value());
}
let repacket = Packet::from_layer(decoded.clone());
let rectx = LayerContext::new(&repacket, 0);
let mut recompiled = Vec::new();
decoded
.compile(&rectx, &mut recompiled)
.expect("decoded ripng recompiles");
assert_eq!(recompiled, bytes);
}
#[test]
fn short_header_returns_structured_error_without_panic() {
let short = [0u8; 3];
let err = decode(&short).expect_err("3 octets is too short");
match err {
CrafterError::BufferTooShort {
context,
required,
available,
} => {
assert!(
context.contains("RIPng"),
"context should mention RIPng, got {context:?}"
);
assert_eq!(required, RIPNG_HEADER_LEN);
assert_eq!(available, short.len());
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
#[test]
fn partial_trailing_rte_returns_structured_error() {
let mut bytes = vec![0x02, 0x01, 0x00, 0x00];
bytes.extend_from_slice(&[0u8; 12]);
let err = decode(&bytes).expect_err("partial RTE is too short");
match err {
CrafterError::BufferTooShort {
required,
available,
..
} => {
assert_eq!(required, RIPNG_RTE_LEN);
assert_eq!(available, 12);
}
other => panic!("expected BufferTooShort, got {other:?}"),
}
}
}
#[cfg(test)]
mod ripng_summary_mentions_command_and_count {
use super::*;
use std::net::Ipv6Addr;
#[test]
fn ripng_summary_mentions_command_and_count() {
let rtes = vec![
RipngRte::route(
"2001:db8::".parse::<Ipv6Addr>().expect("valid prefix"),
64,
1,
),
RipngRte::route(
"2001:db8:1::".parse::<Ipv6Addr>().expect("valid prefix"),
48,
2,
),
RipngRte::route(
"2001:db8:2::".parse::<Ipv6Addr>().expect("valid prefix"),
56,
3,
),
];
let summary = Ripng::response().with_rtes(rtes).summary();
assert!(
summary.contains("Response"),
"summary should name the command, got {summary:?}"
);
assert!(
summary.contains("v1"),
"summary should name the version, got {summary:?}"
);
assert!(
summary.contains('3'),
"summary should report the RTE count, got {summary:?}"
);
}
}
#[cfg(test)]
mod ripng_layer_builder {
use super::*;
use std::net::Ipv6Addr;
#[test]
fn ripng_layer_builder_sets_header_and_rtes() {
let response = Ripng::response();
assert_eq!(response.command(), RipCommand::Response);
assert_eq!(response.command_value(), RIPNG_COMMAND_RESPONSE);
assert_eq!(response.version_value(), RIPNG_VERSION_1);
assert!(response.rtes().is_empty());
let request = Ripng::request();
assert_eq!(request.command(), RipCommand::Request);
assert_eq!(request.command_value(), RIPNG_COMMAND_REQUEST);
assert_eq!(request.version_value(), RIPNG_VERSION_1);
let default = Ripng::new();
assert_eq!(default.command(), RipCommand::Response);
assert_eq!(default.version_value(), RIPNG_VERSION_1);
assert_eq!(default.reserved_value(), 0);
assert!(default.rtes().is_empty());
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let route = RipngRte::route(prefix, 32, 3);
let with_one = Ripng::response().rte(route.clone());
assert_eq!(with_one.rtes().len(), 1);
assert_eq!(with_one.rtes()[0], route);
let second = RipngRte::route(
"2001:db8:1::".parse::<Ipv6Addr>().expect("valid prefix"),
48,
5,
);
let with_two = Ripng::response().with_rtes(vec![route.clone(), second.clone()]);
assert_eq!(with_two.rtes().len(), 2);
assert_eq!(with_two.rtes()[1], second);
}
}
#[cfg(test)]
mod ripng_layer_compiles {
use super::*;
use crate::packet::LayerContext;
use std::net::Ipv6Addr;
#[test]
fn ripng_layer_compiles_header_and_rtes() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let ripng = Ripng::response().rte(RipngRte::route(prefix, 64, 1));
let packet = Packet::from_layer(ripng.clone());
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
ripng.compile(&ctx, &mut out).expect("ripng compiles");
assert_eq!(&out[..RIPNG_HEADER_LEN], &[0x02, 0x01, 0x00, 0x00]);
assert_eq!(out.len(), RIPNG_HEADER_LEN + RIPNG_RTE_LEN);
assert_eq!(ripng.encoded_len(), RIPNG_HEADER_LEN + RIPNG_RTE_LEN);
}
}
#[cfg(test)]
mod ripng_layer_div {
use super::*;
use crate::protocols::ip::v6::Ipv6;
use crate::protocols::transport::Udp;
use std::net::Ipv6Addr;
#[test]
fn ripng_layer_div_composes_into_packet() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let ripng = Ripng::response().rte(RipngRte::route(prefix, 64, 1));
let packet = Ipv6::new()
.src("2001:db8::1".parse::<Ipv6Addr>().expect("valid source"))
.dst(RIPNG_MULTICAST)
/ Udp::new().sport(RIPNG_UDP_PORT).dport(RIPNG_UDP_PORT)
/ ripng;
assert!(packet.layer::<Ripng>().is_some());
}
}
#[cfg(test)]
mod ripng_udp_binding {
use super::*;
use crate::packet::{NetworkLayer, Raw};
use crate::protocols::ip::v6::Ipv6;
use crate::protocols::transport::Udp;
use std::net::Ipv6Addr;
#[test]
fn ripng_decodes_from_udp_521() {
let prefix = "2001:db8::".parse::<Ipv6Addr>().expect("valid prefix");
let ripng = Ripng::response().rte(RipngRte::route(prefix, 64, 1));
let packet = Ipv6::new()
.src("2001:db8::1".parse::<Ipv6Addr>().expect("valid source"))
.dst(RIPNG_MULTICAST)
/ Udp::new().sport(RIPNG_UDP_PORT).dport(RIPNG_UDP_PORT)
/ ripng;
let compiled = packet.compile().expect("ripng stack compiles");
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, compiled.as_bytes())
.expect("ipv6/udp/ripng decodes");
let decoded_ripng = decoded
.layer::<Ripng>()
.expect("decoded packet includes a Ripng layer");
assert_eq!(decoded_ripng.command(), RipCommand::Response);
assert_eq!(decoded_ripng.rtes().len(), 1);
assert!(decoded.layer::<Raw>().is_none());
}
#[test]
fn ripng_non_ripng_udp_521_stays_raw() {
let payload = vec![0xFFu8, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x03];
assert!(
!looks_like_ripng_payload(&payload),
"fixture payload must not look like RIPng"
);
let packet = Ipv6::new()
.src("2001:db8::1".parse::<Ipv6Addr>().expect("valid source"))
.dst("2001:db8::2".parse::<Ipv6Addr>().expect("valid dest"))
/ Udp::new().sport(RIPNG_UDP_PORT).dport(RIPNG_UDP_PORT)
/ Raw::from_bytes(&payload);
let compiled = packet.compile().expect("udp/raw stack compiles");
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, compiled.as_bytes())
.expect("ipv6/udp/raw decodes");
assert!(
decoded.layer::<Ripng>().is_none(),
"non-RIPng port-521 payload must not decode as Ripng"
);
assert!(
decoded.layer::<Raw>().is_some(),
"non-RIPng port-521 payload must remain Raw"
);
}
}
#[cfg(test)]
mod ripng_whole_table_request_builds {
use super::*;
use crate::packet::NetworkLayer;
use crate::protocols::ip::v6::Ipv6;
use std::net::Ipv6Addr;
#[test]
fn ripng_whole_table_request_builds() {
let source = "2001:db8::1".parse::<Ipv6Addr>().expect("valid source");
let packet = ripng_whole_table_request(source);
let compiled = packet.compile().expect("ripng request stack compiles");
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, compiled.as_bytes())
.expect("ipv6/udp/ripng request decodes");
let ipv6 = decoded
.layer::<Ipv6>()
.expect("decoded packet includes an Ipv6 layer");
assert_eq!(ipv6.destination(), RIPNG_MULTICAST);
assert_eq!(
RIPNG_MULTICAST,
"ff02::9".parse::<Ipv6Addr>().expect("ff02::9")
);
let ripng = decoded
.layer::<Ripng>()
.expect("decoded packet includes a Ripng layer");
assert_eq!(ripng.command(), RipCommand::Request);
assert_eq!(ripng.rtes().len(), 1);
assert!(
ripng.rtes()[0].is_whole_table_request(),
"the single RTE must be the whole-table-request sentinel"
);
}
}