From ad1067c8ca420444323569fc8f9c2642eadf7ef9 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Tue, 9 Mar 2021 20:47:50 -0500 Subject: [PATCH 01/18] new pattern for ffi --- core/src/ffi/dpdk.rs | 156 ++++++++++++++++++++++++++++++++ core/src/{ffi.rs => ffi/mod.rs} | 21 +++++ core/src/lib.rs | 3 +- core/src/rt2/mempool.rs | 132 +++++++++++++++++++++++++++ core/src/rt2/mod.rs | 19 ++++ 5 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 core/src/ffi/dpdk.rs rename core/src/{ffi.rs => ffi/mod.rs} (86%) create mode 100644 core/src/rt2/mempool.rs create mode 100644 core/src/rt2/mod.rs diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs new file mode 100644 index 0000000..b9f9ebf --- /dev/null +++ b/core/src/ffi/dpdk.rs @@ -0,0 +1,156 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use capsule_ffi as cffi; + +use super::{AsStr, EasyPtr, ToCString, ToResult}; +use crate::debug; +use anyhow::Result; +use std::fmt; +use std::ops::DerefMut; +use std::os::raw; +use thiserror::Error; + +/// Initializes the Environment Abstraction Layer (EAL). +pub(crate) fn eal_init>(args: Vec) -> Result<()> { + let args = args + .into_iter() + .map(|s| Into::::into(s).into_cstring()) + .collect::>(); + debug!(arguments=?args); + + let mut ptrs = args + .iter() + .map(|s| s.as_ptr() as *mut raw::c_char) + .collect::>(); + let len = ptrs.len() as raw::c_int; + + let parsed = + unsafe { cffi::rte_eal_init(len, ptrs.as_mut_ptr()).into_result(DpdkError::from_errno)? }; + debug!("EAL parsed {} arguments.", parsed); + + Ok(()) +} + +/// Cleans up the Environment Abstraction Layer (EAL). +pub(crate) fn eal_cleanup() -> Result<()> { + unsafe { cffi::rte_eal_cleanup() } + .into_result(DpdkError::from_errno) + .map(|_| ()) +} + +/// An opaque identifier for a physical CPU socket. +/// +/// A socket is also known as a NUMA node. On a multi-socket system, for best +/// performance, ensure that the cores and memory used for packet processing +/// are in the same socket as the network interface card. +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub(crate) struct SocketId(raw::c_int); + +impl SocketId { + /// A socket ID representing any NUMA socket. + pub(crate) const ANY: Self = SocketId(-1); + + /// Returns the ID of the socket the current core is on. + #[inline] + pub(crate) fn current() -> SocketId { + unsafe { SocketId(cffi::rte_socket_id() as raw::c_int) } + } +} + +impl fmt::Debug for SocketId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "socket{}", self.0) + } +} + +impl From for SocketId { + fn from(id: raw::c_int) -> Self { + SocketId(id) + } +} + +/// A `rte_mempool` pointer. +pub(crate) type MempoolPtr = EasyPtr; + +/// Creates a mbuf pool. +pub(crate) fn pktmbuf_pool_create>( + name: S, + capacity: usize, + cache_size: usize, + socket_id: SocketId, +) -> Result { + let name: String = name.into(); + + let ptr = unsafe { + cffi::rte_pktmbuf_pool_create( + name.into_cstring().as_ptr(), + capacity as raw::c_uint, + cache_size as raw::c_uint, + 0, + cffi::RTE_MBUF_DEFAULT_BUF_SIZE as u16, + socket_id.0, + ) + .into_result(|_| DpdkError::new())? + }; + + Ok(EasyPtr(ptr)) +} + +/// Looks up a mempool by the name. +pub(crate) fn mempool_lookup>(name: S) -> Result { + let name: String = name.into(); + + let ptr = unsafe { + cffi::rte_mempool_lookup(name.into_cstring().as_ptr()).into_result(|_| DpdkError::new())? + }; + + Ok(EasyPtr(ptr)) +} + +/// Frees a mempool. +pub(crate) fn mempool_free(ptr: &mut MempoolPtr) { + unsafe { cffi::rte_mempool_free(ptr.deref_mut()) }; +} + +/// An error generated in `libdpdk`. +/// +/// When an FFI call fails, the `errno` is translated into `DpdkError`. +#[derive(Debug, Error)] +#[error("{0}")] +pub(crate) struct DpdkError(String); + +impl DpdkError { + /// Returns the `DpdkError` for the most recent failure on the current + /// thread. + #[inline] + pub(crate) fn new() -> Self { + DpdkError::from_errno(-1) + } + + /// Returns the `DpdkError` for a specific `errno`. + #[inline] + fn from_errno(errno: raw::c_int) -> Self { + let errno = if errno == -1 { + unsafe { cffi::_rte_errno() } + } else { + -errno + }; + DpdkError(unsafe { cffi::rte_strerror(errno).as_str().into() }) + } +} diff --git a/core/src/ffi.rs b/core/src/ffi/mod.rs similarity index 86% rename from core/src/ffi.rs rename to core/src/ffi/mod.rs index 5bb7baa..e46389d 100644 --- a/core/src/ffi.rs +++ b/core/src/ffi/mod.rs @@ -16,12 +16,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +pub(crate) mod dpdk; + pub(crate) use capsule_ffi::*; use crate::warn; use anyhow::Result; use std::error::Error; use std::ffi::{CStr, CString}; +use std::ops::{Deref, DerefMut}; use std::os::raw; use std::ptr::NonNull; @@ -139,3 +142,21 @@ impl ToResult for raw::c_int { } } } + +/// Makes NonNull even easier to use with the downside that it hides the +/// unsafeness of the underlying pointer access. +pub(crate) struct EasyPtr(NonNull); + +impl Deref for EasyPtr { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { self.0.as_ref() } + } +} + +impl DerefMut for EasyPtr { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { self.0.as_mut() } + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 762b545..f943915 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -124,7 +124,7 @@ extern crate self as capsule; pub mod batch; pub mod config; mod dpdk; -mod ffi; +pub(crate) mod ffi; mod macros; #[cfg(feature = "metrics")] #[cfg_attr(docsrs, doc(cfg(all(feature = "default", feature = "metrics"))))] @@ -134,6 +134,7 @@ pub mod packets; #[cfg(feature = "pcap-dump")] #[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] mod pcap; +mod rt2; mod runtime; #[cfg(any(test, feature = "testils"))] #[cfg_attr(docsrs, doc(cfg(feature = "testils")))] diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs new file mode 100644 index 0000000..4ad9341 --- /dev/null +++ b/core/src/rt2/mempool.rs @@ -0,0 +1,132 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use crate::ffi::dpdk::{self, MempoolPtr, SocketId}; +use crate::ffi::AsStr; +use crate::{debug, info}; +use anyhow::Result; +use std::fmt; + +/// A memory pool is an allocator of message buffers, or `Mbuf`. For best +/// performance, each socket should have a dedicated `Mempool`. However, +/// for simplicity, we currently only support one global Mempool. Multi- +/// socket support may be added in the future. +pub(crate) struct Mempool { + ptr: MempoolPtr, +} + +impl Mempool { + /// Creates a new `Mempool`. + /// + /// `capacity` is the maximum number of Mbufs in the pool. The optimum + /// size (in terms of memory usage) is when n is a power of two minus one. + /// + /// `cache_size` is the per core cache size. If `cache_size` is non-zero, + /// caching is enabled. New `Mbuf` will be retrieved first from cache, + /// subsequently from the common pool. The cache can be disabled if + /// `cache_size` is set to 0. + pub(crate) fn new>( + name: S, + capacity: usize, + cache_size: usize, + ) -> Result { + let name: String = name.into(); + let ptr = dpdk::pktmbuf_pool_create(&name, capacity, cache_size, SocketId::current())?; + + info!(mempool = ?name, "pool created."); + + Ok(Self { ptr }) + } + + /// Returns the pool name. + #[inline] + pub(crate) fn name(&self) -> &str { + self.ptr.name[..].as_str() + } + + /// Returns the maximum number of Mbufs in the pool. + #[inline] + pub(crate) fn capacity(&self) -> usize { + self.ptr.size as usize + } + + /// Returns the per core cache size. + #[inline] + pub(crate) fn cache_size(&self) -> usize { + self.ptr.cache_size as usize + } + + /// Returns the socket the pool is allocated from. + #[inline] + pub(crate) fn socket(&self) -> SocketId { + self.ptr.socket_id.into() + } +} + +impl fmt::Debug for Mempool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Mempool") + .field("name", &self.name()) + .field("capacity", &self.capacity()) + .field("cache_size", &self.cache_size()) + .field("socket", &self.socket()) + .finish() + } +} + +impl Drop for Mempool { + fn drop(&mut self) { + unsafe { + dpdk::mempool_free(&mut self.ptr); + } + + debug!(mempool = ?self.name(), "pool freed."); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[capsule::test] + fn create_mempool() -> Result<()> { + let pool = Mempool::new("pool1", 15, 1)?; + + assert_eq!("pool1", pool.name()); + assert_eq!(15, pool.capacity()); + assert_eq!(1, pool.cache_size()); + + Ok(()) + } + + #[capsule::test] + fn drop_mempool() -> Result<()> { + let name = "pool2"; + let pool = Mempool::new(name, 7, 0)?; + + let res = dpdk::mempool_lookup(name); + assert!(res.is_ok()); + + drop(pool); + + let res = dpdk::mempool_lookup(name); + assert!(res.is_err()); + + Ok(()) + } +} diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs new file mode 100644 index 0000000..957f214 --- /dev/null +++ b/core/src/rt2/mod.rs @@ -0,0 +1,19 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +mod mempool; From 6408a4739cc715fe2eb5e8b33cd3fb3fc6bae723 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Fri, 12 Mar 2021 17:22:15 -0500 Subject: [PATCH 02/18] struggle of a lifetime --- core/Cargo.toml | 3 + core/core | 0 core/src/ffi/dpdk.rs | 78 +++++++++++++++++++- core/src/rt2/lcore.rs | 142 ++++++++++++++++++++++++++++++++++++ core/src/rt2/mempool.rs | 5 +- core/src/rt2/mod.rs | 1 + core/src/testils/mod.rs | 4 + ffi/src/bindings.h | 9 +++ ffi/src/bindings_rustdoc.rs | 4 + ffi/src/shim.c | 5 ++ 10 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 core/core create mode 100644 core/src/rt2/lcore.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 17b923a..abdfe1a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,10 +21,13 @@ doctest = false [dependencies] anyhow = "1.0" +async-channel = "1.6" +async-executor = "1.4" capsule-ffi = { version = "0.1.5", path = "../ffi" } capsule-macros = { version = "0.1.5", path = "../macros" } clap = "2.33" criterion = { version = "0.3", optional = true } +futures-lite = "1.11" futures-preview = "=0.3.0-alpha.19" libc = "0.2" metrics-core = { version = "0.5", optional = true } diff --git a/core/core b/core/core new file mode 100644 index 0000000..e69de29 diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index b9f9ebf..95acb47 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -16,14 +16,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use capsule_ffi as cffi; - use super::{AsStr, EasyPtr, ToCString, ToResult}; -use crate::debug; +use crate::{debug, error}; use anyhow::Result; +use capsule_ffi as cffi; use std::fmt; use std::ops::DerefMut; use std::os::raw; +use std::panic::{self, AssertUnwindSafe}; use thiserror::Error; /// Initializes the Environment Abstraction Layer (EAL). @@ -128,6 +128,78 @@ pub(crate) fn mempool_free(ptr: &mut MempoolPtr) { unsafe { cffi::rte_mempool_free(ptr.deref_mut()) }; } +/// An opaque identifier for a logical execution unit of the processor. +#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub(crate) struct LcoreId(raw::c_uint); + +impl LcoreId { + /// Any lcore to indicate that no thread affinity is set. + pub(crate) const ANY: Self = LcoreId(raw::c_uint::MAX); + + /// Returns the ID of the current execution unit or `LcoreId::ANY` when + /// called from a non-EAL thread. + #[inline] + pub(crate) fn current() -> LcoreId { + unsafe { LcoreId(cffi::_rte_lcore_id()) } + } +} + +impl fmt::Debug for LcoreId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "lcore{}", self.0) + } +} + +/// Gets the next enabled lcore ID. +pub(crate) fn get_next_lcore( + id: Option, + skip_master: bool, + wrap: bool, +) -> Option { + let (i, wrap) = match id { + Some(id) => (id.0, wrap as raw::c_int), + None => (raw::c_uint::MAX, 1), + }; + + let skip_master = skip_master as raw::c_int; + + match unsafe { cffi::rte_get_next_lcore(i, skip_master, wrap) } { + cffi::RTE_MAX_LCORE => None, + id @ _ => Some(LcoreId(id)), + } +} + +/// The function passed to `rte_eal_remote_launch`. +unsafe extern "C" fn lcore_fn(arg: *mut raw::c_void) -> raw::c_int +where + F: FnOnce() -> () + Send + 'static, +{ + let f = Box::from_raw(arg as *mut F); + + // in case the closure panics, let's not crash the app. + let result = panic::catch_unwind(AssertUnwindSafe(f)); + + if let Err(err) = result { + error!(lcore = ?LcoreId::current(), error = ?err, "failed to execute closure."); + } + + 0 +} + +/// Launches a function on another lcore. +pub(crate) fn eal_remote_launch(worker_id: LcoreId, f: F) -> Result<()> +where + F: FnOnce() -> () + Send + 'static, +{ + let ptr = Box::into_raw(Box::new(f)) as *mut raw::c_void; + + unsafe { + cffi::rte_eal_remote_launch(Some(lcore_fn::), ptr, worker_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + /// An error generated in `libdpdk`. /// /// When an FFI call fails, the `errno` is translated into `DpdkError`. diff --git a/core/src/rt2/lcore.rs b/core/src/rt2/lcore.rs new file mode 100644 index 0000000..31f5fcf --- /dev/null +++ b/core/src/rt2/lcore.rs @@ -0,0 +1,142 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use crate::ffi::dpdk::{self, LcoreId}; +use crate::info; +use anyhow::Result; +use async_channel::{self, Receiver, Recv, Sender}; +use async_executor::Executor; +use futures_lite::future; +use std::future::Future; +use std::sync::Arc; + +/// Trigger for the shutdown. +pub(crate) struct Trigger(Sender<()>); + +impl Trigger { + /// Triggers the shutdown. + pub(crate) fn fire(self) { + drop(self.0) + } +} + +/// Shutdown wait handle. +pub(crate) struct Shutdown(Receiver<()>); + +impl Shutdown { + /// A future that waits till the trigger is fired. + pub(crate) fn wait(&self) -> Recv<'_, ()> { + self.0.recv() + } +} + +/// Creates a shutdown and trigger pair. +/// +/// Leverages the behavior of an async channel. When the sender is dropped +/// from scope, it closes the channel and causes the receiver side future +/// in the executor queue to resolve. +pub(crate) fn shutdown_trigger() -> (Trigger, Shutdown) { + let (s, r) = async_channel::unbounded(); + (Trigger(s), Shutdown(r)) +} + +/// An async executor abstraction on top of a DPDK logical core. +pub(crate) struct Lcore { + id: LcoreId, + executor: Arc>, + trigger: Option, +} + +impl Lcore { + /// Creates a new executor for the given lcore id. + fn new(id: LcoreId) -> Result { + let executor = Arc::new(Executor::new()); + let (trigger, shutdown) = shutdown_trigger(); + + let executor2 = Arc::clone(&executor); + dpdk::eal_remote_launch(id, move || { + info!(lcore = ?id, "lcore started."); + let _ = future::block_on(executor2.run(shutdown.wait())); + info!(lcore = ?id, "lcore stopped."); + })?; + + Ok(Lcore { + id, + executor, + trigger: Some(trigger), + }) + } + + /// Spawns an async task and waits for it to complete. + pub(crate) fn block_on( + &self, + future: impl Future + Send + 'static, + ) -> T { + let task = self.executor.spawn(future); + future::block_on(task) + } + + /// Spawns a background async task. + pub(crate) fn spawn(&self, future: impl Future + Send + 'static) { + self.executor.spawn(future).detach(); + } +} + +impl Drop for Lcore { + fn drop(&mut self) { + if let Some(trigger) = self.trigger.take() { + info!(lcore = ?self.id, "stopping lcore."); + trigger.fire(); + } + } +} + +/// Returns the enabled worker lcores. +pub(crate) fn lcore_pool() -> Vec { + let mut lcores = Vec::new(); + let mut current = None; + + while let Some(id) = dpdk::get_next_lcore(current, true, false) { + lcores.push(Lcore::new(id).unwrap()); + current = Some(id); + } + + lcores +} + +#[cfg(test)] +mod tests { + use super::*; + use std::thread; + + #[capsule::test] + fn get_current_lcore_id_from_eal() { + let next_id = dpdk::get_next_lcore(None, true, false).expect("panic!"); + let lcore = Lcore::new(next_id).expect("panic!"); + let lcore_id = lcore.block_on(async { LcoreId::current() }); + + assert_eq!(next_id, lcore_id); + } + + #[capsule::test] + fn get_current_lcore_id_from_non_eal() { + let lcore_id = thread::spawn(|| LcoreId::current()).join().expect("panic!"); + + assert_eq!(LcoreId::ANY, lcore_id); + } +} diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs index 4ad9341..e3ccf60 100644 --- a/core/src/rt2/mempool.rs +++ b/core/src/rt2/mempool.rs @@ -91,10 +91,7 @@ impl fmt::Debug for Mempool { impl Drop for Mempool { fn drop(&mut self) { - unsafe { - dpdk::mempool_free(&mut self.ptr); - } - + dpdk::mempool_free(&mut self.ptr); debug!(mempool = ?self.name(), "pool freed."); } } diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 957f214..8e165cf 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -16,4 +16,5 @@ * SPDX-License-Identifier: Apache-2.0 */ +mod lcore; mod mempool; diff --git a/core/src/testils/mod.rs b/core/src/testils/mod.rs index b5ed0c9..5f7b33f 100644 --- a/core/src/testils/mod.rs +++ b/core/src/testils/mod.rs @@ -41,6 +41,10 @@ pub fn cargo_test_init() { "capsule_test".to_owned(), "--no-huge".to_owned(), "--iova-mode=va".to_owned(), + "--master-lcore".to_owned(), + "127".to_owned(), + "--lcores".to_owned(), + "0,1,127@0".to_owned(), ]) .unwrap(); let _ = metrics::init(); diff --git a/ffi/src/bindings.h b/ffi/src/bindings.h index 4f09632..fbfd33f 100644 --- a/ffi/src/bindings.h +++ b/ffi/src/bindings.h @@ -24,7 +24,11 @@ #include #include #include +#include +#include #include +#include +#include // libnuma functions and types #include @@ -41,6 +45,11 @@ */ int _rte_errno(void); +/** + * Return the Application thread ID of the execution unit. + */ +unsigned _rte_lcore_id(void); + /** * Allocate a new mbuf from a mempool. */ diff --git a/ffi/src/bindings_rustdoc.rs b/ffi/src/bindings_rustdoc.rs index 4f76658..1c97bdb 100644 --- a/ffi/src/bindings_rustdoc.rs +++ b/ffi/src/bindings_rustdoc.rs @@ -26077,6 +26077,10 @@ extern "C" { #[doc = " calls to certain functions to determine why those functions failed."] pub fn _rte_errno() -> ::std::os::raw::c_int; } +extern "C" { + #[doc = " Return the Application thread ID of the execution unit."] + pub fn _rte_lcore_id() -> ::std::os::raw::c_uint; +} extern "C" { #[doc = " Allocate a new mbuf from a mempool."] pub fn _rte_pktmbuf_alloc(mp: *mut rte_mempool) -> *mut rte_mbuf; diff --git a/ffi/src/shim.c b/ffi/src/shim.c index 421fdd9..b334ceb 100644 --- a/ffi/src/shim.c +++ b/ffi/src/shim.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -25,6 +26,10 @@ int _rte_errno(void) { return rte_errno; } +unsigned _rte_lcore_id(void) { + return rte_lcore_id(); +} + struct rte_mbuf *_rte_pktmbuf_alloc(struct rte_mempool *mp) { return rte_pktmbuf_alloc(mp); } From 9cc672371c251181fc8a7cfeb47402fe6e1a28f0 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Sun, 14 Mar 2021 19:04:58 -0400 Subject: [PATCH 03/18] more ffi --- core/src/ffi/dpdk.rs | 179 ++++++++++++++++++ core/src/rt2/lcore.rs | 4 + core/src/rt2/mempool.rs | 9 + core/src/rt2/mod.rs | 3 + core/src/rt2/port.rs | 407 ++++++++++++++++++++++++++++++++++++++++ core/src/testils/mod.rs | 29 ++- 6 files changed, 623 insertions(+), 8 deletions(-) create mode 100644 core/src/rt2/port.rs diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 95acb47..a7fd857 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -17,6 +17,7 @@ */ use super::{AsStr, EasyPtr, ToCString, ToResult}; +use crate::net::MacAddr; use crate::{debug, error}; use anyhow::Result; use capsule_ffi as cffi; @@ -24,6 +25,7 @@ use std::fmt; use std::ops::DerefMut; use std::os::raw; use std::panic::{self, AssertUnwindSafe}; +use std::ptr; use thiserror::Error; /// Initializes the Environment Abstraction Layer (EAL). @@ -200,6 +202,183 @@ where } } +/// An opaque identifier for a PMD device port. +#[derive(Copy, Clone)] +pub(crate) struct PortId(u16); + +impl PortId { + /// Returns the ID of the socket the port is connected to. + #[inline] + pub(crate) fn socket(self) -> SocketId { + unsafe { cffi::rte_eth_dev_socket_id(self.0).into() } + } +} + +impl fmt::Debug for PortId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "port{}", self.0) + } +} + +/// Gets the port id from device name. +pub(crate) fn eth_dev_get_port_by_name>(name: S) -> Result { + let name: String = name.into(); + let mut port_id = 0u16; + unsafe { + cffi::rte_eth_dev_get_port_by_name(name.into_cstring().as_ptr(), &mut port_id) + .into_result(DpdkError::from_errno)?; + } + Ok(PortId(port_id)) +} + +/// Retrieves the Ethernet address of a device. +pub(crate) fn eth_macaddr_get(port_id: PortId) -> Result { + let mut addr = cffi::rte_ether_addr::default(); + unsafe { + cffi::rte_eth_macaddr_get(port_id.0, &mut addr).into_result(DpdkError::from_errno)?; + } + Ok(addr.addr_bytes.into()) +} + +/// Retrieves the contextual information of a device. +pub(crate) fn eth_dev_info_get(port_id: PortId) -> Result { + let mut port_info = cffi::rte_eth_dev_info::default(); + unsafe { + cffi::rte_eth_dev_info_get(port_id.0, &mut port_info).into_result(DpdkError::from_errno)?; + } + Ok(port_info) +} + +/// Checks that numbers of Rx and Tx descriptors satisfy descriptors limits +/// from the ethernet device information, otherwise adjust them to boundaries. +pub(crate) fn eth_dev_adjust_nb_rx_tx_desc( + port_id: PortId, + nb_rx_desc: usize, + nb_tx_desc: usize, +) -> Result<(usize, usize)> { + let mut nb_rx_desc = nb_rx_desc as u16; + let mut nb_tx_desc = nb_tx_desc as u16; + + unsafe { + cffi::rte_eth_dev_adjust_nb_rx_tx_desc(port_id.0, &mut nb_rx_desc, &mut nb_tx_desc) + .into_result(DpdkError::from_errno)?; + } + + Ok((nb_rx_desc as usize, nb_tx_desc as usize)) +} + +/// Returns the value of promiscuous mode for a device. +pub(crate) fn eth_promiscuous_get(port_id: PortId) -> bool { + match unsafe { cffi::rte_eth_promiscuous_get(port_id.0).into_result(DpdkError::from_errno) } { + Ok(1) => true, + // assuming port_id is valid, we treat error as mode disabled. + _ => false, + } +} + +/// Enables receipt in promiscuous mode for a device. +pub(crate) fn eth_promiscuous_enable(port_id: PortId) -> Result<()> { + unsafe { + cffi::rte_eth_promiscuous_enable(port_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Disables receipt in promiscuous mode for a device. +pub(crate) fn eth_promiscuous_disable(port_id: PortId) -> Result<()> { + unsafe { + cffi::rte_eth_promiscuous_disable(port_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Returns the value of allmulticast mode for a device. +pub(crate) fn eth_allmulticast_get(port_id: PortId) -> bool { + match unsafe { cffi::rte_eth_allmulticast_get(port_id.0).into_result(DpdkError::from_errno) } { + Ok(1) => true, + // assuming port_id is valid, we treat error as mode disabled. + _ => false, + } +} + +/// Enables the receipt of any multicast frame by a device. +pub(crate) fn eth_allmulticast_enable(port_id: PortId) -> Result<()> { + unsafe { + cffi::rte_eth_allmulticast_enable(port_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Disables the receipt of any multicast frame by a device. +pub(crate) fn eth_allmulticast_disable(port_id: PortId) -> Result<()> { + unsafe { + cffi::rte_eth_allmulticast_disable(port_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Configures a device. +pub(crate) fn eth_dev_configure( + port_id: PortId, + nb_rx_queue: usize, + nb_tx_queue: usize, + eth_conf: &cffi::rte_eth_conf, +) -> Result<()> { + unsafe { + cffi::rte_eth_dev_configure(port_id.0, nb_rx_queue as u16, nb_tx_queue as u16, eth_conf) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Allocates and sets up a receive queue for a device. +pub(crate) fn eth_rx_queue_setup( + port_id: PortId, + rx_queue_id: usize, + nb_rx_desc: usize, + socket_id: SocketId, + rx_conf: Option<&cffi::rte_eth_rxconf>, + mb_pool: &mut MempoolPtr, +) -> Result<()> { + unsafe { + cffi::rte_eth_rx_queue_setup( + port_id.0, + rx_queue_id as u16, + nb_rx_desc as u16, + socket_id.0 as raw::c_uint, + rx_conf.map_or(ptr::null(), |conf| conf), + mb_pool.deref_mut(), + ) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Allocates and sets up a transmit queue for a device. +pub(crate) fn eth_tx_queue_setup( + port_id: PortId, + tx_queue_id: usize, + nb_tx_desc: usize, + socket_id: SocketId, + tx_conf: Option<&cffi::rte_eth_txconf>, +) -> Result<()> { + unsafe { + cffi::rte_eth_tx_queue_setup( + port_id.0, + tx_queue_id as u16, + nb_tx_desc as u16, + socket_id.0 as raw::c_uint, + tx_conf.map_or(ptr::null(), |conf| conf), + ) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + /// An error generated in `libdpdk`. /// /// When an FFI call fails, the `errno` is translated into `DpdkError`. diff --git a/core/src/rt2/lcore.rs b/core/src/rt2/lcore.rs index 31f5fcf..7aa4916 100644 --- a/core/src/rt2/lcore.rs +++ b/core/src/rt2/lcore.rs @@ -64,6 +64,10 @@ pub(crate) struct Lcore { impl Lcore { /// Creates a new executor for the given lcore id. + /// + /// # Errors + /// + /// Returns `DpdkError` if the executor fails to run on the given lcore. fn new(id: LcoreId) -> Result { let executor = Arc::new(Executor::new()); let (trigger, shutdown) = shutdown_trigger(); diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs index e3ccf60..2ae27df 100644 --- a/core/src/rt2/mempool.rs +++ b/core/src/rt2/mempool.rs @@ -40,6 +40,10 @@ impl Mempool { /// caching is enabled. New `Mbuf` will be retrieved first from cache, /// subsequently from the common pool. The cache can be disabled if /// `cache_size` is set to 0. + /// + /// # Errors + /// + /// Returns `DpdkError` if the mempool allocation fails. pub(crate) fn new>( name: S, capacity: usize, @@ -53,6 +57,11 @@ impl Mempool { Ok(Self { ptr }) } + /// Returns the raw pointer. + pub(crate) fn ptr_mut(&mut self) -> &mut MempoolPtr { + &mut self.ptr + } + /// Returns the pool name. #[inline] pub(crate) fn name(&self) -> &str { diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 8e165cf..4fdf9bc 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -18,3 +18,6 @@ mod lcore; mod mempool; +mod port; + +pub(crate) use self::mempool::Mempool; diff --git a/core/src/rt2/port.rs b/core/src/rt2/port.rs new file mode 100644 index 0000000..15b1995 --- /dev/null +++ b/core/src/rt2/port.rs @@ -0,0 +1,407 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use super::Mempool; +use crate::ffi::dpdk::{self, PortId}; +use crate::net::MacAddr; +use crate::{debug, ensure, info, warn}; +use anyhow::Result; +use capsule_ffi as cffi; +use std::fmt; +use thiserror::Error; + +/// A PMD device port. +pub(crate) struct Port { + name: String, + port_id: PortId, + rx_lcores: Vec, + tx_lcores: Vec, +} + +impl Port { + /// Returns the application assigned logical name of the port. + /// + /// For applications with more than one port, this name can be used to + /// identifer the port. + pub(crate) fn name(&self) -> &str { + &self.name + } + + /// Returns the port ID. + pub(crate) fn port_id(&self) -> PortId { + self.port_id + } + + /// Returns the MAC address of the port. + /// + /// If fails to retrieve the MAC address, `MacAddr::default` is returned. + pub(crate) fn mac_addr(&self) -> MacAddr { + dpdk::eth_macaddr_get(self.port_id).unwrap_or_default() + } + + /// Returns whether the port has promiscuous mode enabled. + pub(crate) fn promiscuous(&self) -> bool { + dpdk::eth_promiscuous_get(self.port_id) + } + + /// Returns whether the port has multicast mode enabled. + pub(crate) fn multicast(&self) -> bool { + dpdk::eth_allmulticast_get(self.port_id) + } +} + +impl fmt::Debug for Port { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Port") + .field("name", &self.name()) + .field("port_id", &self.port_id()) + .field("mac_addr", &format_args!("{}", self.mac_addr())) + .field("rx_lcores", &self.rx_lcores) + .field("tx_lcores", &self.tx_lcores) + .field("promiscuous", &self.promiscuous()) + .field("multicast", &self.multicast()) + .finish() + } +} + +/// Port related errors. +#[derive(Debug, Error)] +pub(crate) enum PortError { + /// The maximum number of RX queues is less than the number of queues + /// requested. + #[error("Insufficient number of RX queues. Max is {0}.")] + InsufficientRxQueues(u16), + + /// The maximum number of TX queues is less than the number of queues + /// requested. + #[error("Insufficient number of TX queues. Max is {0}.")] + InsufficientTxQueues(u16), +} + +pub(crate) struct Builder { + name: String, + port_id: PortId, + port_info: cffi::rte_eth_dev_info, + port_conf: cffi::rte_eth_conf, + rx_lcores: Vec, + tx_lcores: Vec, + rxqs: usize, + txqs: usize, +} + +impl Builder { + /// Creates a new port `Builder` with a logical name and device name. + /// + /// The device name can be the following + /// * PCIe address (domain:bus:device.function), for example `0000:02:00.0` + /// * DPDK virtual device name, for example `net_[pcap0|null0|tap0]` + /// + /// # Errors + /// + /// Returns `DpdkError` if the `device` is not found or failed to retrieve + /// the contextual information for the device. + pub(crate) fn for_device, S2: Into>( + name: S1, + device: S2, + ) -> Result { + let name: String = name.into(); + let device: String = device.into(); + + let port_id = dpdk::eth_dev_get_port_by_name(&device)?; + debug!(port = ?name, id = ?port_id, ?device); + + let port_info = dpdk::eth_dev_info_get(port_id)?; + + Ok(Builder { + name, + port_id, + port_info, + port_conf: cffi::rte_eth_conf::default(), + rx_lcores: vec![], + tx_lcores: vec![], + rxqs: port_info.rx_desc_lim.nb_min as usize, + txqs: port_info.tx_desc_lim.nb_min as usize, + }) + } + + /// Sets the lcores to receive packets on. + /// + /// Enables receive side scaling if more than one lcore is used for RX or + /// packet processing is offloaded to the workers. + /// + /// # Errors + /// + /// Returns `PortError` if the maximum number of RX queues is less than + /// the number of lcores assigned. + pub(crate) fn set_rx_lcores(&mut self, lcores: Vec) -> Result<&mut Self> { + ensure!( + !lcores.is_empty() && self.port_info.max_rx_queues >= lcores.len() as u16, + PortError::InsufficientRxQueues(self.port_info.max_rx_queues) + ); + + if lcores.len() > 1 { + const RSS_HF: u64 = + (cffi::ETH_RSS_IP | cffi::ETH_RSS_TCP | cffi::ETH_RSS_UDP | cffi::ETH_RSS_SCTP) + as u64; + + // enables receive side scaling. + self.port_conf.rxmode.mq_mode = cffi::rte_eth_rx_mq_mode::ETH_MQ_RX_RSS; + self.port_conf.rx_adv_conf.rss_conf.rss_hf = + self.port_info.flow_type_rss_offloads & RSS_HF; + + debug!( + port = ?self.name, + rss_hf = self.port_conf.rx_adv_conf.rss_conf.rss_hf, + "receive side scaling enabled." + ); + } + + self.rx_lcores = lcores; + Ok(self) + } + + /// Sets the lcores to transmit packets on. + /// + /// # Errors + /// + /// Returns `PortError` if the maximum number of TX queues is less than + /// the number of lcores assigned. + pub(crate) fn set_tx_lcores(&mut self, lcores: Vec) -> Result<&mut Self> { + ensure!( + !lcores.is_empty() && self.port_info.max_tx_queues >= lcores.len() as u16, + PortError::InsufficientTxQueues(self.port_info.max_tx_queues) + ); + + self.tx_lcores = lcores; + Ok(self) + } + + /// Sets the capacity of each RX queue and TX queue. + /// + /// If the sizes are not within the limits of the device, they are adjusted + /// to the boundaries. + /// + /// # Errors + /// + /// Returns `DpdkError` if failed to set the queue capacity. + pub(crate) fn set_rxq_txq_capacity(&mut self, rxqs: usize, txqs: usize) -> Result<&mut Self> { + let (rxqs2, txqs2) = dpdk::eth_dev_adjust_nb_rx_tx_desc(self.port_id, rxqs, txqs)?; + + info!( + cond: rxqs2 != rxqs, + port = ?self.name, + before = rxqs, + after = rxqs2, + "rx ring size adjusted to limits.", + ); + info!( + cond: txqs2 != txqs, + port = ?self.name, + before = txqs, + after = txqs2, + "tx ring size adjusted to limits.", + ); + + self.rxqs = rxqs2; + self.txqs = txqs2; + Ok(self) + } + + /// Sets the promiscuous mode of the port. + /// + /// # Errors + /// + /// Returns `DpdkError` if the device does not support configurable mode. + pub(crate) fn set_promiscuous(&mut self, enable: bool) -> Result<&mut Self> { + if enable { + dpdk::eth_promiscuous_enable(self.port_id)?; + debug!(port = ?self.name, "promiscuous mode enabled."); + } else { + dpdk::eth_promiscuous_disable(self.port_id)?; + debug!(port = ?self.name, "promiscuous mode disabled."); + } + + Ok(self) + } + + /// Sets the multicast mode of the port. + /// + /// # Errors + /// + /// Returns `DpdkError` if the device does not support configurable mode. + pub(crate) fn set_multicast(&mut self, enable: bool) -> Result<&mut Self> { + if enable { + dpdk::eth_allmulticast_enable(self.port_id)?; + debug!(port = ?self.name, "multicast mode enabled."); + } else { + dpdk::eth_allmulticast_disable(self.port_id)?; + debug!(port = ?self.name, "multicast mode disabled."); + } + + Ok(self) + } + + /// Builds the port. + /// + /// # Errors + /// + /// Returns `DpdkError` if fails to configure the device or any of the + /// rx and tx queues. + pub(crate) fn build(&mut self, mempool: &mut Mempool) -> Result { + // turns on optimization for mbuf fast free. + if self.port_info.tx_offload_capa & cffi::DEV_TX_OFFLOAD_MBUF_FAST_FREE as u64 > 0 { + self.port_conf.txmode.offloads |= cffi::DEV_TX_OFFLOAD_MBUF_FAST_FREE as u64; + debug!(port = ?self.name, "mbuf fast free enabled."); + } + + // configures the device before everything else. + dpdk::eth_dev_configure( + self.port_id, + self.rx_lcores.len(), + self.tx_lcores.len(), + &self.port_conf, + )?; + + let socket = self.port_id.socket(); + warn!( + cond: mempool.socket() != socket, + message = "mempool socket does not match port socket.", + mempool = ?mempool.socket(), + port = ?socket + ); + + // configures the rx queues. + for index in 0..self.rx_lcores.len() { + dpdk::eth_rx_queue_setup( + self.port_id, + index, + self.rxqs, + socket, + None, + mempool.ptr_mut(), + )?; + } + + // configures the tx queues. + for index in 0..self.tx_lcores.len() { + dpdk::eth_tx_queue_setup(self.port_id, index, self.txqs, socket, None)?; + } + + Ok(Port { + name: self.name.clone(), + port_id: self.port_id, + rx_lcores: self.rx_lcores.clone(), + tx_lcores: self.tx_lcores.clone(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[capsule::test] + fn port_not_found() { + assert!(Builder::for_device("test0", "notfound").is_err()); + } + + #[capsule::test] + fn set_rx_lcores() -> Result<()> { + let mut builder = Builder::for_device("test0", "net_ring0")?; + + // ring port has a max rxq of 16. + let lcores = (0..17).collect::>(); + assert!(builder.set_rx_lcores(lcores).is_err()); + + let lcores = (0..16).collect::>(); + assert!(builder.set_rx_lcores(lcores.clone()).is_ok()); + assert_eq!(lcores, builder.rx_lcores); + assert_eq!( + cffi::rte_eth_rx_mq_mode::ETH_MQ_RX_RSS, + builder.port_conf.rxmode.mq_mode + ); + + Ok(()) + } + + #[capsule::test] + fn set_tx_lcores() -> Result<()> { + let mut builder = Builder::for_device("test0", "net_ring0")?; + + // ring port has a max txq of 16. + let lcores = (0..17).collect::>(); + assert!(builder.set_tx_lcores(lcores).is_err()); + + let lcores = (0..16).collect::>(); + assert!(builder.set_tx_lcores(lcores.clone()).is_ok()); + assert_eq!(lcores, builder.tx_lcores); + + Ok(()) + } + + #[capsule::test] + fn set_rxq_txq_capacity() -> Result<()> { + let mut builder = Builder::for_device("test0", "net_ring0")?; + + // unfortunately can't test boundary adjustment + assert!(builder.set_rxq_txq_capacity(32, 32).is_ok()); + assert_eq!(32, builder.rxqs); + assert_eq!(32, builder.txqs); + + Ok(()) + } + + #[capsule::test] + fn set_promiscuous() -> Result<()> { + let mut builder = Builder::for_device("test0", "net_tap0")?; + + assert!(builder.set_promiscuous(true).is_ok()); + assert!(builder.set_promiscuous(false).is_ok()); + + Ok(()) + } + + #[capsule::test] + fn set_multicast() -> Result<()> { + let mut builder = Builder::for_device("test0", "net_tap0")?; + + assert!(builder.set_multicast(true).is_ok()); + assert!(builder.set_multicast(false).is_ok()); + + Ok(()) + } + + #[capsule::test] + fn build_port() -> Result<()> { + let rx_lcores = (0..2).collect::>(); + let tx_lcores = (3..6).collect::>(); + let mut pool = Mempool::new("mp_build_port", 15, 0)?; + let port = Builder::for_device("test0", "net_ring0")? + .set_rx_lcores(rx_lcores.clone())? + .set_tx_lcores(tx_lcores.clone())? + .build(&mut pool)?; + + assert_eq!("test0", port.name()); + assert!(port.promiscuous()); + assert!(port.multicast()); + assert_eq!(rx_lcores, port.rx_lcores); + assert_eq!(tx_lcores, port.tx_lcores); + + Ok(()) + } +} diff --git a/core/src/testils/mod.rs b/core/src/testils/mod.rs index 5f7b33f..468a7ca 100644 --- a/core/src/testils/mod.rs +++ b/core/src/testils/mod.rs @@ -27,7 +27,8 @@ mod rvg; pub use self::packet::*; pub use self::rvg::*; -use crate::dpdk::{self, Mempool, SocketId, MEMPOOL}; +use crate::dpdk::{Mempool, SocketId, MEMPOOL}; +use crate::ffi::dpdk; use crate::metrics; use std::ptr; use std::sync::Once; @@ -38,13 +39,25 @@ static TEST_INIT: Once = Once::new(); pub fn cargo_test_init() { TEST_INIT.call_once(|| { dpdk::eal_init(vec![ - "capsule_test".to_owned(), - "--no-huge".to_owned(), - "--iova-mode=va".to_owned(), - "--master-lcore".to_owned(), - "127".to_owned(), - "--lcores".to_owned(), - "0,1,127@0".to_owned(), + "capsule_test", + "--master-lcore", + "127", + "--lcores", + // 2 logical worker cores, pins master core to physical core 0 + "0,1,127@0", + // allows tests to run without hugepages + "--no-huge", + // allows tests to run without root privilege + "--iova-mode=va", + "--vdev", + // a null device for RX and TX tests + "net_null0", + "--vdev", + // a ring-based device that can be used with assertions + "net_ring0", + "--vdev", + // a TAP device for supported device feature tests + "net_tap0", ]) .unwrap(); let _ = metrics::init(); From 6f03b493aeb25159079ba577133a749a29357448 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Mon, 15 Mar 2021 09:35:53 -0400 Subject: [PATCH 04/18] run, run, run --- core/core | 0 core/src/ffi/dpdk.rs | 20 +- core/src/lib.rs | 2 +- core/src/rt2/config.rs | 448 ++++++++++++++++++++++++++++++++ core/src/rt2/lcore.rs | 38 ++- core/src/rt2/mempool.rs | 13 +- core/src/rt2/mod.rs | 108 +++++++- core/src/rt2/port.rs | 40 ++- examples/skeleton/README.md | 4 +- examples/skeleton/main.rs | 9 +- examples/skeleton/skeleton.toml | 6 +- 11 files changed, 656 insertions(+), 32 deletions(-) delete mode 100644 core/core create mode 100644 core/src/rt2/config.rs diff --git a/core/core b/core/core deleted file mode 100644 index e69de29..0000000 diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index a7fd857..889d509 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -131,7 +131,7 @@ pub(crate) fn mempool_free(ptr: &mut MempoolPtr) { } /// An opaque identifier for a logical execution unit of the processor. -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Copy, Clone)] pub(crate) struct LcoreId(raw::c_uint); impl LcoreId { @@ -144,6 +144,24 @@ impl LcoreId { pub(crate) fn current() -> LcoreId { unsafe { LcoreId(cffi::_rte_lcore_id()) } } + + /// Returns the ID of the main lcore. + #[inline] + pub(crate) fn main() -> LcoreId { + unsafe { LcoreId(cffi::rte_get_master_lcore()) } + } + + /// Returns the ID of the physical CPU socket of the lcore. + #[allow(clippy::trivially_copy_pass_by_ref)] + #[inline] + pub(crate) fn socket(&self) -> SocketId { + unsafe { (cffi::rte_lcore_to_socket_id(self.0) as raw::c_int).into() } + } + + /// Returns the raw value. + pub(crate) fn raw(&self) -> usize { + self.0 as usize + } } impl fmt::Debug for LcoreId { diff --git a/core/src/lib.rs b/core/src/lib.rs index f943915..b42ac8a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -134,7 +134,7 @@ pub mod packets; #[cfg(feature = "pcap-dump")] #[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] mod pcap; -mod rt2; +pub mod rt2; mod runtime; #[cfg(any(test, feature = "testils"))] #[cfg_attr(docsrs, doc(cfg(feature = "testils")))] diff --git a/core/src/rt2/config.rs b/core/src/rt2/config.rs new file mode 100644 index 0000000..bbb70fe --- /dev/null +++ b/core/src/rt2/config.rs @@ -0,0 +1,448 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +//! Toml-based configuration for use with Capsule applications. +//! +//! # Example +//! +//! A configuration from our [`pktdump`] example: +//! +//! ``` +//! app_name = "pktdump" +//! main_core = 0 +//! worker_cores = [0] +//! +//! [mempool] +//! capacity = 65535 +//! cache_size = 256 +//! +//! [[ports]] +//! name = "eth1" +//! device = "net_pcap0" +//! args = "rx_pcap=tcp4.pcap,tx_iface=lo" +//! rx_core = [0] +//! tx_core = [0] +//! +//! [[ports]] +//! name = "eth2" +//! device = "net_pcap1" +//! args = "rx_pcap=tcp6.pcap,tx_iface=lo" +//! rx_core = [0] +//! tx_core = [0] +//! ``` +//! +//! [`pktdump`]: https://github.com/capsule-rs/capsule/tree/master/examples/pktdump + +use anyhow::Result; +use capsule_ffi as cffi; +use clap::{clap_app, crate_version}; +use regex::Regex; +use serde::Deserialize; +use std::fmt; +use std::fs; + +/// Runtime configuration settings. +#[derive(Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RuntimeConfig { + /// Application name. This must be unique if you want to run multiple + /// DPDK applications on the same system. + pub app_name: String, + + /// Indicating whether the process is a secondary process. Secondary + /// process cannot initialize shared memory, but can attach to pre- + /// initialized shared memory by the primary process and create objects + /// in it. Defaults to `false`. + #[serde(default)] + pub secondary: bool, + + /// Application group name. Use this to group primary and secondary + /// processes together in a multi-process setup; and allow them to share + /// the same memory regions. The default value is the `app_name`. Each + /// process works independently. + #[serde(default)] + pub app_group: Option, + + /// The identifier of the main core. This is the core the main thread + /// will run on. + pub main_core: usize, + + /// Worker cores used for packet processing and general async task + /// execution. + pub worker_cores: Vec, + + /// Per mempool settings. On a system with multiple sockets, aka NUMA + /// nodes, one mempool will be allocated for each socket the apllication + /// uses. + #[serde(default)] + pub mempool: MempoolConfig, + + /// The ports to use for the application. Must have at least one. + pub ports: Vec, + + /// Additional DPDK [`parameters`] to pass on for EAL initialization. When + /// set, the values are passed through as is without validation. + /// + /// [`parameters`]: https://doc.dpdk.org/guides/linux_gsg/linux_eal_parameters.html + #[serde(default)] + pub dpdk_args: Option, +} + +impl RuntimeConfig { + fn other_cores(&self) -> Vec { + let mut cores = vec![]; + cores.extend(&self.worker_cores); + + self.ports.iter().for_each(|port| { + if !port.rx_cores.is_empty() { + cores.extend(&port.rx_cores); + } + if !port.tx_cores.is_empty() { + cores.extend(&port.tx_cores); + } + }); + + cores.sort(); + cores.dedup(); + cores + } + + /// Extracts the EAL arguments from runtime settings. + pub(crate) fn to_eal_args(&self) -> Vec { + let mut eal_args = vec![]; + + // adds the app name. + eal_args.push(self.app_name.clone()); + + // adds the proc type. + let proc_type = if self.secondary { + "secondary" + } else { + "primary" + }; + eal_args.push("--proc-type".into()); + eal_args.push(proc_type.into()); + + // adds the mem file prefix. + let prefix = self.app_group.as_ref().unwrap_or(&self.app_name); + eal_args.push("--file-prefix".into()); + eal_args.push(prefix.clone()); + + // adds all the ports. + let pcie = Regex::new(r"^\d{4}:\d{2}:\d{2}\.\d$").unwrap(); + self.ports.iter().for_each(|port| { + if pcie.is_match(port.device.as_str()) { + eal_args.push("--pci-whitelist".into()); + eal_args.push(port.device.clone()); + } else { + let vdev = if let Some(args) = &port.args { + format!("{},{}", port.device, args) + } else { + port.device.clone() + }; + eal_args.push("--vdev".into()); + eal_args.push(vdev); + } + }); + + let mut main = self.main_core; + let others = self.other_cores(); + + // if the main lcore is also used for other tasks, we will assign + // another lcore to be the main, and set the affinity to the same + // physical core/cpu. this is necessary because we need to be able + // to run an executor for other tasks without blocking the main + // application thread. + if others.contains(&main) { + main = cffi::RTE_MAX_LCORE as usize - 1; + } + + // adds the main core. + eal_args.push("--master-lcore".into()); + eal_args.push(main.to_string()); + + // adds all the lcores. + let mut cores = others + .iter() + .map(ToString::to_string) + .collect::>() + .join(","); + cores.push_str(&format!(",{}@{}", main, self.main_core)); + eal_args.push("--lcores".to_owned()); + eal_args.push(cores); + + // adds additional DPDK args. + if let Some(args) = &self.dpdk_args { + eal_args.extend(args.split_ascii_whitespace().map(ToString::to_string)); + } + + eal_args + } +} + +impl fmt::Debug for RuntimeConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("RuntimeConfig"); + d.field("app_name", &self.app_name) + .field("secondary", &self.secondary) + .field( + "app_group", + self.app_group.as_ref().unwrap_or(&self.app_name), + ) + .field("main_core", &self.main_core) + .field("worker_cores", &self.worker_cores) + .field("mempool", &self.mempool) + .field("ports", &self.ports); + if let Some(dpdk_args) = &self.dpdk_args { + d.field("dpdk_args", dpdk_args); + } + d.finish() + } +} + +/// Mempool configuration settings. +#[derive(Clone, Deserialize)] +pub struct MempoolConfig { + /// The maximum number of Mbufs the mempool can allocate. The optimum + /// size (in terms of memory usage) is when n is a power of two minus + /// one. Defaults to `65535` or `2 ^ 16 - 1`. + #[serde(default = "default_capacity")] + pub capacity: usize, + + /// The size of the per core object cache. If cache_size is non-zero, + /// the library will try to limit the accesses to the common lockless + /// pool. The cache can be disabled if the argument is set to 0. Defaults + /// to `0`. + #[serde(default = "default_cache_size")] + pub cache_size: usize, +} + +fn default_capacity() -> usize { + 65535 +} + +fn default_cache_size() -> usize { + 0 +} + +impl Default for MempoolConfig { + fn default() -> Self { + MempoolConfig { + capacity: default_capacity(), + cache_size: default_cache_size(), + } + } +} + +impl fmt::Debug for MempoolConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MempoolConfig") + .field("capacity", &self.capacity) + .field("cache_size", &self.cache_size) + .finish() + } +} + +/// Port configuration settings. +#[derive(Clone, Deserialize)] +pub struct PortConfig { + /// The application assigned logical name of the port. + /// + /// For applications with more than one port, this name can be used to + /// identifer the port. + pub name: String, + + /// The device name of the port. It can be the following formats, + /// + /// * PCIe address, for example `0000:02:00.0` + /// * DPDK virtual device, for example `net_[pcap0|null0|tap0]` + pub device: String, + + /// Additional arguments to configure a virtual device. + #[serde(default)] + pub args: Option, + + /// The lcores to receive packets on. When no lcore specified, the port + /// will be TX only. + #[serde(default)] + pub rx_cores: Vec, + + /// The lcores to transmit packets on. When no lcore specified, the port + /// will be RX only. + #[serde(default)] + pub tx_cores: Vec, + + /// The receive queue size. Defaults to `128`. + #[serde(default = "default_port_rxqs")] + pub rxqs: usize, + + /// The transmit queue size. Defaults to `128`. + #[serde(default = "default_port_txqs")] + pub txqs: usize, + + /// Whether promiscuous mode is enabled for this port. Defaults to `true`. + #[serde(default = "default_promiscuous_mode")] + pub promiscuous: bool, + + /// Whether multicast packet reception is enabled for this port. Defaults + /// to `true`. + #[serde(default = "default_multicast_mode")] + pub multicast: bool, +} + +fn default_port_rxqs() -> usize { + 128 +} + +fn default_port_txqs() -> usize { + 128 +} + +fn default_promiscuous_mode() -> bool { + true +} + +fn default_multicast_mode() -> bool { + true +} + +impl fmt::Debug for PortConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("PortConfig"); + d.field("name", &self.name); + d.field("device", &self.device); + if let Some(args) = &self.args { + d.field("args", args); + } + if !self.rx_cores.is_empty() { + d.field("rx_cores", &self.rx_cores); + } + if !self.tx_cores.is_empty() { + d.field("tx_cores", &self.tx_cores); + } + d.field("rxqs", &self.rxqs) + .field("txqs", &self.txqs) + .field("promiscuous", &self.promiscuous) + .field("multicast", &self.multicast) + .finish() + } +} + +/// Loads the app config from a TOML file. +/// +/// # Example +/// +/// ``` +/// home$ ./myapp -f config.toml +/// ``` +pub fn load_config() -> Result { + let matches = clap_app!(capsule => + (version: crate_version!()) + (@arg file: -f --file +required +takes_value "configuration file") + ) + .get_matches(); + + let path = matches.value_of("file").unwrap(); + let content = fs::read_to_string(path)?; + toml::from_str(&content).map_err(|err| err.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn config_defaults() { + const CONFIG: &str = r#" + app_name = "myapp" + main_core = 0 + worker_cores = [1, 2] + [[ports]] + name = "eth0" + device = "0000:00:01.0" + "#; + + let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); + + assert_eq!(false, config.secondary); + assert_eq!(None, config.app_group); + assert_eq!(None, config.dpdk_args); + assert_eq!(default_capacity(), config.mempool.capacity); + assert_eq!(default_cache_size(), config.mempool.cache_size); + assert_eq!(None, config.ports[0].args); + assert!(config.ports[0].rx_cores.is_empty()); + assert!(config.ports[0].tx_cores.is_empty()); + assert_eq!(default_port_rxqs(), config.ports[0].rxqs); + assert_eq!(default_port_txqs(), config.ports[0].txqs); + assert_eq!(default_promiscuous_mode(), config.ports[0].promiscuous); + assert_eq!(default_multicast_mode(), config.ports[0].multicast); + } + + #[test] + fn config_to_eal_args() { + const CONFIG: &str = r#" + app_name = "myapp" + secondary = false + app_group = "mygroup" + main_core = 0 + worker_cores = [1, 2] + dpdk_args = "-v --log-level eal:8" + [mempool] + capacity = 255 + cache_size = 16 + [[ports]] + name = "eth0" + device = "0000:00:01.0" + rx_cores = [3] + tx_cores = [0] + rxqs = 32 + txqs = 32 + [[ports]] + name = "eth1" + device = "net_pcap0" + args = "rx=lo,tx=lo" + tx_cores = [4] + rxqs = 32 + txqs = 32 + "#; + + let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); + + assert_eq!( + &[ + "myapp", + "--proc-type", + "primary", + "--file-prefix", + "mygroup", + "--pci-whitelist", + "0000:00:01.0", + "--vdev", + "net_pcap0,rx=lo,tx=lo", + "--master-lcore", + "127", + "--lcores", + "0,1,2,3,4,127@0", + "-v", + "--log-level", + "eal:8" + ], + config.to_eal_args().as_slice(), + ) + } +} diff --git a/core/src/rt2/lcore.rs b/core/src/rt2/lcore.rs index 7aa4916..fefa642 100644 --- a/core/src/rt2/lcore.rs +++ b/core/src/rt2/lcore.rs @@ -17,11 +17,12 @@ */ use crate::ffi::dpdk::{self, LcoreId}; -use crate::info; -use anyhow::Result; +use crate::{debug, info}; +use anyhow::{anyhow, Result}; use async_channel::{self, Receiver, Recv, Sender}; use async_executor::Executor; use futures_lite::future; +use std::collections::HashMap; use std::future::Future; use std::sync::Arc; @@ -69,14 +70,15 @@ impl Lcore { /// /// Returns `DpdkError` if the executor fails to run on the given lcore. fn new(id: LcoreId) -> Result { + debug!(?id, "starting lcore."); let executor = Arc::new(Executor::new()); let (trigger, shutdown) = shutdown_trigger(); let executor2 = Arc::clone(&executor); dpdk::eal_remote_launch(id, move || { - info!(lcore = ?id, "lcore started."); + info!(?id, "lcore started."); let _ = future::block_on(executor2.run(shutdown.wait())); - info!(lcore = ?id, "lcore stopped."); + info!(?id, "lcore stopped."); })?; Ok(Lcore { @@ -104,14 +106,36 @@ impl Lcore { impl Drop for Lcore { fn drop(&mut self) { if let Some(trigger) = self.trigger.take() { - info!(lcore = ?self.id, "stopping lcore."); + debug!(id = ?self.id, "stopping lcore."); trigger.fire(); } } } +/// Map to lookup the lcore by the assigned id. +pub(crate) struct LcoreMap(HashMap); + +impl LcoreMap { + /// Returns the lcore with the assigned id. + fn get(&self, id: usize) -> Result<&Lcore> { + self.0 + .get(&id) + .ok_or_else(|| anyhow!("lcore with id '{}' not found.", id)) + } +} + +impl From> for LcoreMap { + fn from(lcores: Vec) -> Self { + let map = lcores + .into_iter() + .map(|lcore| (lcore.id.raw(), lcore)) + .collect::>(); + LcoreMap(map) + } +} + /// Returns the enabled worker lcores. -pub(crate) fn lcore_pool() -> Vec { +pub(crate) fn lcore_pool() -> LcoreMap { let mut lcores = Vec::new(); let mut current = None; @@ -120,7 +144,7 @@ pub(crate) fn lcore_pool() -> Vec { current = Some(id); } - lcores + lcores.into() } #[cfg(test)] diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs index 2ae27df..f119388 100644 --- a/core/src/rt2/mempool.rs +++ b/core/src/rt2/mempool.rs @@ -48,11 +48,12 @@ impl Mempool { name: S, capacity: usize, cache_size: usize, + socket_id: SocketId, ) -> Result { let name: String = name.into(); - let ptr = dpdk::pktmbuf_pool_create(&name, capacity, cache_size, SocketId::current())?; + let ptr = dpdk::pktmbuf_pool_create(&name, capacity, cache_size, socket_id)?; - info!(mempool = ?name, "pool created."); + info!(?name, "pool created."); Ok(Self { ptr }) } @@ -100,8 +101,10 @@ impl fmt::Debug for Mempool { impl Drop for Mempool { fn drop(&mut self) { + let name = self.name().to_string(); + debug!(?name, "freeing mempool."); dpdk::mempool_free(&mut self.ptr); - debug!(mempool = ?self.name(), "pool freed."); + info!(?name, "mempool freed."); } } @@ -111,7 +114,7 @@ mod tests { #[capsule::test] fn create_mempool() -> Result<()> { - let pool = Mempool::new("pool1", 15, 1)?; + let pool = Mempool::new("pool1", 15, 1, SocketId::ANY)?; assert_eq!("pool1", pool.name()); assert_eq!(15, pool.capacity()); @@ -123,7 +126,7 @@ mod tests { #[capsule::test] fn drop_mempool() -> Result<()> { let name = "pool2"; - let pool = Mempool::new(name, 7, 0)?; + let pool = Mempool::new(name, 7, 0, SocketId::ANY)?; let res = dpdk::mempool_lookup(name); assert!(res.is_ok()); diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 4fdf9bc..1826d9e 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -16,8 +16,114 @@ * SPDX-License-Identifier: Apache-2.0 */ +mod config; mod lcore; mod mempool; mod port; -pub(crate) use self::mempool::Mempool; +pub use self::config::*; +pub(crate) use self::lcore::*; +pub(crate) use self::mempool::*; +pub(crate) use self::port::*; + +use crate::ffi::dpdk::{self, LcoreId}; +use crate::{debug, info}; +use anyhow::Result; +use std::fmt; +use std::mem::ManuallyDrop; + +/// The Capsule runtime. +/// +/// The runtime initializes the underlying DPDK environment, and it also manages +/// the task scheduler that executes the packet processing tasks. +pub struct Runtime { + mempool: ManuallyDrop, + lcores: ManuallyDrop, + ports: ManuallyDrop, +} + +impl Runtime { + /// Initializes a new runtime from config settings. + pub fn from_config(config: RuntimeConfig) -> Result { + info!("starting runtime."); + + debug!("initializing EAL ..."); + dpdk::eal_init(config.to_eal_args())?; + + debug!("initializing mempool ..."); + let socket = LcoreId::main().socket(); + let mut mempool = Mempool::new( + "mempool", + config.mempool.capacity, + config.mempool.cache_size, + socket, + )?; + debug!(?mempool); + + debug!("initializing lcore schedulers ..."); + let lcores = self::lcore_pool(); + + info!("initializing ports ..."); + let mut ports = Vec::new(); + for port in config.ports.iter() { + let port = port::Builder::for_device(&port.name, &port.device)? + .set_rxqs_txqs(port.rxqs, port.txqs)? + .set_promiscuous(port.promiscuous)? + .set_multicast(port.multicast)? + .set_rx_lcores(port.rx_cores.clone())? + .set_tx_lcores(port.tx_cores.clone())? + .build(&mut mempool)?; + + debug!(?port); + ports.push(port); + } + + info!("runtime ready."); + + Ok(Runtime { + mempool: ManuallyDrop::new(mempool), + lcores: ManuallyDrop::new(lcores), + ports: ManuallyDrop::new(ports.into()), + }) + } + + /// Starts the runtime execution. + pub fn execute(self) -> Result { + Ok(RuntimeGuard { runtime: self }) + } +} + +impl fmt::Debug for Runtime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Runtime") + .field("mempool", &self.mempool) + .finish() + } +} + +/// The RAII guard to stop and cleanup the runtime resources on drop. +pub struct RuntimeGuard { + runtime: Runtime, +} + +impl Drop for RuntimeGuard { + fn drop(&mut self) { + info!("shutting down runtime."); + + unsafe { + ManuallyDrop::drop(&mut self.runtime.ports); + ManuallyDrop::drop(&mut self.runtime.lcores); + ManuallyDrop::drop(&mut self.runtime.mempool); + } + + debug!("freeing EAL ..."); + let _ = dpdk::eal_cleanup(); + info!("runtime shutdown."); + } +} + +impl fmt::Debug for RuntimeGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RuntimeGuard") + } +} diff --git a/core/src/rt2/port.rs b/core/src/rt2/port.rs index 15b1995..a6ed421 100644 --- a/core/src/rt2/port.rs +++ b/core/src/rt2/port.rs @@ -20,8 +20,9 @@ use super::Mempool; use crate::ffi::dpdk::{self, PortId}; use crate::net::MacAddr; use crate::{debug, ensure, info, warn}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use capsule_ffi as cffi; +use std::collections::HashMap; use std::fmt; use thiserror::Error; @@ -79,6 +80,28 @@ impl fmt::Debug for Port { } } +/// Map to lookup the port by the port name. +pub(crate) struct PortMap(HashMap); + +impl PortMap { + /// Returns the lcore with the assigned id. + fn get(&self, name: &str) -> Result<&Port> { + self.0 + .get(name) + .ok_or_else(|| anyhow!("port with name '{}' not found.", name)) + } +} + +impl From> for PortMap { + fn from(ports: Vec) -> Self { + let ports = ports + .into_iter() + .map(|port| (port.name.clone(), port)) + .collect::>(); + PortMap(ports) + } +} + /// Port related errors. #[derive(Debug, Error)] pub(crate) enum PortError { @@ -123,7 +146,7 @@ impl Builder { let device: String = device.into(); let port_id = dpdk::eth_dev_get_port_by_name(&device)?; - debug!(port = ?name, id = ?port_id, ?device); + debug!(?name, id = ?port_id, ?device); let port_info = dpdk::eth_dev_info_get(port_id)?; @@ -150,7 +173,7 @@ impl Builder { /// the number of lcores assigned. pub(crate) fn set_rx_lcores(&mut self, lcores: Vec) -> Result<&mut Self> { ensure!( - !lcores.is_empty() && self.port_info.max_rx_queues >= lcores.len() as u16, + self.port_info.max_rx_queues >= lcores.len() as u16, PortError::InsufficientRxQueues(self.port_info.max_rx_queues) ); @@ -183,7 +206,7 @@ impl Builder { /// the number of lcores assigned. pub(crate) fn set_tx_lcores(&mut self, lcores: Vec) -> Result<&mut Self> { ensure!( - !lcores.is_empty() && self.port_info.max_tx_queues >= lcores.len() as u16, + self.port_info.max_tx_queues >= lcores.len() as u16, PortError::InsufficientTxQueues(self.port_info.max_tx_queues) ); @@ -199,7 +222,7 @@ impl Builder { /// # Errors /// /// Returns `DpdkError` if failed to set the queue capacity. - pub(crate) fn set_rxq_txq_capacity(&mut self, rxqs: usize, txqs: usize) -> Result<&mut Self> { + pub(crate) fn set_rxqs_txqs(&mut self, rxqs: usize, txqs: usize) -> Result<&mut Self> { let (rxqs2, txqs2) = dpdk::eth_dev_adjust_nb_rx_tx_desc(self.port_id, rxqs, txqs)?; info!( @@ -314,6 +337,7 @@ impl Builder { #[cfg(test)] mod tests { use super::*; + use crate::ffi::dpdk::SocketId; #[capsule::test] fn port_not_found() { @@ -355,11 +379,11 @@ mod tests { } #[capsule::test] - fn set_rxq_txq_capacity() -> Result<()> { + fn set_rxqs_txqs() -> Result<()> { let mut builder = Builder::for_device("test0", "net_ring0")?; // unfortunately can't test boundary adjustment - assert!(builder.set_rxq_txq_capacity(32, 32).is_ok()); + assert!(builder.set_rxqs_txqs(32, 32).is_ok()); assert_eq!(32, builder.rxqs); assert_eq!(32, builder.txqs); @@ -390,7 +414,7 @@ mod tests { fn build_port() -> Result<()> { let rx_lcores = (0..2).collect::>(); let tx_lcores = (3..6).collect::>(); - let mut pool = Mempool::new("mp_build_port", 15, 0)?; + let mut pool = Mempool::new("mp_build_port", 15, 0, SocketId::ANY)?; let port = Builder::for_device("test0", "net_ring0")? .set_rx_lcores(rx_lcores.clone())? .set_tx_lcores(tx_lcores.clone())? diff --git a/examples/skeleton/README.md b/examples/skeleton/README.md index 39e8518..b09dd3e 100644 --- a/examples/skeleton/README.md +++ b/examples/skeleton/README.md @@ -12,6 +12,6 @@ The example is located in the `examples/skeleton` sub-directory. To run the appl ## Explanation -`skeleton.toml` demonstrates the configuration file structure. The application must specify an `app_name`, `master_core`, `mempool`, and at least one `port`. For the skeleton example, the main application thread runs on CPU core `0`. It has a mempool with capacity of `65535` mbufs preallocated. It has one port configured to also run on CPU core `0`, using an in-memory ring-based virtual device. +`skeleton.toml` demonstrates the configuration file structure. The application must specify an `app_name`, `main_core`, `mempool`, and at least one `port`. For the skeleton example, the main application thread runs on CPU core `0`. It has a mempool with capacity of `65535` mbufs preallocated. It has one port configured to also run on CPU core `0`, using an in-memory ring-based virtual device. -The `main` function first sets up the [`tracing`](https://github.com/tokio-rs/tracing) framework to record log output to the console at `TRACE` level. Then it builds a `Runtime` with the settings from `skeleton.toml` and executes that runtime. Because there are no pipelines or tasks scheduled with the runtime, the application doesn't do anything. It simply waits for the timeout duration of 5 seconds and terminates. +The `main` function first sets up the [`tracing`](https://github.com/tokio-rs/tracing) framework to record log output to the console at `TRACE` level. Then it builds a `Runtime` with the settings from `skeleton.toml` and executes that runtime. Because there are no pipelines or tasks scheduled with the runtime, the application doesn't do anything. It terminates immediately. diff --git a/examples/skeleton/main.rs b/examples/skeleton/main.rs index a8ce099..50139ee 100644 --- a/examples/skeleton/main.rs +++ b/examples/skeleton/main.rs @@ -17,8 +17,7 @@ */ use anyhow::Result; -use capsule::config::load_config; -use capsule::Runtime; +use capsule::rt2::{self, Runtime}; use tracing::{debug, Level}; use tracing_subscriber::fmt; @@ -28,8 +27,10 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; + let config = rt2::load_config()?; debug!(?config); - Runtime::build(config)?.execute() + let _ = Runtime::from_config(config)?.execute()?; + + Ok(()) } diff --git a/examples/skeleton/skeleton.toml b/examples/skeleton/skeleton.toml index fbc9ca0..aef3ea5 100644 --- a/examples/skeleton/skeleton.toml +++ b/examples/skeleton/skeleton.toml @@ -1,9 +1,9 @@ app_name = "skeleton" -master_core = 0 +main_core = 0 +worker_cores = [0] dpdk_args = "-v --log-level eal:8" -duration = 5 [[ports]] name = "eth1" device = "net_ring0" - cores = [0] + rx_cores = [0] From afe24b2ed6152feb0d1b0ae5d0cf776168505796 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Tue, 16 Mar 2021 23:11:21 -0400 Subject: [PATCH 05/18] new mbuf allocation --- core/src/dpdk/mbuf.rs | 78 ++++++++++++++++++++++++----------------- core/src/ffi/dpdk.rs | 75 ++++++++++++++++++++++++++++++++++++++- core/src/ffi/mod.rs | 14 ++++++++ core/src/rt2/mempool.rs | 32 ++++++++++++++++- core/src/testils/mod.rs | 20 ++++++----- 5 files changed, 175 insertions(+), 44 deletions(-) diff --git a/core/src/dpdk/mbuf.rs b/core/src/dpdk/mbuf.rs index 0bd3567..74ed1b7 100644 --- a/core/src/dpdk/mbuf.rs +++ b/core/src/dpdk/mbuf.rs @@ -16,15 +16,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::MEMPOOL; -use crate::dpdk::{DpdkError, MempoolError}; -use crate::ffi::{self, ToResult}; +use crate::ffi::dpdk::{self, MbufPtr}; use crate::packets::{Internal, Packet}; +use crate::rt2::Mempool; use crate::{ensure, trace}; use anyhow::Result; +use capsule_ffi as cffi; use std::fmt; use std::mem; -use std::os::raw; use std::ptr::{self, NonNull}; use std::slice; use thiserror::Error; @@ -113,21 +112,21 @@ enum MbufInner { /// Original version of the message buffer that should be freed when it goes /// out of scope, unless the ownership of the pointer is given back to /// DPDK on transmit. - Original(NonNull), + Original(NonNull), /// A clone version of the message buffer that should not be freed when /// it goes out of scope. - Clone(NonNull), + Clone(NonNull), } impl MbufInner { - fn ptr(&self) -> &NonNull { + fn ptr(&self) -> &NonNull { match self { MbufInner::Original(raw) => raw, MbufInner::Clone(raw) => raw, } } - fn ptr_mut(&mut self) -> &mut NonNull { + fn ptr_mut(&mut self) -> &mut NonNull { match self { MbufInner::Original(ref mut raw) => raw, MbufInner::Clone(ref mut raw) => raw, @@ -144,15 +143,15 @@ impl Mbuf { /// /// # Errors /// - /// Returns `MempoolError::Exhausted` if the allocation of mbuf fails. + /// Returns `MempoolPtrUnsetError` if invoked from a non lcore. + /// Returns `DpdkError` if the allocation of mbuf fails. #[inline] pub fn new() -> Result { - let mempool = MEMPOOL.with(|tls| tls.get()); - let raw = - unsafe { ffi::_rte_pktmbuf_alloc(mempool).into_result(|_| MempoolError::Exhausted)? }; + let mut mp = Mempool::thread_local_ptr()?; + let ptr = dpdk::pktmbuf_alloc(&mut mp)?; Ok(Mbuf { - inner: MbufInner::Original(raw), + inner: MbufInner::Original(ptr.into()), }) } @@ -160,7 +159,7 @@ impl Mbuf { /// /// # Errors /// - /// Returns `MempoolError::Exhausted` if the allocation of mbuf fails. + /// Returns `DpdkError` if the allocation of mbuf fails. /// Returns `BufferError::NotResized` if the byte array is larger than /// the maximum mbuf size. #[inline] @@ -173,7 +172,15 @@ impl Mbuf { /// Creates a new `Mbuf` from a raw pointer. #[inline] - pub(crate) unsafe fn from_ptr(ptr: *mut ffi::rte_mbuf) -> Self { + pub(crate) fn from_easyptr(ptr: MbufPtr) -> Self { + Mbuf { + inner: MbufInner::Original(ptr.into()), + } + } + + /// Creates a new `Mbuf` from a raw pointer. + #[inline] + pub(crate) unsafe fn from_ptr(ptr: *mut cffi::rte_mbuf) -> Self { Mbuf { inner: MbufInner::Original(NonNull::new_unchecked(ptr)), } @@ -181,13 +188,13 @@ impl Mbuf { /// Returns the raw struct needed for FFI calls. #[inline] - fn raw(&self) -> &ffi::rte_mbuf { + fn raw(&self) -> &cffi::rte_mbuf { unsafe { self.inner.ptr().as_ref() } } /// Returns the raw struct needed for FFI calls. #[inline] - fn raw_mut(&mut self) -> &mut ffi::rte_mbuf { + fn raw_mut(&mut self) -> &mut cffi::rte_mbuf { unsafe { self.inner.ptr_mut().as_mut() } } @@ -417,7 +424,18 @@ impl Mbuf { /// The `Mbuf` is consumed. It is the caller's the responsibility to /// free the raw pointer after use. Otherwise the buffer is leaked. #[inline] - pub(crate) fn into_ptr(self) -> *mut ffi::rte_mbuf { + pub(crate) fn into_easyptr(self) -> MbufPtr { + let ptr = self.inner.ptr().clone(); + mem::forget(self); + ptr.into() + } + + /// Acquires the underlying raw struct pointer. + /// + /// The `Mbuf` is consumed. It is the caller's the responsibility to + /// free the raw pointer after use. Otherwise the buffer is leaked. + #[inline] + pub(crate) fn into_ptr(self) -> *mut cffi::rte_mbuf { let ptr = self.inner.ptr().as_ptr(); mem::forget(self); ptr @@ -430,25 +448,21 @@ impl Mbuf { /// Returns `DpdkError` if the allocation of mbuf fails. pub fn alloc_bulk(len: usize) -> Result> { let mut ptrs = Vec::with_capacity(len); - let mempool = MEMPOOL.with(|tls| tls.get()); - - let mbufs = unsafe { - ffi::_rte_pktmbuf_alloc_bulk(mempool, ptrs.as_mut_ptr(), len as raw::c_uint) - .into_result(DpdkError::from_errno)?; + let mut mp = Mempool::thread_local_ptr()?; + dpdk::pktmbuf_alloc_bulk(&mut mp, &mut ptrs)?; - ptrs.set_len(len); - ptrs.into_iter() - .map(|ptr| Mbuf::from_ptr(ptr)) - .collect::>() - }; + let mbufs = ptrs.into_iter().map(Mbuf::from_easyptr).collect::>(); Ok(mbufs) } /// Frees the message buffers in bulk. pub(crate) fn free_bulk(mbufs: Vec) { - let ptrs = mbufs.into_iter().map(Mbuf::into_ptr).collect::>(); - super::mbuf_free_bulk(ptrs); + let mut ptrs = mbufs + .into_iter() + .map(Mbuf::into_easyptr) + .collect::>(); + dpdk::pktmbuf_free_bulk(&mut ptrs); } } @@ -469,9 +483,7 @@ impl Drop for Mbuf { match self.inner { MbufInner::Original(_) => { trace!("freeing mbuf@{:p}.", self.raw().buf_addr); - unsafe { - ffi::_rte_pktmbuf_free(self.raw_mut()); - } + dpdk::pktmbuf_free(self.inner.ptr().clone().into()); } MbufInner::Clone(_) => (), } diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 889d509..524a040 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -131,7 +131,7 @@ pub(crate) fn mempool_free(ptr: &mut MempoolPtr) { } /// An opaque identifier for a logical execution unit of the processor. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, Hash, PartialEq)] pub(crate) struct LcoreId(raw::c_uint); impl LcoreId { @@ -397,6 +397,79 @@ pub(crate) fn eth_tx_queue_setup( } } +/// A `rte_mbuf` pointer. +pub(crate) type MbufPtr = EasyPtr; + +/// Allocates a new mbuf from a mempool. +pub(crate) fn pktmbuf_alloc(mp: &mut MempoolPtr) -> Result { + let ptr = + unsafe { cffi::_rte_pktmbuf_alloc(mp.deref_mut()).into_result(|_| DpdkError::new())? }; + + Ok(EasyPtr(ptr)) +} + +/// Allocates a bulk of mbufs. +pub(crate) fn pktmbuf_alloc_bulk(mp: &mut MempoolPtr, mbufs: &mut Vec) -> Result<()> { + let len = mbufs.capacity(); + + unsafe { + cffi::_rte_pktmbuf_alloc_bulk( + mp.deref_mut(), + mbufs.as_mut_ptr() as *mut *mut cffi::rte_mbuf, + len as raw::c_uint, + ) + .into_result(DpdkError::from_errno)?; + + mbufs.set_len(len); + } + + Ok(()) +} + +/// Frees a packet mbuf back into its original mempool. +pub(crate) fn pktmbuf_free(mut m: MbufPtr) { + unsafe { + cffi::_rte_pktmbuf_free(m.deref_mut()); + } +} + +/// Frees a bulk of packet mbufs back into their original mempools. +pub(crate) fn pktmbuf_free_bulk(mbufs: &mut Vec) { + assert!(!mbufs.is_empty()); + + let mut to_free = Vec::with_capacity(mbufs.len()); + let mut pool = mbufs[0].pool; + + for mbuf in mbufs.drain(..) { + if pool == mbuf.pool { + to_free.push(mbuf); + } else { + unsafe { + let len = to_free.len(); + cffi::_rte_mempool_put_bulk( + pool, + to_free.as_ptr() as *const *mut raw::c_void, + len as u32, + ); + to_free.set_len(0); + } + + pool = mbuf.pool; + to_free.push(mbuf); + } + } + + unsafe { + let len = to_free.len(); + cffi::_rte_mempool_put_bulk( + pool, + to_free.as_ptr() as *const *mut raw::c_void, + len as u32, + ); + to_free.set_len(0); + } +} + /// An error generated in `libdpdk`. /// /// When an FFI call fails, the `errno` is translated into `DpdkError`. diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index e46389d..f643607 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -160,3 +160,17 @@ impl DerefMut for EasyPtr { unsafe { self.0.as_mut() } } } + +// delete later? +impl From> for EasyPtr { + fn from(ptr: NonNull) -> Self { + EasyPtr(ptr) + } +} + +// delete later? +impl From> for NonNull { + fn from(ptr: EasyPtr) -> Self { + ptr.0 + } +} diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs index f119388..c931433 100644 --- a/core/src/rt2/mempool.rs +++ b/core/src/rt2/mempool.rs @@ -16,11 +16,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::ffi::dpdk::{self, MempoolPtr, SocketId}; +use crate::ffi::dpdk::{self, LcoreId, MempoolPtr, SocketId}; use crate::ffi::AsStr; use crate::{debug, info}; use anyhow::Result; +use std::cell::Cell; use std::fmt; +use std::ptr::{self, NonNull}; +use thiserror::Error; /// A memory pool is an allocator of message buffers, or `Mbuf`. For best /// performance, each socket should have a dedicated `Mempool`. However, @@ -59,6 +62,7 @@ impl Mempool { } /// Returns the raw pointer. + #[inline] pub(crate) fn ptr_mut(&mut self) -> &mut MempoolPtr { &mut self.ptr } @@ -86,6 +90,19 @@ impl Mempool { pub(crate) fn socket(&self) -> SocketId { self.ptr.socket_id.into() } + + /// Returns the thread local mempool pointer. + /// + /// # Errors + /// + /// Returns `MempoolPtrUnsetError` if the thread local pointer is not + /// set. For example when invoked from a non-EAL thread. + pub(crate) fn thread_local_ptr() -> Result { + let ptr = MEMPOOL.with(|tls| tls.get()); + NonNull::new(ptr) + .ok_or_else(|| MempoolPtrUnsetError(LcoreId::current()).into()) + .map(|ptr| ptr.into()) + } } impl fmt::Debug for Mempool { @@ -108,6 +125,19 @@ impl Drop for Mempool { } } +/// The thread local mempool is not set. +#[derive(Debug, Error)] +#[error("thread local mempool pointer not set for {0:?}.")] +pub(crate) struct MempoolPtrUnsetError(LcoreId); + +thread_local! { + /// The `Mempool` assigned to the core when the core is initialized. + /// `Mbuf::new` uses this pool to allocate new buffers when executed on + /// the core. For best performance, the `Mempool` and the core should + /// share the same socket. + pub(crate) static MEMPOOL: Cell<*mut capsule_ffi::rte_mempool> = Cell::new(ptr::null_mut()); +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/src/testils/mod.rs b/core/src/testils/mod.rs index 468a7ca..2445575 100644 --- a/core/src/testils/mod.rs +++ b/core/src/testils/mod.rs @@ -27,11 +27,13 @@ mod rvg; pub use self::packet::*; pub use self::rvg::*; -use crate::dpdk::{Mempool, SocketId, MEMPOOL}; -use crate::ffi::dpdk; +use crate::ffi::dpdk::{self, SocketId}; use crate::metrics; +use crate::rt2::{Mempool, MEMPOOL}; +use std::ops::DerefMut; use std::ptr; use std::sync::Once; +use std::thread; static TEST_INIT: Once = Once::new(); @@ -64,12 +66,11 @@ pub fn cargo_test_init() { }); } -/// A handle that keeps the mempool in scope for the duration of the test. It -/// will unset the thread-bound mempool on drop. +/// A RAII guard that keeps the mempool in scope for the duration of the +/// test. It will unset the thread-bound mempool on drop. #[derive(Debug)] pub struct MempoolGuard { - #[allow(dead_code)] - inner: Mempool, + _inner: Mempool, } impl Drop for MempoolGuard { @@ -81,7 +82,8 @@ impl Drop for MempoolGuard { /// Creates a new mempool for test that automatically cleans up after the /// test completes. pub fn new_mempool(capacity: usize, cache_size: usize) -> MempoolGuard { - let mut mempool = Mempool::new(capacity, cache_size, SocketId::ANY).unwrap(); - MEMPOOL.with(|tls| tls.set(mempool.raw_mut())); - MempoolGuard { inner: mempool } + let name = format!("test-mp-{:?}", thread::current().id()); + let mut mempool = Mempool::new(name, capacity, cache_size, SocketId::ANY).unwrap(); + MEMPOOL.with(|tls| tls.set(mempool.ptr_mut().deref_mut())); + MempoolGuard { _inner: mempool } } From 6b3447d7abf7976ec14f2858968bbda19b452bed Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Sat, 20 Mar 2021 18:33:15 -0400 Subject: [PATCH 06/18] port rx --- core/src/ffi/dpdk.rs | 76 ++++++++++++++++++-- core/src/rt2/lcore.rs | 67 +++++++---------- core/src/rt2/mod.rs | 60 ++++++++++++++++ core/src/rt2/port.rs | 132 +++++++++++++++++++++++++++++++--- examples/pktdump/Cargo.toml | 1 + examples/pktdump/README.md | 2 +- examples/pktdump/main.rs | 55 +++++++------- examples/pktdump/pktdump.toml | 12 ++-- 8 files changed, 309 insertions(+), 96 deletions(-) diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 524a040..6fc1607 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -22,7 +22,7 @@ use crate::{debug, error}; use anyhow::Result; use capsule_ffi as cffi; use std::fmt; -use std::ops::DerefMut; +use std::ops::{Deref, DerefMut}; use std::os::raw; use std::panic::{self, AssertUnwindSafe}; use std::ptr; @@ -90,6 +90,15 @@ impl From for SocketId { /// A `rte_mempool` pointer. pub(crate) type MempoolPtr = EasyPtr; +impl Clone for MempoolPtr { + fn clone(&self) -> Self { + self.0.clone().into() + } +} + +// Allows the pointer to go across thread/lcore boundaries. +unsafe impl Send for MempoolPtr {} + /// Creates a mbuf pool. pub(crate) fn pktmbuf_pool_create>( name: S, @@ -125,9 +134,14 @@ pub(crate) fn mempool_lookup>(name: S) -> Result { Ok(EasyPtr(ptr)) } +/// Returns the number of elements which have been allocated from the mempool. +pub(crate) fn mempool_in_use_count(mp: &MempoolPtr) -> usize { + unsafe { cffi::rte_mempool_in_use_count(mp.deref()) as usize } +} + /// Frees a mempool. -pub(crate) fn mempool_free(ptr: &mut MempoolPtr) { - unsafe { cffi::rte_mempool_free(ptr.deref_mut()) }; +pub(crate) fn mempool_free(mp: &mut MempoolPtr) { + unsafe { cffi::rte_mempool_free(mp.deref_mut()) }; } /// An opaque identifier for a logical execution unit of the processor. @@ -353,10 +367,26 @@ pub(crate) fn eth_dev_configure( } } +/// An opaque identifier for a port's receive queue. +#[derive(Copy, Clone)] +pub(crate) struct PortRxQueueId(u16); + +impl fmt::Debug for PortRxQueueId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "rxq{}", self.0) + } +} + +impl From for PortRxQueueId { + fn from(id: usize) -> Self { + PortRxQueueId(id as u16) + } +} + /// Allocates and sets up a receive queue for a device. pub(crate) fn eth_rx_queue_setup( port_id: PortId, - rx_queue_id: usize, + rx_queue_id: PortRxQueueId, nb_rx_desc: usize, socket_id: SocketId, rx_conf: Option<&cffi::rte_eth_rxconf>, @@ -365,7 +395,7 @@ pub(crate) fn eth_rx_queue_setup( unsafe { cffi::rte_eth_rx_queue_setup( port_id.0, - rx_queue_id as u16, + rx_queue_id.0, nb_rx_desc as u16, socket_id.0 as raw::c_uint, rx_conf.map_or(ptr::null(), |conf| conf), @@ -376,10 +406,42 @@ pub(crate) fn eth_rx_queue_setup( } } +/// Retrieves a burst of input packets from a receive queue of a device. +pub(crate) fn eth_rx_burst(port_id: PortId, queue_id: PortRxQueueId, rx_pkts: &mut Vec) { + let nb_pkts = rx_pkts.capacity(); + + unsafe { + let len = cffi::_rte_eth_rx_burst( + port_id.0, + queue_id.0, + rx_pkts.as_mut_ptr() as *mut *mut cffi::rte_mbuf, + nb_pkts as u16, + ); + + rx_pkts.set_len(len as usize); + } +} + +/// An opaque identifier for a port's transmit queue. +#[derive(Copy, Clone)] +pub(crate) struct PortTxQueueId(u16); + +impl fmt::Debug for PortTxQueueId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "txq{}", self.0) + } +} + +impl From for PortTxQueueId { + fn from(id: usize) -> Self { + PortTxQueueId(id as u16) + } +} + /// Allocates and sets up a transmit queue for a device. pub(crate) fn eth_tx_queue_setup( port_id: PortId, - tx_queue_id: usize, + tx_queue_id: PortTxQueueId, nb_tx_desc: usize, socket_id: SocketId, tx_conf: Option<&cffi::rte_eth_txconf>, @@ -387,7 +449,7 @@ pub(crate) fn eth_tx_queue_setup( unsafe { cffi::rte_eth_tx_queue_setup( port_id.0, - tx_queue_id as u16, + tx_queue_id.0, nb_tx_desc as u16, socket_id.0 as raw::c_uint, tx_conf.map_or(ptr::null(), |conf| conf), diff --git a/core/src/rt2/lcore.rs b/core/src/rt2/lcore.rs index fefa642..8ddf46c 100644 --- a/core/src/rt2/lcore.rs +++ b/core/src/rt2/lcore.rs @@ -16,51 +16,22 @@ * SPDX-License-Identifier: Apache-2.0 */ +use super::ShutdownTrigger; use crate::ffi::dpdk::{self, LcoreId}; use crate::{debug, info}; -use anyhow::{anyhow, Result}; -use async_channel::{self, Receiver, Recv, Sender}; +use anyhow::Result; use async_executor::Executor; use futures_lite::future; use std::collections::HashMap; use std::future::Future; use std::sync::Arc; - -/// Trigger for the shutdown. -pub(crate) struct Trigger(Sender<()>); - -impl Trigger { - /// Triggers the shutdown. - pub(crate) fn fire(self) { - drop(self.0) - } -} - -/// Shutdown wait handle. -pub(crate) struct Shutdown(Receiver<()>); - -impl Shutdown { - /// A future that waits till the trigger is fired. - pub(crate) fn wait(&self) -> Recv<'_, ()> { - self.0.recv() - } -} - -/// Creates a shutdown and trigger pair. -/// -/// Leverages the behavior of an async channel. When the sender is dropped -/// from scope, it closes the channel and causes the receiver side future -/// in the executor queue to resolve. -pub(crate) fn shutdown_trigger() -> (Trigger, Shutdown) { - let (s, r) = async_channel::unbounded(); - (Trigger(s), Shutdown(r)) -} +use thiserror::Error; /// An async executor abstraction on top of a DPDK logical core. pub(crate) struct Lcore { id: LcoreId, executor: Arc>, - trigger: Option, + shutdown: Option, } impl Lcore { @@ -71,23 +42,29 @@ impl Lcore { /// Returns `DpdkError` if the executor fails to run on the given lcore. fn new(id: LcoreId) -> Result { debug!(?id, "starting lcore."); + let trigger = ShutdownTrigger::new(); let executor = Arc::new(Executor::new()); - let (trigger, shutdown) = shutdown_trigger(); + let handle = trigger.get_wait(); let executor2 = Arc::clone(&executor); dpdk::eal_remote_launch(id, move || { info!(?id, "lcore started."); - let _ = future::block_on(executor2.run(shutdown.wait())); + let _ = future::block_on(executor2.run(handle.wait())); info!(?id, "lcore stopped."); })?; Ok(Lcore { id, executor, - trigger: Some(trigger), + shutdown: Some(trigger), }) } + /// Returns the lcore id. + pub(crate) fn id(&self) -> LcoreId { + self.id + } + /// Spawns an async task and waits for it to complete. pub(crate) fn block_on( &self, @@ -105,22 +82,30 @@ impl Lcore { impl Drop for Lcore { fn drop(&mut self) { - if let Some(trigger) = self.trigger.take() { + if let Some(trigger) = self.shutdown.take() { debug!(id = ?self.id, "stopping lcore."); trigger.fire(); } } } +/// Lcore not found error. +#[derive(Debug, Error)] +#[error("lcore not found.")] +pub(crate) struct LcoreNotFound; + /// Map to lookup the lcore by the assigned id. pub(crate) struct LcoreMap(HashMap); impl LcoreMap { /// Returns the lcore with the assigned id. - fn get(&self, id: usize) -> Result<&Lcore> { - self.0 - .get(&id) - .ok_or_else(|| anyhow!("lcore with id '{}' not found.", id)) + pub(crate) fn get(&self, id: usize) -> Result<&Lcore> { + self.0.get(&id).ok_or_else(|| LcoreNotFound.into()) + } + + /// Returns a lcore iterator. + pub(crate) fn iter(&self) -> impl Iterator { + self.0.values() } } diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 1826d9e..d798326 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -25,12 +25,57 @@ pub use self::config::*; pub(crate) use self::lcore::*; pub(crate) use self::mempool::*; pub(crate) use self::port::*; +pub use crate::dpdk::Mbuf; use crate::ffi::dpdk::{self, LcoreId}; use crate::{debug, info}; use anyhow::Result; +use async_channel::{self, Receiver, Sender}; use std::fmt; use std::mem::ManuallyDrop; +use std::ops::DerefMut; + +/// Trigger for the shutdown. +pub(crate) struct ShutdownTrigger(Sender<()>, Receiver<()>); + +impl ShutdownTrigger { + /// Creates a new shutdown trigger. + /// + /// Leverages the behavior of an async channel. When the sender is dropped + /// from scope, it closes the channel and causes the receiver side future + /// in the executor queue to resolve. + pub(crate) fn new() -> Self { + let (s, r) = async_channel::unbounded(); + Self(s, r) + } + + /// Returns a wait handle. + pub(crate) fn get_wait(&self) -> ShutdownWait { + ShutdownWait(self.1.clone()) + } + + /// Returns whether the trigger is being waited on. + pub(crate) fn is_waited(&self) -> bool { + // a receiver count greater than 1 indicating that there are receiver + // clones in scope, hence the trigger is being waited on. + self.0.receiver_count() > 1 + } + + /// Triggers the shutdown. + pub(crate) fn fire(self) { + drop(self.0) + } +} + +/// Shutdown wait handle. +pub(crate) struct ShutdownWait(Receiver<()>); + +impl ShutdownWait { + /// A future that waits till the shutdown trigger is fired. + pub(crate) async fn wait(&self) { + self.0.recv().await.unwrap_or(()) + } +} /// The Capsule runtime. /// @@ -63,6 +108,11 @@ impl Runtime { debug!("initializing lcore schedulers ..."); let lcores = self::lcore_pool(); + for lcore in lcores.iter() { + let mut ptr = mempool.ptr_mut().clone(); + lcore.block_on(async move { MEMPOOL.with(|tls| tls.set(ptr.deref_mut())) }); + } + info!("initializing ports ..."); let mut ports = Vec::new(); for port in config.ports.iter() { @@ -87,6 +137,16 @@ impl Runtime { }) } + /// Sets the packet processing pipeline for port. + pub fn set_port_pipeline(self, port: &str, f: F) -> Result + where + F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, + { + let port = self.ports.get(port)?; + port.spawn_rx_loops(f, &self.lcores)?; + Ok(self) + } + /// Starts the runtime execution. pub fn execute(self) -> Result { Ok(RuntimeGuard { runtime: self }) diff --git a/core/src/rt2/port.rs b/core/src/rt2/port.rs index a6ed421..ed81f46 100644 --- a/core/src/rt2/port.rs +++ b/core/src/rt2/port.rs @@ -16,14 +16,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::Mempool; -use crate::ffi::dpdk::{self, PortId}; +use super::{LcoreMap, Mbuf, Mempool, ShutdownTrigger}; +use crate::ffi::dpdk::{self, LcoreId, PortId, PortRxQueueId}; use crate::net::MacAddr; use crate::{debug, ensure, info, warn}; -use anyhow::{anyhow, Result}; +use anyhow::Result; use capsule_ffi as cffi; +use futures_lite::future; use std::collections::HashMap; use std::fmt; +use std::sync::Arc; use thiserror::Error; /// A PMD device port. @@ -32,6 +34,7 @@ pub(crate) struct Port { port_id: PortId, rx_lcores: Vec, tx_lcores: Vec, + shutdown: Option, } impl Port { @@ -64,6 +67,44 @@ impl Port { pub(crate) fn multicast(&self) -> bool { dpdk::eth_allmulticast_get(self.port_id) } + + /// Sets the packet processing pipeline. + pub(crate) fn spawn_rx_loops(&self, f: F, lcores: &LcoreMap) -> Result<()> + where + F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, + { + // port is built with the builder, this would not panic. + let shutdown = self.shutdown.as_ref().unwrap(); + + // can't run pipeline without assigned rx cores. + ensure!(self.rx_lcores.len() > 0, PortError::RxNotEnabled); + // pipeline already set if the trigger is waited on. + ensure!(!shutdown.is_waited(), PortError::PipelineSet); + + let f = Arc::new(f); + + for (index, lcore_id) in self.rx_lcores.iter().enumerate() { + let lcore = lcores.get(*lcore_id)?; + let port_id = self.port_id.clone(); + let port_name = self.name.clone(); + let handle = shutdown.get_wait(); + let f = f.clone(); + + debug!(name = ?self.name, lcore = ?lcore.id(), "spawning rx loop."); + + // the rx loop is endless, so we use a shutdown trigger to signal + // when the loop should stop executing. + lcore.spawn(future::or( + async move { + handle.wait().await; + debug!(name = ?port_name, lcore = ?LcoreId::current(), "rx loop stopped."); + }, + rx_loop(port_id, index.into(), 32, f), + )); + } + + Ok(()) + } } impl fmt::Debug for Port { @@ -80,15 +121,49 @@ impl fmt::Debug for Port { } } +async fn rx_loop(port_id: PortId, queue_id: PortRxQueueId, batch_size: usize, f: Arc) +where + F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, +{ + let rxq = PortRxQueue { port_id, queue_id }; + + let mut packets = Vec::with_capacity(batch_size); + + loop { + rxq.receive(&mut packets); + for packet in packets.drain(..) { + let _ = f(packet); + } + + // cooperatively moves to the back of the execution queue, + // making room for other tasks before polling rx again. + future::yield_now().await; + } +} + +/// Port's receive queue. +pub(crate) struct PortRxQueue { + port_id: PortId, + queue_id: PortRxQueueId, +} + +impl PortRxQueue { + /// Receives a burst of packets, up to the `Vec`'s capacity. + pub(crate) fn receive(&self, mbufs: &mut Vec) { + let mut ptrs = Vec::with_capacity(mbufs.capacity()); + dpdk::eth_rx_burst(self.port_id, self.queue_id, &mut ptrs); + ptrs.into_iter() + .for_each(|ptr| mbufs.push(Mbuf::from_easyptr(ptr))); + } +} + /// Map to lookup the port by the port name. pub(crate) struct PortMap(HashMap); impl PortMap { /// Returns the lcore with the assigned id. - fn get(&self, name: &str) -> Result<&Port> { - self.0 - .get(name) - .ok_or_else(|| anyhow!("port with name '{}' not found.", name)) + pub(crate) fn get(&self, name: &str) -> Result<&Port> { + self.0.get(name).ok_or_else(|| PortError::NotFound.into()) } } @@ -105,15 +180,27 @@ impl From> for PortMap { /// Port related errors. #[derive(Debug, Error)] pub(crate) enum PortError { + /// The port is not found. + #[error("port not found.")] + NotFound, + /// The maximum number of RX queues is less than the number of queues /// requested. - #[error("Insufficient number of RX queues. Max is {0}.")] + #[error("insufficient number of receive queues. max is {0}.")] InsufficientRxQueues(u16), /// The maximum number of TX queues is less than the number of queues /// requested. - #[error("Insufficient number of TX queues. Max is {0}.")] + #[error("insufficient number of transmit queues. max is {0}.")] InsufficientTxQueues(u16), + + /// The port does not have RX enabled. + #[error("receive not enabled on port.")] + RxNotEnabled, + + /// The pipeline for the port is already set. + #[error("pipeline already set.")] + PipelineSet, } pub(crate) struct Builder { @@ -312,7 +399,7 @@ impl Builder { for index in 0..self.rx_lcores.len() { dpdk::eth_rx_queue_setup( self.port_id, - index, + index.into(), self.rxqs, socket, None, @@ -322,7 +409,7 @@ impl Builder { // configures the tx queues. for index in 0..self.tx_lcores.len() { - dpdk::eth_tx_queue_setup(self.port_id, index, self.txqs, socket, None)?; + dpdk::eth_tx_queue_setup(self.port_id, index.into(), self.txqs, socket, None)?; } Ok(Port { @@ -330,6 +417,7 @@ impl Builder { port_id: self.port_id, rx_lcores: self.rx_lcores.clone(), tx_lcores: self.tx_lcores.clone(), + shutdown: Some(ShutdownTrigger::new()), }) } } @@ -428,4 +516,26 @@ mod tests { Ok(()) } + + #[capsule::test] + fn port_rx_tx() -> Result<()> { + let mut pool = Mempool::new("mp_port_rx", 15, 0, SocketId::ANY)?; + let port = Builder::for_device("test0", "net_null0")? + .set_rx_lcores(vec![0])? + .build(&mut pool)?; + + let rxq = PortRxQueue { + port_id: port.port_id, + queue_id: 0.into(), + }; + + let mut packets = Vec::with_capacity(4); + assert_eq!(0, packets.len()); + + rxq.receive(&mut packets); + assert_eq!(4, packets.len()); + assert_eq!(4, dpdk::mempool_in_use_count(pool.ptr_mut())); + + Ok(()) + } } diff --git a/examples/pktdump/Cargo.toml b/examples/pktdump/Cargo.toml index 1556e13..f421f28 100644 --- a/examples/pktdump/Cargo.toml +++ b/examples/pktdump/Cargo.toml @@ -19,5 +19,6 @@ doctest = false anyhow = "1.0" capsule = { version = "0.1", path = "../../core" } colored = "2.0" +signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/pktdump/README.md b/examples/pktdump/README.md index c389625..aa6167f 100644 --- a/examples/pktdump/README.md +++ b/examples/pktdump/README.md @@ -12,4 +12,4 @@ The example is located in the `examples/pktdump` sub-directory. To run the appli ## Explanation -Packet captures of IPv4 and IPv6 packets are played back with libpcap based virtual devices. The pipeline uses the `group_by` combinator to separate the packets by the L3 protocol and processes them with either `dump_v4` and `dump_v6`. Both functions use `peek` instead of `parse` to read the packets immutability. +Packet captures of IPv4 and IPv6 packets are played back with libpcap based virtual devices. Both functions use `peek` instead of `parse` to read the packets immutability. diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index 38e6d96..24d6533 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -16,26 +16,32 @@ * SPDX-License-Identifier: Apache-2.0 */ -use anyhow::Result; -use capsule::batch::{Batch, Pipeline, Poll}; -use capsule::config::load_config; +use anyhow::{anyhow, Result}; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::Ipv6; use capsule::packets::ip::IpPacket; use capsule::packets::{EtherTypes, Ethernet, Packet, Tcp, Tcp4, Tcp6}; -use capsule::{compose, Mbuf, PortQueue, Runtime}; +use capsule::rt2::{self, Mbuf, Runtime}; use colored::*; +use signal_hook::consts; +use signal_hook::flag; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use tracing::{debug, Level}; use tracing_subscriber::fmt; #[inline] -fn dump_eth(packet: Mbuf) -> Result { +fn dump_pkt(packet: Mbuf) -> Result<()> { let ethernet = packet.parse::()?; let info_fmt = format!("{:?}", ethernet).magenta().bold(); println!("{}", info_fmt); - Ok(ethernet) + match ethernet.ether_type() { + EtherTypes::Ipv4 => dump_v4(ðernet), + EtherTypes::Ipv6 => dump_v6(ðernet), + _ => Err(anyhow!("not v4 or v6.")), + } } #[inline] @@ -71,36 +77,25 @@ fn dump_tcp(tcp: &Tcp) { println!("{}", flow_fmt); } -fn install(q: PortQueue) -> impl Pipeline { - Poll::new(q.clone()) - .map(dump_eth) - .group_by( - |ethernet| ethernet.ether_type(), - |groups| { - compose!( groups { - EtherTypes::Ipv4 => |group| { - group.for_each(dump_v4) - } - EtherTypes::Ipv6 => |group| { - group.for_each(dump_v6) - } - }); - }, - ) - .send(q) -} - fn main() -> Result<()> { let subscriber = fmt::Subscriber::builder() .with_max_level(Level::DEBUG) .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; + let config = rt2::load_config()?; debug!(?config); - Runtime::build(config)? - .add_pipeline_to_port("eth1", install)? - .add_pipeline_to_port("eth2", install)? - .execute() + let guard = Runtime::from_config(config)? + .set_port_pipeline("eth1", dump_pkt)? + .set_port_pipeline("eth2", dump_pkt)? + .execute()?; + + let term = Arc::new(AtomicBool::new(false)); + flag::register(consts::SIGINT, Arc::clone(&term))?; + println!("ctrl-c to quit ..."); + while !term.load(Ordering::Relaxed) {} + + drop(guard); + Ok(()) } diff --git a/examples/pktdump/pktdump.toml b/examples/pktdump/pktdump.toml index 1ee29b1..83c21ae 100644 --- a/examples/pktdump/pktdump.toml +++ b/examples/pktdump/pktdump.toml @@ -1,6 +1,6 @@ app_name = "pktdump" -master_core = 0 -duration = 5 +main_core = 0 +worker_cores = [0] [mempool] capacity = 65535 @@ -9,11 +9,11 @@ duration = 5 [[ports]] name = "eth1" device = "net_pcap0" - args = "rx_pcap=tcp4.pcap,tx_iface=lo" - cores = [0] + args = "rx_pcap=tcp4.pcap" + rx_cores = [0] [[ports]] name = "eth2" device = "net_pcap1" - args = "rx_pcap=tcp6.pcap,tx_iface=lo" - cores = [0] + args = "rx_pcap=tcp6.pcap" + rx_cores = [0] From 8d54f0dd86e0490d55f671c2fed4cad97ac07f69 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Tue, 23 Mar 2021 22:21:14 -0400 Subject: [PATCH 07/18] port tx and beyond --- core/src/ffi/dpdk.rs | 49 +++++++ core/src/rt2/mod.rs | 25 +++- core/src/rt2/port.rs | 266 ++++++++++++++++++++++++++++++------ examples/ping4d/Cargo.toml | 1 + examples/ping4d/README.md | 28 +++- examples/ping4d/echo.pcap | Bin 1034 -> 0 bytes examples/ping4d/main.rs | 34 +++-- examples/ping4d/ping4d.toml | 10 +- examples/pktdump/main.rs | 9 +- 9 files changed, 353 insertions(+), 69 deletions(-) delete mode 100644 examples/ping4d/echo.pcap diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 6fc1607..3f23299 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -22,6 +22,7 @@ use crate::{debug, error}; use anyhow::Result; use capsule_ffi as cffi; use std::fmt; +use std::mem; use std::ops::{Deref, DerefMut}; use std::os::raw; use std::panic::{self, AssertUnwindSafe}; @@ -459,9 +460,57 @@ pub(crate) fn eth_tx_queue_setup( } } +/// Sends a burst of output packets on a transmit queue of a device. +pub(crate) fn eth_tx_burst( + port_id: PortId, + queue_id: PortTxQueueId, + tx_pkts: &mut Vec, +) -> usize { + let nb_pkts = tx_pkts.len(); + + let sent = unsafe { + cffi::_rte_eth_tx_burst( + port_id.0, + queue_id.0, + tx_pkts.as_mut_ptr() as *mut *mut cffi::rte_mbuf, + nb_pkts as u16, + ) + } as usize; + + if nb_pkts > sent { + // wasn't able to send everything. + mem::forget(tx_pkts.drain(..sent)); + } else { + unsafe { + tx_pkts.set_len(0); + } + } + + sent +} + +/// Starts a device. +pub(crate) fn eth_dev_start(port_id: PortId) -> Result<()> { + unsafe { + cffi::rte_eth_dev_start(port_id.0) + .into_result(DpdkError::from_errno) + .map(|_| ()) + } +} + +/// Stops a device. +pub(crate) fn eth_dev_stop(port_id: PortId) { + unsafe { + cffi::rte_eth_dev_stop(port_id.0); + } +} + /// A `rte_mbuf` pointer. pub(crate) type MbufPtr = EasyPtr; +// Allows the pointer to go across thread/lcore boundaries. +unsafe impl Send for MbufPtr {} + /// Allocates a new mbuf from a mempool. pub(crate) fn pktmbuf_alloc(mp: &mut MempoolPtr) -> Result { let ptr = diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index d798326..3dc8a0d 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -24,7 +24,7 @@ mod port; pub use self::config::*; pub(crate) use self::lcore::*; pub(crate) use self::mempool::*; -pub(crate) use self::port::*; +pub use self::port::{Outbox, Port, PortError, PortMap}; pub use crate::dpdk::Mbuf; use crate::ffi::dpdk::{self, LcoreId}; @@ -88,6 +88,11 @@ pub struct Runtime { } impl Runtime { + /// Returns the configured ports. + pub fn ports(&self) -> &PortMap { + &self.ports + } + /// Initializes a new runtime from config settings. pub fn from_config(config: RuntimeConfig) -> Result { info!("starting runtime."); @@ -116,7 +121,7 @@ impl Runtime { info!("initializing ports ..."); let mut ports = Vec::new(); for port in config.ports.iter() { - let port = port::Builder::for_device(&port.name, &port.device)? + let mut port = port::Builder::for_device(&port.name, &port.device)? .set_rxqs_txqs(port.rxqs, port.txqs)? .set_promiscuous(port.promiscuous)? .set_multicast(port.multicast)? @@ -125,6 +130,12 @@ impl Runtime { .build(&mut mempool)?; debug!(?port); + + if !port.tx_lcores().is_empty() { + port.spawn_tx_loops(&lcores)?; + } + + port.start()?; ports.push(port); } @@ -138,13 +149,13 @@ impl Runtime { } /// Sets the packet processing pipeline for port. - pub fn set_port_pipeline(self, port: &str, f: F) -> Result + pub fn set_port_pipeline(&self, port: &str, f: F) -> Result<()> where - F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, + F: Fn(Mbuf) -> Result<()> + Clone + Send + Sync + 'static, { let port = self.ports.get(port)?; port.spawn_rx_loops(f, &self.lcores)?; - Ok(self) + Ok(()) } /// Starts the runtime execution. @@ -170,6 +181,10 @@ impl Drop for RuntimeGuard { fn drop(&mut self) { info!("shutting down runtime."); + for port in self.runtime.ports.iter_mut() { + port.stop(); + } + unsafe { ManuallyDrop::drop(&mut self.runtime.ports); ManuallyDrop::drop(&mut self.runtime.lcores); diff --git a/core/src/rt2/port.rs b/core/src/rt2/port.rs index ed81f46..6333753 100644 --- a/core/src/rt2/port.rs +++ b/core/src/rt2/port.rs @@ -17,23 +17,25 @@ */ use super::{LcoreMap, Mbuf, Mempool, ShutdownTrigger}; -use crate::ffi::dpdk::{self, LcoreId, PortId, PortRxQueueId}; +use crate::ffi::dpdk::{self, LcoreId, MbufPtr, PortId, PortRxQueueId, PortTxQueueId}; use crate::net::MacAddr; +use crate::packets::Packet; use crate::{debug, ensure, info, warn}; use anyhow::Result; +use async_channel::{self, Receiver, Sender}; use capsule_ffi as cffi; use futures_lite::future; use std::collections::HashMap; use std::fmt; -use std::sync::Arc; use thiserror::Error; /// A PMD device port. -pub(crate) struct Port { +pub struct Port { name: String, port_id: PortId, rx_lcores: Vec, tx_lcores: Vec, + outbox: Option>, shutdown: Option, } @@ -42,7 +44,7 @@ impl Port { /// /// For applications with more than one port, this name can be used to /// identifer the port. - pub(crate) fn name(&self) -> &str { + pub fn name(&self) -> &str { &self.name } @@ -51,60 +53,129 @@ impl Port { self.port_id } + /// Returns the assigned RX lcores. + pub fn rx_lcores(&self) -> &Vec { + &self.rx_lcores + } + + /// Returns the assigned TX lcores. + pub fn tx_lcores(&self) -> &Vec { + &self.tx_lcores + } + /// Returns the MAC address of the port. /// /// If fails to retrieve the MAC address, `MacAddr::default` is returned. - pub(crate) fn mac_addr(&self) -> MacAddr { + pub fn mac_addr(&self) -> MacAddr { dpdk::eth_macaddr_get(self.port_id).unwrap_or_default() } /// Returns whether the port has promiscuous mode enabled. - pub(crate) fn promiscuous(&self) -> bool { + pub fn promiscuous(&self) -> bool { dpdk::eth_promiscuous_get(self.port_id) } /// Returns whether the port has multicast mode enabled. - pub(crate) fn multicast(&self) -> bool { + pub fn multicast(&self) -> bool { dpdk::eth_allmulticast_get(self.port_id) } - /// Sets the packet processing pipeline. + /// Returns the outbox queue. + /// + /// # Errors + /// + /// Returns `PortError::TxNotEnabled` if the port is not configured + /// to transmit packets. + pub fn outbox(&self) -> Result { + self.outbox + .as_ref() + .map(|s| Outbox(self.name.clone(), s.clone())) + .ok_or(PortError::TxNotEnabled.into()) + } + + /// Spawns the port receiving loop. pub(crate) fn spawn_rx_loops(&self, f: F, lcores: &LcoreMap) -> Result<()> where - F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, + F: Fn(Mbuf) -> Result<()> + Clone + Send + Sync + 'static, { // port is built with the builder, this would not panic. let shutdown = self.shutdown.as_ref().unwrap(); - // can't run pipeline without assigned rx cores. + // can't run loop without assigned rx cores. ensure!(self.rx_lcores.len() > 0, PortError::RxNotEnabled); // pipeline already set if the trigger is waited on. ensure!(!shutdown.is_waited(), PortError::PipelineSet); - let f = Arc::new(f); - for (index, lcore_id) in self.rx_lcores.iter().enumerate() { let lcore = lcores.get(*lcore_id)?; - let port_id = self.port_id.clone(); let port_name = self.name.clone(); let handle = shutdown.get_wait(); let f = f.clone(); - debug!(name = ?self.name, lcore = ?lcore.id(), "spawning rx loop."); + debug!(port = ?self.name, lcore = ?lcore.id(), "spawning rx loop."); // the rx loop is endless, so we use a shutdown trigger to signal // when the loop should stop executing. lcore.spawn(future::or( async move { handle.wait().await; - debug!(name = ?port_name, lcore = ?LcoreId::current(), "rx loop stopped."); + debug!(port = ?port_name, lcore = ?LcoreId::current(), "rx loop exited."); }, - rx_loop(port_id, index.into(), 32, f), + rx_loop(self.name.clone(), self.port_id, index.into(), 32, f), )); } Ok(()) } + + /// Spawns the port transmitting loop. + pub(crate) fn spawn_tx_loops(&mut self, lcores: &LcoreMap) -> Result<()> { + // though the channel is unbounded, in reality, it's bounded by the + // mempool size because that's the max number of mbufs the program + // has allocated. + let (sender, receiver) = async_channel::unbounded(); + + for (index, lcore_id) in self.tx_lcores.iter().enumerate() { + let lcore = lcores.get(*lcore_id)?; + let receiver = receiver.clone(); + + debug!(port = ?self.name, lcore = ?lcore.id(), "spawning tx loop."); + + lcore.spawn(tx_loop( + self.name.clone(), + self.port_id, + index.into(), + 32, + receiver, + )) + } + + self.outbox = Some(sender); + Ok(()) + } + + /// Starts the port. This is the final step before packets can be + /// received or transmitted on this port. + /// + /// # Errors + /// + /// Returns `DpdkError` if the port fails to start. + pub(crate) fn start(&self) -> Result<()> { + dpdk::eth_dev_start(self.port_id)?; + info!(port = ?self.name, "port started."); + Ok(()) + } + + /// Stops the port. + pub(crate) fn stop(&mut self) { + if let Some(trigger) = self.shutdown.take() { + debug!(port = ?self.name, "exiting rx loops."); + trigger.fire(); + } + + dpdk::eth_dev_stop(self.port_id); + info!(port = ?self.name, "port stopped."); + } } impl fmt::Debug for Port { @@ -121,18 +192,56 @@ impl fmt::Debug for Port { } } -async fn rx_loop(port_id: PortId, queue_id: PortRxQueueId, batch_size: usize, f: Arc) -where +/// An in-memory queue of packets waiting for transmission. +#[derive(Clone)] +pub struct Outbox(String, Sender); + +impl Outbox { + /// Pushes a new packet to the back of the queue. + pub fn push(&self, packet: P) -> std::result::Result<(), Mbuf> { + self.1 + .try_send(packet.reset().into_easyptr()) + .map_err(|err| Mbuf::from_easyptr(err.into_inner())) + } +} + +impl fmt::Debug for Outbox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}#outbox", self.0) + } +} + +/// Port's receive queue. +pub(crate) struct PortRxQueue { + port_id: PortId, + queue_id: PortRxQueueId, +} + +impl PortRxQueue { + /// Receives a burst of packets, up to the `Vec`'s capacity. + pub(crate) fn receive(&self, mbufs: &mut Vec) { + dpdk::eth_rx_burst(self.port_id, self.queue_id, mbufs); + } +} + +async fn rx_loop( + port_name: String, + port_id: PortId, + queue_id: PortRxQueueId, + batch_size: usize, + f: F, +) where F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, { - let rxq = PortRxQueue { port_id, queue_id }; + debug!(port = ?port_name, lcore = ?LcoreId::current(), "executing rx loop."); - let mut packets = Vec::with_capacity(batch_size); + let rxq = PortRxQueue { port_id, queue_id }; + let mut ptrs = Vec::with_capacity(batch_size); loop { - rxq.receive(&mut packets); - for packet in packets.drain(..) { - let _ = f(packet); + rxq.receive(&mut ptrs); + for ptr in ptrs.drain(..) { + let _ = f(Mbuf::from_easyptr(ptr)); } // cooperatively moves to the back of the execution queue, @@ -141,30 +250,89 @@ where } } -/// Port's receive queue. -pub(crate) struct PortRxQueue { +/// Port's transmit queue. +pub(crate) struct PortTxQueue { port_id: PortId, - queue_id: PortRxQueueId, + queue_id: PortTxQueueId, } -impl PortRxQueue { - /// Receives a burst of packets, up to the `Vec`'s capacity. - pub(crate) fn receive(&self, mbufs: &mut Vec) { - let mut ptrs = Vec::with_capacity(mbufs.capacity()); - dpdk::eth_rx_burst(self.port_id, self.queue_id, &mut ptrs); - ptrs.into_iter() - .for_each(|ptr| mbufs.push(Mbuf::from_easyptr(ptr))); +impl PortTxQueue { + /// Transmits a burst of packets. + /// + /// If the TX is full, the excess packets are dropped. + pub(crate) fn transmit(&self, mbufs: &mut Vec) { + dpdk::eth_tx_burst(self.port_id, self.queue_id, mbufs); + + if !mbufs.is_empty() { + // tx queue is full, we have to drop the excess. + dpdk::pktmbuf_free_bulk(mbufs); + } + } +} + +async fn tx_loop( + port_name: String, + port_id: PortId, + queue_id: PortTxQueueId, + batch_size: usize, + receiver: Receiver, +) { + debug!(port = ?port_name, lcore = ?LcoreId::current(), "executing tx loop."); + + let txq = PortTxQueue { port_id, queue_id }; + let mut ptrs = Vec::with_capacity(batch_size); + + loop { + if let Ok(ptr) = receiver.recv().await { + ptrs.push(ptr); + + // try to batch the packets up to batch size. + for _ in 1..batch_size { + match receiver.try_recv() { + Ok(ptr) => ptrs.push(ptr), + // no more packets to batch, ready to transmit. + Err(_) => break, + } + } + + txq.transmit(&mut ptrs); + + // cooperatively moves to the back of the execution queue, + // making room for other tasks before transmitting again. + future::yield_now().await; + } else { + // this branch can only be reached if the channel is closed, + // indicating the tx loop should exit. + break; + } } + + debug!(port = ?port_name, lcore = ?LcoreId::current(), "tx loop exited."); } /// Map to lookup the port by the port name. -pub(crate) struct PortMap(HashMap); +#[derive(Debug)] +pub struct PortMap(HashMap); impl PortMap { - /// Returns the lcore with the assigned id. - pub(crate) fn get(&self, name: &str) -> Result<&Port> { + /// Returns the port with the assigned name. + /// + /// # Errors + /// + /// Returns `PortError::NotFound` if the port name is not found. + pub fn get(&self, name: &str) -> Result<&Port> { self.0.get(name).ok_or_else(|| PortError::NotFound.into()) } + + /// Returns a port iterator. + pub(crate) fn iter(&self) -> impl Iterator { + self.0.values() + } + + /// Returns a port iterator. + pub(crate) fn iter_mut(&mut self) -> impl Iterator { + self.0.values_mut() + } } impl From> for PortMap { @@ -179,7 +347,7 @@ impl From> for PortMap { /// Port related errors. #[derive(Debug, Error)] -pub(crate) enum PortError { +pub enum PortError { /// The port is not found. #[error("port not found.")] NotFound, @@ -194,15 +362,20 @@ pub(crate) enum PortError { #[error("insufficient number of transmit queues. max is {0}.")] InsufficientTxQueues(u16), - /// The port does not have RX enabled. + /// The port does not have receive enabled. #[error("receive not enabled on port.")] RxNotEnabled, + /// The port does not have transmit enabled. + #[error("transmit not enabled on port.")] + TxNotEnabled, + /// The pipeline for the port is already set. #[error("pipeline already set.")] PipelineSet, } +/// Port builder. pub(crate) struct Builder { name: String, port_id: PortId, @@ -417,6 +590,7 @@ impl Builder { port_id: self.port_id, rx_lcores: self.rx_lcores.clone(), tx_lcores: self.tx_lcores.clone(), + outbox: None, shutdown: Some(ShutdownTrigger::new()), }) } @@ -522,20 +696,30 @@ mod tests { let mut pool = Mempool::new("mp_port_rx", 15, 0, SocketId::ANY)?; let port = Builder::for_device("test0", "net_null0")? .set_rx_lcores(vec![0])? + .set_tx_lcores(vec![0])? .build(&mut pool)?; + let mut packets = Vec::with_capacity(4); + assert_eq!(0, packets.len()); + let rxq = PortRxQueue { port_id: port.port_id, queue_id: 0.into(), }; - let mut packets = Vec::with_capacity(4); - assert_eq!(0, packets.len()); - rxq.receive(&mut packets); assert_eq!(4, packets.len()); assert_eq!(4, dpdk::mempool_in_use_count(pool.ptr_mut())); + let txq = PortTxQueue { + port_id: port.port_id, + queue_id: 0.into(), + }; + + txq.transmit(&mut packets); + assert_eq!(0, packets.len()); + assert_eq!(0, dpdk::mempool_in_use_count(pool.ptr_mut())); + Ok(()) } } diff --git a/examples/ping4d/Cargo.toml b/examples/ping4d/Cargo.toml index c36466f..708cba9 100644 --- a/examples/ping4d/Cargo.toml +++ b/examples/ping4d/Cargo.toml @@ -18,5 +18,6 @@ doctest = false [dependencies] anyhow = "1.0" capsule = { version = "0.1", path = "../../core" } +signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/ping4d/README.md b/examples/ping4d/README.md index 1f02bff..cafa9a5 100644 --- a/examples/ping4d/README.md +++ b/examples/ping4d/README.md @@ -10,6 +10,32 @@ The example is located in the `examples/ping4d` sub-directory. To run the applic /examples/ping4d$ cargo run -- -f ping4d.toml ``` +In a separate Vagrant VM terminal, first add a static entry to the ARP table so packets are routed to the interface `0000:00:08.0` for address `10.100.1.10`, + +``` +vagrant$ sudo arp -i eth3 -s 10.100.1.10 02:00:00:ff:ff:00 +``` + +Check the ARP table to verify that the new entry is added, + +``` +vagrant$ sudo arp -an + +? (10.100.1.10) at 02:00:00:ff:ff:00 [ether] PERM on eth3 +``` + +Now while the application is still running, ping `10.100.1.10`, + +``` +vagrant$ ping -I eth3 10.100.1.10 + +PING 10.100.1.10 (10.100.1.10) from 10.100.1.255 eth3: 56(84) bytes of data. +64 bytes from 10.100.1.10: icmp_seq=1 ttl=255 time=3.96 ms +64 bytes from 10.100.1.10: icmp_seq=2 ttl=255 time=0.799 ms +64 bytes from 10.100.1.10: icmp_seq=3 ttl=255 time=0.525 ms +... +``` + ## Explanation -Ping operates by sending an ICMP echo request packet to the target host and waiting for an ICMP echo reply. The pipeline uses the `replace` combinator to create a new reply packet for each received request packet. The request packet is immutable. +The `ping` utility sends out ICMPv4 echo request packets to address `10.100.1.10`. The packets are routed to the interface `0000:00:08.0` which the application is receiving from. For each echo request, the application generates and sends out an ICMPv4 echo reply packet in response. The `ping` utility calculates the latency as it receives each echo reply. diff --git a/examples/ping4d/echo.pcap b/examples/ping4d/echo.pcap deleted file mode 100644 index 4848b66f32360a0284a814ee94dcb4cdf921e2c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcmca|c+)~A1{MYcU}0bcan{6p{1<0pC;~D;_z+{@FCfVPBHQjJ07V#VtQ-^=d3OL= zLLe>>Frx998W`Ga1bIO+Ow25-Z0sDIT--doeEb4}Lc$`VV&W2#QqnTAa`Fm_O3Es# zYU&!ATG~3gdin;2M#dl))P+&*f*eJl3mD08fCO*Qw>p!Jrkx#GuHKn3SB7nwFlCnU$TBo0nfu zSX5k6T2>AhJ_fIvM1Q&T!hIQK$DdTntU8+ Wvi=RcCi{U+&On%~4m4SXpveH8=+R{W diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 99f78f1..1b2416a 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -17,16 +17,18 @@ */ use anyhow::Result; -use capsule::batch::{Batch, Pipeline, Poll}; -use capsule::config::load_config; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; use capsule::packets::ip::v4::Ipv4; use capsule::packets::{Ethernet, Packet}; -use capsule::{Mbuf, PortQueue, Runtime}; +use capsule::rt2::{self, Mbuf, Outbox, Runtime}; +use signal_hook::consts; +use signal_hook::flag; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use tracing::{debug, Level}; use tracing_subscriber::fmt; -fn reply_echo(packet: &Mbuf) -> Result { +fn reply_echo(packet: Mbuf, eth1: &Outbox) -> Result<()> { let reply = Mbuf::new()?; let ethernet = packet.peek::()?; @@ -50,11 +52,9 @@ fn reply_echo(packet: &Mbuf) -> Result { debug!(?request); debug!(?reply); - Ok(reply) -} + let _ = eth1.push(reply); -fn install(q: PortQueue) -> impl Pipeline { - Poll::new(q.clone()).replace(reply_echo).send(q) + Ok(()) } fn main() -> Result<()> { @@ -63,10 +63,20 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; + let config = rt2::load_config()?; debug!(?config); - Runtime::build(config)? - .add_pipeline_to_port("eth1", install)? - .execute() + let runtime = Runtime::from_config(config)?; + + let outbox = runtime.ports().get("eth1")?.outbox()?; + runtime.set_port_pipeline("eth1", move |packet| reply_echo(packet, &outbox))?; + + let _guard = runtime.execute()?; + + let term = Arc::new(AtomicBool::new(false)); + flag::register(consts::SIGINT, Arc::clone(&term))?; + println!("ctrl-c to quit ..."); + while !term.load(Ordering::Relaxed) {} + + Ok(()) } diff --git a/examples/ping4d/ping4d.toml b/examples/ping4d/ping4d.toml index 62ff3ab..be61da2 100644 --- a/examples/ping4d/ping4d.toml +++ b/examples/ping4d/ping4d.toml @@ -1,6 +1,6 @@ app_name = "ping4d" -master_core = 0 -duration = 5 +main_core = 0 +worker_cores = [0] [mempool] capacity = 65535 @@ -8,6 +8,6 @@ duration = 5 [[ports]] name = "eth1" - device = "net_pcap0" - args = "rx_pcap=echo.pcap,tx_iface=lo" - cores = [0] + device = "0000:00:08.0" + rx_cores = [0] + tx_cores = [1] diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index 24d6533..ef47ae4 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -86,16 +86,15 @@ fn main() -> Result<()> { let config = rt2::load_config()?; debug!(?config); - let guard = Runtime::from_config(config)? - .set_port_pipeline("eth1", dump_pkt)? - .set_port_pipeline("eth2", dump_pkt)? - .execute()?; + let runtime = Runtime::from_config(config)?; + runtime.set_port_pipeline("eth1", dump_pkt)?; + runtime.set_port_pipeline("eth2", dump_pkt)?; + let _guard = runtime.execute()?; let term = Arc::new(AtomicBool::new(false)); flag::register(consts::SIGINT, Arc::clone(&term))?; println!("ctrl-c to quit ..."); while !term.load(Ordering::Relaxed) {} - drop(guard); Ok(()) } From fc0e917ef12fa443c95c26037558fd2ad3cc035f Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Sat, 27 Mar 2021 11:12:55 -0400 Subject: [PATCH 08/18] beef up examples --- Cargo.toml | 1 - core/src/net/mac.rs | 2 +- core/src/packets/mod.rs | 16 ++- core/src/packets/udp.rs | 14 +++ core/src/rt2/lcore.rs | 20 ++- core/src/rt2/mempool.rs | 10 +- core/src/rt2/mod.rs | 21 +++- core/src/rt2/port.rs | 22 +++- examples/kni/Cargo.toml | 4 +- examples/kni/README.md | 69 +++++++--- examples/kni/kni.toml | 15 ++- examples/kni/main.rs | 84 ++++++++++--- examples/nat64/Cargo.toml | 4 +- examples/nat64/README.md | 95 ++++++++++---- examples/nat64/main.rs | 201 +++++++++++++++++------------- examples/nat64/nat64.toml | 14 ++- examples/ping4d/README.md | 30 +++-- examples/ping4d/main.rs | 26 ++-- examples/ping4d/ping4d.toml | 4 +- examples/pktdump/README.md | 12 +- examples/pktdump/main.rs | 48 ++++--- examples/pktdump/pktdump.toml | 6 +- examples/signals/Cargo.toml | 22 ---- examples/signals/README.md | 22 ---- examples/signals/main.rs | 46 ------- examples/signals/signals.toml | 3 - examples/skeleton/README.md | 12 +- examples/skeleton/main.rs | 5 +- examples/skeleton/skeleton.toml | 9 +- examples/syn-flood/Cargo.toml | 6 +- examples/syn-flood/README.md | 38 +++--- examples/syn-flood/main.rs | 119 ++++++++++-------- examples/syn-flood/syn-flood.toml | 10 +- 33 files changed, 598 insertions(+), 412 deletions(-) delete mode 100644 examples/signals/Cargo.toml delete mode 100644 examples/signals/README.md delete mode 100644 examples/signals/main.rs delete mode 100644 examples/signals/signals.toml diff --git a/Cargo.toml b/Cargo.toml index b68afee..78c9c2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "examples/nat64", "examples/ping4d", "examples/pktdump", - "examples/signals", "examples/skeleton", "examples/syn-flood", "ffi", diff --git a/core/src/net/mac.rs b/core/src/net/mac.rs index fa698e6..1429704 100644 --- a/core/src/net/mac.rs +++ b/core/src/net/mac.rs @@ -32,7 +32,7 @@ impl MacAddr { /// Creates a MAC address from 6 octets. #[allow(clippy::many_single_char_names)] - pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> Self { + pub const fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> Self { MacAddr([a, b, c, d, e, f]) } diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index b4d5584..de17b30 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -30,8 +30,8 @@ mod udp; pub use self::ethernet::*; pub use self::tcp::*; pub use self::udp::*; +pub use crate::dpdk::Mbuf; -use crate::Mbuf; use anyhow::{Context, Result}; use std::fmt; use std::marker::PhantomData; @@ -336,6 +336,20 @@ impl Deref for Immutable<'_, T> { } } +/// Mark of the packet as either `Emit` or `Drop`. +/// +/// Together, a `Result` represents all three possible outcome +/// of packet processing. A packet can either be emitted through port TX, +/// intentionally dropped, or aborted due to an error. +#[derive(Debug)] +pub enum Postmark { + /// Packet emitted through a port TX. + Emit, + + /// Packet intentionally dropped. + Drop(Mbuf), +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 62fae34..c146328 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -146,6 +146,19 @@ impl Udp { self.header_mut().checksum = u16be::default(); } + /// Returns the data as a `u8` slice. + #[inline] + pub fn data(&self) -> &[u8] { + if let Ok(data) = self + .mbuf() + .read_data_slice(self.payload_offset(), self.payload_len()) + { + unsafe { &*data.as_ptr() } + } else { + unreachable!() + } + } + /// Returns the 5-tuple that uniquely identifies a UDP connection. #[inline] pub fn flow(&self) -> Flow { @@ -394,6 +407,7 @@ mod tests { assert_eq!(1087, udp.dst_port()); assert_eq!(18, udp.length()); assert_eq!(0x7228, udp.checksum()); + assert_eq!(10, udp.data().len()); } #[capsule::test] diff --git a/core/src/rt2/lcore.rs b/core/src/rt2/lcore.rs index 8ddf46c..15a353d 100644 --- a/core/src/rt2/lcore.rs +++ b/core/src/rt2/lcore.rs @@ -23,12 +23,13 @@ use anyhow::Result; use async_executor::Executor; use futures_lite::future; use std::collections::HashMap; +use std::fmt; use std::future::Future; use std::sync::Arc; use thiserror::Error; /// An async executor abstraction on top of a DPDK logical core. -pub(crate) struct Lcore { +pub struct Lcore { id: LcoreId, executor: Arc>, shutdown: Option, @@ -75,11 +76,17 @@ impl Lcore { } /// Spawns a background async task. - pub(crate) fn spawn(&self, future: impl Future + Send + 'static) { + pub fn spawn(&self, future: impl Future + Send + 'static) { self.executor.spawn(future).detach(); } } +impl fmt::Debug for Lcore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lcore").field("id", &self.id()).finish() + } +} + impl Drop for Lcore { fn drop(&mut self) { if let Some(trigger) = self.shutdown.take() { @@ -92,19 +99,20 @@ impl Drop for Lcore { /// Lcore not found error. #[derive(Debug, Error)] #[error("lcore not found.")] -pub(crate) struct LcoreNotFound; +pub struct LcoreNotFound; /// Map to lookup the lcore by the assigned id. -pub(crate) struct LcoreMap(HashMap); +#[derive(Debug)] +pub struct LcoreMap(HashMap); impl LcoreMap { /// Returns the lcore with the assigned id. - pub(crate) fn get(&self, id: usize) -> Result<&Lcore> { + pub fn get(&self, id: usize) -> Result<&Lcore> { self.0.get(&id).ok_or_else(|| LcoreNotFound.into()) } /// Returns a lcore iterator. - pub(crate) fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.values() } } diff --git a/core/src/rt2/mempool.rs b/core/src/rt2/mempool.rs index c931433..97d8265 100644 --- a/core/src/rt2/mempool.rs +++ b/core/src/rt2/mempool.rs @@ -26,10 +26,8 @@ use std::ptr::{self, NonNull}; use thiserror::Error; /// A memory pool is an allocator of message buffers, or `Mbuf`. For best -/// performance, each socket should have a dedicated `Mempool`. However, -/// for simplicity, we currently only support one global Mempool. Multi- -/// socket support may be added in the future. -pub(crate) struct Mempool { +/// performance, each socket should have a dedicated `Mempool`. +pub struct Mempool { ptr: MempoolPtr, } @@ -75,13 +73,13 @@ impl Mempool { /// Returns the maximum number of Mbufs in the pool. #[inline] - pub(crate) fn capacity(&self) -> usize { + pub fn capacity(&self) -> usize { self.ptr.size as usize } /// Returns the per core cache size. #[inline] - pub(crate) fn cache_size(&self) -> usize { + pub fn cache_size(&self) -> usize { self.ptr.cache_size as usize } diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 3dc8a0d..8894444 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -16,6 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Capsule runtime. + mod config; mod lcore; mod mempool; @@ -23,11 +25,13 @@ mod port; pub use self::config::*; pub(crate) use self::lcore::*; +pub use self::lcore::{Lcore, LcoreMap, LcoreNotFound}; +pub use self::mempool::Mempool; pub(crate) use self::mempool::*; pub use self::port::{Outbox, Port, PortError, PortMap}; -pub use crate::dpdk::Mbuf; use crate::ffi::dpdk::{self, LcoreId}; +use crate::packets::{Mbuf, Postmark}; use crate::{debug, info}; use anyhow::Result; use async_channel::{self, Receiver, Sender}; @@ -88,6 +92,19 @@ pub struct Runtime { } impl Runtime { + /// Returns the mempool. + /// + /// For simplicity, we currently only support one global Mempool. Multi- + /// socket support may be added in the future. + pub fn mempool(&self) -> &Mempool { + &self.mempool + } + + /// Returns the lcores. + pub fn lcores(&self) -> &LcoreMap { + &self.lcores + } + /// Returns the configured ports. pub fn ports(&self) -> &PortMap { &self.ports @@ -151,7 +168,7 @@ impl Runtime { /// Sets the packet processing pipeline for port. pub fn set_port_pipeline(&self, port: &str, f: F) -> Result<()> where - F: Fn(Mbuf) -> Result<()> + Clone + Send + Sync + 'static, + F: Fn(Mbuf) -> Result + Clone + Send + Sync + 'static, { let port = self.ports.get(port)?; port.spawn_rx_loops(f, &self.lcores)?; diff --git a/core/src/rt2/port.rs b/core/src/rt2/port.rs index 6333753..3e65a68 100644 --- a/core/src/rt2/port.rs +++ b/core/src/rt2/port.rs @@ -16,10 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{LcoreMap, Mbuf, Mempool, ShutdownTrigger}; +use super::{LcoreMap, Mempool, ShutdownTrigger}; use crate::ffi::dpdk::{self, LcoreId, MbufPtr, PortId, PortRxQueueId, PortTxQueueId}; use crate::net::MacAddr; -use crate::packets::Packet; +use crate::packets::{Mbuf, Packet, Postmark}; use crate::{debug, ensure, info, warn}; use anyhow::Result; use async_channel::{self, Receiver, Sender}; @@ -96,7 +96,7 @@ impl Port { /// Spawns the port receiving loop. pub(crate) fn spawn_rx_loops(&self, f: F, lcores: &LcoreMap) -> Result<()> where - F: Fn(Mbuf) -> Result<()> + Clone + Send + Sync + 'static, + F: Fn(Mbuf) -> Result + Clone + Send + Sync + 'static, { // port is built with the builder, this would not panic. let shutdown = self.shutdown.as_ref().unwrap(); @@ -231,7 +231,7 @@ async fn rx_loop( batch_size: usize, f: F, ) where - F: Fn(Mbuf) -> Result<()> + Send + Sync + 'static, + F: Fn(Mbuf) -> Result + Send + Sync + 'static, { debug!(port = ?port_name, lcore = ?LcoreId::current(), "executing rx loop."); @@ -240,8 +240,18 @@ async fn rx_loop( loop { rxq.receive(&mut ptrs); + let mut drops = vec![]; + for ptr in ptrs.drain(..) { - let _ = f(Mbuf::from_easyptr(ptr)); + match f(Mbuf::from_easyptr(ptr)) { + Ok(Postmark::Emit) => (), + Ok(Postmark::Drop(ptr)) => drops.push(ptr), + Err(_) => (), + } + } + + if !drops.is_empty() { + Mbuf::free_bulk(drops); } // cooperatively moves to the back of the execution queue, @@ -325,7 +335,7 @@ impl PortMap { } /// Returns a port iterator. - pub(crate) fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.values() } diff --git a/examples/kni/Cargo.toml b/examples/kni/Cargo.toml index c5735db..a27935c 100644 --- a/examples/kni/Cargo.toml +++ b/examples/kni/Cargo.toml @@ -18,7 +18,7 @@ doctest = false [dependencies] anyhow = "1.0" capsule = { version = "0.1", path = "../../core" } -metrics-core = "0.5" -metrics-observer-yaml = "0.1" +colored = "2.0" +signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/kni/README.md b/examples/kni/README.md index 0929ad2..22a5f4b 100644 --- a/examples/kni/README.md +++ b/examples/kni/README.md @@ -1,19 +1,21 @@ # Kernel NIC interface example -The Kernel NIC Interface (KNI) is a DPDK control plane solution that allows userspace applications to exchange packets with the Linux kernel networking stack. See DPDK's [KNI documentation](https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html) for more information. This example is a minimum program that can forward packets to and from the Linux kernel. +The Kernel NIC Interface (KNI) is a DPDK control plane solution that allows userspace applications to exchange packets with the Linux kernel networking stack. See DPDK's [KNI documentation](https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html) for more information. ## Overview -KNI is useful for applications that want to conceptually share the port with the Linux kernel. For example, the application may want to leverage the kernel's built-in ability to handle [ARP](https://tools.ietf.org/html/rfc826) traffic instead of implementing the protocol natively. By enabling KNI for a port, a virtual device with the same name and MAC address as the port is exposed to the Linux kernel. The kernel will be able to receive all packets that are forwarded to this virtual device and the application will receive all packets the kernel sends to it. +KNI is useful for Capsule applications that want to delegate the processing of various network control plane protocols to either the Linux kernel or other implementations that run on Linux. For example, [Address Resolution Protocol](https://tools.ietf.org/html/rfc826) is the mechanism for hosts to discover each other's link layer address on an IPv4 network. Typically, the Linux kernel handles the ARP discovery for all the network interfaces on the host. However, because Capsule-bound network devices are not visible to the kernel, each Capsule application needs its own ARP implementation, otherwise the network won't be able to route packets to it. Or alternatively, an easier approach is for the application to simply leverage the kernel stack implementation by delegating and forwarding ARP packets via KNI. + +This example demonstrates said approach by delegating the processing of [Neighbor Discovery Procotol](https://tools.ietf.org/html/rfc4861), the IPv6 equivalent of ARP, to the Linux kernel. ## Prerequisite -This application requires the kernel module `rte_kni`. Kernel modules are version specific. If you are using our `Vagrant` with `Docker` setup, the module is already preloaded. Otherwise, you will have to compile it by installing the kernel headers or sources required to build kernel modules on your system, then [build `DPDK` from source](https://doc.dpdk.org/guides/linux_gsg/build_dpdk.html). +This application requires the kernel module `rte_kni`. Kernel modules are version specific. If you are using our Vagrant with Docker setup, the module is already preloaded. Otherwise, you will have to compile it by installing the kernel headers or sources required to build kernel modules on your system, then [build `DPDK` from source](https://doc.dpdk.org/guides/linux_gsg/build_dpdk.html). Once the build is complete, load the module with command: -``` -$ sudo insmod /lib/modules/`uname -r`/extra/dpdk/rte_kni.ko +```bash +$ sudo insmod /lib/modules/`uname -r`/extra/dpdk/rte_kni.ko carrier=on ``` We may provide precompiled modules for different kernel versions and Linux distributions in the future. @@ -22,26 +24,65 @@ We may provide precompiled modules for different kernel versions and Linux distr The example is located in the `examples/kni` sub-directory. To run the application, -``` +```bash /examples/kni$ cargo run -- -f kni.toml ``` -While the application is running, the new virtual device is exposed to the kernel, +While the application is running, in a seperate Vagrant VM terminal, check that a new virtual device `kni0` is exposed to the kernel, +```bash +vagrant$ ip link show dev kni0 + +14: kni0: mtu 2034 qdisc noop state DOWN mode DEFAULT group default qlen 1000 + link/ether 6a:80:63:c3:01:42 brd ff:ff:ff:ff:ff:ff ``` -$ ip link -254: kni0: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 - link/ether ba:dc:af:eb:ee:f1 brd ff:ff:ff:ff:ff:ff +Change the MAC address of `kni0` to match the MAC address of the physical interface first; then bring up the link, + +```bash +vagrant$ sudo ip link set dev kni0 address 02:00:00:ff:ff:00 +vagrant$ sudo ip link set dev kni0 up ``` -The kernel can assign an IP address to the device and bring the link up, at which point the kernel and the application can forward each other packets. +Once `kni0` is up, it should be automatically assigned an IPv6 address, we will need this address for the next step, +```bash +vagrant$ ip addr show dev kni0 + +14: kni0: mtu 2034 qdisc pfifo_fast state UP group default qlen 1000 + link/ether 02:00:00:ff:ff:00 brd ff:ff:ff:ff:ff:ff + inet6 fe80::ff:feff:ff00/64 scope link + valid_lft forever preferred_lft forever ``` -$ sudo ip addr add dev kni0 10.0.2.16/24 -$ sudo ip link set up dev kni0 + +Finally use `socat` to read from `stdin`, and send the input messages as UDP packets to this IPv6 address. These packets will be routed to the running Capsule application, + +```bash +vagrant$ socat -d -d -u - udp6:[fe80::ff:feff:ff00%eth3]:6667 + +Hello? +Is there anybody in there? +``` + +The running application should print out, + +``` +Mar 28 19:59:34.805 INFO kni: to kni0: Neighbor Solicitation +Mar 28 19:59:34.810 INFO kni: from kni0: Neighbor Advertisement +Mar 28 19:59:34.811 INFO kni: you said: Hello? + +Mar 28 19:59:39.475 INFO kni: you said: Is there anybody in there? + ``` # Explanation -The assigned port `0000:00:08.0` has KNI support turned on by setting the `kni` flag to `true`. To forward packets received on the port to the kernel, the application adds a simple forwarding pipeline by calling `add_pipeline_to_port`. To forward packets received from the kernel through the port, the application adds another forwarding pipeline by calling `add_kni_rx_pipeline_to_port`. +Capsule leverages the KNI poll mode driver instead of the `librte_kni` API directly. This lets the application to interact with KNI the same way as any other physical or virtual port device. + +The example is configured with one PCI port, `cap0`, and one KNI port, `kni0`. As new packets arrive through `cap0`'s rx, the application will forward all ICMPv6 packets to the `kni0`'s tx. For the sake of simplicity, it is assumed that all ICMPv6 packets received in this example will be NDP messages, and the application is delegating this link layer address discovery process to the kernel stack. In the reverse direction, kernel stack's NDP responses will come in through `kni0`'s rx, and immediately forwarded out through `cap0`'s tx without modifications. Because we assigned `cap0`'s link layer MAC address, `02:00:00:ff:ff:00`, to `kni0` with the `ip link set` command, the NDP responses already contain the correct link layer information. + +When `socat` sends out an UDP packet via `eth3` to `kni0`'s IPv6 address `fe80::ff:feff:ff00`, a lookup is performed trying to find the link layer address of the destination. On the very first attempt, that link layer address is not found through the lookup. A neighbor solicitation message is broadcasted instead to initiate the discovery process. + +`cap0` receives the broadcasted neighbor solicitation message and forwards it to the kernel stack via `kni0`. Kernel responds with a neighbor advertisement message because the IPv6 address matches the address of the `kni0` interface. This response is sent back to `eth3` through `kni0`'s rx then `cap0`'s tx, completing the discovery. + +The link layer address from the response is cached, all UDP packets are routed to `cap0` with this lookup until the cached entry expires. The Capsule application will receive the UDP packets from `socat`. It parses and prints out the data payload. diff --git a/examples/kni/kni.toml b/examples/kni/kni.toml index 0cd8494..1575219 100644 --- a/examples/kni/kni.toml +++ b/examples/kni/kni.toml @@ -1,12 +1,19 @@ app_name = "kni" -master_core = 0 +main_core = 0 +worker_cores = [] [mempool] capacity = 65535 cache_size = 256 [[ports]] - name = "kni0" + name = "cap0" device = "0000:00:08.0" - cores = [1] - kni = true + rx_cores = [1] + tx_cores = [2] + +[[ports]] + name = "kni0" + device = "net_kni0" + rx_cores = [1] + tx_cores = [2] diff --git a/examples/kni/main.rs b/examples/kni/main.rs index dc2fbed..8b0ac94 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -17,19 +17,56 @@ */ use anyhow::Result; -use capsule::config::load_config; -use capsule::metrics; -use capsule::{batch, Runtime}; -use metrics_core::{Builder, Drain, Observe}; -use metrics_observer_yaml::YamlBuilder; -use std::time::Duration; -use tracing::{debug, Level}; +use capsule::packets::icmp::v6::Icmpv6; +use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; +use capsule::packets::ip::ProtocolNumbers; +use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Udp6}; +use capsule::rt2::{self, Outbox, Runtime}; +use colored::Colorize; +use signal_hook::consts; +use signal_hook::flag; +use std::str; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tracing::{info, Level}; use tracing_subscriber::fmt; -fn print_stats() { - let mut observer = YamlBuilder::new().build(); - metrics::global().controller().observe(&mut observer); - println!("{}", observer.drain()); +fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { + let ipv6 = packet.parse::()?.parse::()?; + + match ipv6.next_header() { + ProtocolNumbers::Icmpv6 => { + let icmp = ipv6.parse::>()?; + let fmt = format!("to kni0: {}", icmp.msg_type()).cyan(); + info!("{}", fmt); + let _ = kni0.push(icmp); + Ok(Postmark::Emit) + } + ProtocolNumbers::Udp => { + let udp = ipv6.parse::()?; + let fmt = format!("you said: {}", str::from_utf8(udp.data())?).bright_blue(); + info!("{}", fmt); + Ok(Postmark::Drop(udp.reset())) + } + _ => { + let fmt = format!("not supported: {}", ipv6.next_header()).red(); + info!("{}", fmt); + Ok(Postmark::Drop(ipv6.reset())) + } + } +} + +fn from_kni(packet: Mbuf, cap0: &Outbox) -> Result { + let icmp = packet + .parse::()? + .parse::()? + .parse::>()?; + + let fmt = format!("from kni0: {}", icmp.msg_type()).green(); + info!("{}", fmt); + + let _ = cap0.push(icmp); + Ok(Postmark::Emit) } fn main() -> Result<()> { @@ -38,14 +75,21 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; - debug!(?config); + let config = rt2::load_config()?; + let runtime = Runtime::from_config(config)?; + + let kni0 = runtime.ports().get("kni0")?.outbox()?; + runtime.set_port_pipeline("cap0", move |packet| route_pkt(packet, &kni0))?; + + let cap0 = runtime.ports().get("cap0")?.outbox()?; + runtime.set_port_pipeline("kni0", move |packet| from_kni(packet, &cap0))?; + + let _guard = runtime.execute()?; + + let term = Arc::new(AtomicBool::new(false)); + flag::register(consts::SIGINT, Arc::clone(&term))?; + info!("ctrl-c to quit ..."); + while !term.load(Ordering::Relaxed) {} - Runtime::build(config)? - .add_pipeline_to_port("kni0", |q| { - batch::splice(q.clone(), q.kni().unwrap().clone()) - })? - .add_kni_rx_pipeline_to_port("kni0", batch::splice)? - .add_periodic_task_to_core(0, print_stats, Duration::from_secs(1))? - .execute() + Ok(()) } diff --git a/examples/nat64/Cargo.toml b/examples/nat64/Cargo.toml index 319e30d..c9bf0d1 100644 --- a/examples/nat64/Cargo.toml +++ b/examples/nat64/Cargo.toml @@ -17,8 +17,10 @@ doctest = false [dependencies] anyhow = "1.0" +bimap = "0.6" capsule = { version = "0.1", path = "../../core" } -chashmap = "2.2" +colored = "2.0" once_cell = "1.7" +signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/nat64/README.md b/examples/nat64/README.md index 9534f8c..84c7b4e 100644 --- a/examples/nat64/README.md +++ b/examples/nat64/README.md @@ -1,6 +1,6 @@ # IPv6 to IPv4 network address translation example -**NAT64** is a network address translation gateway that facilitates communitcation between a host on an IPv6 network to another host on an IPv4 network. This example is a simplified implementation of such gateway that can forward TCP traffic between the two networks. Non-TCP or fragmented TCP packets are dropped by the gateway. +**NAT64** is a network address translation gateway that facilitates communitcation between a client on an IPv6 network to a server on an IPv4 network. This example is a simplified implementation of such gateway that can forward TCP traffic between the two networks. ## Overview @@ -21,48 +21,95 @@ A simple network topology may consist of a gateway with two interfaces connected +---------------------+ +---------------+ ``` -We will use the well-known prefix `64:ff9b::/96` as defined in [IETF RFC 6052](https://tools.ietf.org/html/rfc6052#section-2.1) to represent IPv4 addresses to the IPv6 network. Packets from the IPv6 network with a destination address within this prefix are routed to the **NAT64** gateway. +The example will not replicate the above network topology. Instead it will send to and receive from the same dual-stacked network interface `eth3`, and perform translations between the two stacks. Conceptually it will work the same way as a gateway for two single-stacked networks. Also, non-TCP or fragmented TCP packets are dropped. -The gateway also has the address `203.0.113.1` assigned to it on the IPv4 network. +To represent IPv4 addresses to the IPv6 network, the example uses the well-known prefix `64:ff9b::/96` as defined in [IETF RFC 6052](https://tools.ietf.org/html/rfc6052#section-2.1). For example, for the server listening on the address `10.100.1.254`, it's mapped IPv6 address is `64:ff9b::a64:1fe`. + +The gateway also has the address `10.100.1.11` assigned to it on the IPv4 network. ## Running the application The example is located in the `examples/nat64` sub-directory. To run the application, -``` +```bash /examples/nat64$ cargo run -- -f nat64.toml ``` -## Explanation +In a separate Vagrant VM terminal, add a static entry to map the gateway's IPv4 address, `10.100.1.11`, to its link layer address on the IPv4 network, + +```bash +vagrant$ sudo ip neigh add 10.100.1.11 lladdr 02:00:00:ff:ff:01 dev eth3 nud permanent +``` + +Also add a routing rule for the `64:ff9b::/96` subnet, and a static entry to map the server's IPv6 representation, `64:ff9b::a64:1fe`, to the gateway's link layer address on the IPv6 network. -The **NAT64** gateway is configured with two ports. `eth1` is the port connected to the IPv6 network and `eth2` is the port connected to the IPv4 network. Also both ports are assigned the same core, core `1`. +``` +vagrant$ sudo ip route add 64:ff9b::/96 dev eth3 +vagrant$ sudo ip neigh add 64:ff9b::a64:1fe lladdr 02:00:00:ff:ff:00 dev eth3 nud permanent +``` + +Finally start a HTTP server on the IPv4 network, binding to its IP address `10.100.1.254`, +```bash +vagrant$ python3 -m http.server 8080 --bind 10.100.1.254 + +Serving HTTP on 10.100.1.254 port 8080 (http://10.100.1.254:8080/) ... ``` -[[ports]] - name = "eth1" - device = "0000:00:08.0" - cores = [1] - rxd = 512 - txd = 512 - -[[ports]] - name = "eth2" - device = "0000:00:09.0" - cores = [1] - rxd = 512 - txd = 512 + +To test the gateway, `curl` the IPv6 representation of the server address, + +```bash +vagrant$ curl -g -6 -v 'http://[64:ff9b::a64:1fe]:8080' + +... +> GET / HTTP/1.1 +> Host: [64:ff9b::a64:1fe]:8080 +> User-Agent: curl/7.64.0 +> Accept: */* +> +* HTTP 1.0, assume close after body +< HTTP/1.0 200 OK +< Server: SimpleHTTP/0.6 Python/3.7.3 +< Date: Sun, 28 Mar 2021 17:47:08 GMT +< Content-type: text/html; charset=utf-8 +< Content-Length: 956 +... ``` -Because they are assigned the same core, we can install pipelines that forward packets received on `eth1` through `eth2` and `eth2` to `eth1` by using `add_pipeline_to_core`. +## Explanation + +The gateway example is configured with two ports. Conceptually, `cap0` is the port on the IPv6 network receiving packets intended for the `64:ff9b::/96` subnet. `cap1` is the port on the IPv4 network with the address `10.100.1.11`. ### 6-to-4 translation -When the gateway receives an unfragmented IPv6 TCP packet, it will translate the destination address to the IPv4 counterpart by stripping away the `64:ff9b::/96` prefix. The source address will be replaced by the gateway's assigned IPv4 address and the source port will be replaced by a free port on the gateway. This address and port mapping is stored in the global `PORT_MAP`. +The interaction starts with a client, the `curl` program, on the IPv6 network tries to connect to a python HTTP server on the IPv4 network. When `cap0` receives the TCP packet, it will translate the destination address to the IPv4 counterpart by stripping away the `64:ff9b::/96` prefix. The source address will be replaced by the gateway's IPv4 address `10.100.1.11`, and the source port will be replaced by a free port on the gateway. The original source address and port are saved and will be used later to translate the response packets. + +The IPv6 header is removed and replaced by an IPv4 header using the steps outlined in [IETF RFC 6145](https://tools.ietf.org/html/rfc6145#section-5.1). -The IPv6 header is removed and replaced by an IPv4 header using the model outlined in [IETF RFC 6145](https://tools.ietf.org/html/rfc6145#section-5.1). +Once the translation is complete, the packet is transmitted through `cap1` and routed to the python HTTP server. ### 4-to-6 translation -When the gateway receives an unfragmented IPv4 TCP packet, it will translate the source address to the IPv6 counterpart by adding the `64:ff9b::/96` prefix. The TCP destination port is used as the lookup key to find the mapped destination address and port of the IPv6 host. This mapping is stored in the global `ADDR_MAP`. +The response from the python HTTP server is routed to the gateway's IPv4 address because from the server's perspective, the request was originated from `10.100.1.11`, as shown in the access log. + +``` +10.100.1.11 - - [28/Mar/2021 17:47:08] "GET / HTTP/1.1" 200 - +``` + +The response TCP packets are received by `cap1`. It will translate the source address to the IPv6 counterpart by adding the `64:ff9b::/96` prefix. A lookup is performed with the TCP destination port to retrieve the source address and port of the original client on the IPv6 network. -The IPv4 header is removed and replaced by an IPv6 header using the model outlined in [IETF RFC 6145](https://tools.ietf.org/html/rfc6145#section-4.1). +The IPv4 header is removed and replaced by an IPv6 header using the steps outlined in [IETF RFC 6145](https://tools.ietf.org/html/rfc6145#section-4.1). + +Once the translation is complete, the packet is transmitted through `cap0` and routed to the client. + +A HTTP request-response cycle consists of multiple TCP packets, from connection establishment to termination. The entire TCP lifecycle is logged by the example application. `curl` writes out the response text after the process completes. + +## Cleaning up + +To clean up the static routes, + +```bash +vagrant$ sudo ip neigh del 10.100.1.11 dev eth3 +vagrant$ sudo ip neigh del 64:ff9b::a64:1fe dev eth3 +vagrant$ sudo ip route del 64:ff9b::/96 dev eth3 +``` diff --git a/examples/nat64/main.rs b/examples/nat64/main.rs index a0a8f27..4b68d21 100644 --- a/examples/nat64/main.rs +++ b/examples/nat64/main.rs @@ -17,49 +17,100 @@ */ use anyhow::Result; -use capsule::batch::{Batch, Either, Pipeline, Poll}; -use capsule::config::load_config; +use bimap::BiMap; +use capsule::net::MacAddr; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; -use capsule::packets::{Ethernet, Packet, Tcp4, Tcp6}; -use capsule::{Mbuf, PortQueue, Runtime}; -use chashmap::CHashMap; +use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Tcp4, Tcp6}; +use capsule::rt2::{self, Outbox, Runtime}; +use colored::Colorize; use once_cell::sync::Lazy; +use signal_hook::consts; +use signal_hook::flag; use std::collections::HashMap; use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::atomic::{AtomicU16, Ordering}; -use tracing::{debug, Level}; +use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; +use std::sync::{Arc, Mutex}; +use tracing::{info, Level}; use tracing_subscriber::fmt; -const V4_ADDR: Ipv4Addr = Ipv4Addr::new(203, 0, 113, 1); +static PORTS: Lazy>> = Lazy::new(|| Mutex::new(BiMap::new())); +static MACS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); -static PORT_MAP: Lazy> = Lazy::new(CHashMap::new); -static ADDR_MAP: Lazy> = Lazy::new(CHashMap::new); +/// Maps the destination IPv6 address to its IPv4 counterpart by stripping +/// off the 96-bit prefix. +fn map_6to4(addr: Ipv6Addr) -> Ipv4Addr { + let segments = addr.segments(); + let mapped = (segments[6] as u32) << 16 | (segments[7] as u32); + mapped.into() +} -/// Looks up the assigned port for a source IPv6 address and port tuple. -fn assigned_port(addr: Ipv6Addr, port: u16) -> u16 { +/// Looks up the assigned port for an IPv6 source. +fn get_v4_port(mac: MacAddr, ip: Ipv6Addr, port: u16) -> u16 { static NEXT_PORT: AtomicU16 = AtomicU16::new(1025); - let key = (addr, port); - if let Some(value) = PORT_MAP.get(&key) { + let key = (ip, port); + let mut ports = PORTS.lock().unwrap(); + + if let Some(value) = ports.get_by_left(&key) { *value } else { let port = NEXT_PORT.fetch_add(1, Ordering::Relaxed); - PORT_MAP.insert_new(key, port); - ADDR_MAP.insert_new(port, key); + MACS.lock().unwrap().insert(ip, mac); + ports.insert(key, port); port } } -/// Looks up the IPv6 address and port the gateway port is assigned to. -fn assigned_addr(port: u16) -> Option<(Ipv6Addr, u16)> { - ADDR_MAP.get(&port).map(|v| *v) +fn nat_6to4(packet: Mbuf, cap1: &Outbox) -> Result { + const SRC_IP: Ipv4Addr = Ipv4Addr::new(10, 100, 1, 11); + const DST_MAC: MacAddr = MacAddr::new(0x02, 0x00, 0x00, 0xff, 0xff, 0xff); + + let ethernet = packet.parse::()?; + let v6 = ethernet.parse::()?; + + if v6.next_header() == ProtocolNumbers::Tcp { + let dscp = v6.dscp(); + let ecn = v6.ecn(); + let ttl = v6.hop_limit() - 1; + let protocol = v6.next_header(); + let src_ip = v6.src(); + let dst_ip = map_6to4(v6.dst()); + let src_mac = v6.envelope().src(); + + let mut ethernet = v6.remove()?; + ethernet.swap_addresses(); + ethernet.set_dst(DST_MAC); + + let mut v4 = ethernet.push::()?; + v4.set_dscp(dscp); + v4.set_ecn(ecn); + v4.set_ttl(ttl); + v4.set_protocol(protocol); + v4.set_src(SRC_IP); + v4.set_dst(dst_ip); + + let mut tcp = v4.parse::()?; + let port = tcp.src_port(); + tcp.set_src_port(get_v4_port(src_mac, src_ip, port)); + tcp.reconcile_all(); + + let fmt = format!("{:?}", tcp.envelope()).magenta(); + info!("{}", fmt); + let fmt = format!("{:?}", tcp).bright_blue(); + info!("{}", fmt); + + let _ = cap1.push(tcp); + Ok(Postmark::Emit) + } else { + Ok(Postmark::Drop(v6.reset())) + } } /// Maps the source IPv4 address to its IPv6 counterpart with well-known /// prefix `64:ff9b::/96`. -fn map4to6(addr: Ipv4Addr) -> Ipv6Addr { +fn map_4to6(addr: Ipv4Addr) -> Ipv6Addr { let octets = addr.octets(); Ipv6Addr::new( 0x64, @@ -73,105 +124,81 @@ fn map4to6(addr: Ipv4Addr) -> Ipv6Addr { ) } -/// Maps the destination IPv6 address to its IPv4 counterpart by stripping -/// off the 96-bit prefix. -#[inline] -fn map6to4(addr: Ipv6Addr) -> Ipv4Addr { - let segments = addr.segments(); - let mapped = (segments[6] as u32) << 16 | (segments[7] as u32); - mapped.into() +/// Looks up the IPv6 destination +fn get_v6_dst(port: u16) -> Option<(MacAddr, Ipv6Addr, u16)> { + PORTS + .lock() + .unwrap() + .get_by_right(&port) + .and_then(|(ip, port)| MACS.lock().unwrap().get(ip).map(|mac| (*mac, *ip, *port))) } -#[inline] -fn nat_4to6(packet: Mbuf) -> Result> { +fn nat_4to6(packet: Mbuf, cap0: &Outbox) -> Result { let ethernet = packet.parse::()?; let v4 = ethernet.parse::()?; + if v4.protocol() == ProtocolNumbers::Tcp && v4.fragment_offset() == 0 && !v4.more_fragments() { let tcp = v4.peek::()?; - if let Some((dst, port)) = assigned_addr(tcp.dst_port()) { + + if let Some((dst_mac, dst_ip, port)) = get_v6_dst(tcp.dst_port()) { let dscp = v4.dscp(); let ecn = v4.ecn(); let next_header = v4.protocol(); let hop_limit = v4.ttl() - 1; - let src = map4to6(v4.src()); + let src_ip = map_4to6(v4.src()); + + let mut ethernet = v4.remove()?; + ethernet.swap_addresses(); + ethernet.set_dst(dst_mac); - let ethernet = v4.remove()?; let mut v6 = ethernet.push::()?; v6.set_dscp(dscp); v6.set_ecn(ecn); v6.set_next_header(next_header); v6.set_hop_limit(hop_limit); - v6.set_src(src); - v6.set_dst(dst); + v6.set_src(src_ip); + v6.set_dst(dst_ip); let mut tcp = v6.parse::()?; tcp.set_dst_port(port); tcp.reconcile_all(); - Ok(Either::Keep(tcp.reset())) + let fmt = format!("{:?}", tcp.envelope()).cyan(); + info!("{}", fmt); + let fmt = format!("{:?}", tcp).bright_blue(); + info!("{}", fmt); + + let _ = cap0.push(tcp); + Ok(Postmark::Emit) } else { - Ok(Either::Drop(v4.reset())) + Ok(Postmark::Drop(v4.reset())) } } else { - Ok(Either::Drop(v4.reset())) + Ok(Postmark::Drop(v4.reset())) } } -#[inline] -fn nat_6to4(packet: Mbuf) -> Result> { - let ethernet = packet.parse::()?; - let v6 = ethernet.parse::()?; - if v6.next_header() == ProtocolNumbers::Tcp { - let dscp = v6.dscp(); - let ecn = v6.ecn(); - let ttl = v6.hop_limit() - 1; - let protocol = v6.next_header(); - let src = v6.src(); - let dst = map6to4(v6.dst()); - - let ethernet = v6.remove()?; - let mut v4 = ethernet.push::()?; - v4.set_dscp(dscp); - v4.set_ecn(ecn); - v4.set_ttl(ttl); - v4.set_protocol(protocol); - v4.set_src(V4_ADDR); - v4.set_dst(dst); - - let mut tcp = v4.parse::()?; - let port = tcp.src_port(); - tcp.set_src_port(assigned_port(src, port)); - tcp.reconcile_all(); - - Ok(Either::Keep(tcp.reset())) - } else { - Ok(Either::Drop(v6.reset())) - } -} - -fn install_6to4(qs: HashMap) -> impl Pipeline { - Poll::new(qs["eth1"].clone()) - .filter_map(nat_6to4) - .send(qs["eth2"].clone()) -} - -fn install_4to6(qs: HashMap) -> impl Pipeline { - Poll::new(qs["eth2"].clone()) - .filter_map(nat_4to6) - .send(qs["eth1"].clone()) -} - fn main() -> Result<()> { let subscriber = fmt::Subscriber::builder() .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; - debug!(?config); + let config = rt2::load_config()?; + let runtime = Runtime::from_config(config)?; + + let cap1 = runtime.ports().get("cap1")?.outbox()?; + runtime.set_port_pipeline("cap0", move |packet| nat_6to4(packet, &cap1))?; + + let cap0 = runtime.ports().get("cap0")?.outbox()?; + runtime.set_port_pipeline("cap1", move |packet| nat_4to6(packet, &cap0))?; + + let _guard = runtime.execute()?; + + let term = Arc::new(AtomicBool::new(false)); + flag::register(consts::SIGINT, Arc::clone(&term))?; + info!("ctrl-c to quit ..."); + while !term.load(Ordering::Relaxed) {} - Runtime::build(config)? - .add_pipeline_to_core(1, install_6to4)? - .add_pipeline_to_core(1, install_4to6)? - .execute() + Ok(()) } diff --git a/examples/nat64/nat64.toml b/examples/nat64/nat64.toml index 8db5992..e0ba358 100644 --- a/examples/nat64/nat64.toml +++ b/examples/nat64/nat64.toml @@ -1,16 +1,20 @@ app_name = "nat64" -master_core = 0 +main_core = 0 +worker_cores = [] [mempool] capacity = 65535 cache_size = 256 [[ports]] - name = "eth1" + name = "cap0" device = "0000:00:08.0" - cores = [1] + promiscuous = false + rx_cores = [1] + tx_cores = [0] [[ports]] - name = "eth2" + name = "cap1" device = "0000:00:09.0" - cores = [1] + rx_cores = [1] + tx_cores = [0] diff --git a/examples/ping4d/README.md b/examples/ping4d/README.md index cafa9a5..e2c109e 100644 --- a/examples/ping4d/README.md +++ b/examples/ping4d/README.md @@ -6,30 +6,30 @@ The example is located in the `examples/ping4d` sub-directory. To run the application, -``` +```bash /examples/ping4d$ cargo run -- -f ping4d.toml ``` In a separate Vagrant VM terminal, first add a static entry to the ARP table so packets are routed to the interface `0000:00:08.0` for address `10.100.1.10`, -``` -vagrant$ sudo arp -i eth3 -s 10.100.1.10 02:00:00:ff:ff:00 +```bash +vagrant$ sudo ip neigh add 10.100.1.10 lladdr 02:00:00:ff:ff:00 dev eth3 nud permanent ``` Check the ARP table to verify that the new entry is added, -``` -vagrant$ sudo arp -an +```bash +vagrant$ ip neigh show dev eth3 -? (10.100.1.10) at 02:00:00:ff:ff:00 [ether] PERM on eth3 +10.100.1.10 lladdr 02:00:00:ff:ff:00 PERMANENT ``` Now while the application is still running, ping `10.100.1.10`, -``` +```bash vagrant$ ping -I eth3 10.100.1.10 -PING 10.100.1.10 (10.100.1.10) from 10.100.1.255 eth3: 56(84) bytes of data. +PING 10.100.1.10 (10.100.1.10) from 10.100.1.254 eth3: 56(84) bytes of data. 64 bytes from 10.100.1.10: icmp_seq=1 ttl=255 time=3.96 ms 64 bytes from 10.100.1.10: icmp_seq=2 ttl=255 time=0.799 ms 64 bytes from 10.100.1.10: icmp_seq=3 ttl=255 time=0.525 ms @@ -38,4 +38,16 @@ PING 10.100.1.10 (10.100.1.10) from 10.100.1.255 eth3: 56(84) bytes of data. ## Explanation -The `ping` utility sends out ICMPv4 echo request packets to address `10.100.1.10`. The packets are routed to the interface `0000:00:08.0` which the application is receiving from. For each echo request, the application generates and sends out an ICMPv4 echo reply packet in response. The `ping` utility calculates the latency as it receives each echo reply. +`cap0` is configured to receive packets on lcore `0` and transmit packets on lcore `1`. + +The `ping` utility sends out ICMPv4 echo request packets to address `10.100.1.10`. With the static ARP entry, the packets are routed to `cap0`. For each echo request, the application generates and sends out an ICMPv4 echo reply packet in response and drops the original echo request. + +The `ping` utility calculates the latency as it receives each echo reply. + +## Cleaning up + +To clean up the ARP table, + +```bash +vagrant$ sudo ip neigh del 10.100.1.10 dev eth3 +``` diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 1b2416a..14fde50 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -19,16 +19,16 @@ use anyhow::Result; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; use capsule::packets::ip::v4::Ipv4; -use capsule::packets::{Ethernet, Packet}; -use capsule::rt2::{self, Mbuf, Outbox, Runtime}; +use capsule::packets::{Ethernet, Mbuf, Packet, Postmark}; +use capsule::rt2::{self, Outbox, Runtime}; use signal_hook::consts; use signal_hook::flag; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use tracing::{debug, Level}; +use tracing::{info, Level}; use tracing_subscriber::fmt; -fn reply_echo(packet: Mbuf, eth1: &Outbox) -> Result<()> { +fn reply_echo(packet: Mbuf, cap0: &Outbox) -> Result { let reply = Mbuf::new()?; let ethernet = packet.peek::()?; @@ -49,33 +49,31 @@ fn reply_echo(packet: Mbuf, eth1: &Outbox) -> Result<()> { reply.set_data(request.data())?; reply.reconcile_all(); - debug!(?request); - debug!(?reply); + info!(?request); + info!(?reply); - let _ = eth1.push(reply); + let _ = cap0.push(reply); - Ok(()) + Ok(Postmark::Drop(packet)) } fn main() -> Result<()> { let subscriber = fmt::Subscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber)?; let config = rt2::load_config()?; - debug!(?config); - let runtime = Runtime::from_config(config)?; - let outbox = runtime.ports().get("eth1")?.outbox()?; - runtime.set_port_pipeline("eth1", move |packet| reply_echo(packet, &outbox))?; + let outbox = runtime.ports().get("cap0")?.outbox()?; + runtime.set_port_pipeline("cap0", move |packet| reply_echo(packet, &outbox))?; let _guard = runtime.execute()?; let term = Arc::new(AtomicBool::new(false)); flag::register(consts::SIGINT, Arc::clone(&term))?; - println!("ctrl-c to quit ..."); + info!("ctrl-c to quit ..."); while !term.load(Ordering::Relaxed) {} Ok(()) diff --git a/examples/ping4d/ping4d.toml b/examples/ping4d/ping4d.toml index be61da2..c90724d 100644 --- a/examples/ping4d/ping4d.toml +++ b/examples/ping4d/ping4d.toml @@ -1,13 +1,13 @@ app_name = "ping4d" main_core = 0 -worker_cores = [0] +worker_cores = [] [mempool] capacity = 65535 cache_size = 256 [[ports]] - name = "eth1" + name = "cap0" device = "0000:00:08.0" rx_cores = [0] tx_cores = [1] diff --git a/examples/pktdump/README.md b/examples/pktdump/README.md index aa6167f..ed030d4 100644 --- a/examples/pktdump/README.md +++ b/examples/pktdump/README.md @@ -1,15 +1,21 @@ # Packet dump example -An example demonstrating pipeline combinators and packet peeking. +An example demonstrating basic packet processing. ## Running the application The example is located in the `examples/pktdump` sub-directory. To run the application, -``` +```bash /examples/pktdump$ cargo run -- -f pktdump.toml ``` ## Explanation -Packet captures of IPv4 and IPv6 packets are played back with libpcap based virtual devices. Both functions use `peek` instead of `parse` to read the packets immutability. +Packet captures, or pcaps, of IPv4 and IPv6 TCP packets are played back with ports using `libpcap` based virtual devices. `cap0` replays the IPv4 pcap and `cap1` replays the IPv6 pcap. Both ports are receiving on the same worker lcore, and have transmit disabled. + +The parse functions showcase the packet type system. Both IPv4 and IPv6 packet types are preceded by the Ethernet packet type. TCP packet type can succeed either IPv4 or IPv6 packet types. + +Packets are dropped at the end of `dump_pkt`. + +`ctrl-c` terminates the application. diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index ef47ae4..fbaeaf3 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -20,35 +20,35 @@ use anyhow::{anyhow, Result}; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::Ipv6; use capsule::packets::ip::IpPacket; -use capsule::packets::{EtherTypes, Ethernet, Packet, Tcp, Tcp4, Tcp6}; -use capsule::rt2::{self, Mbuf, Runtime}; -use colored::*; +use capsule::packets::{EtherTypes, Ethernet, Mbuf, Packet, Postmark, Tcp, Tcp4, Tcp6}; +use capsule::rt2::{self, Runtime}; +use colored::Colorize; use signal_hook::consts; use signal_hook::flag; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use tracing::{debug, Level}; +use tracing::{info, Level}; use tracing_subscriber::fmt; -#[inline] -fn dump_pkt(packet: Mbuf) -> Result<()> { +fn dump_pkt(packet: Mbuf) -> Result { let ethernet = packet.parse::()?; - let info_fmt = format!("{:?}", ethernet).magenta().bold(); - println!("{}", info_fmt); + let fmt = format!("{:?}", ethernet).magenta().bold(); + info!("{}", fmt); match ethernet.ether_type() { EtherTypes::Ipv4 => dump_v4(ðernet), EtherTypes::Ipv6 => dump_v6(ðernet), _ => Err(anyhow!("not v4 or v6.")), - } + }?; + + Ok(Postmark::Drop(ethernet.reset())) } -#[inline] fn dump_v4(ethernet: &Ethernet) -> Result<()> { let v4 = ethernet.peek::()?; - let info_fmt = format!("{:?}", v4).yellow(); - println!("{}", info_fmt); + let fmt = format!("{:?}", v4).yellow(); + info!("{}", fmt); let tcp = v4.peek::()?; dump_tcp(&tcp); @@ -56,11 +56,10 @@ fn dump_v4(ethernet: &Ethernet) -> Result<()> { Ok(()) } -#[inline] fn dump_v6(ethernet: &Ethernet) -> Result<()> { let v6 = ethernet.peek::()?; - let info_fmt = format!("{:?}", v6).cyan(); - println!("{}", info_fmt); + let fmt = format!("{:?}", v6).cyan(); + info!("{}", fmt); let tcp = v6.peek::()?; dump_tcp(&tcp); @@ -68,32 +67,29 @@ fn dump_v6(ethernet: &Ethernet) -> Result<()> { Ok(()) } -#[inline] fn dump_tcp(tcp: &Tcp) { - let tcp_fmt = format!("{:?}", tcp).green(); - println!("{}", tcp_fmt); + let fmt = format!("{:?}", tcp).green(); + info!("{}", fmt); - let flow_fmt = format!("{:?}", tcp.flow()).bright_blue(); - println!("{}", flow_fmt); + let fmt = format!("{:?}", tcp.flow()).bright_blue(); + info!("{}", fmt); } fn main() -> Result<()> { let subscriber = fmt::Subscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber)?; let config = rt2::load_config()?; - debug!(?config); - let runtime = Runtime::from_config(config)?; - runtime.set_port_pipeline("eth1", dump_pkt)?; - runtime.set_port_pipeline("eth2", dump_pkt)?; + runtime.set_port_pipeline("cap0", dump_pkt)?; + runtime.set_port_pipeline("cap1", dump_pkt)?; let _guard = runtime.execute()?; let term = Arc::new(AtomicBool::new(false)); flag::register(consts::SIGINT, Arc::clone(&term))?; - println!("ctrl-c to quit ..."); + info!("ctrl-c to quit ..."); while !term.load(Ordering::Relaxed) {} Ok(()) diff --git a/examples/pktdump/pktdump.toml b/examples/pktdump/pktdump.toml index 83c21ae..066bd2c 100644 --- a/examples/pktdump/pktdump.toml +++ b/examples/pktdump/pktdump.toml @@ -1,19 +1,19 @@ app_name = "pktdump" main_core = 0 -worker_cores = [0] +worker_cores = [] [mempool] capacity = 65535 cache_size = 256 [[ports]] - name = "eth1" + name = "cap0" device = "net_pcap0" args = "rx_pcap=tcp4.pcap" rx_cores = [0] [[ports]] - name = "eth2" + name = "cap1" device = "net_pcap1" args = "rx_pcap=tcp6.pcap" rx_cores = [0] diff --git a/examples/signals/Cargo.toml b/examples/signals/Cargo.toml deleted file mode 100644 index 41f7eec..0000000 --- a/examples/signals/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "signals" -version = "0.1.0" -authors = ["Capsule Developers "] -license = "Apache-2.0" -edition = "2018" -publish = false -readme = "README.md" -description = """ -Linux signal handling example. -""" - -[[bin]] -name = "signals" -path = "main.rs" -doctest = false - -[dependencies] -anyhow = "1.0" -capsule = { version = "0.1", path = "../../core" } -tracing = "0.1" -tracing-subscriber = "0.2" diff --git a/examples/signals/README.md b/examples/signals/README.md deleted file mode 100644 index 9fb1047..0000000 --- a/examples/signals/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Linux signal handling example - -An example demonstrating how to handle linux signals in a Capsule application. - -## Running the application - -The example is located in the `examples/signals` sub-directory. To run the application, - -``` -/examples/signals$ cargo run -- -f signals.toml -``` - -To send signals to the application, execute these commands in a separate terminal, - -``` -$ kill -s SIGHUP $(pidof signals) -$ kill -s SIGTERM $(pidof signals) -``` - -## Explanation - -The `Runtime` exposes `SIGHUP`, `SIGINT`, and `SIGTERM` to the application. By default, any signal received will terminate the running application. To customize the signal handling, use `set_on_signal` to set a custom handler. A return of `false` will continue the runtime execution and a return of `true` will stop the application. This example will ignore `SIGHUP` and terminate on `SIGINT` (`Ctrl-C`) or `SIGTERM`. diff --git a/examples/signals/main.rs b/examples/signals/main.rs deleted file mode 100644 index b0d7b92..0000000 --- a/examples/signals/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use anyhow::Result; -use capsule::config::load_config; -use capsule::Runtime; -use capsule::UnixSignal::{self, *}; -use tracing::{info, Level}; -use tracing_subscriber::fmt; - -fn on_signal(signal: UnixSignal) -> bool { - info!(?signal); - match signal { - SIGHUP => false, - SIGINT | SIGTERM => true, - } -} - -fn main() -> Result<()> { - let subscriber = fmt::Subscriber::builder() - .with_max_level(Level::INFO) - .finish(); - tracing::subscriber::set_global_default(subscriber)?; - - let config = load_config()?; - let mut runtime = Runtime::build(config)?; - runtime.set_on_signal(on_signal); - - println!("Ctrl-C to stop..."); - runtime.execute() -} diff --git a/examples/signals/signals.toml b/examples/signals/signals.toml deleted file mode 100644 index 1398b96..0000000 --- a/examples/signals/signals.toml +++ /dev/null @@ -1,3 +0,0 @@ -app_name = "signals" -master_core = 0 -ports = [] diff --git a/examples/skeleton/README.md b/examples/skeleton/README.md index b09dd3e..e8ae0b1 100644 --- a/examples/skeleton/README.md +++ b/examples/skeleton/README.md @@ -6,12 +6,18 @@ The base skeleton example is the simplest Capsule application that can be writte The example is located in the `examples/skeleton` sub-directory. To run the application, -``` +```bash /examples/skeleton$ cargo run -- -f skeleton.toml ``` ## Explanation -`skeleton.toml` demonstrates the configuration file structure. The application must specify an `app_name`, `main_core`, `mempool`, and at least one `port`. For the skeleton example, the main application thread runs on CPU core `0`. It has a mempool with capacity of `65535` mbufs preallocated. It has one port configured to also run on CPU core `0`, using an in-memory ring-based virtual device. +`skeleton.toml` demonstrates the configuration file structure. The application must specify an `app_name`, `main_core`, `worker_cores`, `mempool`, and at least one `port`. + +For the skeleton example, the main application thread, referred to as _lcore_ per DPDK terminology, runs on CPU physical core `0`. This main lcore executes the application bootstrapping logic, such as initializing the Capsule runtime. This example does not have any worker tasks, so `worker_cores` is set to empty. + +The global mempool has a preallocated capacity of `65535` mbufs, or message buffers, with a `256` per lcore cache. The mempool's capacity places an upper bound on the total amount of memory used for storing network packets, and is constant for the lifetime of the Capsule application. + +The example has one port configured, named `cap0`, using an in-memory ring-based virtual device. The port's receive loop, aka `rx`, is assigned to run on worker lcore `0`; and it's transmit loop, aka `tx`, is also assigned to run on worker lcore `0`. Both `rx` and `tx` are continuous loops constantly trying to receive and transmit network packets respectively. In practice, especially for heavy work load, they should be executed on separate, dedicated worker lcores for maximum throughput. Sharing the same worker lcore, like in this example, will have negative impact on performance because they are competing with each other for CPU time. -The `main` function first sets up the [`tracing`](https://github.com/tokio-rs/tracing) framework to record log output to the console at `TRACE` level. Then it builds a `Runtime` with the settings from `skeleton.toml` and executes that runtime. Because there are no pipelines or tasks scheduled with the runtime, the application doesn't do anything. It terminates immediately. +The `main` function first sets up the [`tracing`](https://github.com/tokio-rs/tracing) framework to record log output to the console at `DEBUG` level. Then it builds a `Runtime` with the settings from `skeleton.toml` and executes that runtime. Because there are no tasks scheduled with the runtime, the application doesn't do anything. It terminates immediately. The console output shows the lifecycle of the Capsule runtime from initialization to termination. diff --git a/examples/skeleton/main.rs b/examples/skeleton/main.rs index 50139ee..0763f8a 100644 --- a/examples/skeleton/main.rs +++ b/examples/skeleton/main.rs @@ -23,14 +23,15 @@ use tracing_subscriber::fmt; fn main() -> Result<()> { let subscriber = fmt::Subscriber::builder() - .with_max_level(Level::TRACE) + .with_max_level(Level::DEBUG) .finish(); tracing::subscriber::set_global_default(subscriber)?; let config = rt2::load_config()?; debug!(?config); - let _ = Runtime::from_config(config)?.execute()?; + let runtime = Runtime::from_config(config)?; + let _guard = runtime.execute()?; Ok(()) } diff --git a/examples/skeleton/skeleton.toml b/examples/skeleton/skeleton.toml index aef3ea5..b44d130 100644 --- a/examples/skeleton/skeleton.toml +++ b/examples/skeleton/skeleton.toml @@ -1,9 +1,14 @@ app_name = "skeleton" main_core = 0 -worker_cores = [0] +worker_cores = [] dpdk_args = "-v --log-level eal:8" +[mempool] + capacity = 65535 + cache_size = 256 + [[ports]] - name = "eth1" + name = "cap0" device = "net_ring0" rx_cores = [0] + tx_cores = [0] diff --git a/examples/syn-flood/Cargo.toml b/examples/syn-flood/Cargo.toml index 1dbe933..355dd44 100644 --- a/examples/syn-flood/Cargo.toml +++ b/examples/syn-flood/Cargo.toml @@ -17,8 +17,10 @@ doctest = false [dependencies] anyhow = "1.0" +async-io = "1.3" capsule = { version = "0.1", path = "../../core" } -metrics-core = "0.5" -metrics-observer-yaml = "0.1" +futures-lite = "1.11" +rand = "0.8" +signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/syn-flood/README.md b/examples/syn-flood/README.md index c1c10e3..3c71bc9 100644 --- a/examples/syn-flood/README.md +++ b/examples/syn-flood/README.md @@ -1,37 +1,47 @@ # TCP SYN flood example -SYN flood is a form of denial-of-service attack in which the sender attempts to consume resources on the target host by sending large amount of SYN packets. This example demonstrates how to generate new packets as the start of a pipeline. +SYN flood is a form of denial-of-service attack in which the sender attempts to consume resources on the target host by sending large amount of SYN packets. ## Overview SYN flood exploits the [TCP three-way handshake](https://tools.ietf.org/html/rfc793#section-3.4) by sending large amount of SYNs to the target with spoofed source IP addresses. The target will send SYN-ACK to these falsified IP addresses. They will either be unreachable or not respond. +This example demonstrates how to generate new packets instead of receiving packets from a port. + ## Running the application The example is located in the `examples/syn-flood` sub-directory. To run the application, -``` +```bash /examples/syn-flood$ cargo run -- -f syn-flood.toml ``` To observe the `SYN` flood traffic, in the vagrant VM, run `tcpdump` to capture packets sent to the destination IP address and port, -``` -$ sudo tcpdump -nn host 10.100.1.255 and port 80 +```bash +vagrant$ sudo tcpdump -i eth3 -nn host 10.100.1.254 and port 80 + +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on eth3, link-type EN10MB (Ethernet), capture size 262144 bytes +18:59:27.140269 IP 136.178.185.105.0 > 10.100.1.254.80: Flags [S], seq 1, win 10, length 0 +18:59:27.140275 IP 225.67.11.246.0 > 10.100.1.254.80: Flags [S], seq 1, win 10, length 0 +18:59:27.140279 IP 12.164.180.121.0 > 10.100.1.254.80: Flags [S], seq 1, win 10, length 0 +... ``` ## Explanation -The application schedules a periodic pipeline on port `eth1`'s assigned core `1`. The pipeline will repeat every 10 milliseconds. Instead of receiving packets from the port, the pipeline uses `batch::poll_fn` to generate a batch of new SYN packets each iteration and sends them to the interface `eth3` with assigned IP `10.100.1.255` on port `80`. Every packet is assigned a different spoofed source IP address. +`cap0` is configured to transmit on lcore `0` with queue depth set at `2048`. -On the main core `0`, a scheduled task prints out the port metrics once every second. +The example spawns a separate worker task on lcore `1` that will at 50ms interval generate a batch of 128 TCP SYN packets and send them through `cap0`. Each generated TCP SYN will have a random source IP address. The destination is set to `10.100.1.254` on port `80`, which is the address of the `eth3` interface on the host. (On a side note, the 50ms delay is necessary because emulated `virtio` driver is too slow on tx. Without a delay, the mempool is exhausted.) +```bash +vagrant$ ip addr show dev eth3 + +5: eth3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + link/ether 02:00:00:ff:ff:ff brd ff:ff:ff:ff:ff:ff + inet 10.100.1.254/24 brd 10.100.1.255 scope global eth3 + valid_lft forever preferred_lft forever ``` ---- -capsule: - port: - "dropped{port=\"eth1\",dir=\"tx\"}": 87545 - "errors{port=\"eth1\",dir=\"tx\"}": 0 - "octets{port=\"eth1\",dir=\"tx\",core=\"1\"}": 16008570 - "packets{port=\"eth1\",dir=\"tx\",core=\"1\"}": 296455 -``` + +`ctrl-c` to stop the worker task and quit the application. diff --git a/examples/syn-flood/main.rs b/examples/syn-flood/main.rs index ee61243..c0062a4 100644 --- a/examples/syn-flood/main.rs +++ b/examples/syn-flood/main.rs @@ -17,63 +17,60 @@ */ use anyhow::Result; -use capsule::batch::{Batch, Pipeline}; -use capsule::config::load_config; -use capsule::metrics; +use async_io::Timer; use capsule::net::MacAddr; use capsule::packets::ip::v4::Ipv4; -use capsule::packets::{Ethernet, Packet, Tcp4}; -use capsule::{batch, Mbuf, PortQueue, Runtime}; -use metrics_core::{Builder, Drain, Observe}; -use metrics_observer_yaml::YamlBuilder; -use std::collections::HashMap; +use capsule::packets::{Ethernet, Mbuf, Packet, Tcp4}; +use capsule::rt2::{self, Outbox, Runtime}; +use futures_lite::stream::StreamExt; +use signal_hook::consts; +use signal_hook::flag; use std::net::Ipv4Addr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::time::Duration; -use tracing::{debug, error, Level}; +use tracing::{error, info, Level}; use tracing_subscriber::fmt; -fn install(qs: HashMap) -> impl Pipeline { - let src_mac = qs["eth1"].mac_addr(); - let dst_ip = Ipv4Addr::new(10, 100, 1, 255); +async fn syn_flood(src_mac: MacAddr, cap0: Outbox, term: Arc) { + let dst_ip = Ipv4Addr::new(10, 100, 1, 254); let dst_mac = MacAddr::new(0x02, 0x00, 0x00, 0xff, 0xff, 0xff); - // starts the src ip at 10.0.0.0 - let mut next_ip = 10u32 << 24; - - batch::poll_fn(|| { - Mbuf::alloc_bulk(128).unwrap_or_else(|err| { - error!(?err); - vec![] - }) - }) - .map(move |packet| { - let mut ethernet = packet.push::()?; - ethernet.set_src(src_mac); - ethernet.set_dst(dst_mac); - - // +1 to gen the next ip - next_ip += 1; - - let mut v4 = ethernet.push::()?; - v4.set_src(next_ip.into()); - v4.set_dst(dst_ip); - - let mut tcp = v4.push::()?; - tcp.set_syn(); - tcp.set_seq_no(1); - tcp.set_window(10); - tcp.set_dst_port(80); - tcp.reconcile_all(); - - Ok(tcp) - }) - .send(qs["eth1"].clone()) -} + // 50ms delay between batches. + let mut timer = Timer::interval(Duration::from_millis(50)); + + while !term.load(Ordering::Relaxed) { + let _ = timer.next().await; + info!("generating 128 SYN packets."); + + match Mbuf::alloc_bulk(128) { + Ok(mbufs) => mbufs + .into_iter() + .map(|mbuf| -> Result { + let mut ethernet = mbuf.push::()?; + ethernet.set_src(src_mac); + ethernet.set_dst(dst_mac); + + let mut v4 = ethernet.push::()?; + v4.set_src(rand::random::().into()); + v4.set_dst(dst_ip); + + let mut tcp = v4.push::()?; + tcp.set_syn(); + tcp.set_seq_no(1); + tcp.set_window(10); + tcp.set_dst_port(80); + tcp.reconcile_all(); -fn print_stats() { - let mut observer = YamlBuilder::new().build(); - metrics::global().controller().observe(&mut observer); - println!("{}", observer.drain()); + Ok(tcp.reset()) + }) + .filter_map(|res| res.ok()) + .for_each(|mbuf| { + let _ = cap0.push(mbuf); + }), + Err(err) => error!(?err), + } + } } fn main() -> Result<()> { @@ -82,11 +79,25 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = load_config()?; - debug!(?config); + let config = rt2::load_config()?; + let runtime = Runtime::from_config(config)?; + + let term = Arc::new(AtomicBool::new(false)); + + let cap0 = runtime.ports().get("cap0")?; + let outbox = cap0.outbox()?; + let src_mac = cap0.mac_addr(); + + runtime + .lcores() + .get(1)? + .spawn(syn_flood(src_mac, outbox, term.clone())); + + let _guard = runtime.execute()?; + + flag::register(consts::SIGINT, Arc::clone(&term))?; + info!("ctrl-c to quit ..."); + while !term.load(Ordering::Relaxed) {} - Runtime::build(config)? - .add_periodic_pipeline_to_core(1, install, Duration::from_millis(10))? - .add_periodic_task_to_core(0, print_stats, Duration::from_secs(1))? - .execute() + Ok(()) } diff --git a/examples/syn-flood/syn-flood.toml b/examples/syn-flood/syn-flood.toml index 1570e04..1d9c010 100644 --- a/examples/syn-flood/syn-flood.toml +++ b/examples/syn-flood/syn-flood.toml @@ -1,13 +1,13 @@ app_name = "syn-flood" -master_core = 0 +main_core = 0 +worker_cores = [1] [mempool] capacity = 65535 cache_size = 256 [[ports]] - name = "eth1" + name = "cap0" device = "0000:00:08.0" - cores = [1] - rxd = 512 - txd = 512 + tx_cores = [0] + txd = 2048 From 58d428cc8081ded89732243e319faf718882997d Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Mon, 29 Mar 2021 16:33:28 -0400 Subject: [PATCH 09/18] stinky pcap dump --- core/src/ffi/dpdk.rs | 73 +++++++++++++ core/src/ffi/mod.rs | 2 + core/src/ffi/pcap.rs | 156 +++++++++++++++++++++++++++ core/src/rt2/config.rs | 35 +++++- core/src/rt2/mod.rs | 15 ++- core/src/rt2/pcap_dump.rs | 218 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 493 insertions(+), 6 deletions(-) create mode 100644 core/src/ffi/pcap.rs create mode 100644 core/src/rt2/pcap_dump.rs diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 3f23299..1f54ff3 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -125,6 +125,7 @@ pub(crate) fn pktmbuf_pool_create>( } /// Looks up a mempool by the name. +#[cfg(test)] pub(crate) fn mempool_lookup>(name: S) -> Result { let name: String = name.into(); @@ -407,6 +408,57 @@ pub(crate) fn eth_rx_queue_setup( } } +/// Removes an RX or TX packet callback from a given port and queue. +#[allow(dead_code)] +pub(crate) enum RxTxCallbackGuard { + Rx(PortId, PortRxQueueId, *const cffi::rte_eth_rxtx_callback), + Tx(PortId, PortTxQueueId, *const cffi::rte_eth_rxtx_callback), +} + +impl Drop for RxTxCallbackGuard { + fn drop(&mut self) { + if let Err(error) = match self { + RxTxCallbackGuard::Rx(port_id, queue_id, ptr) => { + debug!(port = ?port_id, rxq = ?queue_id, "remove rx callback."); + unsafe { + cffi::rte_eth_remove_rx_callback(port_id.0, queue_id.0, *ptr) + .into_result(DpdkError::from_errno) + } + } + RxTxCallbackGuard::Tx(port_id, queue_id, ptr) => { + debug!(port = ?port_id, txq = ?queue_id, "remove tx callback."); + unsafe { + cffi::rte_eth_remove_tx_callback(port_id.0, queue_id.0, *ptr) + .into_result(DpdkError::from_errno) + } + } + } { + error!(?error); + } + } +} + +/// Adds a callback to be called on packet RX on a given port and queue. +#[allow(dead_code)] +pub(crate) fn eth_add_rx_callback( + port_id: PortId, + queue_id: PortRxQueueId, + callback: cffi::rte_rx_callback_fn, + user_param: &mut T, +) -> Result { + let ptr = unsafe { + cffi::rte_eth_add_rx_callback( + port_id.0, + queue_id.0, + callback, + user_param as *mut T as *mut raw::c_void, + ) + .into_result(|_| DpdkError::new())? + }; + + Ok(RxTxCallbackGuard::Rx(port_id, queue_id, ptr)) +} + /// Retrieves a burst of input packets from a receive queue of a device. pub(crate) fn eth_rx_burst(port_id: PortId, queue_id: PortRxQueueId, rx_pkts: &mut Vec) { let nb_pkts = rx_pkts.capacity(); @@ -460,6 +512,27 @@ pub(crate) fn eth_tx_queue_setup( } } +/// Adds a callback to be called on packet TX on a given port and queue. +#[allow(dead_code)] +pub(crate) fn eth_add_tx_callback( + port_id: PortId, + queue_id: PortTxQueueId, + callback: cffi::rte_tx_callback_fn, + user_param: &mut T, +) -> Result { + let ptr = unsafe { + cffi::rte_eth_add_tx_callback( + port_id.0, + queue_id.0, + callback, + user_param as *mut T as *mut raw::c_void, + ) + .into_result(|_| DpdkError::new())? + }; + + Ok(RxTxCallbackGuard::Tx(port_id, queue_id, ptr)) +} + /// Sends a burst of output packets on a transmit queue of a device. pub(crate) fn eth_tx_burst( port_id: PortId, diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index f643607..b0cac1a 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -17,6 +17,8 @@ */ pub(crate) mod dpdk; +#[cfg(feature = "pcap-dump")] +pub(crate) mod pcap; pub(crate) use capsule_ffi::*; diff --git a/core/src/ffi/pcap.rs b/core/src/ffi/pcap.rs new file mode 100644 index 0000000..812b043 --- /dev/null +++ b/core/src/ffi/pcap.rs @@ -0,0 +1,156 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use super::{AsStr, EasyPtr, ToCString, ToResult}; +use crate::ffi::dpdk::MbufPtr; +use anyhow::Result; +use capsule_ffi as cffi; +use libc; +use std::ops::DerefMut; +use std::os::raw; +use std::ptr; +use thiserror::Error; + +// Ethernet (10Mb, 100Mb, 1000Mb, and up); the 10MB in the DLT_ name is historical. +const DLT_EN10MB: raw::c_int = 1; + +// https://github.com/the-tcpdump-group/libpcap/blob/master/pcap/pcap.h#L152 +#[allow(dead_code)] +const PCAP_ERRBUF_SIZE: usize = 256; + +/// A `pcap_t` pointer. +pub(crate) type PcapPtr = EasyPtr; + +/// Creates a `libpcap` handle needed to call other functions. +pub(crate) fn open_dead() -> Result { + let ptr = unsafe { + cffi::pcap_open_dead(DLT_EN10MB, cffi::RTE_MBUF_DEFAULT_BUF_SIZE as raw::c_int) + .into_result(|_| PcapError::new("Cannot create libpcap handle."))? + }; + + Ok(EasyPtr(ptr)) +} + +/// A `pcap_dumper_t` pointer. +pub(crate) type DumperPtr = EasyPtr; + +/// Opens a file to which to write packets. +pub(crate) fn dump_open>(handle: &mut PcapPtr, filename: S) -> Result { + let filename: String = filename.into(); + let ptr = unsafe { + cffi::pcap_dump_open(handle.deref_mut(), filename.into_cstring().as_ptr()) + .into_result(|_| PcapError::get_error(handle))? + }; + + Ok(EasyPtr(ptr)) +} + +/// Writes a packet to a capture file. +pub(crate) fn dump(dumper: &mut DumperPtr, mbuf: &MbufPtr) { + let mut pkthdr = cffi::pcap_pkthdr::default(); + pkthdr.len = mbuf.data_len as u32; + pkthdr.caplen = pkthdr.len; + + unsafe { + // If this errors, we'll still want to write packet(s) to the pcap, + let _ = libc::gettimeofday( + &mut pkthdr.ts as *mut cffi::timeval as *mut libc::timeval, + ptr::null_mut(), + ); + + cffi::pcap_dump( + dumper.deref_mut() as *mut cffi::pcap_dumper_t as *mut raw::c_uchar, + &pkthdr, + (mbuf.buf_addr as *mut u8).offset(mbuf.data_off as isize), + ); + } +} + +/// Flushes to a savefile packets dumped. +pub(crate) fn dump_flush(dumper: &mut DumperPtr) -> Result<()> { + unsafe { + cffi::pcap_dump_flush(dumper.deref_mut()) + .into_result(|_| PcapError::new("Cannot flush packets to capture file.")) + .map(|_| ()) + } +} + +/// Closes a savefile being written to. +pub(crate) fn dump_close(dumper: &mut DumperPtr) { + unsafe { + cffi::pcap_dump_close(dumper.deref_mut()); + } +} + +/// Closes a capture device or savefile +pub(crate) fn close(handle: &mut PcapPtr) { + unsafe { + cffi::pcap_close(handle.deref_mut()); + } +} + +/// Opens a saved capture file for reading. +#[cfg(test)] +pub(crate) fn open_offline>(filename: S) -> Result { + let filename: String = filename.into(); + let mut errbuf: [raw::c_char; PCAP_ERRBUF_SIZE] = [0; PCAP_ERRBUF_SIZE]; + + let ptr = unsafe { + cffi::pcap_open_offline(filename.into_cstring().as_ptr(), errbuf.as_mut_ptr()) + .into_result(|_| PcapError::new(errbuf.as_str()))? + }; + + Ok(EasyPtr(ptr)) +} + +/// Reads the next packet from a `pcap_t` handle. +#[cfg(test)] +pub(crate) fn next(handle: &mut PcapPtr) -> Result<&[u8]> { + let mut pkthdr: *mut cffi::pcap_pkthdr = ptr::null_mut(); + let mut pktdata: *const raw::c_uchar = ptr::null(); + + unsafe { + match cffi::pcap_next_ex(handle.deref_mut(), &mut pkthdr, &mut pktdata) { + 1 => Ok(std::slice::from_raw_parts( + pktdata, + (*pkthdr).caplen as usize, + )), + _ => Err(PcapError::get_error(handle).into()), + } + } +} + +/// An error generated in `libpcap`. +#[derive(Debug, Error)] +#[error("{0}")] +pub(crate) struct PcapError(String); + +impl PcapError { + /// Returns the `PcapError` with the given error message. + #[inline] + fn new(msg: &str) -> Self { + PcapError(msg.into()) + } + + /// Returns the `PcapError` pertaining to the last `libpcap` error. + #[inline] + fn get_error(handle: &mut PcapPtr) -> Self { + let msg = unsafe { cffi::pcap_geterr(handle.deref_mut()) }; + PcapError::new((msg as *const raw::c_char).as_str()) + } +} diff --git a/core/src/rt2/config.rs b/core/src/rt2/config.rs index bbb70fe..c5b201e 100644 --- a/core/src/rt2/config.rs +++ b/core/src/rt2/config.rs @@ -86,6 +86,12 @@ pub struct RuntimeConfig { /// execution. pub worker_cores: Vec, + /// The root data directory the application writes to. + /// + /// If unset, the default is `/var/capsule/{app_name}`. + #[serde(default)] + pub data_dir: Option, + /// Per mempool settings. On a system with multiple sockets, aka NUMA /// nodes, one mempool will be allocated for each socket the apllication /// uses. @@ -193,6 +199,18 @@ impl RuntimeConfig { eal_args } + + /// Returns the data directory. + #[allow(dead_code)] + pub(crate) fn data_dir(&self) -> String { + self.data_dir.clone().unwrap_or_else(|| { + let base_dir = "/var/capsule"; + match &self.app_group { + Some(group) => format!("{}/{}/{}", base_dir, group, self.app_name), + None => format!("{}/{}", base_dir, self.app_name), + } + }) + } } impl fmt::Debug for RuntimeConfig { @@ -367,7 +385,7 @@ mod tests { use super::*; #[test] - fn config_defaults() { + fn config_defaults() -> Result<()> { const CONFIG: &str = r#" app_name = "myapp" main_core = 0 @@ -377,10 +395,11 @@ mod tests { device = "0000:00:01.0" "#; - let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); + let config: RuntimeConfig = toml::from_str(CONFIG)?; assert_eq!(false, config.secondary); assert_eq!(None, config.app_group); + assert_eq!(None, config.data_dir); assert_eq!(None, config.dpdk_args); assert_eq!(default_capacity(), config.mempool.capacity); assert_eq!(default_cache_size(), config.mempool.cache_size); @@ -391,10 +410,14 @@ mod tests { assert_eq!(default_port_txqs(), config.ports[0].txqs); assert_eq!(default_promiscuous_mode(), config.ports[0].promiscuous); assert_eq!(default_multicast_mode(), config.ports[0].multicast); + + assert_eq!("/var/capsule/myapp", &config.data_dir()); + + Ok(()) } #[test] - fn config_to_eal_args() { + fn config_to_eal_args() -> Result<()> { const CONFIG: &str = r#" app_name = "myapp" secondary = false @@ -421,7 +444,7 @@ mod tests { txqs = 32 "#; - let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); + let config: RuntimeConfig = toml::from_str(CONFIG)?; assert_eq!( &[ @@ -443,6 +466,8 @@ mod tests { "eal:8" ], config.to_eal_args().as_slice(), - ) + ); + + Ok(()) } } diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs index 8894444..cd63ef9 100644 --- a/core/src/rt2/mod.rs +++ b/core/src/rt2/mod.rs @@ -21,6 +21,9 @@ mod config; mod lcore; mod mempool; +#[cfg(feature = "pcap-dump")] +#[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] +mod pcap_dump; mod port; pub use self::config::*; @@ -89,6 +92,8 @@ pub struct Runtime { mempool: ManuallyDrop, lcores: ManuallyDrop, ports: ManuallyDrop, + #[cfg(feature = "pcap-dump")] + pcap_dump: ManuallyDrop, } impl Runtime { @@ -155,13 +160,19 @@ impl Runtime { port.start()?; ports.push(port); } + let ports: PortMap = ports.into(); + + #[cfg(feature = "pcap-dump")] + let pcap_dump = self::pcap_dump::enable_pcap_dump(&config.data_dir(), &ports, &lcores)?; info!("runtime ready."); Ok(Runtime { mempool: ManuallyDrop::new(mempool), lcores: ManuallyDrop::new(lcores), - ports: ManuallyDrop::new(ports.into()), + ports: ManuallyDrop::new(ports), + #[cfg(feature = "pcap-dump")] + pcap_dump: ManuallyDrop::new(pcap_dump), }) } @@ -203,6 +214,8 @@ impl Drop for RuntimeGuard { } unsafe { + #[cfg(feature = "pcap-dump")] + ManuallyDrop::drop(&mut self.runtime.pcap_dump); ManuallyDrop::drop(&mut self.runtime.ports); ManuallyDrop::drop(&mut self.runtime.lcores); ManuallyDrop::drop(&mut self.runtime.mempool); diff --git a/core/src/rt2/pcap_dump.rs b/core/src/rt2/pcap_dump.rs new file mode 100644 index 0000000..8c0a39b --- /dev/null +++ b/core/src/rt2/pcap_dump.rs @@ -0,0 +1,218 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use super::{LcoreMap, PortMap}; +use crate::ffi::dpdk::{self, MbufPtr, RxTxCallbackGuard}; +use crate::ffi::pcap::{self, DumperPtr, PcapPtr}; +use crate::{info, warn}; +use anyhow::{anyhow, Result}; +use capsule_ffi as cffi; +use std::fs; +use std::os::raw; +use std::path::Path; +use std::slice; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Manages the lifecycle of a capture file. +struct CaptureFile { + path: String, + handle: PcapPtr, + dumper: DumperPtr, + guard: Option, +} + +impl CaptureFile { + /// Creates a new pcap file. + fn new(path: &str) -> Result { + let mut handle = pcap::open_dead()?; + let dumper = pcap::dump_open(&mut handle, path)?; + info!(file = ?path, "file opened."); + Ok(CaptureFile { + path: path.to_string(), + handle, + dumper, + guard: None, + }) + } + + /// Sets the RAII guard. + fn set_guard(&mut self, guard: RxTxCallbackGuard) { + self.guard = Some(guard); + } +} + +impl Drop for CaptureFile { + fn drop(&mut self) { + if let Some(guard) = self.guard.take() { + // unwires the rx/tx callback first. + drop(guard); + } + + pcap::dump_close(&mut self.dumper); + pcap::close(&mut self.handle); + info!(file = ?self.path, "file closed."); + } +} + +/// The pcap dump manager. +pub(crate) struct PcapDump { + output_dir: String, + captures: Vec>, +} + +impl PcapDump { + /// Creates a new instance. + pub(crate) fn new(data_dir: &str) -> Result { + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); + let path = Path::new(data_dir) + .join("pdump") + .join(timestamp.to_string()); + fs::create_dir_all(path.clone())?; + let output_dir = path + .to_str() + .ok_or_else(|| anyhow!("bad pdump output dir."))? + .to_string(); + + Ok(PcapDump { + output_dir, + captures: vec![], + }) + } + + /// Creates a new capture file. + fn new_capture(&mut self, filename: &str) -> Result<&mut Box> { + let path = format!("{}/{}", self.output_dir, filename); + let capture = CaptureFile::new(&path)?; + self.captures.push(Box::new(capture)); + Ok(self.captures.last_mut().unwrap()) + } +} + +/// Enables the pcap dump. +pub(crate) fn enable_pcap_dump( + data_dir: &str, + ports: &PortMap, + lcores: &LcoreMap, +) -> Result { + info!("enabling pcap dump ..."); + + let mut pcap_dump = PcapDump::new(data_dir)?; + + for port in ports.iter() { + for (index, lcore_id) in port.rx_lcores().iter().enumerate() { + let lcore = lcores.get(*lcore_id)?; + let filename = format!("{}-rx-{:?}.pcap", port.name(), lcore.id()); + let capture = pcap_dump.new_capture(&filename)?; + let guard = dpdk::eth_add_rx_callback( + port.port_id(), + index.into(), + Some(rx_callback_fn), + capture.as_mut(), + )?; + capture.set_guard(guard); + } + + for (index, lcore_id) in port.tx_lcores().iter().enumerate() { + let lcore = lcores.get(*lcore_id)?; + let filename = format!("{}-tx-{:?}.pcap", port.name(), lcore.id()); + let capture = pcap_dump.new_capture(&filename)?; + let guard = dpdk::eth_add_tx_callback( + port.port_id(), + index.into(), + Some(tx_callback_fn), + capture.as_mut(), + )?; + capture.set_guard(guard); + } + } + + Ok(pcap_dump) +} + +fn dump_mbufs(dumper: &mut DumperPtr, mbufs: &[MbufPtr]) { + for mbuf in mbufs { + pcap::dump(dumper, mbuf); + } + + if let Err(error) = pcap::dump_flush(dumper) { + warn!(?error); + } +} + +unsafe extern "C" fn rx_callback_fn( + _port_id: u16, + _queue_id: u16, + pkts: *mut *mut cffi::rte_mbuf, + num_pkts: u16, + _max_pkts: u16, + user_param: *mut raw::c_void, +) -> u16 { + let capture = Box::leak(Box::from_raw(user_param as *mut CaptureFile)); + let mbufs = slice::from_raw_parts_mut(pkts as *mut MbufPtr, num_pkts as usize); + dump_mbufs(&mut capture.dumper, &mbufs); + num_pkts +} + +unsafe extern "C" fn tx_callback_fn( + _port_id: u16, + _queue_id: u16, + pkts: *mut *mut cffi::rte_mbuf, + num_pkts: u16, + user_param: *mut raw::c_void, +) -> u16 { + let capture = Box::leak(Box::from_raw(user_param as *mut CaptureFile)); + let mbufs = slice::from_raw_parts_mut(pkts as *mut MbufPtr, num_pkts as usize); + dump_mbufs(&mut capture.dumper, &mbufs); + num_pkts +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::packets::Mbuf; + use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; + + #[capsule::test] + fn dump_mbufs_to_file() -> Result<()> { + let filename = "file.pcap"; + let mut capture = CaptureFile::new(filename)?; + + let tcp = Mbuf::from_bytes(&IPV4_TCP_PACKET)?; + let udp = Mbuf::from_bytes(&IPV4_UDP_PACKET)?; + + dump_mbufs( + &mut capture.dumper, + &[tcp.into_easyptr(), udp.into_easyptr()], + ); + + drop(capture); + + // reads the packets from file and assert they are the same. + let mut h2 = pcap::open_offline(filename)?; + let packet = pcap::next(&mut h2)?; + assert_eq!(&IPV4_TCP_PACKET, packet); + let packet = pcap::next(&mut h2)?; + assert_eq!(&IPV4_UDP_PACKET, packet); + + pcap::close(&mut h2); + + fs::remove_file(filename)?; + + Ok(()) + } +} From b06c55af831bb6665fac507f85e2008f74507d0d Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Mon, 29 Mar 2021 18:37:25 -0400 Subject: [PATCH 10/18] spring cleaning --- bench/Cargo.toml | 7 +- bench/combinators.rs | 229 ------ bench/mbuf.rs | 2 +- bench/packets.rs | 4 +- core/Cargo.toml | 11 +- core/src/batch/emit.rs | 56 -- core/src/batch/filter.rs | 69 -- core/src/batch/filter_map.rs | 82 -- core/src/batch/for_each.rs | 63 -- core/src/batch/group_by.rs | 207 ----- core/src/batch/inspect.rs | 61 -- core/src/batch/map.rs | 67 -- core/src/batch/mod.rs | 736 ------------------ core/src/batch/poll.rs | 71 -- core/src/batch/replace.rs | 89 --- core/src/batch/rxtx.rs | 89 --- core/src/batch/send.rs | 151 ---- core/src/config.rs | 513 ------------ core/src/dpdk/kni.rs | 411 ---------- core/src/dpdk/mempool.rs | 180 ----- core/src/dpdk/mod.rs | 244 ------ core/src/dpdk/port.rs | 651 ---------------- core/src/dpdk/stats.rs | 130 ---- core/src/ffi/dpdk.rs | 8 +- core/src/ffi/mod.rs | 2 - core/src/lib.rs | 18 +- core/src/metrics.rs | 140 ---- core/src/packets/arp.rs | 18 +- core/src/packets/ethernet.rs | 9 +- core/src/packets/icmp/v4/echo_reply.rs | 6 +- core/src/packets/icmp/v4/echo_request.rs | 6 +- core/src/packets/icmp/v4/mod.rs | 7 +- core/src/packets/icmp/v4/redirect.rs | 6 +- core/src/packets/icmp/v4/time_exceeded.rs | 6 +- core/src/packets/icmp/v6/echo_reply.rs | 6 +- core/src/packets/icmp/v6/echo_request.rs | 6 +- core/src/packets/icmp/v6/mod.rs | 11 +- core/src/packets/icmp/v6/ndp/mod.rs | 17 +- .../packets/icmp/v6/ndp/neighbor_advert.rs | 6 +- .../packets/icmp/v6/ndp/neighbor_solicit.rs | 6 +- .../icmp/v6/ndp/options/link_layer_addr.rs | 4 +- core/src/packets/icmp/v6/ndp/options/mtu.rs | 4 +- .../icmp/v6/ndp/options/prefix_info.rs | 4 +- .../packets/icmp/v6/ndp/options/redirected.rs | 4 +- core/src/packets/icmp/v6/ndp/redirect.rs | 6 +- core/src/packets/icmp/v6/ndp/router_advert.rs | 6 +- .../src/packets/icmp/v6/ndp/router_solicit.rs | 6 +- core/src/packets/icmp/v6/time_exceeded.rs | 6 +- core/src/packets/icmp/v6/too_big.rs | 6 +- ...tination_unreachable.rs => unreachable.rs} | 6 +- core/src/packets/ip/v4.rs | 6 +- core/src/packets/ip/v6/fragment.rs | 7 +- core/src/packets/ip/v6/mod.rs | 6 +- core/src/packets/ip/v6/srh.rs | 7 +- core/src/{dpdk => packets}/mbuf.rs | 75 +- core/src/packets/mod.rs | 6 +- core/src/packets/size_of.rs | 85 ++ core/src/packets/tcp.rs | 7 +- core/src/packets/udp.rs | 7 +- core/src/pcap.rs | 340 -------- core/src/rt2/mod.rs | 234 ------ core/src/{rt2 => runtime}/config.rs | 0 core/src/runtime/core_map.rs | 386 --------- core/src/{rt2 => runtime}/lcore.rs | 0 core/src/{rt2 => runtime}/mempool.rs | 0 core/src/runtime/mod.rs | 718 ++++------------- core/src/{rt2 => runtime}/pcap_dump.rs | 0 core/src/{rt2 => runtime}/port.rs | 0 core/src/testils/criterion.rs | 55 -- core/src/testils/mod.rs | 4 +- core/src/testils/proptest/arbitrary.rs | 2 +- core/src/testils/proptest/strategy.rs | 3 +- examples/kni/Cargo.toml | 4 +- examples/kni/main.rs | 4 +- examples/nat64/Cargo.toml | 4 +- examples/nat64/main.rs | 4 +- examples/ping4d/Cargo.toml | 4 +- examples/ping4d/main.rs | 4 +- examples/pktdump/Cargo.toml | 4 +- examples/pktdump/main.rs | 4 +- examples/skeleton/Cargo.toml | 4 +- examples/skeleton/main.rs | 4 +- examples/syn-flood/Cargo.toml | 4 +- examples/syn-flood/main.rs | 4 +- ffi/Cargo.toml | 2 +- macros/Cargo.toml | 2 +- macros/src/derive_packet.rs | 2 + macros/src/lib.rs | 2 +- 88 files changed, 371 insertions(+), 6086 deletions(-) delete mode 100644 bench/combinators.rs delete mode 100644 core/src/batch/emit.rs delete mode 100644 core/src/batch/filter.rs delete mode 100644 core/src/batch/filter_map.rs delete mode 100644 core/src/batch/for_each.rs delete mode 100644 core/src/batch/group_by.rs delete mode 100644 core/src/batch/inspect.rs delete mode 100644 core/src/batch/map.rs delete mode 100644 core/src/batch/mod.rs delete mode 100644 core/src/batch/poll.rs delete mode 100644 core/src/batch/replace.rs delete mode 100644 core/src/batch/rxtx.rs delete mode 100644 core/src/batch/send.rs delete mode 100644 core/src/config.rs delete mode 100644 core/src/dpdk/kni.rs delete mode 100644 core/src/dpdk/mempool.rs delete mode 100644 core/src/dpdk/mod.rs delete mode 100644 core/src/dpdk/port.rs delete mode 100644 core/src/dpdk/stats.rs delete mode 100644 core/src/metrics.rs rename core/src/packets/icmp/v6/{destination_unreachable.rs => unreachable.rs} (98%) rename core/src/{dpdk => packets}/mbuf.rs (92%) create mode 100644 core/src/packets/size_of.rs delete mode 100644 core/src/pcap.rs delete mode 100644 core/src/rt2/mod.rs rename core/src/{rt2 => runtime}/config.rs (100%) delete mode 100644 core/src/runtime/core_map.rs rename core/src/{rt2 => runtime}/lcore.rs (100%) rename core/src/{rt2 => runtime}/mempool.rs (100%) rename core/src/{rt2 => runtime}/pcap_dump.rs (100%) rename core/src/{rt2 => runtime}/port.rs (100%) diff --git a/bench/Cargo.toml b/bench/Cargo.toml index e9d2360..2a4de6b 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -11,7 +11,7 @@ Benchmarks for Capsule. [dev-dependencies] anyhow = "1.0" -capsule = { version = "0.1", path = "../core", features = ["testils"] } +capsule = { version = "0.2", path = "../core", features = ["testils"] } criterion = "0.3" proptest = "1.0" @@ -20,11 +20,6 @@ name = "packets" path = "packets.rs" harness = false -[[bench]] -name = "combinators" -path = "combinators.rs" -harness = false - [[bench]] name = "mbuf" path = "mbuf.rs" diff --git a/bench/combinators.rs b/bench/combinators.rs deleted file mode 100644 index 2866bc5..0000000 --- a/bench/combinators.rs +++ /dev/null @@ -1,229 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use anyhow::Result; -use capsule::batch::{Batch, Either}; -use capsule::packets::ip::v4::Ipv4; -use capsule::packets::{Ethernet, Packet}; -use capsule::testils::criterion::BencherExt; -use capsule::testils::proptest::*; -use capsule::{compose, Mbuf}; -use criterion::{criterion_group, criterion_main, Criterion}; -use proptest::prelude::*; -use proptest::strategy; - -const BATCH_SIZE: usize = 500; - -fn filter_true(batch: impl Batch) -> impl Batch { - batch.filter(|_p| true) -} - -fn filter_false(batch: impl Batch) -> impl Batch { - batch.filter(|_p| false) -} - -#[capsule::bench(mempool_capacity = 511)] -fn filters_batch(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::filter"); - - group.bench_function("combinators::filter_true", |b| { - let s = any::(); - b.iter_proptest_combinators(s, filter_true, BATCH_SIZE) - }); - - group.bench_function("combinators::filter_false", |b| { - let s = any::(); - b.iter_proptest_combinators(s, filter_false, BATCH_SIZE) - }); - - group.finish() -} - -fn filter_map(batch: impl Batch) -> impl Batch { - batch.filter_map(|p| { - let ethernet = p.parse::()?; - Ok(Either::Keep(ethernet)) - }) -} - -fn map_then_filter(batch: impl Batch) -> impl Batch { - batch.map(|p| p.parse::()).filter(|_p| true) -} - -#[capsule::bench(mempool_capacity = 511)] -fn filter_map_vs_map_then_filter_batch(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::filter_map_vs_map_then_filter"); - - group.bench_function("combinators::filter_map", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, filter_map, BATCH_SIZE) - }); - - group.bench_function("combinators::map_then_filter", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map_then_filter, BATCH_SIZE) - }); - - group.finish() -} - -fn map(batch: impl Batch) -> impl Batch { - batch.map(|p| p.parse::()) -} - -fn no_batch_map(mbuf: Mbuf) -> Result { - mbuf.parse::() -} - -#[capsule::bench(mempool_capacity = 511)] -fn map_batch_vs_parse(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::map_batch_vs_parse"); - - group.bench_function("combinators::map", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, BATCH_SIZE) - }); - - group.bench_function("combinators::no_batch_map", |b| { - let s = v4_udp(); - b.iter_proptest_batched(s, no_batch_map, BATCH_SIZE) - }); -} - -#[capsule::bench(mempool_capacity = 1023)] -fn map_batches(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::map_on_diff_batch_sizes"); - - group.bench_function("combinators::map_10", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, 10) - }); - - group.bench_function("combinators::map_50", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, 50) - }); - - group.bench_function("combinators::map_150", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, 150) - }); - - group.bench_function("combinators::map_500", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, 500) - }); - - group.bench_function("combinators::map_1000", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map, 1000) - }); - - group.finish() -} - -fn map_parse_errors(batch: impl Batch) -> impl Batch { - batch.map(|p| p.parse::()?.parse::()) -} - -#[capsule::bench(mempool_capacity = 511)] -fn map_errors(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::map_errors_vs_no_errors"); - - group.bench_function("combinators::map_no_errors", |b| { - let s = v4_udp(); - b.iter_proptest_combinators(s, map_parse_errors, BATCH_SIZE) - }); - - group.bench_function("combinators::map_with_errors", |b| { - let s = strategy::Union::new_weighted(vec![(8, v4_udp().boxed()), (2, v6_udp().boxed())]); - b.iter_proptest_combinators(s, map_parse_errors, BATCH_SIZE) - }); -} - -static mut COUNTER: u32 = 0; -fn group_by(batch: impl Batch) -> impl Batch { - unsafe { COUNTER += 1 }; - - unsafe { - batch.group_by( - |_p| COUNTER % 2, - |groups| { - compose!(groups { - 0 => |group| { - group - } - _ => |group| { - group - } - }) - }, - ) - } -} - -#[capsule::bench(mempool_capacity = 511)] -fn group_by_batch(c: &mut Criterion) { - c.bench_function("combinators::group_by", |b| { - let s = any::(); - b.iter_proptest_combinators(s, group_by, BATCH_SIZE) - }); -} - -fn replace(batch: impl Batch) -> impl Batch { - batch.replace(|_p| Mbuf::new()) -} - -fn no_batch_replace(_mbuf: Mbuf) -> Result { - Mbuf::new() -} - -#[capsule::bench(mempool_capacity = 511)] -fn replace_batch(c: &mut Criterion) { - let mut group = c.benchmark_group("combinators::replace_with_new_mbuf_vs_create_new_mbuf"); - - group.bench_function("combinators::replace", |b| { - let s = any::(); - b.iter_proptest_combinators(s, replace, BATCH_SIZE) - }); - - group.bench_function("combinators::no_batch_replace", |b| { - let s = any::(); - b.iter_proptest_batched(s, no_batch_replace, BATCH_SIZE) - }); - - group.finish() -} - -fn bench_config() -> Criterion { - Criterion::default().with_plots() -} - -criterion_group! { - name = benches; - config=bench_config(); - targets=filters_batch, - filter_map_vs_map_then_filter_batch, - map_batch_vs_parse, - group_by_batch, - replace_batch, - map_batches, - map_errors, -} - -criterion_main!(benches); diff --git a/bench/mbuf.rs b/bench/mbuf.rs index c8258e0..62f0ceb 100644 --- a/bench/mbuf.rs +++ b/bench/mbuf.rs @@ -17,7 +17,7 @@ */ use anyhow::Result; -use capsule::Mbuf; +use capsule::packets::Mbuf; use criterion::{criterion_group, criterion_main, Criterion}; const BATCH_SIZE: usize = 100; diff --git a/bench/packets.rs b/bench/packets.rs index a7a381c..35aecd6 100644 --- a/bench/packets.rs +++ b/bench/packets.rs @@ -17,13 +17,13 @@ */ use anyhow::Result; +use capsule::fieldmap; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::{Ipv6, SegmentRouting}; -use capsule::packets::{Ethernet, Packet, Udp4}; +use capsule::packets::{Ethernet, Mbuf, Packet, Udp4}; use capsule::testils::criterion::BencherExt; use capsule::testils::proptest::*; use capsule::testils::{PacketExt, Rvg}; -use capsule::{fieldmap, Mbuf}; use criterion::{criterion_group, criterion_main, Criterion}; use proptest::prelude::*; use std::net::Ipv6Addr; diff --git a/core/Cargo.toml b/core/Cargo.toml index abdfe1a..c2e8dac 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule" -version = "0.1.5" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -23,12 +23,11 @@ doctest = false anyhow = "1.0" async-channel = "1.6" async-executor = "1.4" -capsule-ffi = { version = "0.1.5", path = "../ffi" } -capsule-macros = { version = "0.1.5", path = "../macros" } +capsule-ffi = { version = "0.2.0", path = "../ffi" } +capsule-macros = { version = "0.2.0", path = "../macros" } clap = "2.33" criterion = { version = "0.3", optional = true } futures-lite = "1.11" -futures-preview = "=0.3.0-alpha.19" libc = "0.2" metrics-core = { version = "0.5", optional = true } metrics-runtime = { version = "0.13", optional = true, default-features = false } @@ -37,10 +36,6 @@ proptest = { version = "1.0", optional = true } regex = "1" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" -tokio = "=0.2.0-alpha.6" -tokio-executor = { version = "=0.2.0-alpha.6", features = ["current-thread", "threadpool"] } -tokio-net = { version = "=0.2.0-alpha.6", features = ["signal"] } -tokio-timer = "=0.3.0-alpha.6" toml = "0.5" tracing = "0.1" diff --git a/core/src/batch/emit.rs b/core/src/batch/emit.rs deleted file mode 100644 index a7c677f..0000000 --- a/core/src/batch/emit.rs +++ /dev/null @@ -1,56 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition, PacketTx}; -use crate::packets::Packet; - -/// A batch that transmits the packets through the specified [`PacketTx`]. -/// -/// [`PacketTx`]: crate::batch::PacketTx -#[allow(missing_debug_implementations)] -pub struct Emit { - batch: B, - tx: Tx, -} - -impl Emit { - /// Creates a new `Emit` batch. - #[inline] - pub fn new(batch: B, tx: Tx) -> Self { - Emit { batch, tx } - } -} - -impl Batch for Emit { - type Item = B::Item; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - disp.map(|pkt| { - self.tx.transmit(vec![pkt.reset()]); - Disposition::Emit - }) - }) - } -} diff --git a/core/src/batch/filter.rs b/core/src/batch/filter.rs deleted file mode 100644 index 119d14a..0000000 --- a/core/src/batch/filter.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use crate::packets::Packet; - -/// A batch that filters the packets of the underlying batch. -/// -/// If the predicate evaluates to `false`, the packet is marked as dropped -/// and will short-circuit the remainder of the pipeline. -#[allow(missing_debug_implementations)] -pub struct Filter -where - P: FnMut(&B::Item) -> bool, -{ - batch: B, - predicate: P, -} - -impl Filter -where - P: FnMut(&B::Item) -> bool, -{ - /// Creates a new `Filter` batch. - #[inline] - pub fn new(batch: B, predicate: P) -> Self { - Filter { batch, predicate } - } -} - -impl Batch for Filter -where - P: FnMut(&B::Item) -> bool, -{ - type Item = B::Item; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - disp.map(|pkt| { - if (self.predicate)(&pkt) { - Disposition::Act(pkt) - } else { - Disposition::Drop(pkt.reset()) - } - }) - }) - } -} diff --git a/core/src/batch/filter_map.rs b/core/src/batch/filter_map.rs deleted file mode 100644 index ceebafb..0000000 --- a/core/src/batch/filter_map.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use crate::packets::Packet; -use crate::Mbuf; -use anyhow::Result; - -/// The result of a [`filter_map`]. -/// -/// [`filter_map`]: crate::batch::Batch::filter_map -#[allow(missing_debug_implementations)] -pub enum Either { - /// Keeps the packet as mapped result. - Keep(T), - - /// Drops the packet. - Drop(Mbuf), -} - -/// A batch that both filters and maps the packets of the underlying batch. -/// -/// If the closure returns `Drop`, the packet is marked as dropped. On -/// error, the packet is marked as aborted. In both scenarios, it will -/// short-circuit the remainder of the pipeline. -#[allow(missing_debug_implementations)] -pub struct FilterMap -where - F: FnMut(B::Item) -> Result>, -{ - batch: B, - f: F, -} - -impl FilterMap -where - F: FnMut(B::Item) -> Result>, -{ - /// Creates a new `FilterMap` batch. - #[inline] - pub fn new(batch: B, f: F) -> Self { - FilterMap { batch, f } - } -} - -impl Batch for FilterMap -where - F: FnMut(B::Item) -> Result>, -{ - type Item = T; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - disp.map(|orig| match (self.f)(orig) { - Ok(Either::Keep(new)) => Disposition::Act(new), - Ok(Either::Drop(mbuf)) => Disposition::Drop(mbuf), - Err(e) => Disposition::Abort(e), - }) - }) - } -} diff --git a/core/src/batch/for_each.rs b/core/src/batch/for_each.rs deleted file mode 100644 index eb4c0f4..0000000 --- a/core/src/batch/for_each.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use anyhow::Result; - -/// A batch that calls a closure on packets in the underlying batch. -#[allow(missing_debug_implementations)] -pub struct ForEach -where - F: FnMut(&B::Item) -> Result<()>, -{ - batch: B, - f: F, -} - -impl ForEach -where - F: FnMut(&B::Item) -> Result<()>, -{ - /// Creates a new `ForEach` batch. - #[inline] - pub fn new(batch: B, f: F) -> Self { - ForEach { batch, f } - } -} - -impl Batch for ForEach -where - F: FnMut(&B::Item) -> Result<()>, -{ - type Item = B::Item; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - disp.map(|pkt| match (self.f)(&pkt) { - Ok(_) => Disposition::Act(pkt), - Err(e) => Disposition::Abort(e), - }) - }) - } -} diff --git a/core/src/batch/group_by.rs b/core/src/batch/group_by.rs deleted file mode 100644 index 3836f4e..0000000 --- a/core/src/batch/group_by.rs +++ /dev/null @@ -1,207 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use crate::packets::Packet; -use std::cell::Cell; -use std::collections::{HashMap, VecDeque}; -use std::hash::Hash; -use std::rc::Rc; - -/// A bridge between the main batch pipeline and the branch pipelines -/// created by the [`GroupBy`] combinator. Packets can be fed one at a time -/// through the bridge. Because the pipeline execution is depth first, -/// this is the most efficient way storage wise. -#[allow(missing_debug_implementations)] -#[derive(Default)] -pub struct Bridge(Rc>>); - -impl Bridge { - /// Creates a new, empty bridge. - pub fn new() -> Self { - Bridge(Rc::new(Cell::new(None))) - } - - /// Feeds a packet into the bridge container. - pub fn set(&self, pkt: T) { - self.0.set(Some(pkt)); - } -} - -impl Clone for Bridge { - fn clone(&self) -> Self { - Bridge(Rc::clone(&self.0)) - } -} - -impl Batch for Bridge { - type Item = T; - - fn replenish(&mut self) { - // nothing to do - } - - fn next(&mut self) -> Option> { - self.0.take().map(Disposition::Act) - } -} - -/// Builder closure for a sub batch from a bridge. -pub type GroupByBatchBuilder = dyn FnOnce(Bridge) -> Box>; - -/// A batch that splits the underlying batch into multiple sub batches. -/// -/// A closure is used to extract the discriminator used to determine how to -/// split the packets in the batch. If a packet is unmatched, it will be -/// marked as dropped. On error, the packet is marked as aborted. -/// -/// All the sub batches must have the same packet type as the underlying -/// batch. -#[allow(missing_debug_implementations)] -pub struct GroupBy -where - D: Eq + Clone + Hash, - S: Fn(&B::Item) -> D, -{ - batch: B, - selector: S, - bridge: Bridge, - groups: HashMap>>, - catchall: Box>, - fanouts: VecDeque>, -} - -impl GroupBy -where - D: Eq + Clone + Hash, - S: Fn(&B::Item) -> D, -{ - /// Creates a new `GroupBy` batch. - #[inline] - pub fn new(batch: B, selector: S, composer: C) -> Self - where - C: FnOnce(&mut HashMap, Box>>), - { - // get the builders for the sub batches - let mut builders = HashMap::new(); - composer(&mut builders); - - let bridge = Bridge::new(); - - // build the catchall batch pipeline - let catchall = builders.remove(&None).unwrap()(bridge.clone()); - - // build the rest of the batch pipelines - let groups = builders - .into_iter() - .map(|(key, build)| { - let key = key.unwrap(); - let group = build(bridge.clone()); - (key, group) - }) - .collect::>(); - - GroupBy { - batch, - selector, - bridge, - groups, - catchall, - fanouts: VecDeque::new(), - } - } -} - -impl Batch for GroupBy -where - D: Eq + Clone + Hash, - S: Fn(&B::Item) -> D, -{ - type Item = B::Item; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - if let Some(disp) = self.fanouts.pop_front() { - Some(disp) - } else { - self.batch.next().map(|disp| { - disp.map(|pkt| { - // gets the discriminator key - let key = (self.selector)(&pkt); - - // feeds this packet through the bridge - self.bridge.set(pkt); - - // runs the packet through. the sub-batch could be a fanout - // that produces multiple packets from one input. they are - // temporarily stored in a queue and returned in the subsequent - // iterations. - let batch = match self.groups.get_mut(&key) { - Some(group) => group, - None => &mut self.catchall, - }; - - while let Some(next) = batch.next() { - self.fanouts.push_back(next) - } - - self.fanouts.pop_front().unwrap() - }) - }) - } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __compose { - ($map:ident, $($key:expr => |$arg:tt| $body:block),*) => {{ - $( - $map.insert(Some($key), Box::new(|$arg| Box::new($body))); - )* - }}; -} - -/// Composes the batch builders for the [`group_by`] combinator. -/// -/// [`group_by`]: crate::batch::Batch::group_by -#[macro_export] -macro_rules! compose { - ($map:ident { $($key:expr => |$arg:tt| $body:block)+ }) => {{ - $crate::__compose!($map, $($key => |$arg| $body),*); - $map.insert(None, Box::new(|group| Box::new(group))); - }}; - ($map:ident { $($key:expr => |$arg:tt| $body:block)+ _ => |$_arg:tt| $_body:block }) => {{ - $crate::__compose!($map, $($key => |$arg| $body),*); - $map.insert(None, Box::new(|$_arg| Box::new($_body))); - }}; - ($map:ident { $($key:expr),+ => |$arg:tt| $body:block }) => {{ - $crate::compose!($map { $($key => |$arg| $body)+ }); - }}; - ($map:ident { $($key:expr),+ => |$arg:tt| $body:block _ => |$_arg:tt| $_body:block }) => {{ - $crate::compose!($map { $($key => |$arg| $body)+ _ => |$_arg| $_body }); - }}; - ($map:ident { _ => |$_arg:tt| $_body:block }) => {{ - $map.insert(None, Box::new(|$_arg| Box::new($_body))); - }}; -} diff --git a/core/src/batch/inspect.rs b/core/src/batch/inspect.rs deleted file mode 100644 index 82fa439..0000000 --- a/core/src/batch/inspect.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; - -/// A batch that calls a closure on packets in the underlying batch, including -/// ones that are already dropped, emitted or aborted. -#[allow(missing_debug_implementations)] -pub struct Inspect -where - F: FnMut(&Disposition), -{ - batch: B, - f: F, -} - -impl Inspect -where - F: FnMut(&Disposition), -{ - /// Creates a new `Inspect` batch. - #[inline] - pub fn new(batch: B, f: F) -> Self { - Inspect { batch, f } - } -} - -impl Batch for Inspect -where - F: FnMut(&Disposition), -{ - type Item = B::Item; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - (self.f)(&disp); - disp - }) - } -} diff --git a/core/src/batch/map.rs b/core/src/batch/map.rs deleted file mode 100644 index d52c4df..0000000 --- a/core/src/batch/map.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use crate::packets::Packet; -use anyhow::Result; - -/// A batch that maps the packets of the underlying batch. -/// -/// On error, the packet is marked as `aborted` and will short-circuit the -/// remainder of the pipeline. -#[allow(missing_debug_implementations)] -pub struct Map -where - F: FnMut(B::Item) -> Result, -{ - batch: B, - f: F, -} - -impl Map -where - F: FnMut(B::Item) -> Result, -{ - /// Creates a new `Map` batch. - #[inline] - pub fn new(batch: B, f: F) -> Self { - Map { batch, f } - } -} - -impl Batch for Map -where - F: FnMut(B::Item) -> Result, -{ - type Item = T; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - self.batch.next().map(|disp| { - disp.map(|orig| match (self.f)(orig) { - Ok(new) => Disposition::Act(new), - Err(e) => Disposition::Abort(e), - }) - }) - } -} diff --git a/core/src/batch/mod.rs b/core/src/batch/mod.rs deleted file mode 100644 index e1a82b8..0000000 --- a/core/src/batch/mod.rs +++ /dev/null @@ -1,736 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -//! Combinators that can be applied to batches of packets within a pipeline. - -mod emit; -mod filter; -mod filter_map; -mod for_each; -mod group_by; -mod inspect; -mod map; -mod poll; -mod replace; -mod rxtx; -mod send; - -pub use self::emit::*; -pub use self::filter::*; -pub use self::filter_map::*; -pub use self::for_each::*; -pub use self::group_by::*; -pub use self::inspect::*; -pub use self::map::*; -pub use self::poll::*; -pub use self::replace::*; -pub use self::rxtx::*; -pub use self::send::*; - -use crate::packets::Packet; -use crate::Mbuf; -use anyhow::{Error, Result}; -use std::collections::HashMap; -use std::hash::Hash; - -/// Way to categorize the packets of a batch inside a processing pipeline. -/// The disposition instructs the combinators how to process a packet. -#[allow(missing_debug_implementations)] -pub enum Disposition { - /// Indicating the packet should be processed. - Act(T), - - /// Indicating the packet has already been sent, possibly through a - /// different [`PacketTx`]. - /// - /// [`PacketTx`]: crate::batch::PacketTx - Emit, - - /// Indicating the packet is intentionally dropped from the output. - Drop(Mbuf), - - /// Indicating an error has occurred during processing. The packet will - /// be dropped from the output. Aborted packets are not bulk freed. - /// The packet is returned to mempool when it goes out of scope. - Abort(Error), -} - -impl Disposition { - /// Easy way to map a `Disposition` to a `Disposition` by reducing - /// it down to a map from `T` to `Disposition`. - fn map(self, f: F) -> Disposition - where - F: FnOnce(T) -> Disposition, - { - match self { - Disposition::Act(packet) => f(packet), - Disposition::Emit => Disposition::Emit, - Disposition::Drop(mbuf) => Disposition::Drop(mbuf), - Disposition::Abort(err) => Disposition::Abort(err), - } - } - - /// Returns whether the disposition is `Act`. - pub fn is_act(&self) -> bool { - matches!(self, Disposition::Act(_)) - } - - /// Returns whether the disposition is `Emit`. - pub fn is_emit(&self) -> bool { - matches!(self, Disposition::Emit) - } - - /// Returns whether the disposition is `Drop`. - pub fn is_drop(&self) -> bool { - matches!(self, Disposition::Drop(_)) - } - - /// Returns whether the disposition is `Abort`. - pub fn is_abort(&self) -> bool { - matches!(self, Disposition::Abort(_)) - } -} - -/// Types that can receive packets. -pub trait PacketRx { - /// Receives a batch of packets. - fn receive(&mut self) -> Vec; -} - -/// Types that can trasmit packets. -pub trait PacketTx { - /// Transmits a batch of packets. - fn transmit(&mut self, packets: Vec); -} - -/// Common behaviors to apply on batches of packets. -pub trait Batch { - /// The packet type. - type Item: Packet; - - /// Replenishes the batch with new packets from the source. - fn replenish(&mut self); - - /// Returns the disposition of the next packet in the batch. - /// - /// A value of `None` indicates that the batch is exhausted. To start - /// the next cycle, call [`replenish`] first. - /// - /// [`replenish`]: Batch::replenish - fn next(&mut self) -> Option>; - - /// Creates a batch that transmits all packets through the specified - /// [`PacketTx`]. - /// - /// Use when packets need to be delivered to a destination different - /// from the pipeline's main outbound queue. The send is immediate and - /// is not in batch. Packets sent with `emit` will be out of order - /// relative to other packets in the batch. - /// - /// # Example - /// - /// ``` - /// let (tx, _) = mpsc::channel(); - /// let mut batch = batch.emit(tx); - /// ``` - /// - /// [`PacketTx`]: crate::batch::PacketTx - fn emit(self, tx: Tx) -> Emit - where - Self: Sized, - { - Emit::new(self, tx) - } - - /// Creates a batch that uses a predicate to determine if a packet - /// should be processed or dropped. If the predicate evaluates to `false`, - /// the packet is marked as dropped. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.filter(|packet| { - /// let v4 = packet.parse::()?.parse::()?; - /// v4.ttl() > 0 - /// }); - /// ``` - #[inline] - fn filter

(self, predicate: P) -> Filter - where - P: FnMut(&Self::Item) -> bool, - Self: Sized, - { - Filter::new(self, predicate) - } - - /// Creates a batch that both [`filters`] and [`maps`]. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.filter_map(|packet| { - /// let v4 = packet.parse::()?.parse::()?; - /// if v4.protocol() == ProtocolNumbers::Udp { - /// Ok(Either::Keep(v4)) - /// } else { - /// Ok(Either::Drop(v4.reset())) - /// } - /// }); - /// ``` - /// - /// [`filters`]: Batch::filter - /// [`maps`]: Batch::map - #[inline] - fn filter_map(self, f: F) -> FilterMap - where - F: FnMut(Self::Item) -> Result>, - Self: Sized, - { - FilterMap::new(self, f) - } - - /// Creates a batch that maps the packets to another packet type. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.map(|packet| { - /// packet.parse::()?.parse::() - /// }); - /// ``` - #[inline] - fn map(self, f: F) -> Map - where - F: FnMut(Self::Item) -> Result, - Self: Sized, - { - Map::new(self, f) - } - - /// Calls a closure on each packet of the batch. - /// - /// Can be use for side-effect actions without the need to mutate the - /// packet. However, an error will abort the packet. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.for_each(|packet| { - /// println!("{:?}", packet); - /// Ok(()) - /// }); - /// ```` - #[inline] - fn for_each(self, f: F) -> ForEach - where - F: FnMut(&Self::Item) -> Result<()>, - Self: Sized, - { - ForEach::new(self, f) - } - - /// Calls a closure on each packet of the batch, including ones that are - /// already dropped, emitted or aborted. - /// - /// Unlike [`for_each`], `inspect` does not affect the packet disposition. - /// Useful as a debugging tool. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.inspect(|disp| { - /// if let Disposition::Act(v6) = disp { - /// if v6.hop_limit() > A_HOP_LIMIT { - /// debug!(...); - /// } - /// } - /// }); - /// ``` - /// - /// [`for_each`]: Batch::for_each - #[inline] - fn inspect(self, f: F) -> Inspect - where - F: FnMut(&Disposition), - Self: Sized, - { - Inspect::new(self, f) - } - - /// Splits the packets into multiple sub batches. Each sub batch runs - /// through a separate pipeline, and are then merged back together. - /// - /// `selector` is a closure that receives a reference to the packet and - /// evaluates to a discriminator value. The underlying batch will be split - /// into sub batches based on this value. - /// - /// `composer` is a closure that constructs a hash map of batch pipeline - /// builders for each individual sub pipeline. The [`compose!`] macro is an - /// ergonomic way to write the composer closure. The syntax of the macro - /// loosely resembles the std `match` expression. Each match arm consists of - /// a single discriminator value mapped to a builder closure. - /// - /// If a packet does not match with an arm, it will be passed through to - /// the next combinator. Use the catch all arm `_` to make the matching - /// exhaustive. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.group_by( - /// |packet| packet.protocol(), - /// |groups| { - /// compose!( groups { - /// ProtocolNumbers::Tcp => |group| { - /// group.map(do_tcp) - /// } - /// ProtocolNumbers::Udp => |group| { - /// group.map(do_udp) - /// } - /// _ => |group| { - /// group.map(unmatched) - /// } - /// }) - /// }, - /// ); - /// ``` - /// - /// [`compose!`]: macro@compose - #[inline] - fn group_by(self, selector: S, composer: C) -> GroupBy - where - D: Eq + Clone + Hash, - S: Fn(&Self::Item) -> D, - C: FnOnce(&mut HashMap, Box>>), - Self: Sized, - { - GroupBy::new(self, selector, composer) - } - - /// A batch that replaces each packet with another packet. - /// - /// Use for pipelines that generate new outbound packets based on inbound - /// packets and drop the inbound. - /// - /// # Example - /// - /// ``` - /// let mut batch = batch.replace(|request| { - /// let reply = Mbuf::new()?; - /// let ethernet = request.peek::()?; - /// let mut reply = reply.push::()?; - /// reply.set_src(ethernet.dst()); - /// reply.set_dst(ethernet.src()); - /// - /// ... - /// - /// Ok(reply) - /// }); - fn replace(self, f: F) -> Replace - where - F: FnMut(&Self::Item) -> Result, - Self: Sized, - { - Replace::new(self, f) - } - - /// Turns the batch pipeline into an executable task with default name. - /// - /// Send marks the end of the batch pipeline. No more combinators can be - /// appended after send. - /// - /// To give the pipeline a unique name, use - /// [`send_named`] instead. - /// - /// # Example - /// ``` - /// Poll::new(q.clone()).map(map_fn).send(q); - /// ``` - /// - /// [`send_named`]: Batch::send_named - #[inline] - fn send(self, tx: Tx) -> Send - where - Self: Sized, - { - Batch::send_named(self, "default", tx) - } - - /// Turns the batch pipeline into an executable task. - /// - /// `name` is used for logging and metrics. It does not need to be unique. - /// Multiple pipeline instances with the same name are aggregated together - /// into one set of metrics. Give each pipeline a different name to keep - /// metrics separated. - #[inline] - fn send_named(self, name: &str, tx: Tx) -> Send - where - Self: Sized, - { - Send::new(name.to_owned(), self, tx) - } -} - -/// Trait bound for batch pipelines. Can be used as a convenience for writing -/// pipeline installers. -/// -/// # Example -/// -/// ``` -/// fn install(q: PortQueue) -> impl Pipeline { -/// // install logic -/// } -/// ``` -pub trait Pipeline: futures::Future { - /// Returns the name of the pipeline. - fn name(&self) -> &str; - - /// Runs the pipeline once to process one batch of packets. - fn run_once(&mut self); -} - -/// Splices a [`PacketRx`] directly to a [`PacketTx`] without any intermediary -/// combinators. -/// -/// Useful for pipelines that perform simple forwarding without any packet -/// processing. -/// -/// # Example -/// -/// ``` -/// Runtime::build(config)? -/// .add_pipeline_to_port("kni0", |q| { -/// batch::splice(q.clone(), q.kni().unwrap().clone()) -/// }); -/// ``` -/// -/// [`PacketRx`]: crate::batch::PacketRx -/// [`PacketTx`]: crate::batch::PacketTx -pub fn splice(rx: Rx, tx: Tx) -> impl Pipeline { - Poll::new(rx).send(tx) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::compose; - use crate::packets::ip::v4::Ipv4; - use crate::packets::ip::ProtocolNumbers; - use crate::packets::Ethernet; - use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_TCP_PACKET, IPV4_UDP_PACKET}; - use std::sync::mpsc::{self, TryRecvError}; - - fn new_batch(data: &[&[u8]]) -> impl Batch { - let packets = data - .iter() - .map(|bytes| Mbuf::from_bytes(bytes).unwrap()) - .collect::>(); - - let (mut tx, rx) = mpsc::channel(); - tx.transmit(packets); - let mut batch = Poll::new(rx); - batch.replenish(); - batch - } - - #[capsule::test] - fn emit_batch() { - let (tx, mut rx) = mpsc::channel(); - - let mut batch = new_batch(&[&IPV4_UDP_PACKET]) - .map(|p| p.parse::()) - .emit(tx) - .for_each(|_| panic!("emit broken!")); - - assert!(batch.next().unwrap().is_emit()); - - // sent to the tx - assert_eq!(1, rx.receive().len()); - } - - #[capsule::test] - fn filter_batch() { - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).filter(|_| true); - assert!(batch.next().unwrap().is_act()); - - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).filter(|_| false); - assert!(batch.next().unwrap().is_drop()); - } - - #[capsule::test] - fn filter_map_batch() { - let mut batch = new_batch(&[&IPV4_UDP_PACKET, &ICMPV4_PACKET]).filter_map(|p| { - let v4 = p.parse::()?.parse::()?; - if v4.protocol() == ProtocolNumbers::Udp { - Ok(Either::Keep(v4)) - } else { - Ok(Either::Drop(v4.reset())) - } - }); - - // udp is let through - assert!(batch.next().unwrap().is_act()); - // icmp is dropped - assert!(batch.next().unwrap().is_drop()); - // at the end - assert!(batch.next().is_none()); - } - - #[capsule::test] - fn map_batch() { - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).map(|p| p.parse::()); - assert!(batch.next().unwrap().is_act()); - - // can't shrink the mbuf that much - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).map(|mut p| { - p.shrink(0, 999_999)?; - Ok(p) - }); - assert!(batch.next().unwrap().is_abort()); - } - - #[capsule::test] - fn for_each_batch() { - let mut side_effect = false; - - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).for_each(|_| { - side_effect = true; - Ok(()) - }); - - assert!(batch.next().unwrap().is_act()); - assert!(side_effect); - } - - #[capsule::test] - fn inspect_batch() { - let mut side_effect = false; - - let mut batch = new_batch(&[&IPV4_UDP_PACKET]).inspect(|_| { - side_effect = true; - }); - - assert!(batch.next().unwrap().is_act()); - assert!(side_effect); - } - - #[capsule::test] - fn group_by_batch() { - let mut batch = new_batch(&[&IPV4_TCP_PACKET, &IPV4_UDP_PACKET, &ICMPV4_PACKET]) - .map(|p| p.parse::()?.parse::()) - .group_by( - |p| p.protocol(), - |groups| { - compose!( groups { - ProtocolNumbers::Tcp => |group| { - group.map(|mut p| { - p.set_ttl(1); - Ok(p) - }) - } - ProtocolNumbers::Udp => |group| { - group.map(|mut p| { - p.set_ttl(2); - Ok(p) - }) - } - _ => |group| { - group.filter(|_| { - false - }) - } - }) - }, - ); - - // first one is the tcp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(1, pkt.ttl()); - } - - // next one is the udp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(2, pkt.ttl()); - } - - // last one is the catch all arm - assert!(batch.next().unwrap().is_drop()); - } - - #[capsule::test] - fn group_by_no_catchall() { - let mut batch = new_batch(&[&ICMPV4_PACKET]) - .map(|p| p.parse::()?.parse::()) - .group_by( - |p| p.protocol(), - |groups| { - compose!( groups { - ProtocolNumbers::Tcp => |group| { - group.filter(|_| false) - } - }) - }, - ); - - // did not match, passes through - assert!(batch.next().unwrap().is_act()); - } - - #[capsule::test] - fn group_by_or() { - let mut batch = new_batch(&[&IPV4_TCP_PACKET, &IPV4_UDP_PACKET, &ICMPV4_PACKET]) - .map(|p| p.parse::()?.parse::()) - .group_by( - |p| p.protocol(), - |groups| { - compose!( groups { - ProtocolNumbers::Tcp, ProtocolNumbers::Udp => |group| { - group.map(|mut p| { - p.set_ttl(1); - Ok(p) - }) - } - _ => |group| { - group.filter(|_| { - false - }) - } - }) - }, - ); - - // first one is the tcp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(1, pkt.ttl()); - } - - // next one is the udp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(1, pkt.ttl()); - } - - // last one is the catch all arm - assert!(batch.next().unwrap().is_drop()); - } - - #[capsule::test] - fn group_by_or_no_catchall() { - let mut batch = new_batch(&[&IPV4_TCP_PACKET, &IPV4_UDP_PACKET]) - .map(|p| p.parse::()?.parse::()) - .group_by( - |p| p.protocol(), - |groups| { - compose!( groups { - ProtocolNumbers::Tcp, ProtocolNumbers::Udp => |group| { - group.map(|mut p| { - p.set_ttl(1); - Ok(p) - }) - } - }) - }, - ); - - // first one is the tcp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(1, pkt.ttl()); - } - - // next one is the udp arm - let disp = batch.next().unwrap(); - assert!(disp.is_act()); - if let Disposition::Act(pkt) = disp { - assert_eq!(1, pkt.ttl()); - } - } - - #[capsule::test] - fn group_by_fanout() { - let mut batch = new_batch(&[&IPV4_TCP_PACKET]) - .map(|p| p.parse::()?.parse::()) - .group_by( - |p| p.protocol(), - |groups| { - compose!( groups { - ProtocolNumbers::Tcp => |group| { - group.replace(|_| { - Mbuf::from_bytes(&IPV4_UDP_PACKET)? - .parse::()? - .parse::() - }) - } - }) - }, - ); - - // replace inside group_by will produce a new UDP packet - // and marks the original TCP packet as dropped. - assert!(batch.next().unwrap().is_act()); - assert!(batch.next().unwrap().is_drop()); - assert!(batch.next().is_none()); - } - - #[capsule::test] - fn replace_batch() { - let mut batch = - new_batch(&[&IPV4_UDP_PACKET]).replace(|_| Mbuf::from_bytes(&IPV4_TCP_PACKET)); - - // first one is the replacement - assert!(batch.next().unwrap().is_act()); - // next one is the original - assert!(batch.next().unwrap().is_drop()); - // at the end - assert!(batch.next().is_none()); - } - - #[capsule::test] - fn poll_fn_batch() { - let mut batch = poll_fn(|| vec![Mbuf::new().unwrap()]); - batch.replenish(); - - assert!(batch.next().unwrap().is_act()); - assert!(batch.next().is_none()); - } - - #[capsule::test] - fn splice_pipeline() { - let (mut tx1, rx1) = mpsc::channel(); - let (tx2, rx2) = mpsc::channel(); - - // no packet yet - let mut pipeline = splice(rx1, tx2); - pipeline.run_once(); - assert_eq!(TryRecvError::Empty, rx2.try_recv().unwrap_err()); - - // send one packet - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); - tx1.transmit(vec![packet]); - pipeline.run_once(); - assert!(rx2.try_recv().is_ok()); - } -} diff --git a/core/src/batch/poll.rs b/core/src/batch/poll.rs deleted file mode 100644 index 72023c9..0000000 --- a/core/src/batch/poll.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition, PacketRx, PollRx}; -use crate::Mbuf; -use std::collections::VecDeque; - -/// A batch that polls a receiving source for new packets. -/// -/// This marks the beginning of the pipeline. -#[allow(missing_debug_implementations)] -pub struct Poll { - rx: Rx, - packets: Option>, -} - -impl Poll { - /// Creates a new `Poll` batch. - #[inline] - pub fn new(rx: Rx) -> Self { - Poll { rx, packets: None } - } -} - -impl Batch for Poll { - type Item = Mbuf; - - /// Replenishes the batch with new packets from the RX source. - /// - /// If there are still packets left in the current queue, they are lost. - #[inline] - fn replenish(&mut self) { - // `VecDeque` is not the ideal structure here. We are relying on the - // conversion from `Vec` to `VecDeque` to be allocation-free. but - // unfortunately that's not always the case. We need an efficient and - // allocation-free data structure with pop semantic. - self.packets = Some(self.rx.receive().into()); - } - - #[inline] - fn next(&mut self) -> Option> { - if let Some(q) = self.packets.as_mut() { - q.pop_front().map(Disposition::Act) - } else { - None - } - } -} - -/// Creates a new poll batch from a closure. -pub fn poll_fn(f: F) -> Poll> -where - F: Fn() -> Vec, -{ - Poll::new(PollRx { f }) -} diff --git a/core/src/batch/replace.rs b/core/src/batch/replace.rs deleted file mode 100644 index 2efa0e7..0000000 --- a/core/src/batch/replace.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition}; -use crate::packets::Packet; -use anyhow::Result; - -/// A batch that replaces each packet of the batch with another packet. -/// -/// The original packet is dropped from the batch with the new packet in its -/// place. On error, the packet is `aborted` and will short-circuit the -/// remainder of the pipeline. -#[allow(missing_debug_implementations)] -pub struct Replace -where - F: FnMut(&B::Item) -> Result, -{ - batch: B, - f: F, - slot: Option, -} - -impl Replace -where - F: FnMut(&B::Item) -> Result, -{ - /// Creates a new `Replace` batch. - #[inline] - pub fn new(batch: B, f: F) -> Self { - Replace { - batch, - f, - slot: None, - } - } -} - -impl Batch for Replace -where - F: FnMut(&B::Item) -> Result, -{ - type Item = T; - - #[inline] - fn replenish(&mut self) { - self.batch.replenish(); - } - - #[inline] - fn next(&mut self) -> Option> { - // internally the replace combinator will add a new packet to the - // batch and mark the original as dropped. the iteration grows to - // 2x in length because each item becomes 2 items. - if let Some(pkt) = self.slot.take() { - // has a packet in the temp slot. marks it as dropped. - Some(Disposition::Drop(pkt.reset())) - } else { - // nothing in the slot, fetches a new packet from source. - self.batch.next().map(|disp| { - disp.map(|orig| { - match (self.f)(&orig) { - Ok(new) => { - // keeps the original in the temp slot, we will mark it dropped - // in the iteration that immediately follows. - self.slot.replace(orig); - Disposition::Act(new) - } - Err(e) => Disposition::Abort(e), - } - }) - }) - } - } -} diff --git a/core/src/batch/rxtx.rs b/core/src/batch/rxtx.rs deleted file mode 100644 index c78d65c..0000000 --- a/core/src/batch/rxtx.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -//! Implementations of `PacketRx` and `PacketTx`. -//! -//! Implemented for `PortQueue`. -//! -//! `PacketRx` implemented for `KniRx`. -//! -//! `PacketTx` implemented for `KniTxQueue`. -//! -//! Implemented for the MPSC channel so it can be used as a batch source -//! mostly in tests. - -use super::{PacketRx, PacketTx}; -use crate::{KniRx, KniTxQueue, Mbuf, PortQueue}; -use std::iter; -use std::sync::mpsc::{Receiver, Sender}; - -impl PacketRx for PortQueue { - fn receive(&mut self) -> Vec { - PortQueue::receive(self) - } -} - -impl PacketTx for PortQueue { - fn transmit(&mut self, packets: Vec) { - PortQueue::transmit(self, packets) - } -} - -impl PacketRx for KniRx { - fn receive(&mut self) -> Vec { - KniRx::receive(self) - } -} - -impl PacketTx for KniTxQueue { - fn transmit(&mut self, packets: Vec) { - KniTxQueue::transmit(self, packets) - } -} - -impl PacketRx for Receiver { - fn receive(&mut self) -> Vec { - iter::from_fn(|| self.try_recv().ok()).collect::>() - } -} - -impl PacketTx for Sender { - fn transmit(&mut self, packets: Vec) { - packets.into_iter().for_each(|packet| { - let _ = self.send(packet); - }); - } -} - -/// A batch that polls a closure for packets. -#[allow(missing_debug_implementations)] -pub struct PollRx -where - F: Fn() -> Vec, -{ - pub(crate) f: F, -} - -impl PacketRx for PollRx -where - F: Fn() -> Vec, -{ - fn receive(&mut self) -> Vec { - (self.f)() - } -} diff --git a/core/src/batch/send.rs b/core/src/batch/send.rs deleted file mode 100644 index e96b67a..0000000 --- a/core/src/batch/send.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Batch, Disposition, PacketTx, Pipeline}; -use crate::dpdk::CoreId; -#[cfg(feature = "metrics")] -use crate::metrics::{labels, Counter, SINK}; -use crate::packets::Packet; -use crate::Mbuf; -use futures::{future, Future}; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio_executor::current_thread; - -/// Creates a new pipeline counter. -#[cfg(feature = "metrics")] -fn new_counter(name: &'static str, pipeline: &str) -> Counter { - SINK.scoped("pipeline").counter_with_labels( - name, - labels!( - "pipeline" => pipeline.to_owned(), - "core" => CoreId::current().raw().to_string(), - ), - ) -} - -/// A batch that can be executed as a runtime task. -#[allow(missing_debug_implementations)] -pub struct Send { - name: String, - batch: B, - tx: Tx, - #[cfg(feature = "metrics")] - runs: Counter, - #[cfg(feature = "metrics")] - processed: Counter, - #[cfg(feature = "metrics")] - dropped: Counter, - #[cfg(feature = "metrics")] - errors: Counter, -} - -impl Send { - /// Creates a new `Send` batch. - #[cfg(not(feature = "metrics"))] - #[inline] - pub fn new(name: String, batch: B, tx: Tx) -> Self { - Send { name, batch, tx } - } - - /// Creates a new `Send` batch. - #[cfg(feature = "metrics")] - #[inline] - pub fn new(name: String, batch: B, tx: Tx) -> Self { - let runs = new_counter("runs", &name); - let processed = new_counter("processed", &name); - let dropped = new_counter("dropped", &name); - let errors = new_counter("errors", &name); - Send { - name, - batch, - tx, - runs, - processed, - dropped, - errors, - } - } - - fn run(&mut self) { - // let's get a new batch - self.batch.replenish(); - - let mut transmit_q = Vec::with_capacity(64); - let mut drop_q = Vec::with_capacity(64); - let mut emitted = 0u64; - let mut aborted = 0u64; - - // consume the whole batch to completion - while let Some(disp) = self.batch.next() { - match disp { - Disposition::Act(packet) => transmit_q.push(packet.reset()), - Disposition::Drop(mbuf) => drop_q.push(mbuf), - Disposition::Emit => emitted += 1, - Disposition::Abort(_) => aborted += 1, - } - } - - #[cfg(feature = "metrics")] - { - self.runs.record(1); - self.processed.record(transmit_q.len() as u64 + emitted); - self.dropped.record(drop_q.len() as u64); - self.errors.record(aborted); - } - - if !transmit_q.is_empty() { - self.tx.transmit(transmit_q); - } - - if !drop_q.is_empty() { - Mbuf::free_bulk(drop_q); - } - } -} - -/// By implementing the `Future` trait, `Send` can be spawned onto the tokio -/// executor. Each time the future is polled, it processes one batch of -/// packets before returning the `Poll::Pending` status and yields. -impl Future for Send { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // executes a batch of packets. - self.get_mut().run(); - - // now schedules the waker as a future and yields the core so other - // futures have a chance to run. - let waker = cx.waker().clone(); - current_thread::spawn(future::lazy(|_| waker.wake())); - - Poll::Pending - } -} - -impl Pipeline for Send { - #[inline] - fn name(&self) -> &str { - &self.name - } - - #[inline] - fn run_once(&mut self) { - self.run() - } -} diff --git a/core/src/config.rs b/core/src/config.rs deleted file mode 100644 index 5ae8b14..0000000 --- a/core/src/config.rs +++ /dev/null @@ -1,513 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -//! Toml-based configuration for use with Capsule applications. -//! -//! # Example -//! -//! A configuration from our [`pktdump`] example: -//! ``` -//! app_name = "pktdump" -//! master_core = 0 -//! duration = 5 -//! -//! [mempool] -//! capacity = 65535 -//! cache_size = 256 -//! -//! [[ports]] -//! name = "eth1" -//! device = "net_pcap0" -//! args = "rx_pcap=tcp4.pcap,tx_iface=lo" -//! cores = [0] -//! -//! [[ports]] -//! name = "eth2" -//! device = "net_pcap1" -//! args = "rx_pcap=tcp6.pcap,tx_iface=lo" -//! cores = [0] -//! ``` -//! -//! [`pktdump`]: https://github.com/capsule-rs/capsule/tree/master/examples/pktdump - -use crate::dpdk::CoreId; -use crate::net::{Ipv4Cidr, Ipv6Cidr, MacAddr}; -use anyhow::Result; -use clap::{clap_app, crate_version}; -use regex::Regex; -use serde::{de, Deserialize, Deserializer}; -use std::fmt; -use std::fs; -use std::str::FromStr; -use std::time::Duration; - -// make `CoreId` serde deserializable. -impl<'de> Deserialize<'de> for CoreId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let i = usize::deserialize(deserializer)?; - Ok(CoreId::new(i)) - } -} - -// make `MacAddr` serde deserializable. -impl<'de> Deserialize<'de> for MacAddr { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - MacAddr::from_str(&s).map_err(de::Error::custom) - } -} - -// make `Ipv4Cidr` serde deserializable. -impl<'de> Deserialize<'de> for Ipv4Cidr { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - Ipv4Cidr::from_str(&s).map_err(de::Error::custom) - } -} - -// make `Ipv6Cidr` serde deserializable. -impl<'de> Deserialize<'de> for Ipv6Cidr { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - Ipv6Cidr::from_str(&s).map_err(de::Error::custom) - } -} - -/// Deserializes a duration from seconds expressed as `u64`. -pub fn duration_from_secs<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let secs = u64::deserialize(deserializer)?; - Ok(Duration::from_secs(secs)) -} - -/// Deserializes an option of duration from seconds expressed as `u64`. -pub fn duration_option_from_secs<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - // for now this is the cleanest way to deserialize an option, till a better - // way is implemented, https://github.com/serde-rs/serde/issues/723 - #[derive(Deserialize)] - struct Wrapper(#[serde(deserialize_with = "duration_from_secs")] Duration); - - let option = Option::deserialize(deserializer)?.and_then(|Wrapper(dur)| { - if dur.as_secs() > 0 { - Some(dur) - } else { - None - } - }); - Ok(option) -} - -/// Runtime configuration settings. -#[derive(Clone, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RuntimeConfig { - /// Application name. This must be unique if you want to run multiple - /// DPDK applications on the same system. - pub app_name: String, - - /// Indicating whether the process is a secondary process. Secondary - /// process cannot initialize shared memory, but can attach to pre- - /// initialized shared memory by the primary process and create objects - /// in it. Defaults to `false`. - #[serde(default)] - pub secondary: bool, - - /// Application group name. Use this to group primary and secondary - /// processes together in a multi-process setup; and allow them to share - /// the same memory regions. The default value is the `app_name`. Each - /// process works independently. - #[serde(default)] - pub app_group: Option, - - /// The identifier of the master core. This is the core the main thread - /// will run on. - pub master_core: CoreId, - - /// Additional cores that are available to the application, and can be - /// used for running general tasks. Packet pipelines cannot be run on - /// these cores unless the core is also assigned to a port separately. - /// Defaults to empty list. - #[serde(default)] - pub cores: Vec, - - /// Per mempool settings. On a system with multiple sockets, aka NUMA - /// nodes, one mempool will be allocated for each socket the apllication - /// uses. - #[serde(default)] - pub mempool: MempoolConfig, - - /// The ports to use for the application. Must have at least one. - pub ports: Vec, - - /// Additional DPDK [`parameters`] to pass on for EAL initialization. When - /// set, the values are passed through as is without validation. - /// - /// [`parameters`]: https://doc.dpdk.org/guides/linux_gsg/linux_eal_parameters.html - #[serde(default)] - pub dpdk_args: Option, - - /// If set, the application will stop after the duration expires. Useful - /// for setting a timeout for integration tests. - #[serde(default, deserialize_with = "duration_option_from_secs")] - pub duration: Option, -} - -impl RuntimeConfig { - /// Returns all the cores assigned to the runtime. - pub(crate) fn all_cores(&self) -> Vec { - let mut cores = vec![]; - cores.push(self.master_core); - cores.extend(self.cores.iter()); - - self.ports.iter().for_each(|port| { - cores.extend(port.cores.iter()); - }); - - cores.sort(); - cores.dedup(); - cores - } - - /// Extracts the EAL arguments from runtime settings. - pub(crate) fn to_eal_args(&self) -> Vec { - let mut eal_args = vec![]; - - // adds the app name - eal_args.push(self.app_name.clone()); - - let proc_type = if self.secondary { - "secondary".to_owned() - } else { - "primary".to_owned() - }; - - // adds the proc type - eal_args.push("--proc-type".to_owned()); - eal_args.push(proc_type); - - // adds the mem file prefix - let prefix = self.app_group.as_ref().unwrap_or(&self.app_name); - eal_args.push("--file-prefix".to_owned()); - eal_args.push(prefix.clone()); - - // adds all the ports - let pcie = Regex::new(r"^\d{4}:\d{2}:\d{2}\.\d$").unwrap(); - self.ports.iter().for_each(|port| { - if pcie.is_match(port.device.as_str()) { - eal_args.push("--pci-whitelist".to_owned()); - eal_args.push(port.device.clone()); - } else { - let vdev = if let Some(args) = &port.args { - format!("{},{}", port.device, args) - } else { - port.device.clone() - }; - eal_args.push("--vdev".to_owned()); - eal_args.push(vdev); - } - }); - - // adds the master core - eal_args.push("--master-lcore".to_owned()); - eal_args.push(self.master_core.raw().to_string()); - - // limits the EAL to only the master core. actual threads are - // managed by the runtime not the EAL. - eal_args.push("-l".to_owned()); - eal_args.push(self.master_core.raw().to_string()); - - // adds additional DPDK args - if let Some(args) = &self.dpdk_args { - eal_args.extend(args.split_ascii_whitespace().map(str::to_owned)); - } - - eal_args - } - - /// Returns the number of KNI enabled ports - pub(crate) fn num_knis(&self) -> usize { - self.ports.iter().filter(|p| p.kni).count() - } -} - -impl fmt::Debug for RuntimeConfig { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("runtime"); - d.field("app_name", &self.app_name) - .field("secondary", &self.secondary) - .field( - "app_group", - self.app_group.as_ref().unwrap_or(&self.app_name), - ) - .field("master_core", &self.master_core) - .field("cores", &self.cores) - .field("mempool", &self.mempool) - .field("ports", &self.ports); - if let Some(dpdk_args) = &self.dpdk_args { - d.field("dpdk_args", dpdk_args); - } - if let Some(duration) = &self.duration { - d.field("duration", duration); - } - d.finish() - } -} - -/// Mempool configuration settings. -#[derive(Clone, Deserialize)] -pub struct MempoolConfig { - /// The maximum number of Mbufs the mempool can allocate. The optimum - /// size (in terms of memory usage) is when n is a power of two minus - /// one. Defaults to `65535` or `2 ^ 16 - 1`. - #[serde(default = "default_capacity")] - pub capacity: usize, - - /// The size of the per core object cache. If cache_size is non-zero, - /// the library will try to limit the accesses to the common lockless - /// pool. The cache can be disabled if the argument is set to 0. Defaults - /// to `0`. - #[serde(default = "default_cache_size")] - pub cache_size: usize, -} - -fn default_capacity() -> usize { - 65535 -} - -fn default_cache_size() -> usize { - 0 -} - -impl Default for MempoolConfig { - fn default() -> Self { - MempoolConfig { - capacity: default_capacity(), - cache_size: default_cache_size(), - } - } -} - -impl fmt::Debug for MempoolConfig { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("mempool") - .field("capacity", &self.capacity) - .field("cache_size", &self.cache_size) - .finish() - } -} - -/// Port configuration settings. -#[derive(Clone, Deserialize)] -pub struct PortConfig { - /// The application assigned logical name of the port. - /// - /// For applications with more than one port, this name can be used to - /// identifer the port. - pub name: String, - - /// The device name of the port. It can be the following formats, - /// - /// * PCIe address, for example `0000:02:00.0` - /// * DPDK virtual device, for example `net_[pcap0|null0|tap0]` - pub device: String, - - /// Additional arguments to configure a virtual device. - #[serde(default)] - pub args: Option, - - /// The cores assigned to the port for running the pipelines. The values - /// can overlap with the runtime cores. - pub cores: Vec, - - /// The receive queue capacity. Defaults to `128`. - #[serde(default = "default_port_rxd")] - pub rxd: usize, - - /// The transmit queue capacity. Defaults to `128`. - #[serde(default = "default_port_txd")] - pub txd: usize, - - /// Whether promiscuous mode is enabled for this port. Defaults to `false`. - #[serde(default)] - pub promiscuous: bool, - - /// Whether multicast packet reception is enabled for this port. Defaults - /// to `true`. - #[serde(default = "default_multicast_mode")] - pub multicast: bool, - - /// Whether kernel NIC interface is enabled for this port. with KNI, this - /// port can exchange packets with the kernel networking stack. Defaults - /// to `false`. - #[serde(default)] - pub kni: bool, -} - -fn default_port_rxd() -> usize { - 128 -} - -fn default_port_txd() -> usize { - 128 -} - -fn default_multicast_mode() -> bool { - true -} - -impl fmt::Debug for PortConfig { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("port"); - d.field("name", &self.name); - d.field("device", &self.device); - if let Some(args) = &self.args { - d.field("args", args); - } - d.field("cores", &self.cores) - .field("rxd", &self.rxd) - .field("txd", &self.txd) - .field("promiscuous", &self.promiscuous) - .field("multicast", &self.multicast) - .field("kni", &self.kni) - .finish() - } -} - -/// Loads the app config from a TOML file. -/// -/// # Example -/// -/// ``` -/// home$ ./myapp -f config.toml -/// ``` -pub fn load_config() -> Result { - let matches = clap_app!(capsule => - (version: crate_version!()) - (@arg file: -f --file +required +takes_value "configuration file") - ) - .get_matches(); - - let path = matches.value_of("file").unwrap(); - let content = fs::read_to_string(path)?; - toml::from_str(&content).map_err(|err| err.into()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn config_defaults() { - const CONFIG: &str = r#" - app_name = "myapp" - master_core = 0 - - [[ports]] - name = "eth0" - device = "0000:00:01.0" - cores = [2, 3] - "#; - - let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); - - assert_eq!(false, config.secondary); - assert_eq!(None, config.app_group); - assert!(config.cores.is_empty()); - assert_eq!(None, config.dpdk_args); - assert_eq!(default_capacity(), config.mempool.capacity); - assert_eq!(default_cache_size(), config.mempool.cache_size); - assert_eq!(None, config.ports[0].args); - assert_eq!(default_port_rxd(), config.ports[0].rxd); - assert_eq!(default_port_txd(), config.ports[0].txd); - assert_eq!(false, config.ports[0].promiscuous); - assert_eq!(default_multicast_mode(), config.ports[0].multicast); - assert_eq!(false, config.ports[0].kni); - } - - #[test] - fn config_to_eal_args() { - const CONFIG: &str = r#" - app_name = "myapp" - secondary = false - app_group = "mygroup" - master_core = 0 - cores = [1] - dpdk_args = "-v --log-level eal:8" - - [mempool] - capacity = 255 - cache_size = 16 - - [[ports]] - name = "eth0" - device = "0000:00:01.0" - cores = [2, 3] - rxd = 32 - txd = 32 - - [[ports]] - name = "eth1" - device = "net_pcap0" - args = "rx=lo,tx=lo" - cores = [0, 4] - rxd = 32 - txd = 32 - "#; - - let config: RuntimeConfig = toml::from_str(CONFIG).unwrap(); - - assert_eq!( - &[ - "myapp", - "--proc-type", - "primary", - "--file-prefix", - "mygroup", - "--pci-whitelist", - "0000:00:01.0", - "--vdev", - "net_pcap0,rx=lo,tx=lo", - "--master-lcore", - "0", - "-l", - "0", - "-v", - "--log-level", - "eal:8" - ], - config.to_eal_args().as_slice(), - ) - } -} diff --git a/core/src/dpdk/kni.rs b/core/src/dpdk/kni.rs deleted file mode 100644 index edf6919..0000000 --- a/core/src/dpdk/kni.rs +++ /dev/null @@ -1,411 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Mbuf, PortId}; -use crate::dpdk::DpdkError; -use crate::ffi::{self, AsStr, ToResult}; - -#[cfg(feature = "metrics")] -use crate::metrics::{labels, Counter, SINK}; -use crate::net::MacAddr; -use crate::{debug, error, warn}; -use anyhow::Result; -use futures::{future, Future, StreamExt}; -use std::cmp; -use std::mem; -use std::os::raw; -use std::ptr::{self, NonNull}; -use thiserror::Error; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; - -/// Creates a new KNI counter. -#[cfg(feature = "metrics")] -fn new_counter(name: &'static str, kni: &str, dir: &'static str) -> Counter { - SINK.scoped("kni").counter_with_labels( - name, - labels!( - "kni" => kni.to_string(), - "dir" => dir, - ), - ) -} - -/// The KNI receive handle. Because the underlying interface is single -/// threaded, we must ensure that only one rx handle is created for each -/// interface. -#[allow(missing_debug_implementations)] -pub struct KniRx { - raw: NonNull, - #[cfg(feature = "metrics")] - packets: Counter, - #[cfg(feature = "metrics")] - octets: Counter, -} - -impl KniRx { - /// Creates a new `KniRx`. - #[cfg(not(feature = "metrics"))] - pub fn new(raw: NonNull) -> Self { - KniRx { raw } - } - - /// Creates a new `KniRx`. - #[cfg(feature = "metrics")] - pub fn new(raw: NonNull) -> Self { - let name = unsafe { ffi::rte_kni_get_name(raw.as_ref()).as_str().to_owned() }; - let packets = new_counter("packets", &name, "rx"); - let octets = new_counter("octets", &name, "rx"); - KniRx { - raw, - packets, - octets, - } - } - - /// Receives a burst of packets from the kernel, up to a maximum of - /// **32** packets. - pub fn receive(&mut self) -> Vec { - const RX_BURST_MAX: usize = 32; - let mut ptrs = Vec::with_capacity(RX_BURST_MAX); - - let len = unsafe { - ffi::rte_kni_rx_burst( - self.raw.as_mut(), - ptrs.as_mut_ptr(), - RX_BURST_MAX as raw::c_uint, - ) - }; - - let mbufs = unsafe { - ptrs.set_len(len as usize); - ptrs.into_iter() - .map(|ptr| Mbuf::from_ptr(ptr)) - .collect::>() - }; - - unsafe { - // checks if there are any link change requests, and handle them. - if let Err(err) = - ffi::rte_kni_handle_request(self.raw.as_mut()).into_result(|_| DpdkError::new()) - { - warn!(message = "failed to handle change link requests.", ?err); - } - } - - #[cfg(feature = "metrics")] - { - self.packets.record(mbufs.len() as u64); - - let bytes: usize = mbufs.iter().map(Mbuf::data_len).sum(); - self.octets.record(bytes as u64); - } - - mbufs - } -} - -/// In memory queue for the cores to deliver packets that are destined for -/// the kernel. Then another pipeline will collect these and forward them -/// on in a thread safe way. -#[allow(missing_debug_implementations)] -#[derive(Clone)] -pub struct KniTxQueue { - tx_enque: UnboundedSender>, -} - -impl KniTxQueue { - /// Transmits packets to the KNI tx queue. - pub fn transmit(&mut self, packets: Vec) { - if let Err(err) = self.tx_enque.try_send(packets) { - warn!(message = "failed to send to kni tx queue."); - Mbuf::free_bulk(err.into_inner()); - } - } -} - -/// The KNI transmit handle. Because the underlying interface is single -/// threaded, we must ensure that only one tx handle is created for each -/// interface. -pub(crate) struct KniTx { - raw: NonNull, - tx_deque: Option>>, - #[cfg(feature = "metrics")] - packets: Counter, - #[cfg(feature = "metrics")] - octets: Counter, - #[cfg(feature = "metrics")] - dropped: Counter, -} - -impl KniTx { - /// Creates a new `KniTx`. - #[cfg(not(feature = "metrics"))] - pub(crate) fn new(raw: NonNull, tx_deque: UnboundedReceiver>) -> Self { - KniTx { - raw, - tx_deque: Some(tx_deque), - } - } - - /// Creates a new `KniTx` with stats. - #[cfg(feature = "metrics")] - pub(crate) fn new(raw: NonNull, tx_deque: UnboundedReceiver>) -> Self { - let name = unsafe { ffi::rte_kni_get_name(raw.as_ref()).as_str().to_owned() }; - let packets = new_counter("packets", &name, "tx"); - let octets = new_counter("octets", &name, "tx"); - let dropped = new_counter("dropped", &name, "tx"); - KniTx { - raw, - tx_deque: Some(tx_deque), - packets, - octets, - dropped, - } - } - - /// Sends the packets to the kernel. - pub(crate) fn transmit(&mut self, packets: Vec) { - let mut ptrs = packets.into_iter().map(Mbuf::into_ptr).collect::>(); - - loop { - let to_send = ptrs.len() as raw::c_uint; - let sent = - unsafe { ffi::rte_kni_tx_burst(self.raw.as_mut(), ptrs.as_mut_ptr(), to_send) }; - - if sent > 0 { - #[cfg(feature = "metrics")] - { - self.packets.record(sent as u64); - - let bytes: u64 = ptrs[..sent as usize] - .iter() - .map(|&ptr| unsafe { (*ptr).data_len as u64 }) - .sum(); - self.octets.record(bytes); - } - - if to_send - sent > 0 { - // still have packets not sent. tx queue is full but still making - // progress. we will keep trying until all packets are sent. drains - // the ones already sent first and try again on the rest. - let _ = ptrs.drain(..sent as usize); - } else { - break; - } - } else { - // tx queue is full and we can't make progress, start dropping packets - // to avoid potentially stuck in an endless loop. - #[cfg(feature = "metrics")] - self.dropped.record(to_send as u64); - - super::mbuf_free_bulk(ptrs); - break; - } - } - } - - /// Converts the TX handle into a spawnable pipeline. - pub(crate) fn into_pipeline(mut self) -> impl Future { - self.tx_deque.take().unwrap().for_each(move |packets| { - self.transmit(packets); - future::ready(()) - }) - } -} - -// we need to send tx and rx across threads to run them. -unsafe impl Send for KniRx {} -unsafe impl Send for KniTx {} - -/// KNI errors. -#[derive(Debug, Error)] -pub(crate) enum KniError { - #[error("KNI is not enabled for the port.")] - Disabled, - - #[error("Another core owns the handle.")] - NotAcquired, -} - -/// Kernel NIC interface. This allows the DPDK application to exchange -/// packets with the kernel networking stack. -/// -/// The DPDK implementation is single-threaded TX and RX. Only one thread -/// can receive and one thread can transmit on the interface at a time. To -/// support a multi-queued port with a single virtual interface, a multi -/// producer, single consumer channel is used to collect all the kernel -/// bound packets onto one thread for transmit. -pub(crate) struct Kni { - raw: NonNull, - rx: Option, - tx: Option, - txq: KniTxQueue, -} - -impl Kni { - /// Creates a new KNI. - pub(crate) fn new(raw: NonNull) -> Kni { - let (send, recv) = mpsc::unbounded_channel(); - - // making 3 clones of the same raw pointer. but we know it is safe - // to do because rx and tx happen on two independent queues. so while - // each one is single-threaded, they can function in parallel. - let rx = KniRx::new(raw); - let tx = KniTx::new(raw, recv); - let txq = KniTxQueue { tx_enque: send }; - - Kni { - raw, - rx: Some(rx), - tx: Some(tx), - txq, - } - } - - /// Takes ownership of the RX handle. - pub(crate) fn take_rx(&mut self) -> Result { - self.rx.take().ok_or_else(|| KniError::NotAcquired.into()) - } - - /// Takes ownership of the TX handle. - pub(crate) fn take_tx(&mut self) -> Result { - self.tx.take().ok_or_else(|| KniError::NotAcquired.into()) - } - - /// Returns a TX queue handle to send packets to kernel. - pub(crate) fn txq(&self) -> KniTxQueue { - self.txq.clone() - } - - /// Returns the raw struct needed for FFI calls. - #[inline] - pub(crate) fn raw_mut(&mut self) -> &mut ffi::rte_kni { - unsafe { self.raw.as_mut() } - } -} - -impl Drop for Kni { - fn drop(&mut self) { - debug!("freeing kernel interface."); - - if let Err(err) = - unsafe { ffi::rte_kni_release(self.raw_mut()).into_result(|_| DpdkError::new()) } - { - error!(message = "failed to release KNI device.", ?err); - } - } -} - -/// Does not support changing the link MTU. -extern "C" fn change_mtu(port_id: u16, new_mtu: raw::c_uint) -> raw::c_int { - warn!("ignored change port {} mtu to {}.", port_id, new_mtu); - -1 -} - -/// Does not change the link up/down status, but will return 0 so the -/// command succeeds. -extern "C" fn config_network_if(port_id: u16, if_up: u8) -> raw::c_int { - warn!("ignored change port {} status to {}.", port_id, if_up); - 0 -} - -/// Does not support changing the link MAC address. -extern "C" fn config_mac_address(port_id: u16, _mac_addr: *mut u8) -> raw::c_int { - warn!("ignored change port {} mac address.", port_id); - -1 -} - -/// Does not support changing the link promiscusity. -extern "C" fn config_promiscusity(port_id: u16, to_on: u8) -> raw::c_int { - warn!("ignored change port {} promiscusity to {}.", port_id, to_on); - -1 -} - -/// Builds a KNI device from the configuration values. -pub(crate) struct KniBuilder<'a> { - mempool: &'a mut ffi::rte_mempool, - conf: ffi::rte_kni_conf, - ops: ffi::rte_kni_ops, -} - -impl<'a> KniBuilder<'a> { - /// Creates a new KNI device builder with the mempool for allocating - /// new packets. - pub(crate) fn new(mempool: &'a mut ffi::rte_mempool) -> Self { - KniBuilder { - mempool, - conf: ffi::rte_kni_conf::default(), - ops: ffi::rte_kni_ops::default(), - } - } - - pub(crate) fn name(&mut self, name: &str) -> &mut Self { - unsafe { - self.conf.name = mem::zeroed(); - ptr::copy( - name.as_ptr(), - self.conf.name.as_mut_ptr() as *mut u8, - cmp::min(name.len(), self.conf.name.len()), - ); - } - self - } - - pub(crate) fn port_id(&mut self, port_id: PortId) -> &mut Self { - self.conf.group_id = port_id.raw(); - self.ops.port_id = port_id.raw(); - self - } - - pub(crate) fn mac_addr(&mut self, mac: MacAddr) -> &mut Self { - unsafe { - self.conf.mac_addr = mem::transmute(mac); - } - self - } - - pub(crate) fn finish(&mut self) -> Result { - self.conf.mbuf_size = ffi::RTE_MBUF_DEFAULT_BUF_SIZE; - self.ops.change_mtu = Some(change_mtu); - self.ops.config_network_if = Some(config_network_if); - self.ops.config_mac_address = Some(config_mac_address); - self.ops.config_promiscusity = Some(config_promiscusity); - - unsafe { - ffi::rte_kni_alloc(self.mempool, &self.conf, &mut self.ops) - .into_result(|_| DpdkError::new()) - .map(Kni::new) - } - } -} - -/// Initializes and preallocates the KNI subsystem. -pub(crate) fn kni_init(max: usize) -> Result<()> { - unsafe { - ffi::rte_kni_init(max as raw::c_uint) - .into_result(DpdkError::from_errno) - .map(|_| ()) - } -} - -/// Closes the KNI subsystem. -pub(crate) fn kni_close() { - unsafe { - ffi::rte_kni_close(); - } -} diff --git a/core/src/dpdk/mempool.rs b/core/src/dpdk/mempool.rs deleted file mode 100644 index be3f2d7..0000000 --- a/core/src/dpdk/mempool.rs +++ /dev/null @@ -1,180 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::SocketId; -use crate::dpdk::DpdkError; -use crate::ffi::{self, AsStr, ToCString, ToResult}; -use crate::{debug, info}; -use anyhow::Result; -use std::cell::Cell; -use std::collections::HashMap; -use std::fmt; -use std::os::raw; -use std::ptr::{self, NonNull}; -use std::sync::atomic::{AtomicUsize, Ordering}; -use thiserror::Error; - -/// A memory pool is an allocator of message buffers, or `Mbuf`. For best -/// performance, each socket should have a dedicated `Mempool`. -pub(crate) struct Mempool { - raw: NonNull, -} - -impl Mempool { - /// Creates a new `Mempool` for `Mbuf`. - /// - /// `capacity` is the maximum number of `Mbuf` the `Mempool` can hold. - /// The optimum size (in terms of memory usage) is when n is a power - /// of two minus one. - /// - /// `cache_size` is the per core object cache. If cache_size is non-zero, - /// the library will try to limit the accesses to the common lockless - /// pool. The cache can be disabled if the argument is set to 0. - /// - /// `socket_id` is the socket where the memory should be allocated. The - /// value can be `SocketId::ANY` if there is no constraint. - /// - /// # Errors - /// - /// If allocation fails, then `DpdkError` is returned. - pub(crate) fn new(capacity: usize, cache_size: usize, socket_id: SocketId) -> Result { - static MEMPOOL_COUNT: AtomicUsize = AtomicUsize::new(0); - let n = MEMPOOL_COUNT.fetch_add(1, Ordering::Relaxed); - let name = format!("mempool{}", n); - - let raw = unsafe { - ffi::rte_pktmbuf_pool_create( - name.clone().into_cstring().as_ptr(), - capacity as raw::c_uint, - cache_size as raw::c_uint, - 0, - ffi::RTE_MBUF_DEFAULT_BUF_SIZE as u16, - socket_id.raw(), - ) - .into_result(|_| DpdkError::new())? - }; - - info!("created {}.", name); - Ok(Self { raw }) - } - - /// Returns the raw struct needed for FFI calls. - #[inline] - pub(crate) fn raw(&self) -> &ffi::rte_mempool { - unsafe { self.raw.as_ref() } - } - - /// Returns the raw struct needed for FFI calls. - #[inline] - pub(crate) fn raw_mut(&mut self) -> &mut ffi::rte_mempool { - unsafe { self.raw.as_mut() } - } - - /// Returns the name of the `Mempool`. - #[inline] - pub(crate) fn name(&self) -> &str { - self.raw().name[..].as_str() - } - - #[cfg(feature = "metrics")] - pub(crate) fn stats(&self) -> super::MempoolStats { - super::MempoolStats::build(self) - } -} - -impl fmt::Debug for Mempool { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let raw = self.raw(); - f.debug_struct(self.name()) - .field("capacity", &raw.size) - .field("cache_size", &raw.cache_size) - .field("flags", &format_args!("{:#x}", raw.flags)) - .field("socket", &raw.socket_id) - .finish() - } -} - -impl Drop for Mempool { - fn drop(&mut self) { - debug!("freeing {}.", self.name()); - - unsafe { - ffi::rte_mempool_free(self.raw_mut()); - } - } -} - -thread_local! { - /// `Mempool` on the same socket as the current core. - /// - /// It's set when the core is first initialized. New `Mbuf` is allocated - /// from this `Mempool` when executed on this core. - pub static MEMPOOL: Cell<*mut ffi::rte_mempool> = Cell::new(ptr::null_mut()); -} - -/// Error indicating the `Mempool` is not found or is exhaused. -#[derive(Debug, Error)] -pub(crate) enum MempoolError { - #[error("Cannot allocate a new mbuf from mempool")] - Exhausted, - - #[error("Mempool for {0:?} not found.")] - NotFound(SocketId), -} - -/// A specialized hash map of `SocketId` to `&mut Mempool`. -#[derive(Debug)] -pub(crate) struct MempoolMap<'a> { - inner: HashMap, -} - -impl<'a> MempoolMap<'a> { - /// Creates a new map from a mutable slice. - pub(crate) fn new(mempools: &'a mut [Mempool]) -> Self { - let map = mempools - .iter_mut() - .map(|pool| { - let socket = SocketId(pool.raw().socket_id); - (socket, pool) - }) - .collect::>(); - - Self { inner: map } - } - - /// Returns a mutable reference to the raw mempool corresponding to the - /// socket id. - /// - /// # Errors - /// - /// If the value is not found, `MempoolError::NotFound` is returned. - pub(crate) fn get_raw(&mut self, socket_id: SocketId) -> Result<&mut ffi::rte_mempool> { - self.inner - .get_mut(&socket_id) - .ok_or_else(|| MempoolError::NotFound(socket_id).into()) - .map(|pool| pool.raw_mut()) - } -} - -impl<'a> Default for MempoolMap<'a> { - fn default() -> MempoolMap<'a> { - MempoolMap { - inner: HashMap::new(), - } - } -} diff --git a/core/src/dpdk/mod.rs b/core/src/dpdk/mod.rs deleted file mode 100644 index b0b7929..0000000 --- a/core/src/dpdk/mod.rs +++ /dev/null @@ -1,244 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -mod kni; -mod mbuf; -mod mempool; -mod port; -#[cfg(feature = "metrics")] -mod stats; - -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::kni::*; -#[allow(unreachable_pub)] -pub use self::mbuf::*; -pub(crate) use self::mempool::*; -#[allow(unreachable_pub)] -pub use self::port::*; -#[cfg(feature = "metrics")] -pub(crate) use self::stats::*; - -use crate::debug; -use crate::ffi::{self, AsStr, ToCString, ToResult}; -use crate::net::MacAddr; -use anyhow::Result; -use std::cell::Cell; -use std::fmt; -use std::mem; -use std::os::raw; -use thiserror::Error; - -/// An error generated in `libdpdk`. -/// -/// When an FFI call fails, the `errno` is translated into `DpdkError`. -#[derive(Debug, Error)] -#[error("{0}")] -pub(crate) struct DpdkError(String); - -impl DpdkError { - /// Returns the `DpdkError` for the most recent failure on the current - /// thread. - #[inline] - pub(crate) fn new() -> Self { - DpdkError::from_errno(-1) - } - - /// Returns the `DpdkError` for a specific `errno`. - #[inline] - fn from_errno(errno: raw::c_int) -> Self { - let errno = if errno == -1 { - unsafe { ffi::_rte_errno() } - } else { - -errno - }; - DpdkError(unsafe { ffi::rte_strerror(errno).as_str().into() }) - } -} - -/// An opaque identifier for a physical CPU socket. -/// -/// A socket is also known as a NUMA node. On a multi-socket system, for best -/// performance, ensure that the cores and memory used for packet processing -/// are in the same socket as the network interface card. -#[derive(Copy, Clone, Eq, Hash, PartialEq)] -pub struct SocketId(raw::c_int); - -impl SocketId { - /// A socket ID representing any NUMA node. - pub const ANY: Self = SocketId(-1); - - /// Returns the ID of the socket the current core is on. - #[inline] - pub fn current() -> SocketId { - unsafe { SocketId(ffi::rte_socket_id() as raw::c_int) } - } - - /// Returns all the socket IDs detected on the system. - #[inline] - pub fn all() -> Vec { - unsafe { - (0..ffi::rte_socket_count()) - .map(|idx| ffi::rte_socket_id_by_idx(idx)) - .filter(|&sid| sid != -1) - .map(SocketId) - .collect::>() - } - } - - /// Returns the raw value needed for FFI calls. - #[allow(clippy::trivially_copy_pass_by_ref)] - #[inline] - pub(crate) fn raw(&self) -> raw::c_int { - self.0 - } -} - -impl fmt::Debug for SocketId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "socket{}", self.0) - } -} - -/// An opaque identifier for a physical CPU core. -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct CoreId(usize); - -impl CoreId { - /// Any lcore to indicate that no thread affinity is set. - pub const ANY: Self = CoreId(std::usize::MAX); - - /// Creates a new CoreId from the numeric ID assigned to the core - /// by the system. - #[inline] - pub(crate) fn new(i: usize) -> CoreId { - CoreId(i) - } - - /// Returns the ID of the current core. - #[inline] - pub fn current() -> CoreId { - CURRENT_CORE_ID.with(|tls| tls.get()) - } - - /// Returns the ID of the socket the core is on. - #[allow(clippy::trivially_copy_pass_by_ref)] - #[inline] - pub fn socket_id(&self) -> SocketId { - unsafe { SocketId(ffi::numa_node_of_cpu(self.0 as raw::c_int)) } - } - - /// Returns the raw value. - #[allow(clippy::trivially_copy_pass_by_ref)] - #[inline] - pub(crate) fn raw(&self) -> usize { - self.0 - } - - /// Sets the current thread's affinity to this core. - #[allow(clippy::trivially_copy_pass_by_ref)] - #[inline] - pub(crate) fn set_thread_affinity(&self) -> Result<()> { - unsafe { - // the two types that represent `cpu_set` have identical layout, - // hence it is safe to transmute between them. - let mut set: libc::cpu_set_t = mem::zeroed(); - libc::CPU_SET(self.0, &mut set); - let mut set: ffi::rte_cpuset_t = mem::transmute(set); - ffi::rte_thread_set_affinity(&mut set).into_result(DpdkError::from_errno)?; - } - - CURRENT_CORE_ID.with(|tls| tls.set(*self)); - Ok(()) - } -} - -impl fmt::Debug for CoreId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "core{}", self.0) - } -} - -thread_local! { - static CURRENT_CORE_ID: Cell = Cell::new(CoreId::ANY); -} - -/// Initializes the Environment Abstraction Layer (EAL). -pub(crate) fn eal_init(args: Vec) -> Result<()> { - debug!(arguments=?args); - - let len = args.len() as raw::c_int; - let args = args - .into_iter() - .map(|s| s.into_cstring()) - .collect::>(); - let mut ptrs = args - .iter() - .map(|s| s.as_ptr() as *mut raw::c_char) - .collect::>(); - - let res = unsafe { ffi::rte_eal_init(len, ptrs.as_mut_ptr()) }; - debug!("EAL parsed {} arguments.", res); - - res.into_result(DpdkError::from_errno).map(|_| ()) -} - -/// Cleans up the Environment Abstraction Layer (EAL). -pub(crate) fn eal_cleanup() -> Result<()> { - unsafe { - ffi::rte_eal_cleanup() - .into_result(DpdkError::from_errno) - .map(|_| ()) - } -} - -/// Returns the `MacAddr` of a port. -fn eth_macaddr_get(port_id: u16) -> MacAddr { - let mut addr = ffi::rte_ether_addr::default(); - unsafe { - ffi::rte_eth_macaddr_get(port_id, &mut addr); - } - addr.addr_bytes.into() -} - -/// Frees the `rte_mbuf` in bulk. -pub(crate) fn mbuf_free_bulk(mbufs: Vec<*mut ffi::rte_mbuf>) { - assert!(!mbufs.is_empty()); - - let mut to_free = Vec::with_capacity(mbufs.len()); - let pool = unsafe { (*mbufs[0]).pool }; - - for mbuf in mbufs.into_iter() { - if pool == unsafe { (*mbuf).pool } { - to_free.push(mbuf as *mut raw::c_void); - } else { - unsafe { - let len = to_free.len(); - ffi::_rte_mempool_put_bulk(pool, to_free.as_ptr(), len as u32); - to_free.set_len(0); - } - - to_free.push(mbuf as *mut raw::c_void); - } - } - - unsafe { - let len = to_free.len(); - ffi::_rte_mempool_put_bulk(pool, to_free.as_ptr(), len as u32); - to_free.set_len(0); - } -} diff --git a/core/src/dpdk/port.rs b/core/src/dpdk/port.rs deleted file mode 100644 index 302051e..0000000 --- a/core/src/dpdk/port.rs +++ /dev/null @@ -1,651 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{CoreId, Kni, KniBuilder, KniTxQueue, Mbuf, Mempool, MempoolMap, SocketId}; -use crate::dpdk::DpdkError; -use crate::ffi::{self, AsStr, ToCString, ToResult}; -#[cfg(feature = "metrics")] -use crate::metrics::{labels, Counter, SINK}; -use crate::net::MacAddr; -#[cfg(feature = "pcap-dump")] -use crate::pcap; -use crate::{debug, ensure, info, warn}; -use anyhow::Result; -use std::collections::HashMap; -use std::fmt; -use std::os::raw; -use std::ptr; -use thiserror::Error; - -const DEFAULT_RSS_HF: u64 = - (ffi::ETH_RSS_IP | ffi::ETH_RSS_TCP | ffi::ETH_RSS_UDP | ffi::ETH_RSS_SCTP) as u64; - -/// An opaque identifier for an Ethernet device port. -#[derive(Copy, Clone)] -pub(crate) struct PortId(u16); - -impl PortId { - /// Returns the ID of the socket the port is connected to. - /// - /// Virtual devices do not have real socket IDs. The value returned - /// will be discarded if it does not match any of the system's physical - /// socket IDs. - #[inline] - pub(crate) fn socket_id(self) -> Option { - let id = unsafe { SocketId(ffi::rte_eth_dev_socket_id(self.0)) }; - if SocketId::all().contains(&id) { - Some(id) - } else { - None - } - } - - /// Returns the raw value needed for FFI calls. - #[allow(clippy::trivially_copy_pass_by_ref)] - #[inline] - pub(crate) fn raw(&self) -> u16 { - self.0 - } -} - -impl fmt::Debug for PortId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "port{}", self.0) - } -} - -/// The index of a receive queue. -#[derive(Copy, Clone)] -pub(crate) struct RxQueueIndex(u16); - -impl RxQueueIndex { - /// Returns the raw value needed for FFI calls. - #[allow(clippy::trivially_copy_pass_by_ref, dead_code)] - #[inline] - pub(crate) fn raw(&self) -> u16 { - self.0 - } -} - -/// The index of a transmit queue. -#[derive(Copy, Clone)] -pub(crate) struct TxQueueIndex(u16); - -impl TxQueueIndex { - /// Returns the raw value needed for FFI calls. - #[allow(clippy::trivially_copy_pass_by_ref, dead_code)] - #[inline] - pub(crate) fn raw(&self) -> u16 { - self.0 - } -} - -/// Either queue type (receive or transmit) with associated index. -#[allow(dead_code)] -pub(crate) enum RxTxQueue { - Rx(RxQueueIndex), - Tx(TxQueueIndex), -} - -/// The receive and transmit queue abstraction. Instead of modeling them -/// as two standalone queues, in the run-to-completion mode, they are modeled -/// as a queue pair associated with the core that runs the pipeline from -/// receive to send. -#[allow(missing_debug_implementations)] -#[derive(Clone)] -pub struct PortQueue { - port_id: PortId, - rxq: RxQueueIndex, - txq: TxQueueIndex, - kni: Option, - #[cfg(feature = "metrics")] - received: Option, - #[cfg(feature = "metrics")] - transmitted: Option, - #[cfg(feature = "metrics")] - dropped: Option, -} - -impl PortQueue { - #[cfg(not(feature = "metrics"))] - fn new(port: PortId, rxq: RxQueueIndex, txq: TxQueueIndex) -> Self { - PortQueue { - port_id: port, - rxq, - txq, - kni: None, - } - } - - #[cfg(feature = "metrics")] - fn new(port: PortId, rxq: RxQueueIndex, txq: TxQueueIndex) -> Self { - PortQueue { - port_id: port, - rxq, - txq, - kni: None, - received: None, - transmitted: None, - dropped: None, - } - } - /// Receives a burst of packets from the receive queue, up to a maximum - /// of 32 packets. - pub(crate) fn receive(&self) -> Vec { - const RX_BURST_MAX: usize = 32; - let mut ptrs = Vec::with_capacity(RX_BURST_MAX); - - let len = unsafe { - ffi::_rte_eth_rx_burst( - self.port_id.0, - self.rxq.0, - ptrs.as_mut_ptr(), - RX_BURST_MAX as u16, - ) - }; - - #[cfg(feature = "metrics")] - self.received.as_ref().unwrap().record(len as u64); - - unsafe { - ptrs.set_len(len as usize); - ptrs.into_iter() - .map(|ptr| Mbuf::from_ptr(ptr)) - .collect::>() - } - } - - /// Sends the packets to the transmit queue. - pub(crate) fn transmit(&self, packets: Vec) { - let mut ptrs = packets.into_iter().map(Mbuf::into_ptr).collect::>(); - - loop { - let to_send = ptrs.len() as u16; - let sent = unsafe { - ffi::_rte_eth_tx_burst(self.port_id.0, self.txq.0, ptrs.as_mut_ptr(), to_send) - }; - - if sent > 0 { - #[cfg(feature = "metrics")] - self.transmitted.as_ref().unwrap().record(sent as u64); - - if to_send - sent > 0 { - // still have packets not sent. tx queue is full but still making - // progress. we will keep trying until all packets are sent. drains - // the ones already sent first and try again on the rest. - let _drained = ptrs.drain(..sent as usize).collect::>(); - } else { - break; - } - } else { - // tx queue is full and we can't make progress, start dropping packets - // to avoid potentially stuck in an endless loop. - #[cfg(feature = "metrics")] - self.dropped.as_ref().unwrap().record(ptrs.len() as u64); - - super::mbuf_free_bulk(ptrs); - break; - } - } - } - - /// Returns a handle to send packets to the associated KNI interface. - pub fn kni(&self) -> Option<&KniTxQueue> { - self.kni.as_ref() - } - - /// Sets the TX queue for the KNI interface. - fn set_kni(&mut self, kni: KniTxQueue) { - self.kni = Some(kni); - } - - /// Sets the per queue counters. Some device drivers don't track TX - /// and RX packets per queue. Instead we will track them here for all - /// devices. Additionally we also track the TX packet drops when the - /// TX queue is full. - #[cfg(feature = "metrics")] - fn set_counters(&mut self, port: &str, core_id: CoreId) { - let counter = SINK.scoped("port").counter_with_labels( - "packets", - labels!( - "port" => port.to_owned(), - "dir" => "rx", - "core" => core_id.0.to_string(), - ), - ); - self.received = Some(counter); - - let counter = SINK.scoped("port").counter_with_labels( - "packets", - labels!( - "port" => port.to_owned(), - "dir" => "tx", - "core" => core_id.0.to_string(), - ), - ); - self.transmitted = Some(counter); - - let counter = SINK.scoped("port").counter_with_labels( - "dropped", - labels!( - "port" => port.to_owned(), - "dir" => "tx", - "core" => core_id.0.to_string(), - ), - ); - self.dropped = Some(counter); - } - - /// Returns the MAC address of the port. - pub fn mac_addr(&self) -> MacAddr { - super::eth_macaddr_get(self.port_id.0) - } -} - -/// Error indicating failed to initialize the port. -#[derive(Debug, Error)] -pub(crate) enum PortError { - /// Port is not found. - #[error("Port {0} is not found.")] - NotFound(String), - - #[error("Port is not bound to any cores.")] - CoreNotBound, - - /// The maximum number of RX queues is less than the number of cores - /// assigned to the port. - #[error("Insufficient number of RX queues '{0}'.")] - InsufficientRxQueues(usize), - - /// The maximum number of TX queues is less than the number of cores - /// assigned to the port. - #[error("Insufficient number of TX queues '{0}'.")] - InsufficientTxQueues(usize), -} - -/// An Ethernet device port. -pub(crate) struct Port { - id: PortId, - name: String, - device: String, - queues: HashMap, - kni: Option, - dev_info: ffi::rte_eth_dev_info, -} - -impl Port { - /// Returns the port id. - pub(crate) fn id(&self) -> PortId { - self.id - } - - /// Returns the application assigned logical name of the port. - /// - /// For applications with more than one port, this name can be used to - /// identifer the port. - pub(crate) fn name(&self) -> &str { - self.name.as_str() - } - - /// Returns the MAC address of the port. - pub(crate) fn mac_addr(&self) -> MacAddr { - super::eth_macaddr_get(self.id.0) - } - - /// Returns the available port queues. - pub(crate) fn queues(&self) -> &HashMap { - &self.queues - } - - /// Returns the KNI. - pub(crate) fn kni(&mut self) -> Option<&mut Kni> { - self.kni.as_mut() - } - - /// Starts the port. This is the final step before packets can be - /// received or transmitted on this port. Promiscuous mode is also - /// enabled automatically. - /// - /// # Errors - /// - /// If the port fails to start, `DpdkError` is returned. - pub(crate) fn start(&mut self) -> Result<()> { - unsafe { - ffi::rte_eth_dev_start(self.id.0).into_result(DpdkError::from_errno)?; - } - - info!("started port {}.", self.name()); - Ok(()) - } - - /// Stops the port. - pub(crate) fn stop(&mut self) { - unsafe { - ffi::rte_eth_dev_stop(self.id.0); - } - - info!("stopped port {}.", self.name()); - } - - #[cfg(feature = "metrics")] - pub(crate) fn stats(&self) -> super::PortStats { - super::PortStats::build(self) - } -} - -impl fmt::Debug for Port { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let info = self.dev_info; - f.debug_struct(&self.name()) - .field("device", &self.device) - .field("port", &self.id.0) - .field("mac", &format_args!("\"{}\"", self.mac_addr())) - .field("driver", &info.driver_name.as_str()) - .field("rx_offload", &format_args!("{:#x}", info.rx_offload_capa)) - .field("tx_offload", &format_args!("{:#x}", info.tx_offload_capa)) - .field("max_rxq", &info.max_rx_queues) - .field("max_txq", &info.max_tx_queues) - .field("socket", &self.id.socket_id().map_or(-1, |s| s.0)) - .finish() - } -} - -impl Drop for Port { - fn drop(&mut self) { - debug!("freeing {}.", self.name); - - unsafe { - ffi::rte_eth_dev_close(self.id.0); - } - } -} - -/// Builds a port from the configuration values. -pub(crate) struct PortBuilder<'a> { - name: String, - device: String, - port_id: PortId, - dev_info: ffi::rte_eth_dev_info, - cores: Vec, - mempools: MempoolMap<'a>, - rxd: u16, - txd: u16, -} - -impl<'a> PortBuilder<'a> { - /// Creates a new `PortBuilder` with a logical name and device name. - /// - /// The device name can be the following - /// * PCIe address, for example `0000:02:00.0` - /// * DPDK virtual device, for example `net_[pcap0|null0|tap0]` - /// - /// # Errors - /// - /// If the device is not found, `DpdkError` is returned. - pub(crate) fn new(name: String, device: String) -> Result { - let mut port_id = 0u16; - unsafe { - ffi::rte_eth_dev_get_port_by_name(device.clone().into_cstring().as_ptr(), &mut port_id) - .into_result(DpdkError::from_errno)?; - } - - let port_id = PortId(port_id); - debug!("{} is {:?}.", name, port_id); - - let mut dev_info = ffi::rte_eth_dev_info::default(); - unsafe { - ffi::rte_eth_dev_info_get(port_id.0, &mut dev_info); - } - - Ok(PortBuilder { - name, - device, - port_id, - dev_info, - cores: vec![CoreId::new(0)], - mempools: Default::default(), - rxd: 0, - txd: 0, - }) - } - - /// Sets the processing cores assigned to the port. - /// - /// Each core assigned will receive from and transmit through the port - /// independently using the run-to-completion model. - /// - /// # Errors - /// - /// If either the maximum number of RX or TX queues is less than the - /// number of cores assigned, `PortError` is returned. - pub(crate) fn cores(&mut self, cores: &[CoreId]) -> Result<&mut Self> { - ensure!(!cores.is_empty(), PortError::CoreNotBound); - - let mut cores = cores.to_vec(); - cores.sort(); - cores.dedup(); - let len = cores.len() as u16; - - ensure!( - self.dev_info.max_rx_queues >= len, - PortError::InsufficientRxQueues(self.dev_info.max_rx_queues as usize) - ); - ensure!( - self.dev_info.max_tx_queues >= len, - PortError::InsufficientTxQueues(self.dev_info.max_tx_queues as usize) - ); - - self.cores = cores; - Ok(self) - } - - /// Sets the receive and transmit queues' capacity. - /// - /// `rxd` is the receive queue capacity and `txd` is the trasmit queue - /// capacity. The values are checked against the descriptor limits of - /// the Ethernet device, and are adjusted if they exceed the boundaries. - /// - /// # Errors - /// - /// If the adjustment failed, `DpdkError` is returned. - pub(crate) fn rx_tx_queue_capacity(&mut self, rxd: usize, txd: usize) -> Result<&mut Self> { - let mut rxd2 = rxd as u16; - let mut txd2 = txd as u16; - - unsafe { - ffi::rte_eth_dev_adjust_nb_rx_tx_desc(self.port_id.0, &mut rxd2, &mut txd2) - .into_result(DpdkError::from_errno)?; - } - - info!( - cond: rxd2 != rxd as u16, - message = "adjusted rxd.", - before = rxd, - after = rxd2 - ); - info!( - cond: txd2 != txd as u16, - message = "adjusted txd.", - before = txd, - after = txd2 - ); - - self.rxd = rxd2; - self.txd = txd2; - Ok(self) - } - - /// Sets the available mempools. - pub(crate) fn mempools(&'a mut self, mempools: &'a mut [Mempool]) -> &'a mut Self { - self.mempools = MempoolMap::new(mempools); - self - } - - /// Creates the `Port`. - #[allow(clippy::cognitive_complexity)] - pub(crate) fn finish( - &mut self, - promiscuous: bool, - multicast: bool, - with_kni: bool, - ) -> anyhow::Result { - let len = self.cores.len() as u16; - let mut conf = ffi::rte_eth_conf::default(); - - // turns on receive side scaling if port has multiple cores. - if len > 1 { - conf.rxmode.mq_mode = ffi::rte_eth_rx_mq_mode::ETH_MQ_RX_RSS; - conf.rx_adv_conf.rss_conf.rss_hf = - DEFAULT_RSS_HF & self.dev_info.flow_type_rss_offloads; - } - - // turns on optimization for fast release of mbufs. - if self.dev_info.tx_offload_capa & ffi::DEV_TX_OFFLOAD_MBUF_FAST_FREE as u64 > 0 { - conf.txmode.offloads |= ffi::DEV_TX_OFFLOAD_MBUF_FAST_FREE as u64; - debug!("turned on optimization for fast release of mbufs."); - } - - // must configure the device first before everything else. - unsafe { - ffi::rte_eth_dev_configure(self.port_id.0, len, len, &conf) - .into_result(DpdkError::from_errno)?; - } - - // if the port is virtual, we will allocate it to the socket of - // the first assigned core. - let socket_id = self - .port_id - .socket_id() - .unwrap_or_else(|| self.cores[0].socket_id()); - debug!("{} connected to {:?}.", self.name, socket_id); - - // the socket determines which pool to allocate mbufs from. - let mempool = self.mempools.get_raw(socket_id)?; - - // if the port has kni enabled, we will allocate an interface. - let kni = if with_kni { - let kni = KniBuilder::new(mempool) - .name(&self.name) - .port_id(self.port_id) - .mac_addr(super::eth_macaddr_get(self.port_id.raw())) - .finish()?; - Some(kni) - } else { - None - }; - - let mut queues = HashMap::new(); - - // for each core, we setup a rx/tx queue pair. for simplicity, we - // will use the same index for both queues. - for (idx, &core_id) in self.cores.iter().enumerate() { - // for best performance, the port and cores should connect to - // the same socket. - warn!( - cond: core_id.socket_id() != socket_id, - message = "core socket does not match port socket.", - core = ?core_id, - core_socket = core_id.socket_id().0, - port_socket = socket_id.0 - ); - - // configures the RX queue with defaults - let rxq = RxQueueIndex(idx as u16); - unsafe { - ffi::rte_eth_rx_queue_setup( - self.port_id.0, - rxq.0, - self.rxd, - socket_id.0 as raw::c_uint, - ptr::null(), - mempool, - ) - .into_result(DpdkError::from_errno)?; - } - - // configures the TX queue with defaults - let txq = TxQueueIndex(idx as u16); - unsafe { - ffi::rte_eth_tx_queue_setup( - self.port_id.0, - txq.0, - self.txd, - socket_id.0 as raw::c_uint, - ptr::null(), - ) - .into_result(DpdkError::from_errno)?; - } - - #[cfg(feature = "pcap-dump")] - { - pcap::capture_queue( - self.port_id, - self.name.as_str(), - core_id, - RxTxQueue::Rx(rxq), - )?; - - pcap::capture_queue( - self.port_id, - self.name.as_str(), - core_id, - RxTxQueue::Tx(txq), - )?; - } - - let mut q = PortQueue::new(self.port_id, rxq, txq); - - if let Some(kni) = &kni { - q.set_kni(kni.txq()); - } - - #[cfg(feature = "metrics")] - q.set_counters(&self.name, core_id); - - queues.insert(core_id, q); - debug!("initialized port queue for {:?}.", core_id); - } - - unsafe { - // sets the port's promiscuous mode. - if promiscuous { - ffi::rte_eth_promiscuous_enable(self.port_id.0); - } else { - ffi::rte_eth_promiscuous_disable(self.port_id.0); - } - - // sets the port's multicast mode. - if multicast { - ffi::rte_eth_allmulticast_enable(self.port_id.0); - } else { - ffi::rte_eth_allmulticast_disable(self.port_id.0); - } - } - - info!("initialized port {}.", self.name); - - Ok(Port { - id: self.port_id, - name: self.name.clone(), - device: self.device.clone(), - queues, - kni, - dev_info: self.dev_info, - }) - } -} diff --git a/core/src/dpdk/stats.rs b/core/src/dpdk/stats.rs deleted file mode 100644 index a12f41e..0000000 --- a/core/src/dpdk/stats.rs +++ /dev/null @@ -1,130 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use super::{Mempool, Port, PortId}; -use crate::dpdk::DpdkError; -use crate::ffi::{self, AsStr, ToResult}; -use crate::metrics::{labels, Key, Measurement}; -use anyhow::Result; -use std::ptr::NonNull; - -/// Port stats collector. -pub(crate) struct PortStats { - id: PortId, - name: String, -} - -impl PortStats { - /// Builds a collector from the port. - pub(crate) fn build(port: &Port) -> Self { - PortStats { - id: port.id(), - name: port.name().to_owned(), - } - } - - /// Returns the port name. - pub(crate) fn name(&self) -> &str { - self.name.as_str() - } - - /// Returns a counter with port and direction labels. - fn new_counter(&self, name: &'static str, value: u64, dir: &'static str) -> (Key, Measurement) { - ( - Key::from_name_and_labels( - name, - labels!( - "port" => self.name.clone(), - "dir" => dir, - ), - ), - Measurement::Counter(value), - ) - } - - /// Collects the port stats tracked by DPDK. - pub(crate) fn collect(&self) -> Result> { - let mut stats = ffi::rte_eth_stats::default(); - unsafe { - ffi::rte_eth_stats_get(self.id.raw(), &mut stats).into_result(DpdkError::from_errno)?; - } - - let mut values = Vec::new(); - - values.push(self.new_counter("octets", stats.ibytes, "rx")); - values.push(self.new_counter("octets", stats.obytes, "tx")); - values.push(self.new_counter("dropped", stats.imissed, "rx")); - values.push(self.new_counter("errors", stats.ierrors, "rx")); - values.push(self.new_counter("errors", stats.oerrors, "tx")); - values.push(self.new_counter("no_mbuf", stats.rx_nombuf, "rx")); - - Ok(values) - } -} - -/// Mempool stats collector. -pub(crate) struct MempoolStats { - raw: NonNull, -} - -impl MempoolStats { - /// Builds a collector from the port. - pub(crate) fn build(mempool: &Mempool) -> Self { - MempoolStats { - raw: unsafe { - NonNull::new_unchecked( - mempool.raw() as *const ffi::rte_mempool as *mut ffi::rte_mempool - ) - }, - } - } - - fn raw(&self) -> &ffi::rte_mempool { - unsafe { self.raw.as_ref() } - } - - /// Returns the name of the `Mempool`. - fn name(&self) -> &str { - self.raw().name[..].as_str() - } - - /// Returns a gauge. - fn new_gauge(&self, name: &'static str, value: i64) -> (Key, Measurement) { - ( - Key::from_name_and_labels( - name, - labels!( - "pool" => self.name().to_string(), - ), - ), - Measurement::Gauge(value), - ) - } - - /// Collects the mempool stats. - pub(crate) fn collect(&self) -> Vec<(Key, Measurement)> { - let used = unsafe { ffi::rte_mempool_in_use_count(self.raw()) as i64 }; - let free = self.raw().size as i64 - used; - - vec![self.new_gauge("used", used), self.new_gauge("free", free)] - } -} - -/// Send mempool stats across threads. -unsafe impl Send for MempoolStats {} -unsafe impl Sync for MempoolStats {} diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 1f54ff3..2a64962 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -67,13 +67,8 @@ pub(crate) struct SocketId(raw::c_int); impl SocketId { /// A socket ID representing any NUMA socket. + #[allow(dead_code)] pub(crate) const ANY: Self = SocketId(-1); - - /// Returns the ID of the socket the current core is on. - #[inline] - pub(crate) fn current() -> SocketId { - unsafe { SocketId(cffi::rte_socket_id() as raw::c_int) } - } } impl fmt::Debug for SocketId { @@ -152,6 +147,7 @@ pub(crate) struct LcoreId(raw::c_uint); impl LcoreId { /// Any lcore to indicate that no thread affinity is set. + #[cfg(test)] pub(crate) const ANY: Self = LcoreId(raw::c_uint::MAX); /// Returns the ID of the current execution unit or `LcoreId::ANY` when diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index b0cac1a..d1bcc25 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -20,8 +20,6 @@ pub(crate) mod dpdk; #[cfg(feature = "pcap-dump")] pub(crate) mod pcap; -pub(crate) use capsule_ffi::*; - use crate::warn; use anyhow::Result; use std::error::Error; diff --git a/core/src/lib.rs b/core/src/lib.rs index b42ac8a..df02230 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -83,7 +83,6 @@ //! ## Feature flags //! //! - `default`: Enables metrics by default. -//! - `metrics`: Enables automatic [`metrics`] collection. //! - `pcap-dump`: Enables capturing port traffic to `pcap` files. //! - `testils`: Enables utilities for unit testing and benchmarking. //! - `full`: Enables all features. @@ -94,7 +93,6 @@ //! - [nat64]: IPv6 to IPv4 NAT gateway example. //! - [ping4d]: Ping4 daemon example. //! - [pktdump]: Packet dump example. -//! - [signals]: Linux signal handling example. //! - [skeleton]: Base skeleton example. //! - [syn-flood]: TCP SYN flood example. //! @@ -114,35 +112,21 @@ //! [nat64]: https://github.com/capsule-rs/capsule/tree/master/examples/nat64 //! [ping4d]: https://github.com/capsule-rs/capsule/tree/master/examples/ping4d //! [pktdump]: https://github.com/capsule-rs/capsule/tree/master/examples/pktdump -//! [signals]: https://github.com/capsule-rs/capsule/tree/master/examples/signals //! [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 macros extern crate self as capsule; -pub mod batch; -pub mod config; -mod dpdk; pub(crate) mod ffi; mod macros; -#[cfg(feature = "metrics")] -#[cfg_attr(docsrs, doc(cfg(all(feature = "default", feature = "metrics"))))] -pub mod metrics; pub mod net; pub mod packets; -#[cfg(feature = "pcap-dump")] -#[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] -mod pcap; -pub mod rt2; -mod runtime; +pub mod runtime; #[cfg(any(test, feature = "testils"))] #[cfg_attr(docsrs, doc(cfg(feature = "testils")))] 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}; diff --git a/core/src/metrics.rs b/core/src/metrics.rs deleted file mode 100644 index 8323297..0000000 --- a/core/src/metrics.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -//! Exposes framework metrics, including port, kni, mempool, and pipeline -//! metrics. -//! -//! # Port Metrics -//! -//! * `port.packets`, total number of successfully received or transmitted -//! packets. -//! * `port.octets`, total number of successfully received or transmitted -//! bytes. -//! * `port.dropped`, total number of packets dropped because the receive -//! or transmit queues are full. -//! * `port.errors`, total number of erroneous received packets or packets -//! failed to transmit. -//! * `port.no_mbuf`, total number of packets dropped due to mbuf allocation -//! failures. -//! -//! Each metric is labeled with the port name and a direction, which can be -//! either RX or TX. `port.packets` and `port.dropped` are tracked per core -//! and labeled with the core id. The others are tracked by only the overall -//! metrics. -//! -//! -//! # KNI Metrics -//! -//! * `kni.packets`, total number of successfully received or transmitted -//! packets. -//! * `kni.octets`, total number of successfully received or transmitted bytes. -//! * `kni.dropped`, total number of packets dropped because the transmit -//! queue is full. -//! -//! Each metric is labeled with the KNI interface name and a direction, which -//! can be either RX or TX. -//! -//! -//! # Mempool Metrics -//! -//! * `mempool.used`, total number of mbufs which have been allocated from -//! the mempool. -//! * `mempool.free`, total number of mbufs available for allocation. -//! -//! Each metric is labeled with the mempool name. -//! -//! -//! # Pipeline Metrics -//! -//! * `pipeline.runs`, total number of times the pipeline executes. -//! * `pipeline.processed`, total number of successfully processed packets. -//! * `pipeline.dropped`, total number of packets intentionally dropped. -//! * `pipeline.errors`, total number of packet dropped due to processing -//! errors. -//! -//! Each metric is tracked per core and labeled with the core id and the -//! pipeline name. If the pipeline doesn't have a name, it will be labeled -//! as "default". - -// re-export some metrics types to make feature gated imports easier. -pub(crate) use metrics_core::{labels, Key}; -pub(crate) use metrics_runtime::data::Counter; -pub(crate) use metrics_runtime::Measurement; - -use crate::dpdk::{Mempool, MempoolStats, Port}; -use crate::warn; -use anyhow::{anyhow, Result}; -use metrics_runtime::{Receiver, Sink}; -use once_cell::sync::{Lazy, OnceCell}; - -/// The metrics store. -static RECEIVER: OnceCell = OnceCell::new(); - -/// Safely initializes the metrics store. Because the receiver builder could -/// potentially fail, the `Lazy` convenience type is not safe. -/// -/// Also very important that `init` is not called twice. -pub(crate) fn init() -> Result<()> { - let receiver = Receiver::builder().build()?; - - RECEIVER - .set(receiver) - .map_err(|_| anyhow!("already initialized."))?; - Ok(()) -} - -/// Registers DPDK collected port stats with the metrics store. -pub(crate) fn register_port_stats(ports: &[Port]) { - let stats = ports.iter().map(Port::stats).collect::>(); - SINK.clone().proxy("port", move || { - stats - .iter() - .flat_map(|s| { - s.collect().unwrap_or_else(|err| { - warn!(message = "failed to collect stats.", port = s.name(), ?err); - Vec::new() - }) - }) - .collect() - }); -} - -/// Registers collected mempool stats with the metrics store. -pub(crate) fn register_mempool_stats(mempools: &[Mempool]) { - let stats = mempools.iter().map(Mempool::stats).collect::>(); - SINK.clone().proxy("mempool", move || { - stats.iter().flat_map(MempoolStats::collect).collect() - }); -} - -/// Returns the global metrics store. -/// -/// Metrics are managed using [metrics-rs]. The application can use this to -/// access framework metrics or to add new application metrics. -/// -/// # Panics -/// -/// Panics if `Runtime::build` is not called first. -/// -/// [metrics-rs]: https://github.com/metrics-rs -pub fn global() -> &'static Receiver { - unsafe { RECEIVER.get_unchecked() } -} - -/// The root sink for all framework metrics. -pub(crate) static SINK: Lazy = Lazy::new(|| global().sink().scoped("capsule")); diff --git a/core/src/packets/arp.rs b/core/src/packets/arp.rs index d674404..5c6bc08 100644 --- a/core/src/packets/arp.rs +++ b/core/src/packets/arp.rs @@ -18,10 +18,10 @@ //! Address Resolution Protocol. +use crate::ensure; use crate::net::MacAddr; use crate::packets::types::u16be; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::Ipv4Addr; @@ -533,12 +533,6 @@ pub trait HardwareAddr: SizeOf + Copy + fmt::Display { fn default() -> Self; } -impl SizeOf for MacAddr { - fn size_of() -> usize { - 6 - } -} - impl HardwareAddr for MacAddr { fn addr_type() -> HardwareType { HardwareTypes::Ethernet @@ -561,12 +555,6 @@ pub trait ProtocolAddr: SizeOf + Copy + fmt::Display { fn default() -> Self; } -impl SizeOf for Ipv4Addr { - fn size_of() -> usize { - 4 - } -} - impl ProtocolAddr for Ipv4Addr { fn addr_type() -> ProtocolType { ProtocolTypes::Ipv4 @@ -631,8 +619,8 @@ impl Default for ArpHeader { #[cfg(test)] mod tests { use super::*; + use crate::packets::Mbuf; use crate::testils::byte_arrays::ARP4_PACKET; - use crate::Mbuf; #[test] fn size_of_arp_header() { diff --git a/core/src/packets/ethernet.rs b/core/src/packets/ethernet.rs index eb3a6e9..c1681f4 100644 --- a/core/src/packets/ethernet.rs +++ b/core/src/packets/ethernet.rs @@ -16,12 +16,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::dpdk::BufferError; +use crate::ensure; use crate::net::MacAddr; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::{ensure, Mbuf, SizeOf}; -use anyhow::Result; +use crate::packets::{Internal, Mbuf, Packet, SizeOf}; +use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; @@ -293,7 +292,7 @@ impl Packet for Ethernet { // header will cause a panic. ensure!( packet.mbuf().data_len() >= packet.header_len(), - BufferError::OutOfBuffer(packet.header_len(), packet.mbuf().data_len()) + anyhow!("header size exceeds remaining buffer size.") ); Ok(packet) diff --git a/core/src/packets/icmp/v4/echo_reply.rs b/core/src/packets/icmp/v4/echo_reply.rs index 71c3ac6..8199d24 100644 --- a/core/src/packets/icmp/v4/echo_reply.rs +++ b/core/src/packets/icmp/v4/echo_reply.rs @@ -18,8 +18,7 @@ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -220,8 +219,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ip::v4::Ipv4; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_echo_reply_body() { diff --git a/core/src/packets/icmp/v4/echo_request.rs b/core/src/packets/icmp/v4/echo_request.rs index cd08593..cfca15d 100644 --- a/core/src/packets/icmp/v4/echo_request.rs +++ b/core/src/packets/icmp/v4/echo_request.rs @@ -18,8 +18,7 @@ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -221,8 +220,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ip::v4::Ipv4; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_echo_request_body() { diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index 511e872..fda5e11 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -29,11 +29,11 @@ pub use self::redirect::*; pub use self::time_exceeded::*; pub use capsule_macros::Icmpv4Packet; +use crate::ensure; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::ProtocolNumbers; use crate::packets::types::u16be; -use crate::packets::{checksum, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{checksum, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; @@ -447,9 +447,8 @@ pub trait Icmpv4Packet { mod tests { use super::*; use crate::packets::ip::v4::Ipv4; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_UDP_PACKET}; - use crate::Mbuf; #[test] fn size_of_icmpv4_header() { diff --git a/core/src/packets/icmp/v4/redirect.rs b/core/src/packets/icmp/v4/redirect.rs index 8de0c15..96e52e5 100644 --- a/core/src/packets/icmp/v4/redirect.rs +++ b/core/src/packets/icmp/v4/redirect.rs @@ -18,8 +18,7 @@ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; use crate::packets::ip::v4::IPV4_MIN_MTU; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::net::Ipv4Addr; @@ -217,9 +216,8 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ip::v4::Ipv4; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::IPV4_TCP_PACKET; - use crate::Mbuf; #[test] fn size_of_redirect_body() { diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs index affd0ad..4fd9b05 100644 --- a/core/src/packets/icmp/v4/time_exceeded.rs +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; use crate::packets::ip::v4::IPV4_MIN_MTU; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -181,9 +180,8 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ip::v4::Ipv4; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::IPV4_TCP_PACKET; - use crate::Mbuf; #[test] fn size_of_time_exceeded_body() { diff --git a/core/src/packets/icmp/v6/echo_reply.rs b/core/src/packets/icmp/v6/echo_reply.rs index c2ab433..97f6ba0 100644 --- a/core/src/packets/icmp/v6/echo_reply.rs +++ b/core/src/packets/icmp/v6/echo_reply.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -219,8 +218,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_echo_reply_body() { diff --git a/core/src/packets/icmp/v6/echo_request.rs b/core/src/packets/icmp/v6/echo_request.rs index 5e09813..4ac849d 100644 --- a/core/src/packets/icmp/v6/echo_request.rs +++ b/core/src/packets/icmp/v6/echo_request.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -220,8 +219,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_echo_request_body() { diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index 2fa6814..5cbf777 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -18,25 +18,25 @@ //! Internet Control Message Protocol for IPv6. -mod destination_unreachable; mod echo_reply; mod echo_request; pub mod ndp; mod time_exceeded; mod too_big; +mod unreachable; -pub use self::destination_unreachable::*; pub use self::echo_reply::*; pub use self::echo_request::*; pub use self::time_exceeded::*; pub use self::too_big::*; +pub use self::unreachable::*; pub use capsule_macros::Icmpv6Packet; +use crate::ensure; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::ip::ProtocolNumbers; use crate::packets::types::u16be; -use crate::packets::{checksum, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{checksum, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; @@ -500,9 +500,8 @@ mod tests { use super::*; use crate::packets::icmp::v6::ndp::RouterAdvertisement; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::{ICMPV6_PACKET, IPV6_TCP_PACKET, ROUTER_ADVERT_PACKET}; - use crate::Mbuf; #[test] fn size_of_icmpv6_header() { diff --git a/core/src/packets/icmp/v6/ndp/mod.rs b/core/src/packets/icmp/v6/ndp/mod.rs index f955821..b4ed14d 100644 --- a/core/src/packets/icmp/v6/ndp/mod.rs +++ b/core/src/packets/icmp/v6/ndp/mod.rs @@ -43,10 +43,9 @@ pub use self::redirect::*; pub use self::router_advert::*; pub use self::router_solicit::*; -use crate::dpdk::BufferError; -use crate::packets::{Immutable, Internal, Packet}; -use crate::{ensure, Mbuf, SizeOf}; -use anyhow::Result; +use crate::ensure; +use crate::packets::{Immutable, Internal, Mbuf, Packet, SizeOf}; +use anyhow::{anyhow, Result}; use std::fmt; use std::marker::PhantomData; use std::ptr::NonNull; @@ -174,8 +173,7 @@ impl<'a> ImmutableNdpOption<'a> { /// /// # Errors /// - /// Returns `BufferError::OutOfBuffer` if the buffer does not have - /// enough free space. + /// Returns an error if the buffer does not have enough free space. #[inline] fn new(mbuf: &'a mut Mbuf, offset: usize) -> Result { let tuple = mbuf.read_data(offset)?; @@ -189,7 +187,7 @@ impl<'a> ImmutableNdpOption<'a> { // indicated by the length field stored in the option itself ensure!( option.mbuf.len() >= option.end_offset(), - BufferError::OutOfBuffer(option.end_offset(), option.mbuf.len()) + anyhow!("option size exceeds remaining buffer size.") ); Ok(option) @@ -297,8 +295,7 @@ impl<'a> MutableNdpOption<'a> { /// /// # Errors /// - /// Returns `BufferError::OutOfBuffer` if the buffer does not have - /// enough free space. + /// Returns an error if the buffer does not have enough free space. #[inline] fn new(mbuf: &'a mut Mbuf, offset: usize) -> Result { let tuple = mbuf.read_data(offset)?; @@ -312,7 +309,7 @@ impl<'a> MutableNdpOption<'a> { // indicated by the length field stored in the option itself ensure!( option.mbuf.len() >= option.end_offset(), - BufferError::OutOfBuffer(option.end_offset(), option.mbuf.len()) + anyhow!("option size exceeds remaining buffer size.") ); Ok(option) diff --git a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs index c8c9365..645e09c 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs @@ -20,8 +20,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::net::Ipv6Addr; @@ -272,8 +271,7 @@ impl Default for NeighborAdvertisementBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_neighbor_advertisement_body() { diff --git a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs index bc96e6b..169e0a4 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs @@ -20,8 +20,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::net::Ipv6Addr; @@ -187,8 +186,7 @@ impl Default for NeighborSolicitationBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_neighbor_solicitation_body() { diff --git a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs index 1c5eeb9..de5b26d 100644 --- a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs +++ b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs @@ -16,10 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::net::MacAddr; use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes}; -use crate::packets::Internal; -use crate::{ensure, Mbuf, SizeOf}; +use crate::packets::{Internal, Mbuf, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; diff --git a/core/src/packets/icmp/v6/ndp/options/mtu.rs b/core/src/packets/icmp/v6/ndp/options/mtu.rs index 0d2f8e2..e53cc31 100644 --- a/core/src/packets/icmp/v6/ndp/options/mtu.rs +++ b/core/src/packets/icmp/v6/ndp/options/mtu.rs @@ -16,10 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes}; use crate::packets::types::{u16be, u32be}; -use crate::packets::Internal; -use crate::{ensure, Mbuf, SizeOf}; +use crate::packets::{Internal, Mbuf, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; diff --git a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs index bcd3167..48b4509 100644 --- a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs +++ b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs @@ -16,10 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes}; use crate::packets::types::u32be; -use crate::packets::Internal; -use crate::{ensure, Mbuf, SizeOf}; +use crate::packets::{Internal, Mbuf, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::Ipv6Addr; diff --git a/core/src/packets/icmp/v6/ndp/options/redirected.rs b/core/src/packets/icmp/v6/ndp/options/redirected.rs index b16e094..0e31b68 100644 --- a/core/src/packets/icmp/v6/ndp/options/redirected.rs +++ b/core/src/packets/icmp/v6/ndp/options/redirected.rs @@ -16,10 +16,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes}; use crate::packets::types::{u16be, u32be}; -use crate::packets::Internal; -use crate::{ensure, Mbuf, SizeOf}; +use crate::packets::{Internal, Mbuf, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 3c25382..5d6079d 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -20,8 +20,7 @@ use super::{NdpPacket, RedirectedHeader}; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::net::Ipv6Addr; @@ -243,8 +242,7 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_redirect_body() { diff --git a/core/src/packets/icmp/v6/ndp/router_advert.rs b/core/src/packets/icmp/v6/ndp/router_advert.rs index 630af35..8447e24 100644 --- a/core/src/packets/icmp/v6/ndp/router_advert.rs +++ b/core/src/packets/icmp/v6/ndp/router_advert.rs @@ -20,8 +20,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::{u16be, u32be}; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -303,8 +302,7 @@ struct RouterAdvertisementBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_router_advertisement_body() { diff --git a/core/src/packets/icmp/v6/ndp/router_solicit.rs b/core/src/packets/icmp/v6/ndp/router_solicit.rs index adf9a03..bb12798 100644 --- a/core/src/packets/icmp/v6/ndp/router_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/router_solicit.rs @@ -20,8 +20,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -145,8 +144,7 @@ struct RouterSolicitationBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; - use crate::Mbuf; + use crate::packets::{Ethernet, Mbuf}; #[test] fn size_of_router_solicitation_body() { diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index e1c1e26..2d590bc 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -165,9 +164,8 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::IPV6_TCP_PACKET; - use crate::Mbuf; #[test] fn size_of_time_exceeded_body() { diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index bc5c5cd..3c66147 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -190,9 +189,8 @@ struct PacketTooBigBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::IPV6_TCP_PACKET; - use crate::Mbuf; #[test] fn size_of_packet_too_big() { diff --git a/core/src/packets/icmp/v6/destination_unreachable.rs b/core/src/packets/icmp/v6/unreachable.rs similarity index 98% rename from core/src/packets/icmp/v6/destination_unreachable.rs rename to core/src/packets/icmp/v6/unreachable.rs index e23d29a..0b2d447 100644 --- a/core/src/packets/icmp/v6/destination_unreachable.rs +++ b/core/src/packets/icmp/v6/unreachable.rs @@ -19,8 +19,7 @@ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; -use crate::packets::{Internal, Packet}; -use crate::SizeOf; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; use std::ptr::NonNull; @@ -165,9 +164,8 @@ struct DestinationUnreachableBody { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::IPV6_TCP_PACKET; - use crate::Mbuf; #[test] fn size_of_destination_unreachable_body() { diff --git a/core/src/packets/ip/v4.rs b/core/src/packets/ip/v4.rs index 3605e3b..a9558a3 100644 --- a/core/src/packets/ip/v4.rs +++ b/core/src/packets/ip/v4.rs @@ -18,11 +18,11 @@ //! Internet Protocol v4. +use crate::ensure; use crate::packets::checksum::{self, PseudoHeader}; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::u16be; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv4Addr}; @@ -612,8 +612,8 @@ impl Default for Ipv4Header { mod tests { use super::*; use crate::packets::ip::ProtocolNumbers; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_UDP_PACKET, IPV6_TCP_PACKET}; - use crate::Mbuf; #[test] fn size_of_ipv4_header() { diff --git a/core/src/packets/ip/v6/fragment.rs b/core/src/packets/ip/v6/fragment.rs index 15e90ce..1d926c0 100644 --- a/core/src/packets/ip/v6/fragment.rs +++ b/core/src/packets/ip/v6/fragment.rs @@ -16,12 +16,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::checksum::PseudoHeader; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::ip::{IpPacket, ProtocolNumber, ProtocolNumbers}; use crate::packets::types::{u16be, u32be}; -use crate::packets::{Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::IpAddr; @@ -330,9 +330,8 @@ struct FragmentHeader { mod tests { use super::*; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, IPV6_TCP_PACKET}; - use crate::Mbuf; #[test] fn size_of_fragment_header() { diff --git a/core/src/packets/ip/v6/mod.rs b/core/src/packets/ip/v6/mod.rs index 5cf78f7..2938ce4 100644 --- a/core/src/packets/ip/v6/mod.rs +++ b/core/src/packets/ip/v6/mod.rs @@ -24,11 +24,11 @@ mod srh; pub use self::fragment::*; pub use self::srh::*; +use crate::ensure; use crate::packets::checksum::PseudoHeader; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::{u16be, u32be}; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv6Addr}; @@ -467,8 +467,8 @@ impl Default for Ipv6Header { mod tests { use super::*; use crate::packets::ip::ProtocolNumbers; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_UDP_PACKET, IPV6_TCP_PACKET}; - use crate::Mbuf; #[test] fn size_of_ipv6_header() { diff --git a/core/src/packets/ip/v6/srh.rs b/core/src/packets/ip/v6/srh.rs index 9cefbb9..446d7e2 100644 --- a/core/src/packets/ip/v6/srh.rs +++ b/core/src/packets/ip/v6/srh.rs @@ -16,12 +16,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::checksum::PseudoHeader; use crate::packets::ip::v6::Ipv6Packet; use crate::packets::ip::{IpPacket, ProtocolNumber, ProtocolNumbers}; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv6Addr}; @@ -525,9 +525,8 @@ mod tests { use super::*; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::ProtocolNumbers; - use crate::packets::{Ethernet, Tcp, Tcp6}; + use crate::packets::{Ethernet, Mbuf, Tcp, Tcp6}; use crate::testils::byte_arrays::{IPV6_TCP_PACKET, SR_TCP_PACKET}; - use crate::Mbuf; #[test] fn size_of_segment_routing_header() { diff --git a/core/src/dpdk/mbuf.rs b/core/src/packets/mbuf.rs similarity index 92% rename from core/src/dpdk/mbuf.rs rename to core/src/packets/mbuf.rs index 74ed1b7..b28aa13 100644 --- a/core/src/dpdk/mbuf.rs +++ b/core/src/packets/mbuf.rs @@ -16,9 +16,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +use super::{Internal, Packet, SizeOf}; use crate::ffi::dpdk::{self, MbufPtr}; -use crate::packets::{Internal, Packet}; -use crate::rt2::Mempool; +use crate::runtime::Mempool; use crate::{ensure, trace}; use anyhow::Result; use capsule_ffi as cffi; @@ -28,58 +28,6 @@ use std::ptr::{self, NonNull}; use std::slice; use thiserror::Error; -/// A trait for returning the size of a type in bytes. -/// -/// Size of the structs are used for bound checks when reading and writing -/// packets. -/// -/// -/// # Derivable -/// -/// The `SizeOf` trait can be used with `#[derive]` and defaults to -/// `std::mem::size_of::()`. -/// -/// ``` -/// #[derive(SizeOf)] -/// pub struct Ipv4Header { -/// ... -/// } -/// ``` -pub trait SizeOf { - /// Returns the size of a type in bytes. - fn size_of() -> usize; -} - -impl SizeOf for () { - fn size_of() -> usize { - std::mem::size_of::<()>() - } -} - -impl SizeOf for u8 { - fn size_of() -> usize { - std::mem::size_of::() - } -} - -impl SizeOf for [u8; 2] { - fn size_of() -> usize { - std::mem::size_of::<[u8; 2]>() - } -} - -impl SizeOf for [u8; 16] { - fn size_of() -> usize { - std::mem::size_of::<[u8; 16]>() - } -} - -impl SizeOf for ::std::net::Ipv6Addr { - fn size_of() -> usize { - std::mem::size_of::() - } -} - /// Error indicating buffer access failures. #[derive(Debug, Error)] pub(crate) enum BufferError { @@ -178,14 +126,6 @@ impl Mbuf { } } - /// Creates a new `Mbuf` from a raw pointer. - #[inline] - pub(crate) unsafe fn from_ptr(ptr: *mut cffi::rte_mbuf) -> Self { - Mbuf { - inner: MbufInner::Original(NonNull::new_unchecked(ptr)), - } - } - /// Returns the raw struct needed for FFI calls. #[inline] fn raw(&self) -> &cffi::rte_mbuf { @@ -430,17 +370,6 @@ impl Mbuf { ptr.into() } - /// Acquires the underlying raw struct pointer. - /// - /// The `Mbuf` is consumed. It is the caller's the responsibility to - /// free the raw pointer after use. Otherwise the buffer is leaked. - #[inline] - pub(crate) fn into_ptr(self) -> *mut cffi::rte_mbuf { - let ptr = self.inner.ptr().as_ptr(); - mem::forget(self); - ptr - } - /// Allocates a Vec of `Mbuf`s of `len` size. /// /// # Errors diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index de17b30..0f61fab 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -23,14 +23,18 @@ pub mod checksum; mod ethernet; pub mod icmp; pub mod ip; +mod mbuf; +mod size_of; mod tcp; pub mod types; mod udp; pub use self::ethernet::*; +pub use self::mbuf::*; +pub use self::size_of::*; pub use self::tcp::*; pub use self::udp::*; -pub use crate::dpdk::Mbuf; +pub use capsule_macros::SizeOf; use anyhow::{Context, Result}; use std::fmt; diff --git a/core/src/packets/size_of.rs b/core/src/packets/size_of.rs new file mode 100644 index 0000000..f7a6a30 --- /dev/null +++ b/core/src/packets/size_of.rs @@ -0,0 +1,85 @@ +/* +* Copyright 2019 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +use crate::net::MacAddr; +use std::mem; +use std::net::{Ipv4Addr, Ipv6Addr}; + +/// A trait for returning the size of a type in bytes. +/// +/// Size of the structs are used for bound checks when reading and writing +/// packets. +/// +/// +/// # Derivable +/// +/// The `SizeOf` trait can be used with `#[derive]` and defaults to +/// `std::mem::size_of::()`. +/// +/// ``` +/// #[derive(SizeOf)] +/// pub struct Ipv4Header { +/// ... +/// } +/// ``` +pub trait SizeOf { + /// Returns the size of a type in bytes. + fn size_of() -> usize; +} + +impl SizeOf for () { + fn size_of() -> usize { + mem::size_of::<()>() + } +} + +impl SizeOf for u8 { + fn size_of() -> usize { + mem::size_of::() + } +} + +impl SizeOf for [u8; 2] { + fn size_of() -> usize { + mem::size_of::<[u8; 2]>() + } +} + +impl SizeOf for [u8; 16] { + fn size_of() -> usize { + mem::size_of::<[u8; 16]>() + } +} + +impl SizeOf for MacAddr { + fn size_of() -> usize { + mem::size_of::() + } +} + +impl SizeOf for Ipv4Addr { + fn size_of() -> usize { + mem::size_of::() + } +} + +impl SizeOf for Ipv6Addr { + fn size_of() -> usize { + mem::size_of::() + } +} diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index d0cec42..818649e 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -16,12 +16,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::{u16be, u32be}; -use crate::packets::{checksum, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{checksum, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::IpAddr; @@ -665,9 +665,8 @@ impl Default for TcpHeader { mod tests { use super::*; use crate::packets::ip::v6::SegmentRouting; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET, SR_TCP_PACKET}; - use crate::Mbuf; use std::net::{Ipv4Addr, Ipv6Addr}; #[test] diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index c146328..49c7ba1 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -16,12 +16,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::ensure; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::u16be; -use crate::packets::{checksum, Internal, Packet}; -use crate::{ensure, SizeOf}; +use crate::packets::{checksum, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::IpAddr; @@ -386,9 +386,8 @@ struct UdpHeader { #[cfg(test)] mod tests { use super::*; - use crate::packets::Ethernet; + use crate::packets::{Ethernet, Mbuf}; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; - use crate::Mbuf; use std::net::{Ipv4Addr, Ipv6Addr}; #[test] diff --git a/core/src/pcap.rs b/core/src/pcap.rs deleted file mode 100644 index 0be1032..0000000 --- a/core/src/pcap.rs +++ /dev/null @@ -1,340 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use crate::dpdk::{CoreId, DpdkError, PortId, RxTxQueue}; -use crate::ffi::{self, AsStr, ToCString, ToResult}; -use crate::{debug, error}; -use anyhow::Result; -use std::fmt; -use std::os::raw; -use std::ptr::NonNull; -use thiserror::Error; - -// DLT_EN10MB; LINKTYPE_ETHERNET=1; 10MB is historical -const DLT_EN10MB: raw::c_int = 1; -const PCAP_SNAPSHOT_LEN: raw::c_int = ffi::RTE_MBUF_DEFAULT_BUF_SIZE as raw::c_int; - -/// An error generated in `libpcap`. -/// -/// When an FFI call fails, either a specified error message or an `errno` is -/// translated into a `PcapError`. -#[derive(Debug, Error)] -#[error("{0}")] -struct PcapError(String); - -impl PcapError { - /// Returns the `PcapError` with the given error message. - #[inline] - fn new(msg: &str) -> Self { - PcapError(msg.into()) - } - - /// Returns the `PcapError` pertaining to the last `libpcap` error. - #[inline] - fn get_error(handle: NonNull) -> Self { - let msg = unsafe { ffi::pcap_geterr(handle.as_ptr()) }; - PcapError::new((msg as *const raw::c_char).as_str()) - } -} - -/// Packet Capture (`pcap`) writer/dumper for packets -struct Pcap { - path: String, - handle: NonNull, - dumper: NonNull, -} - -impl Pcap { - /// Creates a file for dumping packets into from a given file path. - fn create(path: &str) -> Result { - unsafe { - let handle = ffi::pcap_open_dead(DLT_EN10MB, PCAP_SNAPSHOT_LEN) - .into_result(|_| PcapError::new("Cannot create packet capture handle."))?; - let dumper = ffi::pcap_dump_open(handle.as_ptr(), path.into_cstring().as_ptr()) - .into_result(|_| PcapError::get_error(handle)) - .map_err(|err| { - ffi::pcap_close(handle.as_ptr()); - err - })?; - - debug!("PCAP file {} created", path); - - Ok(Pcap { - path: path.to_string(), - handle, - dumper, - }) - } - } - - /// Append to already-existing file for dumping packets into from a given - /// file path. - fn append(path: &str) -> Result { - if !std::path::Path::new(path).exists() { - return Err(PcapError::new("Pcap filename path does not exist.").into()); - } - - unsafe { - let handle = ffi::pcap_open_dead(DLT_EN10MB, PCAP_SNAPSHOT_LEN) - .into_result(|_| PcapError::new("Cannot create packet capture handle."))?; - let dumper = ffi::pcap_dump_open_append(handle.as_ptr(), path.into_cstring().as_ptr()) - .into_result(|_| PcapError::get_error(handle)) - .map_err(|err| { - ffi::pcap_close(handle.as_ptr()); - err - })?; - Ok(Pcap { - path: path.to_string(), - handle, - dumper, - }) - } - } - - /// Write packets to `pcap` file handler. - unsafe fn write(&self, ptrs: &[*mut ffi::rte_mbuf]) -> Result<()> { - ptrs.iter().for_each(|&p| self.dump_packet(p)); - - self.flush() - } - - unsafe fn dump_packet(&self, ptr: *mut ffi::rte_mbuf) { - let mut pcap_hdr = ffi::pcap_pkthdr::default(); - pcap_hdr.len = (*ptr).data_len as u32; - pcap_hdr.caplen = pcap_hdr.len; - - // If this errors, we'll still want to write packet(s) to the pcap, - let _ = libc::gettimeofday( - &mut pcap_hdr.ts as *mut ffi::timeval as *mut libc::timeval, - std::ptr::null_mut(), - ); - - ffi::pcap_dump( - self.dumper.as_ptr() as *mut raw::c_uchar, - &pcap_hdr, - ((*ptr).buf_addr as *mut u8).offset((*ptr).data_off as isize), - ); - } - - fn flush(&self) -> Result<()> { - unsafe { - ffi::pcap_dump_flush(self.dumper.as_ptr()) - .into_result(|_| PcapError::new("Cannot flush packets to packet capture")) - .map(|_| ()) - } - } -} - -impl<'a> fmt::Debug for Pcap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("pcap").field("path", &self.path).finish() - } -} - -impl Drop for Pcap { - fn drop(&mut self) { - unsafe { - ffi::pcap_dump_close(self.dumper.as_ptr()); - ffi::pcap_close(self.handle.as_ptr()); - } - } -} - -/// Default formatting for pcap files. -fn format_pcap_file(port_name: &str, core_id: usize, tx_or_rx: &str) -> String { - format!("port-{}-core{}-{}.pcap", port_name, core_id, tx_or_rx) -} - -/// Generate PCAP files for rx/tx queues per port and per core. -pub(crate) fn capture_queue( - port_id: PortId, - port_name: &str, - core: CoreId, - q: RxTxQueue, -) -> Result<()> { - match q { - RxTxQueue::Rx(rxq) => { - Pcap::create(&format_pcap_file(port_name, core.raw(), "rx"))?; - unsafe { - ffi::rte_eth_add_rx_callback( - port_id.raw(), - rxq.raw(), - Some(append_and_write_rx), - port_name.into_cstring().into_raw() as *mut raw::c_void, - ) - .into_result(|_| DpdkError::new())?; - } - } - RxTxQueue::Tx(txq) => { - Pcap::create(&format_pcap_file(port_name, core.raw(), "tx"))?; - unsafe { - ffi::rte_eth_add_tx_callback( - port_id.raw(), - txq.raw(), - Some(append_and_write_tx), - port_name.into_cstring().into_raw() as *mut raw::c_void, - ) - .into_result(|_| DpdkError::new())?; - } - } - } - - Ok(()) -} - -/// Callback fn passed to `rte_eth_add_rx_callback`, which is called on RX -/// with a burst of packets that have been received on a given port and queue. -unsafe extern "C" fn append_and_write_rx( - _port_id: u16, - _queue_id: u16, - pkts: *mut *mut ffi::rte_mbuf, - num_pkts: u16, - _max_pkts: u16, - user_param: *mut raw::c_void, -) -> u16 { - append_and_write( - (user_param as *const raw::c_char).as_str(), - "rx", - std::slice::from_raw_parts_mut(pkts, num_pkts as usize), - ); - num_pkts -} - -/// Callback fn passed to `rte_eth_add_tx_callback`, which is called on TX -/// with a burst of packets immediately before the packets are put onto -/// the hardware queue for transmission. -unsafe extern "C" fn append_and_write_tx( - _port_id: u16, - _queue_id: u16, - pkts: *mut *mut ffi::rte_mbuf, - num_pkts: u16, - user_param: *mut raw::c_void, -) -> u16 { - append_and_write( - (user_param as *const raw::c_char).as_str(), - "tx", - std::slice::from_raw_parts_mut(pkts, num_pkts as usize), - ); - num_pkts -} - -/// Executed within the rx/tx callback functions for writing out to pcap -/// file(s). -fn append_and_write(port: &str, tx_or_rx: &str, ptrs: &[*mut ffi::rte_mbuf]) { - let path = format_pcap_file(port, CoreId::current().raw(), tx_or_rx); - if let Err(err) = Pcap::append(path.as_str()).and_then(|pcap| unsafe { pcap.write(&ptrs) }) { - error!( - message = "Cannot write/append to pcap file.", - pcap = path.as_str(), - ?err - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testils::byte_arrays::IPV4_UDP_PACKET; - use crate::Mbuf; - use std::fs; - use std::ptr; - - fn read_pcap_plen(path: &str) -> u32 { - let mut errbuf = [0i8; ffi::RTE_MBUF_DEFAULT_BUF_SIZE as usize]; - let handle = - unsafe { ffi::pcap_open_offline(path.into_cstring().as_ptr(), errbuf.as_mut_ptr()) }; - - let mut header: *mut ffi::pcap_pkthdr = ptr::null_mut(); - let mut buf: *const libc::c_uchar = ptr::null(); - - let mut ret = 0; - - while let 1 = unsafe { ffi::pcap_next_ex(handle, &mut header, &mut buf) } { - ret += unsafe { (*header).caplen } - } - - unsafe { - ffi::pcap_close(handle); - } - - ret - } - - fn cleanup(path: &str) { - fs::remove_file(path).unwrap(); - } - - #[capsule::test] - fn create_pcap_and_write_packet() { - let writer = Pcap::create("foo.pcap").unwrap(); - let udp = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); - let data_len = udp.data_len(); - - let res = unsafe { writer.write(&[udp.into_ptr()]) }; - - assert!(res.is_ok()); - let len = read_pcap_plen("foo.pcap"); - assert_eq!(data_len as u32, len); - cleanup("foo.pcap"); - } - - #[capsule::test] - fn create_pcap_and_write_packets() { - let writer = Pcap::create("foo1.pcap").unwrap(); - let udp = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); - let data_len1 = udp.data_len(); - let udp2 = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); - let data_len2 = udp2.data_len(); - - let packets = vec![udp.into_ptr(), udp2.into_ptr()]; - let res = unsafe { writer.write(&packets) }; - assert!(res.is_ok()); - let len = read_pcap_plen("foo1.pcap"); - assert_eq!((data_len1 + data_len2) as u32, len); - cleanup("foo1.pcap"); - } - - #[capsule::test] - fn append_to_pcap_and_write_packet() { - let open = Pcap::create("foo2.pcap"); - assert!(open.is_ok()); - - let udp = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); - let data_len = udp.data_len(); - - let writer = Pcap::append("foo2.pcap").unwrap(); - let res = unsafe { writer.write(&[udp.into_ptr()]) }; - - assert!(res.is_ok()); - let len = read_pcap_plen("foo2.pcap"); - assert_eq!(data_len as u32, len); - cleanup("foo2.pcap"); - } - - #[capsule::test] - fn append_to_wrong_pcap() { - let open = Pcap::create("foo3.pcap"); - assert!(open.is_ok()); - - // fails on append to uninitiated pcap - let res = Pcap::append("foo4.pcap"); - assert!(res.is_err()); - - cleanup("foo3.pcap"); - } -} diff --git a/core/src/rt2/mod.rs b/core/src/rt2/mod.rs deleted file mode 100644 index cd63ef9..0000000 --- a/core/src/rt2/mod.rs +++ /dev/null @@ -1,234 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -//! Capsule runtime. - -mod config; -mod lcore; -mod mempool; -#[cfg(feature = "pcap-dump")] -#[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] -mod pcap_dump; -mod port; - -pub use self::config::*; -pub(crate) use self::lcore::*; -pub use self::lcore::{Lcore, LcoreMap, LcoreNotFound}; -pub use self::mempool::Mempool; -pub(crate) use self::mempool::*; -pub use self::port::{Outbox, Port, PortError, PortMap}; - -use crate::ffi::dpdk::{self, LcoreId}; -use crate::packets::{Mbuf, Postmark}; -use crate::{debug, info}; -use anyhow::Result; -use async_channel::{self, Receiver, Sender}; -use std::fmt; -use std::mem::ManuallyDrop; -use std::ops::DerefMut; - -/// Trigger for the shutdown. -pub(crate) struct ShutdownTrigger(Sender<()>, Receiver<()>); - -impl ShutdownTrigger { - /// Creates a new shutdown trigger. - /// - /// Leverages the behavior of an async channel. When the sender is dropped - /// from scope, it closes the channel and causes the receiver side future - /// in the executor queue to resolve. - pub(crate) fn new() -> Self { - let (s, r) = async_channel::unbounded(); - Self(s, r) - } - - /// Returns a wait handle. - pub(crate) fn get_wait(&self) -> ShutdownWait { - ShutdownWait(self.1.clone()) - } - - /// Returns whether the trigger is being waited on. - pub(crate) fn is_waited(&self) -> bool { - // a receiver count greater than 1 indicating that there are receiver - // clones in scope, hence the trigger is being waited on. - self.0.receiver_count() > 1 - } - - /// Triggers the shutdown. - pub(crate) fn fire(self) { - drop(self.0) - } -} - -/// Shutdown wait handle. -pub(crate) struct ShutdownWait(Receiver<()>); - -impl ShutdownWait { - /// A future that waits till the shutdown trigger is fired. - pub(crate) async fn wait(&self) { - self.0.recv().await.unwrap_or(()) - } -} - -/// The Capsule runtime. -/// -/// The runtime initializes the underlying DPDK environment, and it also manages -/// the task scheduler that executes the packet processing tasks. -pub struct Runtime { - mempool: ManuallyDrop, - lcores: ManuallyDrop, - ports: ManuallyDrop, - #[cfg(feature = "pcap-dump")] - pcap_dump: ManuallyDrop, -} - -impl Runtime { - /// Returns the mempool. - /// - /// For simplicity, we currently only support one global Mempool. Multi- - /// socket support may be added in the future. - pub fn mempool(&self) -> &Mempool { - &self.mempool - } - - /// Returns the lcores. - pub fn lcores(&self) -> &LcoreMap { - &self.lcores - } - - /// Returns the configured ports. - pub fn ports(&self) -> &PortMap { - &self.ports - } - - /// Initializes a new runtime from config settings. - pub fn from_config(config: RuntimeConfig) -> Result { - info!("starting runtime."); - - debug!("initializing EAL ..."); - dpdk::eal_init(config.to_eal_args())?; - - debug!("initializing mempool ..."); - let socket = LcoreId::main().socket(); - let mut mempool = Mempool::new( - "mempool", - config.mempool.capacity, - config.mempool.cache_size, - socket, - )?; - debug!(?mempool); - - debug!("initializing lcore schedulers ..."); - let lcores = self::lcore_pool(); - - for lcore in lcores.iter() { - let mut ptr = mempool.ptr_mut().clone(); - lcore.block_on(async move { MEMPOOL.with(|tls| tls.set(ptr.deref_mut())) }); - } - - info!("initializing ports ..."); - let mut ports = Vec::new(); - for port in config.ports.iter() { - let mut port = port::Builder::for_device(&port.name, &port.device)? - .set_rxqs_txqs(port.rxqs, port.txqs)? - .set_promiscuous(port.promiscuous)? - .set_multicast(port.multicast)? - .set_rx_lcores(port.rx_cores.clone())? - .set_tx_lcores(port.tx_cores.clone())? - .build(&mut mempool)?; - - debug!(?port); - - if !port.tx_lcores().is_empty() { - port.spawn_tx_loops(&lcores)?; - } - - port.start()?; - ports.push(port); - } - let ports: PortMap = ports.into(); - - #[cfg(feature = "pcap-dump")] - let pcap_dump = self::pcap_dump::enable_pcap_dump(&config.data_dir(), &ports, &lcores)?; - - info!("runtime ready."); - - Ok(Runtime { - mempool: ManuallyDrop::new(mempool), - lcores: ManuallyDrop::new(lcores), - ports: ManuallyDrop::new(ports), - #[cfg(feature = "pcap-dump")] - pcap_dump: ManuallyDrop::new(pcap_dump), - }) - } - - /// Sets the packet processing pipeline for port. - pub fn set_port_pipeline(&self, port: &str, f: F) -> Result<()> - where - F: Fn(Mbuf) -> Result + Clone + Send + Sync + 'static, - { - let port = self.ports.get(port)?; - port.spawn_rx_loops(f, &self.lcores)?; - Ok(()) - } - - /// Starts the runtime execution. - pub fn execute(self) -> Result { - Ok(RuntimeGuard { runtime: self }) - } -} - -impl fmt::Debug for Runtime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Runtime") - .field("mempool", &self.mempool) - .finish() - } -} - -/// The RAII guard to stop and cleanup the runtime resources on drop. -pub struct RuntimeGuard { - runtime: Runtime, -} - -impl Drop for RuntimeGuard { - fn drop(&mut self) { - info!("shutting down runtime."); - - for port in self.runtime.ports.iter_mut() { - port.stop(); - } - - unsafe { - #[cfg(feature = "pcap-dump")] - ManuallyDrop::drop(&mut self.runtime.pcap_dump); - ManuallyDrop::drop(&mut self.runtime.ports); - ManuallyDrop::drop(&mut self.runtime.lcores); - ManuallyDrop::drop(&mut self.runtime.mempool); - } - - debug!("freeing EAL ..."); - let _ = dpdk::eal_cleanup(); - info!("runtime shutdown."); - } -} - -impl fmt::Debug for RuntimeGuard { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RuntimeGuard") - } -} diff --git a/core/src/rt2/config.rs b/core/src/runtime/config.rs similarity index 100% rename from core/src/rt2/config.rs rename to core/src/runtime/config.rs diff --git a/core/src/runtime/core_map.rs b/core/src/runtime/core_map.rs deleted file mode 100644 index bbf10ff..0000000 --- a/core/src/runtime/core_map.rs +++ /dev/null @@ -1,386 +0,0 @@ -/* -* Copyright 2019 Comcast Cable Communications Management, LLC -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/ - -use crate::dpdk::{CoreId, Mempool, MempoolMap, MEMPOOL}; -use crate::{debug, error, ffi, info}; -use anyhow::Result; -use futures::Future; -use std::collections::{HashMap, HashSet}; -use std::sync::mpsc::{self, Receiver, SyncSender}; -use std::thread::{self, JoinHandle}; -use thiserror::Error; -use tokio::sync::oneshot; -use tokio_executor::current_thread::{self, CurrentThread}; -use tokio_executor::park::ParkThread; -use tokio_net::driver::{self, Reactor}; -use tokio_timer::timer::{self, Timer}; - -/// A sync-channel based park handle. -/// -/// This is designed to be a single use handle. We only need to park the -/// core one time at initialization time. Once unparked, we will never -/// park the core again. -pub(crate) struct Park { - core_id: CoreId, - sender: SyncSender<()>, - receiver: Receiver<()>, -} - -impl Park { - fn new(core_id: CoreId) -> Self { - let (sender, receiver) = mpsc::sync_channel(0); - Park { - core_id, - sender, - receiver, - } - } - - fn unpark(&self) -> Unpark { - Unpark { - core_id: self.core_id, - sender: self.sender.clone(), - } - } - - fn park(&self) { - if let Err(err) = self.receiver.recv() { - // we are not expecting failures, but we will log it in case. - error!(message = "park failed.", core=?self.core_id, ?err); - } - } -} - -/// A sync-channel based unpark handle. -/// -/// This is designed to be a single use handle. We will unpark a core one -/// time after all initialization completes. Do not reinvoke this. -pub(crate) struct Unpark { - core_id: CoreId, - sender: SyncSender<()>, -} - -impl Unpark { - pub(crate) fn unpark(&self) { - if let Err(err) = self.sender.send(()) { - // we are not expecting failures, but we will log it in case. - error!(message = "unpark failed.", core=?self.core_id, ?err); - } - } -} - -/// A tokio oneshot channel based shutdown mechanism. -pub(crate) struct Shutdown { - receiver: oneshot::Receiver<()>, -} - -impl Shutdown { - fn new(core_id: CoreId) -> (Self, ShutdownTrigger) { - let (sender, receiver) = oneshot::channel(); - let shutdown = Shutdown { receiver }; - let trigger = ShutdownTrigger { core_id, sender }; - (shutdown, trigger) - } - - fn into_task(self) -> impl Future { - self.receiver - } -} - -/// A sync-channel based shutdown trigger to terminate a background thread. -pub(crate) struct ShutdownTrigger { - core_id: CoreId, - sender: oneshot::Sender<()>, -} - -impl ShutdownTrigger { - pub(crate) fn shutdown(self) { - if let Err(err) = self.sender.send(()) { - // we are not expecting failures, but we will log it in case. - error!(message = "shutdown failed.", core=?self.core_id, ?err); - } - } -} - -/// A abstraction used to interact with the master/main thread. -/// -/// This is an additional handle to the master thread for performing tasks. -/// Use this `thread` handle to run the main loop. Use the `reactor` handle -/// to catch Unix signals to terminate the main loop. Use the `timer` handle -/// to create new time based tasks with either a `Delay` or `Interval`. -pub(crate) struct MasterExecutor { - pub(crate) reactor: driver::Handle, - pub(crate) timer: timer::Handle, - pub(crate) thread: CurrentThread>, -} - -/// A thread/core abstraction used to interact with a background thread -/// from the master/main thread. -/// -/// When a background thread is first spawned, it is parked and waiting for -/// tasks. Use the `timer` handle to create new time based tasks with either -/// a `Delay` or `Interval`. Use the thread handle to spawn tasks onto the -/// background thread. Use `unpark` when they are ready to execute tasks. -/// -/// The master thread also has an associated `CoreExecutor`, but `unpark` -/// won't do anything because the thread is not parked. Tasks can be spawned -/// onto it with this handle just like a background thread. -pub(crate) struct CoreExecutor { - pub(crate) timer: timer::Handle, - pub(crate) thread: current_thread::Handle, - pub(crate) unpark: Option, - pub(crate) shutdown: Option, - pub(crate) join: Option>, -} - -/// Core errors. -#[derive(Debug, Error)] -pub(crate) enum CoreError { - /// Core is not found. - #[error("{0:?} is not found.")] - NotFound(CoreId), - - /// Core is not assigned to any ports. - #[error("{0:?} is not assigned to any ports.")] - NotAssigned(CoreId), -} - -/// Map of all the core handles. -pub(crate) struct CoreMap { - pub(crate) master_core: MasterExecutor, - pub(crate) cores: HashMap, -} - -/// By default, raw pointers do not implement `Send`. We need a simple -/// wrapper so we can send the mempool pointer to a background thread and -/// assigned it to that thread. Otherwise, we wont' be able to create new -/// `Mbuf`s on the background threads. -struct SendablePtr(*mut ffi::rte_mempool); - -unsafe impl std::marker::Send for SendablePtr {} - -/// Builder for core map. -pub(crate) struct CoreMapBuilder<'a> { - app_name: String, - cores: HashSet, - master_core: CoreId, - mempools: MempoolMap<'a>, -} - -impl<'a> CoreMapBuilder<'a> { - pub(crate) fn new() -> Self { - CoreMapBuilder { - app_name: String::new(), - cores: Default::default(), - master_core: CoreId::new(0), - mempools: Default::default(), - } - } - - pub(crate) fn app_name(&mut self, app_name: &str) -> &mut Self { - self.app_name = app_name.to_owned(); - self - } - - pub(crate) fn cores(&mut self, cores: &[CoreId]) -> &mut Self { - self.cores = cores.iter().cloned().collect(); - self - } - - pub(crate) fn master_core(&mut self, master_core: CoreId) -> &mut Self { - self.master_core = master_core; - self - } - - pub(crate) fn mempools(&'a mut self, mempools: &'a mut [Mempool]) -> &'a mut Self { - self.mempools = MempoolMap::new(mempools); - self - } - - #[allow(clippy::cognitive_complexity)] - pub(crate) fn finish(&'a mut self) -> Result { - let mut map = HashMap::new(); - - // first initializes the master core, which the current running - // thread should be affinitized to. - let socket_id = self.master_core.socket_id(); - let mempool = self.mempools.get_raw(socket_id)?; - - let (master_thread, core_executor) = init_master_core(self.master_core, mempool)?; - - // adds the master core to the map. tasks can be spawned onto the - // master core like any other cores. - map.insert(self.master_core, core_executor); - - info!("initialized master on {:?}.", self.master_core); - - // the core list may also include the master core, to avoid double - // init, let's try remove it just in case. - self.cores.remove(&self.master_core); - - // next initializes all the cores other than the master core - for &core_id in self.cores.iter() { - // finds the mempool that matches the core's socket, and wraps the - // reference in a sendable pointer because we are sending it to - // a background thread - let socket_id = core_id.socket_id(); - let mempool = self.mempools.get_raw(socket_id)?; - let ptr = SendablePtr(mempool); - - // creates a synchronous channel so we can retrieve the executor for - // the background core. - let (sender, receiver) = mpsc::sync_channel(0); - - // spawns a new background thread and initializes a core executor on - // that thread. - let join = thread::Builder::new() - .name(format!("{}-{:?}", self.app_name, core_id)) - .spawn(move || { - debug!("spawned background thread {:?}.", thread::current().id()); - - match init_background_core(core_id, ptr.0) { - Ok((mut thread, park, shutdown, executor)) => { - info!("initialized thread on {:?}.", core_id); - - // keeps a timer handle for later use. - let timer_handle = executor.timer.clone(); - - // sends the executor back to the master core. it's safe to unwrap - // the result because the receiving end is guaranteed to be in scope. - sender.send(Ok(executor)).unwrap(); - - info!("parking {:?}.", core_id); - - // sleeps the thread for now since there's nothing to be done yet. - // once new tasks are spawned, the master core can unpark this and - // let the execution continue. - park.park(); - - info!("unparked {:?}.", core_id); - - // once the thread wakes up, we will run all the spawned tasks and - // wait until a shutdown is triggered from the master core. - let _timer = timer::set_default(&timer_handle); - let _ = thread.block_on(shutdown.into_task()); - - info!("unblocked {:?}.", core_id); - } - // propogates the error back to the master core. - Err(err) => sender.send(Err(err)).unwrap(), - } - })?; - - // blocks and waits for the background thread to finish initialize. - // when done, we add the executor to the map. - let mut executor = receiver.recv().unwrap()?; - executor.join = Some(join); - map.insert(core_id, executor); - } - - Ok(CoreMap { - master_core: master_thread, - cores: map, - }) - } -} - -fn init_master_core( - id: CoreId, - mempool: *mut ffi::rte_mempool, -) -> Result<(MasterExecutor, CoreExecutor)> { - // affinitize the running thread to this core. - id.set_thread_affinity()?; - - // sets the mempool - MEMPOOL.with(|tls| tls.set(mempool)); - - // starts a reactor so we can receive signals on the master core. - let reactor = Reactor::new()?; - let reactor_handle = reactor.handle(); - - // starts a per-core timer so we can schedule timed tasks. - let timer = Timer::new(reactor); - let timer_handle = timer.handle(); - - // starts the single-threaded executor, we can use this handle - // to spawn tasks onto this core from the master core. - let thread = CurrentThread::new_with_park(timer); - let thread_handle = thread.handle(); - - let main = MasterExecutor { - reactor: reactor_handle, - timer: timer_handle.clone(), - thread, - }; - - let executor = CoreExecutor { - timer: timer_handle, - thread: thread_handle, - unpark: None, - shutdown: None, - join: None, - }; - - Ok((main, executor)) -} - -fn init_background_core( - id: CoreId, - mempool: *mut ffi::rte_mempool, -) -> Result<( - CurrentThread>, - Park, - Shutdown, - CoreExecutor, -)> { - // affinitize the running thread to this core. - id.set_thread_affinity()?; - - // sets the mempool - MEMPOOL.with(|tls| tls.set(mempool)); - - // starts a per-core timer so we can schedule timed tasks. - let park = ParkThread::new(); - let timer = Timer::new(park); - let timer_handle = timer.handle(); - - // starts the single-threaded executor, we can use this handle - // to spawn tasks onto this core from the master core. - let thread = CurrentThread::new_with_park(timer); - let thread_handle = thread.handle(); - - // problem with using the regular thread park is when a task is - // spawned, the handle will implicitly unpark the thread. we have - // no way to control that behavior. so instead, we use a channel - // based unpark mechanism to block the thread from further - // execution until we are ready to proceed. - let park = Park::new(id); - - // shutdown handle for the core. - let (shutdown, trigger) = Shutdown::new(id); - - let executor = CoreExecutor { - timer: timer_handle, - thread: thread_handle, - unpark: Some(park.unpark()), - shutdown: Some(trigger), - join: None, - }; - - Ok((thread, park, shutdown, executor)) -} diff --git a/core/src/rt2/lcore.rs b/core/src/runtime/lcore.rs similarity index 100% rename from core/src/rt2/lcore.rs rename to core/src/runtime/lcore.rs diff --git a/core/src/rt2/mempool.rs b/core/src/runtime/mempool.rs similarity index 100% rename from core/src/rt2/mempool.rs rename to core/src/runtime/mempool.rs diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 351d54b..cd63ef9 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -16,615 +16,219 @@ * SPDX-License-Identifier: Apache-2.0 */ -mod core_map; - -pub(crate) use self::core_map::*; - -use crate::batch::Pipeline; -use crate::config::RuntimeConfig; -use crate::dpdk::{ - self, CoreId, KniError, KniRx, Mempool, Port, PortBuilder, PortError, PortQueue, -}; -use crate::{debug, ensure, info}; +//! Capsule runtime. + +mod config; +mod lcore; +mod mempool; +#[cfg(feature = "pcap-dump")] +#[cfg_attr(docsrs, doc(cfg(feature = "pcap-dump")))] +mod pcap_dump; +mod port; + +pub use self::config::*; +pub(crate) use self::lcore::*; +pub use self::lcore::{Lcore, LcoreMap, LcoreNotFound}; +pub use self::mempool::Mempool; +pub(crate) use self::mempool::*; +pub use self::port::{Outbox, Port, PortError, PortMap}; + +use crate::ffi::dpdk::{self, LcoreId}; +use crate::packets::{Mbuf, Postmark}; +use crate::{debug, info}; use anyhow::Result; -use futures::{future, stream, StreamExt}; -use std::collections::{HashMap, HashSet}; +use async_channel::{self, Receiver, Sender}; use std::fmt; use std::mem::ManuallyDrop; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio_executor::current_thread; -use tokio_net::driver; -use tokio_net::signal::unix::{self, SignalKind}; -use tokio_timer::{timer, Interval}; - -/// Supported [Unix signals]. -/// -/// [Unix signals]: https://en.wikipedia.org/wiki/Signal_(IPC)#POSIX_signals -#[derive(Copy, Clone, Debug)] -pub enum UnixSignal { - /// This signal is sent to a process when its controlling terminal is closed. - /// In modern systems, this signal usually means that the controlling pseudo - /// or virtual terminal has been closed. Many daemons will reload their - /// configuration files and reopen their log files instead of exiting when - /// receiving this signal. `nohup` is a command to make a command ignore the - /// signal. - SIGHUP = libc::SIGHUP as isize, - /// This signal is sent to a process by its controlling terminal when a user - /// wishes to interrupt the process. This is typically initiated by pressing - /// `Ctrl-C`, but on some systems, the "delete" character or "break" key can - /// be used. - SIGINT = libc::SIGINT as isize, - /// This signal is sent to a process to request its termination. Unlike the - /// `SIGKILL` signal, it can be caught and interpreted or ignored by the - /// process. This allows the process to perform nice termination releasing - /// resources and saving state if appropriate. `SIGINT` is nearly identical - /// to `SIGTERM`. - SIGTERM = libc::SIGTERM as isize, -} +use std::ops::DerefMut; -/// The Capsule runtime. -/// -/// The runtime initializes the underlying DPDK environment, and it also manages -/// the task scheduler that executes the packet processing pipelines. -pub struct Runtime { - ports: ManuallyDrop>, - mempools: ManuallyDrop>, - core_map: CoreMap, - on_signal: Arc bool>, - config: RuntimeConfig, -} - -impl Runtime { - /// Builds a runtime from config settings. - #[allow(clippy::cognitive_complexity)] - pub fn build(config: RuntimeConfig) -> Result { - info!("initializing EAL..."); - dpdk::eal_init(config.to_eal_args())?; - - #[cfg(feature = "metrics")] - { - info!("initializing metrics subsystem..."); - crate::metrics::init()?; - } - - let cores = config.all_cores(); - - info!("initializing mempools..."); - let sockets = cores.iter().map(CoreId::socket_id).collect::>(); - let mut mempools = vec![]; - for socket in sockets { - let mempool = Mempool::new(config.mempool.capacity, config.mempool.cache_size, socket)?; - debug!(?mempool); - mempools.push(mempool); - } - - info!("intializing cores..."); - let core_map = CoreMapBuilder::new() - .app_name(&config.app_name) - .cores(&cores) - .master_core(config.master_core) - .mempools(&mut mempools) - .finish()?; - - let len = config.num_knis(); - if len > 0 { - info!("initializing KNI subsystem..."); - dpdk::kni_init(len)?; - } - - info!("initializing ports..."); - let mut ports = vec![]; - for conf in config.ports.iter() { - let port = PortBuilder::new(conf.name.clone(), conf.device.clone())? - .cores(&conf.cores)? - .mempools(&mut mempools) - .rx_tx_queue_capacity(conf.rxd, conf.txd)? - .finish(conf.promiscuous, conf.multicast, conf.kni)?; - - debug!(?port); - ports.push(port); - } - - #[cfg(feature = "metrics")] - { - crate::metrics::register_port_stats(&ports); - crate::metrics::register_mempool_stats(&mempools); - } - - info!("runtime ready."); - - Ok(Runtime { - ports: ManuallyDrop::new(ports), - mempools: ManuallyDrop::new(mempools), - core_map, - on_signal: Arc::new(|_| true), - config, - }) - } +/// Trigger for the shutdown. +pub(crate) struct ShutdownTrigger(Sender<()>, Receiver<()>); - #[inline] - fn get_port(&self, name: &str) -> Result<&Port> { - self.ports - .iter() - .find(|p| p.name() == name) - .ok_or_else(|| PortError::NotFound(name.to_owned()).into()) - } - - #[inline] - fn get_port_mut(&mut self, name: &str) -> Result<&mut Port> { - self.ports - .iter_mut() - .find(|p| p.name() == name) - .ok_or_else(|| PortError::NotFound(name.to_owned()).into()) +impl ShutdownTrigger { + /// Creates a new shutdown trigger. + /// + /// Leverages the behavior of an async channel. When the sender is dropped + /// from scope, it closes the channel and causes the receiver side future + /// in the executor queue to resolve. + pub(crate) fn new() -> Self { + let (s, r) = async_channel::unbounded(); + Self(s, r) } - #[inline] - fn get_core(&self, core_id: CoreId) -> Result<&CoreExecutor> { - self.core_map - .cores - .get(&core_id) - .ok_or_else(|| CoreError::NotFound(core_id).into()) + /// Returns a wait handle. + pub(crate) fn get_wait(&self) -> ShutdownWait { + ShutdownWait(self.1.clone()) } - #[inline] - fn get_port_qs(&self, core_id: CoreId) -> Result> { - let map = self - .ports - .iter() - .filter_map(|p| { - p.queues() - .get(&core_id) - .map(|q| (p.name().to_owned(), q.clone())) - }) - .collect::>(); - - ensure!(!map.is_empty(), CoreError::NotAssigned(core_id)); - - Ok(map) + /// Returns whether the trigger is being waited on. + pub(crate) fn is_waited(&self) -> bool { + // a receiver count greater than 1 indicating that there are receiver + // clones in scope, hence the trigger is being waited on. + self.0.receiver_count() > 1 } - /// Sets the Unix signal handler. - /// - /// `SIGHUP`, `SIGINT` and `SIGTERM` are the supported Unix signals. - /// The return of the handler determines whether to terminate the - /// process. `true` indicates the signal is received and the process - /// should be terminated. `false` indicates to discard the signal and - /// keep the process running. - /// - /// # Example - /// - /// ``` - /// Runtime::build(&config)?; - /// .set_on_signal(|signal| match signal { - /// SIGHUP => { - /// reload_config(); - /// false - /// } - /// _ => true, - /// }) - /// .execute(); - /// ``` - pub fn set_on_signal(&mut self, f: F) -> &mut Self - where - F: Fn(UnixSignal) -> bool + 'static, - { - self.on_signal = Arc::new(f); - self + /// Triggers the shutdown. + pub(crate) fn fire(self) { + drop(self.0) } +} - /// Installs a pipeline to a port. The pipeline will run on all the - /// cores assigned to the port. - /// - /// `port` is the logical name that identifies the port. The `installer` - /// is a closure that takes in a [`PortQueue`] and returns a [`Pipeline`] - /// that will be spawned onto the thread executor. - /// - /// # Example - /// - /// ``` - /// Runtime::build(config)? - /// .add_add_pipeline_to_port("eth1", install)? - /// .execute() - /// ``` - /// - /// [`PortQueue`]: crate::PortQueue - /// [`Pipeline`]: crate::batch::Pipeline - pub fn add_pipeline_to_port( - &mut self, - port: &str, - installer: F, - ) -> Result<&mut Self> - where - F: Fn(PortQueue) -> T + Send + Sync + 'static, - { - let port = self.get_port(port)?; - let f = Arc::new(installer); - - for (core_id, port_q) in port.queues() { - let f = f.clone(); - let port_q = port_q.clone(); - let thread = &self.get_core(*core_id)?.thread; - - // spawns the bootstrap. we want the bootstrapping to execute on the - // target core instead of the master core. that way the actual task - // is spawned locally and the type bounds are less restricting. - thread.spawn(future::lazy(move |_| { - let fut = f(port_q); - debug!("spawned pipeline {}.", fut.name()); - current_thread::spawn(fut); - }))?; - - debug!("installed pipeline on port_q for {:?}.", core_id); - } - - info!("installed pipeline for port {}.", port.name()); - - Ok(self) - } +/// Shutdown wait handle. +pub(crate) struct ShutdownWait(Receiver<()>); - /// Installs a pipeline to a KNI enabled port to receive packets coming - /// from the kernel. This pipeline will run on a randomly select core - /// that's assigned to the port. - /// - /// # Remarks - /// - /// This function has be to invoked once per port. Otherwise the packets - /// coming from the kernel will be silently dropped. For the most common - /// use case where the application only needs simple packet forwarding, - /// use [`batch::splice`] to join the kernel's RX with the port's TX. - /// - /// # Example - /// - /// ``` - /// Runtime::build(config)? - /// .add_add_pipeline_to_port("kni0", install)? - /// .add_kni_rx_pipeline_to_port("kni0", batch::splice)? - /// .execute() - /// ``` - /// - /// [`batch::splice`]: crate::batch::splice - pub fn add_kni_rx_pipeline_to_port( - &mut self, - port: &str, - installer: F, - ) -> Result<&mut Self> - where - F: FnOnce(KniRx, PortQueue) -> T + Send + Sync + 'static, - { - // takes ownership of the kni rx handle. - let kni_rx = self - .get_port_mut(port)? - .kni() - .ok_or(KniError::Disabled)? - .take_rx()?; - - // selects a core to run a rx pipeline for this port. the selection is - // randomly choosing the last core we find. if the port has more than one - // core assigned, this will be different from the core that's running the - // tx pipeline. - let port = self.get_port(port)?; - let core_id = port.queues().keys().last().unwrap(); - let port_q = port.queues()[core_id].clone(); - let thread = &self.get_core(*core_id)?.thread; - - // spawns the bootstrap. we want the bootstrapping to execute on the - // target core instead of the master core. - thread.spawn(future::lazy(move |_| { - let fut = installer(kni_rx, port_q); - debug!("spawned kni rx pipeline {}.", fut.name()); - current_thread::spawn(fut); - }))?; - - info!("installed kni rx pipeline for port {}.", port.name()); - - Ok(self) +impl ShutdownWait { + /// A future that waits till the shutdown trigger is fired. + pub(crate) async fn wait(&self) { + self.0.recv().await.unwrap_or(()) } +} - /// Installs a pipeline to a core. All the ports the core is assigned - /// to will be available to the pipeline. - /// - /// `core` is the logical id that identifies the core. The `installer` - /// is a closure that takes in a hashmap of [`PortQueues`] and returns a - /// [`Pipeline`] that will be spawned onto the thread executor of the core. - /// - /// # Example - /// - /// ``` - /// Runtime::build(config)? - /// .add_pipeline_to_core(1, install)? - /// .execute() - /// ``` - /// - /// [`PortQueues`]: crate::PortQueue - /// [`Pipeline`]: crate::batch::Pipeline - pub fn add_pipeline_to_core( - &mut self, - core: usize, - installer: F, - ) -> Result<&mut Self> - where - F: FnOnce(HashMap) -> T + Send + Sync + 'static, - { - let core_id = CoreId::new(core); - let thread = &self.get_core(core_id)?.thread; - let port_qs = self.get_port_qs(core_id)?; - - // spawns the bootstrap. we want the bootstrapping to execute on the - // target core instead of the master core. - thread.spawn(future::lazy(move |_| { - let fut = installer(port_qs); - debug!("spawned pipeline {}.", fut.name()); - current_thread::spawn(fut); - }))?; - - info!("installed pipeline for {:?}.", core_id); - - Ok(self) - } +/// The Capsule runtime. +/// +/// The runtime initializes the underlying DPDK environment, and it also manages +/// the task scheduler that executes the packet processing tasks. +pub struct Runtime { + mempool: ManuallyDrop, + lcores: ManuallyDrop, + ports: ManuallyDrop, + #[cfg(feature = "pcap-dump")] + pcap_dump: ManuallyDrop, +} - /// Installs a periodic pipeline to a core. - /// - /// `core` is the logical id that identifies the core. The `installer` is a - /// closure that takes in a hashmap of [`PortQueues`] and returns a - /// [`Pipeline`] that will be run periodically every `dur` interval. - /// - /// # Remarks - /// - /// All the ports the core is assigned to will be available to this - /// pipeline. However they should only be used to transmit packets. This - /// variant is for pipelines that generate new packets periodically. - /// A new packet batch can be created with [`batch::poll_fn`] and ingested - /// into the pipeline. - /// - /// # Example - /// - /// ``` - /// Runtime::build(config)? - /// .add_periodic_pipeline_to_core(1, install, Duration::from_millis(10))? - /// .execute() - /// ``` +impl Runtime { + /// Returns the mempool. /// - /// [`PortQueues`]: crate::PortQueue - /// [`Pipeline`]: crate::batch::Pipeline - /// [`batch::poll_fn`]: crate::batch::poll_fn - pub fn add_periodic_pipeline_to_core( - &mut self, - core: usize, - installer: F, - dur: Duration, - ) -> Result<&mut Self> - where - F: FnOnce(HashMap) -> T + Send + Sync + 'static, - { - let core_id = CoreId::new(core); - let thread = &self.get_core(core_id)?.thread; - let port_qs = self.get_port_qs(core_id)?; - - // spawns the bootstrap. we want the bootstrapping to execute on the - // target core instead of the master core so the periodic task is - // associated with the correct timer instance. - thread.spawn(future::lazy(move |_| { - let mut pipeline = installer(port_qs); - debug!("spawned periodic pipeline {}.", pipeline.name()); - let fut = Interval::new_interval(dur).for_each(move |_| { - pipeline.run_once(); - future::ready(()) - }); - current_thread::spawn(fut); - }))?; - - info!("installed periodic pipeline for {:?}.", core_id); - - Ok(self) + /// For simplicity, we currently only support one global Mempool. Multi- + /// socket support may be added in the future. + pub fn mempool(&self) -> &Mempool { + &self.mempool } - /// Installs a periodic task to a core. - /// - /// `core` is the logical id that identifies the core. `task` is the - /// closure to execute. The task will rerun every `dur` interval. - /// - /// # Example - /// - /// ``` - /// Runtime::build(config)? - /// .add_periodic_task_to_core(0, print_stats, Duration::from_secs(1))? - /// .execute() - /// ``` - pub fn add_periodic_task_to_core( - &mut self, - core: usize, - mut task: F, - dur: Duration, - ) -> Result<&mut Self> - where - F: FnMut() + Send + Sync + 'static, - { - let core_id = CoreId::new(core); - let thread = &self.get_core(core_id)?.thread; - - // spawns the bootstrap. we want the bootstrapping to execute on the - // target core instead of the master core so the periodic task is - // associated with the correct timer instance. - thread.spawn(future::lazy(move |_| { - let fut = Interval::new_interval(dur).for_each(move |_| { - task(); - future::ready(()) - }); - debug!("spawned periodic task."); - current_thread::spawn(fut); - }))?; - - info!("installed periodic task for {:?}.", core_id); - - Ok(self) + /// Returns the lcores. + pub fn lcores(&self) -> &LcoreMap { + &self.lcores } - /// Blocks the main thread until a timeout expires. - /// - /// This mode is useful for running integration tests. The timeout - /// duration can be set in `RuntimeSettings`. - fn wait_for_timeout(&mut self, timeout: Duration) { - let MasterExecutor { - ref timer, - ref mut thread, - .. - } = self.core_map.master_core; - - let when = Instant::now() + timeout; - let delay = timer.delay(when); - - debug!("waiting for {:?}...", timeout); - let _timer = timer::set_default(&timer); - thread.block_on(delay); - info!("timed out after {:?}.", timeout); + /// Returns the configured ports. + pub fn ports(&self) -> &PortMap { + &self.ports } - /// Blocks the main thread until receives a signal to terminate. - fn wait_for_signal(&mut self) -> Result<()> { - let sighup = unix::signal(SignalKind::hangup())?.map(|_| UnixSignal::SIGHUP); - let sigint = unix::signal(SignalKind::interrupt())?.map(|_| UnixSignal::SIGINT); - let sigterm = unix::signal(SignalKind::terminate())?.map(|_| UnixSignal::SIGTERM); - - // combines the streams together - let stream = stream::select(stream::select(sighup, sigint), sigterm); - - // passes each signal through the `on_signal` closure, and discard - // any that shouldn't stop the execution. - let f = self.on_signal.clone(); - let mut stream = stream.filter(|&signal| future::ready(f(signal))); - - let MasterExecutor { - ref reactor, - ref timer, - ref mut thread, - .. - } = self.core_map.master_core; - - // sets the reactor so we receive the signals and runs the future - // on the master core. the execution stops on the first signal that - // wasn't filtered out. - debug!("waiting for a Unix signal..."); - let _guard = driver::set_default(&reactor); - let _timer = timer::set_default(&timer); - let _ = thread.block_on(stream.next()); - info!("signaled to stop."); + /// Initializes a new runtime from config settings. + pub fn from_config(config: RuntimeConfig) -> Result { + info!("starting runtime."); - Ok(()) - } + debug!("initializing EAL ..."); + dpdk::eal_init(config.to_eal_args())?; - /// Installs the KNI TX pipelines. - fn add_kni_tx_pipelines(&mut self) -> Result<()> { - let mut map = HashMap::new(); - for port in self.ports.iter_mut() { - // selects a core if we need to run a tx pipeline for this port. the - // selection is randomly choosing the first core we find. if the port - // has more than one core assigned, this will be different from the - // core that's running the rx pipeline. - let core_id = *port.queues().keys().next().unwrap(); - - // if the port is kni enabled, then we will take ownership of the - // tx handle. - if let Some(kni) = port.kni() { - map.insert(core_id, kni.take_tx()?); - } + debug!("initializing mempool ..."); + let socket = LcoreId::main().socket(); + let mut mempool = Mempool::new( + "mempool", + config.mempool.capacity, + config.mempool.cache_size, + socket, + )?; + debug!(?mempool); + + debug!("initializing lcore schedulers ..."); + let lcores = self::lcore_pool(); + + for lcore in lcores.iter() { + let mut ptr = mempool.ptr_mut().clone(); + lcore.block_on(async move { MEMPOOL.with(|tls| tls.set(ptr.deref_mut())) }); } - // spawns all the pipelines. - for (core_id, kni_tx) in map.into_iter() { - let thread = &self.get_core(core_id)?.thread; - thread.spawn(kni_tx.into_pipeline())?; + info!("initializing ports ..."); + let mut ports = Vec::new(); + for port in config.ports.iter() { + let mut port = port::Builder::for_device(&port.name, &port.device)? + .set_rxqs_txqs(port.rxqs, port.txqs)? + .set_promiscuous(port.promiscuous)? + .set_multicast(port.multicast)? + .set_rx_lcores(port.rx_cores.clone())? + .set_tx_lcores(port.tx_cores.clone())? + .build(&mut mempool)?; - info!("installed kni tx pipeline on {:?}.", core_id); - } + debug!(?port); - Ok(()) - } + if !port.tx_lcores().is_empty() { + port.spawn_tx_loops(&lcores)?; + } - /// Starts all the ports to receive packets. - fn start_ports(&mut self) -> Result<()> { - for port in self.ports.iter_mut() { port.start()?; + ports.push(port); } + let ports: PortMap = ports.into(); - Ok(()) - } + #[cfg(feature = "pcap-dump")] + let pcap_dump = self::pcap_dump::enable_pcap_dump(&config.data_dir(), &ports, &lcores)?; - /// Unparks all the cores to start task execution. - fn unpark_cores(&mut self) { - for core in self.core_map.cores.values() { - if let Some(unpark) = &core.unpark { - unpark.unpark(); - } - } - } + info!("runtime ready."); - /// Shuts down all the cores to stop task execution. - #[allow(clippy::cognitive_complexity)] - fn shutdown_cores(&mut self) { - for (core_id, core) in &mut self.core_map.cores { - if let Some(trigger) = core.shutdown.take() { - debug!("shutting down {:?}.", core_id); - trigger.shutdown(); - debug!("sent {:?} shutdown trigger.", core_id); - let handle = core.join.take().unwrap(); - let _ = handle.join(); - info!("terminated {:?}.", core_id); - } - } + Ok(Runtime { + mempool: ManuallyDrop::new(mempool), + lcores: ManuallyDrop::new(lcores), + ports: ManuallyDrop::new(ports), + #[cfg(feature = "pcap-dump")] + pcap_dump: ManuallyDrop::new(pcap_dump), + }) } - /// Stops all the ports. - fn stop_ports(&mut self) { - for port in self.ports.iter_mut() { - port.stop(); - } + /// Sets the packet processing pipeline for port. + pub fn set_port_pipeline(&self, port: &str, f: F) -> Result<()> + where + F: Fn(Mbuf) -> Result + Clone + Send + Sync + 'static, + { + let port = self.ports.get(port)?; + port.spawn_rx_loops(f, &self.lcores)?; + Ok(()) } - /// Executes the pipeline(s) until a stop signal is received. - pub fn execute(&mut self) -> Result<()> { - self.add_kni_tx_pipelines()?; - self.start_ports()?; - self.unpark_cores(); - - // runs the app until main loop finishes. - match self.config.duration { - None => self.wait_for_signal()?, - Some(d) => self.wait_for_timeout(d), - }; - - self.shutdown_cores(); - self.stop_ports(); - info!("runtime terminated."); - - Ok(()) + /// Starts the runtime execution. + pub fn execute(self) -> Result { + Ok(RuntimeGuard { runtime: self }) } } -impl<'a> fmt::Debug for Runtime { +impl fmt::Debug for Runtime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("runtime") - .field("runtime configuration", &format!("{:?}", self.config)) + f.debug_struct("Runtime") + .field("mempool", &self.mempool) .finish() } } -impl Drop for Runtime { +/// The RAII guard to stop and cleanup the runtime resources on drop. +pub struct RuntimeGuard { + runtime: Runtime, +} + +impl Drop for RuntimeGuard { fn drop(&mut self) { - // the default rust drop order is self before fields, which is the wrong - // order for what EAL needs. To control the order, we manually drop the - // fields first. - unsafe { - ManuallyDrop::drop(&mut self.ports); - ManuallyDrop::drop(&mut self.mempools); + info!("shutting down runtime."); + + for port in self.runtime.ports.iter_mut() { + port.stop(); } - if self.config.num_knis() > 0 { - debug!("freeing KNI subsystem."); - dpdk::kni_close(); + unsafe { + #[cfg(feature = "pcap-dump")] + ManuallyDrop::drop(&mut self.runtime.pcap_dump); + ManuallyDrop::drop(&mut self.runtime.ports); + ManuallyDrop::drop(&mut self.runtime.lcores); + ManuallyDrop::drop(&mut self.runtime.mempool); } - debug!("freeing EAL."); - dpdk::eal_cleanup().unwrap(); + debug!("freeing EAL ..."); + let _ = dpdk::eal_cleanup(); + info!("runtime shutdown."); + } +} + +impl fmt::Debug for RuntimeGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RuntimeGuard") } } diff --git a/core/src/rt2/pcap_dump.rs b/core/src/runtime/pcap_dump.rs similarity index 100% rename from core/src/rt2/pcap_dump.rs rename to core/src/runtime/pcap_dump.rs diff --git a/core/src/rt2/port.rs b/core/src/runtime/port.rs similarity index 100% rename from core/src/rt2/port.rs rename to core/src/runtime/port.rs diff --git a/core/src/testils/criterion.rs b/core/src/testils/criterion.rs index 8bd66dc..d45268d 100644 --- a/core/src/testils/criterion.rs +++ b/core/src/testils/criterion.rs @@ -22,12 +22,9 @@ //! [criterion]: https://crates.io/crates/criterion use super::Rvg; -use crate::batch::{Batch, PacketTx, Poll}; -use crate::Mbuf; use criterion::{black_box, Bencher}; use proptest::strategy::Strategy; use std::cmp; -use std::sync::mpsc::{self, Receiver}; use std::time::{Duration, Instant}; /// Criterion `Bencher` extension trait. @@ -42,19 +39,6 @@ pub trait BencherExt { where R: FnMut(S::Value) -> O, S: Strategy; - - /// Times a `routine` with an input generated via a `proptest strategy` - /// batch of input that can be polled for benchmarking pipeline combinators - /// in [`Batch`] and then times the iteration of the benchmark - /// over the input. See [`BatchSize`] for details on choosing the batch size. - /// - /// [`BatchSize`]: https://docs.rs/criterion/latest/criterion/enum.BatchSize.html - /// [`Batch`]: crate::batch::Batch - fn iter_proptest_combinators(&mut self, strategy: S, routine: R, batch_size: usize) - where - R: FnMut(Poll>) -> O, - S: Strategy, - O: Batch; } impl BencherExt for Bencher<'_> { @@ -84,43 +68,4 @@ impl BencherExt for Bencher<'_> { total_elapsed }) } - - fn iter_proptest_combinators, O: Batch>( - &mut self, - strategy: S, - mut routine: R, - batch_size: usize, - ) where - R: FnMut(Poll>) -> O, - { - self.iter_custom(|mut iters| { - let mut total_elapsed = Duration::from_secs(0); - let mut gen = Rvg::deterministic(); - while iters > 0 { - let batch_size = cmp::min(batch_size, iters as usize); - let inputs = black_box(gen.generate_vec(&strategy, batch_size)); - let mut outputs = Vec::with_capacity(batch_size); - - let (mut tx, rx) = mpsc::channel(); - tx.transmit(inputs.into_iter().collect::>()); - let mut new_batch = Poll::new(rx); - new_batch.replenish(); - - let start = Instant::now(); - let mut batch = routine(new_batch); - - while let Some(disp) = batch.next() { - outputs.push(disp) - } - - total_elapsed += start.elapsed(); - - black_box(batch); - black_box(outputs); - - iters -= batch_size as u64; - } - total_elapsed - }) - } } diff --git a/core/src/testils/mod.rs b/core/src/testils/mod.rs index 2445575..da4393e 100644 --- a/core/src/testils/mod.rs +++ b/core/src/testils/mod.rs @@ -28,8 +28,7 @@ pub use self::packet::*; pub use self::rvg::*; use crate::ffi::dpdk::{self, SocketId}; -use crate::metrics; -use crate::rt2::{Mempool, MEMPOOL}; +use crate::runtime::{Mempool, MEMPOOL}; use std::ops::DerefMut; use std::ptr; use std::sync::Once; @@ -62,7 +61,6 @@ pub fn cargo_test_init() { "net_tap0", ]) .unwrap(); - let _ = metrics::init(); }); } diff --git a/core/src/testils/proptest/arbitrary.rs b/core/src/testils/proptest/arbitrary.rs index b4b3a39..35c24ae 100644 --- a/core/src/testils/proptest/arbitrary.rs +++ b/core/src/testils/proptest/arbitrary.rs @@ -19,8 +19,8 @@ //! Implementations of `proptest.arbitrary.Arbitrary` trait for //! various types. -use crate::dpdk::Mbuf; use crate::net::MacAddr; +use crate::packets::Mbuf; use proptest::arbitrary::{any, Arbitrary, StrategyFor}; use proptest::strategy::{MapInto, Strategy}; diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index c70c064..e9aa1cf 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -22,9 +22,8 @@ use crate::net::MacAddr; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::{Ipv6, Ipv6Packet, SegmentRouting}; use crate::packets::ip::{Flow, IpPacket, ProtocolNumber, ProtocolNumbers}; -use crate::packets::{EtherType, EtherTypes, Ethernet, Packet, Tcp, Udp}; +use crate::packets::{EtherType, EtherTypes, Ethernet, Mbuf, Packet, Tcp, Udp}; use crate::testils::Rvg; -use crate::Mbuf; use proptest::arbitrary::{any, Arbitrary}; use proptest::collection::vec; use proptest::prop_oneof; diff --git a/examples/kni/Cargo.toml b/examples/kni/Cargo.toml index a27935c..317d5a3 100644 --- a/examples/kni/Cargo.toml +++ b/examples/kni/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kni" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -17,7 +17,7 @@ doctest = false [dependencies] anyhow = "1.0" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } colored = "2.0" signal-hook = "0.3" tracing = "0.1" diff --git a/examples/kni/main.rs b/examples/kni/main.rs index 8b0ac94..4720f36 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -21,7 +21,7 @@ use capsule::packets::icmp::v6::Icmpv6; use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Udp6}; -use capsule::rt2::{self, Outbox, Runtime}; +use capsule::runtime::{self, Outbox, Runtime}; use colored::Colorize; use signal_hook::consts; use signal_hook::flag; @@ -75,7 +75,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; let runtime = Runtime::from_config(config)?; let kni0 = runtime.ports().get("kni0")?.outbox()?; diff --git a/examples/nat64/Cargo.toml b/examples/nat64/Cargo.toml index c9bf0d1..8cc66e3 100644 --- a/examples/nat64/Cargo.toml +++ b/examples/nat64/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nat64" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -18,7 +18,7 @@ doctest = false [dependencies] anyhow = "1.0" bimap = "0.6" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } colored = "2.0" once_cell = "1.7" signal-hook = "0.3" diff --git a/examples/nat64/main.rs b/examples/nat64/main.rs index 4b68d21..e820860 100644 --- a/examples/nat64/main.rs +++ b/examples/nat64/main.rs @@ -23,7 +23,7 @@ use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Tcp4, Tcp6}; -use capsule::rt2::{self, Outbox, Runtime}; +use capsule::runtime::{self, Outbox, Runtime}; use colored::Colorize; use once_cell::sync::Lazy; use signal_hook::consts; @@ -184,7 +184,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; let runtime = Runtime::from_config(config)?; let cap1 = runtime.ports().get("cap1")?.outbox()?; diff --git a/examples/ping4d/Cargo.toml b/examples/ping4d/Cargo.toml index 708cba9..8c4935c 100644 --- a/examples/ping4d/Cargo.toml +++ b/examples/ping4d/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ping4d" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -17,7 +17,7 @@ doctest = false [dependencies] anyhow = "1.0" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } signal-hook = "0.3" tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 14fde50..350e77a 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -20,7 +20,7 @@ use anyhow::Result; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; use capsule::packets::ip::v4::Ipv4; use capsule::packets::{Ethernet, Mbuf, Packet, Postmark}; -use capsule::rt2::{self, Outbox, Runtime}; +use capsule::runtime::{self, Outbox, Runtime}; use signal_hook::consts; use signal_hook::flag; use std::sync::atomic::{AtomicBool, Ordering}; @@ -63,7 +63,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; let runtime = Runtime::from_config(config)?; let outbox = runtime.ports().get("cap0")?.outbox()?; diff --git a/examples/pktdump/Cargo.toml b/examples/pktdump/Cargo.toml index f421f28..be1920e 100644 --- a/examples/pktdump/Cargo.toml +++ b/examples/pktdump/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pktdump" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -17,7 +17,7 @@ doctest = false [dependencies] anyhow = "1.0" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } colored = "2.0" signal-hook = "0.3" tracing = "0.1" diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index fbaeaf3..373062f 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -21,7 +21,7 @@ use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::Ipv6; use capsule::packets::ip::IpPacket; use capsule::packets::{EtherTypes, Ethernet, Mbuf, Packet, Postmark, Tcp, Tcp4, Tcp6}; -use capsule::rt2::{self, Runtime}; +use capsule::runtime::{self, Runtime}; use colored::Colorize; use signal_hook::consts; use signal_hook::flag; @@ -81,7 +81,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; let runtime = Runtime::from_config(config)?; runtime.set_port_pipeline("cap0", dump_pkt)?; runtime.set_port_pipeline("cap1", dump_pkt)?; diff --git a/examples/skeleton/Cargo.toml b/examples/skeleton/Cargo.toml index cbc6925..3906f04 100644 --- a/examples/skeleton/Cargo.toml +++ b/examples/skeleton/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "skeleton" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -17,6 +17,6 @@ doctest = false [dependencies] anyhow = "1.0" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } tracing = "0.1" tracing-subscriber = "0.2" diff --git a/examples/skeleton/main.rs b/examples/skeleton/main.rs index 0763f8a..ccb0de8 100644 --- a/examples/skeleton/main.rs +++ b/examples/skeleton/main.rs @@ -17,7 +17,7 @@ */ use anyhow::Result; -use capsule::rt2::{self, Runtime}; +use capsule::runtime::{self, Runtime}; use tracing::{debug, Level}; use tracing_subscriber::fmt; @@ -27,7 +27,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; debug!(?config); let runtime = Runtime::from_config(config)?; diff --git a/examples/syn-flood/Cargo.toml b/examples/syn-flood/Cargo.toml index 355dd44..f9fa98b 100644 --- a/examples/syn-flood/Cargo.toml +++ b/examples/syn-flood/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn-flood" -version = "0.1.0" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" @@ -18,7 +18,7 @@ doctest = false [dependencies] anyhow = "1.0" async-io = "1.3" -capsule = { version = "0.1", path = "../../core" } +capsule = { version = "0.2", path = "../../core" } futures-lite = "1.11" rand = "0.8" signal-hook = "0.3" diff --git a/examples/syn-flood/main.rs b/examples/syn-flood/main.rs index c0062a4..2fcc451 100644 --- a/examples/syn-flood/main.rs +++ b/examples/syn-flood/main.rs @@ -21,7 +21,7 @@ use async_io::Timer; use capsule::net::MacAddr; use capsule::packets::ip::v4::Ipv4; use capsule::packets::{Ethernet, Mbuf, Packet, Tcp4}; -use capsule::rt2::{self, Outbox, Runtime}; +use capsule::runtime::{self, Outbox, Runtime}; use futures_lite::stream::StreamExt; use signal_hook::consts; use signal_hook::flag; @@ -79,7 +79,7 @@ fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber)?; - let config = rt2::load_config()?; + let config = runtime::load_config()?; let runtime = Runtime::from_config(config)?; let term = Arc::new(AtomicBool::new(false)); diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index d72a4a5..7ed51aa 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-ffi" -version = "0.1.5" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 9926702..9956229 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-macros" -version = "0.1.5" +version = "0.2.0" authors = ["Capsule Developers "] license = "Apache-2.0" edition = "2018" diff --git a/macros/src/derive_packet.rs b/macros/src/derive_packet.rs index 76baf65..5e7de69 100644 --- a/macros/src/derive_packet.rs +++ b/macros/src/derive_packet.rs @@ -82,6 +82,7 @@ pub fn gen_icmpv6(input: syn::DeriveInput) -> TokenStream { fn try_push(mut envelope: Self::Envelope, internal: Internal) -> ::anyhow::Result { use ::capsule::packets::icmp::v6::{Icmpv6, Icmpv6Header, Icmpv6Message}; use ::capsule::packets::ip::{IpPacket, ProtocolNumbers}; + use ::capsule::packets::SizeOf; let offset = envelope.payload_offset(); let mbuf = envelope.mbuf_mut(); @@ -180,6 +181,7 @@ pub fn gen_icmpv4(input: syn::DeriveInput) -> TokenStream { fn try_push(mut envelope: Self::Envelope, internal: ::capsule::packets::Internal) -> ::anyhow::Result { use ::capsule::packets::icmp::v4::{Icmpv4, Icmpv4Header, Icmpv4Message}; use ::capsule::packets::ip::{IpPacket, ProtocolNumbers}; + use ::capsule::packets::SizeOf; let offset = envelope.payload_offset(); let mbuf = envelope.mbuf_mut(); diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 0cfca41..23b816e 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -40,7 +40,7 @@ pub fn derive_size_of(input: TokenStream) -> TokenStream { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let expanded = quote! { - impl #impl_generics SizeOf for #name #ty_generics #where_clause { + impl #impl_generics ::capsule::packets::SizeOf for #name #ty_generics #where_clause { fn size_of() -> usize { std::mem::size_of::() } From c0958e9f51af6fa7ab736e8b83ed442315465e66 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Wed, 31 Mar 2021 09:49:30 -0400 Subject: [PATCH 11/18] fix lint --- core/Cargo.toml | 8 +++----- core/src/ffi/dpdk.rs | 25 +++++++++++------------ core/src/ffi/pcap.rs | 1 - core/src/lib.rs | 3 +-- core/src/packets/mbuf.rs | 2 +- core/src/runtime/config.rs | 2 +- core/src/runtime/lcore.rs | 2 +- core/src/runtime/pcap_dump.rs | 3 +++ core/src/runtime/port.rs | 38 +++++++++++++++-------------------- 9 files changed, 38 insertions(+), 46 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index c2e8dac..6daf60a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -29,8 +29,7 @@ clap = "2.33" criterion = { version = "0.3", optional = true } futures-lite = "1.11" libc = "0.2" -metrics-core = { version = "0.5", optional = true } -metrics-runtime = { version = "0.13", optional = true, default-features = false } +metrics = "0.14" once_cell = "1.7" proptest = { version = "1.0", optional = true } regex = "1" @@ -44,10 +43,9 @@ criterion = "0.3" proptest = { version = "1.0", default-features = false, features = ["default-code-coverage"] } [features] -default = ["metrics"] +default = [] compile_failure = [] # compiler tests to check mutability rules are followed -full = ["metrics", "pcap-dump", "testils"] -metrics = ["metrics-core", "metrics-runtime"] +full = ["pcap-dump", "testils"] pcap-dump = [] testils = ["criterion", "proptest"] diff --git a/core/src/ffi/dpdk.rs b/core/src/ffi/dpdk.rs index 2a64962..8c586a1 100644 --- a/core/src/ffi/dpdk.rs +++ b/core/src/ffi/dpdk.rs @@ -132,6 +132,7 @@ pub(crate) fn mempool_lookup>(name: S) -> Result { } /// Returns the number of elements which have been allocated from the mempool. +#[allow(dead_code)] pub(crate) fn mempool_in_use_count(mp: &MempoolPtr) -> usize { unsafe { cffi::rte_mempool_in_use_count(mp.deref()) as usize } } @@ -197,14 +198,14 @@ pub(crate) fn get_next_lcore( match unsafe { cffi::rte_get_next_lcore(i, skip_master, wrap) } { cffi::RTE_MAX_LCORE => None, - id @ _ => Some(LcoreId(id)), + id => Some(LcoreId(id)), } } /// The function passed to `rte_eal_remote_launch`. unsafe extern "C" fn lcore_fn(arg: *mut raw::c_void) -> raw::c_int where - F: FnOnce() -> () + Send + 'static, + F: FnOnce() + Send + 'static, { let f = Box::from_raw(arg as *mut F); @@ -221,7 +222,7 @@ where /// Launches a function on another lcore. pub(crate) fn eal_remote_launch(worker_id: LcoreId, f: F) -> Result<()> where - F: FnOnce() -> () + Send + 'static, + F: FnOnce() + Send + 'static, { let ptr = Box::into_raw(Box::new(f)) as *mut raw::c_void; @@ -299,11 +300,10 @@ pub(crate) fn eth_dev_adjust_nb_rx_tx_desc( /// Returns the value of promiscuous mode for a device. pub(crate) fn eth_promiscuous_get(port_id: PortId) -> bool { - match unsafe { cffi::rte_eth_promiscuous_get(port_id.0).into_result(DpdkError::from_errno) } { - Ok(1) => true, - // assuming port_id is valid, we treat error as mode disabled. - _ => false, - } + let mode = + unsafe { cffi::rte_eth_promiscuous_get(port_id.0).into_result(DpdkError::from_errno) }; + // assuming port_id is valid, treats Ok(0) and Err(_) both as disabled. + matches!(mode, Ok(1)) } /// Enables receipt in promiscuous mode for a device. @@ -326,11 +326,10 @@ pub(crate) fn eth_promiscuous_disable(port_id: PortId) -> Result<()> { /// Returns the value of allmulticast mode for a device. pub(crate) fn eth_allmulticast_get(port_id: PortId) -> bool { - match unsafe { cffi::rte_eth_allmulticast_get(port_id.0).into_result(DpdkError::from_errno) } { - Ok(1) => true, - // assuming port_id is valid, we treat error as mode disabled. - _ => false, - } + let mode = + unsafe { cffi::rte_eth_allmulticast_get(port_id.0).into_result(DpdkError::from_errno) }; + // assuming port_id is valid, treats Ok(0) and Err(_) both as disabled. + matches!(mode, Ok(1)) } /// Enables the receipt of any multicast frame by a device. diff --git a/core/src/ffi/pcap.rs b/core/src/ffi/pcap.rs index 812b043..f009db4 100644 --- a/core/src/ffi/pcap.rs +++ b/core/src/ffi/pcap.rs @@ -20,7 +20,6 @@ use super::{AsStr, EasyPtr, ToCString, ToResult}; use crate::ffi::dpdk::MbufPtr; use anyhow::Result; use capsule_ffi as cffi; -use libc; use std::ops::DerefMut; use std::os::raw; use std::ptr; diff --git a/core/src/lib.rs b/core/src/lib.rs index df02230..8fdeb4b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -82,7 +82,7 @@ //! //! ## Feature flags //! -//! - `default`: Enables metrics by default. +//! - `default`: None of the features are enabled. //! - `pcap-dump`: Enables capturing port traffic to `pcap` files. //! - `testils`: Enables utilities for unit testing and benchmarking. //! - `full`: Enables all features. @@ -107,7 +107,6 @@ //! [rr]: https://rr-project.org/ //! [README]: https://github.com/capsule-rs/capsule/blob/master/README.md //! [sandbox repo]: https://github.com/capsule-rs/sandbox -//! [`metrics`]: crate::metrics //! [kni]: https://github.com/capsule-rs/capsule/tree/master/examples/kni //! [nat64]: https://github.com/capsule-rs/capsule/tree/master/examples/nat64 //! [ping4d]: https://github.com/capsule-rs/capsule/tree/master/examples/ping4d diff --git a/core/src/packets/mbuf.rs b/core/src/packets/mbuf.rs index b28aa13..b60c020 100644 --- a/core/src/packets/mbuf.rs +++ b/core/src/packets/mbuf.rs @@ -365,7 +365,7 @@ impl Mbuf { /// free the raw pointer after use. Otherwise the buffer is leaked. #[inline] pub(crate) fn into_easyptr(self) -> MbufPtr { - let ptr = self.inner.ptr().clone(); + let ptr = *self.inner.ptr(); mem::forget(self); ptr.into() } diff --git a/core/src/runtime/config.rs b/core/src/runtime/config.rs index c5b201e..6aaf30c 100644 --- a/core/src/runtime/config.rs +++ b/core/src/runtime/config.rs @@ -123,7 +123,7 @@ impl RuntimeConfig { } }); - cores.sort(); + cores.sort_unstable(); cores.dedup(); cores } diff --git a/core/src/runtime/lcore.rs b/core/src/runtime/lcore.rs index 15a353d..7fb1159 100644 --- a/core/src/runtime/lcore.rs +++ b/core/src/runtime/lcore.rs @@ -156,7 +156,7 @@ mod tests { #[capsule::test] fn get_current_lcore_id_from_non_eal() { - let lcore_id = thread::spawn(|| LcoreId::current()).join().expect("panic!"); + let lcore_id = thread::spawn(LcoreId::current).join().expect("panic!"); assert_eq!(LcoreId::ANY, lcore_id); } diff --git a/core/src/runtime/pcap_dump.rs b/core/src/runtime/pcap_dump.rs index 8c0a39b..f8a72f4 100644 --- a/core/src/runtime/pcap_dump.rs +++ b/core/src/runtime/pcap_dump.rs @@ -72,6 +72,9 @@ impl Drop for CaptureFile { /// The pcap dump manager. pub(crate) struct PcapDump { output_dir: String, + // we need the extra level of indirection because we need stable + // pointers to pass to ffi code. + #[allow(clippy::vec_box)] captures: Vec>, } diff --git a/core/src/runtime/port.rs b/core/src/runtime/port.rs index 3e65a68..0a941e5 100644 --- a/core/src/runtime/port.rs +++ b/core/src/runtime/port.rs @@ -90,7 +90,7 @@ impl Port { self.outbox .as_ref() .map(|s| Outbox(self.name.clone(), s.clone())) - .ok_or(PortError::TxNotEnabled.into()) + .ok_or_else(|| PortError::TxNotEnabled.into()) } /// Spawns the port receiving loop. @@ -102,7 +102,7 @@ impl Port { let shutdown = self.shutdown.as_ref().unwrap(); // can't run loop without assigned rx cores. - ensure!(self.rx_lcores.len() > 0, PortError::RxNotEnabled); + ensure!(!self.rx_lcores.is_empty(), PortError::RxNotEnabled); // pipeline already set if the trigger is waited on. ensure!(!shutdown.is_waited(), PortError::PipelineSet); @@ -292,29 +292,23 @@ async fn tx_loop( let txq = PortTxQueue { port_id, queue_id }; let mut ptrs = Vec::with_capacity(batch_size); - loop { - if let Ok(ptr) = receiver.recv().await { - ptrs.push(ptr); - - // try to batch the packets up to batch size. - for _ in 1..batch_size { - match receiver.try_recv() { - Ok(ptr) => ptrs.push(ptr), - // no more packets to batch, ready to transmit. - Err(_) => break, - } + while let Ok(ptr) = receiver.recv().await { + ptrs.push(ptr); + + // try to batch the packets up to batch size. + for _ in 1..batch_size { + match receiver.try_recv() { + Ok(ptr) => ptrs.push(ptr), + // no more packets to batch, ready to transmit. + Err(_) => break, } + } - txq.transmit(&mut ptrs); + txq.transmit(&mut ptrs); - // cooperatively moves to the back of the execution queue, - // making room for other tasks before transmitting again. - future::yield_now().await; - } else { - // this branch can only be reached if the channel is closed, - // indicating the tx loop should exit. - break; - } + // cooperatively moves to the back of the execution queue, + // making room for other tasks before transmitting again. + future::yield_now().await; } debug!(port = ?port_name, lcore = ?LcoreId::current(), "tx loop exited."); From 36ff6aee035e55cfc8db9251612c5b5919971d5f Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Wed, 31 Mar 2021 16:44:14 -0400 Subject: [PATCH 12/18] move ethernet, tcp, udp mods --- bench/packets.rs | 4 +++- core/src/packets/arp.rs | 3 ++- core/src/packets/ethernet.rs | 2 ++ core/src/packets/icmp/v4/echo_reply.rs | 3 ++- core/src/packets/icmp/v4/echo_request.rs | 3 ++- core/src/packets/icmp/v4/mod.rs | 3 ++- core/src/packets/icmp/v4/redirect.rs | 3 ++- core/src/packets/icmp/v4/time_exceeded.rs | 3 ++- core/src/packets/icmp/v6/echo_reply.rs | 3 ++- core/src/packets/icmp/v6/echo_request.rs | 3 ++- core/src/packets/icmp/v6/mod.rs | 3 ++- core/src/packets/icmp/v6/ndp/mod.rs | 2 +- core/src/packets/icmp/v6/ndp/neighbor_advert.rs | 3 ++- core/src/packets/icmp/v6/ndp/neighbor_solicit.rs | 3 ++- .../packets/icmp/v6/ndp/options/link_layer_addr.rs | 3 ++- core/src/packets/icmp/v6/ndp/options/mtu.rs | 3 ++- core/src/packets/icmp/v6/ndp/options/prefix_info.rs | 3 ++- core/src/packets/icmp/v6/ndp/options/redirected.rs | 3 ++- core/src/packets/icmp/v6/ndp/redirect.rs | 3 ++- core/src/packets/icmp/v6/ndp/router_advert.rs | 3 ++- core/src/packets/icmp/v6/ndp/router_solicit.rs | 3 ++- core/src/packets/icmp/v6/time_exceeded.rs | 3 ++- core/src/packets/icmp/v6/too_big.rs | 3 ++- core/src/packets/icmp/v6/unreachable.rs | 3 ++- core/src/packets/ip/v4.rs | 3 ++- core/src/packets/ip/v6/fragment.rs | 3 ++- core/src/packets/ip/v6/mod.rs | 3 ++- core/src/packets/ip/v6/srh.rs | 4 +++- core/src/packets/mbuf.rs | 2 +- core/src/packets/mod.rs | 11 +++++------ core/src/packets/tcp.rs | 5 ++++- core/src/packets/udp.rs | 5 ++++- core/src/testils/packet.rs | 5 ++++- core/src/testils/proptest/strategy.rs | 5 ++++- examples/kni/main.rs | 4 +++- examples/nat64/main.rs | 4 +++- examples/ping4d/main.rs | 3 ++- examples/pktdump/main.rs | 4 +++- examples/syn-flood/main.rs | 4 +++- 39 files changed, 93 insertions(+), 43 deletions(-) diff --git a/bench/packets.rs b/bench/packets.rs index 35aecd6..61ce7c6 100644 --- a/bench/packets.rs +++ b/bench/packets.rs @@ -18,9 +18,11 @@ use anyhow::Result; use capsule::fieldmap; +use capsule::packets::ethernet::Ethernet; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::{Ipv6, SegmentRouting}; -use capsule::packets::{Ethernet, Mbuf, Packet, Udp4}; +use capsule::packets::udp::Udp4; +use capsule::packets::{Mbuf, Packet}; use capsule::testils::criterion::BencherExt; use capsule::testils::proptest::*; use capsule::testils::{PacketExt, Rvg}; diff --git a/core/src/packets/arp.rs b/core/src/packets/arp.rs index 5c6bc08..ab2d104 100644 --- a/core/src/packets/arp.rs +++ b/core/src/packets/arp.rs @@ -20,8 +20,9 @@ use crate::ensure; use crate::net::MacAddr; +use crate::packets::ethernet::{EtherTypes, Ethernet}; use crate::packets::types::u16be; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::Ipv4Addr; diff --git a/core/src/packets/ethernet.rs b/core/src/packets/ethernet.rs index c1681f4..6f6c647 100644 --- a/core/src/packets/ethernet.rs +++ b/core/src/packets/ethernet.rs @@ -16,6 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Ethernet Protocol. + use crate::ensure; use crate::net::MacAddr; use crate::packets::types::u16be; diff --git a/core/src/packets/icmp/v4/echo_reply.rs b/core/src/packets/icmp/v4/echo_reply.rs index 8199d24..35738ba 100644 --- a/core/src/packets/icmp/v4/echo_reply.rs +++ b/core/src/packets/icmp/v4/echo_reply.rs @@ -218,8 +218,9 @@ struct EchoReplyBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_echo_reply_body() { diff --git a/core/src/packets/icmp/v4/echo_request.rs b/core/src/packets/icmp/v4/echo_request.rs index cfca15d..e388abd 100644 --- a/core/src/packets/icmp/v4/echo_request.rs +++ b/core/src/packets/icmp/v4/echo_request.rs @@ -219,8 +219,9 @@ struct EchoRequestBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_echo_request_body() { diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index fda5e11..4375358 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -446,8 +446,9 @@ pub trait Icmpv4Packet { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_UDP_PACKET}; #[test] diff --git a/core/src/packets/icmp/v4/redirect.rs b/core/src/packets/icmp/v4/redirect.rs index 96e52e5..9177800 100644 --- a/core/src/packets/icmp/v4/redirect.rs +++ b/core/src/packets/icmp/v4/redirect.rs @@ -215,8 +215,9 @@ impl Default for RedirectBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; #[test] diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs index 4fd9b05..8a5e697 100644 --- a/core/src/packets/icmp/v4/time_exceeded.rs +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -179,8 +179,9 @@ struct TimeExceededBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/echo_reply.rs b/core/src/packets/icmp/v6/echo_reply.rs index 97f6ba0..02d2d43 100644 --- a/core/src/packets/icmp/v6/echo_reply.rs +++ b/core/src/packets/icmp/v6/echo_reply.rs @@ -217,8 +217,9 @@ struct EchoReplyBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_echo_reply_body() { diff --git a/core/src/packets/icmp/v6/echo_request.rs b/core/src/packets/icmp/v6/echo_request.rs index 4ac849d..d288521 100644 --- a/core/src/packets/icmp/v6/echo_request.rs +++ b/core/src/packets/icmp/v6/echo_request.rs @@ -218,8 +218,9 @@ struct EchoRequestBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_echo_request_body() { diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index 5cbf777..bdd3363 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -498,9 +498,10 @@ pub trait Icmpv6Packet { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::RouterAdvertisement; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV6_PACKET, IPV6_TCP_PACKET, ROUTER_ADVERT_PACKET}; #[test] diff --git a/core/src/packets/icmp/v6/ndp/mod.rs b/core/src/packets/icmp/v6/ndp/mod.rs index b4ed14d..80ad51b 100644 --- a/core/src/packets/icmp/v6/ndp/mod.rs +++ b/core/src/packets/icmp/v6/ndp/mod.rs @@ -551,8 +551,8 @@ pub trait NdpOption<'a> { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::Ethernet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[capsule::test] diff --git a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs index 645e09c..9c532fa 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs @@ -270,8 +270,9 @@ impl Default for NeighborAdvertisementBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_neighbor_advertisement_body() { diff --git a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs index 169e0a4..18f09f5 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs @@ -185,8 +185,9 @@ impl Default for NeighborSolicitationBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_neighbor_solicitation_body() { diff --git a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs index de5b26d..c196745 100644 --- a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs +++ b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs @@ -190,9 +190,10 @@ impl Default for LinkLayerAddressFields { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Packet}; + use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/ndp/options/mtu.rs b/core/src/packets/icmp/v6/ndp/options/mtu.rs index e53cc31..dc533af 100644 --- a/core/src/packets/icmp/v6/ndp/options/mtu.rs +++ b/core/src/packets/icmp/v6/ndp/options/mtu.rs @@ -170,9 +170,10 @@ impl Default for MtuFields { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Packet}; + use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs index 48b4509..d721ca5 100644 --- a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs +++ b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs @@ -308,9 +308,10 @@ impl Default for PrefixInformationFields { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Packet}; + use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/ndp/options/redirected.rs b/core/src/packets/icmp/v6/ndp/options/redirected.rs index 0e31b68..6ba9d19 100644 --- a/core/src/packets/icmp/v6/ndp/options/redirected.rs +++ b/core/src/packets/icmp/v6/ndp/options/redirected.rs @@ -232,9 +232,10 @@ impl Default for RedirectedHeaderFields { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, Redirect}; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Packet}; + use crate::packets::Packet; #[test] fn size_of_redirected_header_fields() { diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 5d6079d..1f3fe65 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -241,8 +241,9 @@ impl Default for RedirectBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_redirect_body() { diff --git a/core/src/packets/icmp/v6/ndp/router_advert.rs b/core/src/packets/icmp/v6/ndp/router_advert.rs index 8447e24..4e1d129 100644 --- a/core/src/packets/icmp/v6/ndp/router_advert.rs +++ b/core/src/packets/icmp/v6/ndp/router_advert.rs @@ -301,8 +301,9 @@ struct RouterAdvertisementBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_router_advertisement_body() { diff --git a/core/src/packets/icmp/v6/ndp/router_solicit.rs b/core/src/packets/icmp/v6/ndp/router_solicit.rs index bb12798..bf122b0 100644 --- a/core/src/packets/icmp/v6/ndp/router_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/router_solicit.rs @@ -143,8 +143,9 @@ struct RouterSolicitationBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; #[test] fn size_of_router_solicitation_body() { diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index 2d590bc..57e572d 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -163,8 +163,9 @@ struct TimeExceededBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index 3c66147..71d1cf5 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -188,8 +188,9 @@ struct PacketTooBigBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; #[test] diff --git a/core/src/packets/icmp/v6/unreachable.rs b/core/src/packets/icmp/v6/unreachable.rs index 0b2d447..827682e 100644 --- a/core/src/packets/icmp/v6/unreachable.rs +++ b/core/src/packets/icmp/v6/unreachable.rs @@ -163,8 +163,9 @@ struct DestinationUnreachableBody { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; #[test] diff --git a/core/src/packets/ip/v4.rs b/core/src/packets/ip/v4.rs index a9558a3..e2c6f1e 100644 --- a/core/src/packets/ip/v4.rs +++ b/core/src/packets/ip/v4.rs @@ -20,9 +20,10 @@ use crate::ensure; use crate::packets::checksum::{self, PseudoHeader}; +use crate::packets::ethernet::{EtherTypes, Ethernet}; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::u16be; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv4Addr}; diff --git a/core/src/packets/ip/v6/fragment.rs b/core/src/packets/ip/v6/fragment.rs index 1d926c0..7d523c4 100644 --- a/core/src/packets/ip/v6/fragment.rs +++ b/core/src/packets/ip/v6/fragment.rs @@ -329,8 +329,9 @@ struct FragmentHeader { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, IPV6_TCP_PACKET}; #[test] diff --git a/core/src/packets/ip/v6/mod.rs b/core/src/packets/ip/v6/mod.rs index 2938ce4..5c10414 100644 --- a/core/src/packets/ip/v6/mod.rs +++ b/core/src/packets/ip/v6/mod.rs @@ -26,9 +26,10 @@ pub use self::srh::*; use crate::ensure; use crate::packets::checksum::PseudoHeader; +use crate::packets::ethernet::{EtherTypes, Ethernet}; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::{u16be, u32be}; -use crate::packets::{EtherTypes, Ethernet, Internal, Packet, SizeOf}; +use crate::packets::{Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv6Addr}; diff --git a/core/src/packets/ip/v6/srh.rs b/core/src/packets/ip/v6/srh.rs index 446d7e2..bdad10b 100644 --- a/core/src/packets/ip/v6/srh.rs +++ b/core/src/packets/ip/v6/srh.rs @@ -523,9 +523,11 @@ impl Default for SegmentRoutingHeader { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::ProtocolNumbers; - use crate::packets::{Ethernet, Mbuf, Tcp, Tcp6}; + use crate::packets::tcp::{Tcp, Tcp6}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV6_TCP_PACKET, SR_TCP_PACKET}; #[test] diff --git a/core/src/packets/mbuf.rs b/core/src/packets/mbuf.rs index b60c020..79d76d0 100644 --- a/core/src/packets/mbuf.rs +++ b/core/src/packets/mbuf.rs @@ -16,8 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{Internal, Packet, SizeOf}; use crate::ffi::dpdk::{self, MbufPtr}; +use crate::packets::{Internal, Packet, SizeOf}; use crate::runtime::Mempool; use crate::{ensure, trace}; use anyhow::Result; diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index 0f61fab..39a5758 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -20,20 +20,17 @@ pub mod arp; pub mod checksum; -mod ethernet; +pub mod ethernet; pub mod icmp; pub mod ip; mod mbuf; mod size_of; -mod tcp; +pub mod tcp; pub mod types; -mod udp; +pub mod udp; -pub use self::ethernet::*; pub use self::mbuf::*; pub use self::size_of::*; -pub use self::tcp::*; -pub use self::udp::*; pub use capsule_macros::SizeOf; use anyhow::{Context, Result}; @@ -358,7 +355,9 @@ pub enum Postmark { mod tests { use super::*; use crate::net::MacAddr; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; + use crate::packets::udp::Udp4; use crate::testils::byte_arrays::IPV4_UDP_PACKET; #[capsule::test] diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index 818649e..b7d11e9 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -16,6 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Transmission Control Protocol. + use crate::ensure; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::Ipv6; @@ -664,8 +666,9 @@ impl Default for TcpHeader { #[cfg(test)] mod tests { use super::*; + use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::SegmentRouting; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET, SR_TCP_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 49c7ba1..541fd1d 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -16,6 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! User Datagram Protocol. + use crate::ensure; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::Ipv6; @@ -386,7 +388,8 @@ struct UdpHeader { #[cfg(test)] mod tests { use super::*; - use crate::packets::{Ethernet, Mbuf}; + use crate::packets::ethernet::Ethernet; + use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; diff --git a/core/src/testils/packet.rs b/core/src/testils/packet.rs index fbdb710..c0e4d86 100644 --- a/core/src/testils/packet.rs +++ b/core/src/testils/packet.rs @@ -16,9 +16,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::{Ipv6, SegmentRouting}; -use crate::packets::{Ethernet, Packet, Tcp, Tcp4, Tcp6, Udp4, Udp6}; +use crate::packets::tcp::{Tcp, Tcp4, Tcp6}; +use crate::packets::udp::{Udp4, Udp6}; +use crate::packets::Packet; /// [`Packet`] extension trait. /// diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index e9aa1cf..12f0707 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -19,10 +19,13 @@ //! Proptest strategies. use crate::net::MacAddr; +use crate::packets::ethernet::{EtherType, EtherTypes, Ethernet}; use crate::packets::ip::v4::Ipv4; use crate::packets::ip::v6::{Ipv6, Ipv6Packet, SegmentRouting}; use crate::packets::ip::{Flow, IpPacket, ProtocolNumber, ProtocolNumbers}; -use crate::packets::{EtherType, EtherTypes, Ethernet, Mbuf, Packet, Tcp, Udp}; +use crate::packets::tcp::Tcp; +use crate::packets::udp::Udp; +use crate::packets::{Mbuf, Packet}; use crate::testils::Rvg; use proptest::arbitrary::{any, Arbitrary}; use proptest::collection::vec; diff --git a/examples/kni/main.rs b/examples/kni/main.rs index 4720f36..15c9a7b 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -17,10 +17,12 @@ */ use anyhow::Result; +use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v6::Icmpv6; use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; -use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Udp6}; +use capsule::packets::udp::Udp6; +use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Outbox, Runtime}; use colored::Colorize; use signal_hook::consts; diff --git a/examples/nat64/main.rs b/examples/nat64/main.rs index e820860..fcac724 100644 --- a/examples/nat64/main.rs +++ b/examples/nat64/main.rs @@ -19,10 +19,12 @@ use anyhow::Result; use bimap::BiMap; use capsule::net::MacAddr; +use capsule::packets::ethernet::Ethernet; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; -use capsule::packets::{Ethernet, Mbuf, Packet, Postmark, Tcp4, Tcp6}; +use capsule::packets::tcp::{Tcp4, Tcp6}; +use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Outbox, Runtime}; use colored::Colorize; use once_cell::sync::Lazy; diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 350e77a..acbf9cd 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -17,9 +17,10 @@ */ use anyhow::Result; +use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; use capsule::packets::ip::v4::Ipv4; -use capsule::packets::{Ethernet, Mbuf, Packet, Postmark}; +use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Outbox, Runtime}; use signal_hook::consts; use signal_hook::flag; diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index 373062f..cd768cc 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -17,10 +17,12 @@ */ use anyhow::{anyhow, Result}; +use capsule::packets::ethernet::{EtherTypes, Ethernet}; use capsule::packets::ip::v4::Ipv4; use capsule::packets::ip::v6::Ipv6; use capsule::packets::ip::IpPacket; -use capsule::packets::{EtherTypes, Ethernet, Mbuf, Packet, Postmark, Tcp, Tcp4, Tcp6}; +use capsule::packets::tcp::{Tcp, Tcp4, Tcp6}; +use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Runtime}; use colored::Colorize; use signal_hook::consts; diff --git a/examples/syn-flood/main.rs b/examples/syn-flood/main.rs index 2fcc451..8f9accb 100644 --- a/examples/syn-flood/main.rs +++ b/examples/syn-flood/main.rs @@ -19,8 +19,10 @@ use anyhow::Result; use async_io::Timer; use capsule::net::MacAddr; +use capsule::packets::ethernet::Ethernet; use capsule::packets::ip::v4::Ipv4; -use capsule::packets::{Ethernet, Mbuf, Packet, Tcp4}; +use capsule::packets::tcp::Tcp4; +use capsule::packets::{Mbuf, Packet}; use capsule::runtime::{self, Outbox, Runtime}; use futures_lite::stream::StreamExt; use signal_hook::consts; From 2420db1587098a86fb572ca50f073886a52366de Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Fri, 7 Jan 2022 18:20:02 -0500 Subject: [PATCH 13/18] add datalink trait for arp --- core/src/packets/arp.rs | 92 +++++++++++------------------------- core/src/packets/ethernet.rs | 14 +++++- core/src/packets/ip/mod.rs | 2 + core/src/packets/mod.rs | 24 ++++++++++ 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/core/src/packets/arp.rs b/core/src/packets/arp.rs index ab2d104..b3e460f 100644 --- a/core/src/packets/arp.rs +++ b/core/src/packets/arp.rs @@ -20,9 +20,9 @@ use crate::ensure; use crate::net::MacAddr; -use crate::packets::ethernet::{EtherTypes, Ethernet}; +use crate::packets::ethernet::{EtherType, EtherTypes, Ethernet}; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet, SizeOf}; +use crate::packets::{Datalink, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::Ipv4Addr; @@ -84,13 +84,13 @@ use std::ptr::NonNull; /// defined by *P Length*. /// /// [IETF RFC 826]: https://tools.ietf.org/html/rfc826 -pub struct Arp { - envelope: Ethernet, +pub struct Arp { + envelope: E, header: NonNull>, offset: usize, } -impl Arp { +impl Arp { #[inline] fn header(&self) -> &ArpHeader { unsafe { self.header.as_ref() } @@ -114,14 +114,20 @@ impl Arp { } /// Returns the protocol type. + /// + /// [IANA] assigned Protocol type numbers share the same space as + /// [`EtherTypes`]. + /// + /// [IANA]: https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-3 + /// [`EtherTypes`]: crate::packets::ethernet::EtherTypes #[inline] - pub fn protocol_type(&self) -> ProtocolType { - ProtocolType::new(self.header().protocol_type.into()) + pub fn protocol_type(&self) -> EtherType { + EtherType::new(self.header().protocol_type.into()) } /// Sets the protocol type. #[inline] - fn set_protocol_type(&mut self, protocol_type: ProtocolType) { + fn set_protocol_type(&mut self, protocol_type: EtherType) { self.header_mut().protocol_type = protocol_type.0.into() } @@ -210,7 +216,7 @@ impl Arp { } } -impl fmt::Debug for Arp { +impl fmt::Debug for Arp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("arp") .field("hardware_type", &format!("{}", self.hardware_type())) @@ -241,9 +247,8 @@ impl fmt::Debug for Arp { } } -impl Packet for Arp { - /// The preceding type for ARP must be `Ethernet`. - type Envelope = Ethernet; +impl Packet for Arp { + type Envelope = E; #[inline] fn envelope(&self) -> &Self::Envelope { @@ -274,7 +279,7 @@ impl Packet for Arp { } } - /// Parses the Ethernet payload as an ARP packet. + /// Parses the payload as an ARP packet. /// /// # Errors /// @@ -292,7 +297,7 @@ impl Packet for Arp { #[inline] fn try_parse(envelope: Self::Envelope, _internal: Internal) -> Result { ensure!( - envelope.ether_type() == EtherTypes::Arp, + envelope.protocol_type() == EtherTypes::Arp, anyhow!("not an ARP packet.") ); @@ -362,7 +367,7 @@ impl Packet for Arp { mbuf.extend(offset, ArpHeader::::size_of())?; let header = mbuf.write_data(offset, &ArpHeader::::default())?; - envelope.set_ether_type(EtherTypes::Arp); + envelope.set_protocol_type(EtherTypes::Arp); let mut packet = Arp { envelope, @@ -427,49 +432,6 @@ impl fmt::Display for HardwareType { } } -/// [IANA] assigned protocol type. -/// -/// See [`ProtocolTypes`] for which are current supported. -/// -/// [IANA]: https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-3 -/// [`ProtocolTypes`]: crate::packets::arp::ProtocolTypes -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -#[repr(C, packed)] -pub struct ProtocolType(u16); - -impl ProtocolType { - /// Creates a new protocol type. - pub fn new(value: u16) -> Self { - ProtocolType(value) - } -} - -/// Supported protocol types. -#[allow(non_snake_case)] -#[allow(non_upper_case_globals)] -pub mod ProtocolTypes { - use super::ProtocolType; - - /// Internet protocol version 4. - pub const Ipv4: ProtocolType = ProtocolType(0x0800); -} - -impl fmt::Display for ProtocolType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - ProtocolTypes::Ipv4 => "IPv4".to_string(), - _ => { - let t = self.0; - format!("0x{:04x}", t) - } - } - ) - } -} - /// [IANA] assigned operation code. /// /// See [`OperationCodes`] for which are current supported. @@ -547,7 +509,7 @@ impl HardwareAddr for MacAddr { /// A trait implemented by ARP protocol address types. pub trait ProtocolAddr: SizeOf + Copy + fmt::Display { /// Returns the associated protocol type of the given address. - fn addr_type() -> ProtocolType; + fn addr_type() -> EtherType; /// Returns the default value. /// @@ -557,8 +519,8 @@ pub trait ProtocolAddr: SizeOf + Copy + fmt::Display { } impl ProtocolAddr for Ipv4Addr { - fn addr_type() -> ProtocolType { - ProtocolTypes::Ipv4 + fn addr_type() -> EtherType { + EtherTypes::Ipv4 } fn default() -> Self { @@ -566,8 +528,8 @@ impl ProtocolAddr for Ipv4Addr { } } -/// A type alias for an IPv4 ARP packet. -pub type Arp4 = Arp; +/// A type alias for an Ethernet IPv4 ARP packet. +pub type Arp4 = Arp; /// ARP header. #[allow(missing_debug_implementations)] @@ -635,7 +597,7 @@ mod tests { let arp4 = ethernet.parse::().unwrap(); assert_eq!(HardwareTypes::Ethernet, arp4.hardware_type()); - assert_eq!(ProtocolTypes::Ipv4, arp4.protocol_type()); + assert_eq!(EtherTypes::Ipv4, arp4.protocol_type()); assert_eq!(6, arp4.hardware_addr_len()); assert_eq!(4, arp4.protocol_addr_len()); assert_eq!(OperationCodes::Request, arp4.operation_code()); @@ -655,7 +617,7 @@ mod tests { // make sure types are set properly assert_eq!(HardwareTypes::Ethernet, arp4.hardware_type()); - assert_eq!(ProtocolTypes::Ipv4, arp4.protocol_type()); + assert_eq!(EtherTypes::Ipv4, arp4.protocol_type()); assert_eq!(6, arp4.hardware_addr_len()); assert_eq!(4, arp4.protocol_addr_len()); diff --git a/core/src/packets/ethernet.rs b/core/src/packets/ethernet.rs index 6f6c647..3de8158 100644 --- a/core/src/packets/ethernet.rs +++ b/core/src/packets/ethernet.rs @@ -21,7 +21,7 @@ use crate::ensure; use crate::net::MacAddr; use crate::packets::types::u16be; -use crate::packets::{Internal, Mbuf, Packet, SizeOf}; +use crate::packets::{Datalink, Internal, Mbuf, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::ptr::NonNull; @@ -331,6 +331,18 @@ impl Packet for Ethernet { } } +impl Datalink for Ethernet { + #[inline] + fn protocol_type(&self) -> EtherType { + self.ether_type() + } + + #[inline] + fn set_protocol_type(&mut self, ether_type: EtherType) { + self.set_ether_type(ether_type) + } +} + /// The protocol identifier of the Ethernet frame payload. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[repr(C, packed)] diff --git a/core/src/packets/ip/mod.rs b/core/src/packets/ip/mod.rs index d56cb58..5c466d1 100644 --- a/core/src/packets/ip/mod.rs +++ b/core/src/packets/ip/mod.rs @@ -273,7 +273,9 @@ mod tests { assert_eq!("TCP", ProtocolNumbers::Tcp.to_string()); assert_eq!("UDP", ProtocolNumbers::Udp.to_string()); assert_eq!("IPv6 Route", ProtocolNumbers::Ipv6Route.to_string()); + assert_eq!("IPv6 Frag", ProtocolNumbers::Ipv6Frag.to_string()); assert_eq!("ICMPv6", ProtocolNumbers::Icmpv6.to_string()); + assert_eq!("ICMPv4", ProtocolNumbers::Icmpv4.to_string()); assert_eq!("0x00", ProtocolNumber::new(0).to_string()); } } diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index 39a5758..32cc00e 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -33,6 +33,7 @@ pub use self::mbuf::*; pub use self::size_of::*; pub use capsule_macros::SizeOf; +use crate::packets::ethernet::EtherType; use anyhow::{Context, Result}; use std::fmt; use std::marker::PhantomData; @@ -301,6 +302,29 @@ pub trait Packet { } } +/// A trait datalink layer protocol can implement. +/// +/// A datalink protocol must implement this trait if it needs to encapsulate +/// network protocols like IP or ARP. Otherwise, it should not implement this +/// trait. +pub trait Datalink: Packet { + /// Gets the encapsulated packet type. + /// + /// Returns the ethernet protocol type codes because ethernet is the most + /// ubiquitous datalink protocol. Other datalink like InfiniBand adopted + /// the ethernet type codes. When implementing a datalink with its own + /// type codes, a translation from ether type is needed. + fn protocol_type(&self) -> EtherType; + + /// Sets the protocol type of the encapsulated packet. + /// + /// Uses the ethernet protocol type codes because ethernet is the most + /// ubiquitous datalink protocol. Other datalink like InfiniBand adopted + /// the ethernet type codes. When implementing a datalink with its own + /// type codes, a translation from ether type is needed. + fn set_protocol_type(&mut self, ether_type: EtherType); +} + /// Immutable smart pointer to a struct. /// /// A smart pointer that prevents the struct from being modified. The main From 7a0c6d3fd721a39e1db23ab0b91310c402417c30 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Fri, 7 Jan 2022 22:20:02 -0500 Subject: [PATCH 14/18] add datalink trait for ipv4 --- core/src/packets/arp.rs | 6 +- core/src/packets/icmp/v4/echo_reply.rs | 29 ++--- core/src/packets/icmp/v4/echo_request.rs | 29 ++--- core/src/packets/icmp/v4/mod.rs | 74 ++++++------ core/src/packets/icmp/v4/redirect.rs | 39 ++++--- core/src/packets/icmp/v4/time_exceeded.rs | 39 ++++--- core/src/packets/ip/v4.rs | 131 ++++++++++++---------- core/src/packets/mod.rs | 51 ++++----- core/src/packets/tcp.rs | 31 ++--- core/src/packets/udp.rs | 31 ++--- core/src/testils/packet.rs | 6 +- core/src/testils/proptest/strategy.rs | 6 +- macros/src/derive_packet.rs | 12 +- 13 files changed, 255 insertions(+), 229 deletions(-) diff --git a/core/src/packets/arp.rs b/core/src/packets/arp.rs index b3e460f..1d69b54 100644 --- a/core/src/packets/arp.rs +++ b/core/src/packets/arp.rs @@ -114,10 +114,10 @@ impl Arp { } /// Returns the protocol type. - /// - /// [IANA] assigned Protocol type numbers share the same space as + /// + /// [IANA] assigned Protocol type numbers share the same space as /// [`EtherTypes`]. - /// + /// /// [IANA]: https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-3 /// [`EtherTypes`]: crate::packets::ethernet::EtherTypes #[inline] diff --git a/core/src/packets/icmp/v4/echo_reply.rs b/core/src/packets/icmp/v4/echo_reply.rs index 35738ba..dc6c482 100644 --- a/core/src/packets/icmp/v4/echo_reply.rs +++ b/core/src/packets/icmp/v4/echo_reply.rs @@ -17,6 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; +use crate::packets::ip::v4::Ipv4Packet; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -46,12 +47,12 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct EchoReply { - icmp: Icmpv4, +pub struct EchoReply { + icmp: Icmpv4, body: NonNull, } -impl EchoReply { +impl EchoReply { #[inline] fn body(&self) -> &EchoReplyBody { unsafe { self.body.as_ref() } @@ -127,7 +128,7 @@ impl EchoReply { } } -impl fmt::Debug for EchoReply { +impl fmt::Debug for EchoReply { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EchoReply") .field("type", &format!("{}", self.msg_type())) @@ -142,24 +143,26 @@ impl fmt::Debug for EchoReply { } } -impl Icmpv4Message for EchoReply { +impl Icmpv4Message for EchoReply { + type Envelope = E; + #[inline] fn msg_type() -> Icmpv4Type { Icmpv4Types::EchoReply } #[inline] - fn icmp(&self) -> &Icmpv4 { + fn icmp(&self) -> &Icmpv4 { &self.icmp } #[inline] - fn icmp_mut(&mut self) -> &mut Icmpv4 { + fn icmp_mut(&mut self) -> &mut Icmpv4 { &mut self.icmp } #[inline] - fn into_icmp(self) -> Icmpv4 { + fn into_icmp(self) -> Icmpv4 { self.icmp } @@ -178,7 +181,7 @@ impl Icmpv4Message for EchoReply { /// Returns an error if the payload does not have sufficient data for /// the echo reply message body. #[inline] - fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { + fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { let mbuf = icmp.mbuf(); let offset = icmp.payload_offset(); let body = mbuf.read_data(offset)?; @@ -193,7 +196,7 @@ impl Icmpv4Message for EchoReply { /// /// Returns an error if the buffer does not have enough free space. #[inline] - fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { + fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { let offset = icmp.payload_offset(); let mbuf = icmp.mbuf_mut(); @@ -219,7 +222,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; #[test] @@ -231,8 +234,8 @@ mod tests { fn push_and_set_echo_reply() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut echo = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut echo = ip4.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/echo_request.rs b/core/src/packets/icmp/v4/echo_request.rs index e388abd..0d8412d 100644 --- a/core/src/packets/icmp/v4/echo_request.rs +++ b/core/src/packets/icmp/v4/echo_request.rs @@ -17,6 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; +use crate::packets::ip::v4::Ipv4Packet; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -47,12 +48,12 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct EchoRequest { - icmp: Icmpv4, +pub struct EchoRequest { + icmp: Icmpv4, body: NonNull, } -impl EchoRequest { +impl EchoRequest { #[inline] fn body(&self) -> &EchoRequestBody { unsafe { self.body.as_ref() } @@ -128,7 +129,7 @@ impl EchoRequest { } } -impl fmt::Debug for EchoRequest { +impl fmt::Debug for EchoRequest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EchoRequest") .field("type", &format!("{}", self.msg_type())) @@ -143,24 +144,26 @@ impl fmt::Debug for EchoRequest { } } -impl Icmpv4Message for EchoRequest { +impl Icmpv4Message for EchoRequest { + type Envelope = E; + #[inline] fn msg_type() -> Icmpv4Type { Icmpv4Types::EchoRequest } #[inline] - fn icmp(&self) -> &Icmpv4 { + fn icmp(&self) -> &Icmpv4 { &self.icmp } #[inline] - fn icmp_mut(&mut self) -> &mut Icmpv4 { + fn icmp_mut(&mut self) -> &mut Icmpv4 { &mut self.icmp } #[inline] - fn into_icmp(self) -> Icmpv4 { + fn into_icmp(self) -> Icmpv4 { self.icmp } @@ -179,7 +182,7 @@ impl Icmpv4Message for EchoRequest { /// Returns an error if the payload does not have sufficient data for /// the echo request message body. #[inline] - fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { + fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { let mbuf = icmp.mbuf(); let offset = icmp.payload_offset(); let body = mbuf.read_data(offset)?; @@ -194,7 +197,7 @@ impl Icmpv4Message for EchoRequest { /// /// Returns an error if the buffer does not have enough free space. #[inline] - fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { + fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { let offset = icmp.payload_offset(); let mbuf = icmp.mbuf_mut(); @@ -220,7 +223,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; #[test] @@ -232,8 +235,8 @@ mod tests { fn push_and_set_echo_request() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut echo = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut echo = ip4.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index 4375358..3ac2fdc 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -30,7 +30,7 @@ pub use self::time_exceeded::*; pub use capsule_macros::Icmpv4Packet; use crate::ensure; -use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v4::Ipv4Packet; use crate::packets::ip::ProtocolNumbers; use crate::packets::types::u16be; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -72,13 +72,13 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 /// [`Icmpv4Message`]: Icmpv4Message -pub struct Icmpv4 { - envelope: Ipv4, +pub struct Icmpv4 { + envelope: E, header: NonNull, offset: usize, } -impl Icmpv4 { +impl Icmpv4 { #[inline] fn header(&self) -> &Icmpv4Header { unsafe { self.header.as_ref() } @@ -135,7 +135,7 @@ impl Icmpv4 { /// Returns an error if the message type in the packet header does not /// match the assigned message type for `T`. #[inline] - pub fn downcast(self) -> Result { + pub fn downcast>(self) -> Result { ensure!( self.msg_type() == T::msg_type(), anyhow!("the ICMPv4 packet is not {}.", T::msg_type()) @@ -145,7 +145,7 @@ impl Icmpv4 { } } -impl fmt::Debug for Icmpv4 { +impl fmt::Debug for Icmpv4 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("icmpv4") .field("type", &format!("{}", self.msg_type())) @@ -158,9 +158,8 @@ impl fmt::Debug for Icmpv4 { } } -impl Packet for Icmpv4 { - /// The preceding type for ICMPv4 packet must be IPv4. - type Envelope = Ipv4; +impl Packet for Icmpv4 { + type Envelope = E; #[inline] fn envelope(&self) -> &Self::Envelope { @@ -204,7 +203,7 @@ impl Packet for Icmpv4 { #[inline] fn try_parse(envelope: Self::Envelope, _internal: Internal) -> Result { ensure!( - envelope.protocol() == ProtocolNumbers::Icmpv4, + envelope.next_protocol() == ProtocolNumbers::Icmpv4, anyhow!("not an ICMPv4 packet.") ); @@ -336,17 +335,20 @@ pub struct Icmpv4Header { /// [`Packet`]: Packet /// [`Icmpv4Packet`]: Icmpv4Packet pub trait Icmpv4Message { + /// The preceding packet type that encapsulates this message. + type Envelope: Ipv4Packet; + /// Returns the assigned message type. fn msg_type() -> Icmpv4Type; /// Returns a reference to the generic ICMPv4 packet. - fn icmp(&self) -> &Icmpv4; + fn icmp(&self) -> &Icmpv4; /// Returns a mutable reference to the generic ICMPv4 packet. - fn icmp_mut(&mut self) -> &mut Icmpv4; + fn icmp_mut(&mut self) -> &mut Icmpv4; /// Converts the message back to the generic ICMPv4 packet. - fn into_icmp(self) -> Icmpv4; + fn into_icmp(self) -> Icmpv4; /// Returns a copy of the message. /// @@ -368,7 +370,7 @@ pub trait Icmpv4Message { /// /// [`Icmpv4::downcast`]: Icmpv4::downcast /// [`msg_type`]: Icmpv4::msg_type - fn try_parse(icmp: Icmpv4, internal: Internal) -> Result + fn try_parse(icmp: Icmpv4, internal: Internal) -> Result where Self: Sized; @@ -386,7 +388,7 @@ pub trait Icmpv4Message { /// /// [`msg_type`]: Icmpv4::msg_type /// [`Packet::push`]: Packet::push - fn try_push(icmp: Icmpv4, internal: Internal) -> Result + fn try_push(icmp: Icmpv4, internal: Internal) -> Result where Self: Sized; @@ -447,7 +449,7 @@ pub trait Icmpv4Packet { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_UDP_PACKET}; @@ -460,16 +462,16 @@ mod tests { fn parse_icmpv4_packet() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let icmpv4 = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let icmp4 = ip4.parse::>().unwrap(); // parses the generic header - assert_eq!(Icmpv4Types::EchoRequest, icmpv4.msg_type()); - assert_eq!(0, icmpv4.code()); - assert_eq!(0x2a5c, icmpv4.checksum()); + assert_eq!(Icmpv4Types::EchoRequest, icmp4.msg_type()); + assert_eq!(0, icmp4.code()); + assert_eq!(0x2a5c, icmp4.checksum()); // downcasts to specific message - let echo = icmpv4.downcast::().unwrap(); + let echo = icmp4.downcast::>().unwrap(); assert_eq!(Icmpv4Types::EchoRequest, echo.msg_type()); assert_eq!(0, echo.code()); assert_eq!(0x2a5c, echo.checksum()); @@ -477,48 +479,48 @@ mod tests { assert_eq!(0x2100, echo.seq_no()); // also can one-step parse - let ipv4 = echo.deparse(); - assert!(ipv4.parse::().is_ok()); + let ip4 = echo.deparse(); + assert!(ip4.parse::>().is_ok()); } #[capsule::test] fn parse_wrong_icmpv4_type() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let icmpv4 = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let icmp4 = ip4.parse::>().unwrap(); - assert!(icmpv4.downcast::().is_err()); + assert!(icmp4.downcast::>().is_err()); } #[capsule::test] fn parse_non_icmpv4_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); - assert!(ipv4.parse::().is_err()); + assert!(ip4.parse::>().is_err()); } #[capsule::test] fn compute_checksum() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let mut icmpv4 = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut icmp4 = ip4.parse::>().unwrap(); - let expected = icmpv4.checksum(); + let expected = icmp4.checksum(); // no payload change but force a checksum recompute anyway - icmpv4.reconcile_all(); - assert_eq!(expected, icmpv4.checksum()); + icmp4.reconcile_all(); + assert_eq!(expected, icmp4.checksum()); } #[capsule::test] fn push_icmpv4_header_without_body() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); - assert!(ipv4.push::().is_err()); + assert!(ip4.push::>().is_err()); } } diff --git a/core/src/packets/icmp/v4/redirect.rs b/core/src/packets/icmp/v4/redirect.rs index 9177800..76d8f4f 100644 --- a/core/src/packets/icmp/v4/redirect.rs +++ b/core/src/packets/icmp/v4/redirect.rs @@ -17,6 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; +use crate::packets::ip::v4::Ipv4Packet; use crate::packets::ip::v4::IPV4_MIN_MTU; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -43,12 +44,12 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct Redirect { - icmp: Icmpv4, +pub struct Redirect { + icmp: Icmpv4, body: NonNull, } -impl Redirect { +impl Redirect { #[inline] fn body(&self) -> &RedirectBody { unsafe { self.body.as_ref() } @@ -96,7 +97,7 @@ impl Redirect { } } -impl fmt::Debug for Redirect { +impl fmt::Debug for Redirect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Redirect") .field("type", &format!("{}", self.msg_type())) @@ -110,24 +111,26 @@ impl fmt::Debug for Redirect { } } -impl Icmpv4Message for Redirect { +impl Icmpv4Message for Redirect { + type Envelope = E; + #[inline] fn msg_type() -> Icmpv4Type { Icmpv4Types::Redirect } #[inline] - fn icmp(&self) -> &Icmpv4 { + fn icmp(&self) -> &Icmpv4 { &self.icmp } #[inline] - fn icmp_mut(&mut self) -> &mut Icmpv4 { + fn icmp_mut(&mut self) -> &mut Icmpv4 { &mut self.icmp } #[inline] - fn into_icmp(self) -> Icmpv4 { + fn into_icmp(self) -> Icmpv4 { self.icmp } @@ -146,7 +149,7 @@ impl Icmpv4Message for Redirect { /// Returns an error if the payload does not have sufficient data for /// the redirect message body. #[inline] - fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { + fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { let mbuf = icmp.mbuf(); let offset = icmp.payload_offset(); let body = mbuf.read_data(offset)?; @@ -161,7 +164,7 @@ impl Icmpv4Message for Redirect { /// /// Returns an error if the buffer does not have enough free space. #[inline] - fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { + fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { let offset = icmp.payload_offset(); let mbuf = icmp.mbuf_mut(); @@ -216,7 +219,7 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; @@ -229,10 +232,10 @@ mod tests { fn push_and_set_redirect() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let tcp_len = ipv4.payload_len(); + let ip4 = ethernet.parse::().unwrap(); + let tcp_len = ip4.payload_len(); - let mut redirect = ipv4.push::().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of() + tcp_len, redirect.payload_len()); @@ -256,8 +259,8 @@ mod tests { // starts with buffer larger than min MTU of 68 bytes. let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut redirect = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert!(redirect.data_len() > IPV4_MIN_MTU); redirect.reconcile_all(); @@ -269,8 +272,8 @@ mod tests { // starts with buffer smaller than min MTU of 68 bytes. let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut redirect = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert!(redirect.data_len() < IPV4_MIN_MTU); redirect.reconcile_all(); diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs index 8a5e697..5391706 100644 --- a/core/src/packets/icmp/v4/time_exceeded.rs +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -17,6 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; +use crate::packets::ip::v4::Ipv4Packet; use crate::packets::ip::v4::IPV4_MIN_MTU; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; @@ -39,12 +40,12 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct TimeExceeded { - icmp: Icmpv4, +pub struct TimeExceeded { + icmp: Icmpv4, body: NonNull, } -impl TimeExceeded { +impl TimeExceeded { /// Returns the offset where the data field in the message body starts. #[inline] fn data_offset(&self) -> usize { @@ -72,7 +73,7 @@ impl TimeExceeded { } } -impl fmt::Debug for TimeExceeded { +impl fmt::Debug for TimeExceeded { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TimeExceeded") .field("type", &format!("{}", self.msg_type())) @@ -85,24 +86,26 @@ impl fmt::Debug for TimeExceeded { } } -impl Icmpv4Message for TimeExceeded { +impl Icmpv4Message for TimeExceeded { + type Envelope = E; + #[inline] fn msg_type() -> Icmpv4Type { Icmpv4Types::TimeExceeded } #[inline] - fn icmp(&self) -> &Icmpv4 { + fn icmp(&self) -> &Icmpv4 { &self.icmp } #[inline] - fn icmp_mut(&mut self) -> &mut Icmpv4 { + fn icmp_mut(&mut self) -> &mut Icmpv4 { &mut self.icmp } #[inline] - fn into_icmp(self) -> Icmpv4 { + fn into_icmp(self) -> Icmpv4 { self.icmp } @@ -121,7 +124,7 @@ impl Icmpv4Message for TimeExceeded { /// Returns an error if the payload does not have sufficient data for /// the time exceeded message body. #[inline] - fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { + fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result { let mbuf = icmp.mbuf(); let offset = icmp.payload_offset(); let body = mbuf.read_data(offset)?; @@ -136,7 +139,7 @@ impl Icmpv4Message for TimeExceeded { /// /// Returns an error if the buffer does not have enough free space. #[inline] - fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { + fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result { let offset = icmp.payload_offset(); let mbuf = icmp.mbuf_mut(); @@ -180,7 +183,7 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; @@ -193,10 +196,10 @@ mod tests { fn push_and_set_time_exceeded() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let tcp_len = ipv4.payload_len(); + let ip4 = ethernet.parse::().unwrap(); + let tcp_len = ip4.payload_len(); - let mut exceeded = ipv4.push::().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -219,8 +222,8 @@ mod tests { // starts with a buffer with a message body larger than min MTU. let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut exceeded = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert!(exceeded.data_len() > IPV4_MIN_MTU); exceeded.reconcile_all(); @@ -232,8 +235,8 @@ mod tests { // starts with a buffer with a message body smaller than min MTU. let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let mut exceeded = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert!(exceeded.data_len() < IPV4_MIN_MTU); exceeded.reconcile_all(); diff --git a/core/src/packets/ip/v4.rs b/core/src/packets/ip/v4.rs index e2c6f1e..e7cac00 100644 --- a/core/src/packets/ip/v4.rs +++ b/core/src/packets/ip/v4.rs @@ -23,7 +23,7 @@ use crate::packets::checksum::{self, PseudoHeader}; use crate::packets::ethernet::{EtherTypes, Ethernet}; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::u16be; -use crate::packets::{Internal, Packet, SizeOf}; +use crate::packets::{Datalink, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv4Addr}; @@ -143,13 +143,13 @@ const FLAGS_MF: u16be = u16be(u16::to_be(0b0010_0000_0000_0000)); /// [IETF RFC 791]: https://tools.ietf.org/html/rfc791#section-3.1 /// [IETF RFC 2474]: https://tools.ietf.org/html/rfc2474 /// [IETF RFC 3168]: https://tools.ietf.org/html/rfc3168 -pub struct Ipv4 { - envelope: Ethernet, +pub struct Ipv4 { + envelope: E, header: NonNull, offset: usize, } -impl Ipv4 { +impl Ipv4 { #[inline] fn header(&self) -> &Ipv4Header { unsafe { self.header.as_ref() } @@ -358,7 +358,7 @@ impl Ipv4 { } } -impl fmt::Debug for Ipv4 { +impl fmt::Debug for Ipv4 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ipv4") .field("src", &format!("{}", self.src())) @@ -381,9 +381,8 @@ impl fmt::Debug for Ipv4 { } } -impl Packet for Ipv4 { - /// The preceding type for an IPv4 packet must be Ethernet. - type Envelope = Ethernet; +impl Packet for Ipv4 { + type Envelope = E; #[inline] fn envelope(&self) -> &Self::Envelope { @@ -427,7 +426,7 @@ impl Packet for Ipv4 { #[inline] fn try_parse(envelope: Self::Envelope, _internal: Internal) -> Result { ensure!( - envelope.ether_type() == EtherTypes::Ipv4, + envelope.protocol_type() == EtherTypes::Ipv4, anyhow!("not an IPv4 packet.") ); @@ -460,7 +459,7 @@ impl Packet for Ipv4 { mbuf.extend(offset, Ipv4Header::size_of())?; let header = mbuf.write_data(offset, &Ipv4Header::default())?; - envelope.set_ether_type(EtherTypes::Ipv4); + envelope.set_protocol_type(EtherTypes::Ipv4); Ok(Ipv4 { envelope, @@ -490,7 +489,7 @@ impl Packet for Ipv4 { } } -impl IpPacket for Ipv4 { +impl IpPacket for Ipv4 { #[inline] fn next_protocol(&self) -> ProtocolNumber { self.protocol() @@ -573,6 +572,16 @@ impl IpPacket for Ipv4 { } } +impl Ipv4Packet for Ipv4 {} + +/// A marker trait for Ipv4 packet. +/// +/// Used as a trait bound for packets succeding Ipv4. +pub trait Ipv4Packet: IpPacket {} + +/// A type alias for an Ethernet IPv4 packet. +pub type Ip4 = Ipv4; + /// IPv4 header. /// /// The header only include the fixed portion of the IPv4 header. @@ -625,22 +634,22 @@ mod tests { fn parse_ipv4_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - - assert_eq!(4, ipv4.version()); - assert_eq!(5, ipv4.ihl()); - assert_eq!(38, ipv4.total_length()); - assert_eq!(43849, ipv4.identification()); - assert_eq!(true, ipv4.dont_fragment()); - assert_eq!(false, ipv4.more_fragments()); - assert_eq!(0, ipv4.fragment_offset()); - assert_eq!(0, ipv4.dscp()); - assert_eq!(0, ipv4.ecn()); - assert_eq!(255, ipv4.ttl()); - assert_eq!(ProtocolNumbers::Udp, ipv4.protocol()); - assert_eq!(0xf700, ipv4.checksum()); - assert_eq!("139.133.217.110", ipv4.src().to_string()); - assert_eq!("139.133.233.2", ipv4.dst().to_string()); + let ip4 = ethernet.parse::().unwrap(); + + assert_eq!(4, ip4.version()); + assert_eq!(5, ip4.ihl()); + assert_eq!(38, ip4.total_length()); + assert_eq!(43849, ip4.identification()); + assert_eq!(true, ip4.dont_fragment()); + assert_eq!(false, ip4.more_fragments()); + assert_eq!(0, ip4.fragment_offset()); + assert_eq!(0, ip4.dscp()); + assert_eq!(0, ip4.ecn()); + assert_eq!(255, ip4.ttl()); + assert_eq!(ProtocolNumbers::Udp, ip4.protocol()); + assert_eq!(0xf700, ip4.checksum()); + assert_eq!("139.133.217.110", ip4.src().to_string()); + assert_eq!("139.133.233.2", ip4.dst().to_string()); } #[capsule::test] @@ -648,55 +657,55 @@ mod tests { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - assert!(ethernet.parse::().is_err()); + assert!(ethernet.parse::().is_err()); } #[capsule::test] fn parse_ipv4_setter_checks() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ipv4 = ethernet.parse::().unwrap(); + let mut ip4 = ethernet.parse::().unwrap(); // Fields - ipv4.set_ihl(ipv4.ihl()); + ip4.set_ihl(ip4.ihl()); // Flags - assert_eq!(true, ipv4.dont_fragment()); - assert_eq!(false, ipv4.more_fragments()); + assert_eq!(true, ip4.dont_fragment()); + assert_eq!(false, ip4.more_fragments()); - ipv4.unset_dont_fragment(); - assert_eq!(false, ipv4.dont_fragment()); - ipv4.set_dont_fragment(); - assert_eq!(true, ipv4.dont_fragment()); + ip4.unset_dont_fragment(); + assert_eq!(false, ip4.dont_fragment()); + ip4.set_dont_fragment(); + assert_eq!(true, ip4.dont_fragment()); - ipv4.set_more_fragments(); - assert_eq!(true, ipv4.more_fragments()); - ipv4.unset_more_fragments(); - assert_eq!(false, ipv4.more_fragments()); + ip4.set_more_fragments(); + assert_eq!(true, ip4.more_fragments()); + ip4.unset_more_fragments(); + assert_eq!(false, ip4.more_fragments()); - ipv4.set_fragment_offset(5); - assert_eq!(5, ipv4.fragment_offset()); + ip4.set_fragment_offset(5); + assert_eq!(5, ip4.fragment_offset()); // DSCP & ECN - assert_eq!(0, ipv4.dscp()); - assert_eq!(0, ipv4.ecn()); - ipv4.set_dscp(10); - ipv4.set_ecn(3); - assert_eq!(10, ipv4.dscp()); - assert_eq!(3, ipv4.ecn()); + assert_eq!(0, ip4.dscp()); + assert_eq!(0, ip4.ecn()); + ip4.set_dscp(10); + ip4.set_ecn(3); + assert_eq!(10, ip4.dscp()); + assert_eq!(3, ip4.ecn()); } #[capsule::test] fn push_ipv4_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); - assert_eq!(4, ipv4.version()); - assert_eq!(Ipv4Header::size_of(), ipv4.len()); + assert_eq!(4, ip4.version()); + assert_eq!(Ipv4Header::size_of(), ip4.len()); // make sure ether type is fixed - assert_eq!(EtherTypes::Ipv4, ipv4.envelope().ether_type()); + assert_eq!(EtherTypes::Ipv4, ip4.envelope().ether_type()); } #[capsule::test] @@ -706,25 +715,25 @@ mod tests { let _ = packet.extend(0, 2000); let ethernet = packet.push::().unwrap(); - let mut ipv4 = ethernet.push::().unwrap(); + let mut ip4 = ethernet.push::().unwrap(); // can't truncate to less than minimum MTU. - assert!(ipv4.truncate(60).is_err()); + assert!(ip4.truncate(60).is_err()); - assert!(ipv4.len() > 1000); - let _ = ipv4.truncate(1000); - assert_eq!(1000, ipv4.len()); + assert!(ip4.len() > 1000); + let _ = ip4.truncate(1000); + assert_eq!(1000, ip4.len()); } #[capsule::test] fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ipv4 = ethernet.parse::().unwrap(); + let mut ip4 = ethernet.parse::().unwrap(); - let expected = ipv4.checksum(); + let expected = ip4.checksum(); // no payload change but force a checksum recompute anyway - ipv4.reconcile_all(); - assert_eq!(expected, ipv4.checksum()); + ip4.reconcile_all(); + assert_eq!(expected, ip4.checksum()); } } diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index 32cc00e..12bd6be 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -303,22 +303,22 @@ pub trait Packet { } /// A trait datalink layer protocol can implement. -/// +/// /// A datalink protocol must implement this trait if it needs to encapsulate /// network protocols like IP or ARP. Otherwise, it should not implement this /// trait. pub trait Datalink: Packet { /// Gets the encapsulated packet type. - /// - /// Returns the ethernet protocol type codes because ethernet is the most + /// + /// Returns the ethernet protocol type codes because ethernet is the most /// ubiquitous datalink protocol. Other datalink like InfiniBand adopted /// the ethernet type codes. When implementing a datalink with its own /// type codes, a translation from ether type is needed. fn protocol_type(&self) -> EtherType; /// Sets the protocol type of the encapsulated packet. - /// - /// Uses the ethernet protocol type codes because ethernet is the most + /// + /// Uses the ethernet protocol type codes because ethernet is the most /// ubiquitous datalink protocol. Other datalink like InfiniBand adopted /// the ethernet type codes. When implementing a datalink with its own /// type codes, a translation from ether type is needed. @@ -380,7 +380,7 @@ mod tests { use super::*; use crate::net::MacAddr; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; + use crate::packets::ip::v4::Ip4; use crate::packets::udp::Udp4; use crate::testils::byte_arrays::IPV4_UDP_PACKET; @@ -390,8 +390,8 @@ mod tests { let len = packet.data_len(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let udp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let udp = ip4.parse::().unwrap(); let reset = udp.reset(); assert_eq!(len, reset.data_len()); @@ -403,39 +403,40 @@ mod tests { let ethernet = packet.peek::().unwrap(); assert_eq!(MacAddr::new(0, 0, 0, 0, 0, 2), ethernet.src()); - let v4 = ethernet.peek::().unwrap(); - assert_eq!(255, v4.ttl()); - let udp = v4.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); + assert_eq!(255, ip4.ttl()); + let udp = ip4.peek::().unwrap(); assert_eq!(39376, udp.src_port()); } #[capsule::test] - fn peek_back_via_envelope() { + fn parse_and_deparse_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let v4 = ethernet.parse::().unwrap(); - let udp = v4.parse::().unwrap(); - let mut v4_2 = udp.deparse(); - v4_2.set_ttl(25); - let udp_2 = v4_2.parse::().unwrap(); - let v4_4 = udp_2.envelope(); - assert_eq!(v4_4.ttl(), 25); + let mut ip4 = ethernet.parse::().unwrap(); + ip4.set_ttl(25); + + let udp = ip4.parse::().unwrap(); + assert_eq!(udp.envelope().ttl(), 25); + + let ip4_2 = udp.deparse(); + assert_eq!(ip4_2.ttl(), 25); } #[capsule::test] fn remove_header_and_payload() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let v4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); - let mut udp = v4.parse::().unwrap(); + let mut udp = ip4.parse::().unwrap(); assert!(udp.payload_len() > 0); let _ = udp.remove_payload(); assert_eq!(0, udp.payload_len()); - let v4 = udp.remove().unwrap(); - assert_eq!(0, v4.payload_len()); + let ip4 = udp.remove().unwrap(); + assert_eq!(0, ip4.payload_len()); } /// Demonstrates that `Packet::peek` behaves as an immutable borrow on @@ -456,9 +457,9 @@ mod tests { fn cannot_mutate_packet_while_peeking_into_payload() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let mut ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); ethernet.set_src(MacAddr::UNSPECIFIED); - assert!(ipv4.payload_len() > 0); + assert!(ip4.payload_len() > 0); } /// Demonstrates that `Packet::peek` returns an immutable packet wrapper. diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index b7d11e9..6e616ab 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -19,7 +19,7 @@ //! Transmission Control Protocol. use crate::ensure; -use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::{u16be, u32be}; @@ -623,8 +623,8 @@ impl Packet for Tcp { } } -/// A type alias for an IPv4 TCP packet. -pub type Tcp4 = Tcp; +/// A type alias for an Ethernet IPv4 TCP packet. +pub type Tcp4 = Tcp; /// A type alias for an IPv6 TCP packet. pub type Tcp6 = Tcp; @@ -667,6 +667,7 @@ impl Default for TcpHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; + use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::SegmentRouting; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET, SR_TCP_PACKET}; @@ -681,8 +682,8 @@ mod tests { fn parse_tcp_packet() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let tcp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let tcp = ip4.parse::().unwrap(); assert_eq!(36869, tcp.src_port()); assert_eq!(23, tcp.dst_port()); @@ -707,17 +708,17 @@ mod tests { fn parse_non_tcp_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); - assert!(ipv4.parse::().is_err()); + assert!(ip4.parse::().is_err()); } #[capsule::test] fn tcp_flow_v4() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let tcp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let tcp = ip4.parse::().unwrap(); let flow = tcp.flow(); assert_eq!("139.133.217.110", flow.src_ip().to_string()); @@ -747,8 +748,8 @@ mod tests { fn set_src_dst_ip() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let mut tcp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut tcp = ip4.parse::().unwrap(); let old_checksum = tcp.checksum(); let new_ip = Ipv4Addr::new(10, 0, 0, 0); @@ -770,8 +771,8 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let mut tcp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut tcp = ip4.parse::().unwrap(); let expected = tcp.checksum(); // no payload change but force a checksum recompute anyway @@ -783,8 +784,8 @@ mod tests { fn push_tcp_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let tcp = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let tcp = ip4.push::().unwrap(); assert_eq!(TcpHeader::size_of(), tcp.len()); assert_eq!(5, tcp.data_offset()); diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 541fd1d..0068a84 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -19,7 +19,7 @@ //! User Datagram Protocol. use crate::ensure; -use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::u16be; @@ -369,8 +369,8 @@ impl Packet for Udp { } } -/// A type alias for an IPv4 UDP packet. -pub type Udp4 = Udp; +/// A type alias for an Ethernet IPv4 UDP packet. +pub type Udp4 = Udp; /// A type alias for an IPv6 UDP packet. pub type Udp6 = Udp; @@ -389,6 +389,7 @@ struct UdpHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; + use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -402,8 +403,8 @@ mod tests { fn parse_udp_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let udp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let udp = ip4.parse::().unwrap(); assert_eq!(39376, udp.src_port()); assert_eq!(1087, udp.dst_port()); @@ -416,17 +417,17 @@ mod tests { fn parse_non_udp_packet() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); - assert!(ipv4.parse::().is_err()); + assert!(ip4.parse::().is_err()); } #[capsule::test] fn udp_flow_v4() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let udp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let udp = ip4.parse::().unwrap(); let flow = udp.flow(); assert_eq!("139.133.217.110", flow.src_ip().to_string()); @@ -440,8 +441,8 @@ mod tests { fn set_src_dst_ip() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let mut udp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut udp = ip4.parse::().unwrap(); let old_checksum = udp.checksum(); let new_ip = Ipv4Addr::new(10, 0, 0, 0); @@ -463,8 +464,8 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - let mut udp = ipv4.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut udp = ip4.parse::().unwrap(); let expected = udp.checksum(); // no payload change but force a checksum recompute anyway @@ -476,8 +477,8 @@ mod tests { fn push_udp_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - let udp = ipv4.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let udp = ip4.push::().unwrap(); assert_eq!(UdpHeader::size_of(), udp.len()); diff --git a/core/src/testils/packet.rs b/core/src/testils/packet.rs index c0e4d86..24d2882 100644 --- a/core/src/testils/packet.rs +++ b/core/src/testils/packet.rs @@ -17,7 +17,7 @@ */ use crate::packets::ethernet::Ethernet; -use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::{Ipv6, SegmentRouting}; use crate::packets::tcp::{Tcp, Tcp4, Tcp6}; use crate::packets::udp::{Udp4, Udp6}; @@ -37,8 +37,8 @@ pub trait PacketExt: Packet + Sized { } /// Converts the packet into an IPv4 packet. - fn into_v4(self) -> Ipv4 { - self.into_eth().parse::().unwrap() + fn into_v4(self) -> Ip4 { + self.into_eth().parse::().unwrap() } /// Converts the packet into a TCP packet inside IPv4. diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index 12f0707..61c0b49 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -20,7 +20,7 @@ use crate::net::MacAddr; use crate::packets::ethernet::{EtherType, EtherTypes, Ethernet}; -use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::{Ipv6, Ipv6Packet, SegmentRouting}; use crate::packets::ip::{Flow, IpPacket, ProtocolNumber, ProtocolNumbers}; use crate::packets::tcp::Tcp; @@ -219,7 +219,7 @@ fn ethernet(ether_type: EtherType, map: &StrategyMap) -> impl Strategy impl Strategy { +fn ipv4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy { ( ethernet(EtherTypes::Ipv4, map), map.ipv4_addr(&field::ipv4_src), @@ -245,7 +245,7 @@ fn ipv4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy().unwrap(); + let mut packet = packet.push::().unwrap(); packet.set_src(src); packet.set_dst(dst); packet.set_dscp(dscp); diff --git a/macros/src/derive_packet.rs b/macros/src/derive_packet.rs index 5e7de69..7b6829b 100644 --- a/macros/src/derive_packet.rs +++ b/macros/src/derive_packet.rs @@ -122,7 +122,7 @@ pub fn gen_icmpv4(input: syn::DeriveInput) -> TokenStream { let name = input.ident; let expanded = quote! { - impl ::capsule::packets::icmp::v4::Icmpv4Packet for #name { + impl ::capsule::packets::icmp::v4::Icmpv4Packet for #name { #[inline] fn msg_type(&self) -> ::capsule::packets::icmp::v4::Icmpv4Type { self.icmp().msg_type() @@ -144,8 +144,8 @@ pub fn gen_icmpv4(input: syn::DeriveInput) -> TokenStream { } } - impl ::capsule::packets::Packet for #name { - type Envelope = ::capsule::packets::ip::v4::Ipv4; + impl ::capsule::packets::Packet for #name { + type Envelope = E; #[inline] fn envelope(&self) -> &Self::Envelope { @@ -174,7 +174,7 @@ pub fn gen_icmpv4(input: syn::DeriveInput) -> TokenStream { #[inline] fn try_parse(envelope: Self::Envelope, _internal: ::capsule::packets::Internal) -> ::anyhow::Result { - envelope.parse::<::capsule::packets::icmp::v4::Icmpv4>()?.downcast::<#name>() + envelope.parse::<::capsule::packets::icmp::v4::Icmpv4>()?.downcast::<#name>() } #[inline] @@ -195,11 +195,11 @@ pub fn gen_icmpv4(input: syn::DeriveInput) -> TokenStream { offset, }; - icmp.header_mut().msg_type = <#name as Icmpv4Message>::msg_type().0; + icmp.header_mut().msg_type = <#name as Icmpv4Message>::msg_type().0; icmp.envelope_mut() .set_next_protocol(ProtocolNumbers::Icmpv4); - <#name as Icmpv4Message>::try_push(icmp, internal) + <#name as Icmpv4Message>::try_push(icmp, internal) } #[inline] From fda00b8cb9387e51c68d949d56ba9a6d94a6e46b Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Fri, 7 Jan 2022 23:09:50 -0500 Subject: [PATCH 15/18] add datalink trait for ipv6 --- core/src/packets/icmp/v6/echo_reply.rs | 6 +- core/src/packets/icmp/v6/echo_request.rs | 6 +- core/src/packets/icmp/v6/mod.rs | 42 +++---- core/src/packets/icmp/v6/ndp/mod.rs | 50 ++++---- .../packets/icmp/v6/ndp/neighbor_advert.rs | 6 +- .../packets/icmp/v6/ndp/neighbor_solicit.rs | 6 +- .../icmp/v6/ndp/options/link_layer_addr.rs | 10 +- core/src/packets/icmp/v6/ndp/options/mtu.rs | 10 +- .../icmp/v6/ndp/options/prefix_info.rs | 10 +- .../packets/icmp/v6/ndp/options/redirected.rs | 6 +- core/src/packets/icmp/v6/ndp/redirect.rs | 10 +- core/src/packets/icmp/v6/ndp/router_advert.rs | 6 +- .../src/packets/icmp/v6/ndp/router_solicit.rs | 6 +- core/src/packets/icmp/v6/time_exceeded.rs | 12 +- core/src/packets/icmp/v6/too_big.rs | 12 +- core/src/packets/icmp/v6/unreachable.rs | 12 +- core/src/packets/ip/v6/fragment.rs | 32 +++--- core/src/packets/ip/v6/mod.rs | 88 +++++++------- core/src/packets/ip/v6/srh.rs | 52 ++++----- core/src/packets/tcp.rs | 12 +- core/src/packets/udp.rs | 6 +- core/src/testils/packet.rs | 32 +++--- core/src/testils/proptest/strategy.rs | 108 +++++++++--------- core/src/testils/rvg.rs | 8 +- 24 files changed, 275 insertions(+), 273 deletions(-) diff --git a/core/src/packets/icmp/v6/echo_reply.rs b/core/src/packets/icmp/v6/echo_reply.rs index 02d2d43..9664106 100644 --- a/core/src/packets/icmp/v6/echo_reply.rs +++ b/core/src/packets/icmp/v6/echo_reply.rs @@ -218,7 +218,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -230,8 +230,8 @@ mod tests { fn push_and_set_echo_reply() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut echo = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut echo = ip6.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/echo_request.rs b/core/src/packets/icmp/v6/echo_request.rs index d288521..08e357e 100644 --- a/core/src/packets/icmp/v6/echo_request.rs +++ b/core/src/packets/icmp/v6/echo_request.rs @@ -219,7 +219,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -231,8 +231,8 @@ mod tests { fn push_and_set_echo_request() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut echo = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut echo = ip6.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index bdd3363..03a1b16 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -500,7 +500,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::RouterAdvertisement; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV6_PACKET, IPV6_TCP_PACKET, ROUTER_ADVERT_PACKET}; @@ -513,16 +513,16 @@ mod tests { fn parse_icmpv6_packet() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let icmpv6 = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let icmp6 = ip6.parse::>().unwrap(); // parses the generic header - assert_eq!(Icmpv6Types::RouterAdvertisement, icmpv6.msg_type()); - assert_eq!(0, icmpv6.code()); - assert_eq!(0xf50c, icmpv6.checksum()); + assert_eq!(Icmpv6Types::RouterAdvertisement, icmp6.msg_type()); + assert_eq!(0, icmp6.code()); + assert_eq!(0xf50c, icmp6.checksum()); // downcasts to specific message - let advert = icmpv6.downcast::>().unwrap(); + let advert = icmp6.downcast::>().unwrap(); assert_eq!(Icmpv6Types::RouterAdvertisement, advert.msg_type()); assert_eq!(0, advert.code()); assert_eq!(0xf50c, advert.checksum()); @@ -534,48 +534,48 @@ mod tests { assert_eq!(0, advert.retrans_timer()); // also can one-step parse - let ipv6 = advert.deparse(); - assert!(ipv6.parse::>().is_ok()); + let ip6 = advert.deparse(); + assert!(ip6.parse::>().is_ok()); } #[capsule::test] fn parse_wrong_icmpv6_type() { let packet = Mbuf::from_bytes(&ICMPV6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let icmpv6 = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let icmp6 = ip6.parse::>().unwrap(); - assert!(icmpv6.downcast::>().is_err()); + assert!(icmp6.downcast::>().is_err()); } #[capsule::test] fn parse_non_icmpv6_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ipv6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn compute_checksum() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut icmpv6 = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut icmp6 = ip6.parse::>().unwrap(); - let expected = icmpv6.checksum(); + let expected = icmp6.checksum(); // no payload change but force a checksum recompute anyway - icmpv6.reconcile_all(); - assert_eq!(expected, icmpv6.checksum()); + icmp6.reconcile_all(); + assert_eq!(expected, icmp6.checksum()); } #[capsule::test] fn push_icmpv6_header_without_body() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); - assert!(ipv6.push::>().is_err()); + assert!(ip6.push::>().is_err()); } } diff --git a/core/src/packets/icmp/v6/ndp/mod.rs b/core/src/packets/icmp/v6/ndp/mod.rs index 80ad51b..01977c5 100644 --- a/core/src/packets/icmp/v6/ndp/mod.rs +++ b/core/src/packets/icmp/v6/ndp/mod.rs @@ -552,15 +552,15 @@ pub trait NdpOption<'a> { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[capsule::test] fn iterate_immutable_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); let mut prefix = false; let mut mtu = false; @@ -588,8 +588,8 @@ mod tests { fn invalid_ndp_option_length() { let packet = Mbuf::from_bytes(&INVALID_OPTION_LENGTH).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); assert!(advert.options_iter().next().is_err()); } @@ -598,8 +598,8 @@ mod tests { fn downcast_immutable_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.peek::().unwrap(); - let ipv6 = ethernet.peek::().unwrap(); - let advert = ipv6.peek::>().unwrap(); + let ip6 = ethernet.peek::().unwrap(); + let advert = ip6.peek::>().unwrap(); let mut iter = advert.options_iter(); @@ -616,8 +616,8 @@ mod tests { fn iterate_mutable_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut prefix = false; let mut mtu = false; @@ -646,8 +646,8 @@ mod tests { fn downcast_mutable_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -664,8 +664,8 @@ mod tests { fn modify_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -683,8 +683,8 @@ mod tests { fn prepend_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut target = options.prepend::>().unwrap(); target.set_option_type_target(); @@ -699,8 +699,8 @@ mod tests { fn append_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut target = options.append::>().unwrap(); target.set_option_type_target(); @@ -723,8 +723,8 @@ mod tests { fn retain_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let _ = options.retain(|option| option.downcast::>().is_ok()); @@ -754,8 +754,8 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut iter = advert.options_iter(); advert.set_code(0); @@ -775,8 +775,8 @@ mod tests { fn cannot_mutate_immutable_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); let mut iter = advert.options_iter(); let mut option = iter.next().unwrap().unwrap(); @@ -802,8 +802,8 @@ mod tests { fn cannot_mutate_options_while_iterating_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs index 9c532fa..a178d16 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs @@ -271,7 +271,7 @@ impl Default for NeighborAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -283,8 +283,8 @@ mod tests { fn push_and_set_neighbor_advertisement() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut advert = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(NeighborAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs index 18f09f5..d27bdd2 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs @@ -186,7 +186,7 @@ impl Default for NeighborSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -198,8 +198,8 @@ mod tests { fn push_and_set_neighbor_solicitation() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut solicit = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut solicit = ip6.push::>().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(NeighborSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs index c196745..0b689e7 100644 --- a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs +++ b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs @@ -192,7 +192,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -205,8 +205,8 @@ mod tests { fn parse_link_layer_address() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -229,8 +229,8 @@ mod tests { fn push_and_set_link_layer_address() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut advert = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut lla = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/mtu.rs b/core/src/packets/icmp/v6/ndp/options/mtu.rs index dc533af..335cee0 100644 --- a/core/src/packets/icmp/v6/ndp/options/mtu.rs +++ b/core/src/packets/icmp/v6/ndp/options/mtu.rs @@ -172,7 +172,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -185,8 +185,8 @@ mod tests { fn parse_mtu() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -209,8 +209,8 @@ mod tests { fn push_and_set_mtu() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut advert = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut mtu = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs index d721ca5..0ab6068 100644 --- a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs +++ b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs @@ -310,7 +310,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -323,8 +323,8 @@ mod tests { fn parse_prefix_information() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut advert = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -355,8 +355,8 @@ mod tests { fn push_and_set_prefix_information() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut advert = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut prefix = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/redirected.rs b/core/src/packets/icmp/v6/ndp/options/redirected.rs index 6ba9d19..e755fbe 100644 --- a/core/src/packets/icmp/v6/ndp/options/redirected.rs +++ b/core/src/packets/icmp/v6/ndp/options/redirected.rs @@ -234,7 +234,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, Redirect}; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Packet; #[test] @@ -249,8 +249,8 @@ mod tests { let packet = Mbuf::from_bytes(&data).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut redirect = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); let mut options = redirect.options_mut(); let mut header = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 1f3fe65..865a63d 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -242,7 +242,7 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -254,8 +254,8 @@ mod tests { fn push_and_set_redirect() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut redirect = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of(), redirect.payload_len()); @@ -276,8 +276,8 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut redirect = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); let mut options = redirect.options_mut(); let _ = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/router_advert.rs b/core/src/packets/icmp/v6/ndp/router_advert.rs index 4e1d129..a2d4904 100644 --- a/core/src/packets/icmp/v6/ndp/router_advert.rs +++ b/core/src/packets/icmp/v6/ndp/router_advert.rs @@ -302,7 +302,7 @@ struct RouterAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -314,8 +314,8 @@ mod tests { fn push_and_set_router_advertisement() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut advert = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(RouterAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/router_solicit.rs b/core/src/packets/icmp/v6/ndp/router_solicit.rs index bf122b0..8092ab6 100644 --- a/core/src/packets/icmp/v6/ndp/router_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/router_solicit.rs @@ -144,7 +144,7 @@ struct RouterSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; #[test] @@ -156,8 +156,8 @@ mod tests { fn push_and_set_router_solicitation() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut solicit = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut solicit = ip6.push::>().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(RouterSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index 57e572d..817d21f 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -164,7 +164,7 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -177,10 +177,10 @@ mod tests { fn push_and_set_time_exceeded() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let tcp_len = ipv6.payload_len(); + let ip6 = ethernet.parse::().unwrap(); + let tcp_len = ip6.payload_len(); - let mut exceeded = ipv6.push::>().unwrap(); + let mut exceeded = ip6.push::>().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -203,12 +203,12 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut exceeded = ipv6.push::>().unwrap(); + let mut exceeded = ip6.push::>().unwrap(); assert!(exceeded.mbuf().data_len() > max_len); exceeded.reconcile_all(); diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index 71d1cf5..32e2363 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -189,7 +189,7 @@ struct PacketTooBigBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -202,10 +202,10 @@ mod tests { fn push_and_set_packet_too_big() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let tcp_len = ipv6.payload_len(); + let ip6 = ethernet.parse::().unwrap(); + let tcp_len = ip6.payload_len(); - let mut too_big = ipv6.push::>().unwrap(); + let mut too_big = ip6.push::>().unwrap(); assert_eq!(4, too_big.header_len()); assert_eq!(PacketTooBigBody::size_of() + tcp_len, too_big.payload_len()); @@ -225,12 +225,12 @@ mod tests { // starts with a buffer. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut too_big = ipv6.push::>().unwrap(); + let mut too_big = ip6.push::>().unwrap(); assert!(too_big.mbuf().data_len() > max_len); too_big.reconcile_all(); diff --git a/core/src/packets/icmp/v6/unreachable.rs b/core/src/packets/icmp/v6/unreachable.rs index 827682e..1c44fe5 100644 --- a/core/src/packets/icmp/v6/unreachable.rs +++ b/core/src/packets/icmp/v6/unreachable.rs @@ -164,7 +164,7 @@ struct DestinationUnreachableBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -177,10 +177,10 @@ mod tests { fn push_and_set_destination_unreachable() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let tcp_len = ipv6.payload_len(); + let ip6 = ethernet.parse::().unwrap(); + let tcp_len = ip6.payload_len(); - let mut unreachable = ipv6.push::>().unwrap(); + let mut unreachable = ip6.push::>().unwrap(); assert_eq!(4, unreachable.header_len()); assert_eq!( @@ -203,12 +203,12 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut unreachable = ipv6.push::>().unwrap(); + let mut unreachable = ip6.push::>().unwrap(); assert!(unreachable.mbuf().data_len() > max_len); unreachable.reconcile_all(); diff --git a/core/src/packets/ip/v6/fragment.rs b/core/src/packets/ip/v6/fragment.rs index 7d523c4..6ee7080 100644 --- a/core/src/packets/ip/v6/fragment.rs +++ b/core/src/packets/ip/v6/fragment.rs @@ -330,7 +330,7 @@ struct FragmentHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, IPV6_TCP_PACKET}; @@ -343,8 +343,8 @@ mod tests { fn parse_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_FRAGMENT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let frag = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let frag = ip6.parse::>().unwrap(); assert_eq!(ProtocolNumbers::Udp, frag.next_header()); assert_eq!(543, frag.fragment_offset()); @@ -356,17 +356,17 @@ mod tests { fn parse_non_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ipv6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn push_and_set_fragment_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); - let mut frag = ipv6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut frag = ip6.push::>().unwrap(); assert_eq!(FragmentHeader::size_of(), frag.len()); assert_eq!(ProtocolNumbers::Ipv6Frag, frag.envelope().next_header()); @@ -393,11 +393,11 @@ mod tests { fn insert_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - let next_header = ipv6.next_header(); - let payload_len = ipv6.payload_len(); - let frag = ipv6.push::>().unwrap(); + let next_header = ip6.next_header(); + let payload_len = ip6.payload_len(); + let frag = ip6.push::>().unwrap(); assert_eq!(ProtocolNumbers::Ipv6Frag, frag.envelope().next_header()); assert_eq!(next_header, frag.next_header()); @@ -408,14 +408,14 @@ mod tests { fn remove_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_FRAGMENT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let frag = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let frag = ip6.parse::>().unwrap(); let next_header = frag.next_header(); let payload_len = frag.payload_len(); - let ipv6 = frag.remove().unwrap(); + let ip6 = frag.remove().unwrap(); - assert_eq!(next_header, ipv6.next_header()); - assert_eq!(payload_len, ipv6.payload_len()); + assert_eq!(next_header, ip6.next_header()); + assert_eq!(payload_len, ip6.payload_len()); } } diff --git a/core/src/packets/ip/v6/mod.rs b/core/src/packets/ip/v6/mod.rs index 5c10414..bbbfaf6 100644 --- a/core/src/packets/ip/v6/mod.rs +++ b/core/src/packets/ip/v6/mod.rs @@ -29,7 +29,7 @@ use crate::packets::checksum::PseudoHeader; use crate::packets::ethernet::{EtherTypes, Ethernet}; use crate::packets::ip::{IpPacket, ProtocolNumber, DEFAULT_IP_TTL}; use crate::packets::types::{u16be, u32be}; -use crate::packets::{Internal, Packet, SizeOf}; +use crate::packets::{Datalink, Internal, Packet, SizeOf}; use anyhow::{anyhow, Result}; use std::fmt; use std::net::{IpAddr, Ipv6Addr}; @@ -96,13 +96,13 @@ const FLOW: u32be = u32be(u32::to_be(0xfffff)); /// [IETF RFC 8200]: https://tools.ietf.org/html/rfc8200#section-3 /// [IETF RFC 2474]: https://tools.ietf.org/html/rfc2474 /// [IETF RFC 3168]: https://tools.ietf.org/html/rfc3168 -pub struct Ipv6 { - envelope: Ethernet, +pub struct Ipv6 { + envelope: E, header: NonNull, offset: usize, } -impl Ipv6 { +impl Ipv6 { #[inline] fn header(&self) -> &Ipv6Header { unsafe { self.header.as_ref() } @@ -210,7 +210,7 @@ impl Ipv6 { } } -impl fmt::Debug for Ipv6 { +impl fmt::Debug for Ipv6 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ipv6") .field("src", &format!("{}", self.src())) @@ -228,9 +228,8 @@ impl fmt::Debug for Ipv6 { } } -impl Packet for Ipv6 { - /// The preceding type for IPv6 packet must be Ethernet. - type Envelope = Ethernet; +impl Packet for Ipv6 { + type Envelope = E; #[inline] fn envelope(&self) -> &Self::Envelope { @@ -274,7 +273,7 @@ impl Packet for Ipv6 { #[inline] fn try_parse(envelope: Self::Envelope, _internal: Internal) -> Result { ensure!( - envelope.ether_type() == EtherTypes::Ipv6, + envelope.protocol_type() == EtherTypes::Ipv6, anyhow!("not an IPv6 packet.") ); @@ -307,7 +306,7 @@ impl Packet for Ipv6 { mbuf.extend(offset, Ipv6Header::size_of())?; let header = mbuf.write_data(offset, &Ipv6Header::default())?; - envelope.set_ether_type(EtherTypes::Ipv6); + envelope.set_protocol_type(EtherTypes::Ipv6); Ok(Ipv6 { envelope, @@ -335,7 +334,7 @@ impl Packet for Ipv6 { } } -impl IpPacket for Ipv6 { +impl IpPacket for Ipv6 { #[inline] fn next_protocol(&self) -> ProtocolNumber { self.next_header() @@ -418,7 +417,7 @@ impl IpPacket for Ipv6 { } } -impl Ipv6Packet for Ipv6 { +impl Ipv6Packet for Ipv6 { #[inline] fn next_header(&self) -> ProtocolNumber { ProtocolNumber::new(self.header().next_header) @@ -439,6 +438,9 @@ pub trait Ipv6Packet: IpPacket { fn set_next_header(&mut self, next_header: ProtocolNumber); } +/// A type alias for an Ethernet IPv6 packet. +pub type Ip6 = Ipv6; + /// IPv6 header. #[derive(Clone, Copy, Debug, SizeOf)] #[repr(C)] @@ -480,17 +482,17 @@ mod tests { fn parse_ipv6_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert_eq!(6, ipv6.version()); - assert_eq!(0, ipv6.dscp()); - assert_eq!(0, ipv6.ecn()); - assert_eq!(0, ipv6.flow_label()); - assert_eq!(24, ipv6.payload_len()); - assert_eq!(ProtocolNumbers::Tcp, ipv6.next_header()); - assert_eq!(2, ipv6.hop_limit()); - assert_eq!("2001:db8:85a3::1", ipv6.src().to_string()); - assert_eq!("2001:db8:85a3::8a2e:370:7334", ipv6.dst().to_string()); + assert_eq!(6, ip6.version()); + assert_eq!(0, ip6.dscp()); + assert_eq!(0, ip6.ecn()); + assert_eq!(0, ip6.flow_label()); + assert_eq!(24, ip6.payload_len()); + assert_eq!(ProtocolNumbers::Tcp, ip6.next_header()); + assert_eq!(2, ip6.hop_limit()); + assert_eq!("2001:db8:85a3::1", ip6.src().to_string()); + assert_eq!("2001:db8:85a3::8a2e:370:7334", ip6.dst().to_string()); } #[capsule::test] @@ -498,38 +500,38 @@ mod tests { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - assert!(ethernet.parse::().is_err()); + assert!(ethernet.parse::().is_err()); } #[capsule::test] fn parse_ipv6_setter_checks() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ipv6 = ethernet.parse::().unwrap(); + let mut ip6 = ethernet.parse::().unwrap(); - assert_eq!(6, ipv6.version()); - assert_eq!(0, ipv6.dscp()); - assert_eq!(0, ipv6.ecn()); - assert_eq!(0, ipv6.flow_label()); - ipv6.set_dscp(10); - ipv6.set_ecn(3); - assert_eq!(6, ipv6.version()); - assert_eq!(10, ipv6.dscp()); - assert_eq!(3, ipv6.ecn()); - assert_eq!(0, ipv6.flow_label()); + assert_eq!(6, ip6.version()); + assert_eq!(0, ip6.dscp()); + assert_eq!(0, ip6.ecn()); + assert_eq!(0, ip6.flow_label()); + ip6.set_dscp(10); + ip6.set_ecn(3); + assert_eq!(6, ip6.version()); + assert_eq!(10, ip6.dscp()); + assert_eq!(3, ip6.ecn()); + assert_eq!(0, ip6.flow_label()); } #[capsule::test] fn push_ipv6_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ipv6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); - assert_eq!(6, ipv6.version()); - assert_eq!(Ipv6Header::size_of(), ipv6.len()); + assert_eq!(6, ip6.version()); + assert_eq!(Ipv6Header::size_of(), ip6.len()); // make sure ether type is fixed - assert_eq!(EtherTypes::Ipv6, ipv6.envelope().ether_type()); + assert_eq!(EtherTypes::Ipv6, ip6.envelope().ether_type()); } #[capsule::test] @@ -539,13 +541,13 @@ mod tests { let _ = packet.extend(0, 1800); let ethernet = packet.push::().unwrap(); - let mut ipv6 = ethernet.push::().unwrap(); + let mut ip6 = ethernet.push::().unwrap(); // can't truncate to less than minimum MTU. - assert!(ipv6.truncate(1200).is_err()); + assert!(ip6.truncate(1200).is_err()); - assert!(ipv6.len() > 1500); - let _ = ipv6.truncate(1500); - assert_eq!(1500, ipv6.len()); + assert!(ip6.len() > 1500); + let _ = ip6.truncate(1500); + assert_eq!(1500, ip6.len()); } } diff --git a/core/src/packets/ip/v6/srh.rs b/core/src/packets/ip/v6/srh.rs index bdad10b..7eb2762 100644 --- a/core/src/packets/ip/v6/srh.rs +++ b/core/src/packets/ip/v6/srh.rs @@ -524,7 +524,7 @@ impl Default for SegmentRoutingHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; + use crate::packets::ip::v6::Ip6; use crate::packets::ip::ProtocolNumbers; use crate::packets::tcp::{Tcp, Tcp6}; use crate::packets::Mbuf; @@ -539,8 +539,8 @@ mod tests { fn parse_segment_routing_packet() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let srh = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); assert_eq!(ProtocolNumbers::Tcp, srh.next_header()); assert_eq!(6, srh.hdr_ext_len()); @@ -560,17 +560,17 @@ mod tests { fn parse_non_segment_routing_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ipv6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn set_segments() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut srh = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut srh = ip6.parse::>().unwrap(); let segment1: Ipv6Addr = "::1".parse().unwrap(); @@ -597,7 +597,7 @@ mod tests { assert!(srh.set_segments(&[]).is_err()); // make sure rest of the packet still valid - let tcp = srh.parse::>>().unwrap(); + let tcp = srh.parse::>>().unwrap(); assert_eq!(3464, tcp.src_port()) } @@ -605,8 +605,8 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let mut srh = ipv6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut srh = ip6.parse::>().unwrap(); let segment1: Ipv6Addr = "::1".parse().unwrap(); let segment2: Ipv6Addr = "::2".parse().unwrap(); @@ -619,7 +619,7 @@ mod tests { assert_eq!(4, srh.segments().len()); srh.set_segments_left(3); - let mut tcp = srh.parse::>>().unwrap(); + let mut tcp = srh.parse::>>().unwrap(); // Should pass as we're using the hard-coded (and wrong) initial // checksum, as it's 0 given above. @@ -639,7 +639,7 @@ mod tests { assert_eq!(1, srh_ret.segments().len()); srh_ret.set_segments_left(0); - let mut tcp_ret = srh_ret.parse::>>().unwrap(); + let mut tcp_ret = srh_ret.parse::>>().unwrap(); tcp_ret.reconcile_all(); assert_eq!(expected, tcp_ret.checksum()); @@ -647,7 +647,7 @@ mod tests { // is still the same segment. let mut srh_fin = tcp_ret.deparse(); srh_fin.set_segments_left(0); - let mut tcp_fin = srh_fin.parse::>>().unwrap(); + let mut tcp_fin = srh_fin.parse::>>().unwrap(); tcp_fin.reconcile_all(); assert_eq!(expected, tcp_fin.checksum()); } @@ -656,9 +656,9 @@ mod tests { fn insert_segment_routing_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let ipv6_payload_len = ipv6.payload_len(); - let srh = ipv6.push::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let ip6_payload_len = ip6.payload_len(); + let srh = ip6.push::>().unwrap(); assert_eq!(2, srh.hdr_ext_len()); assert_eq!(1, srh.segments().len()); @@ -669,32 +669,32 @@ mod tests { assert_eq!(ProtocolNumbers::Ipv6Route, srh.envelope().next_header()); // ipv6 payload is srh payload after push - assert_eq!(ipv6_payload_len, srh.payload_len()); + assert_eq!(ip6_payload_len, srh.payload_len()); // make sure rest of the packet still valid - let tcp = srh.parse::>>().unwrap(); + let tcp = srh.parse::>>().unwrap(); assert_eq!(36869, tcp.src_port()); let mut srh = tcp.deparse(); let srh_packet_len = srh.len(); srh.reconcile_all(); - let ipv6 = srh.deparse(); - assert_ne!(srh_packet_len, ipv6_payload_len as usize); - assert_eq!(srh_packet_len, ipv6.payload_length() as usize) + let ip6 = srh.deparse(); + assert_ne!(srh_packet_len, ip6_payload_len as usize); + assert_eq!(srh_packet_len, ip6.payload_length() as usize) } #[capsule::test] fn remove_segment_routing_packet() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let srh = ipv6.parse::>().unwrap(); - let ipv6 = srh.remove().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); + let ip6 = srh.remove().unwrap(); // make sure next header is fixed - assert_eq!(ProtocolNumbers::Tcp, ipv6.next_header()); + assert_eq!(ProtocolNumbers::Tcp, ip6.next_header()); // make sure rest of the packet still valid - let tcp = ipv6.parse::().unwrap(); + let tcp = ip6.parse::().unwrap(); assert_eq!(3464, tcp.src_port()); } } diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index 6e616ab..e8756dc 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -20,7 +20,7 @@ use crate::ensure; use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::Ipv6; +use crate::packets::ip::v6::Ip6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::{u16be, u32be}; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -626,8 +626,8 @@ impl Packet for Tcp { /// A type alias for an Ethernet IPv4 TCP packet. pub type Tcp4 = Tcp; -/// A type alias for an IPv6 TCP packet. -pub type Tcp6 = Tcp; +/// A type alias for an Ethernet IPv6 TCP packet. +pub type Tcp6 = Tcp; /// TCP header. /// @@ -732,9 +732,9 @@ mod tests { fn tcp_flow_v6() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - let srh = ipv6.parse::>().unwrap(); - let tcp = srh.parse::>>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); + let tcp = srh.parse::>>().unwrap(); let flow = tcp.flow(); assert_eq!("2001:db8:85a3::1", flow.src_ip().to_string()); diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 0068a84..83bcd7a 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -20,7 +20,7 @@ use crate::ensure; use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::Ipv6; +use crate::packets::ip::v6::Ip6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::u16be; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -372,8 +372,8 @@ impl Packet for Udp { /// A type alias for an Ethernet IPv4 UDP packet. pub type Udp4 = Udp; -/// A type alias for an IPv6 UDP packet. -pub type Udp6 = Udp; +/// A type alias for an Ethernet IPv6 UDP packet. +pub type Udp6 = Udp; /// UDP header. #[derive(Clone, Copy, Debug, Default, SizeOf)] diff --git a/core/src/testils/packet.rs b/core/src/testils/packet.rs index 24d2882..a6e3dd2 100644 --- a/core/src/testils/packet.rs +++ b/core/src/testils/packet.rs @@ -18,7 +18,7 @@ use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::{Ipv6, SegmentRouting}; +use crate::packets::ip::v6::{Ip6, SegmentRouting}; use crate::packets::tcp::{Tcp, Tcp4, Tcp6}; use crate::packets::udp::{Udp4, Udp6}; use crate::packets::Packet; @@ -37,43 +37,43 @@ pub trait PacketExt: Packet + Sized { } /// Converts the packet into an IPv4 packet. - fn into_v4(self) -> Ip4 { + fn into_ip4(self) -> Ip4 { self.into_eth().parse::().unwrap() } /// Converts the packet into a TCP packet inside IPv4. - fn into_v4_tcp(self) -> Tcp4 { - self.into_v4().parse::().unwrap() + fn into_tcp4(self) -> Tcp4 { + self.into_ip4().parse::().unwrap() } /// Converts the packet into a UDP packet inside IPv4. - fn into_v4_udp(self) -> Udp4 { - self.into_v4().parse::().unwrap() + fn into_udp4(self) -> Udp4 { + self.into_ip4().parse::().unwrap() } /// Converts the packet into an IPv6 packet. - fn into_v6(self) -> Ipv6 { - self.into_eth().parse::().unwrap() + fn into_ip6(self) -> Ip6 { + self.into_eth().parse::().unwrap() } /// Converts the packet into a TCP packet inside IPv6. - fn into_v6_tcp(self) -> Tcp6 { - self.into_v6().parse::().unwrap() + fn into_tcp6(self) -> Tcp6 { + self.into_ip6().parse::().unwrap() } /// Converts the packet into a UDP packet inside IPv6. - fn into_v6_udp(self) -> Udp6 { - self.into_v6().parse::().unwrap() + fn into_udp6(self) -> Udp6 { + self.into_ip6().parse::().unwrap() } /// Converts the packet into an IPv6 packet with a SRH extension. - fn into_sr(self) -> SegmentRouting { - self.into_v6().parse::>().unwrap() + fn into_sr(self) -> SegmentRouting { + self.into_ip6().parse::>().unwrap() } /// Converts the packet into a TCP packet inside IPv6 with a SRH extension. - fn into_sr_tcp(self) -> Tcp> { - self.into_sr().parse::>>().unwrap() + fn into_sr_tcp(self) -> Tcp> { + self.into_sr().parse::>>().unwrap() } } diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index 61c0b49..0479b39 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -21,7 +21,7 @@ use crate::net::MacAddr; use crate::packets::ethernet::{EtherType, EtherTypes, Ethernet}; use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::{Ipv6, Ipv6Packet, SegmentRouting}; +use crate::packets::ip::v6::{Ip6, Ipv6Packet, SegmentRouting}; use crate::packets::ip::{Flow, IpPacket, ProtocolNumber, ProtocolNumbers}; use crate::packets::tcp::Tcp; use crate::packets::udp::Udp; @@ -219,7 +219,7 @@ fn ethernet(ether_type: EtherType, map: &StrategyMap) -> impl Strategy impl Strategy { +fn ip4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy { ( ethernet(EtherTypes::Ipv4, map), map.ipv4_addr(&field::ipv4_src), @@ -265,7 +265,7 @@ fn ipv4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy impl Strategy { +fn ip6(next_header: ProtocolNumber, map: &StrategyMap) -> impl Strategy { ( ethernet(EtherTypes::Ipv6, map), map.ipv6_addr(&field::ipv6_src), @@ -277,7 +277,7 @@ fn ipv6(next_header: ProtocolNumber, map: &StrategyMap) -> impl Strategy().unwrap(); + let mut packet = packet.push::().unwrap(); packet.set_src(src); packet.set_dst(dst); packet.set_ecn(ecn); @@ -290,7 +290,7 @@ fn ipv6(next_header: ProtocolNumber, map: &StrategyMap) -> impl Strategy( +fn sr( envelope: impl Strategy, next_header: ProtocolNumber, map: &StrategyMap, @@ -407,13 +407,13 @@ fn udp( /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv4` and `next_header` is always /// `ProtocolNumbers::Tcp`. -pub fn v4_tcp() -> impl Strategy { - v4_tcp_with(fieldmap! {}) +pub fn ip4_tcp() -> impl Strategy { + ip4_tcp_with(fieldmap! {}) } /// Returns a strategy to generate IPv4 TCP packets. /// -/// Similar to `v4_tcp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `ip4_tcp`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -421,21 +421,21 @@ pub fn v4_tcp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn v4_tcp_packet() { -/// proptest!(|(packet in v4_tcp_with(fieldmap! { +/// fn ip4_tcp_packet() { +/// proptest!(|(packet in ip4_tcp_with(fieldmap! { /// field::ipv4_src => "127.0.0.1".parse(), /// field::tcp_dst_port => 80 /// }))| { /// let packet = packet.parse::().unwrap(); -/// let v4 = packet.parse::().unwrap(); -/// assert_eq!("127.0.0.1".parse(), v4.src()); -/// let tcp = v4.parse::().unwrap(); +/// let ip4 = packet.parse::().unwrap(); +/// assert_eq!("127.0.0.1".parse(), ip4.src()); +/// let tcp = ip4.parse::().unwrap(); /// assert_eq!(80, tcp.dst_port()); /// }); /// } /// ``` -pub fn v4_tcp_with(map: StrategyMap) -> impl Strategy { - let envelope = ipv4(ProtocolNumbers::Tcp, &map); +pub fn ip4_tcp_with(map: StrategyMap) -> impl Strategy { + let envelope = ip4(ProtocolNumbers::Tcp, &map); tcp(envelope, &map) } @@ -445,13 +445,13 @@ pub fn v4_tcp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv4` and `next_header` is always /// `ProtocolNumbers::Udp`. -pub fn v4_udp() -> impl Strategy { - v4_udp_with(fieldmap! {}) +pub fn ip4_udp() -> impl Strategy { + ip4_udp_with(fieldmap! {}) } /// Returns a strategy to generate IPv4 UDP packets. /// -/// Similar to `v4_udp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `ip4_udp`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -459,21 +459,21 @@ pub fn v4_udp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn v4_udp_packet() { -/// proptest!(|(packet in v4_udp_with(fieldmap! { +/// fn ip4_udp_packet() { +/// proptest!(|(packet in ip4_udp_with(fieldmap! { /// field::ipv4_src => "127.0.0.1".parse(), /// field::udp_dst_port => 53, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let v4 = packet.parse::().unwrap(); -/// prop_assert_eq!("127.0.0.1".parse(), v4.src()); -/// let udp = v4.parse::().unwrap(); +/// let ip4 = packet.parse::().unwrap(); +/// prop_assert_eq!("127.0.0.1".parse(), ip4.src()); +/// let udp = ip4.parse::().unwrap(); /// prop_assert_eq!(53, udp.dst_port()); /// }); /// } /// ``` -pub fn v4_udp_with(map: StrategyMap) -> impl Strategy { - let envelope = ipv4(ProtocolNumbers::Udp, &map); +pub fn ip4_udp_with(map: StrategyMap) -> impl Strategy { + let envelope = ip4(ProtocolNumbers::Udp, &map); udp(envelope, &map) } @@ -483,13 +483,13 @@ pub fn v4_udp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv6` and `next_header` is always /// `ProtocolNumbers::Tcp`. -pub fn v6_tcp() -> impl Strategy { - v6_tcp_with(fieldmap! {}) +pub fn ip6_tcp() -> impl Strategy { + ip6_tcp_with(fieldmap! {}) } /// Returns a strategy to generate IPv6 TCP packets. /// -/// Similar to `v6_tcp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `ip6_tcp`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -497,21 +497,21 @@ pub fn v6_tcp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn v6_tcp_packet() { -/// proptest!(|(packet in v6_tcp_with(fieldmap! { +/// fn ip6_tcp_packet() { +/// proptest!(|(packet in ip6_tcp_with(fieldmap! { /// field::ipv6_src => "::1".parse(), /// field::tcp_dst_port => 80, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let v6 = packet.parse::().unwrap(); -/// prop_assert_eq!("::1".parse(), v6.src()); -/// let tcp = v6.parse::().unwrap(); +/// let ip6 = packet.parse::().unwrap(); +/// prop_assert_eq!("::1".parse(), ip6.src()); +/// let tcp = ip6.parse::().unwrap(); /// prop_assert_eq!(80, tcp.dst_port()); /// }); /// } /// ``` -pub fn v6_tcp_with(map: StrategyMap) -> impl Strategy { - let envelope = ipv6(ProtocolNumbers::Tcp, &map); +pub fn ip6_tcp_with(map: StrategyMap) -> impl Strategy { + let envelope = ip6(ProtocolNumbers::Tcp, &map); tcp(envelope, &map) } @@ -521,13 +521,13 @@ pub fn v6_tcp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv6` and `next_header` is always /// `ProtocolNumbers::Udp`. -pub fn v6_udp() -> impl Strategy { - v6_udp_with(fieldmap! {}) +pub fn ip6_udp() -> impl Strategy { + ip6_udp_with(fieldmap! {}) } /// Returns a strategy to generate IPv6 UDP packets. /// -/// Similar to `v6_udp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `ip6_udp`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -535,21 +535,21 @@ pub fn v6_udp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn v6_udp_packet() { -/// proptest!(|(packet in v6_udp_with(fieldmap! { +/// fn ip6_udp_packet() { +/// proptest!(|(packet in ip6_udp_with(fieldmap! { /// field::ipv6_src => "::1".parse(), /// field::udp_dst_port => 53, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let v6 = packet.parse::().unwrap(); -/// prop_assert_eq!("::1".parse(), v6.src()); -/// let udp = v6.parse::().unwrap(); +/// let ip6 = packet.parse::().unwrap(); +/// prop_assert_eq!("::1".parse(), ip6.src()); +/// let udp = ip6.parse::().unwrap(); /// prop_assert_eq!(53, udp.dst_port()); /// }); /// } /// ``` -pub fn v6_udp_with(map: StrategyMap) -> impl Strategy { - let envelope = ipv6(ProtocolNumbers::Udp, &map); +pub fn ip6_udp_with(map: StrategyMap) -> impl Strategy { + let envelope = ip6(ProtocolNumbers::Udp, &map); udp(envelope, &map) } @@ -580,18 +580,18 @@ pub fn sr_tcp() -> impl Strategy { /// field::tcp_dst_port => 80, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let v6 = packet.parse::().unwrap(); -/// prop_assert_eq!("::1".parse(), v6.src()); -/// let srh = v6.parse::>().unwrap(); -/// prop_assert_eq!(2, srh.segments().len()); -/// let tcp = srh.parse::>>().unwrap(); +/// let ip6 = packet.parse::().unwrap(); +/// prop_assert_eq!("::1".parse(), ip6.src()); +/// let sr = ip6.parse::>().unwrap(); +/// prop_assert_eq!(2, sr.segments().len()); +/// let tcp = sr.parse::>>().unwrap(); /// prop_assert_eq!(80, tcp.dst_port()); /// }); /// } /// ``` pub fn sr_tcp_with(map: StrategyMap) -> impl Strategy { - let envelope = ipv6(ProtocolNumbers::Ipv6Route, &map); - let envelope = srh(envelope, ProtocolNumbers::Tcp, &map); + let envelope = ip6(ProtocolNumbers::Ipv6Route, &map); + let envelope = sr(envelope, ProtocolNumbers::Tcp, &map); tcp(envelope, &map) } @@ -599,7 +599,7 @@ pub fn sr_tcp_with(map: StrategyMap) -> impl Strategy { /// /// The IP addresses and ports are random. The protocol can be /// either TCP, UDP or ICMP. -pub fn v4_flow() -> impl Strategy { +pub fn ip4_flow() -> impl Strategy { ( any::(), any::(), @@ -626,7 +626,7 @@ pub fn v4_flow() -> impl Strategy { /// /// The IP addresses and ports are random. The protocol can be /// either TCP, UDP or ICMP. -pub fn v6_flow() -> impl Strategy { +pub fn ip6_flow() -> impl Strategy { ( any::(), any::(), diff --git a/core/src/testils/rvg.rs b/core/src/testils/rvg.rs index 6bbfbe0..938d211 100644 --- a/core/src/testils/rvg.rs +++ b/core/src/testils/rvg.rs @@ -84,11 +84,11 @@ mod tests { use std::net::Ipv6Addr; #[capsule::test] - fn gen_v4_packet() { + fn gen_ip4_packet() { let mut gen = Rvg::new(); - let packet = gen.generate(&v4_udp()); - let v4 = packet.into_v4(); - assert_eq!(ProtocolNumbers::Udp, v4.protocol()); + let packet = gen.generate(&ip4_udp()); + let ip4 = packet.into_ip4(); + assert_eq!(ProtocolNumbers::Udp, ip4.protocol()); } #[capsule::test] From bfeb7a965b54e15880b293ff61572991532c8b29 Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Fri, 7 Jan 2022 23:41:40 -0500 Subject: [PATCH 16/18] fix compilation errors --- bench/packets.rs | 124 +++++++++++++------------- core/src/testils/proptest/strategy.rs | 48 +++++----- core/src/testils/rvg.rs | 2 +- examples/kni/main.rs | 18 ++-- examples/nat64/main.rs | 93 +++++++++---------- examples/ping4d/main.rs | 14 +-- examples/pktdump/main.rs | 24 ++--- examples/syn-flood/main.rs | 10 +-- 8 files changed, 167 insertions(+), 166 deletions(-) diff --git a/bench/packets.rs b/bench/packets.rs index 61ce7c6..da63e79 100644 --- a/bench/packets.rs +++ b/bench/packets.rs @@ -19,8 +19,8 @@ use anyhow::Result; use capsule::fieldmap; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ipv4; -use capsule::packets::ip::v6::{Ipv6, SegmentRouting}; +use capsule::packets::ip::v4::Ip4; +use capsule::packets::ip::v6::{Ip6, SegmentRouting}; use capsule::packets::udp::Udp4; use capsule::packets::{Mbuf, Packet}; use capsule::testils::criterion::BencherExt; @@ -32,13 +32,13 @@ use std::net::Ipv6Addr; const BATCH_SIZE: usize = 500; -fn single_parse_udp(ipv4: Ipv4) -> Udp4 { - ipv4.parse::().unwrap() +fn single_parse_udp(ip4: Ip4) -> Udp4 { + ip4.parse::().unwrap() } -fn single_peek_udp(ipv4: Ipv4) -> Ipv4 { - ipv4.peek::().unwrap(); - ipv4 +fn single_peek_udp(ip4: Ip4) -> Ip4 { + ip4.peek::().unwrap(); + ip4 } #[capsule::bench(mempool_capacity = 511)] @@ -46,16 +46,16 @@ fn single_peek_vs_parse(c: &mut Criterion) { let mut group = c.benchmark_group("packets::single_peek_vs_parse_on_udp"); group.bench_function("packets::single_parse_udp", |b| { - let s = v4_udp().prop_map(|v| { - let packet = v.into_v4_udp(); + let s = udp4().prop_map(|v| { + let packet = v.into_udp4(); packet.deparse() }); b.iter_proptest_batched(s, single_parse_udp, BATCH_SIZE) }); group.bench_function("packets::single_peek_udp", |b| { - let s = v4_udp().prop_map(|v| { - let packet = v.into_v4_udp(); + let s = udp4().prop_map(|v| { + let packet = v.into_udp4(); packet.deparse() }); b.iter_proptest_batched(s, single_peek_udp, BATCH_SIZE) @@ -66,14 +66,14 @@ fn single_peek_vs_parse(c: &mut Criterion) { fn multi_parse_udp(mbuf: Mbuf) -> Udp4 { let ethernet = mbuf.parse::().unwrap(); - let ipv4 = ethernet.parse::().unwrap(); - ipv4.parse::().unwrap() + let ip4 = ethernet.parse::().unwrap(); + ip4.parse::().unwrap() } fn multi_peek_udp(mbuf: Mbuf) -> Mbuf { let ethernet = mbuf.peek::().unwrap(); - let ipv4 = ethernet.peek::().unwrap(); - ipv4.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); + ip4.peek::().unwrap(); mbuf } @@ -82,70 +82,70 @@ fn multi_peek_vs_parse(c: &mut Criterion) { let mut group = c.benchmark_group("packets::multi_peek_vs_parse_on_udp_packets"); group.bench_function("packets::multi_parse_udp", |b| { - let s = v4_udp(); + let s = udp4(); b.iter_proptest_batched(s, multi_parse_udp, BATCH_SIZE) }); group.bench_function("packets::multi_peek_udp", |b| { - let s = v4_udp(); + let s = udp4(); b.iter_proptest_batched(s, multi_peek_udp, BATCH_SIZE) }); group.finish() } -fn single_parse_srh(ipv6: Ipv6) -> SegmentRouting { - ipv6.parse::>().unwrap() +fn single_parse_sr(ip6: Ip6) -> SegmentRouting { + ip6.parse::>().unwrap() } #[capsule::bench(mempool_capacity = 511)] -fn single_parse_srh_segments_sizes(c: &mut Criterion) { - let mut group = c.benchmark_group("packets::parsing_on_SRH_across_segment_sizes"); +fn single_parse_sr_segments_sizes(c: &mut Criterion) { + let mut group = c.benchmark_group("packets::parsing_on_sr_across_segment_sizes"); let mut rvg = Rvg::new(); - group.bench_function("packets::single_parse_srh::size=1", |b| { + group.bench_function("packets::single_parse_sr::size=1", |b| { let segments = rvg.generate_vec(&any::(), 1); let s = sr_tcp_with(fieldmap! {field::sr_segments => segments}) .prop_map(|v| v.into_sr().deparse()); - b.iter_proptest_batched(s, single_parse_srh, BATCH_SIZE) + b.iter_proptest_batched(s, single_parse_sr, BATCH_SIZE) }); - group.bench_function("packets::single_parse_srh::size=2", |b| { + group.bench_function("packets::single_parse_sr::size=2", |b| { let segments = rvg.generate_vec(&any::(), 2); let s = sr_tcp_with(fieldmap! {field::sr_segments => segments}) .prop_map(|v| v.into_sr().deparse()); - b.iter_proptest_batched(s, single_parse_srh, BATCH_SIZE) + b.iter_proptest_batched(s, single_parse_sr, BATCH_SIZE) }); - group.bench_function("packets::single_parse_srh::size=4", |b| { + group.bench_function("packets::single_parse_sr::size=4", |b| { let segments = rvg.generate_vec(&any::(), 4); let s = sr_tcp_with(fieldmap! {field::sr_segments => segments}) .prop_map(|v| v.into_sr().deparse()); - b.iter_proptest_batched(s, single_parse_srh, BATCH_SIZE) + b.iter_proptest_batched(s, single_parse_sr, BATCH_SIZE) }); - group.bench_function("packets::single_parse_srh::size=8", |b| { + group.bench_function("packets::single_parse_sr::size=8", |b| { let segments = rvg.generate_vec(&any::(), 8); let s = sr_tcp_with(fieldmap! {field::sr_segments => segments}) .prop_map(|v| v.into_sr().deparse()); - b.iter_proptest_batched(s, single_parse_srh, BATCH_SIZE) + b.iter_proptest_batched(s, single_parse_sr, BATCH_SIZE) }); group.finish() } -fn multi_parse_srh(mbuf: Mbuf) -> SegmentRouting { +fn multi_parse_sr(mbuf: Mbuf) -> SegmentRouting { let ethernet = mbuf.parse::().unwrap(); - let ipv6 = ethernet.parse::().unwrap(); - ipv6.parse::>().unwrap() + let ip6 = ethernet.parse::().unwrap(); + ip6.parse::>().unwrap() } #[capsule::bench(mempool_capacity = 511)] -fn multi_parse_upto_variable_srh(c: &mut Criterion) { - c.bench_function("packets::multi_parse_srh", |b| { +fn multi_parse_upto_variable_sr(c: &mut Criterion) { + c.bench_function("packets::multi_parse_sr", |b| { let s = sr_tcp(); - b.iter_proptest_batched(s, multi_parse_srh, BATCH_SIZE) + b.iter_proptest_batched(s, multi_parse_sr, BATCH_SIZE) }); } @@ -158,7 +158,7 @@ fn deparse_udp(udp: Udp4) -> Mbuf { #[capsule::bench(mempool_capacity = 511)] fn deparse(c: &mut Criterion) { c.bench_function("packets::deparse_udp", |b| { - let s = v4_udp().prop_map(|v| v.into_v4_udp()); + let s = udp4().prop_map(|v| v.into_udp4()); b.iter_proptest_batched(s, deparse_udp, BATCH_SIZE) }); } @@ -170,15 +170,15 @@ fn reset_udp(udp: Udp4) -> Mbuf { #[capsule::bench(mempool_capacity = 511)] fn reset(c: &mut Criterion) { c.bench_function("packets::reset_udp", |b| { - let s = v4_udp().prop_map(|v| v.into_v4_udp()); + let s = udp4().prop_map(|v| v.into_udp4()); b.iter_proptest_batched(s, reset_udp, BATCH_SIZE) }); } fn multi_push_udp(mbuf: Mbuf) -> Udp4 { let ethernet = mbuf.push::().unwrap(); - let ipv4 = ethernet.push::().unwrap(); - ipv4.push::().unwrap() + let ip4 = ethernet.push::().unwrap(); + ip4.push::().unwrap() } #[capsule::bench(mempool_capacity = 511)] @@ -189,79 +189,79 @@ fn multi_push(c: &mut Criterion) { }); } -fn single_push_udp(ipv4: Ipv4) -> Udp4 { - ipv4.push::().unwrap() +fn single_push_udp(ip4: Ip4) -> Udp4 { + ip4.push::().unwrap() } #[capsule::bench(mempool_capacity = 511)] fn single_push(c: &mut Criterion) { c.bench_function("packets::single_push_udp", |b| { - let s = v4_udp().prop_map(|v| { - let udp = v.into_v4_udp(); + let s = udp4().prop_map(|v| { + let udp = v.into_udp4(); udp.remove().unwrap() }); b.iter_proptest_batched(s, single_push_udp, BATCH_SIZE) }); } -fn single_remove_udp(udp: Udp4) -> Ipv4 { +fn single_remove_udp(udp: Udp4) -> Ip4 { udp.remove().unwrap() } #[capsule::bench(mempool_capacity = 511)] fn single_remove(c: &mut Criterion) { c.bench_function("packets::single_remove_from_udp", |b| { - let s = v4_udp().prop_map(|v| v.into_v4_udp()); + let s = udp4().prop_map(|v| v.into_udp4()); b.iter_proptest_batched(s, single_remove_udp, BATCH_SIZE) }); } fn multi_remove_udp(udp: Udp4) -> Mbuf { - let ipv4 = udp.remove().unwrap(); - let ethernet = ipv4.remove().unwrap(); + let ip4 = udp.remove().unwrap(); + let ethernet = ip4.remove().unwrap(); ethernet.remove().unwrap() } #[capsule::bench(mempool_capacity = 511)] fn multi_remove(c: &mut Criterion) { c.bench_function("packets::multi_remove_from_udp", |b| { - let s = v4_udp().prop_map(|v| v.into_v4_udp()); + let s = udp4().prop_map(|v| v.into_udp4()); b.iter_proptest_batched(s, multi_remove_udp, BATCH_SIZE) }); } -fn set_srh_segments(mut args: (SegmentRouting, Vec)) -> Result<()> { +fn set_sr_segments(mut args: (SegmentRouting, Vec)) -> Result<()> { args.0.set_segments(&args.1) } #[capsule::bench(mempool_capacity = 511)] -fn set_srh_segments_sizes(c: &mut Criterion) { - let mut group = c.benchmark_group("packets::setting_segments_on_SRH_across_segment_sizes"); +fn set_sr_segments_sizes(c: &mut Criterion) { + let mut group = c.benchmark_group("packets::setting_segments_on_sr_across_segment_sizes"); let mut rvg = Rvg::new(); - group.bench_function("packets::set_srh_segments::size=1", |b| { + group.bench_function("packets::set_sr_segments::size=1", |b| { let segments = rvg.generate_vec(&any::(), 1); let s = (sr_tcp().prop_map(|v| v.into_sr()), Just(segments)); - b.iter_proptest_batched(s, set_srh_segments, BATCH_SIZE) + b.iter_proptest_batched(s, set_sr_segments, BATCH_SIZE) }); - group.bench_function("packets::set_srh_segments::size=2", |b| { + group.bench_function("packets::set_sr_segments::size=2", |b| { let segments = rvg.generate_vec(&any::(), 2); let s = (sr_tcp().prop_map(|v| v.into_sr()), Just(segments)); - b.iter_proptest_batched(s, set_srh_segments, BATCH_SIZE) + b.iter_proptest_batched(s, set_sr_segments, BATCH_SIZE) }); - group.bench_function("packets::set_srh_segments::size=4", |b| { + group.bench_function("packets::set_sr_segments::size=4", |b| { let segments = rvg.generate_vec(&any::(), 4); let s = (sr_tcp().prop_map(|v| v.into_sr()), Just(segments)); - b.iter_proptest_batched(s, set_srh_segments, BATCH_SIZE) + b.iter_proptest_batched(s, set_sr_segments, BATCH_SIZE) }); - group.bench_function("packets::set_srh_segments::size=8", |b| { + group.bench_function("packets::set_sr_segments::size=8", |b| { let segments = rvg.generate_vec(&any::(), 8); let s = (sr_tcp().prop_map(|v| v.into_sr()), Just(segments)); - b.iter_proptest_batched(s, set_srh_segments, BATCH_SIZE) + b.iter_proptest_batched(s, set_sr_segments, BATCH_SIZE) }); group.finish() @@ -276,9 +276,9 @@ criterion_group! { config=bench_config(); targets=single_peek_vs_parse, multi_peek_vs_parse, - single_parse_srh_segments_sizes, - multi_parse_upto_variable_srh, - set_srh_segments_sizes, + single_parse_sr_segments_sizes, + multi_parse_upto_variable_sr, + set_sr_segments_sizes, deparse, single_push, multi_push, diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index 0479b39..86af915 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -407,13 +407,13 @@ fn udp( /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv4` and `next_header` is always /// `ProtocolNumbers::Tcp`. -pub fn ip4_tcp() -> impl Strategy { - ip4_tcp_with(fieldmap! {}) +pub fn tcp4() -> impl Strategy { + tcp4_with(fieldmap! {}) } /// Returns a strategy to generate IPv4 TCP packets. /// -/// Similar to `ip4_tcp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `tcp4`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -421,8 +421,8 @@ pub fn ip4_tcp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn ip4_tcp_packet() { -/// proptest!(|(packet in ip4_tcp_with(fieldmap! { +/// fn tcp4_packet() { +/// proptest!(|(packet in tcp4_with(fieldmap! { /// field::ipv4_src => "127.0.0.1".parse(), /// field::tcp_dst_port => 80 /// }))| { @@ -434,7 +434,7 @@ pub fn ip4_tcp() -> impl Strategy { /// }); /// } /// ``` -pub fn ip4_tcp_with(map: StrategyMap) -> impl Strategy { +pub fn tcp4_with(map: StrategyMap) -> impl Strategy { let envelope = ip4(ProtocolNumbers::Tcp, &map); tcp(envelope, &map) } @@ -445,13 +445,13 @@ pub fn ip4_tcp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv4` and `next_header` is always /// `ProtocolNumbers::Udp`. -pub fn ip4_udp() -> impl Strategy { - ip4_udp_with(fieldmap! {}) +pub fn udp4() -> impl Strategy { + udp4_with(fieldmap! {}) } /// Returns a strategy to generate IPv4 UDP packets. /// -/// Similar to `ip4_udp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `udp4`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -459,8 +459,8 @@ pub fn ip4_udp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn ip4_udp_packet() { -/// proptest!(|(packet in ip4_udp_with(fieldmap! { +/// fn udp4_packet() { +/// proptest!(|(packet in udp4_with(fieldmap! { /// field::ipv4_src => "127.0.0.1".parse(), /// field::udp_dst_port => 53, /// }))| { @@ -472,7 +472,7 @@ pub fn ip4_udp() -> impl Strategy { /// }); /// } /// ``` -pub fn ip4_udp_with(map: StrategyMap) -> impl Strategy { +pub fn udp4_with(map: StrategyMap) -> impl Strategy { let envelope = ip4(ProtocolNumbers::Udp, &map); udp(envelope, &map) } @@ -483,13 +483,13 @@ pub fn ip4_udp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv6` and `next_header` is always /// `ProtocolNumbers::Tcp`. -pub fn ip6_tcp() -> impl Strategy { - ip6_tcp_with(fieldmap! {}) +pub fn tcp6() -> impl Strategy { + tcp6_with(fieldmap! {}) } /// Returns a strategy to generate IPv6 TCP packets. /// -/// Similar to `ip6_tcp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `tcp6`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -497,8 +497,8 @@ pub fn ip6_tcp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn ip6_tcp_packet() { -/// proptest!(|(packet in ip6_tcp_with(fieldmap! { +/// fn tcp6_packet() { +/// proptest!(|(packet in tcp6_with(fieldmap! { /// field::ipv6_src => "::1".parse(), /// field::tcp_dst_port => 80, /// }))| { @@ -510,7 +510,7 @@ pub fn ip6_tcp() -> impl Strategy { /// }); /// } /// ``` -pub fn ip6_tcp_with(map: StrategyMap) -> impl Strategy { +pub fn tcp6_with(map: StrategyMap) -> impl Strategy { let envelope = ip6(ProtocolNumbers::Tcp, &map); tcp(envelope, &map) } @@ -521,13 +521,13 @@ pub fn ip6_tcp_with(map: StrategyMap) -> impl Strategy { /// in order for the packet to be internally consistent. For example, /// `ether_type` is always `EtherTypes::Ipv6` and `next_header` is always /// `ProtocolNumbers::Udp`. -pub fn ip6_udp() -> impl Strategy { - ip6_udp_with(fieldmap! {}) +pub fn udp6() -> impl Strategy { + udp6_with(fieldmap! {}) } /// Returns a strategy to generate IPv6 UDP packets. /// -/// Similar to `ip6_udp`. Some fields can be explicitly set through `fieldmap!`. +/// Similar to `udp6`. Some fields can be explicitly set through `fieldmap!`. /// All other fields are randomly generated. See the `field` enum for a list /// of fields that can be set explicitly. /// @@ -535,8 +535,8 @@ pub fn ip6_udp() -> impl Strategy { /// /// ``` /// #[capsule::test] -/// fn ip6_udp_packet() { -/// proptest!(|(packet in ip6_udp_with(fieldmap! { +/// fn udp6_packet() { +/// proptest!(|(packet in udp6_with(fieldmap! { /// field::ipv6_src => "::1".parse(), /// field::udp_dst_port => 53, /// }))| { @@ -548,7 +548,7 @@ pub fn ip6_udp() -> impl Strategy { /// }); /// } /// ``` -pub fn ip6_udp_with(map: StrategyMap) -> impl Strategy { +pub fn udp6_with(map: StrategyMap) -> impl Strategy { let envelope = ip6(ProtocolNumbers::Udp, &map); udp(envelope, &map) } diff --git a/core/src/testils/rvg.rs b/core/src/testils/rvg.rs index 938d211..3cc8616 100644 --- a/core/src/testils/rvg.rs +++ b/core/src/testils/rvg.rs @@ -86,7 +86,7 @@ mod tests { #[capsule::test] fn gen_ip4_packet() { let mut gen = Rvg::new(); - let packet = gen.generate(&ip4_udp()); + let packet = gen.generate(&udp4()); let ip4 = packet.into_ip4(); assert_eq!(ProtocolNumbers::Udp, ip4.protocol()); } diff --git a/examples/kni/main.rs b/examples/kni/main.rs index 15c9a7b..4838258 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v6::Icmpv6; -use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; +use capsule::packets::ip::v6::{Ip6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::udp::Udp6; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -34,26 +34,26 @@ use tracing::{info, Level}; use tracing_subscriber::fmt; fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { - let ipv6 = packet.parse::()?.parse::()?; + let ip6 = packet.parse::()?.parse::()?; - match ipv6.next_header() { + match ip6.next_header() { ProtocolNumbers::Icmpv6 => { - let icmp = ipv6.parse::>()?; + let icmp = ip6.parse::>()?; let fmt = format!("to kni0: {}", icmp.msg_type()).cyan(); info!("{}", fmt); let _ = kni0.push(icmp); Ok(Postmark::Emit) } ProtocolNumbers::Udp => { - let udp = ipv6.parse::()?; + let udp = ip6.parse::()?; let fmt = format!("you said: {}", str::from_utf8(udp.data())?).bright_blue(); info!("{}", fmt); Ok(Postmark::Drop(udp.reset())) } _ => { - let fmt = format!("not supported: {}", ipv6.next_header()).red(); + let fmt = format!("not supported: {}", ip6.next_header()).red(); info!("{}", fmt); - Ok(Postmark::Drop(ipv6.reset())) + Ok(Postmark::Drop(ip6.reset())) } } } @@ -61,8 +61,8 @@ fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { fn from_kni(packet: Mbuf, cap0: &Outbox) -> Result { let icmp = packet .parse::()? - .parse::()? - .parse::>()?; + .parse::()? + .parse::>()?; let fmt = format!("from kni0: {}", icmp.msg_type()).green(); info!("{}", fmt); diff --git a/examples/nat64/main.rs b/examples/nat64/main.rs index fcac724..eced8fd 100644 --- a/examples/nat64/main.rs +++ b/examples/nat64/main.rs @@ -20,8 +20,8 @@ use anyhow::Result; use bimap::BiMap; use capsule::net::MacAddr; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ipv4; -use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; +use capsule::packets::ip::v4::Ip4; +use capsule::packets::ip::v6::{Ip6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::tcp::{Tcp4, Tcp6}; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -49,7 +49,7 @@ fn map_6to4(addr: Ipv6Addr) -> Ipv4Addr { } /// Looks up the assigned port for an IPv6 source. -fn get_v4_port(mac: MacAddr, ip: Ipv6Addr, port: u16) -> u16 { +fn get_ip4_port(mac: MacAddr, ip: Ipv6Addr, port: u16) -> u16 { static NEXT_PORT: AtomicU16 = AtomicU16::new(1025); let key = (ip, port); @@ -70,32 +70,32 @@ fn nat_6to4(packet: Mbuf, cap1: &Outbox) -> Result { const DST_MAC: MacAddr = MacAddr::new(0x02, 0x00, 0x00, 0xff, 0xff, 0xff); let ethernet = packet.parse::()?; - let v6 = ethernet.parse::()?; - - if v6.next_header() == ProtocolNumbers::Tcp { - let dscp = v6.dscp(); - let ecn = v6.ecn(); - let ttl = v6.hop_limit() - 1; - let protocol = v6.next_header(); - let src_ip = v6.src(); - let dst_ip = map_6to4(v6.dst()); - let src_mac = v6.envelope().src(); - - let mut ethernet = v6.remove()?; + let ip6 = ethernet.parse::()?; + + if ip6.next_header() == ProtocolNumbers::Tcp { + let dscp = ip6.dscp(); + let ecn = ip6.ecn(); + let ttl = ip6.hop_limit() - 1; + let protocol = ip6.next_header(); + let src_ip = ip6.src(); + let dst_ip = map_6to4(ip6.dst()); + let src_mac = ip6.envelope().src(); + + let mut ethernet = ip6.remove()?; ethernet.swap_addresses(); ethernet.set_dst(DST_MAC); - let mut v4 = ethernet.push::()?; - v4.set_dscp(dscp); - v4.set_ecn(ecn); - v4.set_ttl(ttl); - v4.set_protocol(protocol); - v4.set_src(SRC_IP); - v4.set_dst(dst_ip); + let mut ip4 = ethernet.push::()?; + ip4.set_dscp(dscp); + ip4.set_ecn(ecn); + ip4.set_ttl(ttl); + ip4.set_protocol(protocol); + ip4.set_src(SRC_IP); + ip4.set_dst(dst_ip); - let mut tcp = v4.parse::()?; + let mut tcp = ip4.parse::()?; let port = tcp.src_port(); - tcp.set_src_port(get_v4_port(src_mac, src_ip, port)); + tcp.set_src_port(get_ip4_port(src_mac, src_ip, port)); tcp.reconcile_all(); let fmt = format!("{:?}", tcp.envelope()).magenta(); @@ -106,7 +106,7 @@ fn nat_6to4(packet: Mbuf, cap1: &Outbox) -> Result { let _ = cap1.push(tcp); Ok(Postmark::Emit) } else { - Ok(Postmark::Drop(v6.reset())) + Ok(Postmark::Drop(ip6.reset())) } } @@ -127,7 +127,7 @@ fn map_4to6(addr: Ipv4Addr) -> Ipv6Addr { } /// Looks up the IPv6 destination -fn get_v6_dst(port: u16) -> Option<(MacAddr, Ipv6Addr, u16)> { +fn get_ip6_dst(port: u16) -> Option<(MacAddr, Ipv6Addr, u16)> { PORTS .lock() .unwrap() @@ -137,31 +137,32 @@ fn get_v6_dst(port: u16) -> Option<(MacAddr, Ipv6Addr, u16)> { fn nat_4to6(packet: Mbuf, cap0: &Outbox) -> Result { let ethernet = packet.parse::()?; - let v4 = ethernet.parse::()?; + let ip4 = ethernet.parse::()?; - if v4.protocol() == ProtocolNumbers::Tcp && v4.fragment_offset() == 0 && !v4.more_fragments() { - let tcp = v4.peek::()?; + if ip4.protocol() == ProtocolNumbers::Tcp && ip4.fragment_offset() == 0 && !ip4.more_fragments() + { + let tcp = ip4.peek::()?; - if let Some((dst_mac, dst_ip, port)) = get_v6_dst(tcp.dst_port()) { - let dscp = v4.dscp(); - let ecn = v4.ecn(); - let next_header = v4.protocol(); - let hop_limit = v4.ttl() - 1; - let src_ip = map_4to6(v4.src()); + if let Some((dst_mac, dst_ip, port)) = get_ip6_dst(tcp.dst_port()) { + let dscp = ip4.dscp(); + let ecn = ip4.ecn(); + let next_header = ip4.protocol(); + let hop_limit = ip4.ttl() - 1; + let src_ip = map_4to6(ip4.src()); - let mut ethernet = v4.remove()?; + let mut ethernet = ip4.remove()?; ethernet.swap_addresses(); ethernet.set_dst(dst_mac); - let mut v6 = ethernet.push::()?; - v6.set_dscp(dscp); - v6.set_ecn(ecn); - v6.set_next_header(next_header); - v6.set_hop_limit(hop_limit); - v6.set_src(src_ip); - v6.set_dst(dst_ip); + let mut ip6 = ethernet.push::()?; + ip6.set_dscp(dscp); + ip6.set_ecn(ecn); + ip6.set_next_header(next_header); + ip6.set_hop_limit(hop_limit); + ip6.set_src(src_ip); + ip6.set_dst(dst_ip); - let mut tcp = v6.parse::()?; + let mut tcp = ip6.parse::()?; tcp.set_dst_port(port); tcp.reconcile_all(); @@ -173,10 +174,10 @@ fn nat_4to6(packet: Mbuf, cap0: &Outbox) -> Result { let _ = cap0.push(tcp); Ok(Postmark::Emit) } else { - Ok(Postmark::Drop(v4.reset())) + Ok(Postmark::Drop(ip4.reset())) } } else { - Ok(Postmark::Drop(v4.reset())) + Ok(Postmark::Drop(ip4.reset())) } } diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index acbf9cd..1af2e39 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; -use capsule::packets::ip::v4::Ipv4; +use capsule::packets::ip::v4::Ip4; use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Outbox, Runtime}; use signal_hook::consts; @@ -37,14 +37,14 @@ fn reply_echo(packet: Mbuf, cap0: &Outbox) -> Result { reply.set_src(ethernet.dst()); reply.set_dst(ethernet.src()); - let ipv4 = ethernet.peek::()?; - let mut reply = reply.push::()?; - reply.set_src(ipv4.dst()); - reply.set_dst(ipv4.src()); + let ip4 = ethernet.peek::()?; + let mut reply = reply.push::()?; + reply.set_src(ip4.dst()); + reply.set_dst(ip4.src()); reply.set_ttl(255); - let request = ipv4.peek::()?; - let mut reply = reply.push::()?; + let request = ip4.peek::>()?; + let mut reply = reply.push::>()?; reply.set_identifier(request.identifier()); reply.set_seq_no(request.seq_no()); reply.set_data(request.data())?; diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index cd768cc..cd3baf6 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -18,8 +18,8 @@ use anyhow::{anyhow, Result}; use capsule::packets::ethernet::{EtherTypes, Ethernet}; -use capsule::packets::ip::v4::Ipv4; -use capsule::packets::ip::v6::Ipv6; +use capsule::packets::ip::v4::Ip4; +use capsule::packets::ip::v6::Ip6; use capsule::packets::ip::IpPacket; use capsule::packets::tcp::{Tcp, Tcp4, Tcp6}; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -39,31 +39,31 @@ fn dump_pkt(packet: Mbuf) -> Result { info!("{}", fmt); match ethernet.ether_type() { - EtherTypes::Ipv4 => dump_v4(ðernet), - EtherTypes::Ipv6 => dump_v6(ðernet), + EtherTypes::Ipv4 => dump_ip4(ðernet), + EtherTypes::Ipv6 => dump_ip6(ðernet), _ => Err(anyhow!("not v4 or v6.")), }?; Ok(Postmark::Drop(ethernet.reset())) } -fn dump_v4(ethernet: &Ethernet) -> Result<()> { - let v4 = ethernet.peek::()?; - let fmt = format!("{:?}", v4).yellow(); +fn dump_ip4(ethernet: &Ethernet) -> Result<()> { + let ip4 = ethernet.peek::()?; + let fmt = format!("{:?}", ip4).yellow(); info!("{}", fmt); - let tcp = v4.peek::()?; + let tcp = ip4.peek::()?; dump_tcp(&tcp); Ok(()) } -fn dump_v6(ethernet: &Ethernet) -> Result<()> { - let v6 = ethernet.peek::()?; - let fmt = format!("{:?}", v6).cyan(); +fn dump_ip6(ethernet: &Ethernet) -> Result<()> { + let ip6 = ethernet.peek::()?; + let fmt = format!("{:?}", ip6).cyan(); info!("{}", fmt); - let tcp = v6.peek::()?; + let tcp = ip6.peek::()?; dump_tcp(&tcp); Ok(()) diff --git a/examples/syn-flood/main.rs b/examples/syn-flood/main.rs index 8f9accb..865517f 100644 --- a/examples/syn-flood/main.rs +++ b/examples/syn-flood/main.rs @@ -20,7 +20,7 @@ use anyhow::Result; use async_io::Timer; use capsule::net::MacAddr; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ipv4; +use capsule::packets::ip::v4::Ip4; use capsule::packets::tcp::Tcp4; use capsule::packets::{Mbuf, Packet}; use capsule::runtime::{self, Outbox, Runtime}; @@ -53,11 +53,11 @@ async fn syn_flood(src_mac: MacAddr, cap0: Outbox, term: Arc) { ethernet.set_src(src_mac); ethernet.set_dst(dst_mac); - let mut v4 = ethernet.push::()?; - v4.set_src(rand::random::().into()); - v4.set_dst(dst_ip); + let mut ip4 = ethernet.push::()?; + ip4.set_src(rand::random::().into()); + ip4.set_dst(dst_ip); - let mut tcp = v4.push::()?; + let mut tcp = ip4.push::()?; tcp.set_syn(); tcp.set_seq_no(1); tcp.set_window(10); From c7c9f1dd03357a64384f04d4d100391175f1cd1a Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Sat, 8 Jan 2022 01:54:29 -0500 Subject: [PATCH 17/18] remove ip4 and ip6 alias --- bench/packets.rs | 30 +++++------ core/src/packets/icmp/v4/echo_reply.rs | 6 +-- core/src/packets/icmp/v4/echo_request.rs | 6 +-- core/src/packets/icmp/v4/mod.rs | 28 +++++------ core/src/packets/icmp/v4/redirect.rs | 14 +++--- core/src/packets/icmp/v4/time_exceeded.rs | 14 +++--- core/src/packets/icmp/v6/echo_reply.rs | 6 +-- core/src/packets/icmp/v6/echo_request.rs | 6 +-- core/src/packets/icmp/v6/mod.rs | 28 +++++------ core/src/packets/icmp/v6/ndp/mod.rs | 50 +++++++++---------- .../packets/icmp/v6/ndp/neighbor_advert.rs | 6 +-- .../packets/icmp/v6/ndp/neighbor_solicit.rs | 6 +-- .../icmp/v6/ndp/options/link_layer_addr.rs | 10 ++-- core/src/packets/icmp/v6/ndp/options/mtu.rs | 10 ++-- .../icmp/v6/ndp/options/prefix_info.rs | 10 ++-- .../packets/icmp/v6/ndp/options/redirected.rs | 6 +-- core/src/packets/icmp/v6/ndp/redirect.rs | 10 ++-- core/src/packets/icmp/v6/ndp/router_advert.rs | 6 +-- .../src/packets/icmp/v6/ndp/router_solicit.rs | 6 +-- core/src/packets/icmp/v6/time_exceeded.rs | 10 ++-- core/src/packets/icmp/v6/too_big.rs | 10 ++-- core/src/packets/icmp/v6/unreachable.rs | 10 ++-- core/src/packets/ip/v4.rs | 17 +++---- core/src/packets/ip/v6/fragment.rs | 22 ++++---- core/src/packets/ip/v6/mod.rs | 15 +++--- core/src/packets/ip/v6/srh.rs | 36 ++++++------- core/src/packets/mod.rs | 16 +++--- core/src/packets/tcp.rs | 27 +++++----- core/src/packets/udp.rs | 21 ++++---- core/src/testils/packet.rs | 20 ++++---- core/src/testils/proptest/strategy.rs | 26 +++++----- examples/kni/main.rs | 10 ++-- examples/nat64/main.rs | 12 ++--- examples/ping4d/main.rs | 10 ++-- examples/pktdump/main.rs | 8 +-- examples/syn-flood/main.rs | 4 +- 36 files changed, 262 insertions(+), 270 deletions(-) diff --git a/bench/packets.rs b/bench/packets.rs index da63e79..7f8cca7 100644 --- a/bench/packets.rs +++ b/bench/packets.rs @@ -19,8 +19,8 @@ use anyhow::Result; use capsule::fieldmap; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ip4; -use capsule::packets::ip::v6::{Ip6, SegmentRouting}; +use capsule::packets::ip::v4::Ipv4; +use capsule::packets::ip::v6::{Ipv6, SegmentRouting}; use capsule::packets::udp::Udp4; use capsule::packets::{Mbuf, Packet}; use capsule::testils::criterion::BencherExt; @@ -32,11 +32,11 @@ use std::net::Ipv6Addr; const BATCH_SIZE: usize = 500; -fn single_parse_udp(ip4: Ip4) -> Udp4 { +fn single_parse_udp(ip4: Ipv4) -> Udp4 { ip4.parse::().unwrap() } -fn single_peek_udp(ip4: Ip4) -> Ip4 { +fn single_peek_udp(ip4: Ipv4) -> Ipv4 { ip4.peek::().unwrap(); ip4 } @@ -66,13 +66,13 @@ fn single_peek_vs_parse(c: &mut Criterion) { fn multi_parse_udp(mbuf: Mbuf) -> Udp4 { let ethernet = mbuf.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); ip4.parse::().unwrap() } fn multi_peek_udp(mbuf: Mbuf) -> Mbuf { let ethernet = mbuf.peek::().unwrap(); - let ip4 = ethernet.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); ip4.peek::().unwrap(); mbuf } @@ -94,8 +94,8 @@ fn multi_peek_vs_parse(c: &mut Criterion) { group.finish() } -fn single_parse_sr(ip6: Ip6) -> SegmentRouting { - ip6.parse::>().unwrap() +fn single_parse_sr(ip6: Ipv6) -> SegmentRouting { + ip6.parse::>().unwrap() } #[capsule::bench(mempool_capacity = 511)] @@ -135,10 +135,10 @@ fn single_parse_sr_segments_sizes(c: &mut Criterion) { group.finish() } -fn multi_parse_sr(mbuf: Mbuf) -> SegmentRouting { +fn multi_parse_sr(mbuf: Mbuf) -> SegmentRouting { let ethernet = mbuf.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - ip6.parse::>().unwrap() + let ip6 = ethernet.parse::().unwrap(); + ip6.parse::>().unwrap() } #[capsule::bench(mempool_capacity = 511)] @@ -177,7 +177,7 @@ fn reset(c: &mut Criterion) { fn multi_push_udp(mbuf: Mbuf) -> Udp4 { let ethernet = mbuf.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); ip4.push::().unwrap() } @@ -189,7 +189,7 @@ fn multi_push(c: &mut Criterion) { }); } -fn single_push_udp(ip4: Ip4) -> Udp4 { +fn single_push_udp(ip4: Ipv4) -> Udp4 { ip4.push::().unwrap() } @@ -204,7 +204,7 @@ fn single_push(c: &mut Criterion) { }); } -fn single_remove_udp(udp: Udp4) -> Ip4 { +fn single_remove_udp(udp: Udp4) -> Ipv4 { udp.remove().unwrap() } @@ -230,7 +230,7 @@ fn multi_remove(c: &mut Criterion) { }); } -fn set_sr_segments(mut args: (SegmentRouting, Vec)) -> Result<()> { +fn set_sr_segments(mut args: (SegmentRouting, Vec)) -> Result<()> { args.0.set_segments(&args.1) } diff --git a/core/src/packets/icmp/v4/echo_reply.rs b/core/src/packets/icmp/v4/echo_reply.rs index dc6c482..3c99c15 100644 --- a/core/src/packets/icmp/v4/echo_reply.rs +++ b/core/src/packets/icmp/v4/echo_reply.rs @@ -222,7 +222,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; #[test] @@ -234,8 +234,8 @@ mod tests { fn push_and_set_echo_reply() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut echo = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut echo = ip4.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/echo_request.rs b/core/src/packets/icmp/v4/echo_request.rs index 0d8412d..73f8c27 100644 --- a/core/src/packets/icmp/v4/echo_request.rs +++ b/core/src/packets/icmp/v4/echo_request.rs @@ -223,7 +223,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; #[test] @@ -235,8 +235,8 @@ mod tests { fn push_and_set_echo_request() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut echo = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut echo = ip4.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index 3ac2fdc..368ec99 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -449,7 +449,7 @@ pub trait Icmpv4Packet { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_UDP_PACKET}; @@ -462,8 +462,8 @@ mod tests { fn parse_icmpv4_packet() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); - let icmp4 = ip4.parse::>().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let icmp4 = ip4.parse::>().unwrap(); // parses the generic header assert_eq!(Icmpv4Types::EchoRequest, icmp4.msg_type()); @@ -471,7 +471,7 @@ mod tests { assert_eq!(0x2a5c, icmp4.checksum()); // downcasts to specific message - let echo = icmp4.downcast::>().unwrap(); + let echo = icmp4.downcast::>().unwrap(); assert_eq!(Icmpv4Types::EchoRequest, echo.msg_type()); assert_eq!(0, echo.code()); assert_eq!(0x2a5c, echo.checksum()); @@ -480,34 +480,34 @@ mod tests { // also can one-step parse let ip4 = echo.deparse(); - assert!(ip4.parse::>().is_ok()); + assert!(ip4.parse::>().is_ok()); } #[capsule::test] fn parse_wrong_icmpv4_type() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); - let icmp4 = ip4.parse::>().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let icmp4 = ip4.parse::>().unwrap(); - assert!(icmp4.downcast::>().is_err()); + assert!(icmp4.downcast::>().is_err()); } #[capsule::test] fn parse_non_icmpv4_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); - assert!(ip4.parse::>().is_err()); + assert!(ip4.parse::>().is_err()); } #[capsule::test] fn compute_checksum() { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); - let mut icmp4 = ip4.parse::>().unwrap(); + let ip4 = ethernet.parse::().unwrap(); + let mut icmp4 = ip4.parse::>().unwrap(); let expected = icmp4.checksum(); // no payload change but force a checksum recompute anyway @@ -519,8 +519,8 @@ mod tests { fn push_icmpv4_header_without_body() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); - assert!(ip4.push::>().is_err()); + assert!(ip4.push::>().is_err()); } } diff --git a/core/src/packets/icmp/v4/redirect.rs b/core/src/packets/icmp/v4/redirect.rs index 76d8f4f..570cdb7 100644 --- a/core/src/packets/icmp/v4/redirect.rs +++ b/core/src/packets/icmp/v4/redirect.rs @@ -219,7 +219,7 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; @@ -232,10 +232,10 @@ mod tests { fn push_and_set_redirect() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let tcp_len = ip4.payload_len(); - let mut redirect = ip4.push::>().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of() + tcp_len, redirect.payload_len()); @@ -259,8 +259,8 @@ mod tests { // starts with buffer larger than min MTU of 68 bytes. let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut redirect = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert!(redirect.data_len() > IPV4_MIN_MTU); redirect.reconcile_all(); @@ -272,8 +272,8 @@ mod tests { // starts with buffer smaller than min MTU of 68 bytes. let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut redirect = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut redirect = ip4.push::>().unwrap(); assert!(redirect.data_len() < IPV4_MIN_MTU); redirect.reconcile_all(); diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs index 5391706..9e9a1ba 100644 --- a/core/src/packets/icmp/v4/time_exceeded.rs +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -183,7 +183,7 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV4_TCP_PACKET; @@ -196,10 +196,10 @@ mod tests { fn push_and_set_time_exceeded() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let tcp_len = ip4.payload_len(); - let mut exceeded = ip4.push::>().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -222,8 +222,8 @@ mod tests { // starts with a buffer with a message body larger than min MTU. let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut exceeded = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert!(exceeded.data_len() > IPV4_MIN_MTU); exceeded.reconcile_all(); @@ -235,8 +235,8 @@ mod tests { // starts with a buffer with a message body smaller than min MTU. let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); - let mut exceeded = ip4.push::>().unwrap(); + let ip4 = ethernet.push::().unwrap(); + let mut exceeded = ip4.push::>().unwrap(); assert!(exceeded.data_len() < IPV4_MIN_MTU); exceeded.reconcile_all(); diff --git a/core/src/packets/icmp/v6/echo_reply.rs b/core/src/packets/icmp/v6/echo_reply.rs index 9664106..76254ff 100644 --- a/core/src/packets/icmp/v6/echo_reply.rs +++ b/core/src/packets/icmp/v6/echo_reply.rs @@ -218,7 +218,7 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -230,8 +230,8 @@ mod tests { fn push_and_set_echo_reply() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut echo = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut echo = ip6.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/echo_request.rs b/core/src/packets/icmp/v6/echo_request.rs index 08e357e..5208481 100644 --- a/core/src/packets/icmp/v6/echo_request.rs +++ b/core/src/packets/icmp/v6/echo_request.rs @@ -219,7 +219,7 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -231,8 +231,8 @@ mod tests { fn push_and_set_echo_request() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut echo = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut echo = ip6.push::>().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index 03a1b16..c0551aa 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -500,7 +500,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::RouterAdvertisement; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; use crate::testils::byte_arrays::{ICMPV6_PACKET, IPV6_TCP_PACKET, ROUTER_ADVERT_PACKET}; @@ -513,8 +513,8 @@ mod tests { fn parse_icmpv6_packet() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let icmp6 = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let icmp6 = ip6.parse::>().unwrap(); // parses the generic header assert_eq!(Icmpv6Types::RouterAdvertisement, icmp6.msg_type()); @@ -522,7 +522,7 @@ mod tests { assert_eq!(0xf50c, icmp6.checksum()); // downcasts to specific message - let advert = icmp6.downcast::>().unwrap(); + let advert = icmp6.downcast::>().unwrap(); assert_eq!(Icmpv6Types::RouterAdvertisement, advert.msg_type()); assert_eq!(0, advert.code()); assert_eq!(0xf50c, advert.checksum()); @@ -535,34 +535,34 @@ mod tests { // also can one-step parse let ip6 = advert.deparse(); - assert!(ip6.parse::>().is_ok()); + assert!(ip6.parse::>().is_ok()); } #[capsule::test] fn parse_wrong_icmpv6_type() { let packet = Mbuf::from_bytes(&ICMPV6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let icmp6 = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let icmp6 = ip6.parse::>().unwrap(); - assert!(icmp6.downcast::>().is_err()); + assert!(icmp6.downcast::>().is_err()); } #[capsule::test] fn parse_non_icmpv6_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ip6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn compute_checksum() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut icmp6 = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut icmp6 = ip6.parse::>().unwrap(); let expected = icmp6.checksum(); // no payload change but force a checksum recompute anyway @@ -574,8 +574,8 @@ mod tests { fn push_icmpv6_header_without_body() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); - assert!(ip6.push::>().is_err()); + assert!(ip6.push::>().is_err()); } } diff --git a/core/src/packets/icmp/v6/ndp/mod.rs b/core/src/packets/icmp/v6/ndp/mod.rs index 01977c5..9de511d 100644 --- a/core/src/packets/icmp/v6/ndp/mod.rs +++ b/core/src/packets/icmp/v6/ndp/mod.rs @@ -552,15 +552,15 @@ pub trait NdpOption<'a> { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; #[capsule::test] fn iterate_immutable_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); let mut prefix = false; let mut mtu = false; @@ -588,8 +588,8 @@ mod tests { fn invalid_ndp_option_length() { let packet = Mbuf::from_bytes(&INVALID_OPTION_LENGTH).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); assert!(advert.options_iter().next().is_err()); } @@ -598,8 +598,8 @@ mod tests { fn downcast_immutable_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.peek::().unwrap(); - let ip6 = ethernet.peek::().unwrap(); - let advert = ip6.peek::>().unwrap(); + let ip6 = ethernet.peek::().unwrap(); + let advert = ip6.peek::>().unwrap(); let mut iter = advert.options_iter(); @@ -616,8 +616,8 @@ mod tests { fn iterate_mutable_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut prefix = false; let mut mtu = false; @@ -646,8 +646,8 @@ mod tests { fn downcast_mutable_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -664,8 +664,8 @@ mod tests { fn modify_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -683,8 +683,8 @@ mod tests { fn prepend_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut target = options.prepend::>().unwrap(); target.set_option_type_target(); @@ -699,8 +699,8 @@ mod tests { fn append_ndp_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut target = options.append::>().unwrap(); target.set_option_type_target(); @@ -723,8 +723,8 @@ mod tests { fn retain_ndp_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let _ = options.retain(|option| option.downcast::>().is_ok()); @@ -754,8 +754,8 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut iter = advert.options_iter(); advert.set_code(0); @@ -775,8 +775,8 @@ mod tests { fn cannot_mutate_immutable_option() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let advert = ip6.parse::>().unwrap(); let mut iter = advert.options_iter(); let mut option = iter.next().unwrap().unwrap(); @@ -802,8 +802,8 @@ mod tests { fn cannot_mutate_options_while_iterating_options() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs index a178d16..548a80f 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs @@ -271,7 +271,7 @@ impl Default for NeighborAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -283,8 +283,8 @@ mod tests { fn push_and_set_neighbor_advertisement() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(NeighborAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs index d27bdd2..fe6b2ed 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs @@ -186,7 +186,7 @@ impl Default for NeighborSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -198,8 +198,8 @@ mod tests { fn push_and_set_neighbor_solicitation() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut solicit = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut solicit = ip6.push::>().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(NeighborSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs index 0b689e7..38b979d 100644 --- a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs +++ b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs @@ -192,7 +192,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -205,8 +205,8 @@ mod tests { fn parse_link_layer_address() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -229,8 +229,8 @@ mod tests { fn push_and_set_link_layer_address() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut lla = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/mtu.rs b/core/src/packets/icmp/v6/ndp/options/mtu.rs index 335cee0..d75e689 100644 --- a/core/src/packets/icmp/v6/ndp/options/mtu.rs +++ b/core/src/packets/icmp/v6/ndp/options/mtu.rs @@ -172,7 +172,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -185,8 +185,8 @@ mod tests { fn parse_mtu() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -209,8 +209,8 @@ mod tests { fn push_and_set_mtu() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut mtu = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs index 0ab6068..834d834 100644 --- a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs +++ b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs @@ -310,7 +310,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement}; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Packet; use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET; @@ -323,8 +323,8 @@ mod tests { fn parse_prefix_information() { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut advert = ip6.parse::>().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -355,8 +355,8 @@ mod tests { fn push_and_set_prefix_information() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); let mut options = advert.options_mut(); let mut prefix = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/redirected.rs b/core/src/packets/icmp/v6/ndp/options/redirected.rs index e755fbe..37d49a8 100644 --- a/core/src/packets/icmp/v6/ndp/options/redirected.rs +++ b/core/src/packets/icmp/v6/ndp/options/redirected.rs @@ -234,7 +234,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::{NdpPacket, Redirect}; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Packet; #[test] @@ -249,8 +249,8 @@ mod tests { let packet = Mbuf::from_bytes(&data).unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); let mut options = redirect.options_mut(); let mut header = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 865a63d..8c00bfa 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -242,7 +242,7 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -254,8 +254,8 @@ mod tests { fn push_and_set_redirect() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of(), redirect.payload_len()); @@ -276,8 +276,8 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut redirect = ip6.push::>().unwrap(); let mut options = redirect.options_mut(); let _ = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/router_advert.rs b/core/src/packets/icmp/v6/ndp/router_advert.rs index a2d4904..98a76d1 100644 --- a/core/src/packets/icmp/v6/ndp/router_advert.rs +++ b/core/src/packets/icmp/v6/ndp/router_advert.rs @@ -302,7 +302,7 @@ struct RouterAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -314,8 +314,8 @@ mod tests { fn push_and_set_router_advertisement() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut advert = ip6.push::>().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(RouterAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/router_solicit.rs b/core/src/packets/icmp/v6/ndp/router_solicit.rs index 8092ab6..5f8987e 100644 --- a/core/src/packets/icmp/v6/ndp/router_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/router_solicit.rs @@ -144,7 +144,7 @@ struct RouterSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -156,8 +156,8 @@ mod tests { fn push_and_set_router_solicitation() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut solicit = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut solicit = ip6.push::>().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(RouterSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index 817d21f..b60b668 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -164,7 +164,7 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -177,10 +177,10 @@ mod tests { fn push_and_set_time_exceeded() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut exceeded = ip6.push::>().unwrap(); + let mut exceeded = ip6.push::>().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -203,12 +203,12 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut exceeded = ip6.push::>().unwrap(); + let mut exceeded = ip6.push::>().unwrap(); assert!(exceeded.mbuf().data_len() > max_len); exceeded.reconcile_all(); diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index 32e2363..111fd93 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -189,7 +189,7 @@ struct PacketTooBigBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -202,10 +202,10 @@ mod tests { fn push_and_set_packet_too_big() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut too_big = ip6.push::>().unwrap(); + let mut too_big = ip6.push::>().unwrap(); assert_eq!(4, too_big.header_len()); assert_eq!(PacketTooBigBody::size_of() + tcp_len, too_big.payload_len()); @@ -225,12 +225,12 @@ mod tests { // starts with a buffer. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut too_big = ip6.push::>().unwrap(); + let mut too_big = ip6.push::>().unwrap(); assert!(too_big.mbuf().data_len() > max_len); too_big.reconcile_all(); diff --git a/core/src/packets/icmp/v6/unreachable.rs b/core/src/packets/icmp/v6/unreachable.rs index 1c44fe5..48c00e1 100644 --- a/core/src/packets/icmp/v6/unreachable.rs +++ b/core/src/packets/icmp/v6/unreachable.rs @@ -164,7 +164,7 @@ struct DestinationUnreachableBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; use crate::testils::byte_arrays::IPV6_TCP_PACKET; @@ -177,10 +177,10 @@ mod tests { fn push_and_set_destination_unreachable() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut unreachable = ip6.push::>().unwrap(); + let mut unreachable = ip6.push::>().unwrap(); assert_eq!(4, unreachable.header_len()); assert_eq!( @@ -203,12 +203,12 @@ mod tests { // starts with a buffer larger than min MTU. let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut unreachable = ip6.push::>().unwrap(); + let mut unreachable = ip6.push::>().unwrap(); assert!(unreachable.mbuf().data_len() > max_len); unreachable.reconcile_all(); diff --git a/core/src/packets/ip/v4.rs b/core/src/packets/ip/v4.rs index e7cac00..4006ad7 100644 --- a/core/src/packets/ip/v4.rs +++ b/core/src/packets/ip/v4.rs @@ -143,7 +143,7 @@ const FLAGS_MF: u16be = u16be(u16::to_be(0b0010_0000_0000_0000)); /// [IETF RFC 791]: https://tools.ietf.org/html/rfc791#section-3.1 /// [IETF RFC 2474]: https://tools.ietf.org/html/rfc2474 /// [IETF RFC 3168]: https://tools.ietf.org/html/rfc3168 -pub struct Ipv4 { +pub struct Ipv4 { envelope: E, header: NonNull, offset: usize, @@ -579,9 +579,6 @@ impl Ipv4Packet for Ipv4 {} /// Used as a trait bound for packets succeding Ipv4. pub trait Ipv4Packet: IpPacket {} -/// A type alias for an Ethernet IPv4 packet. -pub type Ip4 = Ipv4; - /// IPv4 header. /// /// The header only include the fixed portion of the IPv4 header. @@ -634,7 +631,7 @@ mod tests { fn parse_ipv4_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); assert_eq!(4, ip4.version()); assert_eq!(5, ip4.ihl()); @@ -657,14 +654,14 @@ mod tests { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - assert!(ethernet.parse::().is_err()); + assert!(ethernet.parse::().is_err()); } #[capsule::test] fn parse_ipv4_setter_checks() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ip4 = ethernet.parse::().unwrap(); + let mut ip4 = ethernet.parse::().unwrap(); // Fields ip4.set_ihl(ip4.ihl()); @@ -699,7 +696,7 @@ mod tests { fn push_ipv4_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); assert_eq!(4, ip4.version()); assert_eq!(Ipv4Header::size_of(), ip4.len()); @@ -715,7 +712,7 @@ mod tests { let _ = packet.extend(0, 2000); let ethernet = packet.push::().unwrap(); - let mut ip4 = ethernet.push::().unwrap(); + let mut ip4 = ethernet.push::().unwrap(); // can't truncate to less than minimum MTU. assert!(ip4.truncate(60).is_err()); @@ -729,7 +726,7 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ip4 = ethernet.parse::().unwrap(); + let mut ip4 = ethernet.parse::().unwrap(); let expected = ip4.checksum(); // no payload change but force a checksum recompute anyway diff --git a/core/src/packets/ip/v6/fragment.rs b/core/src/packets/ip/v6/fragment.rs index 6ee7080..64e24f7 100644 --- a/core/src/packets/ip/v6/fragment.rs +++ b/core/src/packets/ip/v6/fragment.rs @@ -330,7 +330,7 @@ struct FragmentHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, IPV6_TCP_PACKET}; @@ -343,8 +343,8 @@ mod tests { fn parse_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_FRAGMENT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let frag = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let frag = ip6.parse::>().unwrap(); assert_eq!(ProtocolNumbers::Udp, frag.next_header()); assert_eq!(543, frag.fragment_offset()); @@ -356,17 +356,17 @@ mod tests { fn parse_non_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ip6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn push_and_set_fragment_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); - let mut frag = ip6.push::>().unwrap(); + let ip6 = ethernet.push::().unwrap(); + let mut frag = ip6.push::>().unwrap(); assert_eq!(FragmentHeader::size_of(), frag.len()); assert_eq!(ProtocolNumbers::Ipv6Frag, frag.envelope().next_header()); @@ -393,11 +393,11 @@ mod tests { fn insert_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); let next_header = ip6.next_header(); let payload_len = ip6.payload_len(); - let frag = ip6.push::>().unwrap(); + let frag = ip6.push::>().unwrap(); assert_eq!(ProtocolNumbers::Ipv6Frag, frag.envelope().next_header()); assert_eq!(next_header, frag.next_header()); @@ -408,8 +408,8 @@ mod tests { fn remove_fragment_packet() { let packet = Mbuf::from_bytes(&IPV6_FRAGMENT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let frag = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let frag = ip6.parse::>().unwrap(); let next_header = frag.next_header(); let payload_len = frag.payload_len(); diff --git a/core/src/packets/ip/v6/mod.rs b/core/src/packets/ip/v6/mod.rs index bbbfaf6..796ac1e 100644 --- a/core/src/packets/ip/v6/mod.rs +++ b/core/src/packets/ip/v6/mod.rs @@ -96,7 +96,7 @@ const FLOW: u32be = u32be(u32::to_be(0xfffff)); /// [IETF RFC 8200]: https://tools.ietf.org/html/rfc8200#section-3 /// [IETF RFC 2474]: https://tools.ietf.org/html/rfc2474 /// [IETF RFC 3168]: https://tools.ietf.org/html/rfc3168 -pub struct Ipv6 { +pub struct Ipv6 { envelope: E, header: NonNull, offset: usize, @@ -438,9 +438,6 @@ pub trait Ipv6Packet: IpPacket { fn set_next_header(&mut self, next_header: ProtocolNumber); } -/// A type alias for an Ethernet IPv6 packet. -pub type Ip6 = Ipv6; - /// IPv6 header. #[derive(Clone, Copy, Debug, SizeOf)] #[repr(C)] @@ -482,7 +479,7 @@ mod tests { fn parse_ipv6_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); assert_eq!(6, ip6.version()); assert_eq!(0, ip6.dscp()); @@ -500,14 +497,14 @@ mod tests { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - assert!(ethernet.parse::().is_err()); + assert!(ethernet.parse::().is_err()); } #[capsule::test] fn parse_ipv6_setter_checks() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ip6 = ethernet.parse::().unwrap(); + let mut ip6 = ethernet.parse::().unwrap(); assert_eq!(6, ip6.version()); assert_eq!(0, ip6.dscp()); @@ -525,7 +522,7 @@ mod tests { fn push_ipv6_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip6 = ethernet.push::().unwrap(); + let ip6 = ethernet.push::().unwrap(); assert_eq!(6, ip6.version()); assert_eq!(Ipv6Header::size_of(), ip6.len()); @@ -541,7 +538,7 @@ mod tests { let _ = packet.extend(0, 1800); let ethernet = packet.push::().unwrap(); - let mut ip6 = ethernet.push::().unwrap(); + let mut ip6 = ethernet.push::().unwrap(); // can't truncate to less than minimum MTU. assert!(ip6.truncate(1200).is_err()); diff --git a/core/src/packets/ip/v6/srh.rs b/core/src/packets/ip/v6/srh.rs index 7eb2762..6e8f215 100644 --- a/core/src/packets/ip/v6/srh.rs +++ b/core/src/packets/ip/v6/srh.rs @@ -524,7 +524,7 @@ impl Default for SegmentRoutingHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ip6; + use crate::packets::ip::v6::Ipv6; use crate::packets::ip::ProtocolNumbers; use crate::packets::tcp::{Tcp, Tcp6}; use crate::packets::Mbuf; @@ -539,8 +539,8 @@ mod tests { fn parse_segment_routing_packet() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let srh = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); assert_eq!(ProtocolNumbers::Tcp, srh.next_header()); assert_eq!(6, srh.hdr_ext_len()); @@ -560,17 +560,17 @@ mod tests { fn parse_non_segment_routing_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); - assert!(ip6.parse::>().is_err()); + assert!(ip6.parse::>().is_err()); } #[capsule::test] fn set_segments() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut srh = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut srh = ip6.parse::>().unwrap(); let segment1: Ipv6Addr = "::1".parse().unwrap(); @@ -597,7 +597,7 @@ mod tests { assert!(srh.set_segments(&[]).is_err()); // make sure rest of the packet still valid - let tcp = srh.parse::>>().unwrap(); + let tcp = srh.parse::>>().unwrap(); assert_eq!(3464, tcp.src_port()) } @@ -605,8 +605,8 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let mut srh = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let mut srh = ip6.parse::>().unwrap(); let segment1: Ipv6Addr = "::1".parse().unwrap(); let segment2: Ipv6Addr = "::2".parse().unwrap(); @@ -619,7 +619,7 @@ mod tests { assert_eq!(4, srh.segments().len()); srh.set_segments_left(3); - let mut tcp = srh.parse::>>().unwrap(); + let mut tcp = srh.parse::>>().unwrap(); // Should pass as we're using the hard-coded (and wrong) initial // checksum, as it's 0 given above. @@ -639,7 +639,7 @@ mod tests { assert_eq!(1, srh_ret.segments().len()); srh_ret.set_segments_left(0); - let mut tcp_ret = srh_ret.parse::>>().unwrap(); + let mut tcp_ret = srh_ret.parse::>>().unwrap(); tcp_ret.reconcile_all(); assert_eq!(expected, tcp_ret.checksum()); @@ -647,7 +647,7 @@ mod tests { // is still the same segment. let mut srh_fin = tcp_ret.deparse(); srh_fin.set_segments_left(0); - let mut tcp_fin = srh_fin.parse::>>().unwrap(); + let mut tcp_fin = srh_fin.parse::>>().unwrap(); tcp_fin.reconcile_all(); assert_eq!(expected, tcp_fin.checksum()); } @@ -656,9 +656,9 @@ mod tests { fn insert_segment_routing_packet() { let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); + let ip6 = ethernet.parse::().unwrap(); let ip6_payload_len = ip6.payload_len(); - let srh = ip6.push::>().unwrap(); + let srh = ip6.push::>().unwrap(); assert_eq!(2, srh.hdr_ext_len()); assert_eq!(1, srh.segments().len()); @@ -671,7 +671,7 @@ mod tests { // ipv6 payload is srh payload after push assert_eq!(ip6_payload_len, srh.payload_len()); // make sure rest of the packet still valid - let tcp = srh.parse::>>().unwrap(); + let tcp = srh.parse::>>().unwrap(); assert_eq!(36869, tcp.src_port()); let mut srh = tcp.deparse(); @@ -686,8 +686,8 @@ mod tests { fn remove_segment_routing_packet() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let srh = ip6.parse::>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); let ip6 = srh.remove().unwrap(); // make sure next header is fixed diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index 12bd6be..65df752 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -380,7 +380,7 @@ mod tests { use super::*; use crate::net::MacAddr; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; + use crate::packets::ip::v4::Ipv4; use crate::packets::udp::Udp4; use crate::testils::byte_arrays::IPV4_UDP_PACKET; @@ -390,7 +390,7 @@ mod tests { let len = packet.data_len(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let udp = ip4.parse::().unwrap(); let reset = udp.reset(); @@ -403,7 +403,7 @@ mod tests { let ethernet = packet.peek::().unwrap(); assert_eq!(MacAddr::new(0, 0, 0, 0, 0, 2), ethernet.src()); - let ip4 = ethernet.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); assert_eq!(255, ip4.ttl()); let udp = ip4.peek::().unwrap(); assert_eq!(39376, udp.src_port()); @@ -413,7 +413,7 @@ mod tests { fn parse_and_deparse_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let mut ip4 = ethernet.parse::().unwrap(); + let mut ip4 = ethernet.parse::().unwrap(); ip4.set_ttl(25); let udp = ip4.parse::().unwrap(); @@ -427,7 +427,7 @@ mod tests { fn remove_header_and_payload() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let mut udp = ip4.parse::().unwrap(); assert!(udp.payload_len() > 0); @@ -445,11 +445,11 @@ mod tests { /// borrow through peek. /// /// ``` - /// | let ipv4 = ethernet.peek::().unwrap(); + /// | let ip4 = ethernet.peek::().unwrap(); /// | -------- immutable borrow occurs here /// | ethernet.set_src(MacAddr::UNSPECIFIED); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here - /// | assert!(ipv4.payload_len() > 0); + /// | assert!(ip4.payload_len() > 0); /// | ---- immutable borrow later used here /// ``` #[test] @@ -457,7 +457,7 @@ mod tests { fn cannot_mutate_packet_while_peeking_into_payload() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let mut ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.peek::().unwrap(); + let ip4 = ethernet.peek::().unwrap(); ethernet.set_src(MacAddr::UNSPECIFIED); assert!(ip4.payload_len() > 0); } diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index e8756dc..2eb28d3 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -19,8 +19,8 @@ //! Transmission Control Protocol. use crate::ensure; -use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::Ip6; +use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::{u16be, u32be}; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -624,10 +624,10 @@ impl Packet for Tcp { } /// A type alias for an Ethernet IPv4 TCP packet. -pub type Tcp4 = Tcp; +pub type Tcp4 = Tcp; /// A type alias for an Ethernet IPv6 TCP packet. -pub type Tcp6 = Tcp; +pub type Tcp6 = Tcp; /// TCP header. /// @@ -667,7 +667,6 @@ impl Default for TcpHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; use crate::packets::ip::v6::SegmentRouting; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET, SR_TCP_PACKET}; @@ -682,7 +681,7 @@ mod tests { fn parse_tcp_packet() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let tcp = ip4.parse::().unwrap(); assert_eq!(36869, tcp.src_port()); @@ -708,7 +707,7 @@ mod tests { fn parse_non_tcp_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); assert!(ip4.parse::().is_err()); } @@ -717,7 +716,7 @@ mod tests { fn tcp_flow_v4() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let tcp = ip4.parse::().unwrap(); let flow = tcp.flow(); @@ -732,9 +731,9 @@ mod tests { fn tcp_flow_v6() { let packet = Mbuf::from_bytes(&SR_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip6 = ethernet.parse::().unwrap(); - let srh = ip6.parse::>().unwrap(); - let tcp = srh.parse::>>().unwrap(); + let ip6 = ethernet.parse::().unwrap(); + let srh = ip6.parse::>().unwrap(); + let tcp = srh.parse::>>().unwrap(); let flow = tcp.flow(); assert_eq!("2001:db8:85a3::1", flow.src_ip().to_string()); @@ -748,7 +747,7 @@ mod tests { fn set_src_dst_ip() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let mut tcp = ip4.parse::().unwrap(); let old_checksum = tcp.checksum(); @@ -771,7 +770,7 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let mut tcp = ip4.parse::().unwrap(); let expected = tcp.checksum(); @@ -784,7 +783,7 @@ mod tests { fn push_tcp_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); let tcp = ip4.push::().unwrap(); assert_eq!(TcpHeader::size_of(), tcp.len()); diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 83bcd7a..256c686 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -19,8 +19,8 @@ //! User Datagram Protocol. use crate::ensure; -use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::Ip6; +use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v6::Ipv6; use crate::packets::ip::{Flow, IpPacket, ProtocolNumbers}; use crate::packets::types::u16be; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -370,10 +370,10 @@ impl Packet for Udp { } /// A type alias for an Ethernet IPv4 UDP packet. -pub type Udp4 = Udp; +pub type Udp4 = Udp; /// A type alias for an Ethernet IPv6 UDP packet. -pub type Udp6 = Udp; +pub type Udp6 = Udp; /// UDP header. #[derive(Clone, Copy, Debug, Default, SizeOf)] @@ -389,7 +389,6 @@ struct UdpHeader { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ip4; use crate::packets::Mbuf; use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -403,7 +402,7 @@ mod tests { fn parse_udp_packet() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let udp = ip4.parse::().unwrap(); assert_eq!(39376, udp.src_port()); @@ -417,7 +416,7 @@ mod tests { fn parse_non_udp_packet() { let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); assert!(ip4.parse::().is_err()); } @@ -426,7 +425,7 @@ mod tests { fn udp_flow_v4() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let udp = ip4.parse::().unwrap(); let flow = udp.flow(); @@ -441,7 +440,7 @@ mod tests { fn set_src_dst_ip() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let mut udp = ip4.parse::().unwrap(); let old_checksum = udp.checksum(); @@ -464,7 +463,7 @@ mod tests { fn compute_checksum() { let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let ip4 = ethernet.parse::().unwrap(); + let ip4 = ethernet.parse::().unwrap(); let mut udp = ip4.parse::().unwrap(); let expected = udp.checksum(); @@ -477,7 +476,7 @@ mod tests { fn push_udp_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let ip4 = ethernet.push::().unwrap(); + let ip4 = ethernet.push::().unwrap(); let udp = ip4.push::().unwrap(); assert_eq!(UdpHeader::size_of(), udp.len()); diff --git a/core/src/testils/packet.rs b/core/src/testils/packet.rs index a6e3dd2..ec04928 100644 --- a/core/src/testils/packet.rs +++ b/core/src/testils/packet.rs @@ -17,8 +17,8 @@ */ use crate::packets::ethernet::Ethernet; -use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::{Ip6, SegmentRouting}; +use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v6::{Ipv6, SegmentRouting}; use crate::packets::tcp::{Tcp, Tcp4, Tcp6}; use crate::packets::udp::{Udp4, Udp6}; use crate::packets::Packet; @@ -37,8 +37,8 @@ pub trait PacketExt: Packet + Sized { } /// Converts the packet into an IPv4 packet. - fn into_ip4(self) -> Ip4 { - self.into_eth().parse::().unwrap() + fn into_ip4(self) -> Ipv4 { + self.into_eth().parse::().unwrap() } /// Converts the packet into a TCP packet inside IPv4. @@ -52,8 +52,8 @@ pub trait PacketExt: Packet + Sized { } /// Converts the packet into an IPv6 packet. - fn into_ip6(self) -> Ip6 { - self.into_eth().parse::().unwrap() + fn into_ip6(self) -> Ipv6 { + self.into_eth().parse::().unwrap() } /// Converts the packet into a TCP packet inside IPv6. @@ -67,13 +67,13 @@ pub trait PacketExt: Packet + Sized { } /// Converts the packet into an IPv6 packet with a SRH extension. - fn into_sr(self) -> SegmentRouting { - self.into_ip6().parse::>().unwrap() + fn into_sr(self) -> SegmentRouting { + self.into_ip6().parse::>().unwrap() } /// Converts the packet into a TCP packet inside IPv6 with a SRH extension. - fn into_sr_tcp(self) -> Tcp> { - self.into_sr().parse::>>().unwrap() + fn into_sr_tcp(self) -> Tcp> { + self.into_sr().parse::>>().unwrap() } } diff --git a/core/src/testils/proptest/strategy.rs b/core/src/testils/proptest/strategy.rs index 86af915..5e08e0a 100644 --- a/core/src/testils/proptest/strategy.rs +++ b/core/src/testils/proptest/strategy.rs @@ -20,8 +20,8 @@ use crate::net::MacAddr; use crate::packets::ethernet::{EtherType, EtherTypes, Ethernet}; -use crate::packets::ip::v4::Ip4; -use crate::packets::ip::v6::{Ip6, Ipv6Packet, SegmentRouting}; +use crate::packets::ip::v4::Ipv4; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet, SegmentRouting}; use crate::packets::ip::{Flow, IpPacket, ProtocolNumber, ProtocolNumbers}; use crate::packets::tcp::Tcp; use crate::packets::udp::Udp; @@ -219,7 +219,7 @@ fn ethernet(ether_type: EtherType, map: &StrategyMap) -> impl Strategy impl Strategy { +fn ip4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy { ( ethernet(EtherTypes::Ipv4, map), map.ipv4_addr(&field::ipv4_src), @@ -245,7 +245,7 @@ fn ip4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy().unwrap(); + let mut packet = packet.push::().unwrap(); packet.set_src(src); packet.set_dst(dst); packet.set_dscp(dscp); @@ -265,7 +265,7 @@ fn ip4(protocol: ProtocolNumber, map: &StrategyMap) -> impl Strategy impl Strategy { +fn ip6(next_header: ProtocolNumber, map: &StrategyMap) -> impl Strategy { ( ethernet(EtherTypes::Ipv6, map), map.ipv6_addr(&field::ipv6_src), @@ -277,7 +277,7 @@ fn ip6(next_header: ProtocolNumber, map: &StrategyMap) -> impl Strategy().unwrap(); + let mut packet = packet.push::().unwrap(); packet.set_src(src); packet.set_dst(dst); packet.set_ecn(ecn); @@ -427,7 +427,7 @@ pub fn tcp4() -> impl Strategy { /// field::tcp_dst_port => 80 /// }))| { /// let packet = packet.parse::().unwrap(); -/// let ip4 = packet.parse::().unwrap(); +/// let ip4 = packet.parse::().unwrap(); /// assert_eq!("127.0.0.1".parse(), ip4.src()); /// let tcp = ip4.parse::().unwrap(); /// assert_eq!(80, tcp.dst_port()); @@ -465,7 +465,7 @@ pub fn udp4() -> impl Strategy { /// field::udp_dst_port => 53, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let ip4 = packet.parse::().unwrap(); +/// let ip4 = packet.parse::().unwrap(); /// prop_assert_eq!("127.0.0.1".parse(), ip4.src()); /// let udp = ip4.parse::().unwrap(); /// prop_assert_eq!(53, udp.dst_port()); @@ -503,7 +503,7 @@ pub fn tcp6() -> impl Strategy { /// field::tcp_dst_port => 80, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let ip6 = packet.parse::().unwrap(); +/// let ip6 = packet.parse::().unwrap(); /// prop_assert_eq!("::1".parse(), ip6.src()); /// let tcp = ip6.parse::().unwrap(); /// prop_assert_eq!(80, tcp.dst_port()); @@ -541,7 +541,7 @@ pub fn udp6() -> impl Strategy { /// field::udp_dst_port => 53, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let ip6 = packet.parse::().unwrap(); +/// let ip6 = packet.parse::().unwrap(); /// prop_assert_eq!("::1".parse(), ip6.src()); /// let udp = ip6.parse::().unwrap(); /// prop_assert_eq!(53, udp.dst_port()); @@ -580,11 +580,11 @@ pub fn sr_tcp() -> impl Strategy { /// field::tcp_dst_port => 80, /// }))| { /// let packet = packet.parse::().unwrap(); -/// let ip6 = packet.parse::().unwrap(); +/// let ip6 = packet.parse::().unwrap(); /// prop_assert_eq!("::1".parse(), ip6.src()); -/// let sr = ip6.parse::>().unwrap(); +/// let sr = ip6.parse::>().unwrap(); /// prop_assert_eq!(2, sr.segments().len()); -/// let tcp = sr.parse::>>().unwrap(); +/// let tcp = sr.parse::>>().unwrap(); /// prop_assert_eq!(80, tcp.dst_port()); /// }); /// } diff --git a/examples/kni/main.rs b/examples/kni/main.rs index 4838258..10cbb51 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v6::Icmpv6; -use capsule::packets::ip::v6::{Ip6, Ipv6Packet}; +use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::udp::Udp6; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -34,11 +34,11 @@ use tracing::{info, Level}; use tracing_subscriber::fmt; fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { - let ip6 = packet.parse::()?.parse::()?; + let ip6 = packet.parse::()?.parse::()?; match ip6.next_header() { ProtocolNumbers::Icmpv6 => { - let icmp = ip6.parse::>()?; + let icmp = ip6.parse::>()?; let fmt = format!("to kni0: {}", icmp.msg_type()).cyan(); info!("{}", fmt); let _ = kni0.push(icmp); @@ -61,8 +61,8 @@ fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { fn from_kni(packet: Mbuf, cap0: &Outbox) -> Result { let icmp = packet .parse::()? - .parse::()? - .parse::>()?; + .parse::()? + .parse::>()?; let fmt = format!("from kni0: {}", icmp.msg_type()).green(); info!("{}", fmt); diff --git a/examples/nat64/main.rs b/examples/nat64/main.rs index eced8fd..132b132 100644 --- a/examples/nat64/main.rs +++ b/examples/nat64/main.rs @@ -20,8 +20,8 @@ use anyhow::Result; use bimap::BiMap; use capsule::net::MacAddr; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ip4; -use capsule::packets::ip::v6::{Ip6, Ipv6Packet}; +use capsule::packets::ip::v4::Ipv4; +use capsule::packets::ip::v6::{Ipv6, Ipv6Packet}; use capsule::packets::ip::ProtocolNumbers; use capsule::packets::tcp::{Tcp4, Tcp6}; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -70,7 +70,7 @@ fn nat_6to4(packet: Mbuf, cap1: &Outbox) -> Result { const DST_MAC: MacAddr = MacAddr::new(0x02, 0x00, 0x00, 0xff, 0xff, 0xff); let ethernet = packet.parse::()?; - let ip6 = ethernet.parse::()?; + let ip6 = ethernet.parse::()?; if ip6.next_header() == ProtocolNumbers::Tcp { let dscp = ip6.dscp(); @@ -85,7 +85,7 @@ fn nat_6to4(packet: Mbuf, cap1: &Outbox) -> Result { ethernet.swap_addresses(); ethernet.set_dst(DST_MAC); - let mut ip4 = ethernet.push::()?; + let mut ip4 = ethernet.push::()?; ip4.set_dscp(dscp); ip4.set_ecn(ecn); ip4.set_ttl(ttl); @@ -137,7 +137,7 @@ fn get_ip6_dst(port: u16) -> Option<(MacAddr, Ipv6Addr, u16)> { fn nat_4to6(packet: Mbuf, cap0: &Outbox) -> Result { let ethernet = packet.parse::()?; - let ip4 = ethernet.parse::()?; + let ip4 = ethernet.parse::()?; if ip4.protocol() == ProtocolNumbers::Tcp && ip4.fragment_offset() == 0 && !ip4.more_fragments() { @@ -154,7 +154,7 @@ fn nat_4to6(packet: Mbuf, cap0: &Outbox) -> Result { ethernet.swap_addresses(); ethernet.set_dst(dst_mac); - let mut ip6 = ethernet.push::()?; + let mut ip6 = ethernet.push::()?; ip6.set_dscp(dscp); ip6.set_ecn(ecn); ip6.set_next_header(next_header); diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 1af2e39..137b108 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -19,7 +19,7 @@ use anyhow::Result; use capsule::packets::ethernet::Ethernet; use capsule::packets::icmp::v4::{EchoReply, EchoRequest}; -use capsule::packets::ip::v4::Ip4; +use capsule::packets::ip::v4::Ipv4; use capsule::packets::{Mbuf, Packet, Postmark}; use capsule::runtime::{self, Outbox, Runtime}; use signal_hook::consts; @@ -37,14 +37,14 @@ fn reply_echo(packet: Mbuf, cap0: &Outbox) -> Result { reply.set_src(ethernet.dst()); reply.set_dst(ethernet.src()); - let ip4 = ethernet.peek::()?; - let mut reply = reply.push::()?; + let ip4 = ethernet.peek::()?; + let mut reply = reply.push::()?; reply.set_src(ip4.dst()); reply.set_dst(ip4.src()); reply.set_ttl(255); - let request = ip4.peek::>()?; - let mut reply = reply.push::>()?; + let request = ip4.peek::>()?; + let mut reply = reply.push::>()?; reply.set_identifier(request.identifier()); reply.set_seq_no(request.seq_no()); reply.set_data(request.data())?; diff --git a/examples/pktdump/main.rs b/examples/pktdump/main.rs index cd3baf6..dcb946a 100644 --- a/examples/pktdump/main.rs +++ b/examples/pktdump/main.rs @@ -18,8 +18,8 @@ use anyhow::{anyhow, Result}; use capsule::packets::ethernet::{EtherTypes, Ethernet}; -use capsule::packets::ip::v4::Ip4; -use capsule::packets::ip::v6::Ip6; +use capsule::packets::ip::v4::Ipv4; +use capsule::packets::ip::v6::Ipv6; use capsule::packets::ip::IpPacket; use capsule::packets::tcp::{Tcp, Tcp4, Tcp6}; use capsule::packets::{Mbuf, Packet, Postmark}; @@ -48,7 +48,7 @@ fn dump_pkt(packet: Mbuf) -> Result { } fn dump_ip4(ethernet: &Ethernet) -> Result<()> { - let ip4 = ethernet.peek::()?; + let ip4 = ethernet.peek::()?; let fmt = format!("{:?}", ip4).yellow(); info!("{}", fmt); @@ -59,7 +59,7 @@ fn dump_ip4(ethernet: &Ethernet) -> Result<()> { } fn dump_ip6(ethernet: &Ethernet) -> Result<()> { - let ip6 = ethernet.peek::()?; + let ip6 = ethernet.peek::()?; let fmt = format!("{:?}", ip6).cyan(); info!("{}", fmt); diff --git a/examples/syn-flood/main.rs b/examples/syn-flood/main.rs index 865517f..fce4154 100644 --- a/examples/syn-flood/main.rs +++ b/examples/syn-flood/main.rs @@ -20,7 +20,7 @@ use anyhow::Result; use async_io::Timer; use capsule::net::MacAddr; use capsule::packets::ethernet::Ethernet; -use capsule::packets::ip::v4::Ip4; +use capsule::packets::ip::v4::Ipv4; use capsule::packets::tcp::Tcp4; use capsule::packets::{Mbuf, Packet}; use capsule::runtime::{self, Outbox, Runtime}; @@ -53,7 +53,7 @@ async fn syn_flood(src_mac: MacAddr, cap0: Outbox, term: Arc) { ethernet.set_src(src_mac); ethernet.set_dst(dst_mac); - let mut ip4 = ethernet.push::()?; + let mut ip4 = ethernet.push::()?; ip4.set_src(rand::random::().into()); ip4.set_dst(dst_ip); From b1e9b9d0fbcc0b35bc9b75b5e04d03c87040f02b Mon Sep 17 00:00:00 2001 From: drunkirishcoder Date: Sat, 8 Jan 2022 19:00:58 -0500 Subject: [PATCH 18/18] simplify icmp and arp typing --- core/src/packets/arp.rs | 63 +++++++++---------- core/src/packets/ethernet.rs | 6 +- core/src/packets/icmp/v4/echo_reply.rs | 7 +-- core/src/packets/icmp/v4/echo_request.rs | 7 +-- core/src/packets/icmp/v4/mod.rs | 25 ++++---- core/src/packets/icmp/v4/redirect.rs | 16 +++-- core/src/packets/icmp/v4/time_exceeded.rs | 16 +++-- core/src/packets/icmp/v6/echo_reply.rs | 7 +-- core/src/packets/icmp/v6/echo_request.rs | 7 +-- core/src/packets/icmp/v6/mod.rs | 31 +++++---- core/src/packets/icmp/v6/ndp/mod.rs | 38 +++++------ .../packets/icmp/v6/ndp/neighbor_advert.rs | 7 +-- .../packets/icmp/v6/ndp/neighbor_solicit.rs | 7 +-- .../icmp/v6/ndp/options/link_layer_addr.rs | 4 +- core/src/packets/icmp/v6/ndp/options/mtu.rs | 4 +- .../icmp/v6/ndp/options/prefix_info.rs | 4 +- .../packets/icmp/v6/ndp/options/redirected.rs | 4 +- core/src/packets/icmp/v6/ndp/redirect.rs | 9 ++- core/src/packets/icmp/v6/ndp/router_advert.rs | 7 +-- .../src/packets/icmp/v6/ndp/router_solicit.rs | 7 +-- core/src/packets/icmp/v6/time_exceeded.rs | 13 ++-- core/src/packets/icmp/v6/too_big.rs | 13 ++-- core/src/packets/icmp/v6/unreachable.rs | 13 ++-- core/src/packets/ip/v4.rs | 10 +-- core/src/packets/ip/v6/fragment.rs | 6 +- core/src/packets/ip/v6/mod.rs | 8 +-- core/src/packets/ip/v6/srh.rs | 6 +- core/src/packets/mod.rs | 14 ++--- core/src/packets/tcp.rs | 12 ++-- core/src/packets/udp.rs | 12 ++-- core/src/runtime/pcap_dump.rs | 10 +-- core/src/testils/byte_arrays.rs | 8 +-- examples/kni/main.rs | 4 +- examples/ping4d/main.rs | 4 +- 34 files changed, 194 insertions(+), 215 deletions(-) diff --git a/core/src/packets/arp.rs b/core/src/packets/arp.rs index 1d69b54..030032d 100644 --- a/core/src/packets/arp.rs +++ b/core/src/packets/arp.rs @@ -84,7 +84,7 @@ use std::ptr::NonNull; /// defined by *P Length*. /// /// [IETF RFC 826]: https://tools.ietf.org/html/rfc826 -pub struct Arp { +pub struct Arp { envelope: E, header: NonNull>, offset: usize, @@ -528,9 +528,6 @@ impl ProtocolAddr for Ipv4Addr { } } -/// A type alias for an Ethernet IPv4 ARP packet. -pub type Arp4 = Arp; - /// ARP header. #[allow(missing_debug_implementations)] #[derive(Copy, SizeOf)] @@ -583,7 +580,7 @@ impl Default for ArpHeader { mod tests { use super::*; use crate::packets::Mbuf; - use crate::testils::byte_arrays::ARP4_PACKET; + use crate::testils::byte_arrays::ARP_PACKET; #[test] fn size_of_arp_header() { @@ -592,48 +589,48 @@ mod tests { #[capsule::test] fn parse_arp_packet() { - let packet = Mbuf::from_bytes(&ARP4_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&ARP_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); - let arp4 = ethernet.parse::().unwrap(); + let arp = ethernet.parse::().unwrap(); - assert_eq!(HardwareTypes::Ethernet, arp4.hardware_type()); - assert_eq!(EtherTypes::Ipv4, arp4.protocol_type()); - assert_eq!(6, arp4.hardware_addr_len()); - assert_eq!(4, arp4.protocol_addr_len()); - assert_eq!(OperationCodes::Request, arp4.operation_code()); - assert_eq!("00:00:00:00:00:01", arp4.sender_hardware_addr().to_string()); - assert_eq!("139.133.217.110", arp4.sender_protocol_addr().to_string()); - assert_eq!("00:00:00:00:00:00", arp4.target_hardware_addr().to_string()); - assert_eq!("139.133.233.2", arp4.target_protocol_addr().to_string()); + assert_eq!(HardwareTypes::Ethernet, arp.hardware_type()); + assert_eq!(EtherTypes::Ipv4, arp.protocol_type()); + assert_eq!(6, arp.hardware_addr_len()); + assert_eq!(4, arp.protocol_addr_len()); + assert_eq!(OperationCodes::Request, arp.operation_code()); + assert_eq!("00:00:00:00:00:01", arp.sender_hardware_addr().to_string()); + assert_eq!("139.133.217.110", arp.sender_protocol_addr().to_string()); + assert_eq!("00:00:00:00:00:00", arp.target_hardware_addr().to_string()); + assert_eq!("139.133.233.2", arp.target_protocol_addr().to_string()); } #[capsule::test] fn push_arp_packet() { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); - let mut arp4 = ethernet.push::().unwrap(); + let mut arp = ethernet.push::().unwrap(); - assert_eq!(ArpHeader::::size_of(), arp4.len()); + assert_eq!(ArpHeader::::size_of(), arp.len()); // make sure types are set properly - assert_eq!(HardwareTypes::Ethernet, arp4.hardware_type()); - assert_eq!(EtherTypes::Ipv4, arp4.protocol_type()); - assert_eq!(6, arp4.hardware_addr_len()); - assert_eq!(4, arp4.protocol_addr_len()); + assert_eq!(HardwareTypes::Ethernet, arp.hardware_type()); + assert_eq!(EtherTypes::Ipv4, arp.protocol_type()); + assert_eq!(6, arp.hardware_addr_len()); + assert_eq!(4, arp.protocol_addr_len()); // check the setters - arp4.set_operation_code(OperationCodes::Reply); - assert_eq!(OperationCodes::Reply, arp4.operation_code()); - arp4.set_sender_hardware_addr(MacAddr::new(0, 0, 0, 0, 0, 1)); - assert_eq!("00:00:00:00:00:01", arp4.sender_hardware_addr().to_string()); - arp4.set_sender_protocol_addr(Ipv4Addr::new(10, 0, 0, 1)); - assert_eq!("10.0.0.1", arp4.sender_protocol_addr().to_string()); - arp4.set_target_hardware_addr(MacAddr::new(0, 0, 0, 0, 0, 2)); - assert_eq!("00:00:00:00:00:02", arp4.target_hardware_addr().to_string()); - arp4.set_target_protocol_addr(Ipv4Addr::new(10, 0, 0, 2)); - assert_eq!("10.0.0.2", arp4.target_protocol_addr().to_string()); + arp.set_operation_code(OperationCodes::Reply); + assert_eq!(OperationCodes::Reply, arp.operation_code()); + arp.set_sender_hardware_addr(MacAddr::new(0, 0, 0, 0, 0, 1)); + assert_eq!("00:00:00:00:00:01", arp.sender_hardware_addr().to_string()); + arp.set_sender_protocol_addr(Ipv4Addr::new(10, 0, 0, 1)); + assert_eq!("10.0.0.1", arp.sender_protocol_addr().to_string()); + arp.set_target_hardware_addr(MacAddr::new(0, 0, 0, 0, 0, 2)); + assert_eq!("00:00:00:00:00:02", arp.target_hardware_addr().to_string()); + arp.set_target_protocol_addr(Ipv4Addr::new(10, 0, 0, 2)); + assert_eq!("10.0.0.2", arp.target_protocol_addr().to_string()); // make sure the ether type is fixed - assert_eq!(EtherTypes::Arp, arp4.envelope().ether_type()); + assert_eq!(EtherTypes::Arp, arp.envelope().ether_type()); } } diff --git a/core/src/packets/ethernet.rs b/core/src/packets/ethernet.rs index 3de8158..67a06b3 100644 --- a/core/src/packets/ethernet.rs +++ b/core/src/packets/ethernet.rs @@ -491,7 +491,7 @@ impl SizeOf for EthernetHeader { #[cfg(test)] mod tests { use super::*; - use crate::testils::byte_arrays::{IPV4_UDP_PACKET, VLAN_DOT1Q_PACKET, VLAN_QINQ_PACKET}; + use crate::testils::byte_arrays::{UDP4_PACKET, VLAN_DOT1Q_PACKET, VLAN_QINQ_PACKET}; #[test] fn size_of_ethernet_header() { @@ -508,7 +508,7 @@ mod tests { #[capsule::test] fn parse_ethernet_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); assert_eq!("00:00:00:00:00:01", ethernet.dst().to_string()); @@ -542,7 +542,7 @@ mod tests { #[capsule::test] fn swap_addresses() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let mut ethernet = packet.parse::().unwrap(); ethernet.swap_addresses(); diff --git a/core/src/packets/icmp/v4/echo_reply.rs b/core/src/packets/icmp/v4/echo_reply.rs index 3c99c15..c1a4e5f 100644 --- a/core/src/packets/icmp/v4/echo_reply.rs +++ b/core/src/packets/icmp/v4/echo_reply.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; -use crate::packets::ip::v4::Ipv4Packet; +use crate::packets::ip::v4::{Ipv4, Ipv4Packet}; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -47,7 +47,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct EchoReply { +pub struct EchoReply { icmp: Icmpv4, body: NonNull, } @@ -222,7 +222,6 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; #[test] @@ -235,7 +234,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut echo = ip4.push::>().unwrap(); + let mut echo = ip4.push::().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/echo_request.rs b/core/src/packets/icmp/v4/echo_request.rs index 73f8c27..480bbec 100644 --- a/core/src/packets/icmp/v4/echo_request.rs +++ b/core/src/packets/icmp/v4/echo_request.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; -use crate::packets::ip::v4::Ipv4Packet; +use crate::packets::ip::v4::{Ipv4, Ipv4Packet}; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -48,7 +48,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct EchoRequest { +pub struct EchoRequest { icmp: Icmpv4, body: NonNull, } @@ -223,7 +223,6 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; #[test] @@ -236,7 +235,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut echo = ip4.push::>().unwrap(); + let mut echo = ip4.push::().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v4/mod.rs b/core/src/packets/icmp/v4/mod.rs index 368ec99..ab725cc 100644 --- a/core/src/packets/icmp/v4/mod.rs +++ b/core/src/packets/icmp/v4/mod.rs @@ -30,7 +30,7 @@ pub use self::time_exceeded::*; pub use capsule_macros::Icmpv4Packet; use crate::ensure; -use crate::packets::ip::v4::Ipv4Packet; +use crate::packets::ip::v4::{Ipv4, Ipv4Packet}; use crate::packets::ip::ProtocolNumbers; use crate::packets::types::u16be; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -72,7 +72,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 /// [`Icmpv4Message`]: Icmpv4Message -pub struct Icmpv4 { +pub struct Icmpv4 { envelope: E, header: NonNull, offset: usize, @@ -449,9 +449,8 @@ pub trait Icmpv4Packet { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{ICMPV4_PACKET, IPV4_UDP_PACKET}; + use crate::testils::byte_arrays::{ICMPV4_PACKET, UDP4_PACKET}; #[test] fn size_of_icmpv4_header() { @@ -463,7 +462,7 @@ mod tests { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); - let icmp4 = ip4.parse::>().unwrap(); + let icmp4 = ip4.parse::().unwrap(); // parses the generic header assert_eq!(Icmpv4Types::EchoRequest, icmp4.msg_type()); @@ -471,7 +470,7 @@ mod tests { assert_eq!(0x2a5c, icmp4.checksum()); // downcasts to specific message - let echo = icmp4.downcast::>().unwrap(); + let echo = icmp4.downcast::().unwrap(); assert_eq!(Icmpv4Types::EchoRequest, echo.msg_type()); assert_eq!(0, echo.code()); assert_eq!(0x2a5c, echo.checksum()); @@ -480,7 +479,7 @@ mod tests { // also can one-step parse let ip4 = echo.deparse(); - assert!(ip4.parse::>().is_ok()); + assert!(ip4.parse::().is_ok()); } #[capsule::test] @@ -488,18 +487,18 @@ mod tests { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); - let icmp4 = ip4.parse::>().unwrap(); + let icmp4 = ip4.parse::().unwrap(); - assert!(icmp4.downcast::>().is_err()); + assert!(icmp4.downcast::().is_err()); } #[capsule::test] fn parse_non_icmpv4_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); - assert!(ip4.parse::>().is_err()); + assert!(ip4.parse::().is_err()); } #[capsule::test] @@ -507,7 +506,7 @@ mod tests { let packet = Mbuf::from_bytes(&ICMPV4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); - let mut icmp4 = ip4.parse::>().unwrap(); + let mut icmp4 = ip4.parse::().unwrap(); let expected = icmp4.checksum(); // no payload change but force a checksum recompute anyway @@ -521,6 +520,6 @@ mod tests { let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - assert!(ip4.push::>().is_err()); + assert!(ip4.push::().is_err()); } } diff --git a/core/src/packets/icmp/v4/redirect.rs b/core/src/packets/icmp/v4/redirect.rs index 570cdb7..43f936b 100644 --- a/core/src/packets/icmp/v4/redirect.rs +++ b/core/src/packets/icmp/v4/redirect.rs @@ -17,8 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; -use crate::packets::ip::v4::Ipv4Packet; -use crate::packets::ip::v4::IPV4_MIN_MTU; +use crate::packets::ip::v4::{Ipv4, Ipv4Packet, IPV4_MIN_MTU}; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; use std::fmt; @@ -44,7 +43,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct Redirect { +pub struct Redirect { icmp: Icmpv4, body: NonNull, } @@ -219,9 +218,8 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; - use crate::testils::byte_arrays::IPV4_TCP_PACKET; + use crate::testils::byte_arrays::TCP4_PACKET; #[test] fn size_of_redirect_body() { @@ -230,12 +228,12 @@ mod tests { #[capsule::test] fn push_and_set_redirect() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let tcp_len = ip4.payload_len(); - let mut redirect = ip4.push::>().unwrap(); + let mut redirect = ip4.push::().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of() + tcp_len, redirect.payload_len()); @@ -260,7 +258,7 @@ mod tests { let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut redirect = ip4.push::>().unwrap(); + let mut redirect = ip4.push::().unwrap(); assert!(redirect.data_len() > IPV4_MIN_MTU); redirect.reconcile_all(); @@ -273,7 +271,7 @@ mod tests { let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut redirect = ip4.push::>().unwrap(); + let mut redirect = ip4.push::().unwrap(); assert!(redirect.data_len() < IPV4_MIN_MTU); redirect.reconcile_all(); diff --git a/core/src/packets/icmp/v4/time_exceeded.rs b/core/src/packets/icmp/v4/time_exceeded.rs index 9e9a1ba..152e895 100644 --- a/core/src/packets/icmp/v4/time_exceeded.rs +++ b/core/src/packets/icmp/v4/time_exceeded.rs @@ -17,8 +17,7 @@ */ use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types}; -use crate::packets::ip::v4::Ipv4Packet; -use crate::packets::ip::v4::IPV4_MIN_MTU; +use crate::packets::ip::v4::{Ipv4, Ipv4Packet, IPV4_MIN_MTU}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -40,7 +39,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 792]: https://tools.ietf.org/html/rfc792 #[derive(Icmpv4Packet)] -pub struct TimeExceeded { +pub struct TimeExceeded { icmp: Icmpv4, body: NonNull, } @@ -183,9 +182,8 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v4::Ipv4; use crate::packets::Mbuf; - use crate::testils::byte_arrays::IPV4_TCP_PACKET; + use crate::testils::byte_arrays::TCP4_PACKET; #[test] fn size_of_time_exceeded_body() { @@ -194,12 +192,12 @@ mod tests { #[capsule::test] fn push_and_set_time_exceeded() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let tcp_len = ip4.payload_len(); - let mut exceeded = ip4.push::>().unwrap(); + let mut exceeded = ip4.push::().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -223,7 +221,7 @@ mod tests { let packet = Mbuf::from_bytes(&[42; 100]).unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut exceeded = ip4.push::>().unwrap(); + let mut exceeded = ip4.push::().unwrap(); assert!(exceeded.data_len() > IPV4_MIN_MTU); exceeded.reconcile_all(); @@ -236,7 +234,7 @@ mod tests { let packet = Mbuf::from_bytes(&[42; 50]).unwrap(); let ethernet = packet.push::().unwrap(); let ip4 = ethernet.push::().unwrap(); - let mut exceeded = ip4.push::>().unwrap(); + let mut exceeded = ip4.push::().unwrap(); assert!(exceeded.data_len() < IPV4_MIN_MTU); exceeded.reconcile_all(); diff --git a/core/src/packets/icmp/v6/echo_reply.rs b/core/src/packets/icmp/v6/echo_reply.rs index 76254ff..de435ac 100644 --- a/core/src/packets/icmp/v6/echo_reply.rs +++ b/core/src/packets/icmp/v6/echo_reply.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -47,7 +47,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-4.2 #[derive(Icmpv6Packet)] -pub struct EchoReply { +pub struct EchoReply { icmp: Icmpv6, body: NonNull, } @@ -218,7 +218,6 @@ struct EchoReplyBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -231,7 +230,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut echo = ip6.push::>().unwrap(); + let mut echo = ip6.push::().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoReplyBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/echo_request.rs b/core/src/packets/icmp/v6/echo_request.rs index 5208481..fb11273 100644 --- a/core/src/packets/icmp/v6/echo_request.rs +++ b/core/src/packets/icmp/v6/echo_request.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -48,7 +48,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-4.1 #[derive(Icmpv6Packet)] -pub struct EchoRequest { +pub struct EchoRequest { icmp: Icmpv6, body: NonNull, } @@ -219,7 +219,6 @@ struct EchoRequestBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -232,7 +231,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut echo = ip6.push::>().unwrap(); + let mut echo = ip6.push::().unwrap(); assert_eq!(4, echo.header_len()); assert_eq!(EchoRequestBody::size_of(), echo.payload_len()); diff --git a/core/src/packets/icmp/v6/mod.rs b/core/src/packets/icmp/v6/mod.rs index c0551aa..a2b7e96 100644 --- a/core/src/packets/icmp/v6/mod.rs +++ b/core/src/packets/icmp/v6/mod.rs @@ -33,7 +33,7 @@ pub use self::unreachable::*; pub use capsule_macros::Icmpv6Packet; use crate::ensure; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::ip::ProtocolNumbers; use crate::packets::types::u16be; use crate::packets::{checksum, Internal, Packet, SizeOf}; @@ -70,14 +70,14 @@ use std::ptr::NonNull; /// /// ``` /// if ipv6.next_header() == NextHeaders::Icmpv6 { -/// let icmpv6 = ipv6.parse::>().unwrap(); +/// let icmpv6 = ipv6.parse::().unwrap(); /// println!("{}", icmpv6.msg_type()); /// } /// ``` /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443 /// [`Icmpv6Message`]: Icmpv6Message -pub struct Icmpv6 { +pub struct Icmpv6 { envelope: E, header: NonNull, offset: usize, @@ -377,8 +377,8 @@ pub struct Icmpv6Header { /// # Example /// /// ``` -/// let icmpv6 = ipv6.parse::>()?; -/// let reply = icmpv6.downcast::>()?; +/// let icmpv6 = ipv6.parse::()?; +/// let reply = icmpv6.downcast::()?; /// ``` /// /// [ICMPv6]: Icmpv6 @@ -500,9 +500,8 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::icmp::v6::ndp::RouterAdvertisement; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{ICMPV6_PACKET, IPV6_TCP_PACKET, ROUTER_ADVERT_PACKET}; + use crate::testils::byte_arrays::{ICMPV6_PACKET, ROUTER_ADVERT_PACKET, TCP6_PACKET}; #[test] fn size_of_icmpv6_header() { @@ -514,7 +513,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let icmp6 = ip6.parse::>().unwrap(); + let icmp6 = ip6.parse::().unwrap(); // parses the generic header assert_eq!(Icmpv6Types::RouterAdvertisement, icmp6.msg_type()); @@ -522,7 +521,7 @@ mod tests { assert_eq!(0xf50c, icmp6.checksum()); // downcasts to specific message - let advert = icmp6.downcast::>().unwrap(); + let advert = icmp6.downcast::().unwrap(); assert_eq!(Icmpv6Types::RouterAdvertisement, advert.msg_type()); assert_eq!(0, advert.code()); assert_eq!(0xf50c, advert.checksum()); @@ -535,7 +534,7 @@ mod tests { // also can one-step parse let ip6 = advert.deparse(); - assert!(ip6.parse::>().is_ok()); + assert!(ip6.parse::().is_ok()); } #[capsule::test] @@ -543,18 +542,18 @@ mod tests { let packet = Mbuf::from_bytes(&ICMPV6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let icmp6 = ip6.parse::>().unwrap(); + let icmp6 = ip6.parse::().unwrap(); - assert!(icmp6.downcast::>().is_err()); + assert!(icmp6.downcast::().is_err()); } #[capsule::test] fn parse_non_icmpv6_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - assert!(ip6.parse::>().is_err()); + assert!(ip6.parse::().is_err()); } #[capsule::test] @@ -562,7 +561,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut icmp6 = ip6.parse::>().unwrap(); + let mut icmp6 = ip6.parse::().unwrap(); let expected = icmp6.checksum(); // no payload change but force a checksum recompute anyway @@ -576,6 +575,6 @@ mod tests { let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - assert!(ip6.push::>().is_err()); + assert!(ip6.push::().is_err()); } } diff --git a/core/src/packets/icmp/v6/ndp/mod.rs b/core/src/packets/icmp/v6/ndp/mod.rs index 9de511d..2be9f43 100644 --- a/core/src/packets/icmp/v6/ndp/mod.rs +++ b/core/src/packets/icmp/v6/ndp/mod.rs @@ -63,7 +63,7 @@ pub trait NdpPacket: Packet { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut iter = advert.options_iter(); /// /// while let Some(option) = iter.next()? { @@ -220,7 +220,7 @@ impl<'a> ImmutableNdpOption<'a> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut iter = advert.options(); /// /// while let Some(option) = iter.next()? { @@ -342,7 +342,7 @@ impl<'a> MutableNdpOption<'a> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut iter = advert.options_mut().iter(); /// /// while let Some(option) = iter.next()? { @@ -416,7 +416,7 @@ impl NdpOptions<'_> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut options = advert.options_mut(); /// let mut iter = options.iter(); /// @@ -437,7 +437,7 @@ impl NdpOptions<'_> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut options = advert.options_mut(); /// let mut source = options.prepend::>()?; /// source.set_option_type_source(); @@ -452,7 +452,7 @@ impl NdpOptions<'_> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut options = advert.options_mut(); /// let mut source = options.append::>()?; /// source.set_option_type_source(); @@ -471,7 +471,7 @@ impl NdpOptions<'_> { /// # Example /// /// ``` - /// let advert = ipv6.parse::>()?; + /// let advert = ipv6.parse::()?; /// let mut options = advert.options_mut(); /// let _ = options.retain(|option| option.option_type() == NdpOptionTypes::PrefixInformation); /// ``` @@ -560,7 +560,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let advert = ip6.parse::().unwrap(); let mut prefix = false; let mut mtu = false; @@ -589,7 +589,7 @@ mod tests { let packet = Mbuf::from_bytes(&INVALID_OPTION_LENGTH).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let advert = ip6.parse::().unwrap(); assert!(advert.options_iter().next().is_err()); } @@ -599,7 +599,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.peek::().unwrap(); let ip6 = ethernet.peek::().unwrap(); - let advert = ip6.peek::>().unwrap(); + let advert = ip6.peek::().unwrap(); let mut iter = advert.options_iter(); @@ -617,7 +617,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut prefix = false; let mut mtu = false; @@ -647,7 +647,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -665,7 +665,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -684,7 +684,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut target = options.prepend::>().unwrap(); target.set_option_type_target(); @@ -700,7 +700,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut target = options.append::>().unwrap(); target.set_option_type_target(); @@ -724,7 +724,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let _ = options.retain(|option| option.downcast::>().is_ok()); @@ -755,7 +755,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut iter = advert.options_iter(); advert.set_code(0); @@ -776,7 +776,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let advert = ip6.parse::>().unwrap(); + let advert = ip6.parse::().unwrap(); let mut iter = advert.options_iter(); let mut option = iter.next().unwrap().unwrap(); @@ -803,7 +803,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs index 548a80f..2b03fa1 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_advert.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_advert.rs @@ -18,7 +18,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::u16be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -78,7 +78,7 @@ const O_FLAG: u8 = 0b0010_0000; /// /// [IETF RFC 4861]: https://tools.ietf.org/html/rfc4861#section-4.4 #[derive(Icmpv6Packet)] -pub struct NeighborAdvertisement { +pub struct NeighborAdvertisement { icmp: Icmpv6, body: NonNull, } @@ -271,7 +271,6 @@ impl Default for NeighborAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -284,7 +283,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let mut advert = ip6.push::().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(NeighborAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs index fe6b2ed..f0327ca 100644 --- a/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/neighbor_solicit.rs @@ -18,7 +18,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -56,7 +56,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4861]: https://tools.ietf.org/html/rfc4861#section-4.3 #[derive(Icmpv6Packet)] -pub struct NeighborSolicitation { +pub struct NeighborSolicitation { icmp: Icmpv6, body: NonNull, } @@ -186,7 +186,6 @@ impl Default for NeighborSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -199,7 +198,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut solicit = ip6.push::>().unwrap(); + let mut solicit = ip6.push::().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(NeighborSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs index 38b979d..ab5e8da 100644 --- a/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs +++ b/core/src/packets/icmp/v6/ndp/options/link_layer_addr.rs @@ -206,7 +206,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -230,7 +230,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let mut advert = ip6.push::().unwrap(); let mut options = advert.options_mut(); let mut lla = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/mtu.rs b/core/src/packets/icmp/v6/ndp/options/mtu.rs index d75e689..4a463c8 100644 --- a/core/src/packets/icmp/v6/ndp/options/mtu.rs +++ b/core/src/packets/icmp/v6/ndp/options/mtu.rs @@ -186,7 +186,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -210,7 +210,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let mut advert = ip6.push::().unwrap(); let mut options = advert.options_mut(); let mut mtu = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs index 834d834..9cf5da1 100644 --- a/core/src/packets/icmp/v6/ndp/options/prefix_info.rs +++ b/core/src/packets/icmp/v6/ndp/options/prefix_info.rs @@ -324,7 +324,7 @@ mod tests { let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); - let mut advert = ip6.parse::>().unwrap(); + let mut advert = ip6.parse::().unwrap(); let mut options = advert.options_mut(); let mut iter = options.iter(); @@ -356,7 +356,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let mut advert = ip6.push::().unwrap(); let mut options = advert.options_mut(); let mut prefix = options.append::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/options/redirected.rs b/core/src/packets/icmp/v6/ndp/options/redirected.rs index 37d49a8..97c7525 100644 --- a/core/src/packets/icmp/v6/ndp/options/redirected.rs +++ b/core/src/packets/icmp/v6/ndp/options/redirected.rs @@ -70,7 +70,7 @@ use std::ptr::NonNull; /// ``` /// let ethernet = orig_ipv6.deparse(); /// let ipv6 = ethernet.push::()?; -/// let mut redirect = ipv6.push::>()?; +/// let mut redirect = ipv6.push::()?; /// let mut options = redirect.options_mut(); /// let _ = options.prepend::>(); /// redirect.reconcile(); @@ -250,7 +250,7 @@ mod tests { let packet = Mbuf::from_bytes(&data).unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let mut redirect = ip6.push::().unwrap(); let mut options = redirect.options_mut(); let mut header = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/redirect.rs b/core/src/packets/icmp/v6/ndp/redirect.rs index 8c00bfa..8ace688 100644 --- a/core/src/packets/icmp/v6/ndp/redirect.rs +++ b/core/src/packets/icmp/v6/ndp/redirect.rs @@ -18,7 +18,7 @@ use super::{NdpPacket, RedirectedHeader}; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -68,7 +68,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4861]: https://tools.ietf.org/html/rfc2461#section-4.5 #[derive(Icmpv6Packet)] -pub struct Redirect { +pub struct Redirect { icmp: Icmpv6, body: NonNull, } @@ -242,7 +242,6 @@ impl Default for RedirectBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -255,7 +254,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let mut redirect = ip6.push::().unwrap(); assert_eq!(4, redirect.header_len()); assert_eq!(RedirectBody::size_of(), redirect.payload_len()); @@ -277,7 +276,7 @@ mod tests { let packet = Mbuf::from_bytes(&[42; 1600]).unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut redirect = ip6.push::>().unwrap(); + let mut redirect = ip6.push::().unwrap(); let mut options = redirect.options_mut(); let _ = options.prepend::>().unwrap(); diff --git a/core/src/packets/icmp/v6/ndp/router_advert.rs b/core/src/packets/icmp/v6/ndp/router_advert.rs index 98a76d1..45423d1 100644 --- a/core/src/packets/icmp/v6/ndp/router_advert.rs +++ b/core/src/packets/icmp/v6/ndp/router_advert.rs @@ -18,7 +18,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::{u16be, u32be}; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -97,7 +97,7 @@ const O_FLAG: u8 = 0b0100_0000; /// /// [IETF RFC 4861]: https://tools.ietf.org/html/rfc4861#section-4.2 #[derive(Icmpv6Packet)] -pub struct RouterAdvertisement { +pub struct RouterAdvertisement { icmp: Icmpv6, body: NonNull, } @@ -302,7 +302,6 @@ struct RouterAdvertisementBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -315,7 +314,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut advert = ip6.push::>().unwrap(); + let mut advert = ip6.push::().unwrap(); assert_eq!(4, advert.header_len()); assert_eq!(RouterAdvertisementBody::size_of(), advert.payload_len()); diff --git a/core/src/packets/icmp/v6/ndp/router_solicit.rs b/core/src/packets/icmp/v6/ndp/router_solicit.rs index 5f8987e..29dcf00 100644 --- a/core/src/packets/icmp/v6/ndp/router_solicit.rs +++ b/core/src/packets/icmp/v6/ndp/router_solicit.rs @@ -18,7 +18,7 @@ use super::NdpPacket; use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::Ipv6Packet; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -49,7 +49,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4861]: https://tools.ietf.org/html/rfc4861#section-4.1 #[derive(Icmpv6Packet)] -pub struct RouterSolicitation { +pub struct RouterSolicitation { icmp: Icmpv6, body: NonNull, } @@ -144,7 +144,6 @@ struct RouterSolicitationBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; #[test] @@ -157,7 +156,7 @@ mod tests { let packet = Mbuf::new().unwrap(); let ethernet = packet.push::().unwrap(); let ip6 = ethernet.push::().unwrap(); - let mut solicit = ip6.push::>().unwrap(); + let mut solicit = ip6.push::().unwrap(); assert_eq!(4, solicit.header_len()); assert_eq!(RouterSolicitationBody::size_of(), solicit.payload_len()); diff --git a/core/src/packets/icmp/v6/time_exceeded.rs b/core/src/packets/icmp/v6/time_exceeded.rs index b60b668..c80b6cc 100644 --- a/core/src/packets/icmp/v6/time_exceeded.rs +++ b/core/src/packets/icmp/v6/time_exceeded.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -41,7 +41,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-3.3 #[derive(Icmpv6Packet)] -pub struct TimeExceeded { +pub struct TimeExceeded { icmp: Icmpv6, body: NonNull, } @@ -164,9 +164,8 @@ struct TimeExceededBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; - use crate::testils::byte_arrays::IPV6_TCP_PACKET; + use crate::testils::byte_arrays::TCP6_PACKET; #[test] fn size_of_time_exceeded_body() { @@ -175,12 +174,12 @@ mod tests { #[capsule::test] fn push_and_set_time_exceeded() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut exceeded = ip6.push::>().unwrap(); + let mut exceeded = ip6.push::().unwrap(); assert_eq!(4, exceeded.header_len()); assert_eq!( @@ -208,7 +207,7 @@ mod tests { // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut exceeded = ip6.push::>().unwrap(); + let mut exceeded = ip6.push::().unwrap(); assert!(exceeded.mbuf().data_len() > max_len); exceeded.reconcile_all(); diff --git a/core/src/packets/icmp/v6/too_big.rs b/core/src/packets/icmp/v6/too_big.rs index 111fd93..a83c0e3 100644 --- a/core/src/packets/icmp/v6/too_big.rs +++ b/core/src/packets/icmp/v6/too_big.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -43,7 +43,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-3.2 #[derive(Icmpv6Packet)] -pub struct PacketTooBig { +pub struct PacketTooBig { icmp: Icmpv6, body: NonNull, } @@ -189,9 +189,8 @@ struct PacketTooBigBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; - use crate::testils::byte_arrays::IPV6_TCP_PACKET; + use crate::testils::byte_arrays::TCP6_PACKET; #[test] fn size_of_packet_too_big() { @@ -200,12 +199,12 @@ mod tests { #[capsule::test] fn push_and_set_packet_too_big() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut too_big = ip6.push::>().unwrap(); + let mut too_big = ip6.push::().unwrap(); assert_eq!(4, too_big.header_len()); assert_eq!(PacketTooBigBody::size_of() + tcp_len, too_big.payload_len()); @@ -230,7 +229,7 @@ mod tests { // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut too_big = ip6.push::>().unwrap(); + let mut too_big = ip6.push::().unwrap(); assert!(too_big.mbuf().data_len() > max_len); too_big.reconcile_all(); diff --git a/core/src/packets/icmp/v6/unreachable.rs b/core/src/packets/icmp/v6/unreachable.rs index 48c00e1..a9afc87 100644 --- a/core/src/packets/icmp/v6/unreachable.rs +++ b/core/src/packets/icmp/v6/unreachable.rs @@ -17,7 +17,7 @@ */ use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types}; -use crate::packets::ip::v6::{Ipv6Packet, IPV6_MIN_MTU}; +use crate::packets::ip::v6::{Ipv6, Ipv6Packet, IPV6_MIN_MTU}; use crate::packets::types::u32be; use crate::packets::{Internal, Packet, SizeOf}; use anyhow::Result; @@ -41,7 +41,7 @@ use std::ptr::NonNull; /// /// [IETF RFC 4443]: https://tools.ietf.org/html/rfc4443#section-3.1 #[derive(Icmpv6Packet)] -pub struct DestinationUnreachable { +pub struct DestinationUnreachable { icmp: Icmpv6, body: NonNull, } @@ -164,9 +164,8 @@ struct DestinationUnreachableBody { mod tests { use super::*; use crate::packets::ethernet::Ethernet; - use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; - use crate::testils::byte_arrays::IPV6_TCP_PACKET; + use crate::testils::byte_arrays::TCP6_PACKET; #[test] fn size_of_destination_unreachable_body() { @@ -175,12 +174,12 @@ mod tests { #[capsule::test] fn push_and_set_destination_unreachable() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); let tcp_len = ip6.payload_len(); - let mut unreachable = ip6.push::>().unwrap(); + let mut unreachable = ip6.push::().unwrap(); assert_eq!(4, unreachable.header_len()); assert_eq!( @@ -208,7 +207,7 @@ mod tests { // the max packet len is MTU + Ethernet header let max_len = IPV6_MIN_MTU + 14; - let mut unreachable = ip6.push::>().unwrap(); + let mut unreachable = ip6.push::().unwrap(); assert!(unreachable.mbuf().data_len() > max_len); unreachable.reconcile_all(); diff --git a/core/src/packets/ip/v4.rs b/core/src/packets/ip/v4.rs index 4006ad7..95f897a 100644 --- a/core/src/packets/ip/v4.rs +++ b/core/src/packets/ip/v4.rs @@ -620,7 +620,7 @@ mod tests { use super::*; use crate::packets::ip::ProtocolNumbers; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV4_UDP_PACKET, IPV6_TCP_PACKET}; + use crate::testils::byte_arrays::{TCP6_PACKET, UDP4_PACKET}; #[test] fn size_of_ipv4_header() { @@ -629,7 +629,7 @@ mod tests { #[capsule::test] fn parse_ipv4_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); @@ -651,7 +651,7 @@ mod tests { #[capsule::test] fn parse_non_ipv4_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); assert!(ethernet.parse::().is_err()); @@ -659,7 +659,7 @@ mod tests { #[capsule::test] fn parse_ipv4_setter_checks() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let mut ip4 = ethernet.parse::().unwrap(); @@ -724,7 +724,7 @@ mod tests { #[capsule::test] fn compute_checksum() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let mut ip4 = ethernet.parse::().unwrap(); diff --git a/core/src/packets/ip/v6/fragment.rs b/core/src/packets/ip/v6/fragment.rs index 64e24f7..eacb1ac 100644 --- a/core/src/packets/ip/v6/fragment.rs +++ b/core/src/packets/ip/v6/fragment.rs @@ -332,7 +332,7 @@ mod tests { use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::Ipv6; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, IPV6_TCP_PACKET}; + use crate::testils::byte_arrays::{IPV6_FRAGMENT_PACKET, TCP6_PACKET}; #[test] fn size_of_fragment_header() { @@ -354,7 +354,7 @@ mod tests { #[capsule::test] fn parse_non_fragment_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); @@ -391,7 +391,7 @@ mod tests { #[capsule::test] fn insert_fragment_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); diff --git a/core/src/packets/ip/v6/mod.rs b/core/src/packets/ip/v6/mod.rs index 796ac1e..6b77931 100644 --- a/core/src/packets/ip/v6/mod.rs +++ b/core/src/packets/ip/v6/mod.rs @@ -468,7 +468,7 @@ mod tests { use super::*; use crate::packets::ip::ProtocolNumbers; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV4_UDP_PACKET, IPV6_TCP_PACKET}; + use crate::testils::byte_arrays::{TCP6_PACKET, UDP4_PACKET}; #[test] fn size_of_ipv6_header() { @@ -477,7 +477,7 @@ mod tests { #[capsule::test] fn parse_ipv6_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); @@ -494,7 +494,7 @@ mod tests { #[capsule::test] fn parse_non_ipv6_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); assert!(ethernet.parse::().is_err()); @@ -502,7 +502,7 @@ mod tests { #[capsule::test] fn parse_ipv6_setter_checks() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let mut ip6 = ethernet.parse::().unwrap(); diff --git a/core/src/packets/ip/v6/srh.rs b/core/src/packets/ip/v6/srh.rs index 6e8f215..3cce4c6 100644 --- a/core/src/packets/ip/v6/srh.rs +++ b/core/src/packets/ip/v6/srh.rs @@ -528,7 +528,7 @@ mod tests { use crate::packets::ip::ProtocolNumbers; use crate::packets::tcp::{Tcp, Tcp6}; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV6_TCP_PACKET, SR_TCP_PACKET}; + use crate::testils::byte_arrays::{SR_TCP_PACKET, TCP6_PACKET}; #[test] fn size_of_segment_routing_header() { @@ -558,7 +558,7 @@ mod tests { #[capsule::test] fn parse_non_segment_routing_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); @@ -654,7 +654,7 @@ mod tests { #[capsule::test] fn insert_segment_routing_packet() { - let packet = Mbuf::from_bytes(&IPV6_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP6_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip6 = ethernet.parse::().unwrap(); let ip6_payload_len = ip6.payload_len(); diff --git a/core/src/packets/mod.rs b/core/src/packets/mod.rs index 65df752..86ea0fb 100644 --- a/core/src/packets/mod.rs +++ b/core/src/packets/mod.rs @@ -382,11 +382,11 @@ mod tests { use crate::packets::ethernet::Ethernet; use crate::packets::ip::v4::Ipv4; use crate::packets::udp::Udp4; - use crate::testils::byte_arrays::IPV4_UDP_PACKET; + use crate::testils::byte_arrays::UDP4_PACKET; #[capsule::test] fn parse_and_reset_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let len = packet.data_len(); let ethernet = packet.parse::().unwrap(); @@ -399,7 +399,7 @@ mod tests { #[capsule::test] fn peek_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.peek::().unwrap(); assert_eq!(MacAddr::new(0, 0, 0, 0, 0, 2), ethernet.src()); @@ -411,7 +411,7 @@ mod tests { #[capsule::test] fn parse_and_deparse_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let mut ip4 = ethernet.parse::().unwrap(); ip4.set_ttl(25); @@ -425,7 +425,7 @@ mod tests { #[capsule::test] fn remove_header_and_payload() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); @@ -455,7 +455,7 @@ mod tests { #[test] #[cfg(feature = "compile_failure")] fn cannot_mutate_packet_while_peeking_into_payload() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let mut ethernet = packet.parse::().unwrap(); let ip4 = ethernet.peek::().unwrap(); ethernet.set_src(MacAddr::UNSPECIFIED); @@ -472,7 +472,7 @@ mod tests { #[test] #[cfg(feature = "compile_failure")] fn cannot_mutate_immutable_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.peek::().unwrap(); ethernet.set_src(MacAddr::UNSPECIFIED); } diff --git a/core/src/packets/tcp.rs b/core/src/packets/tcp.rs index 2eb28d3..7391e56 100644 --- a/core/src/packets/tcp.rs +++ b/core/src/packets/tcp.rs @@ -669,7 +669,7 @@ mod tests { use crate::packets::ethernet::Ethernet; use crate::packets::ip::v6::SegmentRouting; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET, SR_TCP_PACKET}; + use crate::testils::byte_arrays::{SR_TCP_PACKET, TCP4_PACKET, UDP4_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; #[test] @@ -679,7 +679,7 @@ mod tests { #[capsule::test] fn parse_tcp_packet() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let tcp = ip4.parse::().unwrap(); @@ -705,7 +705,7 @@ mod tests { #[capsule::test] fn parse_non_tcp_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); @@ -714,7 +714,7 @@ mod tests { #[capsule::test] fn tcp_flow_v4() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let tcp = ip4.parse::().unwrap(); @@ -745,7 +745,7 @@ mod tests { #[capsule::test] fn set_src_dst_ip() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let mut tcp = ip4.parse::().unwrap(); @@ -768,7 +768,7 @@ mod tests { #[capsule::test] fn compute_checksum() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let mut tcp = ip4.parse::().unwrap(); diff --git a/core/src/packets/udp.rs b/core/src/packets/udp.rs index 256c686..42512e8 100644 --- a/core/src/packets/udp.rs +++ b/core/src/packets/udp.rs @@ -390,7 +390,7 @@ mod tests { use super::*; use crate::packets::ethernet::Ethernet; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; + use crate::testils::byte_arrays::{TCP4_PACKET, UDP4_PACKET}; use std::net::{Ipv4Addr, Ipv6Addr}; #[test] @@ -400,7 +400,7 @@ mod tests { #[capsule::test] fn parse_udp_packet() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let udp = ip4.parse::().unwrap(); @@ -414,7 +414,7 @@ mod tests { #[capsule::test] fn parse_non_udp_packet() { - let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&TCP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); @@ -423,7 +423,7 @@ mod tests { #[capsule::test] fn udp_flow_v4() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let udp = ip4.parse::().unwrap(); @@ -438,7 +438,7 @@ mod tests { #[capsule::test] fn set_src_dst_ip() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let mut udp = ip4.parse::().unwrap(); @@ -461,7 +461,7 @@ mod tests { #[capsule::test] fn compute_checksum() { - let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap(); + let packet = Mbuf::from_bytes(&UDP4_PACKET).unwrap(); let ethernet = packet.parse::().unwrap(); let ip4 = ethernet.parse::().unwrap(); let mut udp = ip4.parse::().unwrap(); diff --git a/core/src/runtime/pcap_dump.rs b/core/src/runtime/pcap_dump.rs index f8a72f4..87d8b50 100644 --- a/core/src/runtime/pcap_dump.rs +++ b/core/src/runtime/pcap_dump.rs @@ -188,15 +188,15 @@ unsafe extern "C" fn tx_callback_fn( mod tests { use super::*; use crate::packets::Mbuf; - use crate::testils::byte_arrays::{IPV4_TCP_PACKET, IPV4_UDP_PACKET}; + use crate::testils::byte_arrays::{TCP4_PACKET, UDP4_PACKET}; #[capsule::test] fn dump_mbufs_to_file() -> Result<()> { let filename = "file.pcap"; let mut capture = CaptureFile::new(filename)?; - let tcp = Mbuf::from_bytes(&IPV4_TCP_PACKET)?; - let udp = Mbuf::from_bytes(&IPV4_UDP_PACKET)?; + let tcp = Mbuf::from_bytes(&TCP4_PACKET)?; + let udp = Mbuf::from_bytes(&UDP4_PACKET)?; dump_mbufs( &mut capture.dumper, @@ -208,9 +208,9 @@ mod tests { // reads the packets from file and assert they are the same. let mut h2 = pcap::open_offline(filename)?; let packet = pcap::next(&mut h2)?; - assert_eq!(&IPV4_TCP_PACKET, packet); + assert_eq!(&TCP4_PACKET, packet); let packet = pcap::next(&mut h2)?; - assert_eq!(&IPV4_UDP_PACKET, packet); + assert_eq!(&UDP4_PACKET, packet); pcap::close(&mut h2); diff --git a/core/src/testils/byte_arrays.rs b/core/src/testils/byte_arrays.rs index 068732e..f35818d 100644 --- a/core/src/testils/byte_arrays.rs +++ b/core/src/testils/byte_arrays.rs @@ -64,7 +64,7 @@ pub const VLAN_QINQ_PACKET: [u8; 68] = [ /// An ARP packet. #[rustfmt::skip] -pub const ARP4_PACKET: [u8; 42] = [ +pub const ARP_PACKET: [u8; 42] = [ // Ethernet header 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, @@ -90,7 +90,7 @@ pub const ARP4_PACKET: [u8; 42] = [ /// An IPv4 TCP packet. #[rustfmt::skip] -pub const IPV4_TCP_PACKET: [u8; 58] = [ +pub const TCP4_PACKET: [u8; 58] = [ // Ethernet header 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, @@ -124,7 +124,7 @@ pub const IPV4_TCP_PACKET: [u8; 58] = [ /// An IPv4 UDP packet. #[rustfmt::skip] -pub const IPV4_UDP_PACKET: [u8; 52] = [ +pub const UDP4_PACKET: [u8; 52] = [ // Ethernet header 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, @@ -152,7 +152,7 @@ pub const IPV4_UDP_PACKET: [u8; 52] = [ /// An IPv6 TCP packet. #[rustfmt::skip] -pub const IPV6_TCP_PACKET: [u8; 78] = [ +pub const TCP6_PACKET: [u8; 78] = [ // Ethernet header 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, diff --git a/examples/kni/main.rs b/examples/kni/main.rs index 10cbb51..d64fcc1 100644 --- a/examples/kni/main.rs +++ b/examples/kni/main.rs @@ -38,7 +38,7 @@ fn route_pkt(packet: Mbuf, kni0: &Outbox) -> Result { match ip6.next_header() { ProtocolNumbers::Icmpv6 => { - let icmp = ip6.parse::>()?; + let icmp = ip6.parse::()?; let fmt = format!("to kni0: {}", icmp.msg_type()).cyan(); info!("{}", fmt); let _ = kni0.push(icmp); @@ -62,7 +62,7 @@ fn from_kni(packet: Mbuf, cap0: &Outbox) -> Result { let icmp = packet .parse::()? .parse::()? - .parse::>()?; + .parse::()?; let fmt = format!("from kni0: {}", icmp.msg_type()).green(); info!("{}", fmt); diff --git a/examples/ping4d/main.rs b/examples/ping4d/main.rs index 137b108..1a1e5ac 100644 --- a/examples/ping4d/main.rs +++ b/examples/ping4d/main.rs @@ -43,8 +43,8 @@ fn reply_echo(packet: Mbuf, cap0: &Outbox) -> Result { reply.set_dst(ip4.src()); reply.set_ttl(255); - let request = ip4.peek::>()?; - let mut reply = reply.push::>()?; + let request = ip4.peek::()?; + let mut reply = reply.push::()?; reply.set_identifier(request.identifier()); reply.set_seq_no(request.seq_no()); reply.set_data(request.data())?;