Skip to content

Commit

Permalink
icmp redux (capsule-rs#89)
Browse files Browse the repository at this point in the history
rework ICMP v4 and v6
  • Loading branch information
drunkirishcoder authored Apr 27, 2020
1 parent ab7682d commit 3d2830b
Show file tree
Hide file tree
Showing 18 changed files with 1,833 additions and 1,072 deletions.
5 changes: 2 additions & 3 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@
//! [`skeleton`]: https://github.com/capsule-rs/capsule/tree/master/examples/skeleton
//! [`syn-flood`]: https://github.com/capsule-rs/capsule/tree/master/examples/syn-flood
// alias for the test macro
#[cfg(test)]
// alias for the macros
extern crate self as capsule;

pub mod batch;
Expand All @@ -132,7 +131,7 @@ pub mod testils;

pub use self::dpdk::{KniRx, KniTxQueue, Mbuf, PortQueue, SizeOf};
pub use self::runtime::{Runtime, UnixSignal};
pub use capsule_macros::SizeOf;
#[cfg(any(test, feature = "testils"))]
#[cfg_attr(docsrs, doc(cfg(feature = "testils")))]
pub use capsule_macros::{bench, test};
pub use capsule_macros::{Icmpv4Packet, Icmpv6Packet, SizeOf};
149 changes: 118 additions & 31 deletions core/src/packets/icmp/v4/echo_reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
* SPDX-License-Identifier: Apache-2.0
*/

use crate::packets::icmp::v4::{Icmpv4, Icmpv4Packet, Icmpv4Payload, Icmpv4Type, Icmpv4Types};
use crate::packets::Packet;
use crate::{Icmpv4Packet, SizeOf};
use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types};
use crate::packets::{Internal, Packet};
use crate::SizeOf;
use failure::Fallible;
use std::fmt;
use std::ptr::NonNull;

/// Echo Reply Message defined in [`IETF RFC 792`].
///
Expand All @@ -43,64 +44,65 @@ use std::fmt;
///
/// *Data*: The data from the invoking Echo Request message.
///
/// The fields are accessible through [`Icmpv4<EchoReply>`].
///
/// [`IETF RFC 792`]: https://tools.ietf.org/html/rfc792
/// [`Icmpv4<EchoReply>`]: Icmpv4
#[derive(Clone, Copy, Debug, Default, Icmpv4Packet, SizeOf)]
#[repr(C, packed)]
#[derive(Icmpv4Packet)]
pub struct EchoReply {
identifier: u16,
seq_no: u16,
icmp: Icmpv4,
body: NonNull<EchoReplyBody>,
}

