diff --git a/src/enc.rs b/src/enc.rs new file mode 100644 index 00000000..5aeefccd --- /dev/null +++ b/src/enc.rs @@ -0,0 +1,128 @@ +use netlink_packet_utils::DecodeError; + +/// An identifier for an encapsulation key used by a tunnel. +/// +/// Examples include a VNIs for VXLAN and Geneve tunnels, ERSPAN/GRE keys, and +/// GTP tunnel keys. +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct EncKeyId(u32); + +impl EncKeyId { + /// Create a new `EncKeyId` without checking the validity of the ID value. + /// + /// # Safety + /// + /// Failure to ensure the ID is within the valid range for the tunnel in + /// question may lead to semantically invalid netlink messages. + /// + /// If you know the tunnel type (e.g., vxlan) and wish to confirm that the + /// ID is within the valid range of values for that tunnel type, use the + /// corresponding new method (e.g., `new_vxlan_vni`). + #[must_use] + pub const fn new_unchecked(id: u32) -> Self { + Self(id) + } + + /// Create a new `EncKeyId` and confirm that it is within the valid range + /// of vxlan vni values. + /// + /// # Errors + /// Returns an error if the ID is zero or greater than or equal to 2^24. + pub fn new_vxlan_vni(id: u32) -> Result { + crate::net::vxlan::Vni::new(id).map(Into::into) + } + + /// Create a new `EncKeyId` and confirm that it is within the valid range + /// of geneve vni values. + /// + /// # Errors + /// + /// Returns an error if the ID is greater than or equal to 2^24. + pub fn new_geneve_vni(id: u32) -> Result { + match Self::new_nbit::<24>(id) { + Ok(id) => Ok(id), + Err(_) => Err(DecodeError::from( + "Geneve VNI must be less than 2^24, received {id}", + )), + } + } + + /// Create a new `EncKeyId` in the space of valid GRE keys. + /// + /// # Safety + /// + /// Since GRE keys are 32 bits and all values are legal, this method is not + /// failable. + #[must_use] + pub fn new_gre_key(id: u32) -> Self { + Self(id) + } + + /// Create a new `EncKeyId` and confirm that it is within the valid range + /// of gtp tunnel key values. + /// + /// # Errors + /// + /// Returns an error if the ID is zero. + pub fn new_gtp_key(id: u32) -> Result { + if id == 0 { + return Err(DecodeError::from( + "zero is not a legal GTP tunnel key", + )); + } + Ok(Self(id)) + } + + /// Create a new `EncKeyId` and confirm that it is within the valid range + /// of N bit values. + /// + /// # Errors + /// + /// Returns an error if the ID is greater than or equal to 2^N. + const fn new_nbit(id: u32) -> Result { + if id >= (1 << N) { + return Err(KeyTooLarge); + }; + Ok(Self(id)) + } +} + +impl From for u32 { + fn from(id: EncKeyId) -> u32 { + id.0 + } +} + +impl AsRef for EncKeyId { + fn as_ref(&self) -> &u32 { + &self.0 + } +} + +impl From for EncKeyId { + /// Convert `u32` to an `EncKeyId`. + /// + /// # Safety + /// + /// This conversion is infallible but may produce a semantically invalid key + /// depending on the tunnel type. + /// + /// If you know the tunnel type (e.g., vxlan) and wish to confirm that the + /// ID is within the valid range of values for that tunnel type, use the + /// corresponding "new" method on the `EncKeyId` type (e.g., + /// `EncKeyId::new_vxlan_vni`). + fn from(id: u32) -> Self { + Self(id) + } +} + +#[derive(Debug)] +#[must_use] +struct KeyTooLarge; + +impl From for EncKeyId { + fn from(vni: crate::net::vxlan::Vni) -> Self { + Self(vni.into()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 16a808bf..57a4014e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,10 +21,14 @@ mod address_family_linux; #[cfg(any(target_os = "linux", target_os = "fuchsia"))] pub use self::address_family_linux::AddressFamily; +mod enc; +pub use self::enc::*; + #[cfg(target_os = "freebsd")] mod address_family_freebsd; #[cfg(target_os = "freebsd")] pub use self::address_family_freebsd::AddressFamily; +pub mod net; #[cfg(not(any( target_os = "linux", @@ -32,6 +36,7 @@ pub use self::address_family_freebsd::AddressFamily; target_os = "freebsd", )))] mod address_family_fallback; + #[cfg(not(any( target_os = "linux", target_os = "fuchsia", diff --git a/src/message.rs b/src/message.rs index 3529f07a..c40db9e0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,14 +1,16 @@ // SPDX-License-Identifier: MIT use anyhow::Context; +use netlink_packet_core::{ + NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, +}; use netlink_packet_utils::{ DecodeError, Emitable, Parseable, ParseableParametrized, }; -use netlink_packet_core::{ - NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, +use crate::tc::{ + TcActionMessage, TcActionMessageBuffer, TcMessage, TcMessageBuffer, }; - use crate::{ address::{AddressHeader, AddressMessage, AddressMessageBuffer}, link::{LinkMessage, LinkMessageBuffer}, @@ -18,7 +20,6 @@ use crate::{ prefix::{PrefixMessage, PrefixMessageBuffer}, route::{RouteHeader, RouteMessage, RouteMessageBuffer}, rule::{RuleMessage, RuleMessageBuffer}, - tc::{TcMessage, TcMessageBuffer}, }; const RTM_NEWLINK: u16 = 16; @@ -46,9 +47,9 @@ const RTM_GETTCLASS: u16 = 42; const RTM_NEWTFILTER: u16 = 44; const RTM_DELTFILTER: u16 = 45; const RTM_GETTFILTER: u16 = 46; -// const RTM_NEWACTION: u16 = 48; -// const RTM_DELACTION: u16 = 49; -// const RTM_GETACTION: u16 = 50; +const RTM_NEWACTION: u16 = 48; +const RTM_DELACTION: u16 = 49; +const RTM_GETACTION: u16 = 50; const RTM_NEWPREFIX: u16 = 52; // const RTM_GETMULTICAST: u16 = 58; // const RTM_GETANYCAST: u16 = 62; @@ -192,18 +193,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> let msg = match RouteMessageBuffer::new_checked(&buf.inner()) { Ok(buf) => RouteMessage::parse(&buf) .context("invalid route message")?, - // HACK: iproute2 sends invalid RTM_GETROUTE message, where - // the header is limited to the - // interface family (1 byte) and 3 bytes of padding. + // HACK: iproute2 sends an invalid RTM_GETROUTE message, + // where the header is limited to the interface family + // (1 byte) and 3 bytes of padding. Err(e) => { - // Not only does iproute2 sends invalid messages, it's - // also inconsistent in - // doing so: for link and address messages, the length - // advertised in the - // netlink header includes the 3 bytes of padding but it - // does not seem to be the case - // for the route message, hence the buf.length() == 1 - // check. + // Not only does iproute2 send invalid messages, it's + // also inconsistent in doing so: for link and address + // messages, the length advertised in the netlink header + // includes the 3 bytes of padding, but it does not seem + // to be the case for the route message, hence the + // `buf.length() == 1` check. if (buf.inner().len() == 4 || buf.inner().len() == 1) && message_type == RTM_GETROUTE { @@ -252,6 +251,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> _ => unreachable!(), } } + // TC Messages RTM_NEWQDISC | RTM_DELQDISC | RTM_GETQDISC | RTM_NEWTCLASS | RTM_DELTCLASS | RTM_GETTCLASS | RTM_NEWTFILTER @@ -291,6 +291,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> } } + // TC action messages + RTM_NEWACTION | RTM_DELACTION | RTM_GETACTION => { + let msg = TcActionMessage::parse( + &TcActionMessageBuffer::new_checked(&buf.inner()) + .context("invalid tc action message buffer")?, + ) + .context("invalid tc action message")?; + match message_type { + RTM_NEWACTION => RouteNetlinkMessage::NewTrafficAction(msg), + RTM_DELACTION => RouteNetlinkMessage::DelTrafficAction(msg), + RTM_GETACTION => RouteNetlinkMessage::GetTrafficAction(msg), + _ => unreachable!(), + } + } + // ND ID Messages RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => { let err = "invalid nsid message"; @@ -348,6 +363,9 @@ pub enum RouteNetlinkMessage { NewTrafficFilter(TcMessage), DelTrafficFilter(TcMessage), GetTrafficFilter(TcMessage), + NewTrafficAction(TcActionMessage), + DelTrafficAction(TcActionMessage), + GetTrafficAction(TcActionMessage), NewTrafficChain(TcMessage), DelTrafficChain(TcMessage), GetTrafficChain(TcMessage), @@ -460,6 +478,18 @@ impl RouteNetlinkMessage { matches!(self, RouteNetlinkMessage::GetTrafficFilter(_)) } + pub fn is_new_action(&self) -> bool { + matches!(self, RouteNetlinkMessage::NewTrafficAction(_)) + } + + pub fn is_del_action(&self) -> bool { + matches!(self, RouteNetlinkMessage::DelTrafficAction(_)) + } + + pub fn is_get_action(&self) -> bool { + matches!(self, RouteNetlinkMessage::GetTrafficAction(_)) + } + pub fn is_new_chain(&self) -> bool { matches!(self, RouteNetlinkMessage::NewTrafficChain(_)) } @@ -528,6 +558,9 @@ impl RouteNetlinkMessage { NewTrafficFilter(_) => RTM_NEWTFILTER, DelTrafficFilter(_) => RTM_DELTFILTER, GetTrafficFilter(_) => RTM_GETTFILTER, + NewTrafficAction(_) => RTM_NEWACTION, + DelTrafficAction(_) => RTM_DELACTION, + GetTrafficAction(_) => RTM_GETACTION, NewTrafficChain(_) => RTM_NEWCHAIN, DelTrafficChain(_) => RTM_DELCHAIN, GetTrafficChain(_) => RTM_GETCHAIN, @@ -598,7 +631,12 @@ impl Emitable for RouteNetlinkMessage { | NewRule(ref msg) | DelRule(ref msg) | GetRule(ref msg) - => msg.buffer_len() + => msg.buffer_len(), + + | NewTrafficAction(ref msg) + | DelTrafficAction(ref msg) + | GetTrafficAction(ref msg) + => msg.buffer_len(), } } @@ -658,7 +696,12 @@ impl Emitable for RouteNetlinkMessage { | NewRule(ref msg) | DelRule(ref msg) | GetRule(ref msg) - => msg.emit(buffer) + => msg.emit(buffer), + + | NewTrafficAction(ref msg) + | DelTrafficAction(ref msg) + | GetTrafficAction(ref msg) + => msg.emit(buffer), } } } diff --git a/src/net/arp.rs b/src/net/arp.rs new file mode 100644 index 00000000..ce8f0e61 --- /dev/null +++ b/src/net/arp.rs @@ -0,0 +1,138 @@ +const RESERVED: u8 = 0; +const REQUEST: u8 = 1; +const REPLY: u8 = 2; +const REQUEST_REVERSE: u8 = 3; +const REPLY_REVERSE: u8 = 4; +const DRARP_REQUEST: u8 = 5; +const DRARP_REPLY: u8 = 6; +const DRARP_ERROR: u8 = 7; +const IN_ARP_REQUEST: u8 = 8; +const IN_ARP_REPLY: u8 = 9; +const ARP_NAK: u8 = 10; +const MARS_REQUEST: u8 = 11; +const MARS_MULTI: u8 = 12; +const MARS_MSERV: u8 = 13; +const MARS_JOIN: u8 = 14; +const MARS_LEAVE: u8 = 15; +const MARS_NAK: u8 = 16; +const MARS_UNSERV: u8 = 17; +const MARS_SJOIN: u8 = 18; +const MARS_SLEAVE: u8 = 19; +const MARS_GROUP_LIST_REQUEST: u8 = 20; +const MARS_GROUP_LIST_REPLY: u8 = 21; +const MARS_REDIRECT_MAP: u8 = 22; +const MAPO_SUNARP: u8 = 23; +const OP_EXP1: u8 = 24; +const OP_EXP2: u8 = 25; + +/// Enum of ARP operation codes. +/// +/// List from [iana.org][1] +/// +/// [1]: https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml +#[derive(Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Hash)] +#[non_exhaustive] +#[repr(u8)] +pub enum Operation { + Reserved = RESERVED, + Request = REQUEST, + Reply = REPLY, + RequestReverse = REQUEST_REVERSE, + ReplyReverse = REPLY_REVERSE, + DrarpRequest = DRARP_REQUEST, + DrarpReply = DRARP_REPLY, + DrarpError = DRARP_ERROR, + InArpRequest = IN_ARP_REQUEST, + InArpReply = IN_ARP_REPLY, + ArpNak = ARP_NAK, + MarsRequest = MARS_REQUEST, + MarsMulti = MARS_MULTI, + MarsMServ = MARS_MSERV, + MarsJoin = MARS_JOIN, + MarsLeave = MARS_LEAVE, + MarsNAK = MARS_NAK, + MarsUnserv = MARS_UNSERV, + MarsSJoin = MARS_SJOIN, + MarsSLeave = MARS_SLEAVE, + MarsGroupListRequest = MARS_GROUP_LIST_REQUEST, + MarsGroupListReply = MARS_GROUP_LIST_REPLY, + MarsRedirectMap = MARS_REDIRECT_MAP, + MapoSUnarp = MAPO_SUNARP, + OpExp1 = OP_EXP1, + OpExp2 = OP_EXP2, + Other(u8), +} + +impl AsRef for Operation { + fn as_ref(&self) -> &u8 { + match self { + Operation::Reserved => &RESERVED, + Operation::Request => &REQUEST, + Operation::Reply => &REPLY, + Operation::RequestReverse => &REQUEST_REVERSE, + Operation::ReplyReverse => &REPLY_REVERSE, + Operation::DrarpRequest => &DRARP_REQUEST, + Operation::DrarpReply => &DRARP_REPLY, + Operation::DrarpError => &DRARP_ERROR, + Operation::InArpRequest => &IN_ARP_REQUEST, + Operation::InArpReply => &IN_ARP_REPLY, + Operation::ArpNak => &ARP_NAK, + Operation::MarsRequest => &MARS_REQUEST, + Operation::MarsMulti => &MARS_MULTI, + Operation::MarsMServ => &MARS_MSERV, + Operation::MarsJoin => &MARS_JOIN, + Operation::MarsLeave => &MARS_LEAVE, + Operation::MarsNAK => &MARS_NAK, + Operation::MarsUnserv => &MARS_UNSERV, + Operation::MarsSJoin => &MARS_SJOIN, + Operation::MarsSLeave => &MARS_SLEAVE, + Operation::MarsGroupListRequest => &MARS_GROUP_LIST_REQUEST, + Operation::MarsGroupListReply => &MARS_GROUP_LIST_REPLY, + Operation::MarsRedirectMap => &MARS_REDIRECT_MAP, + Operation::MapoSUnarp => &MAPO_SUNARP, + Operation::OpExp1 => &OP_EXP1, + Operation::OpExp2 => &OP_EXP2, + Operation::Other(x) => x, + } + } +} + +impl From for Operation { + fn from(value: u8) -> Self { + match value { + RESERVED => Operation::Reserved, + REQUEST => Operation::Request, + REPLY => Operation::Reply, + REQUEST_REVERSE => Operation::RequestReverse, + REPLY_REVERSE => Operation::ReplyReverse, + DRARP_REQUEST => Operation::DrarpRequest, + DRARP_REPLY => Operation::DrarpReply, + DRARP_ERROR => Operation::DrarpError, + IN_ARP_REQUEST => Operation::InArpRequest, + IN_ARP_REPLY => Operation::InArpReply, + ARP_NAK => Operation::ArpNak, + MARS_REQUEST => Operation::MarsRequest, + MARS_MULTI => Operation::MarsMulti, + MARS_MSERV => Operation::MarsMServ, + MARS_JOIN => Operation::MarsJoin, + MARS_LEAVE => Operation::MarsLeave, + MARS_NAK => Operation::MarsNAK, + MARS_UNSERV => Operation::MarsUnserv, + MARS_SJOIN => Operation::MarsSJoin, + MARS_SLEAVE => Operation::MarsSLeave, + MARS_GROUP_LIST_REQUEST => Operation::MarsGroupListRequest, + MARS_GROUP_LIST_REPLY => Operation::MarsGroupListReply, + MARS_REDIRECT_MAP => Operation::MarsRedirectMap, + MAPO_SUNARP => Operation::MapoSUnarp, + OP_EXP1 => Operation::OpExp1, + OP_EXP2 => Operation::OpExp2, + x => Operation::Other(x), + } + } +} + +impl From for u8 { + fn from(value: Operation) -> Self { + *value.as_ref() + } +} diff --git a/src/net/ethernet.rs b/src/net/ethernet.rs new file mode 100644 index 00000000..8c29a082 --- /dev/null +++ b/src/net/ethernet.rs @@ -0,0 +1,408 @@ +use netlink_packet_utils::DecodeError; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Mac([u8; 6]); +pub type MacMask = Mac; + +impl AsRef<[u8; 6]> for Mac { + fn as_ref(&self) -> &[u8; 6] { + &self.0 + } +} + +impl From<[u8; 6]> for Mac { + fn from(val: [u8; 6]) -> Self { + Self(val) + } +} + +impl From for [u8; 6] { + fn from(val: Mac) -> Self { + val.0 + } +} + +const ETH_TYPE_IPV4: u16 = 0x0800; +const ETH_TYPE_ARP: u16 = 0x0806; +const ETH_TYPE_WAKE_ON_LAN: u16 = 0x0842; +const ETH_TYPE_STREAM_RESERVATION_PROTOCOL: u16 = 0x22EA; +const ETH_TYPE_AUDIO_VIDEO_TRANSPORT_PROTOCOL: u16 = 0x22F0; +const ETH_TYPE_IETF_TRILL_PROTOCOL: u16 = 0x22F3; +const ETH_TYPE_REVERSE_ARP: u16 = 0x8035; +const ETH_TYPE_APPLE_TALK: u16 = 0x809B; +const ETH_TYPE_APPLE_TALK_ADDRESS_RESOLUTION_PROTOCOL: u16 = 0x80F3; +const ETH_TYPE_VLAN: u16 = 0x8100; +const ETH_TYPE_SIMPLE_LOOP_PREVENTION_PROTOCOL: u16 = 0x8102; +const ETH_TYPE_VIRTUAL_LINK_AGGREGATION_CONTROL_PROTOCOL: u16 = 0x8103; +const ETH_TYPE_IPX: u16 = 0x8137; +const ETH_TYPE_QNX_QNET: u16 = 0x8204; +const ETH_TYPE_IPV6: u16 = 0x86DD; +const ETH_TYPE_ETHERNET_FLOW_CONTROL: u16 = 0x8808; +const ETH_TYPE_ETHERNET_SLOW_PROTOCOLS: u16 = 0x8809; +const ETH_TYPE_COBRA_NET: u16 = 0x8819; +const ETH_TYPE_MPLS_UNICAST: u16 = 0x8847; +const ETH_TYPE_MPLS_MULTICAST: u16 = 0x8848; +const ETH_TYPE_PPPOE_DISCOVERY: u16 = 0x8863; +const ETH_TYPE_PPPOE: u16 = 0x8864; +const ETH_TYPE_EAP_OVER_LAN: u16 = 0x888E; +const ETH_TYPE_PROFINET: u16 = 0x8892; +const ETH_TYPE_HYPER_SCSI: u16 = 0x889A; +const ETH_TYPE_ATA_OVER_ETHERNET: u16 = 0x88A2; +const ETH_TYPE_ETHER_CAT_PROTOCOL: u16 = 0x88A4; +const ETH_TYPE_QINQ: u16 = 0x88A8; +const ETH_TYPE_GOOSE: u16 = 0x88B8; +const ETH_TYPE_GSE_MANAGEMENT_SERVICES: u16 = 0x88B9; +const ETH_TYPE_SVSAMPLED_VALUE_TRANSMISSION: u16 = 0x88BA; +const ETH_TYPE_MIKRO_TIK_ROMON: u16 = 0x88BF; +const ETH_TYPE_LINK_LAYER_DISCOVERY_PROTOCOL: u16 = 0x88CC; +const ETH_TYPE_SERCOS_III: u16 = 0x88CD; +const ETH_TYPE_HOME_PLUG_GREEN_PHY: u16 = 0x88E1; +const ETH_TYPE_MEDIA_REDUNDANCY_PROTOCOL: u16 = 0x88E3; +const ETH_TYPE_MAC_SEC: u16 = 0x88E5; +const ETH_TYPE_PROVIDER_BACKBONE_BRIDGES: u16 = 0x88E7; +const ETH_TYPE_PTP: u16 = 0x88F7; +const ETH_TYPE_NC_SI: u16 = 0x88F8; +const ETH_TYPE_PARALLEL_REDUNDANCY_PROTOCOL: u16 = 0x88FB; +const ETH_TYPE_CFM: u16 = 0x8902; +const ETH_TYPE_FCOE: u16 = 0x8906; +const ETH_TYPE_FCOE_INITIALIZATION: u16 = 0x8914; +const ETH_TYPE_RO_CE: u16 = 0x8915; +const ETH_TYPE_TT_ETHERNET_PROTOCOL_CONTROL_FRAME: u16 = 0x891D; +const ETH_TYPE_HSR: u16 = 0x892F; +const ETH_TYPE_ETHERNET_CONFIGURATION_TESTING: u16 = 0x9000; +const ETH_TYPE_REDUNDANCY_TAG: u16 = 0xF1C1; + +/// Ethernet Type (Ethertype) +/// +/// Enum of Ethertypes found in ethernet frame headers. +/// The list is not exhaustive or authoritative and includes only the most +/// common Ethertypes. +/// The list is based on the [Ethernet Type Wikipedia page][1]. +/// +/// [1]: https://en.wikipedia.org/wiki/EtherType +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +#[repr(u16)] +#[non_exhaustive] +pub enum Ethertype { + IPv4 = ETH_TYPE_IPV4, + Arp = ETH_TYPE_ARP, + WakeOnLan = ETH_TYPE_WAKE_ON_LAN, + StreamReservationProtocol = ETH_TYPE_STREAM_RESERVATION_PROTOCOL, + AudioVideoTransportProtocol = ETH_TYPE_AUDIO_VIDEO_TRANSPORT_PROTOCOL, + Trill = ETH_TYPE_IETF_TRILL_PROTOCOL, + ReverseArp = ETH_TYPE_REVERSE_ARP, + AppleTalk = ETH_TYPE_APPLE_TALK, + AppleTalkAddressResolutionProtocol = + ETH_TYPE_APPLE_TALK_ADDRESS_RESOLUTION_PROTOCOL, + Vlan = ETH_TYPE_VLAN, + SimpleLoopPreventionProtocol = ETH_TYPE_SIMPLE_LOOP_PREVENTION_PROTOCOL, + VirtualLinkAggregationControlProtocol = + ETH_TYPE_VIRTUAL_LINK_AGGREGATION_CONTROL_PROTOCOL, + Ipx = ETH_TYPE_IPX, + QnxQnet = ETH_TYPE_QNX_QNET, + IPv6 = ETH_TYPE_IPV6, + EthernetFlowControl = ETH_TYPE_ETHERNET_FLOW_CONTROL, + EthernetSlowProtocols = ETH_TYPE_ETHERNET_SLOW_PROTOCOLS, + CobraNet = ETH_TYPE_COBRA_NET, + MplsUnicast = ETH_TYPE_MPLS_UNICAST, + MplsMulticast = ETH_TYPE_MPLS_MULTICAST, + PPPoEDiscovery = ETH_TYPE_PPPOE_DISCOVERY, + PPPoE = ETH_TYPE_PPPOE, + EapOverLan = ETH_TYPE_EAP_OVER_LAN, + Profinet = ETH_TYPE_PROFINET, + HyperScsi = ETH_TYPE_HYPER_SCSI, + AtaOverEthernet = ETH_TYPE_ATA_OVER_ETHERNET, + EtherCatProtocol = ETH_TYPE_ETHER_CAT_PROTOCOL, + Qinq = ETH_TYPE_QINQ, + Goose = ETH_TYPE_GOOSE, + GseManagementServices = ETH_TYPE_GSE_MANAGEMENT_SERVICES, + SvsampledValueTransmission = ETH_TYPE_SVSAMPLED_VALUE_TRANSMISSION, + MikroTikRoMon = ETH_TYPE_MIKRO_TIK_ROMON, + LinkLayerDiscoveryProtocol = ETH_TYPE_LINK_LAYER_DISCOVERY_PROTOCOL, + SercosIII = ETH_TYPE_SERCOS_III, + HomePlugGreenPhy = ETH_TYPE_HOME_PLUG_GREEN_PHY, + MediaRedundancyProtocol = ETH_TYPE_MEDIA_REDUNDANCY_PROTOCOL, + MACsec = ETH_TYPE_MAC_SEC, + ProviderBackboneBridges = ETH_TYPE_PROVIDER_BACKBONE_BRIDGES, + Ptp = ETH_TYPE_PTP, + NcSi = ETH_TYPE_NC_SI, + ParallelRedundancyProtocol = ETH_TYPE_PARALLEL_REDUNDANCY_PROTOCOL, + Cfm = ETH_TYPE_CFM, + FCoE = ETH_TYPE_FCOE, + FCoEInitialization = ETH_TYPE_FCOE_INITIALIZATION, + RoCE = ETH_TYPE_RO_CE, + TtEthernetProtocolControlFrame = + ETH_TYPE_TT_ETHERNET_PROTOCOL_CONTROL_FRAME, + Hsr = ETH_TYPE_HSR, + EthernetConfigurationTesting = ETH_TYPE_ETHERNET_CONFIGURATION_TESTING, + RedundancyTag = ETH_TYPE_REDUNDANCY_TAG, + Other(u16), +} + +impl Ethertype { + /// Returns the value as big-endian bytes. + #[must_use] + pub fn as_be_bytes(&self) -> [u8; 2] { + self.as_ref().to_be_bytes() + } +} + +impl AsRef for Ethertype { + fn as_ref(&self) -> &u16 { + match self { + Ethertype::IPv4 => Ð_TYPE_IPV4, + Ethertype::Arp => Ð_TYPE_ARP, + Ethertype::WakeOnLan => Ð_TYPE_WAKE_ON_LAN, + Ethertype::StreamReservationProtocol => { + Ð_TYPE_STREAM_RESERVATION_PROTOCOL + } + Ethertype::AudioVideoTransportProtocol => { + Ð_TYPE_AUDIO_VIDEO_TRANSPORT_PROTOCOL + } + Ethertype::Trill => Ð_TYPE_IETF_TRILL_PROTOCOL, + Ethertype::ReverseArp => Ð_TYPE_REVERSE_ARP, + Ethertype::AppleTalk => Ð_TYPE_APPLE_TALK, + Ethertype::AppleTalkAddressResolutionProtocol => { + Ð_TYPE_APPLE_TALK_ADDRESS_RESOLUTION_PROTOCOL + } + Ethertype::Vlan => Ð_TYPE_VLAN, + Ethertype::SimpleLoopPreventionProtocol => { + Ð_TYPE_SIMPLE_LOOP_PREVENTION_PROTOCOL + } + Ethertype::VirtualLinkAggregationControlProtocol => { + Ð_TYPE_VIRTUAL_LINK_AGGREGATION_CONTROL_PROTOCOL + } + Ethertype::Ipx => Ð_TYPE_IPX, + Ethertype::QnxQnet => Ð_TYPE_QNX_QNET, + Ethertype::IPv6 => Ð_TYPE_IPV6, + Ethertype::EthernetFlowControl => Ð_TYPE_ETHERNET_FLOW_CONTROL, + Ethertype::EthernetSlowProtocols => { + Ð_TYPE_ETHERNET_SLOW_PROTOCOLS + } + Ethertype::CobraNet => Ð_TYPE_COBRA_NET, + Ethertype::MplsUnicast => Ð_TYPE_MPLS_UNICAST, + Ethertype::MplsMulticast => Ð_TYPE_MPLS_MULTICAST, + Ethertype::PPPoEDiscovery => Ð_TYPE_PPPOE_DISCOVERY, + Ethertype::PPPoE => Ð_TYPE_PPPOE, + Ethertype::EapOverLan => Ð_TYPE_EAP_OVER_LAN, + Ethertype::Profinet => Ð_TYPE_PROFINET, + Ethertype::HyperScsi => Ð_TYPE_HYPER_SCSI, + Ethertype::AtaOverEthernet => Ð_TYPE_ATA_OVER_ETHERNET, + Ethertype::EtherCatProtocol => Ð_TYPE_ETHER_CAT_PROTOCOL, + Ethertype::Qinq => Ð_TYPE_QINQ, + Ethertype::Goose => Ð_TYPE_GOOSE, + Ethertype::GseManagementServices => { + Ð_TYPE_GSE_MANAGEMENT_SERVICES + } + Ethertype::SvsampledValueTransmission => { + Ð_TYPE_SVSAMPLED_VALUE_TRANSMISSION + } + Ethertype::MikroTikRoMon => Ð_TYPE_MIKRO_TIK_ROMON, + Ethertype::LinkLayerDiscoveryProtocol => { + Ð_TYPE_LINK_LAYER_DISCOVERY_PROTOCOL + } + Ethertype::SercosIII => Ð_TYPE_SERCOS_III, + Ethertype::HomePlugGreenPhy => Ð_TYPE_HOME_PLUG_GREEN_PHY, + Ethertype::MediaRedundancyProtocol => { + Ð_TYPE_MEDIA_REDUNDANCY_PROTOCOL + } + Ethertype::MACsec => Ð_TYPE_MAC_SEC, + Ethertype::ProviderBackboneBridges => { + Ð_TYPE_PROVIDER_BACKBONE_BRIDGES + } + Ethertype::Ptp => Ð_TYPE_PTP, + Ethertype::NcSi => Ð_TYPE_NC_SI, + Ethertype::ParallelRedundancyProtocol => { + Ð_TYPE_PARALLEL_REDUNDANCY_PROTOCOL + } + Ethertype::Cfm => Ð_TYPE_CFM, + Ethertype::FCoE => Ð_TYPE_FCOE, + Ethertype::FCoEInitialization => Ð_TYPE_FCOE_INITIALIZATION, + Ethertype::RoCE => Ð_TYPE_RO_CE, + Ethertype::TtEthernetProtocolControlFrame => { + Ð_TYPE_TT_ETHERNET_PROTOCOL_CONTROL_FRAME + } + Ethertype::Hsr => Ð_TYPE_HSR, + Ethertype::EthernetConfigurationTesting => { + Ð_TYPE_ETHERNET_CONFIGURATION_TESTING + } + Ethertype::RedundancyTag => Ð_TYPE_REDUNDANCY_TAG, + Ethertype::Other(other) => other, + } + } +} + +impl From for Ethertype { + fn from(val: u16) -> Self { + match val { + ETH_TYPE_IPV4 => Ethertype::IPv4, + ETH_TYPE_ARP => Ethertype::Arp, + ETH_TYPE_WAKE_ON_LAN => Ethertype::WakeOnLan, + ETH_TYPE_STREAM_RESERVATION_PROTOCOL => { + Ethertype::StreamReservationProtocol + } + ETH_TYPE_AUDIO_VIDEO_TRANSPORT_PROTOCOL => { + Ethertype::AudioVideoTransportProtocol + } + ETH_TYPE_IETF_TRILL_PROTOCOL => Ethertype::Trill, + ETH_TYPE_REVERSE_ARP => Ethertype::ReverseArp, + ETH_TYPE_APPLE_TALK => Ethertype::AppleTalk, + ETH_TYPE_APPLE_TALK_ADDRESS_RESOLUTION_PROTOCOL => { + Ethertype::AppleTalkAddressResolutionProtocol + } + ETH_TYPE_VLAN => Ethertype::Vlan, + ETH_TYPE_SIMPLE_LOOP_PREVENTION_PROTOCOL => { + Ethertype::SimpleLoopPreventionProtocol + } + ETH_TYPE_VIRTUAL_LINK_AGGREGATION_CONTROL_PROTOCOL => { + Ethertype::VirtualLinkAggregationControlProtocol + } + ETH_TYPE_IPX => Ethertype::Ipx, + ETH_TYPE_QNX_QNET => Ethertype::QnxQnet, + ETH_TYPE_IPV6 => Ethertype::IPv6, + ETH_TYPE_ETHERNET_FLOW_CONTROL => Ethertype::EthernetFlowControl, + ETH_TYPE_ETHERNET_SLOW_PROTOCOLS => { + Ethertype::EthernetSlowProtocols + } + ETH_TYPE_COBRA_NET => Ethertype::CobraNet, + ETH_TYPE_MPLS_UNICAST => Ethertype::MplsUnicast, + ETH_TYPE_MPLS_MULTICAST => Ethertype::MplsMulticast, + ETH_TYPE_PPPOE_DISCOVERY => Ethertype::PPPoEDiscovery, + ETH_TYPE_PPPOE => Ethertype::PPPoE, + ETH_TYPE_EAP_OVER_LAN => Ethertype::EapOverLan, + ETH_TYPE_PROFINET => Ethertype::Profinet, + ETH_TYPE_HYPER_SCSI => Ethertype::HyperScsi, + ETH_TYPE_ATA_OVER_ETHERNET => Ethertype::AtaOverEthernet, + ETH_TYPE_ETHER_CAT_PROTOCOL => Ethertype::EtherCatProtocol, + ETH_TYPE_QINQ => Ethertype::Qinq, + ETH_TYPE_GOOSE => Ethertype::Goose, + ETH_TYPE_GSE_MANAGEMENT_SERVICES => { + Ethertype::GseManagementServices + } + ETH_TYPE_SVSAMPLED_VALUE_TRANSMISSION => { + Ethertype::SvsampledValueTransmission + } + ETH_TYPE_MIKRO_TIK_ROMON => Ethertype::MikroTikRoMon, + ETH_TYPE_LINK_LAYER_DISCOVERY_PROTOCOL => { + Ethertype::LinkLayerDiscoveryProtocol + } + ETH_TYPE_SERCOS_III => Ethertype::SercosIII, + ETH_TYPE_HOME_PLUG_GREEN_PHY => Ethertype::HomePlugGreenPhy, + ETH_TYPE_MEDIA_REDUNDANCY_PROTOCOL => { + Ethertype::MediaRedundancyProtocol + } + ETH_TYPE_MAC_SEC => Ethertype::MACsec, + ETH_TYPE_PROVIDER_BACKBONE_BRIDGES => { + Ethertype::ProviderBackboneBridges + } + ETH_TYPE_PTP => Ethertype::Ptp, + ETH_TYPE_NC_SI => Ethertype::NcSi, + ETH_TYPE_PARALLEL_REDUNDANCY_PROTOCOL => { + Ethertype::ParallelRedundancyProtocol + } + ETH_TYPE_CFM => Ethertype::Cfm, + ETH_TYPE_FCOE => Ethertype::FCoE, + ETH_TYPE_FCOE_INITIALIZATION => Ethertype::FCoEInitialization, + ETH_TYPE_RO_CE => Ethertype::RoCE, + ETH_TYPE_TT_ETHERNET_PROTOCOL_CONTROL_FRAME => { + Ethertype::TtEthernetProtocolControlFrame + } + ETH_TYPE_HSR => Ethertype::Hsr, + ETH_TYPE_ETHERNET_CONFIGURATION_TESTING => { + Ethertype::EthernetConfigurationTesting + } + ETH_TYPE_REDUNDANCY_TAG => Ethertype::RedundancyTag, + _ => Self::Other(val), + } + } +} + +impl From for u16 { + fn from(val: Ethertype) -> Self { + *val.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct VlanId(u16); + +impl VlanId { + /// Creates a new `VlanId` value. + /// + /// # Errors + /// + /// Returns an error if the ID is greater than or equal to 4096. + pub fn new(id: u16) -> Result { + if id >= 4096 { + return Err(DecodeError::from("VLAN ID must be less than 4096")); + } + Ok(Self(id)) + } +} + +impl TryFrom for VlanId { + type Error = DecodeError; + + fn try_from(id: u16) -> Result { + Self::new(id) + } +} + +impl AsRef for VlanId { + fn as_ref(&self) -> &u16 { + &self.0 + } +} + +impl From for u16 { + fn from(val: VlanId) -> Self { + val.0 + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct VlanPrio(u8); + +impl VlanPrio { + /// Creates a new `VlanPrio` value. + /// + /// # Errors + /// + /// Returns an error if the priority is greater than 7. + pub fn new(prio: u8) -> Result { + if prio > Self::HIGHEST.into() { + return Err(DecodeError::from("VLAN priority must be less than 8")); + } + Ok(Self(prio)) + } + + const BEST_EFFORT: Self = Self(0); + const HIGHEST: Self = Self(7); +} + +impl TryFrom for VlanPrio { + type Error = DecodeError; + + fn try_from(prio: u8) -> Result { + Self::new(prio) + } +} + +impl AsRef for VlanPrio { + fn as_ref(&self) -> &u8 { + &self.0 + } +} + +impl From for u8 { + fn from(val: VlanPrio) -> Self { + val.0 + } +} + +impl Default for VlanPrio { + fn default() -> Self { + Self::BEST_EFFORT + } +} diff --git a/src/net/icmpv4.rs b/src/net/icmpv4.rs new file mode 100644 index 00000000..ca781a6e --- /dev/null +++ b/src/net/icmpv4.rs @@ -0,0 +1,1266 @@ +const ECHO_REPLY: u8 = 0; +const DESTINATION_UNREACHABLE: u8 = 3; +const SOURCE_QUENCH: u8 = 4; // deprecated by iana +const REDIRECT: u8 = 5; +const ALTERNATE_HOST_ADDRESS: u8 = 6; // deprecated by iana +const ECHO_REQUEST: u8 = 8; +const ROUTER_ADVERTISEMENT: u8 = 9; +const ROUTER_SOLICITATION: u8 = 10; +const TIME_EXCEEDED: u8 = 11; +const PARAMETER_PROBLEM: u8 = 12; +const TIMESTAMP_REQUEST: u8 = 13; +const TIMESTAMP_REPLY: u8 = 14; +const INFORMATION_REQUEST: u8 = 15; // deprecated by iana +const INFORMATION_REPLY: u8 = 16; // deprecated by iana +const ADDRESS_MASK_REQUEST: u8 = 17; // deprecated by iana +const ADDRESS_MASK_REPLY: u8 = 18; // deprecated by iana +const TRACEROUTE: u8 = 30; // deprecated by iana +const DATAGRAM_CONVERSION_ERROR: u8 = 31; // deprecated by iana +const MOBILE_HOST_REDIRECT: u8 = 32; // deprecated by iana +const IPV6_WHERE_ARE_YOU: u8 = 33; // deprecated by iana +const IPV6_IAM_HERE: u8 = 34; // deprecated by iana +const MOBILE_REGISTRATION_REQUEST: u8 = 35; // deprecated by iana +const MOBILE_REGISTRATION_REPLY: u8 = 36; // deprecated by iana +const DOMAIN_NAME_REQUEST: u8 = 37; // deprecated by iana +const DOMAIN_NAME_REPLY: u8 = 38; // deprecated by iana +const SKIP: u8 = 39; // deprecated by iana +const PHOTURIS: u8 = 40; +const EXTENDED_ECHO_REQUEST: u8 = 42; +const EXTENDED_ECHO_REPLY: u8 = 43; + +/// Enum of `ICMPv4` message types. +/// +/// This enum is non-exhaustive as more `Type`s may be added in the future by +/// the IANA. +/// +/// Codes sourced from [iana.org][1] +/// +/// [1]: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Type { + EchoReply = ECHO_REPLY, + DestinationUnreachable = DESTINATION_UNREACHABLE, + SourceQuench = SOURCE_QUENCH, // deprecated by iana + Redirect = REDIRECT, + AlternateHostAddress = ALTERNATE_HOST_ADDRESS, // deprecated by iana + EchoRequest = ECHO_REQUEST, + RouterAdvertisement = ROUTER_ADVERTISEMENT, + RouterSolicitation = ROUTER_SOLICITATION, + TimeExceeded = TIME_EXCEEDED, + ParameterProblem = PARAMETER_PROBLEM, + TimestampRequest = TIMESTAMP_REQUEST, + TimestampReply = TIMESTAMP_REPLY, + InformationRequest = INFORMATION_REQUEST, // deprecated by iana + InformationReply = INFORMATION_REPLY, // deprecated by iana + AddressMaskRequest = ADDRESS_MASK_REQUEST, // deprecated by iana + AddressMaskReply = ADDRESS_MASK_REPLY, // deprecated by iana + Traceroute = TRACEROUTE, // deprecated by iana + DatagramConversionError = DATAGRAM_CONVERSION_ERROR, // deprecated by iana + MobileHostRedirect = MOBILE_HOST_REDIRECT, // deprecated by iana + Ipv6WhereAreYou = IPV6_WHERE_ARE_YOU, // deprecated by iana + Ipv6IAmHere = IPV6_IAM_HERE, // deprecated by iana + MobileRegistrationRequest = MOBILE_REGISTRATION_REQUEST, /* deprecated + * by iana */ + MobileRegistrationReply = MOBILE_REGISTRATION_REPLY, // deprecated by iana + DomainNameRequest = DOMAIN_NAME_REQUEST, // deprecated by iana + DomainNameReply = DOMAIN_NAME_REPLY, // deprecated by iana + Skip = SKIP, // deprecated by iana + Photuris = PHOTURIS, + ExtendedEchoRequest = EXTENDED_ECHO_REQUEST, + ExtendedEchoReply = EXTENDED_ECHO_REPLY, + Other(u8), +} + +impl AsRef for Type { + fn as_ref(&self) -> &u8 { + match self { + Type::EchoReply => &ECHO_REPLY, + Type::DestinationUnreachable => &DESTINATION_UNREACHABLE, + Type::SourceQuench => &SOURCE_QUENCH, + Type::Redirect => &REDIRECT, + Type::AlternateHostAddress => &ALTERNATE_HOST_ADDRESS, + Type::EchoRequest => &ECHO_REQUEST, + Type::RouterAdvertisement => &ROUTER_ADVERTISEMENT, + Type::RouterSolicitation => &ROUTER_SOLICITATION, + Type::TimeExceeded => &TIME_EXCEEDED, + Type::ParameterProblem => &PARAMETER_PROBLEM, + Type::TimestampRequest => &TIMESTAMP_REQUEST, + Type::TimestampReply => &TIMESTAMP_REPLY, + Type::InformationRequest => &INFORMATION_REQUEST, + Type::InformationReply => &INFORMATION_REPLY, + Type::AddressMaskRequest => &ADDRESS_MASK_REQUEST, + Type::AddressMaskReply => &ADDRESS_MASK_REPLY, + Type::Traceroute => &TRACEROUTE, + Type::DatagramConversionError => &DATAGRAM_CONVERSION_ERROR, + Type::MobileHostRedirect => &MOBILE_HOST_REDIRECT, + Type::Ipv6WhereAreYou => &IPV6_WHERE_ARE_YOU, + Type::Ipv6IAmHere => &IPV6_IAM_HERE, + Type::MobileRegistrationRequest => &MOBILE_REGISTRATION_REQUEST, + Type::MobileRegistrationReply => &MOBILE_REGISTRATION_REPLY, + Type::DomainNameRequest => &DOMAIN_NAME_REQUEST, + Type::DomainNameReply => &DOMAIN_NAME_REPLY, + Type::Skip => &SKIP, + Type::Photuris => &PHOTURIS, + Type::ExtendedEchoRequest => &EXTENDED_ECHO_REQUEST, + Type::ExtendedEchoReply => &EXTENDED_ECHO_REPLY, + Type::Other(x) => x, + } + } +} + +impl From for Type { + fn from(value: u8) -> Self { + match value { + ECHO_REPLY => Type::EchoReply, + DESTINATION_UNREACHABLE => Type::DestinationUnreachable, + SOURCE_QUENCH => Type::SourceQuench, + REDIRECT => Type::Redirect, + ALTERNATE_HOST_ADDRESS => Type::AlternateHostAddress, + ECHO_REQUEST => Type::EchoRequest, + ROUTER_ADVERTISEMENT => Type::RouterAdvertisement, + ROUTER_SOLICITATION => Type::RouterSolicitation, + TIME_EXCEEDED => Type::TimeExceeded, + PARAMETER_PROBLEM => Type::ParameterProblem, + TIMESTAMP_REQUEST => Type::TimestampRequest, + TIMESTAMP_REPLY => Type::TimestampReply, + INFORMATION_REQUEST => Type::InformationRequest, + INFORMATION_REPLY => Type::InformationReply, + ADDRESS_MASK_REQUEST => Type::AddressMaskRequest, + ADDRESS_MASK_REPLY => Type::AddressMaskReply, + TRACEROUTE => Type::Traceroute, + DATAGRAM_CONVERSION_ERROR => Type::DatagramConversionError, + MOBILE_HOST_REDIRECT => Type::MobileHostRedirect, + IPV6_WHERE_ARE_YOU => Type::Ipv6WhereAreYou, + IPV6_IAM_HERE => Type::Ipv6IAmHere, + MOBILE_REGISTRATION_REQUEST => Type::MobileRegistrationRequest, + MOBILE_REGISTRATION_REPLY => Type::MobileRegistrationReply, + DOMAIN_NAME_REQUEST => Type::DomainNameRequest, + DOMAIN_NAME_REPLY => Type::DomainNameReply, + SKIP => Type::Skip, + PHOTURIS => Type::Photuris, + EXTENDED_ECHO_REQUEST => Type::ExtendedEchoRequest, + EXTENDED_ECHO_REPLY => Type::ExtendedEchoReply, + x => Type::Other(x), + } + } +} + +const NO_CODE: u8 = 0; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum EchoReply { + NoCode = NO_CODE, + Other(u8), +} + +impl AsRef for EchoReply { + fn as_ref(&self) -> &u8 { + match self { + EchoReply::NoCode => &NO_CODE, + EchoReply::Other(x) => x, + } + } +} + +impl From for EchoReply { + fn from(value: u8) -> Self { + match value { + NO_CODE => EchoReply::NoCode, + x => EchoReply::Other(x), + } + } +} + +impl From for u8 { + fn from(value: EchoReply) -> u8 { + *value.as_ref() + } +} + +const NET_UNREACHABLE: u8 = 0; +const HOST_UNREACHABLE: u8 = 1; +const PROTOCOL_UNREACHABLE: u8 = 2; +const PORT_UNREACHABLE: u8 = 3; +const FRAGMENTATION_NEEDED_AND_DONT_FRAGMENT_WAS_SET: u8 = 4; +const SOURCE_ROUTE_FAILED: u8 = 5; +const DESTINATION_NETWORK_UNKNOWN: u8 = 6; +const DESTINATION_HOST_UNKNOWN: u8 = 7; +const SOURCE_HOST_ISOLATED: u8 = 8; +const COMMUNICATION_WITH_DESTINATION_NETWORK_IS_ADMINISTRATIVELY_PROHIBITED: + u8 = 9; +const COMMUNICATION_WITH_DESTINATION_HOST_IS_ADMINISTRATIVELY_PROHIBITED: u8 = + 10; +const DESTINATION_NETWORK_UNREACHABLE_FOR_TYPE_OF_SERVICE: u8 = 11; +const DESTINATION_HOST_UNREACHABLE_FOR_TYPE_OF_SERVICE: u8 = 12; +const COMMUNICATION_ADMINISTRATIVELY_PROHIBITED: u8 = 13; +const HOST_PRECEDENCE_VIOLATION: u8 = 14; +const PRECEDENCE_CUTOFF_IN_EFFECT: u8 = 15; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum DestinationUnreachable { + NetUnreachable = NET_UNREACHABLE, + HostUnreachable = HOST_UNREACHABLE, + ProtocolUnreachable = PROTOCOL_UNREACHABLE, + PortUnreachable = PORT_UNREACHABLE, + FragmentationNeededAndDontFragmentWasSet = + FRAGMENTATION_NEEDED_AND_DONT_FRAGMENT_WAS_SET, + SourceRouteFailed = SOURCE_ROUTE_FAILED, + DestinationNetworkUnknown = DESTINATION_NETWORK_UNKNOWN, + DestinationHostUnknown = DESTINATION_HOST_UNKNOWN, + SourceHostIsolated = SOURCE_HOST_ISOLATED, + CommunicationWithDestinationNetworkIsAdministrativelyProhibited = + COMMUNICATION_WITH_DESTINATION_NETWORK_IS_ADMINISTRATIVELY_PROHIBITED, + CommunicationWithDestinationHostIsAdministrativelyProhibited = + COMMUNICATION_WITH_DESTINATION_HOST_IS_ADMINISTRATIVELY_PROHIBITED, + DestinationNetworkUnreachableForTypeOfService = + DESTINATION_NETWORK_UNREACHABLE_FOR_TYPE_OF_SERVICE, + DestinationHostUnreachableForTypeOfService = + DESTINATION_HOST_UNREACHABLE_FOR_TYPE_OF_SERVICE, + CommunicationAdministrativelyProhibited = + COMMUNICATION_ADMINISTRATIVELY_PROHIBITED, + HostPrecedenceViolation = HOST_PRECEDENCE_VIOLATION, + PrecedenceCutoffInEffect = PRECEDENCE_CUTOFF_IN_EFFECT, + Other(u8), +} + +impl AsRef for DestinationUnreachable { + fn as_ref(&self) -> &u8 { + match self { + DestinationUnreachable::NetUnreachable => &NET_UNREACHABLE, + DestinationUnreachable::HostUnreachable => &HOST_UNREACHABLE, + DestinationUnreachable::ProtocolUnreachable => &PROTOCOL_UNREACHABLE, + DestinationUnreachable::PortUnreachable => &PORT_UNREACHABLE, + DestinationUnreachable::FragmentationNeededAndDontFragmentWasSet => &FRAGMENTATION_NEEDED_AND_DONT_FRAGMENT_WAS_SET, + DestinationUnreachable::SourceRouteFailed => &SOURCE_ROUTE_FAILED, + DestinationUnreachable::DestinationNetworkUnknown => &DESTINATION_NETWORK_UNKNOWN, + DestinationUnreachable::DestinationHostUnknown => &DESTINATION_HOST_UNKNOWN, + DestinationUnreachable::SourceHostIsolated => &SOURCE_HOST_ISOLATED, + DestinationUnreachable::CommunicationWithDestinationNetworkIsAdministrativelyProhibited => &COMMUNICATION_WITH_DESTINATION_NETWORK_IS_ADMINISTRATIVELY_PROHIBITED, + DestinationUnreachable::CommunicationWithDestinationHostIsAdministrativelyProhibited => &COMMUNICATION_WITH_DESTINATION_HOST_IS_ADMINISTRATIVELY_PROHIBITED, + DestinationUnreachable::DestinationNetworkUnreachableForTypeOfService => &DESTINATION_NETWORK_UNREACHABLE_FOR_TYPE_OF_SERVICE, + DestinationUnreachable::DestinationHostUnreachableForTypeOfService => &DESTINATION_HOST_UNREACHABLE_FOR_TYPE_OF_SERVICE, + DestinationUnreachable::CommunicationAdministrativelyProhibited => &COMMUNICATION_ADMINISTRATIVELY_PROHIBITED, + DestinationUnreachable::HostPrecedenceViolation => &HOST_PRECEDENCE_VIOLATION, + DestinationUnreachable::PrecedenceCutoffInEffect => &PRECEDENCE_CUTOFF_IN_EFFECT, + DestinationUnreachable::Other(x) => x, + } + } +} + +impl From for DestinationUnreachable { + fn from(value: u8) -> Self { + match value { + NET_UNREACHABLE => DestinationUnreachable::NetUnreachable, + HOST_UNREACHABLE => DestinationUnreachable::HostUnreachable, + PROTOCOL_UNREACHABLE => DestinationUnreachable::ProtocolUnreachable, + PORT_UNREACHABLE => DestinationUnreachable::PortUnreachable, + FRAGMENTATION_NEEDED_AND_DONT_FRAGMENT_WAS_SET => DestinationUnreachable::FragmentationNeededAndDontFragmentWasSet, + SOURCE_ROUTE_FAILED => DestinationUnreachable::SourceRouteFailed, + DESTINATION_NETWORK_UNKNOWN => DestinationUnreachable::DestinationNetworkUnknown, + DESTINATION_HOST_UNKNOWN => DestinationUnreachable::DestinationHostUnknown, + SOURCE_HOST_ISOLATED => DestinationUnreachable::SourceHostIsolated, + COMMUNICATION_WITH_DESTINATION_NETWORK_IS_ADMINISTRATIVELY_PROHIBITED => DestinationUnreachable::CommunicationWithDestinationNetworkIsAdministrativelyProhibited, + COMMUNICATION_WITH_DESTINATION_HOST_IS_ADMINISTRATIVELY_PROHIBITED => DestinationUnreachable::CommunicationWithDestinationHostIsAdministrativelyProhibited, + DESTINATION_NETWORK_UNREACHABLE_FOR_TYPE_OF_SERVICE => DestinationUnreachable::DestinationNetworkUnreachableForTypeOfService, + DESTINATION_HOST_UNREACHABLE_FOR_TYPE_OF_SERVICE => DestinationUnreachable::DestinationHostUnreachableForTypeOfService, + COMMUNICATION_ADMINISTRATIVELY_PROHIBITED => DestinationUnreachable::CommunicationAdministrativelyProhibited, + HOST_PRECEDENCE_VIOLATION => DestinationUnreachable::HostPrecedenceViolation, + PRECEDENCE_CUTOFF_IN_EFFECT => DestinationUnreachable::PrecedenceCutoffInEffect, + x => DestinationUnreachable::Other(x), + } + } +} + +impl From for u8 { + fn from(value: DestinationUnreachable) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum SourceQuench { + NoCode = 0, + Other(u8), +} + +impl AsRef for SourceQuench { + fn as_ref(&self) -> &u8 { + match self { + SourceQuench::NoCode => &0, + SourceQuench::Other(x) => x, + } + } +} + +impl From for SourceQuench { + fn from(value: u8) -> Self { + match value { + 0 => SourceQuench::NoCode, + x => SourceQuench::Other(x), + } + } +} + +impl From for u8 { + fn from(value: SourceQuench) -> u8 { + *value.as_ref() + } +} + +mod redirect { + pub(super) const NET: u8 = 0; + pub(super) const HOST: u8 = 1; + pub(super) const TO_SAND_NET: u8 = 2; + pub(super) const TO_SAND_HOST: u8 = 3; +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Redirect { + Net = redirect::NET, + Host = redirect::HOST, + ToSAndNet = redirect::TO_SAND_NET, + ToSAndHost = redirect::TO_SAND_HOST, + Other(u8), +} + +impl AsRef for Redirect { + fn as_ref(&self) -> &u8 { + match self { + Redirect::Net => &redirect::NET, + Redirect::Host => &redirect::HOST, + Redirect::ToSAndNet => &redirect::TO_SAND_NET, + Redirect::ToSAndHost => &redirect::TO_SAND_HOST, + Redirect::Other(x) => x, + } + } +} + +impl From for Redirect { + fn from(value: u8) -> Self { + match value { + redirect::NET => Redirect::Net, + redirect::HOST => Redirect::Host, + redirect::TO_SAND_NET => Redirect::ToSAndNet, + redirect::TO_SAND_HOST => Redirect::ToSAndHost, + x => Redirect::Other(x), + } + } +} + +impl From for u8 { + fn from(value: Redirect) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum AlternateHostAddress { + NoCode = 0, + Other(u8), +} + +impl AsRef for AlternateHostAddress { + fn as_ref(&self) -> &u8 { + match self { + AlternateHostAddress::NoCode => &0, + AlternateHostAddress::Other(x) => x, + } + } +} + +impl From for AlternateHostAddress { + fn from(value: u8) -> Self { + match value { + 0 => AlternateHostAddress::NoCode, + x => AlternateHostAddress::Other(x), + } + } +} + +impl From for u8 { + fn from(value: AlternateHostAddress) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum EchoRequest { + NoCode = 0, + Other(u8), +} + +impl AsRef for EchoRequest { + fn as_ref(&self) -> &u8 { + match self { + EchoRequest::NoCode => &0, + EchoRequest::Other(x) => x, + } + } +} + +impl From for EchoRequest { + fn from(value: u8) -> Self { + match value { + 0 => EchoRequest::NoCode, + x => EchoRequest::Other(x), + } + } +} + +impl From for u8 { + fn from(value: EchoRequest) -> u8 { + *value.as_ref() + } +} + +const DOES_NOT_ROUTE_COMMON_TRAFFIC: u8 = 16; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum RouterAdvertisement { + NoCode = 0, + DoesNotRouteCommonTraffic = DOES_NOT_ROUTE_COMMON_TRAFFIC, + Other(u8), +} + +impl AsRef for RouterAdvertisement { + fn as_ref(&self) -> &u8 { + match self { + RouterAdvertisement::NoCode => &0, + RouterAdvertisement::DoesNotRouteCommonTraffic => { + &DOES_NOT_ROUTE_COMMON_TRAFFIC + } + RouterAdvertisement::Other(x) => x, + } + } +} + +impl From for RouterAdvertisement { + fn from(value: u8) -> Self { + match value { + 0 => RouterAdvertisement::NoCode, + DOES_NOT_ROUTE_COMMON_TRAFFIC => { + RouterAdvertisement::DoesNotRouteCommonTraffic + } + x => RouterAdvertisement::Other(x), + } + } +} + +impl From for u8 { + fn from(value: RouterAdvertisement) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum RouterSolicitation { + NoCode = 0, + Other(u8), +} + +impl AsRef for RouterSolicitation { + fn as_ref(&self) -> &u8 { + match self { + RouterSolicitation::NoCode => &0, + RouterSolicitation::Other(x) => x, + } + } +} + +impl From for RouterSolicitation { + fn from(value: u8) -> Self { + match value { + 0 => RouterSolicitation::NoCode, + x => RouterSolicitation::Other(x), + } + } +} + +impl From for u8 { + fn from(value: RouterSolicitation) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum TimeExceeded { + TtlExceededInTransit = 0, + FragmentReassembly = 1, + Other(u8), +} + +impl AsRef for TimeExceeded { + fn as_ref(&self) -> &u8 { + match self { + TimeExceeded::TtlExceededInTransit => &0, + TimeExceeded::FragmentReassembly => &1, + TimeExceeded::Other(x) => x, + } + } +} + +impl From for TimeExceeded { + fn from(value: u8) -> Self { + match value { + 0 => TimeExceeded::TtlExceededInTransit, + 1 => TimeExceeded::FragmentReassembly, + x => TimeExceeded::Other(x), + } + } +} + +impl From for u8 { + fn from(value: TimeExceeded) -> u8 { + *value.as_ref() + } +} + +const POINTER_INDICATES_THE_ERROR: u8 = 0; +const MISSING_A_REQUIRED_OPTION: u8 = 1; +const BAD_LENGTH: u8 = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum ParameterProblem { + PointerIndicatesTheError = POINTER_INDICATES_THE_ERROR, + MissingARequiredOption = MISSING_A_REQUIRED_OPTION, + BadLength = BAD_LENGTH, + Other(u8), +} + +impl AsRef for ParameterProblem { + fn as_ref(&self) -> &u8 { + match self { + ParameterProblem::PointerIndicatesTheError => { + &POINTER_INDICATES_THE_ERROR + } + ParameterProblem::MissingARequiredOption => { + &MISSING_A_REQUIRED_OPTION + } + ParameterProblem::BadLength => &BAD_LENGTH, + ParameterProblem::Other(x) => x, + } + } +} + +impl From for ParameterProblem { + fn from(value: u8) -> Self { + match value { + POINTER_INDICATES_THE_ERROR => { + ParameterProblem::PointerIndicatesTheError + } + MISSING_A_REQUIRED_OPTION => { + ParameterProblem::MissingARequiredOption + } + BAD_LENGTH => ParameterProblem::BadLength, + x => ParameterProblem::Other(x), + } + } +} + +impl From for u8 { + fn from(value: ParameterProblem) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum TimestampRequest { + NoCode = 0, + Other(u8), +} + +impl AsRef for TimestampRequest { + fn as_ref(&self) -> &u8 { + match self { + TimestampRequest::NoCode => &0, + TimestampRequest::Other(x) => x, + } + } +} + +impl From for TimestampRequest { + fn from(value: u8) -> Self { + match value { + 0 => TimestampRequest::NoCode, + x => TimestampRequest::Other(x), + } + } +} + +impl From for u8 { + fn from(value: TimestampRequest) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum TimestampReply { + NoCode = 0, + Other(u8), +} + +impl AsRef for TimestampReply { + fn as_ref(&self) -> &u8 { + match self { + TimestampReply::NoCode => &0, + TimestampReply::Other(x) => x, + } + } +} + +impl From for TimestampReply { + fn from(value: u8) -> Self { + match value { + 0 => TimestampReply::NoCode, + x => TimestampReply::Other(x), + } + } +} + +impl From for u8 { + fn from(value: TimestampReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum InformationRequest { + NoCode = 0, + Other(u8), +} + +impl AsRef for InformationRequest { + fn as_ref(&self) -> &u8 { + match self { + InformationRequest::NoCode => &0, + InformationRequest::Other(x) => x, + } + } +} + +impl From for InformationRequest { + fn from(value: u8) -> Self { + match value { + 0 => InformationRequest::NoCode, + x => InformationRequest::Other(x), + } + } +} + +impl From for u8 { + fn from(value: InformationRequest) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum InformationReply { + NoCode = 0, + Other(u8), +} + +impl AsRef for InformationReply { + fn as_ref(&self) -> &u8 { + match self { + InformationReply::NoCode => &0, + InformationReply::Other(x) => x, + } + } +} + +impl From for InformationReply { + fn from(value: u8) -> Self { + match value { + 0 => InformationReply::NoCode, + x => InformationReply::Other(x), + } + } +} + +impl From for u8 { + fn from(value: InformationReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum AddressMaskRequest { + NoCode = 0, + Other(u8), +} + +impl AsRef for AddressMaskRequest { + fn as_ref(&self) -> &u8 { + match self { + AddressMaskRequest::NoCode => &0, + AddressMaskRequest::Other(x) => x, + } + } +} + +impl From for AddressMaskRequest { + fn from(value: u8) -> Self { + match value { + 0 => AddressMaskRequest::NoCode, + x => AddressMaskRequest::Other(x), + } + } +} + +impl From for u8 { + fn from(value: AddressMaskRequest) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum AddressMaskReply { + NoCode = 0, + Other(u8), +} + +impl AsRef for AddressMaskReply { + fn as_ref(&self) -> &u8 { + match self { + AddressMaskReply::NoCode => &0, + AddressMaskReply::Other(x) => x, + } + } +} + +impl From for AddressMaskReply { + fn from(value: u8) -> Self { + match value { + 0 => AddressMaskReply::NoCode, + x => AddressMaskReply::Other(x), + } + } +} + +impl From for u8 { + fn from(value: AddressMaskReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Traceroute { + NoCode = 0, + Other(u8), +} + +impl AsRef for Traceroute { + fn as_ref(&self) -> &u8 { + match self { + Traceroute::NoCode => &0, + Traceroute::Other(x) => x, + } + } +} + +impl From for Traceroute { + fn from(value: u8) -> Self { + match value { + 0 => Traceroute::NoCode, + x => Traceroute::Other(x), + } + } +} + +impl From for u8 { + fn from(value: Traceroute) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum DatagramConversionError { + Other(u8), +} + +impl AsRef for DatagramConversionError { + fn as_ref(&self) -> &u8 { + match self { + DatagramConversionError::Other(x) => x, + } + } +} + +impl From for DatagramConversionError { + fn from(value: u8) -> Self { + DatagramConversionError::Other(value) + } +} + +impl From for u8 { + fn from(value: DatagramConversionError) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum MobileHostRedirect { + Other(u8), +} + +impl AsRef for MobileHostRedirect { + fn as_ref(&self) -> &u8 { + match self { + MobileHostRedirect::Other(x) => x, + } + } +} + +impl From for MobileHostRedirect { + fn from(value: u8) -> Self { + MobileHostRedirect::Other(value) + } +} + +impl From for u8 { + fn from(value: MobileHostRedirect) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Ipv6WhereAreYou { + Other(u8), +} + +impl AsRef for Ipv6WhereAreYou { + fn as_ref(&self) -> &u8 { + match self { + Ipv6WhereAreYou::Other(x) => x, + } + } +} + +impl From for Ipv6WhereAreYou { + fn from(value: u8) -> Self { + Ipv6WhereAreYou::Other(value) + } +} + +impl From for u8 { + fn from(value: Ipv6WhereAreYou) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Ipv6IAmHere { + Other(u8), +} + +impl AsRef for Ipv6IAmHere { + fn as_ref(&self) -> &u8 { + match self { + Ipv6IAmHere::Other(x) => x, + } + } +} + +impl From for Ipv6IAmHere { + fn from(value: u8) -> Self { + Ipv6IAmHere::Other(value) + } +} + +impl From for u8 { + fn from(value: Ipv6IAmHere) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum MobileRegistrationRequest { + Other(u8), +} + +impl AsRef for MobileRegistrationRequest { + fn as_ref(&self) -> &u8 { + match self { + MobileRegistrationRequest::Other(x) => x, + } + } +} + +impl From for MobileRegistrationRequest { + fn from(value: u8) -> Self { + MobileRegistrationRequest::Other(value) + } +} + +impl From for u8 { + fn from(value: MobileRegistrationRequest) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum MobileRegistrationReply { + Other(u8), +} + +impl AsRef for MobileRegistrationReply { + fn as_ref(&self) -> &u8 { + match self { + MobileRegistrationReply::Other(x) => x, + } + } +} + +impl From for MobileRegistrationReply { + fn from(value: u8) -> Self { + MobileRegistrationReply::Other(value) + } +} + +impl From for u8 { + fn from(value: MobileRegistrationReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum DomainNameRequest { + Other(u8), +} + +impl AsRef for DomainNameRequest { + fn as_ref(&self) -> &u8 { + match self { + DomainNameRequest::Other(x) => x, + } + } +} + +impl From for DomainNameRequest { + fn from(value: u8) -> Self { + DomainNameRequest::Other(value) + } +} + +impl From for u8 { + fn from(value: DomainNameRequest) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum DomainNameReply { + Other(u8), +} + +impl AsRef for DomainNameReply { + fn as_ref(&self) -> &u8 { + match self { + DomainNameReply::Other(x) => x, + } + } +} + +impl From for DomainNameReply { + fn from(value: u8) -> Self { + DomainNameReply::Other(value) + } +} + +impl From for u8 { + fn from(value: DomainNameReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Skip { + Other(u8), +} + +impl AsRef for Skip { + fn as_ref(&self) -> &u8 { + match self { + Skip::Other(x) => x, + } + } +} + +impl From for Skip { + fn from(value: u8) -> Self { + Skip::Other(value) + } +} + +impl From for u8 { + fn from(value: Skip) -> u8 { + *value.as_ref() + } +} + +const BAD_SPI: u8 = 0; +const AUTHENTICATION_FAILED: u8 = 1; +const DECOMPRESSION_FAILED: u8 = 2; +const DECRYPTION_FAILED: u8 = 3; +const NEED_AUTHENTICATION: u8 = 4; +const NEED_AUTHORIZATION: u8 = 5; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Photuris { + BadSpi = BAD_SPI, + AuthenticationFailed = AUTHENTICATION_FAILED, + DecompressionFailed = DECOMPRESSION_FAILED, + DecryptionFailed = DECRYPTION_FAILED, + NeedAuthentication = NEED_AUTHENTICATION, + NeedAuthorization = NEED_AUTHORIZATION, + Other(u8), +} + +impl AsRef for Photuris { + fn as_ref(&self) -> &u8 { + match self { + Photuris::BadSpi => &BAD_SPI, + Photuris::AuthenticationFailed => &AUTHENTICATION_FAILED, + Photuris::DecompressionFailed => &DECOMPRESSION_FAILED, + Photuris::DecryptionFailed => &DECRYPTION_FAILED, + Photuris::NeedAuthentication => &NEED_AUTHENTICATION, + Photuris::NeedAuthorization => &NEED_AUTHORIZATION, + Photuris::Other(x) => x, + } + } +} + +impl From for Photuris { + fn from(value: u8) -> Self { + match value { + BAD_SPI => Photuris::BadSpi, + AUTHENTICATION_FAILED => Photuris::AuthenticationFailed, + DECOMPRESSION_FAILED => Photuris::DecompressionFailed, + DECRYPTION_FAILED => Photuris::DecryptionFailed, + NEED_AUTHENTICATION => Photuris::NeedAuthentication, + NEED_AUTHORIZATION => Photuris::NeedAuthorization, + x => Photuris::Other(x), + } + } +} + +impl From for u8 { + fn from(value: Photuris) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum ExtendedEchoRequest { + NoError = 0, + Other(u8), +} + +impl AsRef for ExtendedEchoRequest { + fn as_ref(&self) -> &u8 { + match self { + ExtendedEchoRequest::NoError => &0, + ExtendedEchoRequest::Other(x) => x, + } + } +} + +impl From for ExtendedEchoRequest { + fn from(value: u8) -> Self { + match value { + 0 => ExtendedEchoRequest::NoError, + x => ExtendedEchoRequest::Other(x), + } + } +} + +impl From for u8 { + fn from(value: ExtendedEchoRequest) -> u8 { + *value.as_ref() + } +} + +const NO_ERROR: u8 = 0; +const MALFORMED_QUERY: u8 = 1; +const NO_SUCH_INTERFACE: u8 = 2; +const NO_SUCH_TABLE_ENTRY: u8 = 3; +const MULTIPLE_INTERFACES_SATISFY_QUERY: u8 = 4; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum ExtendedEchoReply { + NoError = NO_ERROR, + MalformedQuery = MALFORMED_QUERY, + NoSuchInterface = NO_SUCH_INTERFACE, + NoSuchTableEntry = NO_SUCH_TABLE_ENTRY, + MultipleInterfacesSatisfyQuery = MULTIPLE_INTERFACES_SATISFY_QUERY, + Other(u8), +} + +impl AsRef for ExtendedEchoReply { + fn as_ref(&self) -> &u8 { + match self { + ExtendedEchoReply::NoError => &NO_ERROR, + ExtendedEchoReply::MalformedQuery => &MALFORMED_QUERY, + ExtendedEchoReply::NoSuchInterface => &NO_SUCH_INTERFACE, + ExtendedEchoReply::NoSuchTableEntry => &NO_SUCH_TABLE_ENTRY, + ExtendedEchoReply::MultipleInterfacesSatisfyQuery => { + &MULTIPLE_INTERFACES_SATISFY_QUERY + } + ExtendedEchoReply::Other(x) => x, + } + } +} + +impl From for ExtendedEchoReply { + fn from(value: u8) -> Self { + match value { + NO_ERROR => ExtendedEchoReply::NoError, + MALFORMED_QUERY => ExtendedEchoReply::MalformedQuery, + NO_SUCH_INTERFACE => ExtendedEchoReply::NoSuchInterface, + NO_SUCH_TABLE_ENTRY => ExtendedEchoReply::NoSuchTableEntry, + MULTIPLE_INTERFACES_SATISFY_QUERY => { + ExtendedEchoReply::MultipleInterfacesSatisfyQuery + } + x => ExtendedEchoReply::Other(x), + } + } +} + +impl From for u8 { + fn from(value: ExtendedEchoReply) -> u8 { + *value.as_ref() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +#[repr(u8)] +pub enum Code { + EchoReply(EchoReply), + DestinationUnreachable(DestinationUnreachable), + SourceQuench(SourceQuench), + Redirect(Redirect), + AlternateHostAddress(AlternateHostAddress), + EchoRequest(EchoRequest), + RouterAdvertisement(RouterAdvertisement), + RouterSolicitation(RouterSolicitation), + TimeExceeded(TimeExceeded), + ParameterProblem(ParameterProblem), + TimestampRequest(TimestampRequest), + TimestampReply(TimestampReply), + InformationRequest(InformationRequest), + InformationReply(InformationReply), + AddressMaskRequest(AddressMaskRequest), + AddressMaskReply(AddressMaskReply), + Traceroute(Traceroute), + DatagramConversionError(DatagramConversionError), + MobileHostRedirect(MobileHostRedirect), + Ipv6WhereAreYou(Ipv6WhereAreYou), + Ipv6IAmHere(Ipv6IAmHere), + MobileRegistrationRequest(MobileRegistrationRequest), + MobileRegistrationReply(MobileRegistrationReply), + DomainNameRequest(DomainNameRequest), + DomainNameReply(DomainNameReply), + Skip(Skip), + Photuris(Photuris), + ExtendedEchoRequest(ExtendedEchoRequest), + ExtendedEchoReply(ExtendedEchoReply), + Other(u8), +} + +impl AsRef for Code { + fn as_ref(&self) -> &u8 { + match self { + Code::EchoReply(x) => x.as_ref(), + Code::DestinationUnreachable(x) => x.as_ref(), + Code::SourceQuench(x) => x.as_ref(), + Code::Redirect(x) => x.as_ref(), + Code::AlternateHostAddress(x) => x.as_ref(), + Code::EchoRequest(x) => x.as_ref(), + Code::RouterAdvertisement(x) => x.as_ref(), + Code::RouterSolicitation(x) => x.as_ref(), + Code::TimeExceeded(x) => x.as_ref(), + Code::ParameterProblem(x) => x.as_ref(), + Code::TimestampRequest(x) => x.as_ref(), + Code::TimestampReply(x) => x.as_ref(), + Code::InformationRequest(x) => x.as_ref(), + Code::InformationReply(x) => x.as_ref(), + Code::AddressMaskRequest(x) => x.as_ref(), + Code::AddressMaskReply(x) => x.as_ref(), + Code::Traceroute(x) => x.as_ref(), + Code::DatagramConversionError(x) => x.as_ref(), + Code::MobileHostRedirect(x) => x.as_ref(), + Code::Ipv6WhereAreYou(x) => x.as_ref(), + Code::Ipv6IAmHere(x) => x.as_ref(), + Code::MobileRegistrationRequest(x) => x.as_ref(), + Code::MobileRegistrationReply(x) => x.as_ref(), + Code::DomainNameRequest(x) => x.as_ref(), + Code::DomainNameReply(x) => x.as_ref(), + Code::Skip(x) => x.as_ref(), + Code::Photuris(x) => x.as_ref(), + Code::ExtendedEchoRequest(x) => x.as_ref(), + Code::ExtendedEchoReply(x) => x.as_ref(), + Code::Other(x) => x, + } + } +} diff --git a/src/net/icmpv6.rs b/src/net/icmpv6.rs new file mode 100644 index 00000000..fdc54dfc --- /dev/null +++ b/src/net/icmpv6.rs @@ -0,0 +1,237 @@ +const DESTINATION_UNREACHABLE: u8 = 1; +const PACKET_TOO_BIG: u8 = 2; +const TIME_EXCEEDED: u8 = 3; +const PARAMETER_PROBLEM: u8 = 4; +const ECHO_REQUEST: u8 = 128; +const ECHO_REPLY: u8 = 129; +const MULTICAST_LISTENER_QUERY: u8 = 130; +const MULTICAST_LISTENER_REPORT: u8 = 131; +const MULTICAST_LISTENER_DONE: u8 = 132; +const ROUTER_SOLICITATION: u8 = 133; +const ROUTER_ADVERTISEMENT: u8 = 134; +const NEIGHBOR_SOLICITATION: u8 = 135; +const NEIGHBOR_ADVERTISEMENT: u8 = 136; +const REDIRECT_MESSAGE: u8 = 137; +const ROUTER_RENUMBERING: u8 = 138; +const ICMP_NODE_INFORMATION_QUERY: u8 = 139; +const ICMP_NODE_INFORMATION_RESPONSE: u8 = 140; +const INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE: u8 = 141; +const INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE: u8 = 142; +const VERSION2_MULTICAST_LISTENER_REPORT: u8 = 143; +const HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE: u8 = 144; +const HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE: u8 = 145; +const MOBILE_PREFIX_SOLICITATION: u8 = 146; +const MOBILE_PREFIX_ADVERTISEMENT: u8 = 147; +const CERTIFICATION_PATH_SOLICITATION_MESSAGE: u8 = 148; +const CERTIFICATION_PATH_ADVERTISEMENT_MESSAGE: u8 = 149; +const EXPERIMENTAL_MOBILITY_PROTOCOLS: u8 = 150; +const MULTICAST_ROUTER_ADVERTISEMENT: u8 = 151; +const MULTICAST_ROUTER_SOLICITATION: u8 = 152; +const MULTICAST_ROUTER_TERMINATION: u8 = 153; +const FMIPV6_MESSAGES: u8 = 154; +const RPLCONTROL_MESSAGE: u8 = 155; +const ILNPV6_LOCATOR_UPDATE_MESSAGE: u8 = 156; +const DUPLICATE_ADDRESS_REQUEST: u8 = 157; +const DUPLICATE_ADDRESS_CONFIRMATION: u8 = 158; +const MPLCONTROL_MESSAGE: u8 = 159; +const EXTENDED_ECHO_REQUEST: u8 = 160; +const EXTENDED_ECHO_REPLY: u8 = 161; + +/// Enum of `ICMPv6` message types. +/// +/// This enum is not exhaustive. +/// List sourced from [iana.org][1] +/// +/// [1]: https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-2 +#[derive(Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Hash)] +#[non_exhaustive] +#[repr(u8)] +pub enum Type { + DestinationUnreachable = DESTINATION_UNREACHABLE, + PacketTooBig = PACKET_TOO_BIG, + TimeExceeded = TIME_EXCEEDED, + ParameterProblem = PARAMETER_PROBLEM, + EchoRequest = ECHO_REQUEST, + EchoReply = ECHO_REPLY, + MulticastListenerQuery = MULTICAST_LISTENER_QUERY, + MulticastListenerReport = MULTICAST_LISTENER_REPORT, + MulticastListenerDone = MULTICAST_LISTENER_DONE, + RouterSolicitation = ROUTER_SOLICITATION, + RouterAdvertisement = ROUTER_ADVERTISEMENT, + NeighborSolicitation = NEIGHBOR_SOLICITATION, + NeighborAdvertisement = NEIGHBOR_ADVERTISEMENT, + RedirectMessage = REDIRECT_MESSAGE, + RouterRenumbering = ROUTER_RENUMBERING, + IcmpNodeInformationQuery = ICMP_NODE_INFORMATION_QUERY, + IcmpNodeInformationResponse = ICMP_NODE_INFORMATION_RESPONSE, + InverseNeighborDiscoverySolicitationMessage = + INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE, + InverseNeighborDiscoveryAdvertisementMessage = + INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE, + Version2MulticastListenerReport = VERSION2_MULTICAST_LISTENER_REPORT, + HomeAgentAddressDiscoveryRequestMessage = + HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE, + HomeAgentAddressDiscoveryReplyMessage = + HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE, + MobilePrefixSolicitation = MOBILE_PREFIX_SOLICITATION, + MobilePrefixAdvertisement = MOBILE_PREFIX_ADVERTISEMENT, + CertificationPathSolicitationMessage = + CERTIFICATION_PATH_SOLICITATION_MESSAGE, + CertificationPathAdvertisementMessage = + CERTIFICATION_PATH_ADVERTISEMENT_MESSAGE, + ExperimentalMobilityProtocols = EXPERIMENTAL_MOBILITY_PROTOCOLS, + MulticastRouterAdvertisement = MULTICAST_ROUTER_ADVERTISEMENT, + MulticastRouterSolicitation = MULTICAST_ROUTER_SOLICITATION, + MulticastRouterTermination = MULTICAST_ROUTER_TERMINATION, + FMIPv6Messages = FMIPV6_MESSAGES, + RPLControlMessage = RPLCONTROL_MESSAGE, + ILNPv6LocatorUpdateMessage = ILNPV6_LOCATOR_UPDATE_MESSAGE, + DuplicateAddressRequest = DUPLICATE_ADDRESS_REQUEST, + DuplicateAddressConfirmation = DUPLICATE_ADDRESS_CONFIRMATION, + MPLControlMessage = MPLCONTROL_MESSAGE, + ExtendedEchoRequest = EXTENDED_ECHO_REQUEST, + ExtendedEchoReply = EXTENDED_ECHO_REPLY, + Other(u8), +} + +impl AsRef for Type { + fn as_ref(&self) -> &u8 { + match self { + Type::DestinationUnreachable => &DESTINATION_UNREACHABLE, + Type::PacketTooBig => &PACKET_TOO_BIG, + Type::TimeExceeded => &TIME_EXCEEDED, + Type::ParameterProblem => &PARAMETER_PROBLEM, + Type::EchoRequest => &ECHO_REQUEST, + Type::EchoReply => &ECHO_REPLY, + Type::MulticastListenerQuery => &MULTICAST_LISTENER_QUERY, + Type::MulticastListenerReport => &MULTICAST_LISTENER_REPORT, + Type::MulticastListenerDone => &MULTICAST_LISTENER_DONE, + Type::RouterSolicitation => &ROUTER_SOLICITATION, + Type::RouterAdvertisement => &ROUTER_ADVERTISEMENT, + Type::NeighborSolicitation => &NEIGHBOR_SOLICITATION, + Type::NeighborAdvertisement => &NEIGHBOR_ADVERTISEMENT, + Type::RedirectMessage => &REDIRECT_MESSAGE, + Type::RouterRenumbering => &ROUTER_RENUMBERING, + Type::IcmpNodeInformationQuery => &ICMP_NODE_INFORMATION_QUERY, + Type::IcmpNodeInformationResponse => { + &ICMP_NODE_INFORMATION_RESPONSE + } + Type::InverseNeighborDiscoverySolicitationMessage => { + &INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE + } + Type::InverseNeighborDiscoveryAdvertisementMessage => { + &INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE + } + Type::Version2MulticastListenerReport => { + &VERSION2_MULTICAST_LISTENER_REPORT + } + Type::HomeAgentAddressDiscoveryRequestMessage => { + &HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE + } + Type::HomeAgentAddressDiscoveryReplyMessage => { + &HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE + } + Type::MobilePrefixSolicitation => &MOBILE_PREFIX_SOLICITATION, + Type::MobilePrefixAdvertisement => &MOBILE_PREFIX_ADVERTISEMENT, + Type::CertificationPathSolicitationMessage => { + &CERTIFICATION_PATH_SOLICITATION_MESSAGE + } + Type::CertificationPathAdvertisementMessage => { + &CERTIFICATION_PATH_ADVERTISEMENT_MESSAGE + } + Type::ExperimentalMobilityProtocols => { + &EXPERIMENTAL_MOBILITY_PROTOCOLS + } + Type::MulticastRouterAdvertisement => { + &MULTICAST_ROUTER_ADVERTISEMENT + } + Type::MulticastRouterSolicitation => &MULTICAST_ROUTER_SOLICITATION, + Type::MulticastRouterTermination => &MULTICAST_ROUTER_TERMINATION, + Type::FMIPv6Messages => &FMIPV6_MESSAGES, + Type::RPLControlMessage => &RPLCONTROL_MESSAGE, + Type::ILNPv6LocatorUpdateMessage => &ILNPV6_LOCATOR_UPDATE_MESSAGE, + Type::DuplicateAddressRequest => &DUPLICATE_ADDRESS_REQUEST, + Type::DuplicateAddressConfirmation => { + &DUPLICATE_ADDRESS_CONFIRMATION + } + Type::MPLControlMessage => &MPLCONTROL_MESSAGE, + Type::ExtendedEchoRequest => &EXTENDED_ECHO_REQUEST, + Type::ExtendedEchoReply => &EXTENDED_ECHO_REPLY, + Type::Other(x) => x, + } + } +} + +impl From for Type { + fn from(value: u8) -> Self { + match value { + DESTINATION_UNREACHABLE => Self::DestinationUnreachable, + PACKET_TOO_BIG => Self::PacketTooBig, + TIME_EXCEEDED => Self::TimeExceeded, + PARAMETER_PROBLEM => Self::ParameterProblem, + ECHO_REQUEST => Self::EchoRequest, + ECHO_REPLY => Self::EchoReply, + MULTICAST_LISTENER_QUERY => Self::MulticastListenerQuery, + MULTICAST_LISTENER_REPORT => Self::MulticastListenerReport, + MULTICAST_LISTENER_DONE => Self::MulticastListenerDone, + ROUTER_SOLICITATION => Self::RouterSolicitation, + ROUTER_ADVERTISEMENT => Self::RouterAdvertisement, + NEIGHBOR_SOLICITATION => Self::NeighborSolicitation, + NEIGHBOR_ADVERTISEMENT => Self::NeighborAdvertisement, + REDIRECT_MESSAGE => Self::RedirectMessage, + ROUTER_RENUMBERING => Self::RouterRenumbering, + ICMP_NODE_INFORMATION_QUERY => Self::IcmpNodeInformationQuery, + ICMP_NODE_INFORMATION_RESPONSE => Self::IcmpNodeInformationResponse, + INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE => { + Self::InverseNeighborDiscoverySolicitationMessage + } + INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE => { + Self::InverseNeighborDiscoveryAdvertisementMessage + } + VERSION2_MULTICAST_LISTENER_REPORT => { + Self::Version2MulticastListenerReport + } + HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE => { + Self::HomeAgentAddressDiscoveryRequestMessage + } + HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE => { + Self::HomeAgentAddressDiscoveryReplyMessage + } + MOBILE_PREFIX_SOLICITATION => Self::MobilePrefixSolicitation, + MOBILE_PREFIX_ADVERTISEMENT => Self::MobilePrefixAdvertisement, + CERTIFICATION_PATH_SOLICITATION_MESSAGE => { + Self::CertificationPathSolicitationMessage + } + CERTIFICATION_PATH_ADVERTISEMENT_MESSAGE => { + Self::CertificationPathAdvertisementMessage + } + EXPERIMENTAL_MOBILITY_PROTOCOLS => { + Self::ExperimentalMobilityProtocols + } + MULTICAST_ROUTER_ADVERTISEMENT => { + Self::MulticastRouterAdvertisement + } + MULTICAST_ROUTER_SOLICITATION => Self::MulticastRouterSolicitation, + MULTICAST_ROUTER_TERMINATION => Self::MulticastRouterTermination, + FMIPV6_MESSAGES => Self::FMIPv6Messages, + RPLCONTROL_MESSAGE => Self::RPLControlMessage, + ILNPV6_LOCATOR_UPDATE_MESSAGE => Self::ILNPv6LocatorUpdateMessage, + DUPLICATE_ADDRESS_REQUEST => Self::DuplicateAddressRequest, + DUPLICATE_ADDRESS_CONFIRMATION => { + Self::DuplicateAddressConfirmation + } + MPLCONTROL_MESSAGE => Self::MPLControlMessage, + EXTENDED_ECHO_REQUEST => Self::ExtendedEchoRequest, + EXTENDED_ECHO_REPLY => Self::ExtendedEchoReply, + x => Self::Other(x), + } + } +} + +impl From for u8 { + fn from(value: Type) -> Self { + *value.as_ref() + } +} + +pub type Code = u8; diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 00000000..c1fc0a54 --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,8 @@ +//! General purpose networking abstractions. + +pub mod arp; +pub mod ethernet; +pub mod icmpv4; +pub mod icmpv6; +pub mod mpls; +pub mod vxlan; diff --git a/src/net/mpls.rs b/src/net/mpls.rs new file mode 100644 index 00000000..f77ca907 --- /dev/null +++ b/src/net/mpls.rs @@ -0,0 +1,208 @@ +use anyhow::Error; +use std::convert::TryFrom; +use std::fmt; +use std::fmt::{Display, Formatter}; + +/// An MPLS label. +/// +/// The MPLS label is a 20-bit value used to identify a particular forwarding +/// equivalence class (FEC) or to apply a particular operation to a packet. +/// +/// See [RFC 3032][1] for more information. +/// +/// # Outstanding questions: +/// +/// Should we add handling for reserved labels as per the [IANA specs][2]? +/// +/// [1]: https://datatracker.ietf.org/doc/html/rfc3032 +/// [2]: https://www.iana.org/assignments/mpls-label-values/mpls-label-values.xhtml +#[derive(Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Hash)] +#[repr(transparent)] +pub struct Label(u32); + +impl Label { + /// Creates a new `Label` value. + /// Note: + /// this function will allow you to create a label with a value greater than + /// 0xFFFFF. + /// Use `new` if you want to ensure that the label + /// value is less than 0xFFFFF. + #[must_use] + pub fn new_unchecked(label: u32) -> Self { + Self(label) + } + + /// Creates a new `Label` value, ensuring that the label value is a legal + /// MPLS label. + /// + /// # Safety + /// You must ensure that the label value is less than 0xFFFFF if you want + /// the resulting `Label` value to be semantically valid. + /// + /// # Errors + /// Returns an error if the label value is greater than 20 bits (2^20 - 1 or + /// 0xFFFFF). + pub fn new(label: u32) -> Result { + if label > 0xFFFFF { + Err(Error::msg("MPLS label must be less than 0xFFFFF"))?; + } + Ok(Self(label)) + } +} + +impl TryFrom for Label { + type Error = Error; + + fn try_from(label: u32) -> Result { + Self::new(label) + } +} + +impl From