use std::fmt;
use crate::packet::ipv4::Ipv4HeaderOpt;
use crate::packet::ipv6::Ipv6HeaderExt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IpipType {
Ipip,
Sit,
Ip4in6,
Ip6Tnl,
}
impl IpipType {
pub fn name(&self) -> &'static str {
match self {
Self::Ipip => "IPIP",
Self::Sit => "SIT",
Self::Ip4in6 => "IP4in6",
Self::Ip6Tnl => "IP6Tnl",
}
}
pub fn description(&self) -> &'static str {
match self {
Self::Ipip => "IPv4-in-IPv4 (RFC 2003)",
Self::Sit => "IPv6-in-IPv4 (RFC 4213)",
Self::Ip4in6 => "IPv4-in-IPv6 (RFC 2473)",
Self::Ip6Tnl => "IPv6-in-IPv6 (RFC 2473)",
}
}
}
impl fmt::Display for IpipType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone)]
pub enum OuterIpHeader<'a> {
V4(Ipv4HeaderOpt<'a>),
V6(Ipv6HeaderExt<'a>),
}
impl<'a> fmt::Display for OuterIpHeader<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OuterIpHeader::V4(h) => write!(f, "{}", h),
OuterIpHeader::V6(h) => write!(f, "{}", h),
}
}
}
#[derive(Debug, Clone)]
pub struct IpipTunnel<'a> {
tunnel_type: IpipType,
outer_header: OuterIpHeader<'a>,
}
impl<'a> IpipTunnel<'a> {
#[inline]
pub fn new(tunnel_type: IpipType, outer_header: OuterIpHeader<'a>) -> Self {
Self {
tunnel_type,
outer_header,
}
}
#[inline]
pub fn ipip(outer: Ipv4HeaderOpt<'a>) -> Self {
Self::new(IpipType::Ipip, OuterIpHeader::V4(outer))
}
#[inline]
pub fn sit(outer: Ipv4HeaderOpt<'a>) -> Self {
Self::new(IpipType::Sit, OuterIpHeader::V4(outer))
}
#[inline]
pub fn ip4in6(outer: Ipv6HeaderExt<'a>) -> Self {
Self::new(IpipType::Ip4in6, OuterIpHeader::V6(outer))
}
#[inline]
pub fn ip6tnl(outer: Ipv6HeaderExt<'a>) -> Self {
Self::new(IpipType::Ip6Tnl, OuterIpHeader::V6(outer))
}
#[inline]
pub fn tunnel_type(&self) -> IpipType {
self.tunnel_type
}
#[inline]
pub fn name(&self) -> &'static str {
self.tunnel_type.name()
}
#[inline]
pub fn outer_header(&self) -> &OuterIpHeader<'a> {
&self.outer_header
}
#[inline]
pub fn outer_ipv4(&self) -> Option<&Ipv4HeaderOpt<'a>> {
match &self.outer_header {
OuterIpHeader::V4(h) => Some(h),
OuterIpHeader::V6(_) => None,
}
}
#[inline]
pub fn outer_ipv6(&self) -> Option<&Ipv6HeaderExt<'a>> {
match &self.outer_header {
OuterIpHeader::V4(_) => None,
OuterIpHeader::V6(h) => Some(h),
}
}
}
impl fmt::Display for IpipTunnel<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.tunnel_type.name(), self.outer_header)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tunnel_type_names() {
assert_eq!(IpipType::Ipip.name(), "IPIP");
assert_eq!(IpipType::Sit.name(), "SIT");
assert_eq!(IpipType::Ip4in6.name(), "IP4in6");
assert_eq!(IpipType::Ip6Tnl.name(), "IP6Tnl");
}
#[test]
fn test_tunnel_type_descriptions() {
assert_eq!(IpipType::Ipip.description(), "IPv4-in-IPv4 (RFC 2003)");
assert_eq!(IpipType::Sit.description(), "IPv6-in-IPv4 (RFC 4213)");
assert_eq!(IpipType::Ip4in6.description(), "IPv4-in-IPv6 (RFC 2473)");
assert_eq!(IpipType::Ip6Tnl.description(), "IPv6-in-IPv6 (RFC 2473)");
}
#[test]
fn test_tunnel_type_display() {
assert_eq!(format!("{}", IpipType::Ipip), "IPIP");
assert_eq!(format!("{}", IpipType::Sit), "SIT");
assert_eq!(format!("{}", IpipType::Ip4in6), "IP4in6");
assert_eq!(format!("{}", IpipType::Ip6Tnl), "IP6Tnl");
}
}