-
Notifications
You must be signed in to change notification settings - Fork 7
Implement IPv4 checksum and use inverse maps for ethernet sculpting #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,34 +14,55 @@ use crate::{Layer, Packet, ENCAP_TYPE_ETH}; | |
|
|
||
| pub const ETH_HEADER_LENGTH: usize = 14_usize; | ||
|
|
||
| /// A Map maintaining EtherType -> Creator fns for Layer Creators of L3 Layers. | ||
| /// | ||
| /// The creator function simply creates a `default` L3 struct that implements the dissector | ||
| /// for the Layer. | ||
| pub fn get_ethertypes_map() -> &'static RwLock<HashMap<EtherType, LayerCreatorFn>> { | ||
| /// A Map maintaining EtherType -> Creator fns for Layer Creators of L3 Layers. | ||
| /// | ||
| /// The creator function simply creates a `default` L3 struct that implements the dissector | ||
| /// for the Layer. | ||
| pub(crate) static ETHERTYPES_MAP: OnceLock<RwLock<HashMap<EtherType, LayerCreatorFn>>> = | ||
| OnceLock::new(); | ||
| static ETHERTYPES_MAP: OnceLock<RwLock<HashMap<EtherType, LayerCreatorFn>>> = OnceLock::new(); | ||
| ETHERTYPES_MAP.get_or_init(|| RwLock::new(HashMap::new())) | ||
| } | ||
|
|
||
| /// A Map maintaining String -> EtherType of L3 Layers. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add more description for the map as a documentation. If we simply read this documentation it is not clear why this map exists and how it is to be used. For example something like this. |
||
| pub fn get_inv_ethertypes_map() -> &'static RwLock<HashMap<String, EtherType>> { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be this function is gated by |
||
| static INV_ETHERTYPES_MAP: OnceLock<RwLock<HashMap<String, EtherType>>> = OnceLock::new(); | ||
| INV_ETHERTYPES_MAP.get_or_init(|| RwLock::new(HashMap::new())) | ||
| } | ||
|
|
||
| // Register our Encap Types with the Packet. | ||
| pub(crate) fn register_defaults() -> Result<(), Error> { | ||
| get_ethertypes_map(); | ||
|
|
||
| Packet::register_encap_type(ENCAP_TYPE_ETH, Ethernet::creator) | ||
| } | ||
|
|
||
| /// Register for a given EtherType | ||
| /// | ||
| /// A Layer that would handle subsequent decoding for a given Ethertype, should register itself | ||
| /// by calling this function. For example [`crate::layers::ipv4`] would call `register_ethertype` | ||
| /// with [`EtherType`] value of 0x0800, passing the creator function for that layer. | ||
| pub fn register_ethertype(eth_type: EtherType, layer: LayerCreatorFn) -> Result<(), Error> { | ||
| let mut map = get_ethertypes_map().write().unwrap(); | ||
| if map.contains_key(ð_type) { | ||
| return Err(Error::RegisterError(format!("ether_type: {}", eth_type))); | ||
| /// by calling this function. An optional name value can be provided to be used during packet creation. | ||
| /// For example [`crate::layers::ipv4`] would call `register_ethertype` with [`EtherType`] | ||
| /// value of 0x0800, and a name value of "IPv4" passing the creator function for that layer. | ||
| pub fn register_ethertype( | ||
aadilshabier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| eth_type: EtherType, | ||
| name: Option<&str>, | ||
| layer: LayerCreatorFn, | ||
| ) -> Result<(), Error> { | ||
| { | ||
| let mut map = get_ethertypes_map().write().unwrap(); | ||
| if map.contains_key(ð_type) { | ||
| return Err(Error::RegisterError(format!("ether_type: {}", eth_type))); | ||
| } | ||
| map.insert(eth_type, layer); | ||
| } | ||
|
|
||
| if let Some(name) = name { | ||
| let mut inv_map = get_inv_ethertypes_map().write().unwrap(); | ||
aadilshabier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if inv_map.contains_key(name) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return Err(Error::RegisterError(format!( | ||
| "Cannot find EtherType for : {}", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error should be |
||
| name | ||
| ))); | ||
| } | ||
| inv_map.insert(name.to_string(), eth_type); | ||
| } | ||
| map.insert(eth_type, layer); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
@@ -103,16 +124,15 @@ impl Layer for Ethernet { | |
| result.extend(self.dst_mac.as_slice()); | ||
| result.extend(self.src_mac.as_slice()); | ||
|
|
||
| let ethertype: u16 = match info { | ||
| "ARP" => crate::types::ETHERTYPE_ARP, | ||
| "IPv4" => crate::types::ETHERTYPE_IP, | ||
| "IPv6" => crate::types::ETHERTYPE_IP6, | ||
| // FIXME: can also be `ETHERTYPE_MPLS_MULTICAST` | ||
| "MPLS" => crate::types::ETHERTYPE_MPLS_UNICAST, | ||
| "raw" => 0xffff, | ||
| // NOTE: should return Err instead | ||
| _ => unimplemented!(), | ||
| }; | ||
| let ethertype = get_inv_ethertypes_map() | ||
| .read() | ||
| .unwrap() | ||
| .get(info) | ||
| .copied() | ||
| .unwrap_or_else(|| match info { | ||
| "raw" => crate::types::ETHERTYPE_RAW, | ||
| _ => todo!("Return Err here instead"), | ||
| }); | ||
|
|
||
| result.extend(ethertype.to_be_bytes()); | ||
| result.extend(next_layer.unwrap_or_default()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,11 +32,8 @@ fn get_protocol_map() -> &'static RwLock<HashMap<u8, LayerCreatorFn>> { | |
| // Right now only Ethernet is Supported | ||
| pub(crate) fn register_defaults() -> Result<(), Error> { | ||
| use crate::layers::ethernet::register_ethertype; | ||
|
|
||
| get_protocol_map(); | ||
|
|
||
| register_ethertype(crate::types::ETHERTYPE_IP, IPv4::creator)?; | ||
|
|
||
| let name = Some(IPv4::default().name()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| register_ethertype(crate::types::ETHERTYPE_IP, name, IPv4::creator)?; | ||
| Ok(()) | ||
| } | ||
|
|
||
|
|
@@ -85,7 +82,10 @@ pub enum IPOption { | |
| pub struct IPv4 { | ||
| version: u8, | ||
| hdr_len: u8, | ||
| tos: u8, | ||
| #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u8")] | ||
| dscp: u8, | ||
| #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u8")] | ||
| ecn: u8, | ||
| len: u16, | ||
| #[serde(serialize_with = "crate::types::hex::serialize_lower_hex_u16")] | ||
| id: u16, | ||
|
|
@@ -256,8 +256,16 @@ impl IPv4 { | |
| } | ||
|
|
||
| #[cfg(feature = "sculpting")] | ||
| fn calculate_checksum(_bytes: &[u8]) -> u16 { | ||
| 0 | ||
| fn calculate_checksum(bytes: &[u8]) -> u16 { | ||
| // 16 bit one's complement of one's complement sum of all 16 bit words | ||
| let len = bytes.len(); | ||
| let mut csum = 0_u32; | ||
| for i in 0..len / 2 { | ||
| let word = u16::from_be_bytes(bytes[2 * i..2 * (i + 1)].try_into().unwrap()); | ||
| csum += word as u32; | ||
| } | ||
| csum = ((csum >> 16) + (csum & 0xffff)) as u32; | ||
| (!csum & 0xffff) as u16 | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -279,7 +287,8 @@ impl Layer for IPv4 { | |
| data: hex::encode(bytes), | ||
| }); | ||
| } | ||
| self.tos = bytes[1]; | ||
| self.dscp = bytes[1] >> 2; | ||
| self.ecn = bytes[1] & 0b11; | ||
| self.len = u16::from_be_bytes(bytes[2..4].try_into().unwrap()); | ||
| self.id = u16::from_be_bytes(bytes[4..6].try_into().unwrap()); | ||
| let flags_offset = u16::from_be_bytes(bytes[6..8].try_into().unwrap()); | ||
|
|
@@ -337,9 +346,11 @@ impl Layer for IPv4 { | |
|
|
||
| let mut result = Vec::with_capacity(self.len as usize); | ||
|
|
||
| let byte = (self.version << 4) | self.hdr_len; | ||
| let mut byte = (self.version << 4) | self.hdr_len; | ||
| result.push(byte); | ||
|
|
||
| byte = (self.dscp << 2) | self.ecn; | ||
| result.push(byte); | ||
| result.push(self.tos); | ||
| result.extend(self.len.to_be_bytes()); | ||
| result.extend(self.id.to_be_bytes()); | ||
|
|
||
|
|
@@ -357,10 +368,11 @@ impl Layer for IPv4 { | |
| todo!(); | ||
| } | ||
|
|
||
| result.extend(next_layer.unwrap_or_default()); | ||
|
|
||
| let checksum = IPv4::calculate_checksum(&result); | ||
| result[checksum_start..checksum_start + 2].copy_from_slice(&checksum.to_be_bytes()); | ||
|
|
||
| result.extend(next_layer.unwrap_or_default()); | ||
|
|
||
| Ok(result) | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ | |
| use core::convert::TryInto; | ||
|
|
||
| use std::collections::HashMap; | ||
| use std::sync::{RwLock,OnceLock}; | ||
| use std::sync::{OnceLock, RwLock}; | ||
|
|
||
| use serde::Serialize; | ||
|
|
||
|
|
@@ -14,7 +14,6 @@ use crate::Layer; | |
| /// Basic Length of the IPv6 Header | ||
| pub const IPV6_BASE_HEADER_LENGTH: usize = 40_usize; | ||
|
|
||
|
|
||
| fn get_next_headers_map() -> &'static RwLock<HashMap<u8, LayerCreatorFn>> { | ||
| static NEXT_HEADERS_MAP: OnceLock<RwLock<HashMap<u8, LayerCreatorFn>>> = OnceLock::new(); | ||
| NEXT_HEADERS_MAP.get_or_init(|| RwLock::new(HashMap::new())) | ||
|
|
@@ -26,8 +25,8 @@ fn get_next_headers_map() -> &'static RwLock<HashMap<u8, LayerCreatorFn>> { | |
| pub(crate) fn register_defaults() -> Result<(), Error> { | ||
| use crate::layers::ethernet::register_ethertype; | ||
|
|
||
| get_next_headers_map(); | ||
| register_ethertype(crate::types::ETHERTYPE_IP6, IPv6::creator)?; | ||
| let name = Some(IPv6::default().name()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| register_ethertype(crate::types::ETHERTYPE_IP6, name, IPv6::creator)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,8 +16,10 @@ pub const MPLS_HEADER_LENGTH: usize = 4_usize; | |
|
|
||
| // Register Ourselves to the Ethernet layer, as this is a 2.5 layer protocol | ||
| pub(crate) fn register_defaults() -> Result<(), Error> { | ||
| ethernet::register_ethertype(ETHERTYPE_MPLS_UNICAST, MPLS::creator)?; | ||
| ethernet::register_ethertype(ETHERTYPE_MPLS_MULTICAST, MPLS::creator) | ||
| let name = Some(MPLS::default().name()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ethernet::register_ethertype(ETHERTYPE_MPLS_UNICAST, name, MPLS::creator)?; | ||
| ethernet::register_ethertype(ETHERTYPE_MPLS_MULTICAST, None, MPLS::creator)?; | ||
| Ok(()) | ||
| } | ||
|
|
||
| #[derive(Debug, Default, Serialize, Copy, Clone)] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
short_name(short_nameis convenient and usually alllowercase)