Skip to content

Commit

Permalink
Make PacketBuilder to sculpt Packets and Layer::stack_and_encode
Browse files Browse the repository at this point in the history
- Implement `PacketBuilder` to sculpt packets.
- Implement `Layer::stack_and_encode` for Ethernet and IPv4.
- Implement `as_slice` and `as_mut_slice` for MACAddress, IPv4Address and
  IPv6Address.
- Create example `packet_sculpting.rs`.
- Add new `crate::Error` variant SculptingError.

Signed-off-by: Mohammad Aadil Shabier <[email protected]>
  • Loading branch information
aadilshabier authored and gabhijit committed May 4, 2024
1 parent df86e98 commit 593fb6f
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ path = "examples/packet_as_json.rs"
name = "pcap"
path = "examples/pcap_example.rs"

[[example]]
name = "packet_sculpting"
path = "examples/packet_sculpting.rs"

[build-dependencies]
syn = { version = "1.0", features = ["full"]}
walkdir = "2"
Expand Down
33 changes: 33 additions & 0 deletions examples/packet_sculpting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::error::Error;

use scalpel::{layers, ENCAP_TYPE_ETH};

fn main() -> Result<(), Box<dyn Error>> {
let _ = scalpel::register_defaults()?;

let eth_layer = Box::new(layers::ethernet::Ethernet::new());
let ipv4_layer = Box::new(layers::ipv4::IPv4::new());
let bytes = [0x12, 0x34, 0x56, 0x78, 0x90];

let builder = scalpel::builder::PacketBuilder::new()
.stack(eth_layer)?
.stack(ipv4_layer)?
.stack_bytes(&bytes);

let (_, result) = builder.build().unwrap();

let res_string = result[0]
.iter()
.fold(String::new(), |acc, num| {
acc + "0x" + &num.to_string() + " "
})
.trim_end()
.to_string();

println!("res: {:#?}", res_string);

let p = scalpel::Packet::from_bytes(&result[0], ENCAP_TYPE_ETH);
println!("{}", serde_json::to_string_pretty(&p.unwrap()).unwrap());

Ok(())
}
65 changes: 65 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{errors::Error, Layer, Packet};

#[derive(Debug, Default)]
pub struct PacketBuilder {
inner: Packet,
}

impl PacketBuilder {
pub fn new() -> Self {
Self::default()
}

/// Stacks a layer on top of the Packet. The return value on success is the
/// `PacketBuilder` on success. Failure happens when a layer is attempted to
/// be pushed after pushing bytes. In this case it returns a
/// [SculptingError][`crate::errors::Error::SculptingError`].
pub fn stack(mut self, layer: Box<dyn Layer + Send>) -> Result<Self, Error> {
if self.inner.unprocessed.is_empty() {
self.inner.layers.push(layer);
Ok(self)
} else {
Err(Error::SculptingError(
"Cannot push layer on top of raw byte layer".to_string(),
))
}
}

/// Stacks bytes on top of the packet. If the layer already contains some bytes
/// the new bytes are concatenated to the previous bytes in the packet.
pub fn stack_bytes(mut self, bytes: &[u8]) -> Self {
self.inner.unprocessed.extend(bytes);
self
}

pub fn build(mut self) -> Result<(Packet, Vec<Vec<u8>>), Error> {
let len = self.inner.layers.len();
if len < 1 {
return Err(Error::SculptingError(
"Packet to build does not contain any layers".to_string(),
));
}
let mut results = vec![];

// last layer
let next_layer = if self.inner.unprocessed.is_empty() {
None
} else {
Some(self.inner.unprocessed.as_slice())
};
let mut bytes = self.inner.layers[len - 1].stack_and_encode(next_layer, "raw")?;
results.push(bytes);

for i in (0..len - 1).rev() {
let next_layer = results.last().map(|layer| layer.as_slice());
let info = self.inner.layers[i + 1].name();
bytes = self.inner.layers[i].stack_and_encode(next_layer, info)?;
results.push(bytes);
}

results.reverse();
Ok((self.inner, results))
}

// TODO: Add metadata related functions
}
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub enum Error {

/// A layer registration error.
RegisterError(String),

/// Error in sculpting
SculptingError(String),
}

// FIXME: Should work with `no_std`
Expand Down
16 changes: 16 additions & 0 deletions src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ pub trait Layer: Send + Debug + erased_serde::Serialize {
bytes: &[u8],
) -> Result<(Option<Box<dyn Layer + Send>>, usize), Error>;

/// Main 'encoder' function.
///
/// The return value is a `Vec<u8>` on success. This indicates the encoded packet of the
/// layer. Typically return a [SculptingError][`crate::errors::Error::SculptingError`],
/// but may as well return other values. This updates the fields of the packet using the
/// encoded bytes of the next layer(`next_layer`), and `info`, to provide additional details
/// about the packet. This can be the result of [Layer::name()][`Self::name()`]
/// of the next packet.
fn stack_and_encode(
&mut self,
_next_layer: Option<&[u8]>,
_info: &str,
) -> Result<Vec<u8>, Error> {
unimplemented!()
}

/// Name for the given layer.
fn name(&self) -> &'static str;

Expand Down
36 changes: 36 additions & 0 deletions src/layers/ethernet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ pub struct Ethernet {
}

