diff options
| author | luwenpeng <[email protected]> | 2023-07-28 11:13:11 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-08-01 11:44:23 +0800 |
| commit | 53c5af3ec08f55933abc8983827a40171374aaf1 (patch) | |
| tree | bd2d729569547ba7e98775e53503e1fcd4cef406 | |
| parent | 5d64a760623e0e1cdc402c3167015fed57ec3c39 (diff) | |
[feature] Support EthernetFrame Decode
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/protocol/ethernet.rs | 206 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 1 |
4 files changed, 209 insertions, 0 deletions
@@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +nom = "7" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c8b4a9f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod protocol;
\ No newline at end of file diff --git a/src/protocol/ethernet.rs b/src/protocol/ethernet.rs new file mode 100644 index 0000000..6b77a7d --- /dev/null +++ b/src/protocol/ethernet.rs @@ -0,0 +1,206 @@ +use nom::number; +use nom::IResult; + +/****************************************************************************** + * Struct + ******************************************************************************/ + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MacAddress(pub [u8; 6]); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EtherType { + LANMIN, // 802.3 Min data length + LANMAX, // 802.3 Max data length + IPv4, // Internet Protocol version 4 (IPv4) [RFC7042] + ARP, // Address Resolution Protocol (ARP) [RFC7042] + WOL, // Wake on LAN + TRILL, // IETF TRILL Protocol [IEEE] + DECnet, // DECnet Phase IV + RARP, // Reverse Address Resolution Protocol (RARP) [RFC903] + AppleTalk, // AppleTalk - EtherTalk [Apple] + AARP, // AppleTalk Address Resolution Protocol (AARP) [Apple] + VLAN, // VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq[5] + IPX, // IPX [Xerox] + Qnet, // QNX Qnet [QNX Software Systems] + IPv6, // Internet Protocol Version 6 (IPv6) [RFC7042] + FlowControl, // Ethernet Flow Control [IEEE 802.3x] + CobraNet, // CobraNet [CobraNet] + MPLSuni, // MPLS Unicast [RFC 3032] + MPLSmulti, // MPLS Multicast [RFC 5332] + PPPoEdiscovery, // PPPOE Discovery Stage [RFC 2516] + PPPoEsession, // PPPoE Session Stage [RFC 2516] + HomePlug, // HomePlug 1.0 MME + EAPOL, // EAP over LAN (IEEE 802.1X) + PROFINET, // PROFINET Protocol + HyperSCSI, // HyperSCSI (SCSI over Ethernet) + ATAOE, // ATA over Ethernet + EtherCAT, // EtherCAT Protocol + QinQ, // Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq[5] + Powerlink, // Ethernet Powerlink[citation needed] + GOOSE, // GOOSE (Generic Object Oriented Substation event) + GSE, // GSE (Generic Substation Events) Management Services + LLDP, // Link Layer Discovery Protocol (LLDP) [IEEE 802.1AB] + SERCOS, // SERCOS III + HomePlugAV, // HomePlug AV MME[citation needed] + MRP, // Media Redundancy Protocol (IEC62439-2) + MACsec, // MAC security (IEEE 802.1AE) + PBB, // Provider Backbone Bridges (PBB) (IEEE 802.1ah) + PTP, // Precision Time Protocol (PTP) over Ethernet [IEEE 1588] + PRP, // Parallel Redundancy Protocol (PRP) + CFM, // IEEE 802.1ag Connectivity Fault Management (CFM) Protocol / ITU-T Recommendation Y.1731 (OAM) + FCoE, // Fibre Channel over Ethernet (FCoE) + FCoEi, // FCoE Initialization Protocol + RoCE, // RDMA over Converged Ethernet (RoCE) + TTE, // TTEthernet Protocol Control Frame (TTE) + HSR, // High-availability Seamless Redundancy (HSR) + CTP, // Ethernet Configuration Testing Protocol[6] + VLANdouble, // VLAN-tagged (IEEE 802.1Q) frame with double tagging + Other(u16), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EthernetFrame { + pub source_mac: MacAddress, + pub dest_mac: MacAddress, + pub ether_type: EtherType, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +impl From<u16> for EtherType { + fn from(raw: u16) -> Self { + match raw { + 0x002E => Self::LANMIN, + 0x05DC => Self::LANMAX, + 0x0800 => Self::IPv4, + 0x0806 => Self::ARP, + 0x0842 => Self::WOL, + 0x22F3 => Self::TRILL, + 0x6003 => Self::DECnet, + 0x8035 => Self::RARP, + 0x809B => Self::AppleTalk, + 0x80F3 => Self::AARP, + 0x8100 => Self::VLAN, + 0x8137 => Self::IPX, + 0x8204 => Self::Qnet, + 0x86DD => Self::IPv6, + 0x8808 => Self::FlowControl, + 0x8819 => Self::CobraNet, + 0x8847 => Self::MPLSuni, + 0x8848 => Self::MPLSmulti, + 0x8863 => Self::PPPoEdiscovery, + 0x8864 => Self::PPPoEsession, + 0x887B => Self::HomePlug, + 0x888E => Self::EAPOL, + 0x8892 => Self::PROFINET, + 0x889A => Self::HyperSCSI, + 0x88A2 => Self::ATAOE, + 0x88A4 => Self::EtherCAT, + 0x88A8 => Self::QinQ, + 0x88AB => Self::Powerlink, + 0x88B8 => Self::GOOSE, + 0x88B9 => Self::GSE, + 0x88CC => Self::LLDP, + 0x88CD => Self::SERCOS, + 0x88E1 => Self::HomePlugAV, + 0x88E3 => Self::MRP, + 0x88E5 => Self::MACsec, + 0x88E7 => Self::PBB, + 0x88F7 => Self::PTP, + 0x88FB => Self::PRP, + 0x8902 => Self::CFM, + 0x8906 => Self::FCoE, + 0x8914 => Self::FCoEi, + 0x8915 => Self::RoCE, + 0x891D => Self::TTE, + 0x892F => Self::HSR, + 0x9000 => Self::CTP, + 0x9100 => Self::VLANdouble, + other => Self::Other(other), + } + } +} + +impl EtherType { + fn decode(input: &[u8]) -> IResult<&[u8], EtherType> { + let (input, ether_type) = number::streaming::be_u16(input)?; + + Ok((input, ether_type.into())) + } +} + +impl MacAddress { + fn decode(input: &[u8]) -> IResult<&[u8], MacAddress> { + let (input, mac_address) = nom::bytes::streaming::take(6u8)(input)?; + + Ok((input, MacAddress(<[u8; 6]>::try_from(mac_address).unwrap()))) + } +} + +impl EthernetFrame { + pub fn decode(input: &[u8]) -> IResult<&[u8], EthernetFrame> { + let (input, dest_mac) = MacAddress::decode(input)?; + let (input, source_mac) = MacAddress::decode(input)?; + let (input, ether_type) = EtherType::decode(input)?; + + Ok(( + input, + EthernetFrame { + source_mac, + dest_mac, + ether_type, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::{EtherType, EthernetFrame, MacAddress}; + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn ethernet_frame_decode() { + /* + * Ethernet II, Src: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea), Dst: Charge-A_08:02:be (4c:bc:98:08:02:be) + * Destination: Charge-A_08:02:be (4c:bc:98:08:02:be) + * Address: Charge-A_08:02:be (4c:bc:98:08:02:be) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * Address: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Type: IPv4 (0x0800) + */ + let bytes = [ + 0x4c, 0xbc, 0x98, 0x08, 0x02, 0xbe, /* Destination Address */ + 0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea, /* Source Address */ + 0x08, 0x00, /* Type */ + 0xff, /* Payload */ + ]; + + let expectation = EthernetFrame { + source_mac: MacAddress([0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea]), + dest_mac: MacAddress([0x4c, 0xbc, 0x98, 0x08, 0x02, 0xbe]), + ether_type: EtherType::IPv4, + }; + + assert_eq!(EthernetFrame::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let ethernet = EthernetFrame::decode(&bytes); + if let Ok((payload, header)) = ethernet { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000..6d1ec83 --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1 @@ +pub mod ethernet;
\ No newline at end of file |