impl Icmpv4Payload for EchoReply {
fn msg_type() -> Icmpv4Type {
Icmpv4Types::EchoReply
impl EchoReply {
#[inline]
fn body(&self) -> &EchoReplyBody {
unsafe { self.body.as_ref() }
}

#[inline]
fn body_mut(&mut self) -> &mut EchoReplyBody {
unsafe { self.body.as_mut() }
}
}

impl Icmpv4<EchoReply> {
/// Returns the identifier.
#[inline]
pub fn identifier(&self) -> u16 {
u16::from_be(self.payload().identifier)
u16::from_be(self.body().identifier)
}

/// Sets the identifier.
#[inline]
pub fn set_identifier(&mut self, identifier: u16) {
self.payload_mut().identifier = u16::to_be(identifier);
self.body_mut().identifier = u16::to_be(identifier);
}

/// Returns the sequence number.
#[inline]
pub fn seq_no(&self) -> u16 {
u16::from_be(self.payload().seq_no)
u16::from_be(self.body().seq_no)
}

/// Sets the sequence number.
#[inline]
pub fn set_seq_no(&mut self, seq_no: u16) {
self.payload_mut().seq_no = u16::to_be(seq_no);
self.body_mut().seq_no = u16::to_be(seq_no);
}

/// Returns the offset where the data field in the message body starts.
#[inline]
fn data_offset(&self) -> usize {
self.payload_offset() + EchoReply::size_of()
self.payload_offset() + EchoReplyBody::size_of()
}

/// Returns the length of the data field in the message body.
#[inline]
fn data_len(&self) -> usize {
self.payload_len() - EchoReply::size_of()
self.payload_len() - EchoReplyBody::size_of()
}

/// Returns the data as a `u8` slice.
#[inline]
pub fn data(&self) -> &[u8] {
if let Ok(data) = self
.icmp()
.mbuf()
.read_data_slice(self.data_offset(), self.data_len())
{
Expand All @@ -115,18 +117,13 @@ impl Icmpv4<EchoReply> {
pub fn set_data(&mut self, data: &[u8]) -> Fallible<()> {
let offset = self.data_offset();
let len = data.len() as isize - self.data_len() as isize;
self.mbuf_mut().resize(offset, len)?;
self.mbuf_mut().write_data_slice(offset, data)?;
self.icmp_mut().mbuf_mut().resize(offset, len)?;
self.icmp_mut().mbuf_mut().write_data_slice(offset, data)?;
Ok(())
}

#[inline]
fn reconcile(&mut self) {
self.compute_checksum();
}
}

impl fmt::Debug for Icmpv4<EchoReply> {
impl fmt::Debug for EchoReply {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("icmpv4")
.field("type", &format!("{}", self.msg_type()))
Expand All @@ -141,12 +138,102 @@ impl fmt::Debug for Icmpv4<EchoReply> {
}
}

impl Icmpv4Message for EchoReply {
#[inline]
fn msg_type() -> Icmpv4Type {
Icmpv4Types::EchoReply
}

#[inline]
fn icmp(&self) -> &Icmpv4 {
&self.icmp
}

#[inline]
fn icmp_mut(&mut self) -> &mut Icmpv4 {
&mut self.icmp
}

#[inline]
fn into_icmp(self) -> Icmpv4 {
self.icmp
}

#[inline]
unsafe fn clone(&self, internal: Internal) -> Self {
EchoReply {
icmp: self.icmp.clone(internal),
body: self.body,
}
}

#[inline]
fn try_parse(icmp: Icmpv4, _internal: Internal) -> Fallible<Self> {
let mbuf = icmp.mbuf();
let offset = icmp.payload_offset();
let body = mbuf.read_data(offset)?;

Ok(EchoReply { icmp, body })
}

#[inline]
fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Fallible<Self> {
let offset = icmp.payload_offset();
let mbuf = icmp.mbuf_mut();

mbuf.extend(offset, EchoReplyBody::size_of())?;
let body = mbuf.write_data(offset, &EchoReplyBody::default())?;

Ok(EchoReply { icmp, body })
}
}

/// The ICMPv4 Echo Reply message body.
///
/// This contains only the fixed portion of the message body. data is parsed
/// separately.
#[derive(Clone, Copy, Debug, Default, SizeOf)]
#[repr(C, packed)]
struct EchoReplyBody {
identifier: u16,
seq_no: u16,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::packets::ip::v4::Ipv4;
use crate::packets::Ethernet;
use crate::Mbuf;

#[test]
fn size_of_echo_reply() {
assert_eq!(4, EchoReply::size_of());
fn size_of_echo_reply_body() {
assert_eq!(4, EchoReplyBody::size_of());
}

#[capsule::test]
fn push_and_set_echo_reply() {
let packet = Mbuf::new().unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let ipv4 = ethernet.push::<Ipv4>().unwrap();
let mut echo = ipv4.push::<EchoReply>().unwrap();

assert_eq!(4, echo.header_len());
assert_eq!(EchoReplyBody::size_of(), echo.payload_len());
assert_eq!(Icmpv4Types::EchoReply, echo.msg_type());
assert_eq!(0, echo.code());

echo.set_identifier(42);
assert_eq!(42, echo.identifier());
echo.set_seq_no(7);
assert_eq!(7, echo.seq_no());

let data = [0; 10];
assert!(echo.set_data(&data).is_ok());
assert_eq!(&data, echo.data());
assert_eq!(EchoReplyBody::size_of() + 10, echo.payload_len());

echo.reconcile_all();
assert!(echo.checksum() != 0);
}
}
Loading

0 comments on commit 3d2830b

Please sign in to comment.