summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-07-28 11:13:11 +0800
committerluwenpeng <[email protected]>2023-08-01 11:44:23 +0800
commit53c5af3ec08f55933abc8983827a40171374aaf1 (patch)
treebd2d729569547ba7e98775e53503e1fcd4cef406
parent5d64a760623e0e1cdc402c3167015fed57ec3c39 (diff)
[feature] Support EthernetFrame Decode
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs1
-rw-r--r--src/protocol/ethernet.rs206
-rw-r--r--src/protocol/mod.rs1
4 files changed, 209 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a75baa2..bf33680 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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