use crate::network::Asn;
use enum_primitive_derive::Primitive;
use serde::Serialize;
use std::fmt::Formatter;
use std::net::{Ipv4Addr, Ipv6Addr};
#[derive(Debug, PartialEq, Copy, Clone, Eq)]
pub enum MetaCommunity {
Community(Community),
ExtendedCommunity(ExtendedCommunity),
LargeCommunity(LargeCommunity),
}
#[derive(Debug, PartialEq, Copy, Clone, Eq)]
pub enum Community {
NoExport,
NoAdvertise,
NoExportSubConfed,
Custom(Asn, u16),
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct LargeCommunity {
pub global_administrator: u32,
pub local_data: [u32; 2],
}
impl LargeCommunity {
pub fn new(global_administrator: u32, local_data: [u32; 2]) -> LargeCommunity {
LargeCommunity {
global_administrator,
local_data,
}
}
}
#[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone)]
pub enum ExtendedCommunityType {
TransitiveTwoOctetAsSpecific = 0x00,
TransitiveIpv4AddressSpecific = 0x01,
TransitiveFourOctetAsSpecific = 0x02,
TransitiveOpaque = 0x03,
NonTransitiveTwoOctetAsSpecific = 0x40,
NonTransitiveIpv4AddressSpecific = 0x41,
NonTransitiveFourOctetAsSpecific = 0x42,
NonTransitiveOpaque = 0x43,
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub enum ExtendedCommunity {
TransitiveTwoOctetAsSpecific(TwoOctetAsSpecific),
TransitiveIpv4AddressSpecific(Ipv4AddressSpecific),
TransitiveFourOctetAsSpecific(FourOctetAsSpecific),
TransitiveOpaque(Opaque),
NonTransitiveTwoOctetAsSpecific(TwoOctetAsSpecific),
NonTransitiveIpv4AddressSpecific(Ipv4AddressSpecific),
NonTransitiveFourOctetAsSpecific(FourOctetAsSpecific),
NonTransitiveOpaque(Opaque),
Ipv6AddressSpecific(Ipv6AddressSpecific),
Raw([u8; 8]),
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct Ipv6AddressSpecific {
pub ec_type: u8,
pub ec_subtype: u8,
pub global_administrator: Ipv6Addr,
pub local_administrator: [u8; 2],
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct TwoOctetAsSpecific {
pub ec_type: u8,
pub ec_subtype: u8,
pub global_administrator: Asn,
pub local_administrator: [u8; 4],
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct FourOctetAsSpecific {
pub ec_type: u8,
pub ec_subtype: u8,
pub global_administrator: Asn,
pub local_administrator: [u8; 2],
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct Ipv4AddressSpecific {
pub ec_type: u8,
pub ec_subtype: u8,
pub global_administrator: Ipv4Addr,
pub local_administrator: [u8; 2],
}
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub struct Opaque {
pub ec_type: u8,
pub ec_subtype: u8,
pub value: [u8; 6],
}
fn bytes_to_string(bytes: &[u8]) -> String {
bytes
.iter()
.map(|x| format!("{:02X}", x))
.collect::<Vec<String>>()
.join("")
}
impl std::fmt::Display for Community {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Community::NoExport => {
"no-export".to_string()
}
Community::NoAdvertise => {
"no-advertise".to_string()
}
Community::NoExportSubConfed => {
"no-export-sub-confed".to_string()
}
Community::Custom(asn, value) => {
format!("{}:{}", asn, value)
}
}
)
}
}
impl std::fmt::Display for LargeCommunity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"lg:{}:{}:{}",
self.global_administrator, self.local_data[0], self.local_data[1]
)
}
}
impl std::fmt::Display for ExtendedCommunity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ExtendedCommunity::TransitiveTwoOctetAsSpecific(ec)
| ExtendedCommunity::NonTransitiveTwoOctetAsSpecific(ec) => {
format!(
"ecas2:{}:{}:{}:{}",
ec.ec_type,
ec.ec_subtype,
ec.global_administrator,
bytes_to_string(&ec.local_administrator)
)
}
ExtendedCommunity::TransitiveIpv4AddressSpecific(ec)
| ExtendedCommunity::NonTransitiveIpv4AddressSpecific(ec) => {
format!(
"ecv4:{}:{}:{}:{}",
ec.ec_type,
ec.ec_subtype,
ec.global_administrator,
bytes_to_string(&ec.local_administrator)
)
}
ExtendedCommunity::TransitiveFourOctetAsSpecific(ec)
| ExtendedCommunity::NonTransitiveFourOctetAsSpecific(ec) => {
format!(
"ecas4:{}:{}:{}:{}",
ec.ec_type,
ec.ec_subtype,
ec.global_administrator,
bytes_to_string(&ec.local_administrator)
)
}
ExtendedCommunity::TransitiveOpaque(ec)
| ExtendedCommunity::NonTransitiveOpaque(ec) => {
format!(
"ecop:{}:{}:{}",
ec.ec_type,
ec.ec_subtype,
bytes_to_string(&ec.value)
)
}
ExtendedCommunity::Ipv6AddressSpecific(ec) => {
format!(
"ecv6:{}:{}:{}:{}",
ec.ec_type,
ec.ec_subtype,
ec.global_administrator,
bytes_to_string(&ec.local_administrator)
)
}
ExtendedCommunity::Raw(ec) => {
format!("ecraw:{}", bytes_to_string(ec))
}
}
)
}
}
impl std::fmt::Display for MetaCommunity {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
MetaCommunity::Community(c) => {
c.to_string()
}
MetaCommunity::ExtendedCommunity(c) => {
c.to_string()
}
MetaCommunity::LargeCommunity(c) => {
c.to_string()
}
}
)
}
}
macro_rules! impl_serialize {
($a:ident) => {
impl Serialize for $a {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}
};
}
impl_serialize!(Community);
impl_serialize!(ExtendedCommunity);
impl_serialize!(LargeCommunity);
impl_serialize!(MetaCommunity);