impl Ethernet {
pub fn new() -> Self {
Self {
ethertype: 0xFFFF,
..Default::default()
}
}

pub(crate) fn creator() -> Box<dyn Layer + Send> {
Box::<Ethernet>::default()
}
Expand Down Expand Up @@ -84,6 +91,35 @@ impl Layer for Ethernet {
}
}

fn stack_and_encode(
&mut self,
next_layer: Option<&[u8]>,
info: &str,
) -> Result<Vec<u8>, Error> {
let mut result = Vec::with_capacity(ETH_HEADER_LENGTH);

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!(),
};

result.extend(ethertype.to_be_bytes());
result.extend(next_layer.unwrap_or_default());

// TODO: pad to 64 bytes(minimum packet size)

Ok(result)
}

fn name(&self) -> &'static str {
"Ethernet"
}
Expand Down
58 changes: 57 additions & 1 deletion src/layers/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub const IPV4_OPTION_RR: u8 = 7;
pub const IPV4_OPTION_MTUP: u8 = 11;
pub const IPV4_OPTION_MTUR: u8 = 12;

pub const IPPROTO_RAW: u8 = 0xFF;

fn get_protocol_map() -> &'static RwLock<HashMap<u8, LayerCreatorFn>> {
static PROTOCOLS_MAP: OnceLock<RwLock<HashMap<u8, LayerCreatorFn>>> = OnceLock::new();
PROTOCOLS_MAP.get_or_init(|| RwLock::new(HashMap::new()))
Expand Down Expand Up @@ -101,7 +103,17 @@ pub struct IPv4 {
}

impl IPv4 {
pub fn creator() -> Box<dyn Layer + Send> {
pub fn new() -> Self {
Self {
version: 4,
hdr_len: (IPV4_BASE_HEADER_LENGTH / 4) as u8,
proto: IPPROTO_RAW,
len: IPV4_BASE_HEADER_LENGTH as u16,
..Default::default()
}
}

pub(crate) fn creator() -> Box<dyn Layer + Send> {
Box::<IPv4>::default()
}
}
Expand Down Expand Up @@ -242,6 +254,10 @@ impl IPv4 {

Ok(((len as u8, data), i))
}

fn calculate_checksum(_bytes: &[u8]) -> u16 {
0
}
}

impl Layer for IPv4 {
Expand Down Expand Up @@ -305,6 +321,46 @@ impl Layer for IPv4 {
fn short_name(&self) -> &'static str {
"ip"
}

fn stack_and_encode(
&mut self,
next_layer: Option<&[u8]>,
info: &str,
) -> Result<Vec<u8>, Error> {
self.len = self.hdr_len as u16 * 4 + next_layer.unwrap_or_default().len() as u16;
self.proto = match info {
"raw" => IPPROTO_RAW,
_ => self.proto,
};

let mut result = Vec::with_capacity(self.len as usize);

let byte = (self.version << 4) | self.hdr_len;
result.push(byte);
result.push(self.tos);
result.extend(self.len.to_be_bytes());
result.extend(self.id.to_be_bytes());

let word = (self.flags as u16) << 13 | self.frag_offset;
result.extend(word.to_be_bytes());
result.push(self.ttl);
result.push(self.proto);

let checksum_start = result.len();
result.extend(self.checksum.to_be_bytes());
result.extend(self.src_addr.as_slice());
result.extend(self.dst_addr.as_slice());

if !self.options.is_empty() {
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());
Ok(result)
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub mod layer;

pub mod types;

pub mod builder;

#[doc(inline)]
pub use layers::register_defaults;

Expand Down
24 changes: 24 additions & 0 deletions src/types/ipaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ use crate::errors::Error as CrateError;
#[derive(Default, Clone, PartialEq, Eq)]
pub struct IPv4Address([u8; 4]);

impl IPv4Address {
/// Returns a slice containing the entire inner array.
pub const fn as_slice(&self) -> &[u8] {
&self.0
}

/// Returns a mutable slice containing the entire inner array.
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.0
}
}

impl From<[u8; 4]> for IPv4Address {
fn from(value: [u8; 4]) -> Self {
Self(value)
Expand Down Expand Up @@ -71,6 +83,18 @@ impl Serialize for IPv4Address {
#[derive(Default, Clone, PartialEq, Eq)]
pub struct IPv6Address([u16; 8]);

impl IPv6Address {
/// Returns a slice containing the entire inner array.
pub const fn as_slice(&self) -> &[u16] {
&self.0
}

/// Returns a mutable slice containing the entire inner array.
pub fn as_mut_slice(&mut self) -> &mut [u16] {
&mut self.0
}
}

impl From<[u16; 8]> for IPv6Address {
fn from(value: [u16; 8]) -> Self {
Self(value)
Expand Down
18 changes: 18 additions & 0 deletions src/types/macaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ use crate::errors::Error as CrateError;
#[derive(Default, Clone)]
pub struct MACAddress([u8; 6]);

impl MACAddress {
/// Returns a slice containing the entire inner array.
pub const fn as_slice(&self) -> &[u8] {
&self.0
}

/// Returns a mutable slice containing the entire inner array.
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.0
}
}

impl Serialize for MACAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand All @@ -22,6 +34,12 @@ impl Serialize for MACAddress {
}
}

impl From<[u8; 6]> for MACAddress {
fn from(value: [u8; 6]) -> Self {
Self(value)
}
}

impl TryFrom<&'_ [u8]> for MACAddress {
type Error = CrateError;

Expand Down

0 comments on commit 593fb6f

Please sign in to comment.