├── examples ├── lpm │ ├── sudo │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── lpm.rs ├── signals │ ├── data │ │ └── expect.out │ ├── Cargo.toml │ ├── check.sh │ └── src │ │ └── main.rs ├── ipv4or6 │ ├── data │ │ ├── ipv4_tcp.pcap │ │ ├── ipv6_tcp.pcap │ │ ├── expect_ipv6.out │ │ └── expect_ipv4.out │ ├── Cargo.toml │ ├── check.sh │ └── src │ │ └── main.rs ├── macswap │ ├── data │ │ ├── http_lemmy.pcap │ │ └── expect.out │ ├── check.sh │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── op-errors │ ├── data │ │ ├── ipv6_tcp.pcap │ │ ├── op_errors.out │ │ └── expect_v6.out │ ├── check.sh │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── mtu-too-big │ ├── data │ │ ├── in_bounds.pcap │ │ ├── over_bounds.pcap │ │ ├── expect_ipv6.out │ │ └── expect_icmpv6_toobig.out │ ├── Cargo.toml │ ├── check.sh │ └── src │ │ └── main.rs ├── echo-reply │ ├── data │ │ ├── echo_request.pcap │ │ └── echo_reply.out │ ├── check.sh │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tcp-reconstruction │ ├── data │ │ ├── http_lemmy.pcap │ │ └── expect.out │ ├── check.sh │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── embedded-scheduler │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── chain │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── collect-metrics │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── acl-fw │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── nat-tcp-v4 │ └── Cargo.toml ├── maglev │ └── Cargo.toml ├── embedded-scheduler-dependency │ ├── Cargo.toml │ └── src │ │ └── main.rs └── sctp │ ├── ctl │ └── controller.py │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── control.rs ├── framework ├── src │ ├── packets │ │ └── icmp │ │ │ ├── mod.rs │ │ │ ├── v4 │ │ │ └── mod.rs │ │ │ └── v6 │ │ │ ├── too_big.rs │ │ │ ├── ndp │ │ │ ├── mod.rs │ │ │ ├── options │ │ │ │ └── mtu.rs │ │ │ ├── neighbor_solicit.rs │ │ │ └── router_solicit.rs │ │ │ ├── echo_reply.rs │ │ │ └── echo_request.rs │ ├── allocators │ │ ├── mod.rs │ │ └── cache_aligned.rs │ ├── common │ │ ├── mod.rs │ │ └── errors.rs │ ├── native │ │ ├── mod.rs │ │ ├── zcsi.rs │ │ └── mbuf.rs │ ├── native_include │ │ ├── mod.rs │ │ └── dpdk-headers.h │ ├── state │ │ ├── mod.rs │ │ ├── dp_mergeable.rs │ │ └── cp_mergeable.rs │ ├── interface │ │ ├── mod.rs │ │ ├── port │ │ │ ├── mod.rs │ │ │ └── virt_port.rs │ │ └── dpdk.rs │ ├── testing │ │ ├── arbitrary.rs │ │ ├── strategy.rs │ │ └── mod.rs │ ├── utils │ │ ├── cidr │ │ │ └── mod.rs │ │ ├── asm.rs │ │ └── mod.rs │ ├── operators │ │ ├── emit_batch.rs │ │ ├── filter_batch.rs │ │ ├── foreach_batch.rs │ │ ├── map_batch.rs │ │ ├── filtermap_batch.rs │ │ ├── receive_batch.rs │ │ ├── send_batch.rs │ │ └── groupby_batch.rs │ ├── control │ │ ├── mod.rs │ │ ├── linux │ │ │ └── epoll.rs │ │ └── sctp.rs │ ├── scheduler │ │ ├── mod.rs │ │ └── embedded_scheduler.rs │ ├── lib.rs │ └── shared_state │ │ ├── shared_vec.rs │ │ ├── mod.rs │ │ └── directory.rs ├── .gitignore ├── tests │ └── ring_buffer.rs ├── Cargo.toml └── build.rs ├── .dockerignore ├── scripts ├── disable_aslr.sh ├── tuning │ ├── energy.sh │ └── read_cpu_dma_latency.py ├── travis-setup.sh ├── read-target.py ├── check-examples.py └── translate.py ├── native ├── include │ ├── dpdk.h │ ├── pmd.h │ ├── mempool.h │ └── simd.h ├── fmt.sh ├── .clang-format ├── utils.c ├── ethpacket.c ├── test │ ├── Makefile │ └── test.c ├── Makefile └── ring.c ├── .hooks └── pre-commit ├── Cargo.toml ├── netbricks-codegen ├── Cargo.toml └── src │ └── lib.rs ├── examples.sh ├── LICENSE.md ├── .gitignore ├── .travis.yml ├── docker.mk └── Makefile /examples/lpm/sudo: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod v4; 2 | pub mod v6; 3 | -------------------------------------------------------------------------------- /examples/signals/data/expect.out: -------------------------------------------------------------------------------- 1 | [WARN] SIGHUP. 2 | [WARN] SIGTERM. 3 | -------------------------------------------------------------------------------- /framework/src/allocators/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::cache_aligned::*; 2 | mod cache_aligned; 3 | -------------------------------------------------------------------------------- /framework/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod errors; 3 | 4 | pub use self::errors::*; 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | _* 2 | .vagrant 3 | .travis 4 | target 5 | framework/target 6 | *.log 7 | */*/target 8 | -------------------------------------------------------------------------------- /framework/src/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod libnuma; 2 | pub(crate) mod mbuf; 3 | pub(crate) mod zcsi; 4 | -------------------------------------------------------------------------------- /examples/ipv4or6/data/ipv4_tcp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/ipv4or6/data/ipv4_tcp.pcap -------------------------------------------------------------------------------- /examples/ipv4or6/data/ipv6_tcp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/ipv4or6/data/ipv6_tcp.pcap -------------------------------------------------------------------------------- /examples/macswap/data/http_lemmy.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/macswap/data/http_lemmy.pcap -------------------------------------------------------------------------------- /examples/op-errors/data/ipv6_tcp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/op-errors/data/ipv6_tcp.pcap -------------------------------------------------------------------------------- /examples/mtu-too-big/data/in_bounds.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/mtu-too-big/data/in_bounds.pcap -------------------------------------------------------------------------------- /examples/echo-reply/data/echo_request.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/echo-reply/data/echo_request.pcap -------------------------------------------------------------------------------- /examples/mtu-too-big/data/over_bounds.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/mtu-too-big/data/over_bounds.pcap -------------------------------------------------------------------------------- /examples/tcp-reconstruction/data/http_lemmy.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamofockham/NetBricks/HEAD/examples/tcp-reconstruction/data/http_lemmy.pcap -------------------------------------------------------------------------------- /examples/mtu-too-big/data/expect_ipv6.out: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 2 | -------------------------------------------------------------------------------- /examples/mtu-too-big/data/expect_icmpv6_toobig.out: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:01 > 00:00:00:00:00:00, ethertype IPv6 (0x86dd), length 1294: fe80::3 > fe80::1: ICMP6, packet too big, mtu 1280, length 1240 2 | -------------------------------------------------------------------------------- /scripts/disable_aslr.sh: -------------------------------------------------------------------------------- 1 | # ASLR or Address-Space Layout Randomization 2 | # https://doc.dpdk.org/guides/prog_guide/multi_proc_support.html 3 | 4 | echo 0 | sudo tee /proc/sys/kernel/randomize_va_space 5 | -------------------------------------------------------------------------------- /framework/src/native_include/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(dead_code)] 5 | #![allow(improper_ctypes)] 6 | #![allow(clippy::all)] 7 | include!(concat!(env!("OUT_DIR"), "/dpdk_bindings.rs")); 8 | -------------------------------------------------------------------------------- /framework/src/native_include/dpdk-headers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | -------------------------------------------------------------------------------- /native/include/dpdk.h: -------------------------------------------------------------------------------- 1 | #ifndef __DPDK_H__ 2 | #define __DPDK_H__ 3 | /* Call this from the main thread on ZCSI to initialize things */ 4 | int init_system(int core); 5 | 6 | /* Called from all secondary threads on ZCSI */ 7 | int init_thread(int tid, int core); 8 | #include "mempool.h" 9 | #endif 10 | -------------------------------------------------------------------------------- /framework/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::cp_mergeable::*; 2 | pub use self::dp_mergeable::*; 3 | pub use self::mergeable::*; 4 | pub use self::reordered_buffer::*; 5 | pub use self::ring_buffer::*; 6 | mod cp_mergeable; 7 | mod dp_mergeable; 8 | mod mergeable; 9 | pub mod reordered_buffer; 10 | mod ring_buffer; 11 | -------------------------------------------------------------------------------- /native/fmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" 3 | for c_file in ${BASE_DIR}/*.c; do 4 | echo ${c_file} 5 | clang-format -style=file -i ${c_file} 6 | done 7 | 8 | for h_file in ${BASE_DIR}/include/*.h; do 9 | echo ${h_file} 10 | clang-format -style=file -i ${h_file} 11 | done 12 | -------------------------------------------------------------------------------- /framework/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | 13 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 14 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /examples/macswap/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=macswap 3 | PORT_OPTIONS="dpdk:eth_pcap0,rx_pcap=data/http_lemmy.pcap,tx_pcap=/tmp/out.pcap" 4 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS -c 1 -d 1 5 | 6 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect.out 7 | 8 | result=$? 9 | echo ---- 10 | if [[ $result != 0 ]]; then 11 | echo FAIL 12 | exit $result 13 | else 14 | echo PASS 15 | fi 16 | -------------------------------------------------------------------------------- /examples/echo-reply/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=echo-reply 3 | 4 | PORT_OPTIONS1="dpdk:eth_pcap0,rx_pcap=data/echo_request.pcap,tx_pcap=/tmp/out.pcap" 5 | 6 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS1 -c 0 -c 1 -d 1 7 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/echo_reply.out 8 | 9 | result=$? 10 | echo ---- 11 | if [[ $result != 0 ]]; then 12 | echo FAIL 13 | exit $result 14 | else 15 | echo PASS 16 | fi 17 | -------------------------------------------------------------------------------- /examples/embedded-scheduler/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | use netbricks::scheduler::*; 3 | 4 | fn test_func(id: &str) { 5 | println!("Running function {}", id); 6 | } 7 | 8 | fn main() { 9 | let mut sched = embedded_scheduler::EmbeddedScheduler::new(); 10 | let handle0 = sched.add_task(|| test_func("task-0")).unwrap(); 11 | let handle1 = sched.add_task(|| test_func("task-1")).unwrap(); 12 | println!("Initialized"); 13 | sched.exec_task(handle1); 14 | sched.exec_task(handle0); 15 | } 16 | -------------------------------------------------------------------------------- /framework/src/interface/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dpdk; 2 | mod port; 3 | 4 | pub use self::port::*; 5 | use common::*; 6 | use native::mbuf::MBuf; 7 | 8 | /// Generic trait for objects that can receive packets. 9 | pub trait PacketRx: Send { 10 | fn recv(&self, pkts: &mut [*mut MBuf]) -> Result; 11 | } 12 | 13 | /// Generic trait for objects that can send packets. 14 | pub trait PacketTx: Send { 15 | fn send(&self, pkts: &mut [*mut MBuf]) -> Result; 16 | } 17 | 18 | pub trait PacketRxTx: PacketRx + PacketTx {} 19 | -------------------------------------------------------------------------------- /scripts/tuning/energy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" 4 | sudo modprobe msr 5 | if [ -e ${BASE_DIR}/x86_energy_perf_policy ]; then 6 | sudo $BASE_DIR/x86_energy_perf_policy performance # Set ourselves to performance. 7 | else 8 | sudo x86_energy_perf_policy performance 9 | fi 10 | sudo $BASE_DIR/pmqos-static.py cpu_dma_latency=0 # Tune Linux QoS to reduce DMA latency 11 | sudo wrmsr -a 0x620 0x3f3f # Turn off uncore frequency scaling and select max frequency 12 | -------------------------------------------------------------------------------- /examples/tcp-reconstruction/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=tcp-payload 3 | PORT_OPTIONS="dpdk:eth_pcap0,rx_pcap=data/http_lemmy.pcap,tx_pcap=/tmp/out.pcap" 4 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS -c 1\ 5 | |& tee /dev/tty | sed -n '1,/BEGIN TEST OUTPUT/!p' | diff - data/expect.out 6 | 7 | C='\033[1;34m' 8 | NC='\033[0m' 9 | 10 | echo -e "${C}RUNNING: $TEST_NAME${NC}" 11 | 12 | result=$? 13 | echo ---- 14 | if [[ $result != 0 ]]; then 15 | echo FAIL 16 | exit $result 17 | else 18 | echo PASS 19 | fi 20 | -------------------------------------------------------------------------------- /native/include/pmd.h: -------------------------------------------------------------------------------- 1 | #ifndef __PMD_H__ 2 | #define __PMD_H__ 3 | 4 | int num_pmd_ports(); 5 | int get_pmd_ports(struct rte_eth_dev_info* info, int len); 6 | void enumerate_pmd_ports(); 7 | int init_pmd_port(int port, int rxqs, int txqs, int rxq_core[], int txq_core[], int nrxd, int ntxd, 8 | int loopback, int tso, int csumoffload); 9 | int free_pmd_port(int port); 10 | int recv_pkts(int port, int qid, mbuf_array_t pkts, int len); 11 | int send_pkts(int port, int qid, mbuf_array_t pkts, int len); 12 | #endif 13 | -------------------------------------------------------------------------------- /framework/src/testing/arbitrary.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of `proptest.arbitrary.Arbitrary` trait for 2 | //! various types. 3 | 4 | use crate::packets::MacAddr; 5 | use proptest::arbitrary::{any, Arbitrary, StrategyFor}; 6 | use proptest::strategy::{MapInto, Strategy}; 7 | 8 | impl Arbitrary for MacAddr { 9 | type Parameters = (); 10 | type Strategy = MapInto, Self>; 11 | 12 | fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { 13 | any::<[u8; 6]>().prop_map_into() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo +nightly fmt --version &>/dev/null 4 | if [ $? != 0 ]; then 5 | printf "[pre_commit] \033[0;31merror\033[0m: \"cargo +nightly fmt\" not available?\n" 6 | exit 1 7 | fi 8 | 9 | result=0 10 | 11 | printf "[pre_commit] rustfmt \n" 12 | make fmt 13 | if [ $? != 0 ]; then 14 | result=1 15 | fi 16 | 17 | 18 | if [ $result != 0 ]; then 19 | printf "\033[0;31mfail\033[0m \n" 20 | printf "[pre_commit] error(s) in formatting files: \n" 21 | else 22 | printf "\033[0;32mok\033[0m \n" 23 | fi 24 | 25 | exit $result 26 | -------------------------------------------------------------------------------- /examples/macswap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macswap" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: mac swap" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/macswap" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework"} 14 | 15 | [features] 16 | default = [] 17 | print = [] 18 | -------------------------------------------------------------------------------- /native/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: google 2 | IndentWidth: 4 3 | 4 | AlignAfterOpenBracket: true 5 | AlignConsecutiveAssignments: true 6 | AllowShortBlocksOnASingleLine: false 7 | AllowShortCaseLabelsOnASingleLine: false 8 | AllowShortFunctionsOnASingleLine: Empty 9 | AllowShortIfStatementsOnASingleLine: false 10 | AllowShortLoopsOnASingleLine: false 11 | BinPackArguments: true 12 | BinPackParameters: true 13 | ColumnLimit: 100 14 | DerivePointerAlignment: true 15 | PenaltyExcessCharacter: 1 16 | SpaceBeforeParens: ControlStatements 17 | TabWidth: 4 18 | UseTab: Never 19 | -------------------------------------------------------------------------------- /examples/echo-reply/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "echo-reply" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: echo reply" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/echo-reply" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework"} 14 | 15 | [features] 16 | default = [] 17 | print = [] 18 | -------------------------------------------------------------------------------- /examples/mtu-too-big/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mtu-too-big" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: when > mtu---packet-to-big" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/mtu-too-big" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework"} 14 | 15 | [features] 16 | default = [] 17 | print = [] 18 | -------------------------------------------------------------------------------- /examples/ipv4or6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipv4or6" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: ipv4 and ipv6 packets" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/ipv4or6" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | colored = ">= 1.6" 14 | netbricks = { path = "../../framework"} 15 | 16 | [features] 17 | default = [] 18 | print = [] 19 | -------------------------------------------------------------------------------- /examples/op-errors/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=op-errors 3 | PORT_OPTIONS1="dpdk:eth_pcap0,rx_pcap=data/ipv6_tcp.pcap,tx_pcap=/tmp/out.pcap" 4 | 5 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS1 -c 1 -d 1 6 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect_v6.out 7 | TEST_OUTPUT=$? 8 | 9 | cat test.log | tee /dev/tty | diff - data/op_errors.out 10 | TEST_LOG=$? 11 | 12 | result=$? 13 | echo ---- 14 | if [[ $TEST_OUTPUT != 0 ]] || [[ $TEST_LOG != 0 ]]; then 15 | echo "FAIL: Test Output - $TEST_OUTPUT | Test Log - $TEST_LOG" 16 | exit 1 17 | else 18 | echo "PASS" 19 | fi 20 | -------------------------------------------------------------------------------- /examples/signals/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signals" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: signal handling" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/signals" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | log = ">= 0.4" 14 | simplelog = "0.5" 15 | netbricks = { path = "../../framework" } 16 | 17 | [features] 18 | default = [] 19 | print = [] 20 | -------------------------------------------------------------------------------- /examples/chain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: chaining" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/chain" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | getopts = ">= 0.2" 14 | netbricks = { path = "../../framework" } 15 | 16 | [features] 17 | default = [] 18 | print = [] 19 | -------------------------------------------------------------------------------- /examples/collect-metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "collect-metrics" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: collecting metrics as timer task" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/collect-metrics" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | lazy_static = ">= 1.3" 14 | netbricks = { path = "../../framework"} 15 | 16 | [features] 17 | default = [] 18 | print = [] 19 | -------------------------------------------------------------------------------- /framework/src/utils/cidr/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | 3 | pub use self::v4::Ipv4Cidr; 4 | pub use self::v6::Ipv6Cidr; 5 | use failure::Fail; 6 | 7 | pub mod v4; 8 | pub mod v6; 9 | 10 | #[derive(Debug, Fail)] 11 | #[fail(display = "Failed to parse CIDR: {}", _0)] 12 | pub struct CidrParseError((String)); 13 | 14 | pub trait Cidr: Sized { 15 | type Addr; 16 | 17 | fn address(&self) -> Self::Addr; 18 | fn length(&self) -> usize; 19 | fn new(address: Self::Addr, length: usize) -> Result; 20 | fn contains(&self, address: Self::Addr) -> bool; 21 | fn contains_ip(&self, ip: IpAddr) -> bool; 22 | } 23 | -------------------------------------------------------------------------------- /examples/embedded-scheduler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-scheduler" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: embedded-scheduler works" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/embedded-scheduler" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework"} 14 | 15 | [features] 16 | default = [] 17 | print = [] 18 | -------------------------------------------------------------------------------- /examples/op-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "op-errors" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "Example: handling errors w/ operators" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/op-errors" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | failure = "0.1" 14 | log = ">= 0.4" 15 | simplelog = "0.5" 16 | netbricks = { path = "../../framework"} 17 | 18 | [features] 19 | default = [] 20 | print = [] 21 | -------------------------------------------------------------------------------- /examples/signals/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=signals 3 | 4 | PORT_OPTIONS="dpdk:eth_af_packet,iface=lo" 5 | 6 | nohup ../../build.sh run $TEST_NAME -p $PORT_OPTIONS -c 1 & 7 | # Extra time to load the signaler 8 | sleep 5 9 | PID=`pidof signals` 10 | kill -HUP "$PID" 11 | sleep 3 12 | kill -TERM "$PID" 13 | sleep 3 14 | 15 | echo ---- 16 | 17 | pidof signals 18 | if [[ $? == 0 ]]; then 19 | kill -9 "$PID" 20 | echo "FAIL: process still running" 21 | exit 1 22 | fi 23 | 24 | cat test.log | tee /dev/tty | diff - data/expect.out 25 | if [[ $? != 0 ]]; then 26 | echo "FAIL: wrong output" 27 | exit 1 28 | fi 29 | 30 | echo "PASS" 31 | -------------------------------------------------------------------------------- /examples/acl-fw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "acl-fw" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: acl forwarding table" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/acl-fw" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | fnv = ">= 1.0" 14 | lazy_static = ">= 1.3" 15 | netbricks = { path = "../../framework"} 16 | 17 | [features] 18 | default = [] 19 | print = [] 20 | -------------------------------------------------------------------------------- /examples/nat-tcp-v4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nat-tcp-v4" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: tcp ipv4 nat" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/nat-tcp-v4" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | fnv = ">= 1.0" 14 | lazy_static = ">= 1.3" 15 | netbricks = { path = "../../framework"} 16 | 17 | [features] 18 | default = [] 19 | print = [] 20 | -------------------------------------------------------------------------------- /scripts/travis-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install dependencies 4 | sudo apt-get -q update 5 | sudo apt-get -q install -y cmake mg make git linux-headers-`uname -r` linux-image-extra-$(uname -r) 6 | 7 | # Allocate 1024 hugepages of 2 MB 8 | # Change can be validated by executing 'cat /proc/meminfo | grep Huge' 9 | echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages 10 | 11 | # Allocate 1024 hugepages of 2 MB at startup 12 | echo "vm.nr_hugepages = 1024" >> /etc/sysctl.conf 13 | 14 | # Install the uio_pci_generic driver 15 | modprobe uio_pci_generic 16 | 17 | # Load modules at boot 18 | echo "uio" >> /etc/modules 19 | echo "uio_pci_generic" >> /etc/modules 20 | -------------------------------------------------------------------------------- /examples/lpm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lpm" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: lpm" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/lpm" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | colored = ">= 1.6" 14 | fnv = ">= 1.0" 15 | lazy_static = ">= 1.3" 16 | rand = "0.6" 17 | netbricks = { path = "../../framework" } 18 | 19 | [features] 20 | default = [] 21 | print = [] 22 | -------------------------------------------------------------------------------- /examples/maglev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "maglev" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda "] 5 | description = "Example: maglev" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/maglev" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework" } 14 | time = ">= 0.1" 15 | getopts = ">= 0.2" 16 | lazy_static = ">= 1.3" 17 | rand = "0.6" 18 | fnv = ">= 1.0" 19 | twox-hash = ">= 1.2" 20 | 21 | [features] 22 | default = [] 23 | print = [] 24 | -------------------------------------------------------------------------------- /examples/embedded-scheduler-dependency/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-scheduler-dependency" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: embedded-scheduler as dep works" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/embedded-scheduler-dependency" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | netbricks = { path = "../../framework"} 14 | 15 | [features] 16 | default = [] 17 | print = [] 18 | -------------------------------------------------------------------------------- /examples/sctp/ctl/controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import asyncio 4 | import struct 5 | async def connect_and_test(inter_message_gap): 6 | (reader, writer) = await asyncio.open_connection('127.0.0.1', 8001) 7 | print("Connected") 8 | times = int(300.0 / float(inter_message_gap)) 9 | for i in range(0, times): 10 | to_write = struct.pack('qBBBBBB', 2, 0x68, 0x05, 0xca, 0x33, 0xfd, 0xc9) 11 | writer.write(to_write) 12 | await writer.drain() 13 | await asyncio.sleep(inter_message_gap) 14 | 15 | if __name__ == "__main__": 16 | loop = asyncio.get_event_loop() 17 | loop.run_until_complete(connect_and_test(0.0001)) 18 | loop.close() 19 | -------------------------------------------------------------------------------- /examples/tcp-reconstruction/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tcp-payload" 3 | version = "0.1.0" 4 | authors = ["Steven H. Wang "] 5 | description = "Example: Tcp Reconstruction" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/tcp-reconstruction" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | time = ">= 0.1.0" 14 | getopts = ">= 0.2.19" 15 | rand = "0.6" 16 | fnv = ">= 1.0.6" 17 | twox-hash = ">= 1.2.0" 18 | netbricks = { path = "../../framework" } 19 | 20 | [features] 21 | default = [] 22 | print = [] 23 | -------------------------------------------------------------------------------- /scripts/tuning/read_cpu_dma_latency.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | import signal 4 | import struct 5 | import sys 6 | import time 7 | 8 | ALLOWED_INTERFACES = [ "cpu_dma_latency", "network_latency", "network_throughput" ] 9 | def read_pmqos(name): 10 | filename = "/dev/%s" % name 11 | old = open(filename) 12 | old_value = struct.unpack("i", old.read())[0] 13 | print "PMQOS value for %s is %d"%(name, old_value) 14 | if __name__=="__main__": 15 | if len(sys.argv) < 2: 16 | print "Must specify what to read" 17 | sys.exit(1) 18 | read = sys.argv[1] 19 | if read not in ALLOWED_INTERFACES: 20 | print "Cannot read %s"%read 21 | sys.exit(1) 22 | read_pmqos(read) 23 | -------------------------------------------------------------------------------- /examples/sctp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sctp" 3 | version = "0.1.0" 4 | authors = ["Aurojit Panda ", "William of Ockham "] 5 | description = "Example: sctp w/ control server task" 6 | license = "..." 7 | repository = "https://github.com/williamofockham/NetBricks/tree/master/examples/sctp" 8 | readme = "..." 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [dependencies] 13 | nix = ">=0.13" 14 | rust-sctp = { git="https://github.com/netsys/rust-sctp" } 15 | netbricks = { path = "../../framework", features = ["sctp"] } 16 | 17 | [features] 18 | default = [] 19 | print = [] 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "framework", 4 | "netbricks-codegen", 5 | "examples/acl-fw", 6 | "examples/chain", 7 | "examples/collect-metrics", 8 | "examples/echo-reply", 9 | "examples/embedded-scheduler", 10 | "examples/embedded-scheduler-dependency", 11 | "examples/ipv4or6", 12 | "examples/lpm", 13 | "examples/macswap", 14 | "examples/maglev", 15 | "examples/mtu-too-big", 16 | "examples/nat-tcp-v4", 17 | "examples/op-errors", 18 | "examples/sctp", 19 | "examples/signals", 20 | # "examples/tcp-reconstruction" 21 | ] 22 | 23 | [profile.release] 24 | opt-level = 3 25 | lto = true 26 | rpath = true 27 | debug = true 28 | debug-assertions = false 29 | -------------------------------------------------------------------------------- /native/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Make rte_hash_crc available to Rust. This adds some cost, will look into producing a pure Rust 6 | // version. 7 | uint32_t crc_hash_native(const void* data, uint32_t len, uint32_t initial) { 8 | return rte_hash_crc(data, len, initial); 9 | } 10 | 11 | uint16_t ipv4_cksum(const void* iphdr) { 12 | return rte_ipv4_cksum((const struct ipv4_hdr*)iphdr); 13 | } 14 | 15 | uint16_t ipv4_phdr_chksum(const struct ipv4_hdr* ipv4_hdr, uint64_t ol_flags) { 16 | return rte_ipv4_phdr_cksum(ipv4_hdr, ol_flags); 17 | } 18 | 19 | int validate_tx_offload(const struct rte_mbuf* m) { 20 | return rte_validate_tx_offload(m); 21 | } 22 | -------------------------------------------------------------------------------- /netbricks-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netbricks-codegen" 3 | version = "0.1.0" 4 | authors = ["William of Ockham "] 5 | description = "A network function framework written in Rust and using DPDK" 6 | license = "ISC" 7 | repository = "https://github.com/williamofockham/NetBricks" 8 | readme = "../README.md" 9 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 10 | categories = ["network-functions", "framework"] 11 | 12 | [lib] 13 | doctest = false 14 | proc-macro = true 15 | 16 | [dependencies] 17 | proc-macro2 = "0.4" 18 | quote = "0.6" 19 | syn = { version = "0.15", features = ["extra-traits", "full"] } 20 | 21 | [features] 22 | nightly = ["proc-macro2/nightly"] 23 | -------------------------------------------------------------------------------- /examples/ipv4or6/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=ipv4or6 3 | 4 | PORT_OPTIONS1="dpdk:eth_pcap0,rx_pcap=data/ipv4_tcp.pcap,tx_pcap=/tmp/out.pcap" 5 | PORT_OPTIONS2="dpdk:eth_pcap0,rx_pcap=data/ipv6_tcp.pcap,tx_pcap=/tmp/out.pcap" 6 | 7 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS1 -c 1 -d 1 8 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect_ipv4.out 9 | TEST_IPv4=$? 10 | 11 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS2 -c 1 -d 1 12 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect_ipv6.out 13 | TEST_IPv6=$? 14 | 15 | echo ---- 16 | if [[ $TEST_IPv4 != 0 ]] || [[ $TEST_IPv6 != 0 ]]; then 17 | echo "FAIL: IPv4 Test - $TEST_IPv4 | IPv6 Test - $TEST_IPv6" 18 | exit 1 19 | else 20 | echo "PASS" 21 | fi 22 | -------------------------------------------------------------------------------- /examples/echo-reply/data/echo_reply.out: -------------------------------------------------------------------------------- 1 | c2:01:51:fa:00:00 > c2:00:51:fa:00:00, ethertype IPv6 (0x86dd), length 114: 2001:db8:0:12::2 > 2001:db8:0:12::1: ICMP6, echo reply, seq 0, length 60 2 | c2:01:51:fa:00:00 > c2:00:51:fa:00:00, ethertype IPv6 (0x86dd), length 114: 2001:db8:0:12::2 > 2001:db8:0:12::1: ICMP6, echo reply, seq 1, length 60 3 | c2:01:51:fa:00:00 > c2:00:51:fa:00:00, ethertype IPv6 (0x86dd), length 114: 2001:db8:0:12::2 > 2001:db8:0:12::1: ICMP6, echo reply, seq 2, length 60 4 | c2:01:51:fa:00:00 > c2:00:51:fa:00:00, ethertype IPv6 (0x86dd), length 114: 2001:db8:0:12::2 > 2001:db8:0:12::1: ICMP6, echo reply, seq 3, length 60 5 | c2:01:51:fa:00:00 > c2:00:51:fa:00:00, ethertype IPv6 (0x86dd), length 114: 2001:db8:0:12::2 > 2001:db8:0:12::1: ICMP6, echo reply, seq 4, length 60 6 | -------------------------------------------------------------------------------- /examples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Lists all the examples that are runnable and/or testable. 3 | # This is used by the build script. 4 | export examples=( 5 | examples/echo-reply 6 | examples/ipv4or6 7 | examples/macswap 8 | examples/mtu-too-big 9 | examples/op-errors 10 | examples/signals 11 | ### Runnable examples | No Tests associated 12 | ### ======================================= 13 | examples/acl-fw 14 | examples/chain 15 | examples/collect-metrics 16 | examples/embedded-scheduler 17 | examples/embedded-scheduler-dependency 18 | examples/lpm 19 | examples/nat-tcp-v4 20 | examples/sctp 21 | examples/maglev 22 | # examples/tcp-reconstruction 23 | ) 24 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v4/mod.rs: -------------------------------------------------------------------------------- 1 | //TODO: Fill Out 2 | 3 | #[cfg(test)] 4 | #[rustfmt::skip] 5 | pub const ICMPV4_PACKET: [u8; 74] = [ 6 | // ** ethernet header 7 | 0x00, 0x50, 0x56, 0xe0, 0x14, 0x49, 8 | 0x00, 0x0c, 0x29, 0x34, 0x0B, 0xde, 9 | 0x08, 0x00, 10 | // ** IPv4 header 11 | 0x45, 0x00, 0x00, 0x3c, 12 | 0xd7, 0x43, 0x00, 0x00, 13 | 0x80, 0x01, 0x2b, 0x73, 14 | 0xc0, 0xa8, 0x9e, 0x8b, 15 | 0xae, 0x89, 0x2a, 0x4d, 16 | // ** ICMPv4 header 17 | 0x08, 0x00, 0x2a, 0x5c, 0x02, 0x00, 18 | 0x21, 0x00, 0x61, 0x62, 0x63, 0x64, 19 | 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 20 | 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 21 | 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 22 | 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 23 | 0x66, 0x67, 0x68, 0x69, 24 | ]; 25 | -------------------------------------------------------------------------------- /examples/mtu-too-big/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TEST_NAME=mtu-too-big 3 | 4 | PORT_OPTIONS1="dpdk:eth_pcap0,rx_pcap=data/in_bounds.pcap,tx_pcap=/tmp/out.pcap" 5 | PORT_OPTIONS2="dpdk:eth_pcap0,rx_pcap=data/over_bounds.pcap,tx_pcap=/tmp/out.pcap" 6 | 7 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS1 -c 1 -d 1 8 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect_ipv6.out 9 | IN_BOUNDS_IPv6=$? 10 | 11 | ../../build.sh run $TEST_NAME -p $PORT_OPTIONS2 -c 1 -d 1 12 | tcpdump -tner /tmp/out.pcap | tee /dev/tty | diff - data/expect_icmpv6_toobig.out 13 | OVER_BOUNDS_IPv6=$? 14 | 15 | echo ---- 16 | if [[ $IN_BOUNDS_IPv6 != 0 ]] || [[ $OVER_BOUNDS_IPv6 != 0 ]]; then 17 | echo "FAIL: V6 IN-BOUNDS TEST - $IN_BOUNDS_IPv6 | V6 OVER-BOUNDS TEST - $OVER_BOUNDS_IPv6" 18 | exit 1 19 | else 20 | echo "PASS" 21 | fi 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019, Aurojit Panda (UC Berkeley NetSys Lab) and Occam Engineering 4 | @Comcast. 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | build 11 | Cargo.lock 12 | /target/ 13 | target/ 14 | .make.dep 15 | *.tar.gz 16 | /native/test/test 17 | /native/*.o 18 | /native/*.so 19 | cobertura.xml 20 | 21 | # Remove rustfmt backups 22 | *.rs.bk 23 | *.o 24 | *.so 25 | 26 | # Vim files 27 | *.vi 28 | *.swp 29 | 30 | # cscope 31 | cscope* 32 | 33 | # vagrant 34 | .vagrant 35 | Vagrantfile 36 | 37 | # editor 38 | .vscode 39 | 40 | # intellij 41 | .idea 42 | *.iml 43 | 44 | # pcap 45 | out.pcap 46 | *.lnk 47 | 48 | # logs 49 | *.log 50 | *.out 51 | !examples/*/data/*.out 52 | 53 | # wireedit 54 | *.lnk 55 | 56 | .DS_Store 57 | 58 | # other 59 | moongen 60 | netbricks 61 | utils 62 | !framework/src/utils 63 | -------------------------------------------------------------------------------- /framework/src/operators/emit_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError}; 2 | use crate::packets::Packet; 3 | 4 | /// Lazily-evaluated emit operator 5 | /// 6 | /// Interrupts processing with a short-circuit error that simply emits the packet 7 | pub struct EmitBatch { 8 | source: B, 9 | } 10 | 11 | impl EmitBatch { 12 | #[inline] 13 | pub fn new(source: B) -> Self { 14 | EmitBatch { source } 15 | } 16 | } 17 | 18 | impl Batch for EmitBatch { 19 | type Item = B::Item; 20 | 21 | #[inline] 22 | fn next(&mut self) -> Option> { 23 | self.source.next().map(|item| match item { 24 | Ok(packet) => Err(PacketError::Emit(packet.mbuf())), 25 | e @ Err(_) => e, 26 | }) 27 | } 28 | 29 | #[inline] 30 | fn receive(&mut self) { 31 | self.source.receive(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /native/ethpacket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mempool.h" 5 | 6 | void set_packet_data(mbuf_array_t array, int cnt, int offset, void* data, int size) { 7 | for (int i = 0; i < cnt; i++) { 8 | void* dest = rte_pktmbuf_mtod_offset(array[i], void*, offset); 9 | rte_memcpy(dest, data, size); 10 | } 11 | } 12 | 13 | void set_packet_data_at_offset(mbuf_array_t array, int* offsets, int cnt, void* data, int size) { 14 | for (int i = 0; i < cnt; i++) { 15 | void* dest = rte_pktmbuf_mtod_offset(array[i], void*, offsets[i]); 16 | rte_memcpy(dest, data, size); 17 | } 18 | } 19 | 20 | void set_ether_type(mbuf_array_t array, int cnt, uint16_t ether) { 21 | for (int i = 0; i < cnt; i++) { 22 | struct ether_hdr* hdr = rte_pktmbuf_mtod(array[i], struct ether_hdr*); 23 | hdr->ether_type = ether; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/read-target.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Take the output of Cargo.toml and print target name. 4 | """ 5 | from __future__ import print_function 6 | import sys 7 | import json 8 | 9 | def main(inp): 10 | try: 11 | o = json.loads(inp) 12 | except: 13 | print("Failed to interpret JSON", file=sys.stderr) 14 | if 'targets' in o: 15 | for target in o['targets']: 16 | if 'kind' in target and (target['kind'] == 'bin' or 'bin' in target['kind']): 17 | print(target['name']) 18 | else: 19 | print("No kind found") 20 | 21 | if __name__=="__main__": 22 | if len(sys.argv) < 2: 23 | print("Usage: %s json"%sys.argv[0], file=sys.stderr) 24 | sys.exit(1) 25 | if len(sys.argv) == 2 and sys.argv[1] == '-': 26 | inp = sys.stdin.read() 27 | main(inp) 28 | else: 29 | main(' '.join(sys.argv[1:])) 30 | -------------------------------------------------------------------------------- /examples/op-errors/data/op_errors.out: -------------------------------------------------------------------------------- 1 | [WARN] v6: fe80::1 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 2 | [WARN] v6: fe80::3 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 3 | [WARN] v6: fe80::4 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 4 | [WARN] v6: fe80::5 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 5 | [WARN] v6: fe80::6 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 6 | [WARN] v6: fe80::7 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 7 | [WARN] v6: fe80::8 > fe80::2, version: 6, dscp: 0, ecn: 0, flow_label: 0, len: 20, next_header: TCP, hop_limit: 64 8 | [ERROR] directed by danny devito 9 | [ERROR] directed by danny devito 10 | [ERROR] directed by danny devito 11 | -------------------------------------------------------------------------------- /framework/src/common/errors.rs: -------------------------------------------------------------------------------- 1 | use failure::Error; 2 | 3 | pub type Result = ::std::result::Result; 4 | 5 | #[macro_export] 6 | macro_rules! error_chain { 7 | ($error:expr) => { 8 | error!("{}", $crate::common::string_chain($error)) 9 | }; 10 | } 11 | 12 | #[macro_export] 13 | macro_rules! warn_chain { 14 | ($error:expr) => { 15 | warn!("{}", $crate::common::string_chain($error)) 16 | }; 17 | } 18 | 19 | /// Converts a `failure::Error` to a string with the causes and the backtrace. 20 | pub fn string_chain(e: &Error) -> String { 21 | let mut error = e.to_string(); 22 | 23 | for cause in e.iter_causes() { 24 | error.push_str(&format!("\nCaused by: {}", cause)); 25 | } 26 | 27 | if let Ok("1") = ::std::env::var("RUST_BACKTRACE") 28 | .as_ref() 29 | .map(|s| s.as_str()) 30 | { 31 | error.push_str(&format!("\nBacktrace:\n{}", e.backtrace())) 32 | } 33 | 34 | error 35 | } 36 | -------------------------------------------------------------------------------- /native/include/mempool.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMPOOL_H__ 2 | #define __MEMPOOL_H__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | RTE_DECLARE_PER_LCORE(int, _mempool_core); 11 | 12 | typedef struct rte_mbuf* restrict* restrict mbuf_array_t; 13 | /* Called by system initialization */ 14 | int init_mempool_core(int core); 15 | int init_mempool(int master_core, unsigned int mempool_size, unsigned int mcache_size, unsigned short slots); 16 | int init_secondary_mempool(const char* mempool_name); 17 | int find_secondary_mempool(); 18 | struct rte_mbuf* mbuf_alloc(); 19 | void mbuf_free(struct rte_mbuf* buf); 20 | int mbuf_alloc_bulk(mbuf_array_t array, uint16_t len, int cnt); 21 | int mbuf_free_bulk(mbuf_array_t array, int cnt); 22 | struct rte_mempool* get_pframe_pool(int coreid, int sid); 23 | struct rte_mempool* get_mempool_for_core(int coreid); 24 | #endif 25 | -------------------------------------------------------------------------------- /examples/op-errors/data/expect_v6.out: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 2 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::3.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 3 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::4.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 4 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::5.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 5 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::6.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 6 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::7.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 7 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::8.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 8 | -------------------------------------------------------------------------------- /scripts/check-examples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | """ 5 | import sys 6 | import os 7 | def main(*directories): 8 | directory_set = set(directories) 9 | base_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'test') 10 | print("Searching in %s"%(base_dir)) 11 | for (dirpath, dirnames, filenames) in os.walk(base_dir): 12 | for filename in filenames: 13 | if filename == "Cargo.toml": 14 | rest,dir=os.path.split(dirpath) 15 | root=os.path.split(rest)[1] 16 | test_dir=os.path.join(root, dir) 17 | if root == 'test' and test_dir not in directory_set: 18 | print("Found Cargo.toml in %s but not in build.sh"%(test_dir)) 19 | sys.exit(1) 20 | sys.exit(0) 21 | if __name__ == "__main__": 22 | if len(sys.argv) == 1: 23 | print("Usage: %s json"%sys.argv[0], file=sys.stderr) 24 | sys.exit(1) 25 | else: 26 | main(*sys.argv[1:]) 27 | -------------------------------------------------------------------------------- /framework/src/control/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "linux")] 2 | pub use self::epoll::*; 3 | 4 | #[cfg(target_os = "linux")] 5 | #[path = "linux/epoll.rs"] 6 | mod epoll; 7 | #[cfg(feature = "sctp")] 8 | pub mod sctp; 9 | pub mod tcp; 10 | 11 | use std::os::unix::io::RawFd; 12 | 13 | pub type Available = u64; 14 | 15 | pub const NONE: u64 = 0x0; 16 | pub const READ: u64 = 0x1; 17 | pub const WRITE: u64 = 0x2; 18 | pub const HUP: u64 = 0x4; 19 | 20 | pub struct IOScheduler { 21 | fd: RawFd, 22 | scheduler: PollHandle, 23 | token: Token, 24 | } 25 | 26 | impl IOScheduler { 27 | pub fn new(scheduler: PollHandle, fd: RawFd, token: Token) -> IOScheduler { 28 | scheduler.new_io_fd(fd, token); 29 | IOScheduler { 30 | fd, 31 | scheduler, 32 | token, 33 | } 34 | } 35 | 36 | pub fn schedule_read(&self) { 37 | self.scheduler.schedule_read_rawfd(self.fd, self.token); 38 | } 39 | 40 | pub fn schedule_write(&self) { 41 | self.scheduler.schedule_write_rawfd(self.fd, self.token); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /framework/src/utils/asm.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub fn cpuid() { 3 | unsafe { 4 | asm!("movl $$0x2, %eax":::"eax"); 5 | asm!("movl $$0x0, %ecx":::"ecx"); 6 | asm!("cpuid" 7 | : 8 | : 9 | : "rax rbx rcx rdx"); 10 | } 11 | } 12 | 13 | #[inline] 14 | pub fn rdtsc_unsafe() -> u64 { 15 | unsafe { 16 | let low: u32; 17 | let high: u32; 18 | asm!("rdtsc" 19 | : "={eax}" (low), "={edx}" (high) 20 | : 21 | : "rdx rax" 22 | : "volatile"); 23 | (u64::from(high) << 32) | u64::from(low) 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn rdtscp_unsafe() -> u64 { 29 | let high: u32; 30 | let low: u32; 31 | unsafe { 32 | asm!("rdtscp" 33 | : "={eax}" (low), "={edx}" (high) 34 | : 35 | : "ecx" 36 | : "volatile"); 37 | (u64::from(high) << 32) | u64::from(low) 38 | } 39 | } 40 | 41 | #[inline] 42 | pub fn pause() { 43 | unsafe { 44 | asm!("pause"::::"volatile"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /framework/tests/ring_buffer.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | use netbricks::state::*; 3 | 4 | #[test] 5 | fn alloc_test() { 6 | let rb = RingBuffer::new(1).unwrap(); 7 | drop(rb); 8 | } 9 | 10 | #[test] 11 | fn write_at_offset_test() { 12 | let mut rb = RingBuffer::new(4096).unwrap(); 13 | let input = vec![1, 2, 3, 4]; 14 | rb.write_at_offset(4095, &input[..]); 15 | let mut output: Vec<_> = (0..input.len()).map(|_| 0).collect(); 16 | rb.read_from_offset(4095, &mut output[..]); 17 | for idx in 0..input.len() { 18 | assert_eq!(input[idx], output[idx]); 19 | } 20 | } 21 | 22 | #[test] 23 | fn read_write_tail_test() { 24 | let mut rb = RingBuffer::new(4096).unwrap(); 25 | let input: Vec<_> = (0..8192).map(|i| (i & 0xff) as u8).collect(); 26 | let written = rb.write_at_tail(&input[..]); 27 | assert_eq!(written, 4095); 28 | let mut output: Vec<_> = (0..8192).map(|_| 0).collect(); 29 | let read = rb.read_from_head(&mut output[..]); 30 | assert_eq!(read, written); 31 | for idx in 0..read { 32 | assert_eq!(input[idx], output[idx]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /scripts/translate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from clang.cindex import * 3 | 4 | def FindStruct(node, name): 5 | 6 | if node.kind is CursorKind.STRUCT_DECL and node.spelling == name: 7 | return node 8 | for c in node.get_children(): 9 | u = FindStruct(c, name) 10 | if u: 11 | return u 12 | return None 13 | 14 | def PrintTypes(scursor): 15 | offset = 0 16 | for c in scursor.get_children(): 17 | if c.kind is not CursorKind.FIELD_DECL: 18 | print c.kind 19 | continue 20 | var = ''.join(x for x in c.spelling.title() if x is not '_') 21 | if var == "Cacheline1": 22 | assert(offset < 64) 23 | offset = 64 24 | if c.type.get_size() == 0: 25 | continue 26 | type = c.type.spelling if c.type.kind is not TypeKind.POINTER else "IntPtr" 27 | print offset, var, type, c.type.get_size() 28 | offset += c.type.get_size() 29 | 30 | if __name__ == "__main__": 31 | f = sys.argv[1] 32 | index = Index.create() 33 | tu = index.parse(sys.argv[1], ["-DRTE_NEXT_ABI"]) 34 | cursor = FindStruct(tu.cursor, "rte_mbuf") 35 | print cursor.location.line 36 | PrintTypes(cursor) 37 | -------------------------------------------------------------------------------- /framework/src/interface/port/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::phy_port::*; 2 | pub use self::virt_port::*; 3 | use allocators::*; 4 | use common::*; 5 | use interface::{PacketRx, PacketTx}; 6 | use native::mbuf::MBuf; 7 | use packets::MacAddr; 8 | use std::sync::atomic::AtomicUsize; 9 | 10 | mod phy_port; 11 | mod virt_port; 12 | 13 | pub trait PortInfo { 14 | fn mac_address(&self) -> MacAddr; 15 | } 16 | 17 | /// Statistics for PMD port. 18 | pub struct PortStats { 19 | pub stats: AtomicUsize, 20 | } 21 | 22 | impl PortStats { 23 | pub fn new() -> CacheAligned { 24 | CacheAligned::allocate(PortStats { 25 | stats: AtomicUsize::new(0), 26 | }) 27 | } 28 | } 29 | 30 | impl PacketRx for CacheAligned { 31 | #[inline] 32 | fn recv(&self, pkts: &mut [*mut MBuf]) -> Result { 33 | T::recv(&*self, pkts) 34 | } 35 | } 36 | 37 | impl PacketTx for CacheAligned { 38 | #[inline] 39 | fn send(&self, pkts: &mut [*mut MBuf]) -> Result { 40 | T::send(&*self, pkts) 41 | } 42 | } 43 | 44 | impl PortInfo for CacheAligned { 45 | #[inline] 46 | fn mac_address(&self) -> MacAddr { 47 | T::mac_address(&*self) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: xenial 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | language: generic 9 | 10 | notifications: 11 | email: false 12 | slack: 13 | secure: L6KjcG0lpme5cZ3y0GDTB7zlWC8uvZlSbRkxv5gz+bnUopcJ/i3pyFVVdmu24lAHhxwZDrPtMoVlSVNLq/mK5Jybq/38bsO7qWkNuv9GbRokbkjU+FQ/JAvvImeuJzaL7Pjcg8bmjW1B/sKw3nFB8gmtHe4hHamu0M9TaSR2dgRU78MizQ4aAxYrpl53fusngy9qVZdQvY1LvEGXzlsP+RwQnkvmq92wXA8+sl1Nds3AgorrAkpiv7uAMjxmAo8ZcL/jMn3IBZ7IjgDBIttg1sRn327j4MxZQ1qq6D5fAnUUX4SgFoSxHWEJ42oFH/UDDBpPtTITdeSzUIfTMQY71pRHf37CeVLpjfQ2ud9kGT2mrvrYh8ayeQ54iTgLPzc3XEm28mvzkZX7upi1/It27MObmjVFsLyrh1/4EASsKowfZzNNnmX1KSU/96kbnSf5J8fHExiedBIPu2O8bGeTe7/dE9WLTfrm0IoSGfLdJQIz+pKgCLpwqe3OCX4Wl/pW7JXl3pnx3EV2YncatiarL1EkyJoN2EQR3Jo/ixEuA6I8/9ge9BdHM3sNXlyYNu2a3F7O/g8E51yif6FMRshws1HrOajFcXc6Pw7P0VTNr360koxA7DRDjoO8qLYFSJPjHpFRQvsxVFv8tzYts6NtDG0WWRSgJnkHcTpnbdqfrNQ= 14 | 15 | services: 16 | - docker 17 | 18 | before_script: 19 | - sudo ./scripts/travis-setup.sh 20 | 21 | jobs: 22 | include: 23 | - stage: test 24 | script: 25 | - make -f docker.mk run-tests 26 | after_success: 27 | - make -f docker.mk run-cov && bash <(curl -s https://codecov.io/bash) 28 | - stage: lint 29 | script: 30 | - make -f docker.mk run-lint 31 | 32 | -------------------------------------------------------------------------------- /examples/embedded-scheduler-dependency/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | use netbricks::scheduler::*; 3 | 4 | pub struct DepTask { 5 | id: String, 6 | deps: Vec, 7 | } 8 | 9 | impl Executable for DepTask { 10 | fn execute(&mut self) { 11 | println!("Task -- {}", self.id); 12 | } 13 | fn dependencies(&mut self) -> Vec { 14 | self.deps.clone() 15 | } 16 | } 17 | impl DepTask { 18 | pub fn new(parent: usize, id: &str) -> DepTask { 19 | DepTask { 20 | id: String::from(id), 21 | deps: vec![parent], 22 | } 23 | } 24 | } 25 | 26 | fn test_func(id: &str) { 27 | println!("Base Task -- {}", id); 28 | } 29 | 30 | fn main() { 31 | let mut sched = embedded_scheduler::EmbeddedScheduler::new(); 32 | let handle0 = sched.add_task(|| test_func("task-0")).unwrap(); 33 | let other_handles = { 34 | let mut prev_handle = handle0; 35 | let mut nhandles = vec![]; 36 | 37 | for i in 0..10 { 38 | let task = sched 39 | .add_task(DepTask::new(prev_handle, format!("id-{}", i).as_str())) 40 | .unwrap(); 41 | nhandles.push(task); 42 | prev_handle = task; 43 | } 44 | nhandles 45 | }; 46 | let len = other_handles.len(); 47 | sched.exec_task(other_handles[len - 1]); 48 | } 49 | -------------------------------------------------------------------------------- /framework/src/operators/filter_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError}; 2 | use crate::packets::Packet; 3 | 4 | /// Lazily-evaluated filter operator 5 | /// 6 | /// If the predicate evaluates to `false`, the packet is marked as 7 | /// dropped and will short-circuit the remainder of the pipeline. 8 | pub struct FilterBatch 9 | where 10 | P: FnMut(&B::Item) -> bool, 11 | { 12 | source: B, 13 | predicate: P, 14 | } 15 | 16 | impl FilterBatch 17 | where 18 | P: FnMut(&B::Item) -> bool, 19 | { 20 | #[inline] 21 | pub fn new(source: B, predicate: P) -> Self { 22 | FilterBatch { source, predicate } 23 | } 24 | } 25 | 26 | impl Batch for FilterBatch 27 | where 28 | P: FnMut(&B::Item) -> bool, 29 | { 30 | type Item = B::Item; 31 | 32 | #[inline] 33 | fn next(&mut self) -> Option> { 34 | self.source.next().map(|item| match item { 35 | Ok(packet) => { 36 | if (self.predicate)(&packet) { 37 | Ok(packet) 38 | } else { 39 | Err(PacketError::Drop(packet.mbuf())) 40 | } 41 | } 42 | e @ Err(_) => e, 43 | }) 44 | } 45 | 46 | #[inline] 47 | fn receive(&mut self) { 48 | self.source.receive(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /framework/src/scheduler/mod.rs: -------------------------------------------------------------------------------- 1 | /// All projects involve building a thread pool. This is the task equivalent for 2 | /// the threadpool in `NetBricks`. 3 | /// 4 | /// Anything that implements Runnable can be polled by the scheduler. This thing 5 | /// can be a `Batch` (e.g., `SendBatch`) or something else (e.g., the `GroupBy` 6 | /// operator). 7 | use common::*; 8 | use failure::Fail; 9 | 10 | pub use self::context::*; 11 | pub use self::standalone_scheduler::*; 12 | 13 | mod context; 14 | pub mod embedded_scheduler; 15 | mod standalone_scheduler; 16 | 17 | /// Errors related to schedulers/scheduling 18 | // TODO: extend this, as we probably want more error handling over 19 | // scheduling 20 | #[derive(Debug, Fail)] 21 | pub enum SchedulerError { 22 | #[fail(display = "No scheduler running on core {}", _0)] 23 | NoRunningSchedulerOnCore(i32), 24 | } 25 | 26 | pub trait Executable { 27 | fn execute(&mut self); 28 | fn dependencies(&mut self) -> Vec; 29 | } 30 | 31 | impl Executable for F 32 | where 33 | F: FnMut(), 34 | { 35 | fn execute(&mut self) { 36 | (*self)() 37 | } 38 | 39 | fn dependencies(&mut self) -> Vec { 40 | vec![] 41 | } 42 | } 43 | 44 | pub trait Scheduler { 45 | fn add_task(&mut self, task: T) -> Result 46 | where 47 | Self: Sized; 48 | } 49 | -------------------------------------------------------------------------------- /netbricks-codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "128"] 2 | 3 | extern crate proc_macro; 4 | extern crate quote; 5 | extern crate syn; 6 | 7 | use proc_macro::TokenStream; 8 | use quote::quote; 9 | use syn::{parse_macro_input, ItemFn}; 10 | 11 | /// Procedural Macro for running dpdk-tests within a single-thread. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// #[cfg(test)] 17 | /// pub mod tests { 18 | /// use super::*; 19 | /// use netbricks::testing::dpdk_test; 20 | /// 21 | /// #[dpdk_test] 22 | /// fn test_drop() { 23 | /// ... 24 | /// assert!(drop); 25 | /// } 26 | /// ``` 27 | #[proc_macro_attribute] 28 | pub fn dpdk_test(_args: TokenStream, input: TokenStream) -> TokenStream { 29 | let input = parse_macro_input!(input as ItemFn); 30 | 31 | let test = &input.ident; 32 | let block = &input.block; 33 | 34 | let run = quote! { 35 | #[test] 36 | fn #test() { 37 | let f = ::netbricks::testing::DPDK_TEST_POOL.spawn_handle(::netbricks::testing::lazy(|| { 38 | ::std::panic::catch_unwind(|| { 39 | #block 40 | }) 41 | })); 42 | 43 | if let Err(e) = ::netbricks::testing::Future::wait(f) { 44 | ::std::panic::resume_unwind(e); 45 | } 46 | } 47 | }; 48 | 49 | run.into() 50 | } 51 | -------------------------------------------------------------------------------- /framework/src/operators/foreach_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError}; 2 | use crate::packets::Packet; 3 | use failure::Error; 4 | 5 | /// Lazily-evaluate foreach operator 6 | /// 7 | /// Works on reference of packet for side-effects. 8 | /// 9 | /// On error, the packet is marked as aborted and will short-circuit the 10 | /// remainder of the pipeline. 11 | pub struct ForEachBatch 12 | where 13 | F: FnMut(&B::Item) -> Result<(), Error>, 14 | { 15 | source: B, 16 | fun: F, 17 | } 18 | 19 | impl ForEachBatch 20 | where 21 | F: FnMut(&B::Item) -> Result<(), Error>, 22 | { 23 | #[inline] 24 | pub fn new(source: B, fun: F) -> Self { 25 | ForEachBatch { source, fun } 26 | } 27 | } 28 | 29 | impl Batch for ForEachBatch 30 | where 31 | F: FnMut(&B::Item) -> Result<(), Error>, 32 | { 33 | type Item = B::Item; 34 | 35 | #[inline] 36 | fn next(&mut self) -> Option> { 37 | self.source.next().map(|item| match item { 38 | Ok(packet) => match (self.fun)(&packet) { 39 | Ok(_) => Ok(packet), 40 | Err(e) => Err(PacketError::Abort(packet.mbuf(), e)), 41 | }, 42 | Err(e) => Err(e), 43 | }) 44 | } 45 | 46 | #[inline] 47 | fn receive(&mut self) { 48 | self.source.receive(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/ipv4or6/data/expect_ipv6.out: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 2 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 3 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 4 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 5 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 6 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 7 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 8 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 9 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 10 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv6 (0x86dd), length 74: fe80::1.1025 > fe80::2.1024: Flags [S], seq 1, win 10, length 0 11 | -------------------------------------------------------------------------------- /framework/src/operators/map_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError}; 2 | use crate::packets::Packet; 3 | use failure::Error; 4 | 5 | /// Lazily-evaluate map operator 6 | /// 7 | /// On error, the packet is marked as aborted and will short-circuit the 8 | /// remainder of the pipeline. 9 | pub struct MapBatch 10 | where 11 | M: FnMut(B::Item) -> Result, 12 | { 13 | source: B, 14 | map: M, 15 | } 16 | 17 | impl MapBatch 18 | where 19 | M: FnMut(B::Item) -> Result, 20 | { 21 | #[inline] 22 | pub fn new(source: B, map: M) -> Self { 23 | MapBatch { source, map } 24 | } 25 | } 26 | 27 | impl Batch for MapBatch 28 | where 29 | M: FnMut(B::Item) -> Result, 30 | { 31 | type Item = T; 32 | 33 | #[inline] 34 | fn next(&mut self) -> Option> { 35 | self.source.next().map(|item| { 36 | match item { 37 | Ok(packet) => { 38 | // TODO: can this be more efficient? 39 | let mbuf = packet.mbuf(); 40 | (self.map)(packet).map_err(|e| PacketError::Abort(mbuf, e)) 41 | } 42 | Err(e) => Err(e), 43 | } 44 | }) 45 | } 46 | 47 | #[inline] 48 | fn receive(&mut self) { 49 | self.source.receive(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/ipv4or6/data/expect_ipv4.out: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 2 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 3 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 4 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 5 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 6 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 7 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 8 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 9 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 10 | 00:00:00:00:00:00 > 00:00:00:00:00:01, ethertype IPv4 (0x0800), length 60: 10.0.0.1.1025 > 10.0.0.2.1024: Flags [S], seq 1:7, win 10, length 6 11 | -------------------------------------------------------------------------------- /examples/signals/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate netbricks; 4 | extern crate simplelog; 5 | use log::Level; 6 | use netbricks::common::Result; 7 | use netbricks::config::load_config; 8 | use netbricks::runtime::{Runtime, SIGHUP, SIGTERM}; 9 | use simplelog::{Config, LevelFilter, WriteLogger}; 10 | use std::fs::File as StdFile; 11 | 12 | fn start_logger() -> Result<()> { 13 | WriteLogger::init( 14 | LevelFilter::Warn, 15 | Config { 16 | time: None, 17 | level: Some(Level::Error), 18 | target: Some(Level::Debug), 19 | location: Some(Level::Trace), 20 | time_format: None, 21 | }, 22 | StdFile::create("test.log").unwrap(), 23 | ) 24 | .map_err(|e| e.into()) 25 | } 26 | 27 | fn on_signal(signal: i32) -> std::result::Result<(), i32> { 28 | match signal { 29 | SIGHUP => { 30 | warn!("SIGHUP."); 31 | Ok(()) 32 | } 33 | SIGTERM => { 34 | warn!("SIGTERM."); 35 | Err(0) 36 | } 37 | _ => { 38 | warn!("unknown signal."); 39 | Err(1) 40 | } 41 | } 42 | } 43 | 44 | fn main() -> Result<()> { 45 | start_logger()?; 46 | let configuration = load_config()?; 47 | println!("{}", configuration); 48 | let mut runtime = Runtime::init(&configuration)?; 49 | runtime.set_on_signal(on_signal); 50 | runtime.execute() 51 | } 52 | -------------------------------------------------------------------------------- /examples/macswap/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | use netbricks::common::Result; 3 | use netbricks::config::load_config; 4 | use netbricks::interface::{PacketRx, PacketTx}; 5 | use netbricks::operators::{Batch, ReceiveBatch}; 6 | use netbricks::packets::{Ethernet, Packet, RawPacket}; 7 | use netbricks::runtime::Runtime; 8 | use netbricks::scheduler::Scheduler; 9 | use std::fmt::Display; 10 | 11 | fn install(ports: Vec, sched: &mut S) 12 | where 13 | T: PacketRx + PacketTx + Display + Clone + 'static, 14 | S: Scheduler + Sized, 15 | { 16 | for port in &ports { 17 | println!("Receiving port {}", port); 18 | } 19 | 20 | let pipelines: Vec<_> = ports 21 | .iter() 22 | .map(|port| { 23 | ReceiveBatch::new(port.clone()) 24 | .map(macswap) 25 | .send(port.clone()) 26 | }) 27 | .collect(); 28 | 29 | println!("Running {} pipelines", pipelines.len()); 30 | for pipeline in pipelines { 31 | sched.add_task(pipeline).unwrap(); 32 | } 33 | } 34 | 35 | fn macswap(packet: RawPacket) -> Result { 36 | assert!(packet.refcnt() == 1); 37 | let mut ethernet = packet.parse::()?; 38 | ethernet.swap_addresses(); 39 | Ok(ethernet) 40 | } 41 | 42 | fn main() -> Result<()> { 43 | let configuration = load_config()?; 44 | println!("{}", configuration); 45 | let mut runtime = Runtime::init(&configuration)?; 46 | runtime.add_pipeline_to_run(install); 47 | runtime.execute() 48 | } 49 | -------------------------------------------------------------------------------- /framework/src/operators/filtermap_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError}; 2 | use crate::packets::Packet; 3 | use failure::Error; 4 | 5 | /// Lazily-evaluate filter_map operator 6 | /// 7 | /// On error, the packet is marked as aborted and will short-circuit the 8 | /// remainder of the pipeline. 9 | pub struct FilterMapBatch 10 | where 11 | F: FnMut(B::Item) -> Result, Error>, 12 | { 13 | source: B, 14 | f: F, 15 | } 16 | 17 | impl FilterMapBatch 18 | where 19 | F: FnMut(B::Item) -> Result, Error>, 20 | { 21 | #[inline] 22 | pub fn new(source: B, f: F) -> Self { 23 | FilterMapBatch { source, f } 24 | } 25 | } 26 | 27 | impl Batch for FilterMapBatch 28 | where 29 | F: FnMut(B::Item) -> Result, Error>, 30 | { 31 | type Item = T; 32 | 33 | #[inline] 34 | fn next(&mut self) -> Option> { 35 | self.source.next().map(|item| match item { 36 | Ok(packet) => { 37 | let mbuf = packet.mbuf(); 38 | match (self.f)(packet) { 39 | Ok(Some(p)) => Ok(p), 40 | Ok(None) => Err(PacketError::Drop(mbuf)), 41 | Err(e) => Err(PacketError::Abort(mbuf, e)), 42 | } 43 | } 44 | Err(e) => Err(e), 45 | }) 46 | } 47 | 48 | #[inline] 49 | fn receive(&mut self) { 50 | self.source.receive(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /native/include/simd.h: -------------------------------------------------------------------------------- 1 | #ifndef _SIMD_H_ 2 | #define _SIMD_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define __xmm_aligned __attribute__((aligned(16))) 9 | #define __ymm_aligned __attribute__((aligned(32))) 10 | #define __zmm_aligned __attribute__((aligned(64))) 11 | 12 | #if !__SSSE3__ 13 | #error CPU must be at least Core 2 or equivalent (SSSE3 required) 14 | #endif 15 | 16 | static inline void print_m128i(__m128i a) { 17 | uint32_t b[4] __xmm_aligned; 18 | 19 | *((__m128i *)b) = a; 20 | printf("%08x %08x %08x %08x\n", b[0], b[1], b[2], b[3]); 21 | } 22 | 23 | static inline __m128i gather_m128i(void *a, void *b) { 24 | #if 1 25 | /* faster (in a tight loop test. sometimes slower...) */ 26 | __m128i t = _mm_loadl_epi64((__m128i *)a); 27 | return (__m128i)_mm_loadh_pd((__m128d)t, (double *)b); 28 | #else 29 | return _mm_set_epi64x(*((uint64_t *)b), *((uint64_t *)a)); 30 | #endif 31 | } 32 | 33 | #if __AVX__ 34 | 35 | static inline void print_m256i(__m256i a) { 36 | uint32_t b[8] __ymm_aligned; 37 | 38 | *((__m256i *)b) = a; 39 | printf("%08x %08x %08x %08x %08x %08x %08x %08x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); 40 | } 41 | 42 | static inline __m256d concat_two_m128d(__m128d a, __m128d b) { 43 | #if 1 44 | /* faster */ 45 | return _mm256_insertf128_pd(_mm256_castpd128_pd256(a), b, 1); 46 | #else 47 | return _mm256_permute2f128_si256(_mm256_castsi128_si256(a), _mm256_castsi128_si256(b), (2 << 4) | 0); 48 | #endif 49 | } 50 | 51 | #endif /* __AVX__ */ 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /examples/tcp-reconstruction/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_syntax)] 2 | #![feature(asm)] 3 | extern crate fnv; 4 | extern crate getopts; 5 | extern crate netbricks; 6 | extern crate rand; 7 | extern crate time; 8 | use self::nf::*; 9 | use netbricks::allocators::CacheAligned; 10 | use netbricks::config::*; 11 | use netbricks::interface::*; 12 | use netbricks::operators::*; 13 | use netbricks::scheduler::*; 14 | use std::env; 15 | use std::sync::Arc; 16 | use std::thread::sleep; 17 | use std::time::Duration; 18 | mod nf; 19 | 20 | fn test(ports: Vec>, sched: &mut S) { 21 | let pipelines: Vec<_> = ports 22 | .iter() 23 | .map(|port| reconstruction(ReceiveBatch::new(port.clone()), sched).send(port.clone())) 24 | .collect(); 25 | for pipeline in pipelines { 26 | sched.add_task(pipeline).unwrap(); 27 | } 28 | } 29 | 30 | fn main() { 31 | let opts = basic_opts(); 32 | 33 | let args: Vec = env::args().collect(); 34 | let matches = match opts.parse(&args[1..]) { 35 | Ok(m) => m, 36 | Err(f) => panic!(f.to_string()), 37 | }; 38 | let mut configuration = read_matches(&matches, &opts); 39 | configuration.pool_size = 256; // Travis allows 512 hugepages, but reliably continguously produces 256. 40 | 41 | let mut config = initialize_system(&configuration).unwrap(); 42 | config.start_schedulers(); 43 | 44 | config.add_pipeline_to_run(Arc::new(move |p, s: &mut StandaloneScheduler| test(p, s))); 45 | println!("BEGIN TEST OUTPUT"); 46 | config.execute(); 47 | 48 | sleep(Duration::from_secs(10)); 49 | } 50 | -------------------------------------------------------------------------------- /framework/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Used for cache alignment. 2 | // https://github.com/rust-lang/rust/issues/32838 3 | #![feature(allocator_api)] 4 | #![feature(asm)] 5 | // https://github.com/rust-lang/rust/issues/27730 6 | // common workaround: https://github.com/rayon-rs/rayon-hash/blob/master/src/ptr.rs 7 | #![feature(ptr_internals)] 8 | // https://github.com/rust-lang/rust/issues/31844 9 | #![feature(specialization)] 10 | 11 | #[macro_use] 12 | extern crate clap; 13 | extern crate config as config_rs; 14 | #[cfg_attr(test, macro_use)] 15 | extern crate failure; 16 | extern crate fallible_iterator; 17 | extern crate fnv; 18 | extern crate hex; 19 | #[macro_use] 20 | extern crate lazy_static; 21 | extern crate libc; 22 | #[macro_use] 23 | extern crate log; 24 | extern crate net2; 25 | extern crate netbricks_codegen; 26 | #[cfg(unix)] 27 | extern crate nix; 28 | #[cfg(any(test, feature = "test"))] 29 | #[cfg_attr(any(test, feature = "test"), macro_use)] 30 | extern crate proptest; 31 | extern crate regex; 32 | #[cfg(feature = "sctp")] 33 | extern crate sctp; 34 | extern crate self as netbricks; 35 | extern crate serde; 36 | #[macro_use] 37 | extern crate serde_derive; 38 | extern crate tokio; 39 | extern crate tokio_signal; 40 | extern crate tokio_threadpool; 41 | extern crate twox_hash; 42 | 43 | #[macro_use] 44 | pub mod common; 45 | pub mod allocators; 46 | pub mod config; 47 | pub mod control; 48 | pub mod interface; 49 | #[allow(dead_code)] 50 | mod native; 51 | mod native_include; 52 | pub mod operators; 53 | pub mod packets; 54 | pub mod runtime; 55 | pub mod scheduler; 56 | pub mod shared_state; 57 | pub mod state; 58 | #[cfg(any(test, feature = "test"))] 59 | pub mod testing; 60 | pub mod utils; 61 | -------------------------------------------------------------------------------- /docker.mk: -------------------------------------------------------------------------------- 1 | # Docker-specific Makefile for Netbricks Project 2 | # ============================================== 3 | 4 | BASE_DIR = $(shell pwd) 5 | SANDBOX ?= williamofockham/sandbox:nightly-2019-10-28 6 | 7 | MOUNTS = -v /lib/modules:/lib/modules \ 8 | -v /usr/src:/usr/src \ 9 | -v /dev/hugepages:/dev/hugepages 10 | 11 | .PHONY: pull-sandbox run run-cov run-lint run-tests 12 | 13 | pull-sandbox: 14 | @docker pull $(SANDBOX) 15 | 16 | run: pull-sandbox 17 | @docker run -it --rm --privileged --network=host \ 18 | -w /opt \ 19 | $(MOUNTS) \ 20 | -v $(BASE_DIR):/opt/netbricks \ 21 | -v $(BASE_DIR)/moongen:/opt/moongen \ 22 | -e LD_LIBRARY_PATH=/opt/netbricks/target/native:$LD_LIBRARY_PATH \ 23 | $(SANDBOX) /bin/bash 24 | 25 | run-cov: pull-sandbox 26 | @docker run -it --rm --privileged --network=host \ 27 | -w /opt/netbricks \ 28 | $(MOUNTS) \ 29 | -v $(BASE_DIR):/opt/netbricks \ 30 | -v $(BASE_DIR)/moongen:/opt/moongen \ 31 | -e LD_LIBRARY_PATH=/opt/netbricks/target/native:$LD_LIBRARY_PATH \ 32 | $(SANDBOX) make cov 33 | 34 | run-lint: pull-sandbox 35 | @docker run -it --rm --privileged --network=host \ 36 | -w /opt/netbricks \ 37 | $(MOUNTS) \ 38 | -v $(BASE_DIR):/opt/netbricks \ 39 | -v $(BASE_DIR)/moongen:/opt/moongen \ 40 | -e LD_LIBRARY_PATH=/opt/netbricks/target/native:$LD_LIBRARY_PATH \ 41 | $(SANDBOX) make lint 42 | 43 | run-tests: pull-sandbox 44 | @docker run -it --rm --privileged --network=host \ 45 | -w /opt/netbricks \ 46 | $(MOUNTS) \ 47 | -v $(BASE_DIR):/opt/netbricks \ 48 | -v $(BASE_DIR)/moongen:/opt/moongen \ 49 | -e LD_LIBRARY_PATH=/opt/netbricks/target/native:$LD_LIBRARY_PATH \ 50 | $(SANDBOX) make test 51 | -------------------------------------------------------------------------------- /examples/sctp/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | extern crate nix; 3 | extern crate sctp; 4 | 5 | use self::control::*; 6 | use netbricks::common::Result; 7 | use netbricks::config::load_config; 8 | use netbricks::control::sctp::*; 9 | use netbricks::interface::{PacketRx, PacketTx}; 10 | use netbricks::operators::{Batch, ReceiveBatch}; 11 | use netbricks::packets::{Ethernet, Packet, RawPacket}; 12 | use netbricks::runtime::Runtime; 13 | use netbricks::scheduler::Scheduler; 14 | use std::fmt::Display; 15 | use std::net::*; 16 | use std::str::FromStr; 17 | mod control; 18 | 19 | fn install(ports: Vec, sched: &mut S) 20 | where 21 | T: PacketRx + PacketTx + Display + Clone + 'static, 22 | S: Scheduler + Sized, 23 | { 24 | let pipelines: Vec<_> = ports 25 | .iter() 26 | .map(|port| ReceiveBatch::new(port.clone()).map(swap).send(port.clone())) 27 | .collect(); 28 | 29 | println!("Running {} pipelines", pipelines.len()); 30 | 31 | for pipeline in pipelines { 32 | sched.add_task(pipeline).unwrap(); 33 | } 34 | 35 | let addr = SocketAddrV4::new(Ipv4Addr::from_str("0.0.0.0").unwrap(), 8001); 36 | let control = SctpControlServer::::new_streaming(SocketAddr::V4(addr)); 37 | sched.add_task(control).unwrap(); 38 | } 39 | 40 | fn swap(packet: RawPacket) -> Result { 41 | let mut ethernet = packet.parse::()?; 42 | ethernet.swap_addresses(); 43 | Ok(ethernet) 44 | } 45 | 46 | fn main() -> Result<()> { 47 | let configuration = load_config()?; 48 | println!("{}", configuration); 49 | let mut runtime = Runtime::init(&configuration)?; 50 | runtime.add_pipeline_to_run(install); 51 | runtime.execute() 52 | } 53 | -------------------------------------------------------------------------------- /framework/src/operators/receive_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError, BATCH_SIZE}; 2 | use crate::interface::PacketRx; 3 | use crate::native::mbuf::MBuf; 4 | use crate::packets::RawPacket; 5 | 6 | /// Receive operator 7 | /// 8 | /// Marks the start of a pipeline. 9 | pub struct ReceiveBatch { 10 | port: Rx, 11 | buffers: Vec<*mut MBuf>, 12 | index: usize, 13 | } 14 | 15 | impl ReceiveBatch { 16 | #[inline] 17 | pub fn new(port: Rx) -> Self { 18 | ReceiveBatch { 19 | port, 20 | buffers: Vec::<*mut MBuf>::with_capacity(BATCH_SIZE), 21 | index: 0, 22 | } 23 | } 24 | } 25 | 26 | impl Batch for ReceiveBatch { 27 | type Item = RawPacket; 28 | 29 | #[inline] 30 | fn next(&mut self) -> Option> { 31 | // TODO: better if this is a queue 32 | if self.buffers.len() > self.index { 33 | let mbuf = self.buffers[self.index]; 34 | self.index += 1; 35 | Some(Ok(RawPacket::from_mbuf(mbuf))) 36 | } else { 37 | self.buffers.clear(); 38 | self.index = 0; 39 | None 40 | } 41 | } 42 | 43 | #[inline] 44 | fn receive(&mut self) { 45 | unsafe { 46 | let capacity = self.buffers.capacity(); 47 | self.buffers.set_len(capacity); 48 | match self.port.recv(self.buffers.as_mut_slice()) { 49 | Ok(received) => self.buffers.set_len(received as usize), 50 | // the underlying DPDK method `rte_eth_rx_burst` will 51 | // never return an error. The error arm is unreachable 52 | _ => unreachable!(), 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /framework/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netbricks" 3 | version = "0.3.0" 4 | authors = ["William of Ockham ", "Aurojit Panda "] 5 | description = "A network function framework written in Rust and using DPDK" 6 | build = "build.rs" 7 | license = "ISC" 8 | repository = "https://github.com/williamofockham/NetBricks" 9 | readme = "../README.md" 10 | keywords = ["netbricks", "network-functions", "nfs", "packet-processing"] 11 | categories = ["network-functions", "framework"] 12 | 13 | [lib] 14 | doctest = false 15 | name = "netbricks" 16 | path = "src/lib.rs" 17 | 18 | [badges] 19 | travis-ci = { repository = "williamofockham/NetBricks", branch = "master" } 20 | codecov = { repository = "williamofockham/NetBricks", branch = "master", service = "github" } 21 | 22 | [dependencies] 23 | clap = "2.33" 24 | config = "0.9" 25 | failure = "0.1" 26 | fallible-iterator = "0.2" 27 | fnv = ">= 1.0" 28 | hex = "0.3" 29 | lazy_static = ">= 1.3" 30 | libc = ">= 0.2" 31 | log = { version = "0.4", features = ["std", "serde"] } 32 | netbricks-codegen = { path = "../netbricks-codegen" } 33 | net2 = "0.2" 34 | # NIX restricts us to just unix for now, we can fix this if someone cares at a later point. 35 | nix = ">= 0.13" 36 | proptest = { version = "0.9", optional = true } 37 | regex = ">= 1.1" 38 | rust-sctp = { git="https://github.com/netsys/rust-sctp", optional = true } 39 | serde = ">= 1.0" 40 | serde_derive = ">= 1.0" 41 | tokio = "0.1" 42 | tokio-signal = "0.2" 43 | tokio-threadpool = "0.1" 44 | twox-hash = ">= 1.2" 45 | 46 | [features] 47 | default = [] 48 | performance = [] 49 | sctp = ["rust-sctp"] 50 | test = ["proptest"] 51 | 52 | [dev-dependencies.proptest] 53 | version = "0.9" 54 | default-features = false 55 | features = ["default-code-coverage"] 56 | 57 | [build-dependencies] 58 | # Use Bindgen to generate DPDK structures. 59 | bindgen = "0.51" 60 | -------------------------------------------------------------------------------- /framework/src/testing/strategy.rs: -------------------------------------------------------------------------------- 1 | //! proptest strategies 2 | 3 | use crate::packets::ip::{Flow, ProtocolNumbers}; 4 | use proptest::arbitrary::any; 5 | use proptest::strategy::{Just, Strategy}; 6 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 7 | 8 | /// Returns a strategy to generate IPv4 flows 9 | /// 10 | /// The IP addresses and ports are random. The protocol can be 11 | /// either TCP, UDP or ICMP. 12 | pub fn v4_flow() -> impl Strategy { 13 | ( 14 | any::(), 15 | any::(), 16 | any::(), 17 | any::(), 18 | prop_oneof![ 19 | Just(ProtocolNumbers::Tcp), 20 | Just(ProtocolNumbers::Udp), 21 | Just(ProtocolNumbers::Icmpv4), 22 | ], 23 | ) 24 | .prop_map(|(src_ip, dst_ip, src_port, dst_port, protocol)| { 25 | Flow::new( 26 | IpAddr::V4(src_ip), 27 | IpAddr::V4(dst_ip), 28 | src_port, 29 | dst_port, 30 | protocol, 31 | ) 32 | }) 33 | } 34 | 35 | /// Returns a strategy to generate IPv6 flows 36 | /// 37 | /// The IP addresses and ports are random. The protocol can be 38 | /// either TCP, UDP or ICMP. 39 | pub fn v6_flow() -> impl Strategy { 40 | ( 41 | any::(), 42 | any::(), 43 | any::(), 44 | any::(), 45 | prop_oneof![ 46 | Just(ProtocolNumbers::Tcp), 47 | Just(ProtocolNumbers::Udp), 48 | Just(ProtocolNumbers::Icmpv6), 49 | ], 50 | ) 51 | .prop_map(|(src_ip, dst_ip, src_port, dst_port, protocol)| { 52 | Flow::new( 53 | IpAddr::V6(src_ip), 54 | IpAddr::V6(dst_ip), 55 | src_port, 56 | dst_port, 57 | protocol, 58 | ) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PORT ?= "0000:00:09.0" 2 | CORE ?= 0 3 | BASE_DIR = $(shell git rev-parse --show-toplevel) 4 | POOL_SIZE ?= 512 5 | GH=https://github.com/williamofockham 6 | 7 | .PHONY: build build-all build-ex build-nb build-rel build-rel-ex clean cov fmt \ 8 | init lint native run run-rel test watch watch-test 9 | 10 | build: 11 | @./build.sh build 12 | 13 | build-all: 14 | @./build.sh build_all 15 | 16 | build-ex: 17 | ifdef EXAMPLE 18 | @./build.sh build_example $(EXAMPLE) 19 | else 20 | @./build.sh build_example 21 | endif 22 | 23 | build-nb: 24 | @./build.sh build_fmwk 25 | 26 | build-rel: 27 | @./build.sh build_rel 28 | 29 | build-rel-ex: 30 | ifdef EXAMPLE 31 | @./build.sh build_example_rel $(EXAMPLE) 32 | else 33 | @./build.sh build_example 34 | endif 35 | 36 | cov: 37 | @./build.sh cov 38 | 39 | clean: 40 | @./build.sh clean 41 | 42 | fmt: 43 | @./build.sh fmt 44 | 45 | init: 46 | @mkdir -p $(BASE_DIR)/.git/hooks && ln -s -f $(BASE_DIR)/.hooks/pre-commit $(BASE_DIR)/.git/hooks/pre-commit 47 | -git clone $(GH)/utils.git 48 | -ln -s utils/Vagrantfile 49 | -git clone --recurse-submodules $(GH)/moongen.git 50 | 51 | lint: 52 | @./build.sh lint 53 | 54 | native: 55 | @./build.sh build_native 56 | 57 | run: 58 | ifdef EXAMPLE 59 | @./build.sh run $(EXAMPLE) -p $(PORT) -c $(CORE) --pool-size=$(POOL_SIZE) 60 | else 61 | @./build.sh run 62 | endif 63 | 64 | run-rel: 65 | ifdef EXAMPLE 66 | @./build.sh run_rel $(EXAMPLE) -p $(PORT) -c $(CORE) --pool-size=$(POOL_SIZE) 67 | else 68 | @./build.sh run_rel 69 | endif 70 | 71 | test: 72 | ifdef TEST 73 | @unset RUST_BACKTRACE 74 | @./build.sh build_example $(TEST) 75 | @./build.sh test $(TEST) 76 | @export RUST_BACKTRACE=1 77 | else 78 | @unset RUST_BACKTRACE 79 | @./build.sh build 80 | @./build.sh test 81 | @export RUST_BACKTRACE=1 82 | endif 83 | 84 | watch: 85 | @cargo watch --poll -x build -w framework/src 86 | 87 | watch-test: 88 | @cargo watch --poll -x test -w framework/src 89 | -------------------------------------------------------------------------------- /examples/sctp/src/control.rs: -------------------------------------------------------------------------------- 1 | use netbricks::control::sctp::*; 2 | use netbricks::control::IOScheduler; 3 | use nix::errno; 4 | use sctp::SctpStream; 5 | use std::net::SocketAddr; 6 | 7 | pub struct ControlListener { 8 | scheduler: IOScheduler, 9 | stream: SctpStream, 10 | buffer: Vec, 11 | } 12 | impl SctpControlAgent for ControlListener { 13 | fn new(address: SocketAddr, stream: SctpStream, scheduler: IOScheduler) -> ControlListener { 14 | println!("New connection from {}", address); 15 | scheduler.schedule_read(); 16 | ControlListener { 17 | scheduler, 18 | stream, 19 | buffer: (0..1024).map(|_| 0).collect(), 20 | } 21 | } 22 | 23 | fn handle_read_ready(&mut self) -> bool { 24 | let mut schedule = true; 25 | while { 26 | let read = self.stream.recvmsg(&mut self.buffer[..]); 27 | match read { 28 | Ok((size, stream)) => { 29 | println!("Received message on stream {} of size {}", stream, size); 30 | true 31 | } 32 | Err(e) => { 33 | if let Some(e) = e.raw_os_error() { 34 | if errno::from_i32(e) != errno::Errno::EAGAIN { 35 | schedule = false; 36 | } else { 37 | schedule = true; 38 | } 39 | } else { 40 | schedule = false; 41 | } 42 | false 43 | } 44 | } 45 | } {} 46 | if schedule { 47 | self.scheduler.schedule_read(); 48 | }; 49 | schedule 50 | } 51 | 52 | fn handle_write_ready(&mut self) -> bool { 53 | panic!("No writes expected"); 54 | } 55 | 56 | fn handle_hup(&mut self) -> bool { 57 | println!("Hanging up"); 58 | false 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /framework/src/native/zcsi.rs: -------------------------------------------------------------------------------- 1 | use super::mbuf::MBuf; 2 | use packets::MacAddr; 3 | use std::os::raw::c_char; 4 | #[link(name = "zcsi")] 5 | extern "C" { 6 | pub fn init_system_whitelisted( 7 | name: *const c_char, 8 | nlen: i32, 9 | core: i32, 10 | whitelist: *mut *const c_char, 11 | wlcount: i32, 12 | pool_size: u32, 13 | cache_size: u32, 14 | slots: u16, 15 | ) -> i32; 16 | pub fn init_thread(tid: i32, core: i32) -> i32; 17 | pub fn init_secondary( 18 | name: *const c_char, 19 | nlen: i32, 20 | core: i32, 21 | vdevs: *mut *const c_char, 22 | vdev_count: i32, 23 | ) -> i32; 24 | pub fn init_pmd_port( 25 | port: i32, 26 | rxqs: i32, 27 | txqs: i32, 28 | rx_cores: *const i32, 29 | tx_cores: *const i32, 30 | nrxd: i32, 31 | ntxd: i32, 32 | loopback: i32, 33 | tso: i32, 34 | csumoffload: i32, 35 | ) -> i32; 36 | pub fn free_pmd_port(port: i32) -> i32; 37 | pub fn recv_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32; 38 | pub fn send_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32; 39 | pub fn num_pmd_ports() -> i32; 40 | pub fn rte_eth_macaddr_get(port: i32, address: *mut MacAddr); 41 | pub fn init_bess_eth_ring(ifname: *const c_char, core: i32) -> i32; 42 | pub fn init_ovs_eth_ring(iface: i32, core: i32) -> i32; 43 | pub fn find_port_with_pci_address(pciaddr: *const c_char) -> i32; 44 | pub fn attach_device( 45 | identifier: *const c_char, 46 | port_id_ptr: *mut i32, 47 | max_port_ids: i32, 48 | ) -> i32; 49 | pub fn max_rxqs(port: i32) -> i32; 50 | pub fn max_txqs(port: i32) -> i32; 51 | pub fn mbuf_alloc() -> *mut MBuf; 52 | pub fn mbuf_free(buf: *mut MBuf); 53 | pub fn mbuf_alloc_bulk(array: *mut *mut MBuf, len: u16, cnt: i32) -> i32; 54 | pub fn mbuf_free_bulk(array: *mut *mut MBuf, cnt: i32) -> i32; 55 | pub fn crc_hash_native(to_hash: *const u8, size: u32, iv: u32) -> u32; 56 | pub fn ipv4_cksum(payload: *const u8) -> u16; 57 | } 58 | -------------------------------------------------------------------------------- /framework/src/allocators/cache_aligned.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::{Alloc, Global, Layout}; 2 | use std::fmt; 3 | use std::mem::size_of; 4 | use std::ops::{Deref, DerefMut}; 5 | use std::ptr::{self, NonNull, Unique}; 6 | 7 | const CACHE_LINE_SIZE: usize = 64; 8 | unsafe fn allocate_cache_line(size: usize) -> *mut u8 { 9 | Global 10 | .alloc_zeroed(Layout::from_size_align(size, CACHE_LINE_SIZE).unwrap()) 11 | .unwrap() 12 | .as_ptr() as *mut u8 13 | } 14 | 15 | pub struct CacheAligned { 16 | ptr: Unique, 17 | } 18 | 19 | impl Drop for CacheAligned { 20 | fn drop(&mut self) { 21 | unsafe { 22 | Global.dealloc( 23 | NonNull::::new_unchecked(self.ptr.as_ptr() as *mut _), 24 | Layout::from_size_align(size_of::(), CACHE_LINE_SIZE).unwrap(), 25 | ); 26 | } 27 | } 28 | } 29 | 30 | impl Deref for CacheAligned { 31 | type Target = T; 32 | fn deref(&self) -> &T { 33 | unsafe { self.ptr.as_ref() } 34 | } 35 | } 36 | 37 | impl DerefMut for CacheAligned { 38 | fn deref_mut(&mut self) -> &mut T { 39 | unsafe { self.ptr.as_mut() } 40 | } 41 | } 42 | 43 | impl CacheAligned { 44 | pub fn allocate(src: T) -> CacheAligned { 45 | unsafe { 46 | let alloc = allocate_cache_line(size_of::()) as *mut T; 47 | ptr::write(alloc, src); 48 | CacheAligned { 49 | ptr: Unique::new(alloc).unwrap(), 50 | } 51 | } 52 | } 53 | } 54 | 55 | impl Clone for CacheAligned 56 | where 57 | T: Clone, 58 | { 59 | fn clone(&self) -> CacheAligned { 60 | unsafe { 61 | let alloc = allocate_cache_line(size_of::()) as *mut T; 62 | ptr::copy(self.ptr.as_ptr() as *const T, alloc, 1); 63 | CacheAligned { 64 | ptr: Unique::new(alloc).unwrap(), 65 | } 66 | } 67 | } 68 | } 69 | 70 | impl fmt::Display for CacheAligned 71 | where 72 | T: fmt::Display, 73 | { 74 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 75 | T::fmt(&*self, f) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /native/test/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | ifndef RTE_SDK 4 | $(error RTE_SDK is undefined) 5 | endif 6 | 7 | ifndef RTE_TARGET 8 | $(error RTE_TARGET is undefined) 9 | endif 10 | 11 | ifneq ($(wildcard $(RTE_SDK)/$(RTE_TARGET)*),) 12 | DPDK_INC_DIR = $(RTE_SDK)/$(RTE_TARGET)/include 13 | DPDK_LIB_DIR = $(RTE_SDK)/$(RTE_TARGET)/lib 14 | else 15 | DPDK_INC_DIR = $(RTE_SDK)/build/include 16 | DPDK_LIB_DIR = $(RTE_SDK)/build/lib 17 | endif 18 | ADAPTOR_DIR=$(abspath ../) 19 | DPDK_INCLUDE=$(ADAPTOR_DIR)/include 20 | DPDK_LIB=$(ADAPTOR_DIR) 21 | 22 | LDFLAGS += -L$(DPDK_LIB_DIR) -L$(DPDK_LIB) 23 | LIBS += -Wl,--whole-archive -ldpdk -Wl,--no-whole-archive -Wl,-rpath=$(DPDK_LIB_DIR) 24 | LIBS += -lm -lpthread -ldl -lpcap -ldpdk -lzcsi 25 | 26 | # change fpic to fPIC if something fails 27 | CFLAGS = -std=gnu99 -g3 -ggdb3 -O3 -Wall -Werror -m64 -march=nehalem \ 28 | -Wno-unused-function -Wno-unused-but-set-variable \ 29 | -I$(DPDK_INC_DIR) \ 30 | -I$(DPDK_INCLUDE) \ 31 | -D_GNU_SOURCE \ 32 | -fPIC 33 | 34 | SRCS = $(wildcard *.c) 35 | OBJS = $(SRCS:.c=.o) 36 | HEADERS = $(wildcard include/*.h) 37 | PROD = test 38 | 39 | DEPS = .make.dep 40 | 41 | OSTYPE ?= $(shell uname) 42 | ifeq ($(OSTYPE),Darwin) 43 | MAKEJOBS := $(shell sysctl -n hw.physicalcpu) 44 | else 45 | MAKEJOBS := $(shell nproc || echo 1) 46 | endif 47 | 48 | # if multiple targets are specified, do them one by one */ 49 | ifneq ($(words $(MAKECMDGOALS)),1) 50 | 51 | .NOTPARALLEL: 52 | $(sort all $(MAKECMDGOALS)): 53 | @$(MAKE) --no-print-directory -f $(firstword $(MAKEFILE_LIST)) $@ 54 | 55 | else 56 | 57 | # parallel build by default 58 | CORES ?= $(shell nproc || echo $(MAKEJOBS)) 59 | MAKEFLAGS += -j $(CORES) 60 | 61 | .PHONY: all clean tags cscope 62 | 63 | all: $(DEPS) $(PROD) 64 | 65 | $(DEPS): $(SRCS) $(HEADERS) 66 | @$(CC) $(CFLAGS) -MM $(SRCS) | sed 's|\(.*\)\.o: \(.*\)\.c|\2.o: \2.c|' > $(DEPS); 67 | 68 | $(PROD): $(OBJS) 69 | @echo $(LDFLAGS) 70 | $(CC) -shared $(OBJS) -o $@ $(LDFLAGS) $(LIBS) 71 | 72 | -include $(DEPS) 73 | 74 | clean: 75 | rm -f $(DEPS) $(PROD) *.o 76 | 77 | tags: 78 | @ctags -R * 79 | 80 | cscope: 81 | @rm -f cscope.* 82 | @find . -name "*.c" -o -name "*.h" > cscope.files 83 | cscope -b -q -k 84 | @rm -f cscope.files 85 | endif 86 | -------------------------------------------------------------------------------- /framework/src/shared_state/shared_vec.rs: -------------------------------------------------------------------------------- 1 | use super::{open_shared, SharedMemory}; 2 | use std::borrow::Borrow; 3 | use std::hash::{Hash, Hasher}; 4 | use std::ops::{Index, IndexMut, Range, RangeFrom, RangeTo}; 5 | use utils::round_to_pages; 6 | 7 | #[allow(dead_code)] // FIXME: While WIP 8 | pub struct SharedVec { 9 | vec: Vec, 10 | shared: SharedMemory, 11 | modified: bool, 12 | } 13 | 14 | impl SharedVec { 15 | pub fn new_with_capacity(name: &str, capacity: usize) -> SharedVec { 16 | let capacity_pages = round_to_pages(capacity); 17 | unsafe { 18 | SharedVec { 19 | vec: Vec::with_capacity(capacity), 20 | shared: open_shared(name, capacity_pages), 21 | modified: false, 22 | } 23 | } 24 | } 25 | } 26 | 27 | impl Borrow<[T]> for SharedVec { 28 | fn borrow(&self) -> &[T] { 29 | self.vec.borrow() 30 | } 31 | } 32 | 33 | impl Hash for SharedVec { 34 | fn hash(&self, state: &mut H) 35 | where 36 | H: Hasher, 37 | { 38 | self.vec.hash(state) 39 | } 40 | } 41 | 42 | impl Index for SharedVec { 43 | type Output = T; 44 | fn index(&self, index: usize) -> &T { 45 | self.vec.index(index) 46 | } 47 | } 48 | 49 | impl Index> for SharedVec { 50 | type Output = [T]; 51 | fn index(&self, index: Range) -> &[T] { 52 | self.vec.index(index) 53 | } 54 | } 55 | 56 | impl Index> for SharedVec { 57 | type Output = [T]; 58 | fn index(&self, index: RangeTo) -> &[T] { 59 | self.vec.index(index) 60 | } 61 | } 62 | 63 | impl Index> for SharedVec { 64 | type Output = [T]; 65 | fn index(&self, index: RangeFrom) -> &[T] { 66 | self.vec.index(index) 67 | } 68 | } 69 | 70 | impl IndexMut for SharedVec { 71 | fn index_mut(&mut self, index: usize) -> &mut T { 72 | self.modified = true; 73 | self.vec.index_mut(index) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /framework/src/shared_state/mod.rs: -------------------------------------------------------------------------------- 1 | /// Shareable data structures. 2 | pub mod directory; 3 | pub use self::shared_vec::*; 4 | mod shared_vec; 5 | use libc::{self, c_void, close, ftruncate, mmap, munmap, shm_open, shm_unlink}; 6 | use std::ffi::CString; 7 | use std::io::Error; 8 | use std::ptr; 9 | use utils::PAGE_SIZE; 10 | 11 | struct SharedMemory { 12 | pub mem: *mut T, 13 | name: CString, 14 | size: usize, 15 | } 16 | 17 | impl Drop for SharedMemory { 18 | fn drop(&mut self) { 19 | unsafe { 20 | let size = self.size; 21 | let _ret = munmap(self.mem as *mut c_void, size); // Unmap pages. 22 | // Record munmap failure. 23 | let shm_ret = shm_unlink(self.name.as_ptr()); 24 | assert!(shm_ret == 0, "Could not unlink shared memory region"); 25 | } 26 | } 27 | } 28 | 29 | unsafe fn open_shared(name: &str, size: usize) -> SharedMemory { 30 | // Make sure size is page aligned 31 | assert!(size & !PAGE_SIZE == 0); 32 | let name = CString::new(name).unwrap(); 33 | let mut fd = shm_open( 34 | name.as_ptr(), 35 | libc::O_CREAT | libc::O_EXCL | libc::O_RDWR, 36 | 0o700, 37 | ); 38 | if fd == -1 { 39 | if let Some(e) = Error::last_os_error().raw_os_error() { 40 | if e == libc::EEXIST { 41 | shm_unlink(name.as_ptr()); 42 | fd = shm_open( 43 | name.as_ptr(), 44 | libc::O_CREAT | libc::O_EXCL | libc::O_RDWR, 45 | 0o700, 46 | ); 47 | } 48 | } 49 | }; 50 | assert!(fd >= 0, "Could not create shared memory segment"); 51 | let ftret = ftruncate(fd, size as i64); 52 | assert!(ftret == 0, "Could not truncate"); 53 | let address = mmap( 54 | ptr::null_mut(), 55 | size, 56 | libc::PROT_READ | libc::PROT_WRITE, 57 | libc::MAP_POPULATE | libc::MAP_PRIVATE, 58 | fd, 59 | 0, 60 | ); 61 | if address == libc::MAP_FAILED { 62 | let err_string = CString::new("mmap failed").unwrap(); 63 | libc::perror(err_string.as_ptr()); 64 | panic!("Could not mmap shared region"); 65 | } 66 | close(fd); 67 | SharedMemory { 68 | mem: address as *mut T, 69 | name, 70 | size, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /framework/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::path::Path; 5 | 6 | /// Cargo runs main in this file to get some additional settings (e.g., 7 | /// LD_LIBRARY_PATH). It reads the printed output looking for certain variables, 8 | /// see [here](http://doc.crates.io/build-script.html) for documentation. 9 | fn main() { 10 | // Get the directory where we are building. 11 | let cargo_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 12 | let dpdk_dir = env::var("RTE_SDK").unwrap(); 13 | let dpdk_build = Path::new(&dpdk_dir).join("build"); 14 | 15 | let dpdk_libs = dpdk_build.clone().join("lib"); 16 | let native_path = Path::new(&cargo_dir) 17 | .parent() 18 | .unwrap() 19 | .join("target") 20 | .join("native"); 21 | println!( 22 | "cargo:rustc-link-search=native={}", 23 | dpdk_libs.to_str().unwrap() 24 | ); 25 | if dpdk_libs.join("libdpdk.so").exists() { 26 | println!("cargo:rustc-link-lib=dpdk"); 27 | } 28 | println!( 29 | "cargo:rustc-link-search=native={}", 30 | native_path.to_str().unwrap() 31 | ); 32 | let header_path = Path::new(&cargo_dir) 33 | .join("src") 34 | .join("native_include") 35 | .join("dpdk-headers.h"); 36 | let dpdk_include_path = dpdk_build.clone().join("include"); 37 | println!("Header path {:?}", header_path.to_str()); 38 | 39 | let bindings = bindgen::Builder::default() 40 | .header(header_path.to_str().unwrap()) 41 | .whitelist_type(r"(rte|cmdline|ether|eth|arp|vlan|vxlan)_.*") 42 | .whitelist_function(r"(_rte|rte|cmdline|lcore|ether|eth|arp|is)_.*") 43 | .whitelist_var( 44 | r"(RTE|CMDLINE|ETHER|ARP|VXLAN|BONDING|LCORE|MEMPOOL|ARP|PKT|EXT_ATTACHED|IND_ATTACHED|lcore|rte|cmdline|per_lcore)_.*", 45 | ) 46 | .derive_copy(true) 47 | .derive_debug(true) 48 | .derive_default(true) 49 | .derive_partialeq(true) 50 | .rust_target(bindgen::RustTarget::Nightly) 51 | .clang_args(vec!["-I", dpdk_include_path.to_str().unwrap()].iter()) 52 | .rustfmt_bindings(true) 53 | .generate() 54 | .expect("Unable to generate DPDK bindings"); 55 | let out_dir = env::var("OUT_DIR").unwrap(); 56 | let dpdk_bindings = Path::new(&out_dir).join("dpdk_bindings.rs"); 57 | 58 | bindings 59 | .write_to_file(dpdk_bindings) 60 | .expect("Could not write bindings"); 61 | } 62 | -------------------------------------------------------------------------------- /framework/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for writing tests 2 | //! 3 | //! To compile the utilities with NetBricks, enable the feature `test` 4 | //! in the project's Cargo.toml. 5 | //! 6 | //! # Example 7 | //! 8 | //! ``` 9 | //! [dev-dependencies] 10 | //! netbricks = { version = "1.0", features = ["test"] } 11 | //! ``` 12 | 13 | mod arbitrary; 14 | mod packet; 15 | mod strategy; 16 | 17 | pub use self::packet::*; 18 | pub use self::strategy::*; 19 | pub use netbricks_codegen::dpdk_test; 20 | pub use tokio::prelude::future::{lazy, Future}; 21 | 22 | use crate::packets::ip::v4::Ipv4; 23 | use crate::packets::ip::v6::{Ipv6, SegmentRouting}; 24 | use crate::packets::{Ethernet, Packet, Tcp, Udp}; 25 | 26 | lazy_static! { 27 | pub static ref DPDK_TEST_POOL: tokio_threadpool::ThreadPool = tokio_threadpool::Builder::new() 28 | .pool_size(1) 29 | .after_start(|| crate::interface::dpdk::init_system_wl_with_mempool( 30 | "dpdk_tests", 31 | 0, 32 | &[], 33 | 32, 34 | 0 35 | )) 36 | .build(); 37 | } 38 | 39 | /// `Packet` extension trait. 40 | /// 41 | /// Methods for packet conversion that make testing less verbose. Does not 42 | /// guarantee that the result of the conversion will be a valid packet, 43 | /// and will `panic` if the conversion fails. 44 | pub trait PacketExt: Packet + Sized { 45 | fn into_eth(self) -> Ethernet { 46 | self.reset().parse::().unwrap() 47 | } 48 | 49 | fn into_v4(self) -> Ipv4 { 50 | self.into_eth().parse::().unwrap() 51 | } 52 | 53 | fn into_v4_tcp(self) -> Tcp { 54 | self.into_v4().parse::>().unwrap() 55 | } 56 | 57 | fn into_v4_udp(self) -> Udp { 58 | self.into_v4().parse::>().unwrap() 59 | } 60 | 61 | fn into_v6(self) -> Ipv6 { 62 | self.into_eth().parse::().unwrap() 63 | } 64 | 65 | fn into_v6_tcp(self) -> Tcp { 66 | self.into_v6().parse::>().unwrap() 67 | } 68 | 69 | fn into_v6_udp(self) -> Udp { 70 | self.into_v6().parse::>().unwrap() 71 | } 72 | 73 | fn into_sr(self) -> SegmentRouting { 74 | self.into_v6().parse::>().unwrap() 75 | } 76 | 77 | fn into_sr_tcp(self) -> Tcp> { 78 | self.into_sr().parse::>>().unwrap() 79 | } 80 | } 81 | 82 | impl PacketExt for T where T: Packet + Sized {} 83 | -------------------------------------------------------------------------------- /native/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | ifndef RTE_SDK 4 | $(error RTE_SDK is undefined) 5 | endif 6 | 7 | ifndef RTE_TARGET 8 | RTE_TARGET=build 9 | endif 10 | 11 | ifneq ($(wildcard $(RTE_SDK)/$(RTE_TARGET)*),) 12 | DPDK_INC_DIR = $(RTE_SDK)/$(RTE_TARGET)/include 13 | DPDK_LIB_DIR = $(RTE_SDK)/$(RTE_TARGET)/lib 14 | else 15 | DPDK_INC_DIR = $(RTE_SDK)/build/include 16 | DPDK_LIB_DIR = $(RTE_SDK)/build/lib 17 | endif 18 | 19 | LDFLAGS += -L$(DPDK_LIB_DIR) 20 | LIBS += -Wl,--whole-archive -ldpdk -Wl,--no-whole-archive -Wl,-rpath=$(DPDK_LIB_DIR) 21 | #LIBS += -ldpdk -Wl,-rpath=$(DPDK_LIB_DIR) 22 | LIBS += -lm -lpthread -ldl -lpcap -lnuma -lz 23 | 24 | # change fpic to fPIC if something fails 25 | CFLAGS = -std=gnu99 -g3 -ggdb3 -O3 -Wall -Werror -m64 -march=nehalem \ 26 | -Wno-unused-function -Wno-unused-but-set-variable \ 27 | -I$(DPDK_INC_DIR) -Iinclude/\ 28 | -D_GNU_SOURCE \ 29 | -fPIC 30 | 31 | SRCS = $(wildcard *.c) 32 | OBJS = $(SRCS:.c=.o) 33 | HEADERS = $(wildcard include/*.h) 34 | PROD = libzcsi.so 35 | PROD_STATIC = libzcsi.a 36 | 37 | DEPS = .make.dep 38 | 39 | ifeq ($(OSTYPE),Darwin) 40 | MAKEJOBS := $(shell sysctl -n hw.physicalcpu) 41 | else 42 | MAKEJOBS := $(shell nproc || echo 1) 43 | endif 44 | 45 | # if multiple targets are specified, do them one by one */ 46 | ifneq ($(words $(MAKECMDGOALS)),1) 47 | 48 | .NOTPARALLEL: 49 | $(sort all $(MAKECMDGOALS)): 50 | @$(MAKE) --no-print-directory -f $(firstword $(MAKEFILE_LIST)) $@ 51 | 52 | else 53 | 54 | # parallel build by default 55 | CORES ?= $(shell nproc || echo $(MAKEJOBS)) 56 | MAKEFLAGS += -j $(CORES) 57 | INSTALL_PATH = $(abspath ../target/native) 58 | 59 | .PHONY: all clean tags cscope all-static 60 | 61 | all: $(DEPS) $(PROD) 62 | 63 | all-static: $(DEPS) $(PROD_STATIC) 64 | 65 | install: $(DEPS) $(PROD) | $(INSTALL_PATH) 66 | cp $(PROD) $(INSTALL_PATH) 67 | 68 | $(INSTALL_PATH): 69 | mkdir -p $(INSTALL_PATH) 70 | 71 | $(DEPS): $(SRCS) $(HEADERS) 72 | @echo $(RTE_SDK) $(DPDK_INC_DIR) 73 | @$(CC) $(CFLAGS) -MM $(SRCS) | sed 's|\(.*\)\.o: \(.*\)\.c|\2.o: \2.c|' > $(DEPS); 74 | 75 | $(PROD_STATIC): $(OBJS) 76 | ar rcs $(PROD_STATIC) $(OBJS) 77 | 78 | $(PROD): $(OBJS) 79 | $(CC) -shared $(OBJS) -o $@ $(LDFLAGS) $(LIBS) 80 | 81 | -include $(DEPS) 82 | 83 | clean: 84 | rm -f $(DEPS) $(PROD) $(PROD_STATIC) *.o || true 85 | rm -f $(INSTALL_PATH)/$(PROD) || true 86 | rmdir $(INSTALL_PATH) || true 87 | 88 | tags: 89 | @ctags -R * 90 | 91 | cscope: 92 | @rm -f cscope.* 93 | @find . -name "*.c" -o -name "*.h" > cscope.files 94 | cscope -b -q -k 95 | @rm -f cscope.files 96 | endif 97 | -------------------------------------------------------------------------------- /framework/src/interface/port/virt_port.rs: -------------------------------------------------------------------------------- 1 | use crate::interface::{PacketRx, PacketTx, PortStats}; 2 | use allocators::*; 3 | use common::*; 4 | use native::mbuf::MBuf; 5 | use native::zcsi::*; 6 | use std::fmt; 7 | use std::sync::atomic::Ordering; 8 | use std::sync::Arc; 9 | 10 | pub struct VirtualPort { 11 | stats_rx: Arc>, 12 | stats_tx: Arc>, 13 | } 14 | 15 | #[derive(Clone)] 16 | pub struct VirtualQueue { 17 | stats_rx: Arc>, 18 | stats_tx: Arc>, 19 | } 20 | 21 | impl fmt::Display for VirtualQueue { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | write!(f, "virtual queue") 24 | } 25 | } 26 | 27 | impl PacketTx for VirtualQueue { 28 | #[inline] 29 | fn send(&self, pkts: &mut [*mut MBuf]) -> Result { 30 | let len = pkts.len() as i32; 31 | let update = self.stats_tx.stats.load(Ordering::Relaxed) + len as usize; 32 | self.stats_tx.stats.store(update, Ordering::Relaxed); 33 | unsafe { 34 | mbuf_free_bulk(pkts.as_mut_ptr(), len); 35 | } 36 | Ok(len as u32) 37 | } 38 | } 39 | 40 | impl PacketRx for VirtualQueue { 41 | /// Send a batch of packets out this PortQueue. Note this method is internal to NetBricks (should not be directly 42 | /// called). 43 | #[inline] 44 | fn recv(&self, pkts: &mut [*mut MBuf]) -> Result { 45 | let len = pkts.len() as i32; 46 | let status = unsafe { mbuf_alloc_bulk(pkts.as_mut_ptr(), 60, len) }; 47 | let alloced = if status == 0 { len } else { 0 }; 48 | let update = self.stats_rx.stats.load(Ordering::Relaxed) + alloced as usize; 49 | self.stats_rx.stats.store(update, Ordering::Relaxed); 50 | Ok(alloced as u32) 51 | } 52 | } 53 | 54 | impl VirtualPort { 55 | pub fn new(_queues: i32) -> Result> { 56 | Ok(Arc::new(VirtualPort { 57 | stats_rx: Arc::new(PortStats::new()), 58 | stats_tx: Arc::new(PortStats::new()), 59 | })) 60 | } 61 | 62 | pub fn new_virtual_queue(&self, _queue: i32) -> Result> { 63 | Ok(CacheAligned::allocate(VirtualQueue { 64 | stats_rx: self.stats_rx.clone(), 65 | stats_tx: self.stats_tx.clone(), 66 | })) 67 | } 68 | 69 | /// Get stats for an RX/TX queue pair. 70 | pub fn stats(&self) -> (usize, usize) { 71 | ( 72 | self.stats_rx.stats.load(Ordering::Relaxed), 73 | self.stats_tx.stats.load(Ordering::Relaxed), 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/mtu-too-big/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate netbricks; 3 | use netbricks::common::Result; 4 | use netbricks::config::load_config; 5 | use netbricks::interface::{PacketRx, PacketTx}; 6 | use netbricks::operators::{Batch, ReceiveBatch}; 7 | use netbricks::packets::icmp::v6::{Icmpv6, PacketTooBig}; 8 | use netbricks::packets::ip::v6::{Ipv6, Ipv6Packet, IPV6_MIN_MTU}; 9 | use netbricks::packets::ip::ProtocolNumbers; 10 | use netbricks::packets::{EtherTypes, Ethernet, EthernetHeader, Fixed, Packet}; 11 | use netbricks::runtime::Runtime; 12 | use netbricks::scheduler::Scheduler; 13 | use std::fmt::Display; 14 | 15 | fn install(ports: Vec, sched: &mut S) 16 | where 17 | T: PacketRx + PacketTx + Display + Clone + 'static, 18 | S: Scheduler + Sized, 19 | { 20 | println!("Receiving started"); 21 | 22 | let pipelines: Vec<_> = ports 23 | .iter() 24 | .map(|port| { 25 | ReceiveBatch::new(port.clone()) 26 | .map(|p| p.parse::()) 27 | .group_by( 28 | |eth| { 29 | eth.ether_type() == EtherTypes::Ipv6 30 | && eth.len() > IPV6_MIN_MTU + EthernetHeader::size() 31 | }, 32 | |groups| { 33 | compose! { 34 | groups, 35 | true => |group| { 36 | group.map(reject_too_big) 37 | }, 38 | false => |group| { 39 | group 40 | } 41 | } 42 | }, 43 | ) 44 | .send(port.clone()) 45 | }) 46 | .collect(); 47 | 48 | println!("Running {} pipelines", pipelines.len()); 49 | 50 | for pipeline in pipelines { 51 | sched.add_task(pipeline).unwrap(); 52 | } 53 | } 54 | 55 | fn reject_too_big(mut ethernet: Ethernet) -> Result { 56 | ethernet.swap_addresses(); 57 | let mut ipv6 = ethernet.parse::()?; 58 | let src = ipv6.src(); 59 | let dst = ipv6.dst(); 60 | ipv6.set_src(dst); 61 | ipv6.set_dst(src); 62 | ipv6.set_next_header(ProtocolNumbers::Icmpv6); 63 | 64 | let mut too_big = ipv6.push::>()?; 65 | too_big.set_mtu(IPV6_MIN_MTU as u32); 66 | too_big.cascade(); 67 | 68 | Ok(too_big.deparse().deparse()) 69 | } 70 | 71 | fn main() -> Result<()> { 72 | let configuration = load_config()?; 73 | println!("{}", configuration); 74 | let mut runtime = Runtime::init(&configuration)?; 75 | runtime.add_pipeline_to_run(install); 76 | runtime.execute() 77 | } 78 | -------------------------------------------------------------------------------- /examples/op-errors/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate netbricks; 4 | extern crate simplelog; 5 | #[macro_use] 6 | extern crate failure; 7 | 8 | use log::Level; 9 | use netbricks::common::Result; 10 | use netbricks::config::load_config; 11 | use netbricks::interface::{PacketRx, PacketTx}; 12 | use netbricks::operators::{Batch, ReceiveBatch}; 13 | use netbricks::packets::ip::v6::Ipv6; 14 | use netbricks::packets::{EtherTypes, Ethernet, Packet}; 15 | use netbricks::runtime::Runtime; 16 | use netbricks::scheduler::Scheduler; 17 | use netbricks::utils::cidr::v6::Ipv6Cidr; 18 | use netbricks::utils::cidr::Cidr; 19 | use simplelog::{Config as SimpleConfig, LevelFilter, WriteLogger}; 20 | use std::fmt::Display; 21 | use std::fs::File as StdFile; 22 | use std::str::FromStr; 23 | 24 | static BAD_PREFIX: &str = "da75::0/16"; 25 | 26 | fn start_logger() { 27 | WriteLogger::init( 28 | LevelFilter::Warn, 29 | SimpleConfig { 30 | time: None, 31 | level: Some(Level::Error), 32 | target: Some(Level::Debug), 33 | location: Some(Level::Trace), 34 | time_format: None, 35 | }, 36 | StdFile::create("test.log").unwrap(), 37 | ) 38 | .unwrap(); 39 | } 40 | 41 | fn install(ports: Vec, sched: &mut S) 42 | where 43 | T: PacketRx + PacketTx + Display + Clone + 'static, 44 | S: Scheduler + Sized, 45 | { 46 | println!("Receiving started"); 47 | 48 | let pipelines: Vec<_> = ports 49 | .iter() 50 | .map(|port| { 51 | ReceiveBatch::new(port.clone()) 52 | .map(|p| p.parse::()) 53 | .filter(|p| p.ether_type() == EtherTypes::Ipv6) 54 | .map(|p| { 55 | let v6 = p.parse::()?; 56 | throw_mama_from_the_train(v6) 57 | }) 58 | .for_each(|p| { 59 | warn!("v6: {}", p); 60 | Ok(()) 61 | }) 62 | .send(port.clone()) 63 | }) 64 | .collect(); 65 | println!("Running {} pipelines", pipelines.len()); 66 | for pipeline in pipelines { 67 | sched.add_task(pipeline).unwrap(); 68 | } 69 | } 70 | 71 | fn throw_mama_from_the_train(packet: Ipv6) -> Result { 72 | let cidr = Ipv6Cidr::from_str(BAD_PREFIX).unwrap(); 73 | 74 | if cidr.contains(packet.src()) { 75 | bail!("directed by danny devito") 76 | } else { 77 | Ok(packet) 78 | } 79 | } 80 | 81 | fn main() -> Result<()> { 82 | start_logger(); 83 | let configuration = load_config()?; 84 | println!("{}", configuration); 85 | let mut runtime = Runtime::init(&configuration)?; 86 | runtime.add_pipeline_to_run(install); 87 | runtime.execute() 88 | } 89 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/too_big.rs: -------------------------------------------------------------------------------- 1 | use crate::packets::icmp::v6::{Icmpv6, Icmpv6Packet, Icmpv6Payload, Icmpv6Type, Icmpv6Types}; 2 | use crate::packets::ip::v6::Ipv6Packet; 3 | use crate::packets::{buffer, EthernetHeader, Fixed, Packet}; 4 | use std::fmt; 5 | 6 | /* From https://tools.ietf.org/html/rfc4443#section-3.2 7 | Packet Too Big Message 8 | 9 | 0 1 2 3 10 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 11 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 12 | | Type | Code | Checksum | 13 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 14 | | MTU | 15 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | As much of invoking packet | 17 | + as possible without the ICMPv6 packet + 18 | | exceeding the minimum IPv6 MTU [IPv6] | 19 | 20 | MTU The Maximum Transmission Unit of the next-hop link. 21 | */ 22 | 23 | /// Packet too big message 24 | #[derive(Clone, Copy, Default, Debug)] 25 | #[repr(C, packed)] 26 | pub struct PacketTooBig { 27 | mtu: u32, 28 | } 29 | 30 | impl Icmpv6Payload for PacketTooBig { 31 | fn msg_type() -> Icmpv6Type { 32 | Icmpv6Types::PacketTooBig 33 | } 34 | } 35 | 36 | impl Icmpv6 { 37 | #[inline] 38 | pub fn mtu(&self) -> u32 { 39 | u32::from_be(self.payload().mtu) 40 | } 41 | 42 | #[inline] 43 | pub fn set_mtu(&mut self, mtu: u32) { 44 | self.payload_mut().mtu = u32::to_be(mtu); 45 | } 46 | } 47 | 48 | impl fmt::Display for Icmpv6 { 49 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 50 | write!( 51 | f, 52 | "type: {}, code: {}, checksum: 0x{:04x}, mtu: {}", 53 | self.msg_type(), 54 | self.code(), 55 | self.checksum(), 56 | self.mtu() 57 | ) 58 | } 59 | } 60 | 61 | impl Packet for Icmpv6 { 62 | #[inline] 63 | fn cascade(&mut self) { 64 | // assuming inside an ethernet frame 65 | let max_len = self.mtu() as usize + EthernetHeader::size(); 66 | // only err if nothing to trim, ignore the result 67 | let _ = buffer::trim(self.mbuf(), max_len); 68 | 69 | self.compute_checksum(); 70 | self.envelope_mut().cascade(); 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | 78 | #[test] 79 | fn size_of_packet_too_big() { 80 | assert_eq!(4, PacketTooBig::size()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /framework/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::asm::*; 2 | mod asm; 3 | pub mod cidr; 4 | 5 | pub const PAGE_SIZE: usize = 4096; // Page size in bytes, not using huge pages here. 6 | 7 | /// Round a given buffer to page size units. 8 | #[inline] 9 | pub fn round_to_pages(buffer_size: usize) -> usize { 10 | (buffer_size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1) 11 | } 12 | 13 | /// Round a 64-bit integer to its nearest power of 2. 14 | #[inline] 15 | pub fn round_to_power_of_2(mut size: usize) -> usize { 16 | size = size.wrapping_sub(1); 17 | size |= size >> 1; 18 | size |= size >> 2; 19 | size |= size >> 4; 20 | size |= size >> 8; 21 | size |= size >> 16; 22 | size |= size >> 32; 23 | size = size.wrapping_add(1); 24 | size 25 | } 26 | 27 | /// clears a bit in a byte, pos is the zero-based position in the byte starting from the left 28 | #[inline] 29 | pub fn clear_bit(original: u8, pos: u8) -> u8 { 30 | let swap_pos = 7 - pos; 31 | let mask = 1 << swap_pos; 32 | original & !mask 33 | } 34 | 35 | /// sets a bit in a byte, pos is the zero-based position in the byte starting from the left 36 | #[inline] 37 | pub fn set_bit(original: u8, pos: u8) -> u8 { 38 | let swap_pos = 7 - pos; 39 | let mask = 1 << swap_pos; 40 | original | mask 41 | } 42 | 43 | /// gets a bit value as a bool in a byte, pos is the zero-based position in the byte starting from left 44 | #[inline] 45 | pub fn get_bit(original: u8, pos: u8) -> bool { 46 | let swap_pos = 7 - pos; 47 | let mask = 1 << swap_pos; 48 | (original & mask) != 0 49 | } 50 | 51 | /// Flips a bit in a byte to on or off 52 | /// 53 | /// # Arguments 54 | /// 55 | /// * `original` - the byte to flip a bit on 56 | /// * `pos` - the zero-based position of the bit in the byte to flip 57 | /// * `on` - a boolean indicating whether the bit should be set (true) or cleared (false) 58 | #[inline] 59 | pub fn flip_bit(original: u8, pos: u8, on: bool) -> u8 { 60 | if on { 61 | set_bit(original, pos) 62 | } else { 63 | clear_bit(original, pos) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | 71 | // test flipping a bit at a known position 72 | #[test] 73 | fn flippin_bits() { 74 | let original: u8 = 0b0110_1000; 75 | 76 | // we will clear the 3rd bit (2nd position) 77 | let cleared: u8 = 0b0100_1000; 78 | 79 | // lets turn off the bit, and check that it is cleared 80 | let mut result = flip_bit(original, 2, false); 81 | assert_eq!(result, cleared); 82 | assert_eq!(get_bit(result, 2), false); 83 | 84 | // turn the bit back on, make sure it is set 85 | result = flip_bit(result, 2, true); 86 | assert_eq!(result, original); 87 | assert_eq!(get_bit(result, 2), true); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /framework/src/scheduler/embedded_scheduler.rs: -------------------------------------------------------------------------------- 1 | use super::{Executable, Scheduler}; 2 | use common::*; 3 | use std::default::Default; 4 | 5 | /// Used to keep stats about each pipeline and eventually grant tokens, etc. 6 | struct Runnable { 7 | pub task: Box, 8 | pub dependencies: Vec, 9 | } 10 | 11 | impl Runnable { 12 | pub fn from_task(mut task: T) -> Runnable { 13 | let deps = task.dependencies(); 14 | Runnable { 15 | task: Box::new(task), 16 | dependencies: deps, 17 | } 18 | } 19 | } 20 | 21 | /// This scheduler is designed to allow NetBricks to be embedded in other vswitches (e.g., Bess). As a result it neither 22 | /// does any of the resource accounting `Scheduler` attempts to do at the moment, nor does it have anything that just 23 | /// runs tasks in a loop. 24 | pub struct EmbeddedScheduler { 25 | /// The set of runnable items. Note we currently don't have a blocked queue. 26 | tasks: Vec, 27 | } 28 | 29 | const DEFAULT_TASKQ_SIZE: usize = 256; 30 | 31 | impl Default for EmbeddedScheduler { 32 | fn default() -> EmbeddedScheduler { 33 | EmbeddedScheduler::new() 34 | } 35 | } 36 | 37 | impl Scheduler for EmbeddedScheduler { 38 | /// Add a task, and return a handle allowing the task to be run. 39 | fn add_task(&mut self, task: T) -> Result { 40 | self.tasks.push(Runnable::from_task(task)); 41 | Ok(self.tasks.len()) 42 | } 43 | } 44 | 45 | impl EmbeddedScheduler { 46 | /// Create a new Bess scheduler. 47 | pub fn new() -> EmbeddedScheduler { 48 | EmbeddedScheduler { 49 | tasks: Vec::with_capacity(DEFAULT_TASKQ_SIZE), 50 | } 51 | } 52 | 53 | /// Run specified task. 54 | pub fn exec_task(&mut self, task_id: usize) { 55 | { 56 | let len = self.tasks[task_id - 1].dependencies.len(); 57 | for dep in 0..len { 58 | let dep_task = self.tasks[task_id - 1].dependencies[dep]; 59 | self.exec_task(dep_task) 60 | } 61 | } 62 | self.tasks[task_id - 1].task.execute(); 63 | } 64 | 65 | fn display_dependencies_internal(&self, task_id: usize, depth: usize) { 66 | { 67 | let len = self.tasks[task_id - 1].dependencies.len(); 68 | for dep in 0..len { 69 | let dep_task = self.tasks[task_id - 1].dependencies[dep]; 70 | self.display_dependencies_internal(dep_task, depth + 1) 71 | } 72 | } 73 | info!("{} Task {}", depth, task_id); 74 | } 75 | 76 | /// For debugging purposes 77 | pub fn display_dependencies(&mut self, task_id: usize) { 78 | self.display_dependencies_internal(task_id, 0) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/echo-reply/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate netbricks; 2 | use netbricks::common::Result; 3 | use netbricks::config::load_config; 4 | use netbricks::interface::{PacketRx, PacketTx}; 5 | use netbricks::operators::{mpsc_batch, Batch, Enqueue, MpscProducer, ReceiveBatch}; 6 | use netbricks::packets::icmp::v6::{EchoReply, EchoRequest, Icmpv6}; 7 | use netbricks::packets::ip::v6::{Ipv6, Ipv6Packet}; 8 | use netbricks::packets::ip::ProtocolNumbers; 9 | use netbricks::packets::{EtherTypes, Ethernet, Packet, RawPacket}; 10 | use netbricks::runtime::Runtime; 11 | use netbricks::scheduler::Scheduler; 12 | use std::fmt::Display; 13 | 14 | const PIPELINE_CORE: i32 = 0; 15 | 16 | fn install(ports: Vec, sched: &mut S) 17 | where 18 | T: PacketRx + PacketTx + Display + Clone + 'static, 19 | S: Scheduler + Sized, 20 | { 21 | println!("Echo reply pipeline"); 22 | 23 | let (producer, outbound) = mpsc_batch(); 24 | let outbound = outbound.send(ports[0].clone()); 25 | sched.add_task(outbound).unwrap(); 26 | 27 | println!("Receiving started"); 28 | 29 | let pipelines: Vec<_> = ports 30 | .iter() 31 | .map(move |port| { 32 | let producer = producer.clone(); 33 | ReceiveBatch::new(port.clone()) 34 | .map(move |p| reply_echo(p, &producer)) 35 | .filter(|_| false) 36 | .send(port.clone()) 37 | }) 38 | .collect(); 39 | 40 | println!("Running {} pipelines", pipelines.len() + 1); 41 | 42 | for pipeline in pipelines { 43 | sched.add_task(pipeline).unwrap(); 44 | } 45 | } 46 | 47 | fn reply_echo(packet: RawPacket, producer: &MpscProducer) -> Result> { 48 | let reply = RawPacket::new()?; 49 | 50 | let ethernet = packet.parse::()?; 51 | let mut reply = reply.push::()?; 52 | reply.set_src(ethernet.dst()); 53 | reply.set_dst(ethernet.src()); 54 | reply.set_ether_type(EtherTypes::Ipv6); 55 | 56 | let ipv6 = ethernet.parse::()?; 57 | let mut reply = reply.push::()?; 58 | reply.set_src(ipv6.dst()); 59 | reply.set_dst(ipv6.src()); 60 | reply.set_next_header(ProtocolNumbers::Icmpv6); 61 | 62 | let icmpv6 = ipv6.parse::>()?; 63 | let echo = icmpv6.downcast::()?; 64 | let mut reply = reply.push::>()?; 65 | reply.set_identifier(echo.identifier()); 66 | reply.set_seq_no(echo.seq_no()); 67 | reply.set_data(echo.data())?; 68 | reply.cascade(); 69 | 70 | producer.enqueue(reply.reset()); 71 | 72 | Ok(echo) 73 | } 74 | 75 | fn main() -> Result<()> { 76 | let configuration = load_config()?; 77 | println!("{}", configuration); 78 | let mut runtime = Runtime::init(&configuration)?; 79 | let _ = runtime.add_pipeline_to_core(PIPELINE_CORE, install); 80 | runtime.execute() 81 | } 82 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/ndp/mod.rs: -------------------------------------------------------------------------------- 1 | mod neighbor_advert; 2 | mod neighbor_solicit; 3 | mod options; 4 | mod router_advert; 5 | mod router_solicit; 6 | 7 | pub use self::neighbor_advert::*; 8 | pub use self::neighbor_solicit::*; 9 | pub use self::options::*; 10 | pub use self::router_advert::*; 11 | pub use self::router_solicit::*; 12 | 13 | use super::{Icmpv6, Icmpv6Packet, Icmpv6Payload}; 14 | use crate::common::Result; 15 | use crate::packets::ip::v6::Ipv6Packet; 16 | use crate::packets::Packet; 17 | 18 | /// NDP message payload marker 19 | pub trait NdpPayload: Icmpv6Payload {} 20 | 21 | /// Common behaviors shared by NDP packets 22 | /// 23 | /// NDP packets are also ICMPv6 packets. 24 | pub trait NdpPacket: Icmpv6Packet { 25 | /// Returns an iterator that iterates through the options in the NDP packet 26 | fn options(&self) -> NdpOptionsIterator; 27 | /// Add option to NDP messaged 28 | fn push_option(&self) -> Result; 29 | } 30 | 31 | impl NdpPacket for Icmpv6 32 | where 33 | Icmpv6: Icmpv6Packet, 34 | { 35 | fn options(&self) -> NdpOptionsIterator { 36 | let mbuf = self.mbuf(); 37 | let offset = self.payload_offset() + P::size(); 38 | NdpOptionsIterator::new(mbuf, offset) 39 | } 40 | 41 | fn push_option(&self) -> Result { 42 | T::do_push(self.mbuf) 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | use crate::packets::ethernet::{Ethernet, MacAddr}; 50 | use crate::packets::icmp::v6::ndp::{NdpOptions, SOURCE_LINK_LAYER_ADDR}; 51 | use crate::packets::ip::v6::Ipv6; 52 | use crate::packets::ip::ProtocolNumbers; 53 | use crate::packets::raw::RawPacket; 54 | use crate::testing::dpdk_test; 55 | use fallible_iterator::FallibleIterator; 56 | use std::str::FromStr; 57 | 58 | #[dpdk_test] 59 | fn test_add_source_link_layer_address() { 60 | let mac_addr = MacAddr::from_str("01:00:00:00:00:00").unwrap(); 61 | let raw_packet = RawPacket::new().unwrap(); 62 | let eth = raw_packet.push::().unwrap(); 63 | let mut ipv6 = eth.push::().unwrap(); 64 | ipv6.set_next_header(ProtocolNumbers::Icmpv6); 65 | let router_advert = ipv6.push::>().unwrap(); 66 | 67 | let mut option: LinkLayerAddress = router_advert.push_option().unwrap(); 68 | option.set_addr(mac_addr); 69 | option.set_option_type(SOURCE_LINK_LAYER_ADDR); 70 | 71 | let mut iter = router_advert.options(); 72 | 73 | while let Ok(Some(option_parse)) = iter.next() { 74 | if let NdpOptions::SourceLinkLayerAddress(option_type) = option_parse { 75 | assert_eq!(option_type.addr(), mac_addr); 76 | } else { 77 | panic!("Option was not source link layer address"); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /framework/src/operators/send_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, PacketError, BATCH_SIZE}; 2 | use crate::interface::PacketTx; 3 | use crate::native::mbuf::MBuf; 4 | use crate::native::zcsi::mbuf_free_bulk; 5 | use crate::packets::Packet; 6 | use crate::scheduler::Executable; 7 | 8 | /// Send operator 9 | /// 10 | /// Marks the end of a pipeline. 11 | pub struct SendBatch { 12 | source: B, 13 | port: Tx, 14 | transmit_q: Vec<*mut MBuf>, 15 | drop_q: Vec<*mut MBuf>, 16 | } 17 | 18 | impl SendBatch { 19 | #[inline] 20 | pub fn new(source: B, port: Tx) -> Self { 21 | SendBatch { 22 | source, 23 | port, 24 | transmit_q: Vec::with_capacity(BATCH_SIZE), 25 | drop_q: Vec::with_capacity(BATCH_SIZE), 26 | } 27 | } 28 | } 29 | 30 | impl Executable for SendBatch { 31 | fn execute(&mut self) { 32 | self.source.receive(); 33 | 34 | let transmit_q = &mut self.transmit_q; 35 | let drop_q = &mut self.drop_q; 36 | 37 | while let Some(item) = self.source.next() { 38 | match item { 39 | Ok(packet) => { 40 | transmit_q.push(packet.mbuf()); 41 | } 42 | Err(PacketError::Emit(mbuf)) => { 43 | transmit_q.push(mbuf); 44 | } 45 | Err(PacketError::Drop(mbuf)) => { 46 | drop_q.push(mbuf); 47 | } 48 | Err(PacketError::Abort(mbuf, err)) => { 49 | error_chain!(&err); 50 | drop_q.push(mbuf); 51 | } 52 | } 53 | } 54 | 55 | if !transmit_q.is_empty() { 56 | let mut to_send = transmit_q.len(); 57 | while to_send > 0 { 58 | match self.port.send(transmit_q.as_mut_slice()) { 59 | Ok(sent) => { 60 | let sent = sent as usize; 61 | to_send -= sent; 62 | if to_send > 0 { 63 | transmit_q.drain(..sent); 64 | } 65 | } 66 | // the underlying DPDK method `rte_eth_tx_burst` will 67 | // never return an error. The error arm is unreachable 68 | _ => unreachable!(), 69 | } 70 | } 71 | unsafe { 72 | transmit_q.set_len(0); 73 | } 74 | } 75 | 76 | if !drop_q.is_empty() { 77 | let len = drop_q.len(); 78 | let ptr = drop_q.as_mut_ptr(); 79 | unsafe { 80 | // never have a non-zero return 81 | mbuf_free_bulk(ptr, len as i32); 82 | drop_q.set_len(0); 83 | } 84 | } 85 | } 86 | 87 | #[inline] 88 | fn dependencies(&mut self) -> Vec { 89 | vec![] 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /framework/src/state/dp_mergeable.rs: -------------------------------------------------------------------------------- 1 | use fnv::FnvHasher; 2 | use packets::ip::Flow; 3 | use std::collections::hash_map::Iter; 4 | use std::collections::HashMap; 5 | use std::hash::BuildHasherDefault; 6 | use std::ops::AddAssign; 7 | 8 | /// A generic store for associating some merge-able type with each flow. Note, 9 | /// the merge must be commutative, we do not guarantee ordering for things being 10 | /// merged. The merge function is implemented by implementing the 11 | /// [`AddAssign`](https://doc.rust-lang.org/std/ops/trait.AddAssign.html) trait 12 | /// and overriding the `add_assign` method there. We assume that the quantity 13 | /// stored here does not need to be accessed by the control plane and can only 14 | /// be accessed from the data plane. The `cache_size` should be tuned depending 15 | /// on whether gets or puts are the most common operation in this table. 16 | /// 17 | /// #[FIXME] 18 | /// Garbage collection. 19 | type FnvHash = BuildHasherDefault; 20 | const VEC_SIZE: usize = 1 << 24; 21 | #[derive(Clone, Default)] 22 | pub struct DpMergeableStore + Default> { 23 | /// Contains the counts on the data path. 24 | state: HashMap, 25 | cache: Vec<(Flow, T)>, 26 | cache_size: usize, 27 | } 28 | 29 | const CACHE_SIZE: usize = 1 << 14; 30 | impl + Default> DpMergeableStore { 31 | pub fn with_cache_and_size(cache: usize, size: usize) -> DpMergeableStore { 32 | DpMergeableStore { 33 | state: HashMap::with_capacity_and_hasher(size, Default::default()), 34 | cache: Vec::with_capacity(cache), 35 | cache_size: cache, 36 | } 37 | } 38 | 39 | pub fn new() -> DpMergeableStore { 40 | DpMergeableStore::with_cache_and_size(CACHE_SIZE, VEC_SIZE) 41 | } 42 | 43 | fn merge_cache(&mut self) { 44 | self.state.extend(self.cache.drain(0..)); 45 | } 46 | 47 | /// Change the value for the given `Flow`. 48 | #[inline] 49 | pub fn update(&mut self, flow: Flow, inc: T) { 50 | { 51 | self.cache.push((flow, inc)); 52 | } 53 | if self.cache.len() >= self.cache_size { 54 | self.merge_cache(); 55 | } 56 | } 57 | 58 | /// Remove an entry from the table. 59 | #[inline] 60 | pub fn remove(&mut self, flow: &Flow) -> T { 61 | self.merge_cache(); 62 | self.state.remove(flow).unwrap_or_else(Default::default) 63 | } 64 | 65 | /// Iterate over all the stored entries. This is a bit weird to do in the data plane. 66 | /// 67 | /// #[Warning] 68 | /// This might have severe performance penalties. 69 | pub fn iter(&mut self) -> Iter { 70 | self.merge_cache(); 71 | self.state.iter() 72 | } 73 | 74 | /// Length of the table. 75 | pub fn len(&mut self) -> usize { 76 | self.merge_cache(); 77 | self.state.len() 78 | } 79 | 80 | /// Is table empty 81 | pub fn is_empty(&self) -> bool { 82 | self.state.is_empty() && self.cache.is_empty() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /framework/src/shared_state/directory.rs: -------------------------------------------------------------------------------- 1 | use super::{open_shared, SharedMemory}; 2 | use std::mem::size_of; 3 | use std::sync::atomic::*; 4 | use utils::PAGE_SIZE; 5 | /// A directory of shared structures. 6 | 7 | const MAX_LEN: usize = 256; // 255 byte names 8 | const DIRECTORY_PAGES: usize = 2; // Dedicate 2 pages to the directory. 9 | const BYTE_SIZE: usize = DIRECTORY_PAGES * PAGE_SIZE; 10 | 11 | /// Directory header for shared data. 12 | #[repr(C)] 13 | pub struct DirectoryHeader { 14 | entries: AtomicUsize, 15 | // Used to signal that snapshotting is in progress. 16 | current_version: AtomicUsize, 17 | committed_version: AtomicUsize, 18 | length: usize, 19 | } 20 | 21 | #[repr(packed, C)] 22 | pub struct DirectoryEntry { 23 | pub name: [u8; MAX_LEN], 24 | } 25 | 26 | pub struct Directory { 27 | head: *mut DirectoryHeader, 28 | data: *mut DirectoryEntry, 29 | // Need this to make sure memory is not dropped 30 | _shared_memory: SharedMemory, 31 | entry: usize, 32 | len: usize, 33 | } 34 | 35 | impl Directory { 36 | pub fn new(name: &str) -> Directory { 37 | unsafe { 38 | let shared = open_shared(name, BYTE_SIZE); 39 | let head = shared.mem as *mut DirectoryHeader; 40 | (*head).current_version.store(1, Ordering::SeqCst); 41 | let header_size = size_of::(); 42 | let entry_size = size_of::(); 43 | let entries = (BYTE_SIZE - header_size) / entry_size; 44 | let entry = (head.offset(1) as *mut u8) as *mut DirectoryEntry; 45 | (*head).length = entries; 46 | (*head).entries.store(0, Ordering::Release); 47 | (*head).committed_version.store(1, Ordering::SeqCst); 48 | Directory { 49 | head, 50 | data: entry, 51 | _shared_memory: shared, 52 | entry: 0, 53 | len: entries, 54 | } 55 | } 56 | } 57 | 58 | pub fn register_new_entry(&mut self, name: &str) -> Option { 59 | let entry = self.entry; 60 | if entry >= self.len || name.len() >= MAX_LEN { 61 | None 62 | } else { 63 | unsafe { 64 | let entry_ptr = self.data.add(entry); 65 | (*entry_ptr).name.copy_from_slice(name.as_bytes()); 66 | (*self.head).entries.store(entry, Ordering::Release); 67 | } 68 | self.entry += 1; 69 | Some(entry) 70 | } 71 | } 72 | 73 | #[inline] 74 | pub fn begin_snapshot(&mut self) { 75 | unsafe { 76 | (*self.head).current_version.fetch_add(1, Ordering::SeqCst); 77 | } 78 | } 79 | 80 | #[inline] 81 | pub fn end_snapshot(&mut self) { 82 | unsafe { 83 | let version = (*self.head).current_version.load(Ordering::Acquire); 84 | (*self.head) 85 | .committed_version 86 | .store(version, Ordering::Release); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/macswap/data/expect.out: -------------------------------------------------------------------------------- 1 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 74: 128.32.144.82.43748 > 66.96.160.131.80: Flags [S], seq 2542439059, win 29200, options [mss 1460,sackOK,TS val 36323029 ecr 0,nop,wscale 7], length 0 2 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 74: 66.96.160.131.80 > 128.32.144.82.43748: Flags [S.], seq 634151470, ack 2542439060, win 4380, options [mss 1460,nop,nop,TS val 2377788335 ecr 36323029,sackOK,eol], length 0 3 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [.], ack 1, win 29200, options [nop,nop,TS val 36323047 ecr 2377788335], length 0 4 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 144: 128.32.144.82.43748 > 66.96.160.131.80: Flags [P.], seq 1:79, ack 1, win 29200, options [nop,nop,TS val 36323047 ecr 2377788335], length 78: HTTP: GET / HTTP/1.1 5 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 66: 66.96.160.131.80 > 128.32.144.82.43748: Flags [.], ack 79, win 4458, options [nop,nop,TS val 2377788409 ecr 36323047], length 0 6 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 1514: 66.96.160.131.80 > 128.32.144.82.43748: Flags [P.], seq 1:1449, ack 79, win 4458, options [nop,nop,TS val 2377788410 ecr 36323047], length 1448: HTTP: HTTP/1.1 200 OK 7 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [.], ack 1449, win 31856, options [nop,nop,TS val 36323066 ecr 2377788410], length 0 8 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 1514: 66.96.160.131.80 > 128.32.144.82.43748: Flags [P.], seq 1449:2897, ack 79, win 4458, options [nop,nop,TS val 2377788410 ecr 36323047], length 1448: HTTP 9 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [.], ack 2897, win 34752, options [nop,nop,TS val 36323066 ecr 2377788410], length 0 10 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 231: 66.96.160.131.80 > 128.32.144.82.43748: Flags [P.], seq 2897:3062, ack 79, win 4458, options [nop,nop,TS val 2377788410 ecr 36323047], length 165: HTTP 11 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [.], ack 3062, win 37648, options [nop,nop,TS val 36323066 ecr 2377788410], length 0 12 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [F.], seq 79, ack 3062, win 37648, options [nop,nop,TS val 36323066 ecr 2377788410], length 0 13 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 66: 66.96.160.131.80 > 128.32.144.82.43748: Flags [.], ack 80, win 4458, options [nop,nop,TS val 2377788484 ecr 36323066], length 0 14 | 0c:c4:7a:a9:28:1e > 00:1c:73:3c:e4:d0, ethertype IPv4 (0x0800), length 66: 66.96.160.131.80 > 128.32.144.82.43748: Flags [F.], seq 3062, ack 80, win 4458, options [nop,nop,TS val 2377788484 ecr 36323066], length 0 15 | 00:1c:73:00:00:99 > 0c:c4:7a:a9:28:1e, ethertype IPv4 (0x0800), length 66: 128.32.144.82.43748 > 66.96.160.131.80: Flags [.], ack 3063, win 37648, options [nop,nop,TS val 36323085 ecr 2377788484], length 0 16 | -------------------------------------------------------------------------------- /examples/ipv4or6/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate colored; 2 | #[macro_use] 3 | extern crate netbricks; 4 | use colored::*; 5 | use netbricks::common::Result; 6 | use netbricks::config::load_config; 7 | use netbricks::interface::{PacketRx, PacketTx}; 8 | use netbricks::operators::{Batch, ReceiveBatch}; 9 | use netbricks::packets::ip::v4::Ipv4; 10 | use netbricks::packets::ip::v6::Ipv6; 11 | use netbricks::packets::ip::IpPacket; 12 | use netbricks::packets::{EtherTypes, Ethernet, Packet, RawPacket, Tcp}; 13 | use netbricks::runtime::Runtime; 14 | use netbricks::scheduler::Scheduler; 15 | use std::fmt::Display; 16 | 17 | fn install(ports: Vec, sched: &mut S) 18 | where 19 | T: PacketRx + PacketTx + Display + Clone + 'static, 20 | S: Scheduler + Sized, 21 | { 22 | println!("Receiving started"); 23 | 24 | let pipelines: Vec<_> = ports 25 | .iter() 26 | .map(|port| { 27 | ReceiveBatch::new(port.clone()) 28 | .map(eth_nf) 29 | .group_by( 30 | |ethernet| ethernet.ether_type(), 31 | |groups| { 32 | compose!( 33 | groups, 34 | EtherTypes::Ipv4 => |group| { 35 | group.for_each(ipv4_nf) 36 | }, 37 | EtherTypes::Ipv6 => |group| { 38 | group.for_each(ipv6_nf) 39 | } 40 | ); 41 | }, 42 | ) 43 | .send(port.clone()) 44 | }) 45 | .collect(); 46 | 47 | println!("Running {} pipelines", pipelines.len()); 48 | 49 | for pipeline in pipelines { 50 | sched.add_task(pipeline).unwrap(); 51 | } 52 | } 53 | 54 | #[inline] 55 | pub fn eth_nf(packet: RawPacket) -> Result { 56 | let ethernet = packet.parse::()?; 57 | 58 | let info_fmt = format!("[eth] {}", ethernet).magenta().bold(); 59 | println!("{}", info_fmt); 60 | 61 | Ok(ethernet) 62 | } 63 | 64 | #[inline] 65 | pub fn ipv4_nf(ethernet: &Ethernet) -> Result<()> { 66 | let ipv4 = ethernet.peek::()?; 67 | let info_fmt = format!("[ipv4] {}, [offset] {}", ipv4, ipv4.offset()).yellow(); 68 | println!("{}", info_fmt); 69 | 70 | let tcp = ipv4.peek::>()?; 71 | print_tcp(&tcp); 72 | 73 | Ok(()) 74 | } 75 | 76 | #[inline] 77 | pub fn ipv6_nf(ethernet: &Ethernet) -> Result<()> { 78 | let ipv6 = ethernet.peek::()?; 79 | let info_fmt = format!("[ipv6] {}, [offset] {}", ipv6, ipv6.offset()).cyan(); 80 | println!("{}", info_fmt); 81 | 82 | let tcp = ipv6.peek::>()?; 83 | print_tcp(&tcp); 84 | 85 | Ok(()) 86 | } 87 | 88 | #[inline] 89 | fn print_tcp(tcp: &Tcp) { 90 | let tcp_fmt = format!("[tcp] {}", tcp).green(); 91 | println!("{}", tcp_fmt); 92 | 93 | let flow_fmt = format!("[flow] {}", tcp.flow()).bright_blue(); 94 | println!("{}", flow_fmt); 95 | } 96 | 97 | fn main() -> Result<()> { 98 | let configuration = load_config()?; 99 | println!("{}", configuration); 100 | let mut runtime = Runtime::init(&configuration)?; 101 | runtime.add_pipeline_to_run(install); 102 | runtime.execute() 103 | } 104 | -------------------------------------------------------------------------------- /examples/tcp-reconstruction/data/expect.out: -------------------------------------------------------------------------------- 1 | Starting scheduler on 1 2 | GET / HTTP/1.1 3 | Host: lemmykoopa.com 4 | User-Agent: curl/7.47.0 5 | Accept: */* 6 | 7 | 8 | HTTP/1.1 200 OK 9 | Date: Fri, 09 Jun 2017 16:40:07 GMT 10 | Content-Type: text/html 11 | Content-Length: 2719 12 | Connection: keep-alive 13 | Keep-Alive: timeout=30 14 | Server: Apache/2 15 | Last-Modified: Sat, 03 Jul 2010 20:22:32 GMT 16 | ETag: "a9f-48a8176991200" 17 | Cache-Control: max-age=3600 18 | Expires: Fri, 09 Jun 2017 17:39:34 GMT 19 | Accept-Ranges: bytes 20 | Age: 33 21 | 22 | 23 | 24 | 25 | 26 | 27 | Lemmy�s Land 28 | 29 | 30 | 31 |
33 |

34 | 35 |

Ha ha! I am Lemmy 36 | Koopa, and you have foolishly fallen right into my trap. But don't worry- 37 | there is nothing to fear in Lemmy's Land, unless the idea of 43 fun-filled 38 | sections fills you with terror. If you feel brave and want to have some 39 | fun, I dare you to click on me and advance into my trap. But if you feel 40 | the need to stick to some crazy schedule and don't want to have a good 41 | time, then run over to Mario and he will rescue you. 42 |

43 |

Click on me to go into my Land! I dare ya! 44 |

Ha! Not even Mario can help you! There is no way out, so you may as well click on me. 45 |

Thank you for 46 | being the 47 |
48 |
person to allow 49 | me to capture you. Enjoy!

50 | 51 |

Disclaimer: "Super" Mario 52 | and all related characters are property of Nintendo and I didn't make them. 53 | Clawdia, Susan, Bagels, and Playful were introduced by me, other original 54 | characters are property of their respective authors. Most content on this 55 | site is the original work of creative fans and should not be taken as factual 56 | depictions of the official source material. All submissions have been accredited 57 | to their author(s); submissions without a credit belong to me. If you find 58 | something that has been stolen from another site, please 59 | Email 60 | me so that I may remove it. Taking material from Lemmy's Land for anything 61 | other than personal use is prohibited except by permission of the author. 62 | Lemmy's Land is copyright unto to me, all rights and lefts are reserved. 63 | Straights are still available. 64 | 65 | 66 | 67 | packet received for untracked flow did not have SYN flag, skipping. 68 | -------------------------------------------------------------------------------- /examples/collect-metrics/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | extern crate netbricks; 4 | use netbricks::common::Result; 5 | use netbricks::config::load_config; 6 | use netbricks::interface::{PacketRx, PacketTx}; 7 | use netbricks::operators::{Batch, ReceiveBatch}; 8 | use netbricks::packets::ip::v4::Ipv4; 9 | use netbricks::packets::ip::v6::{Ipv6, Ipv6Packet}; 10 | use netbricks::packets::ip::{ProtocolNumber, ProtocolNumbers}; 11 | use netbricks::packets::{EtherTypes, Ethernet, Packet, RawPacket}; 12 | use netbricks::runtime::Runtime; 13 | use netbricks::scheduler::Scheduler; 14 | use std::fmt::Display; 15 | use std::sync::atomic::{AtomicU64, Ordering}; 16 | use std::time::Duration; 17 | 18 | struct Metrics { 19 | tcp: AtomicU64, 20 | udp: AtomicU64, 21 | icmp: AtomicU64, 22 | other: AtomicU64, 23 | } 24 | 25 | impl Metrics { 26 | fn new() -> Metrics { 27 | Metrics { 28 | tcp: AtomicU64::new(0), 29 | udp: AtomicU64::new(0), 30 | icmp: AtomicU64::new(0), 31 | other: AtomicU64::new(0), 32 | } 33 | } 34 | 35 | fn increment(&self, bucket: ProtocolNumber) { 36 | let counter = match bucket { 37 | ProtocolNumbers::Tcp => &self.tcp, 38 | ProtocolNumbers::Udp => &self.udp, 39 | ProtocolNumbers::Icmpv4 | ProtocolNumbers::Icmpv6 => &self.icmp, 40 | _ => &self.other, 41 | }; 42 | counter.fetch_add(1, Ordering::Relaxed); 43 | } 44 | 45 | fn print(&self) { 46 | println!( 47 | "OVERALL: TCP {}, UDP {}, ICMP {}, OTHER {}", 48 | self.tcp.load(Ordering::Relaxed), 49 | self.udp.load(Ordering::Relaxed), 50 | self.icmp.load(Ordering::Relaxed), 51 | self.other.load(Ordering::Relaxed) 52 | ); 53 | } 54 | } 55 | 56 | lazy_static! { 57 | static ref METRICS: Metrics = Metrics::new(); 58 | } 59 | 60 | fn install(ports: Vec, sched: &mut S) 61 | where 62 | T: PacketRx + PacketTx + Display + Clone + 'static, 63 | S: Scheduler + Sized, 64 | { 65 | let pipelines: Vec<_> = ports 66 | .iter() 67 | .map(|port| { 68 | ReceiveBatch::new(port.clone()) 69 | .map(count_packets) 70 | .send(port.clone()) 71 | }) 72 | .collect(); 73 | 74 | println!("Running {} pipelines", pipelines.len()); 75 | 76 | for pipeline in pipelines { 77 | sched.add_task(pipeline).unwrap(); 78 | } 79 | } 80 | 81 | fn count_packets(packet: RawPacket) -> Result { 82 | let ethernet = packet.parse::()?; 83 | match ethernet.ether_type() { 84 | EtherTypes::Ipv4 => { 85 | let ipv4 = ethernet.parse::()?; 86 | METRICS.increment(ipv4.protocol()); 87 | Ok(ipv4.reset()) 88 | } 89 | EtherTypes::Ipv6 => { 90 | let ipv6 = ethernet.parse::()?; 91 | METRICS.increment(ipv6.next_header()); 92 | Ok(ipv6.reset()) 93 | } 94 | _ => Ok(ethernet.reset()), 95 | } 96 | } 97 | 98 | fn main() -> Result<()> { 99 | let configuration = load_config()?; 100 | println!("{}", configuration); 101 | let mut runtime = Runtime::init(&configuration)?; 102 | runtime.add_pipeline_to_run(install); 103 | runtime.add_task_to_run(|| METRICS.print(), Duration::from_secs(1)); 104 | runtime.execute() 105 | } 106 | -------------------------------------------------------------------------------- /examples/chain/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate getopts; 2 | extern crate netbricks; 3 | use getopts::Options; 4 | use netbricks::common::Result; 5 | use netbricks::config::load_config; 6 | use netbricks::interface::{PacketRx, PacketTx}; 7 | use netbricks::operators::{Batch, ReceiveBatch}; 8 | use netbricks::packets::ip::v4::Ipv4; 9 | use netbricks::packets::{Ethernet, Packet, RawPacket}; 10 | use netbricks::runtime::Runtime; 11 | use netbricks::scheduler::Scheduler; 12 | use std::env; 13 | use std::fmt::Display; 14 | 15 | fn install(ports: Vec, sched: &mut S, chain_len: u32, chain_pos: u32) 16 | where 17 | T: PacketRx + PacketTx + Display + Clone + 'static, 18 | S: Scheduler + Sized, 19 | { 20 | println!("Receiving started"); 21 | 22 | for port in &ports { 23 | println!( 24 | "Receiving port {} on chain len {} pos {}", 25 | port, chain_len, chain_pos 26 | ); 27 | } 28 | 29 | let pipelines: Vec<_> = ports 30 | .iter() 31 | .map(|port| { 32 | ReceiveBatch::new(port.clone()) 33 | .filter_map(move |p| chain(p, chain_len, chain_pos)) 34 | .send(port.clone()) 35 | }) 36 | .collect(); 37 | 38 | println!("Running {} pipelines", pipelines.len()); 39 | 40 | for pipeline in pipelines { 41 | sched.add_task(pipeline).unwrap(); 42 | } 43 | } 44 | 45 | #[inline] 46 | pub fn chain_nf(packet: RawPacket) -> Result { 47 | let mut ethernet = packet.parse::()?; 48 | ethernet.swap_addresses(); 49 | let mut ipv4 = ethernet.parse::()?; 50 | let ttl = ipv4.ttl(); 51 | ipv4.set_ttl(ttl - 1); 52 | Ok(ipv4.deparse().deparse()) 53 | } 54 | 55 | #[inline] 56 | pub fn chain(packet: RawPacket, len: u32, pos: u32) -> Result> { 57 | let mut chained = chain_nf(packet)?; 58 | 59 | for _ in 1..len { 60 | chained = chain_nf(chained)?; 61 | } 62 | 63 | let chained_eth = chained.parse::()?; 64 | let chained_ipv4 = chained_eth.parse::()?; 65 | 66 | if chained_ipv4.ttl() != 0 { 67 | Ok(None) 68 | } else { 69 | let mut chained_eth = chained_ipv4.deparse(); 70 | 71 | if len % 2 == 0 || pos % 2 == 1 { 72 | chained_eth.swap_addresses(); 73 | Ok(Some(chained_eth)) 74 | } else { 75 | Ok(Some(chained_eth)) 76 | } 77 | } 78 | } 79 | 80 | fn extra_opts() -> (u32, u32) { 81 | let mut opts = Options::new(); 82 | opts.optopt("l", "chain", "Chain length", "length"); 83 | opts.optopt( 84 | "j", 85 | "position", 86 | "Chain position (when externally chained)", 87 | "position", 88 | ); 89 | let args: Vec = env::args().collect(); 90 | let matches = match opts.parse(&args[1..]) { 91 | Ok(m) => m, 92 | Err(f) => panic!(f.to_string()), 93 | }; 94 | 95 | let chain_len = matches 96 | .opt_str("l") 97 | .unwrap_or_else(|| String::from("1")) 98 | .parse() 99 | .expect("Could not parse chain length"); 100 | 101 | let chain_pos = matches 102 | .opt_str("j") 103 | .unwrap_or_else(|| String::from("0")) 104 | .parse() 105 | .expect("Could not parse chain position"); 106 | 107 | (chain_len, chain_pos) 108 | } 109 | 110 | fn main() -> Result<()> { 111 | let (chain_len, chain_pos) = extra_opts(); 112 | let configuration = load_config()?; 113 | println!("{}", configuration); 114 | let mut runtime = Runtime::init(&configuration)?; 115 | runtime.add_pipeline_to_run(move |p, s| install(p, s, chain_len, chain_pos)); 116 | runtime.execute() 117 | } 118 | -------------------------------------------------------------------------------- /framework/src/control/linux/epoll.rs: -------------------------------------------------------------------------------- 1 | use super::{Available, HUP, NONE, READ, WRITE}; 2 | use nix::sys::epoll::*; 3 | use std::default::Default; 4 | use std::os::unix::io::AsRawFd; 5 | use std::os::unix::io::RawFd; 6 | use std::slice; 7 | 8 | pub type Token = u64; 9 | 10 | pub struct PollHandle { 11 | epoll_fd: RawFd, 12 | } 13 | 14 | impl PollHandle { 15 | pub fn schedule_read(&self, file: &Fd, token: Token) { 16 | self.schedule_read_rawfd(file.as_raw_fd(), token); 17 | } 18 | 19 | pub fn schedule_read_rawfd(&self, fd: RawFd, token: Token) { 20 | let mut event = EpollEvent::new( 21 | EpollFlags::EPOLLIN | EpollFlags::EPOLLET | EpollFlags::EPOLLONESHOT, 22 | token, 23 | ); 24 | epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).unwrap(); 25 | } 26 | 27 | pub fn schedule_write(&self, file: &Fd, token: Token) { 28 | self.schedule_write_rawfd(file.as_raw_fd(), token); 29 | } 30 | 31 | pub fn schedule_write_rawfd(&self, fd: RawFd, token: Token) { 32 | let mut event = EpollEvent::new( 33 | EpollFlags::EPOLLOUT | EpollFlags::EPOLLET | EpollFlags::EPOLLONESHOT, 34 | token, 35 | ); 36 | epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).unwrap(); 37 | } 38 | 39 | /// This assumes file is already set to be non-blocking. This must also be called only the first time round. 40 | pub fn new_io_port(&self, file: &Fd, token: Token) { 41 | self.new_io_fd(file.as_raw_fd(), token); 42 | } 43 | 44 | pub fn new_io_fd(&self, fd: RawFd, token: Token) { 45 | let mut event = EpollEvent::new(EpollFlags::EPOLLET | EpollFlags::EPOLLONESHOT, token); 46 | epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, fd, &mut event).unwrap(); 47 | } 48 | } 49 | 50 | pub struct PollScheduler { 51 | epoll_fd: RawFd, 52 | ready_tokens: Vec, 53 | events: usize, 54 | } 55 | 56 | impl Default for PollScheduler { 57 | fn default() -> PollScheduler { 58 | PollScheduler::new() 59 | } 60 | } 61 | 62 | impl PollScheduler { 63 | pub fn new_poll_handle(&self) -> PollHandle { 64 | PollHandle { 65 | epoll_fd: self.epoll_fd, 66 | } 67 | } 68 | 69 | pub fn new() -> PollScheduler { 70 | PollScheduler { 71 | epoll_fd: epoll_create().unwrap(), 72 | ready_tokens: Vec::with_capacity(32), 73 | events: 0, 74 | } 75 | } 76 | 77 | #[inline] 78 | fn epoll_kind_to_available(&self, kind: EpollFlags) -> Available { 79 | let mut available = NONE; 80 | if kind.contains(EpollFlags::EPOLLIN) { 81 | available |= READ 82 | }; 83 | if kind.contains(EpollFlags::EPOLLOUT) { 84 | available |= WRITE 85 | }; 86 | if kind.contains(EpollFlags::EPOLLHUP) || kind.contains(EpollFlags::EPOLLERR) { 87 | available |= HUP 88 | }; 89 | available 90 | } 91 | 92 | pub fn get_token_noblock(&mut self) -> Option<(Token, Available)> { 93 | if self.events > 0 { 94 | self.events -= 1; 95 | self.ready_tokens.pop() 96 | } else { 97 | let dest = unsafe { 98 | slice::from_raw_parts_mut( 99 | self.ready_tokens.as_mut_ptr(), 100 | self.ready_tokens.capacity(), 101 | ) 102 | }; 103 | self.events = epoll_wait(self.epoll_fd, dest, 0).unwrap(); 104 | unsafe { self.ready_tokens.set_len(self.events) }; 105 | self.ready_tokens.pop() 106 | } 107 | .map(|t| (t.data(), self.epoll_kind_to_available(t.events()))) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/lpm/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate colored; 2 | extern crate fnv; 3 | #[macro_use] 4 | extern crate lazy_static; 5 | #[macro_use] 6 | extern crate netbricks; 7 | extern crate rand; 8 | use self::lpm::*; 9 | use colored::*; 10 | use netbricks::common::Result; 11 | use netbricks::config::load_config; 12 | use netbricks::interface::{PacketRx, PacketTx}; 13 | use netbricks::operators::{Batch, ReceiveBatch}; 14 | use netbricks::runtime::Runtime; 15 | use netbricks::scheduler::Scheduler; 16 | use rand::distributions::Uniform; 17 | use rand::{thread_rng, Rng}; 18 | use std::fmt::Display; 19 | use std::net::Ipv4Addr; 20 | use std::sync::Arc; 21 | use std::sync::RwLock; 22 | mod lpm; 23 | 24 | lazy_static! { 25 | static ref LOOKUP_TABLE: Arc> = { 26 | let mut rng = thread_rng(); 27 | let mut lpm_table = IPLookup::new(); 28 | 29 | for _ in 1..100 { 30 | let a: u8 = rng.sample(Uniform::new_inclusive(0, 255)); 31 | let b: u8 = rng.sample(Uniform::new_inclusive(0, 255)); 32 | let c: u8 = rng.sample(Uniform::new_inclusive(0, 255)); 33 | let d: u8 = rng.sample(Uniform::new_inclusive(0, 255)); 34 | lpm_table.insert_ipv4(Ipv4Addr::new(a, b, c, d), 32, 1); 35 | } 36 | 37 | lpm_table.construct_table(); 38 | Arc::new(RwLock::new(lpm_table)) 39 | }; 40 | } 41 | 42 | fn install(ports: Vec, sched: &mut S) 43 | where 44 | T: PacketRx + PacketTx + Display + Clone + 'static, 45 | S: Scheduler + Sized, 46 | { 47 | println!("Receiving started"); 48 | for port in &ports { 49 | println!("Receiving port {}", port); 50 | } 51 | 52 | let pipelines: Vec<_> = ports 53 | .iter() 54 | .map(|port| { 55 | ReceiveBatch::new(port.clone()) 56 | .map(lpm) 57 | .group_by( 58 | |v4| LOOKUP_TABLE.read().unwrap().lookup_entry(v4.src()) as usize, 59 | |groups| { 60 | compose!(groups, 61 | 0 => |group| { 62 | group.for_each(|p| { 63 | let info_fmt = format!("{}", p.src()).magenta().bold(); 64 | println!("{}", info_fmt); 65 | Ok(()) 66 | }) 67 | }, 68 | 1 => |group| { 69 | group.for_each(|p| { 70 | let info_fmt = format!("{}", p.src()).red().bold(); 71 | println!("{}", info_fmt); 72 | Ok(()) 73 | }) 74 | }, 75 | 2 => |group| { 76 | group.for_each(|p| { 77 | let info_fmt = format!("{}", p.src()).blue().bold(); 78 | println!("{}", info_fmt); 79 | Ok(()) 80 | }) 81 | } 82 | ); 83 | }, 84 | ) 85 | .send(port.clone()) 86 | }) 87 | .collect(); 88 | 89 | println!("Running {} pipelines", pipelines.len()); 90 | for pipeline in pipelines { 91 | sched.add_task(pipeline).unwrap(); 92 | } 93 | } 94 | 95 | fn main() -> Result<()> { 96 | let configuration = load_config()?; 97 | println!("{}", configuration); 98 | let mut runtime = Runtime::init(&configuration)?; 99 | runtime.add_pipeline_to_run(install); 100 | runtime.execute() 101 | } 102 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/echo_reply.rs: -------------------------------------------------------------------------------- 1 | use crate::common::Result; 2 | use crate::packets::icmp::v6::{Icmpv6, Icmpv6Packet, Icmpv6Payload, Icmpv6Type, Icmpv6Types}; 3 | use crate::packets::ip::v6::Ipv6Packet; 4 | use crate::packets::{buffer, Fixed, Packet}; 5 | use std::fmt; 6 | 7 | /* From https://tools.ietf.org/html/rfc4443#section-4.2 8 | Echo Reply Message 9 | 10 | 0 1 2 3 11 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 12 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | | Type | Code | Checksum | 14 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 15 | | Identifier | Sequence Number | 16 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 17 | | Data ... 18 | +-+-+-+-+- 19 | 20 | Identifier The identifier from the invoking Echo Request message. 21 | 22 | Sequence Number 23 | The sequence number from the invoking Echo Request 24 | message. 25 | 26 | Data The data from the invoking Echo Request message. 27 | */ 28 | 29 | /// Echo reply message 30 | #[derive(Clone, Copy, Default, Debug)] 31 | #[repr(C, packed)] 32 | pub struct EchoReply { 33 | identifier: u16, 34 | seq_no: u16, 35 | } 36 | 37 | impl Icmpv6Payload for EchoReply { 38 | fn msg_type() -> Icmpv6Type { 39 | Icmpv6Types::EchoReply 40 | } 41 | } 42 | 43 | impl Icmpv6 { 44 | #[inline] 45 | pub fn identifier(&self) -> u16 { 46 | u16::from_be(self.payload().identifier) 47 | } 48 | 49 | #[inline] 50 | pub fn set_identifier(&mut self, identifier: u16) { 51 | self.payload_mut().identifier = u16::to_be(identifier); 52 | } 53 | 54 | #[inline] 55 | pub fn seq_no(&self) -> u16 { 56 | u16::from_be(self.payload().seq_no) 57 | } 58 | 59 | #[inline] 60 | pub fn set_seq_no(&mut self, seq_no: u16) { 61 | self.payload_mut().seq_no = u16::to_be(seq_no); 62 | } 63 | 64 | /// Returns the offset where the data field in the message body starts 65 | #[inline] 66 | fn data_offset(&self) -> usize { 67 | self.payload_offset() + EchoReply::size() 68 | } 69 | 70 | /// Returns the length of the data field in the message body 71 | #[inline] 72 | fn data_len(&self) -> usize { 73 | self.payload_len() - EchoReply::size() 74 | } 75 | 76 | #[inline] 77 | pub fn data(&self) -> &[u8] { 78 | if let Ok(data) = buffer::read_slice(self.mbuf(), self.data_offset(), self.data_len()) { 79 | unsafe { &(*data) } 80 | } else { 81 | unreachable!() 82 | } 83 | } 84 | 85 | #[inline] 86 | pub fn set_data(&mut self, data: &[u8]) -> Result<()> { 87 | buffer::realloc( 88 | self.mbuf(), 89 | self.data_offset(), 90 | data.len() as isize - self.data_len() as isize, 91 | )?; 92 | buffer::write_slice(self.mbuf(), self.data_offset(), data)?; 93 | Ok(()) 94 | } 95 | } 96 | 97 | impl fmt::Display for Icmpv6 { 98 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 99 | write!( 100 | f, 101 | "type: {}, code: {}, checksum: 0x{:04x}, identifier: {}, seq_no: {}", 102 | self.msg_type(), 103 | self.code(), 104 | self.checksum(), 105 | self.identifier(), 106 | self.seq_no(), 107 | ) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | use crate::packets::Fixed; 115 | 116 | #[test] 117 | fn size_of_echo_reply() { 118 | assert_eq!(4, EchoReply::size()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /examples/lpm/src/lpm.rs: -------------------------------------------------------------------------------- 1 | use fnv::FnvHasher; 2 | use netbricks::common::Result; 3 | use netbricks::packets::ip::v4::Ipv4; 4 | use netbricks::packets::{Ethernet, Packet, RawPacket}; 5 | use std::collections::HashMap; 6 | use std::convert::From; 7 | use std::hash::BuildHasherDefault; 8 | use std::net::Ipv4Addr; 9 | 10 | type FnvHash = BuildHasherDefault; 11 | 12 | pub struct IPLookup { 13 | tbl24: Vec, 14 | tbl_long: Vec, 15 | current_tbl_long: usize, 16 | raw_entries: Vec>, 17 | } 18 | 19 | const TBL24_SIZE: usize = ((1 << 24) + 1); 20 | const RAW_SIZE: usize = 33; 21 | const OVERFLOW_MASK: u16 = 0x8000; 22 | 23 | #[derive(Default, Clone)] 24 | struct Empty; 25 | impl Default for IPLookup { 26 | fn default() -> IPLookup { 27 | IPLookup { 28 | tbl24: (0..TBL24_SIZE).map(|_| 0).collect(), 29 | tbl_long: (0..TBL24_SIZE).map(|_| 0).collect(), 30 | current_tbl_long: 0, 31 | raw_entries: (0..RAW_SIZE).map(|_| Default::default()).collect(), 32 | } 33 | } 34 | } 35 | 36 | impl IPLookup { 37 | pub fn new() -> IPLookup { 38 | Default::default() 39 | } 40 | 41 | pub fn insert_ipv4(&mut self, ip: Ipv4Addr, len: usize, gate: u16) { 42 | let ip_u32 = u32::from(ip); 43 | self.insert(ip_u32, len, gate); 44 | } 45 | 46 | pub fn insert(&mut self, ip: u32, len: usize, gate: u16) { 47 | self.raw_entries[len].insert(ip, gate); 48 | } 49 | 50 | pub fn construct_table(&mut self) { 51 | for i in 0..25 { 52 | for (k, v) in &self.raw_entries[i] { 53 | let start = (k >> 8) as usize; 54 | let end = (start + (1 << (24 - i))) as usize; 55 | for pfx in start..end { 56 | self.tbl24[pfx] = *v; 57 | } 58 | } 59 | } 60 | for i in 25..RAW_SIZE { 61 | for (k, v) in &self.raw_entries[i] { 62 | let addr = *k as usize; 63 | let t24entry = self.tbl24[addr >> 8]; 64 | if (t24entry & OVERFLOW_MASK) == 0 { 65 | // Not overflown and entered yet 66 | let ctlb = self.current_tbl_long; 67 | let start = ctlb + (addr & 0xff); // Look at last 8 bits (since first 24 are predetermined. 68 | let end = start + (1 << (32 - i)); 69 | for j in ctlb..(ctlb + 256) { 70 | if j < start || j >= end { 71 | self.tbl_long[j] = t24entry; 72 | } else { 73 | self.tbl_long[j] = *v; 74 | } 75 | } 76 | self.tbl24[addr >> 8] = ((ctlb >> 8) as u16) | OVERFLOW_MASK; 77 | self.current_tbl_long += 256; 78 | } else { 79 | let start = (((t24entry & (!OVERFLOW_MASK)) as usize) << 8) + (addr & 0xff); 80 | let end = start + (1 << (32 - i)); 81 | for j in start..end { 82 | self.tbl_long[j] = *v; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | #[inline] 90 | pub fn lookup_entry(&self, ip: Ipv4Addr) -> u16 { 91 | let addr = u32::from(ip) as usize; 92 | let t24entry = self.tbl24[addr >> 8]; 93 | if (t24entry & OVERFLOW_MASK) > 0 { 94 | let index = (((t24entry & !OVERFLOW_MASK) as usize) << 8) + (addr & 0xff); 95 | self.tbl_long[index] 96 | } else { 97 | t24entry 98 | } 99 | } 100 | } 101 | 102 | pub fn lpm(packet: RawPacket) -> Result { 103 | let mut ethernet = packet.parse::()?; 104 | ethernet.swap_addresses(); 105 | let v4 = ethernet.parse::()?; 106 | Ok(v4) 107 | } 108 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/echo_request.rs: -------------------------------------------------------------------------------- 1 | use crate::common::Result; 2 | use crate::packets::icmp::v6::{Icmpv6, Icmpv6Packet, Icmpv6Payload, Icmpv6Type, Icmpv6Types}; 3 | use crate::packets::ip::v6::Ipv6Packet; 4 | use crate::packets::{buffer, Fixed, Packet}; 5 | use std::fmt; 6 | 7 | /* From https://tools.ietf.org/html/rfc4443#section-4.1 8 | Echo Request Message 9 | 10 | 0 1 2 3 11 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 12 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | | Type | Code | Checksum | 14 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 15 | | Identifier | Sequence Number | 16 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 17 | | Data ... 18 | +-+-+-+-+- 19 | 20 | Identifier An identifier to aid in matching Echo Replies 21 | to this Echo Request. May be zero. 22 | 23 | Sequence Number 24 | A sequence number to aid in matching Echo Replies 25 | to this Echo Request. May be zero. 26 | 27 | Data Zero or more octets of arbitrary data. 28 | */ 29 | 30 | /// Echo request message 31 | #[derive(Clone, Copy, Default, Debug)] 32 | #[repr(C, packed)] 33 | pub struct EchoRequest { 34 | identifier: u16, 35 | seq_no: u16, 36 | } 37 | 38 | impl Icmpv6Payload for EchoRequest { 39 | fn msg_type() -> Icmpv6Type { 40 | Icmpv6Types::EchoRequest 41 | } 42 | } 43 | 44 | impl Icmpv6 { 45 | #[inline] 46 | pub fn identifier(&self) -> u16 { 47 | u16::from_be(self.payload().identifier) 48 | } 49 | 50 | #[inline] 51 | pub fn set_identifier(&mut self, identifier: u16) { 52 | self.payload_mut().identifier = u16::to_be(identifier); 53 | } 54 | 55 | #[inline] 56 | pub fn seq_no(&self) -> u16 { 57 | u16::from_be(self.payload().seq_no) 58 | } 59 | 60 | #[inline] 61 | pub fn set_seq_no(&mut self, seq_no: u16) { 62 | self.payload_mut().seq_no = u16::to_be(seq_no); 63 | } 64 | 65 | /// Returns the offset where the data field in the message body starts 66 | #[inline] 67 | fn data_offset(&self) -> usize { 68 | self.payload_offset() + EchoRequest::size() 69 | } 70 | 71 | /// Returns the length of the data field in the message body 72 | #[inline] 73 | fn data_len(&self) -> usize { 74 | self.payload_len() - EchoRequest::size() 75 | } 76 | 77 | #[inline] 78 | pub fn data(&self) -> &[u8] { 79 | if let Ok(data) = buffer::read_slice(self.mbuf(), self.data_offset(), self.data_len()) { 80 | unsafe { &(*data) } 81 | } else { 82 | unreachable!() 83 | } 84 | } 85 | 86 | #[inline] 87 | pub fn set_data(&mut self, data: &[u8]) -> Result<()> { 88 | buffer::realloc( 89 | self.mbuf(), 90 | self.data_offset(), 91 | data.len() as isize - self.data_len() as isize, 92 | )?; 93 | buffer::write_slice(self.mbuf(), self.data_offset(), data)?; 94 | Ok(()) 95 | } 96 | } 97 | 98 | impl fmt::Display for Icmpv6 { 99 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 100 | write!( 101 | f, 102 | "type: {}, code: {}, checksum: 0x{:04x}, identifier: {}, seq_no: {}", 103 | self.msg_type(), 104 | self.code(), 105 | self.checksum(), 106 | self.identifier(), 107 | self.seq_no(), 108 | ) 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use super::*; 115 | 116 | #[test] 117 | fn size_of_echo_request() { 118 | assert_eq!(4, EchoRequest::size()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /framework/src/native/mbuf.rs: -------------------------------------------------------------------------------- 1 | use super::super::native_include as ldpdk; 2 | pub type MBuf = ldpdk::rte_mbuf; 3 | 4 | impl MBuf { 5 | #[inline] 6 | pub fn read_metadata_slot(mbuf: *mut MBuf, slot: usize) -> usize { 7 | unsafe { 8 | let ptr = (mbuf.offset(1) as *mut usize).add(slot); 9 | *ptr 10 | } 11 | } 12 | 13 | #[inline] 14 | pub fn write_metadata_slot(mbuf: *mut MBuf, slot: usize, value: usize) { 15 | unsafe { 16 | let ptr = (mbuf.offset(1) as *mut usize).add(slot); 17 | *ptr = value; 18 | } 19 | } 20 | 21 | #[inline] 22 | pub unsafe fn metadata_as(mbuf: *const MBuf, slot: usize) -> *const T { 23 | (mbuf.offset(1) as *const usize).add(slot) as *const T 24 | } 25 | 26 | #[inline] 27 | pub unsafe fn mut_metadata_as(mbuf: *mut MBuf, slot: usize) -> *mut T { 28 | (mbuf.offset(1) as *mut usize).add(slot) as *mut T 29 | } 30 | 31 | #[inline] 32 | pub fn data_address(&self, offset: usize) -> *mut u8 { 33 | unsafe { (self.buf_addr as *mut u8).offset(self.data_off as isize + offset as isize) } 34 | } 35 | 36 | /// Returns the total allocated size of this mbuf segment. 37 | /// This is a constant. 38 | #[inline] 39 | pub fn buf_len(&self) -> usize { 40 | self.buf_len as usize 41 | } 42 | 43 | /// Returns the length of data in this mbuf segment. 44 | #[inline] 45 | pub fn data_len(&self) -> usize { 46 | self.data_len as usize 47 | } 48 | 49 | /// Returns the size of the packet (across multiple mbuf segment). 50 | #[inline] 51 | pub fn pkt_len(&self) -> usize { 52 | self.pkt_len as usize 53 | } 54 | 55 | #[inline] 56 | fn pkt_headroom(&self) -> usize { 57 | self.data_off as usize 58 | } 59 | 60 | #[inline] 61 | fn pkt_tailroom(&self) -> usize { 62 | self.buf_len() - self.data_off as usize - self.data_len() 63 | } 64 | 65 | /// Add data to the beginning of the packet. This might fail (i.e., return 0) when no more headroom is left. 66 | #[inline] 67 | pub fn add_data_beginning(&mut self, len: usize) -> usize { 68 | // If only we could add a likely here. 69 | if len > self.pkt_headroom() { 70 | 0 71 | } else { 72 | self.data_off -= len as u16; 73 | self.data_len += len as u16; 74 | self.pkt_len += len as u32; 75 | len 76 | } 77 | } 78 | 79 | /// Add data to the end of a packet buffer. This might fail (i.e., return 0) when no more tailroom is left. We do 80 | /// not currently deal with packet with multiple segments. 81 | #[inline] 82 | pub fn add_data_end(&mut self, len: usize) -> usize { 83 | if len > self.pkt_tailroom() { 84 | 0 85 | } else { 86 | self.data_len += len as u16; 87 | self.pkt_len += len as u32; 88 | len 89 | } 90 | } 91 | 92 | #[inline] 93 | pub fn remove_data_beginning(&mut self, len: usize) -> usize { 94 | if len > self.data_len() { 95 | 0 96 | } else { 97 | self.data_off += len as u16; 98 | self.data_len -= len as u16; 99 | self.pkt_len -= len as u32; 100 | len 101 | } 102 | } 103 | 104 | #[inline] 105 | pub fn remove_data_end(&mut self, len: usize) -> usize { 106 | if len > self.data_len() { 107 | 0 108 | } else { 109 | self.data_len -= len as u16; 110 | self.pkt_len -= len as u32; 111 | len 112 | } 113 | } 114 | 115 | #[inline] 116 | pub fn refcnt(&self) -> u16 { 117 | unsafe { self.__bindgen_anon_2.refcnt } 118 | } 119 | 120 | #[inline] 121 | pub fn reference(&mut self) { 122 | unsafe { 123 | self.__bindgen_anon_2.refcnt += 1; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /framework/src/operators/groupby_batch.rs: -------------------------------------------------------------------------------- 1 | use super::{Batch, Enqueue, PacketError, QueueBatch, SingleThreadedQueue}; 2 | use std::collections::HashMap; 3 | 4 | pub type PipelineBuilder = 5 | dyn FnMut(QueueBatch>) -> Box>; 6 | 7 | /// Lazily-evaluate group_by operator 8 | /// 9 | /// When unmatched, the packet is marked as dropped and will short-circuit 10 | /// the remainder of the pipeline. 11 | /// 12 | /// On error, the packet is marked as aborted and will short-circuit the 13 | /// remainder of the pipeline. 14 | pub struct GroupByBatch 15 | where 16 | K: Eq + Clone + std::hash::Hash, 17 | S: Fn(&B::Item) -> K, 18 | { 19 | source: B, 20 | selector: S, 21 | producer: SingleThreadedQueue, 22 | groups: HashMap>>, 23 | default: Box>, 24 | } 25 | 26 | impl GroupByBatch 27 | where 28 | K: Eq + Clone + std::hash::Hash, 29 | S: Fn(&B::Item) -> K, 30 | { 31 | #[inline] 32 | pub fn new(source: B, selector: S, composer: C) -> Self 33 | where 34 | C: FnOnce(&mut HashMap, Box>>) -> (), 35 | { 36 | let queue = SingleThreadedQueue::::new(1); 37 | let mut groups = HashMap::, Box>>::new(); 38 | composer(&mut groups); 39 | 40 | let default = groups.remove(&None).unwrap()(QueueBatch::new(queue.clone())); 41 | 42 | let groups = groups 43 | .into_iter() 44 | .map(|(key, mut build)| { 45 | let key = key.unwrap(); 46 | let group = build(QueueBatch::new(queue.clone())); 47 | (key, group) 48 | }) 49 | .collect::>(); 50 | 51 | GroupByBatch { 52 | source, 53 | selector, 54 | producer: queue, 55 | groups, 56 | default, 57 | } 58 | } 59 | } 60 | 61 | impl Batch for GroupByBatch 62 | where 63 | K: Eq + Clone + std::hash::Hash, 64 | S: Fn(&B::Item) -> K, 65 | { 66 | type Item = B::Item; 67 | 68 | #[inline] 69 | fn next(&mut self) -> Option> { 70 | self.source.next().map(|item| match item { 71 | Ok(packet) => { 72 | let key = (self.selector)(&packet); 73 | match self.groups.get_mut(&key) { 74 | Some(group) => { 75 | self.producer.enqueue(packet); 76 | group.next().unwrap() 77 | } 78 | None => { 79 | self.producer.enqueue(packet); 80 | self.default.next().unwrap() 81 | } 82 | } 83 | } 84 | Err(e) => Err(e), 85 | }) 86 | } 87 | 88 | #[inline] 89 | fn receive(&mut self) { 90 | self.source.receive(); 91 | } 92 | } 93 | 94 | #[doc(hidden)] 95 | #[macro_export] 96 | macro_rules! __compose { 97 | ($map:ident, $($key:expr => |$arg:tt| $body:block),*) => {{ 98 | $( 99 | $map.insert(Some($key), Box::new(|$arg| Box::new($body))); 100 | )* 101 | }}; 102 | } 103 | 104 | /// Composes the pipelines for the group_by operator 105 | #[macro_export] 106 | macro_rules! compose { 107 | ($map:ident, $($key:expr => |$arg:tt| $body:block),*) => {{ 108 | $crate::__compose!($map, $($key => |$arg| $body),*); 109 | $map.insert(None, Box::new(|group| Box::new(group))); 110 | }}; 111 | ($map:ident, $($key:expr => |$arg:tt| $body:block),*,_ => |$_arg:tt| $_body:block) => {{ 112 | $crate::__compose!($map, $($key => |$arg| $body),*); 113 | $map.insert(None, Box::new(|$_arg| Box::new($_body))); 114 | }}; 115 | ($map:ident, _ => |$_arg:tt| $_body:block) => {{ 116 | $map.insert(None, Box::new(|$_arg| Box::new($_body))); 117 | }}; 118 | } 119 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/ndp/options/mtu.rs: -------------------------------------------------------------------------------- 1 | use super::MTU; 2 | use crate::common::Result; 3 | use crate::native::mbuf::MBuf; 4 | use crate::packets::icmp::v6::ndp::NdpOption; 5 | use crate::packets::{buffer, Fixed, ParseError}; 6 | use std::fmt; 7 | 8 | /* From https://tools.ietf.org/html/rfc4861#section-4.6.4 9 | MTU 10 | 11 | 0 1 2 3 12 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 13 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 14 | | Type | Length | Reserved | 15 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | MTU | 17 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 18 | 19 | Type 5 20 | 21 | Length 1 22 | 23 | Reserved This field is unused. It MUST be initialized to 24 | zero by the sender and MUST be ignored by the 25 | receiver. 26 | 27 | MTU 32-bit unsigned integer. The recommended MTU for 28 | the link. 29 | */ 30 | 31 | #[derive(Clone, Copy, Debug)] 32 | #[repr(C, packed)] 33 | struct MtuFields { 34 | option_type: u8, 35 | length: u8, 36 | reserved: u16, 37 | mtu: u32, 38 | } 39 | 40 | impl Default for MtuFields { 41 | fn default() -> MtuFields { 42 | MtuFields { 43 | option_type: MTU, 44 | length: 1, 45 | reserved: 0, 46 | mtu: 0, 47 | } 48 | } 49 | } 50 | 51 | /// Maximum transmission unit option 52 | pub struct Mtu { 53 | fields: *mut MtuFields, 54 | offset: usize, 55 | } 56 | 57 | impl Mtu { 58 | /// Parses the MTU option from the message buffer at offset 59 | #[inline] 60 | pub fn parse(mbuf: *mut MBuf, offset: usize) -> Result { 61 | let fields = buffer::read_item::(mbuf, offset)?; 62 | if unsafe { (*fields).length } != (MtuFields::size() as u8 / 8) { 63 | Err(ParseError::new("Invalid MTU option length").into()) 64 | } else { 65 | Ok(Mtu { fields, offset }) 66 | } 67 | } 68 | 69 | /// Returns the message buffer offset for this option 70 | pub fn offset(&self) -> usize { 71 | self.offset 72 | } 73 | 74 | #[inline] 75 | fn fields(&self) -> &MtuFields { 76 | unsafe { &(*self.fields) } 77 | } 78 | 79 | #[inline] 80 | fn fields_mut(&mut self) -> &mut MtuFields { 81 | unsafe { &mut (*self.fields) } 82 | } 83 | 84 | #[inline] 85 | pub fn option_type(&self) -> u8 { 86 | self.fields().option_type 87 | } 88 | 89 | pub fn length(&self) -> u8 { 90 | self.fields().length 91 | } 92 | 93 | pub fn mtu(&self) -> u32 { 94 | u32::from_be(self.fields().mtu) 95 | } 96 | 97 | pub fn set_mtu(&mut self, mtu: u32) { 98 | self.fields_mut().mtu = u32::to_be(mtu); 99 | } 100 | } 101 | 102 | impl fmt::Display for Mtu { 103 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 104 | write!( 105 | f, 106 | "type: {}, length: {}, mtu: {}", 107 | self.option_type(), 108 | self.length(), 109 | self.mtu() 110 | ) 111 | } 112 | } 113 | 114 | impl NdpOption for Mtu { 115 | #![allow(clippy::not_unsafe_ptr_arg_deref)] 116 | #[inline] 117 | fn do_push(mbuf: *mut MBuf) -> Result 118 | where 119 | Self: Sized, 120 | { 121 | let offset = unsafe { (*mbuf).data_len() }; 122 | 123 | buffer::alloc(mbuf, offset, MtuFields::size())?; 124 | 125 | let fields = buffer::write_item::(mbuf, offset, &Default::default())?; 126 | Ok(Mtu { fields, offset }) 127 | } 128 | } 129 | 130 | #[cfg(test)] 131 | mod tests { 132 | use super::*; 133 | 134 | #[test] 135 | fn size_of_mtu() { 136 | assert_eq!(8, MtuFields::size()); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /framework/src/interface/dpdk.rs: -------------------------------------------------------------------------------- 1 | use config::{NetBricksConfiguration, DEFAULT_CACHE_SIZE, DEFAULT_POOL_SIZE}; 2 | use native::libnuma; 3 | use native::zcsi; 4 | use std::cell::Cell; 5 | use std::ffi::CString; 6 | 7 | // Not used currently, but we may want to re-use the concept of MetaData or 8 | // just remove completely. 9 | pub const METADATA_SLOTS: u16 = 16; 10 | 11 | /// Initialize the system, whitelisting some set of NICs and allocating mempool of given size. 12 | pub fn init_system_wl_with_mempool( 13 | name: &str, 14 | core: i32, 15 | pci: &[String], 16 | pool_size: u32, 17 | cache_size: u32, 18 | ) { 19 | let name_cstr = CString::new(name).unwrap(); 20 | let pci_cstr: Vec<_> = pci.iter().map(|p| CString::new(&p[..]).unwrap()).collect(); 21 | let mut whitelist: Vec<_> = pci_cstr.iter().map(|p| p.as_ptr()).collect(); 22 | 23 | unsafe { 24 | let ret = zcsi::init_system_whitelisted( 25 | name_cstr.as_ptr(), 26 | name.len() as i32, 27 | core, 28 | whitelist.as_mut_ptr(), 29 | pci.len() as i32, 30 | pool_size, 31 | cache_size, 32 | METADATA_SLOTS, 33 | ); 34 | if ret != 0 { 35 | panic!("Could not initialize the system errno {}", ret) 36 | } 37 | } 38 | } 39 | 40 | /// Initialize the system, whitelisting some set of NICs. 41 | pub fn init_system_wl(name: &str, core: i32, pci: &[String]) { 42 | init_system_wl_with_mempool(name, core, pci, DEFAULT_POOL_SIZE, DEFAULT_CACHE_SIZE); 43 | set_numa_domain(); 44 | } 45 | 46 | /// Initialize the system as a DPDK secondary process with a set of VDEVs. User must specify mempool name to use. 47 | pub fn init_system_secondary(name: &str, core: i32) { 48 | let name_cstr = CString::new(name).unwrap(); 49 | let mut vdev_list = vec![]; 50 | unsafe { 51 | let ret = zcsi::init_secondary( 52 | name_cstr.as_ptr(), 53 | name.len() as i32, 54 | core, 55 | vdev_list.as_mut_ptr(), 56 | 0, 57 | ); 58 | if ret != 0 { 59 | panic!("Could not initialize secondary process errno {}", ret) 60 | } 61 | } 62 | set_numa_domain(); 63 | } 64 | 65 | /// Initialize the system based on the supplied scheduler configuration. 66 | pub fn init_system(config: &NetBricksConfiguration) { 67 | if config.name.is_empty() { 68 | panic!("Configuration must provide a name."); 69 | } 70 | 71 | // We init with all devices blacklisted and rely on the attach logic to white list them as necessary. 72 | if config.secondary { 73 | // We do not have control over any of the other settings in this case. 74 | init_system_secondary(&config.name[..], config.primary_core); 75 | } else { 76 | init_system_wl_with_mempool( 77 | &config.name[..], 78 | config.primary_core, 79 | &[], 80 | config.pool_size, 81 | config.cache_size, 82 | ); 83 | } 84 | set_numa_domain(); 85 | } 86 | 87 | thread_local!(static NUMA_DOMAIN: Cell = Cell::new(-1)); 88 | 89 | pub fn set_numa_domain() { 90 | let domain = unsafe { 91 | if libnuma::numa_available() == -1 { 92 | info!("No NUMA information found, support disabled"); 93 | -1 94 | } else { 95 | let domain = libnuma::numa_preferred(); 96 | info!("Running on node {}", domain); 97 | domain 98 | } 99 | }; 100 | NUMA_DOMAIN.with(|f| f.set(domain)) 101 | } 102 | 103 | /// Affinitize a pthread to a core and assign a DPDK thread ID. 104 | pub fn init_thread(tid: i32, core: i32) { 105 | let numa = unsafe { zcsi::init_thread(tid, core) }; 106 | NUMA_DOMAIN.with(|f| { 107 | f.set(numa); 108 | }); 109 | if numa == -1 { 110 | info!("No NUMA information found, support disabled"); 111 | } else { 112 | info!("Running on node {}", numa); 113 | }; 114 | } 115 | 116 | #[inline] 117 | pub fn get_domain() -> i32 { 118 | NUMA_DOMAIN.with(|f| f.get()) 119 | } 120 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/ndp/neighbor_solicit.rs: -------------------------------------------------------------------------------- 1 | use crate::packets::icmp::v6::ndp::NdpPayload; 2 | use crate::packets::icmp::v6::{Icmpv6, Icmpv6Packet, Icmpv6Payload, Icmpv6Type, Icmpv6Types}; 3 | use crate::packets::ip::v6::Ipv6Packet; 4 | use std::fmt; 5 | use std::net::Ipv6Addr; 6 | 7 | /* From https://tools.ietf.org/html/rfc4861#section-4.3 8 | Neighbor Solicitation Message Format 9 | 10 | 0 1 2 3 11 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 12 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | | Type | Code | Checksum | 14 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 15 | | Reserved | 16 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 17 | | | 18 | + + 19 | | | 20 | + Target Address + 21 | | | 22 | + + 23 | | | 24 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | | Options ... 26 | +-+-+-+-+-+-+-+-+-+-+-+- 27 | 28 | Reserved This field is unused. It MUST be initialized to 29 | zero by the sender and MUST be ignored by the 30 | receiver. 31 | 32 | Target Address The IP address of the target of the solicitation. 33 | It MUST NOT be a multicast address. 34 | 35 | Possible options: 36 | 37 | Source link-layer address 38 | The link-layer address for the sender. MUST NOT be 39 | included when the source IP address is the 40 | unspecified address. Otherwise, on link layers 41 | that have addresses this option MUST be included in 42 | multicast solicitations and SHOULD be included in 43 | unicast solicitations. 44 | */ 45 | 46 | /// NDP neighbor solicitation message 47 | #[derive(Debug)] 48 | #[repr(C)] 49 | pub struct NeighborSolicitation { 50 | reserved: u32, 51 | target_addr: Ipv6Addr, 52 | } 53 | 54 | impl Default for NeighborSolicitation { 55 | fn default() -> NeighborSolicitation { 56 | NeighborSolicitation { 57 | reserved: 0, 58 | target_addr: Ipv6Addr::UNSPECIFIED, 59 | } 60 | } 61 | } 62 | 63 | impl Icmpv6Payload for NeighborSolicitation { 64 | #[inline] 65 | fn msg_type() -> Icmpv6Type { 66 | Icmpv6Types::NeighborSolicitation 67 | } 68 | } 69 | 70 | impl NdpPayload for NeighborSolicitation {} 71 | 72 | /// NDP neighbor solicitation packet 73 | impl Icmpv6 { 74 | #[inline] 75 | pub fn reserved(&self) -> u32 { 76 | u32::from_be(self.payload().reserved) 77 | } 78 | 79 | #[inline] 80 | pub fn target_addr(&self) -> Ipv6Addr { 81 | self.payload().target_addr 82 | } 83 | 84 | #[inline] 85 | pub fn set_target_addr(&mut self, target_addr: Ipv6Addr) { 86 | self.payload_mut().target_addr = target_addr 87 | } 88 | } 89 | 90 | impl fmt::Display for Icmpv6 { 91 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 | write!( 93 | f, 94 | "type: {}, code: {}, checksum: 0x{:04x}, reserved: {}, target address: {}", 95 | self.msg_type(), 96 | self.code(), 97 | self.checksum(), 98 | self.reserved(), 99 | self.target_addr() 100 | ) 101 | } 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use super::*; 107 | use packets::Fixed; 108 | 109 | #[test] 110 | fn size_of_neighbor_solicitation() { 111 | assert_eq!(20, NeighborSolicitation::size()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /native/test/test.c: -------------------------------------------------------------------------------- 1 | // TODO: Fix - segfaults due to include version expectation. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct node { 14 | int tid; 15 | int core; 16 | int queue; 17 | }; 18 | 19 | int cursec() { 20 | struct timeval t; 21 | gettimeofday(&t, NULL); 22 | return t.tv_sec; 23 | } 24 | 25 | #define PORT_OUT 1 26 | #define PORT_IN 0 27 | void* thr(void* arg) { 28 | struct node* n = arg; 29 | struct rte_mbuf* restrict pkts[32]; 30 | int i; 31 | int q = n->queue; 32 | int start_sec = cursec(); 33 | int rcvd = 0; 34 | int sent = 0; 35 | init_thread(n->tid, n->core); 36 | if (q >= 20) { 37 | printf("Somehow, queue beyond 20\n"); 38 | } 39 | while (1) { 40 | /*int recv;*/ 41 | i = mbuf_alloc_bulk(pkts, 60, 32); 42 | if (i != 0) { 43 | printf("Error allocating packets %d\n", i); 44 | break; 45 | } else { 46 | int send, recv; 47 | 48 | /* Start setting MAC address */ 49 | for (i = 0; i < 32; i++) { 50 | struct ether_hdr* hdr = rte_pktmbuf_mtod(pkts[i], struct ether_hdr*); 51 | hdr->d_addr.addr_bytes[5] = (10 * q) + 1; 52 | hdr->s_addr.addr_bytes[5] = (10 * q) + 2; 53 | hdr->ether_type = rte_cpu_to_be_16(0x0800); 54 | /*rte_mbuf_sanity_check(pkts[i], 1);*/ 55 | } 56 | send = send_pkts(PORT_OUT, q, pkts, 32); 57 | for (i = send; i < 32; i++) { 58 | mbuf_free(pkts[i]); 59 | } 60 | recv = recv_pkts(PORT_IN, q, pkts, 32); 61 | rcvd += recv; 62 | sent += send; 63 | if (cursec() != start_sec) { 64 | printf("%d %d rx=%d tx=%d\n", n->core, (cursec() - start_sec), rcvd, sent); 65 | /*printf("recv_pkt\n");*/ 66 | /*rte_pktmbuf_dump(stdout, pkts[0], 16384);*/ 67 | start_sec = cursec(); 68 | rcvd = 0; 69 | sent = 0; 70 | } 71 | for (int i = 0; i < recv; i++) { 72 | mbuf_free(pkts[i]); 73 | } 74 | } 75 | } 76 | printf("Socket ID (%d) is %d. DONE\n", n->core, rte_socket_id()); 77 | return NULL; 78 | } 79 | 80 | void dump() { 81 | printf("pkt_len %lu\n", offsetof(struct rte_mbuf, pkt_len)); 82 | printf("sizeof(rte_eth_dev_info) %lu\n", sizeof(struct rte_eth_dev_info)); 83 | } 84 | 85 | #define THREADS 2 86 | int main(int argc, char* argv[]) { 87 | /*dump();*/ 88 | pthread_t thread[20]; 89 | struct node n[20]; 90 | int rxq_cores[20]; 91 | int txq_cores[20]; 92 | int ret = init_system(1); 93 | 94 | assert(ret == 0); 95 | rxq_cores[0] = 10; 96 | rxq_cores[1] = 11; 97 | txq_cores[0] = 10; 98 | txq_cores[1] = 11; 99 | 100 | /*for (int i = 0; i < 20; i++) {*/ 101 | /*rxq_cores[i] = i;*/ 102 | /*txq_cores[i] = i;*/ 103 | /*}*/ 104 | enumerate_pmd_ports(); 105 | ret = init_pmd_port(PORT_OUT, THREADS, THREADS, rxq_cores, txq_cores, 256, 256, PORT_OUT == PORT_IN, 0, 0); 106 | assert(ret == 0); 107 | if (PORT_IN != PORT_OUT) { 108 | ret = init_pmd_port(PORT_IN, THREADS, THREADS, rxq_cores, txq_cores, 128, 512, 0, 0, 0); 109 | assert(ret == 0); 110 | } 111 | n[0].tid = 10; 112 | n[0].core = 10; 113 | n[0].queue = 0; 114 | n[1].tid = 11; 115 | n[1].core = 11; 116 | n[1].queue = 1; 117 | /*thr(&n[0]);*/ 118 | pthread_create(&thread[0], NULL, &thr, &n[0]); 119 | pthread_create(&thread[1], NULL, &thr, &n[1]); 120 | /*for (int i = 0; i < THREADS; i++) {*/ 121 | /*n[i].tid = 64 - i;*/ 122 | /*n[i].core = i;*/ 123 | /*pthread_create(&thread[i],*/ 124 | /*NULL,*/ 125 | /*&thr,*/ 126 | /*&n[i]);*/ 127 | /*}*/ 128 | 129 | for (int i = 0; i < THREADS; i++) { 130 | pthread_join(thread[i], NULL); 131 | } 132 | free_pmd_port(PORT_OUT); 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /framework/src/state/cp_mergeable.rs: -------------------------------------------------------------------------------- 1 | use packets::ip::Flow; 2 | use std::collections::hash_map::Iter; 3 | use std::collections::HashMap; 4 | use std::hash::BuildHasherDefault; 5 | use std::ops::AddAssign; 6 | use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; 7 | use twox_hash::XxHash; 8 | 9 | type XxHasher = BuildHasherDefault; 10 | const VEC_SIZE: usize = 1 << 24; 11 | 12 | /// A generic store for associating some merge-able type with each flow. Note, 13 | /// the merge must be commutative, we do not guarantee ordering for things being 14 | /// merged. The merge function is implemented by implementing the 15 | /// [`AddAssign`](https://doc.rust-lang.org/std/ops/trait.AddAssign.html) trait 16 | /// and overriding the `add_assign` method there. We assume that the stored 17 | /// quantity needs to only be accessed from the control plane, and cannot be 18 | /// accessed from the data plane. 19 | /// 20 | /// #[FIXME] 21 | /// Garbage collection. 22 | #[derive(Clone)] 23 | pub struct CpMergeableStoreDataPath + Default + Clone> { 24 | /// Contains the counts on the data path. 25 | cache: Vec<(Flow, T)>, 26 | /// How many updates has this counter seen. 27 | updates: usize, 28 | /// How many updates to see before sending. 29 | delay: usize, 30 | channel: SyncSender>, 31 | } 32 | 33 | pub struct CpMergeableStoreControlPlane + Default + Clone> { 34 | /// The actual values. 35 | flow_counters: HashMap, 36 | channel: Receiver>, 37 | } 38 | 39 | impl + Default + Clone> CpMergeableStoreDataPath { 40 | /// Change the value for the given `Flow`. 41 | #[inline] 42 | pub fn update(&mut self, flow: Flow, inc: T) { 43 | self.cache.push((flow, inc)); 44 | self.updates += 1; 45 | if self.updates >= self.delay { 46 | self.updates = 0; 47 | let _ = self.channel.try_send(self.cache.drain(0..).collect()); 48 | } 49 | } 50 | } 51 | 52 | impl + Default + Clone> CpMergeableStoreControlPlane { 53 | fn update_internal(&mut self, v: Vec<(Flow, T)>) { 54 | for (flow, c) in v { 55 | *(self 56 | .flow_counters 57 | .entry(flow) 58 | .or_insert_with(Default::default)) += c; 59 | } 60 | } 61 | 62 | /// Call periodically to drain the queue. 63 | pub fn recv(&mut self) { 64 | match self.channel.try_recv() { 65 | Err(_) => (), 66 | Ok(v) => self.update_internal(v), 67 | } 68 | } 69 | 70 | pub fn get(&self, flow: &Flow) -> T { 71 | match self.flow_counters.get(flow) { 72 | Some(i) => i.clone(), 73 | None => Default::default(), 74 | } 75 | } 76 | 77 | pub fn iter(&self) -> Iter { 78 | self.flow_counters.iter() 79 | } 80 | 81 | pub fn len(&self) -> usize { 82 | self.flow_counters.len() 83 | } 84 | 85 | pub fn is_empty(&self) -> bool { 86 | self.flow_counters.is_empty() 87 | } 88 | 89 | /// Remove an entry from the table. 90 | #[inline] 91 | pub fn remove(&mut self, flow: &Flow) -> T { 92 | self.flow_counters 93 | .remove(flow) 94 | .unwrap_or_else(Default::default) 95 | } 96 | } 97 | 98 | /// Create a `CpMergeableStore`. `delay` specifies the number of buckets buffered together, while `channel_size` 99 | /// specifies the number of outstanding messages. 100 | pub fn new_cp_mergeable_store + Default + Clone>( 101 | delay: usize, 102 | channel_size: usize, 103 | ) -> ( 104 | CpMergeableStoreDataPath, 105 | Box>, 106 | ) { 107 | let (sender, receiver) = sync_channel(channel_size); 108 | ( 109 | CpMergeableStoreDataPath { 110 | cache: Vec::with_capacity(delay), 111 | updates: 0, 112 | delay, 113 | channel: sender, 114 | }, 115 | Box::new(CpMergeableStoreControlPlane { 116 | // FIXME: Don't need this to be quite this big? 117 | flow_counters: HashMap::with_capacity_and_hasher(VEC_SIZE, Default::default()), 118 | channel: receiver, 119 | }), 120 | ) 121 | } 122 | -------------------------------------------------------------------------------- /framework/src/packets/icmp/v6/ndp/router_solicit.rs: -------------------------------------------------------------------------------- 1 | use crate::packets::icmp::v6::ndp::NdpPayload; 2 | use crate::packets::icmp::v6::{Icmpv6, Icmpv6Packet, Icmpv6Payload, Icmpv6Type, Icmpv6Types}; 3 | use crate::packets::ip::v6::Ipv6Packet; 4 | use std::fmt; 5 | 6 | /* From https://tools.ietf.org/html/rfc4861#section-4.1 7 | Router Solicitation Message Format 8 | 9 | 0 1 2 3 10 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 11 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 12 | | Type | Code | Checksum | 13 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 14 | | Reserved | 15 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | Options ... 17 | +-+-+-+-+-+-+-+-+-+-+-+- 18 | 19 | Reserved This field is unused. It MUST be initialized to 20 | zero by the sender and MUST be ignored by the 21 | receiver. 22 | 23 | Valid Options: 24 | 25 | Source link-layer address 26 | The link-layer address of the sender, if 27 | known. MUST NOT be included if the Source Address 28 | is the unspecified address. Otherwise, it SHOULD 29 | be included on link layers that have addresses. 30 | */ 31 | 32 | /// NDP router solicitation message 33 | #[derive(Clone, Copy, Default, Debug)] 34 | #[repr(C, packed)] 35 | pub struct RouterSolicitation { 36 | reserved: u32, 37 | } 38 | 39 | impl Icmpv6Payload for RouterSolicitation { 40 | #[inline] 41 | fn msg_type() -> Icmpv6Type { 42 | Icmpv6Types::RouterSolicitation 43 | } 44 | } 45 | 46 | impl NdpPayload for RouterSolicitation {} 47 | 48 | /// NDP router solicitation packet 49 | impl Icmpv6 { 50 | #[inline] 51 | pub fn reserved(&self) -> u32 { 52 | u32::from_be(self.payload().reserved) 53 | } 54 | } 55 | 56 | impl fmt::Display for Icmpv6 { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | write!( 59 | f, 60 | "type: {}, code: {}, checksum: 0x{:04x}, reserved: {}", 61 | self.msg_type(), 62 | self.code(), 63 | self.checksum(), 64 | self.reserved() 65 | ) 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | #[rustfmt::skip] 71 | pub const ROUTER_SOLICIT_PACKET: [u8; 70] = [ 72 | // ** ethernet header 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 75 | 0x86, 0xDD, 76 | // ** IPv6 header 77 | 0x60, 0x00, 0x00, 0x00, 78 | // payload length 79 | 0x00, 0x10, 80 | 0x3a, 81 | 0xff, 82 | 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xf0, 0x45, 0xff, 0xfe, 0x0c, 0x66, 0x4b, 83 | 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 84 | // ** ICMPv6 header 85 | // type 86 | 0x85, 87 | // code 88 | 0x00, 89 | // checksum 90 | 0xf5, 0x0c, 91 | // ** router solicitation message 92 | // reserved 93 | 0x00, 0x00, 0x00, 0x00, 94 | // ** source link-layer address option 95 | 0x01, 0x01, 0x70, 0x3a, 0xcb, 0x1b, 0xf9, 0x7a 96 | ]; 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | use super::*; 101 | use crate::packets::icmp::v6::{Icmpv6Message, Icmpv6Parse, Icmpv6Types}; 102 | use crate::packets::ip::v6::Ipv6; 103 | use crate::packets::{Ethernet, Fixed, Packet, RawPacket}; 104 | use crate::testing::dpdk_test; 105 | 106 | #[test] 107 | fn size_of_router_solicitation() { 108 | assert_eq!(4, RouterSolicitation::size()); 109 | } 110 | 111 | #[dpdk_test] 112 | fn parse_router_solicitation_packet() { 113 | let packet = RawPacket::from_bytes(&ROUTER_SOLICIT_PACKET).unwrap(); 114 | let ethernet = packet.parse::().unwrap(); 115 | let ipv6 = ethernet.parse::().unwrap(); 116 | 117 | if let Ok(Icmpv6Message::RouterSolicitation(solicit)) = ipv6.parse_icmpv6() { 118 | assert_eq!(Icmpv6Types::RouterSolicitation, solicit.msg_type()); 119 | assert_eq!(0, solicit.reserved()); 120 | } else { 121 | panic!("bad packet"); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /native/ring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "mempool.h" 14 | 15 | /** 16 | * This file provides utility methods to connect to various vSwitches (OVS, 17 | * Bess) using RTE_RING, this is mostly to allow emulating fast container based 18 | * connections. 19 | **/ 20 | 21 | #define __cacheline_aligned __attribute__((aligned(64))) 22 | #define PORT_NAME_LEN 128 23 | #define MAX_QUEUES_PER_DIR 32 24 | /* This is equivalent to the old bar */ 25 | struct rte_ring_bar { 26 | char name[PORT_NAME_LEN]; 27 | 28 | /* The term RX/TX could be very confusing for a virtual switch. 29 | * Instead, we use the "incoming/outgoing" convention: 30 | * - incoming: outside -> SoftNIC 31 | * - outgoing: SoftNIC -> outside */ 32 | int num_inc_q; 33 | int num_out_q; 34 | 35 | struct rte_ring *inc_qs[MAX_QUEUES_PER_DIR]; 36 | 37 | struct rte_ring *out_qs[MAX_QUEUES_PER_DIR]; 38 | }; 39 | 40 | #define PORT_DIR_PREFIX "sn_vports" 41 | #define PORT_FNAME_LEN 128 + 256 42 | 43 | static int init_bess_ring(const char *ifname, struct rte_mempool *mempool) { 44 | struct rte_ring_bar *bar; 45 | int i; 46 | 47 | FILE *fd; 48 | char port_file[PORT_FNAME_LEN]; 49 | int port; 50 | struct rte_eth_conf null_conf; 51 | 52 | memset(&null_conf, 0, sizeof(struct rte_eth_conf)); 53 | 54 | snprintf(port_file, PORT_FNAME_LEN, "%s/%s/%s", P_tmpdir, PORT_DIR_PREFIX, ifname); 55 | fd = fopen(port_file, "r"); 56 | if (!fd) { 57 | RTE_LOG(WARNING, PMD, "Could not open %s\n", port_file); 58 | return -EINVAL; 59 | } 60 | /* Assuming we need to read one pointer */ 61 | i = fread(&bar, 8, 1, fd); 62 | fclose(fd); 63 | if (i != 1) { 64 | RTE_LOG(WARNING, PMD, "Read %d bytes\n", i); 65 | return -EINVAL; 66 | } 67 | if (bar == NULL) { 68 | RTE_LOG(WARNING, PMD, "Could not find bar\n"); 69 | return -EINVAL; 70 | } 71 | RTE_LOG(INFO, PMD, "Initing vport %p found name %s\n", bar, bar->name); 72 | RTE_LOG(INFO, PMD, "String length %d\n", PORT_NAME_LEN); 73 | RTE_LOG(INFO, PMD, "num_inc_q offset %d\n", (int)offsetof(struct rte_ring_bar, num_inc_q)); 74 | RTE_LOG(INFO, PMD, "num_out_q offset %d\n", (int)offsetof(struct rte_ring_bar, num_out_q)); 75 | RTE_LOG(INFO, PMD, "Going to init ring port %s\n", ifname); 76 | RTE_LOG(INFO, PMD, "Going to create port %s %d %d\n", bar->name, bar->num_out_q, bar->num_inc_q); 77 | port = rte_eth_from_rings(bar->name, bar->out_qs, bar->num_out_q, bar->inc_qs, bar->num_inc_q, 0); 78 | for (i = 0; i < bar->num_out_q; i++) { 79 | rte_eth_rx_queue_setup(port, i, 32, 0, NULL, mempool); 80 | } 81 | for (i = 0; i < bar->num_inc_q; i++) { 82 | rte_eth_tx_queue_setup(port, i, 32, 0, NULL); 83 | } 84 | RTE_LOG(INFO, PMD, "Port %d\n", port); 85 | return port; 86 | // Do not call rte_eth_dev_configure, else everything breaks 87 | } 88 | 89 | /* FIXME: Take name length */ 90 | int init_bess_eth_ring(const char *ifname, int core) { 91 | return init_bess_ring(ifname, get_mempool_for_core(core)); 92 | } 93 | 94 | static int init_ovs_ring(int iface, struct rte_mempool *mempool) { 95 | const int Q_NAME = 64; 96 | const char *RING_NAME = "dpdkr%u"; 97 | const char *MP_CLIENT_RXQ_NAME = "dpdkr%u_tx"; 98 | const char *MP_CLIENT_TXQ_NAME = "dpdkr%u_rx"; 99 | char rxq_name[Q_NAME]; 100 | char txq_name[Q_NAME]; 101 | char port_name[Q_NAME]; 102 | struct rte_ring *rxq = NULL; 103 | struct rte_ring *txq = NULL; 104 | int port = 0; 105 | /* Get queue names */ 106 | snprintf(rxq_name, Q_NAME, MP_CLIENT_RXQ_NAME, iface); 107 | snprintf(txq_name, Q_NAME, MP_CLIENT_TXQ_NAME, iface); 108 | snprintf(port_name, Q_NAME, RING_NAME, iface); 109 | rxq = rte_ring_lookup(rxq_name); 110 | txq = rte_ring_lookup(txq_name); 111 | 112 | port = rte_eth_from_rings(port_name, &rxq, 1, &txq, 1, 0); 113 | rte_eth_rx_queue_setup(port, 0, 32, 0, NULL, mempool); 114 | rte_eth_tx_queue_setup(port, 0, 32, 0, NULL); 115 | return port; 116 | } 117 | 118 | int init_ovs_eth_ring(int iface, int core) { 119 | return init_ovs_ring(iface, get_mempool_for_core(core)); 120 | } 121 | -------------------------------------------------------------------------------- /framework/src/control/sctp.rs: -------------------------------------------------------------------------------- 1 | use super::{Available, IOScheduler, PollHandle, PollScheduler, Token, HUP, READ, WRITE}; 2 | use fnv::FnvHasher; 3 | use scheduler::Executable; 4 | /// SCTP Connections. 5 | use sctp::*; 6 | use std::collections::HashMap; 7 | use std::hash::BuildHasherDefault; 8 | use std::marker::PhantomData; 9 | use std::net::{SocketAddr, ToSocketAddrs}; 10 | use std::os::unix::io::AsRawFd; 11 | 12 | pub trait SctpControlAgent { 13 | fn new(address: SocketAddr, stream: SctpStream, scheduler: IOScheduler) -> Self; 14 | fn handle_read_ready(&mut self) -> bool; 15 | fn handle_write_ready(&mut self) -> bool; 16 | fn handle_hup(&mut self) -> bool; 17 | } 18 | 19 | type FnvHash = BuildHasherDefault; 20 | pub struct SctpControlServer { 21 | listener: SctpListener, 22 | scheduler: PollScheduler, 23 | handle: PollHandle, 24 | next_token: Token, 25 | listener_token: Token, 26 | phantom_t: PhantomData, 27 | connections: HashMap, 28 | } 29 | 30 | impl Executable for SctpControlServer { 31 | fn execute(&mut self) { 32 | self.schedule(); 33 | } 34 | 35 | #[inline] 36 | fn dependencies(&mut self) -> Vec { 37 | vec![] 38 | } 39 | } 40 | 41 | // FIXME: Add one-to-many SCTP support? 42 | impl SctpControlServer { 43 | pub fn new_streaming(address: A) -> SctpControlServer { 44 | let listener = SctpListener::bind(address).unwrap(); 45 | listener.set_nonblocking(true).unwrap(); 46 | let scheduler = PollScheduler::new(); 47 | let listener_token = 0; 48 | let handle = scheduler.new_poll_handle(); 49 | handle.new_io_port(&listener, listener_token); 50 | handle.schedule_read(&listener, listener_token); 51 | SctpControlServer { 52 | listener, 53 | scheduler, 54 | handle, 55 | next_token: listener_token + 1, 56 | listener_token, 57 | phantom_t: PhantomData, 58 | connections: HashMap::with_capacity_and_hasher(32, Default::default()), 59 | } 60 | } 61 | 62 | fn listen(&mut self) { 63 | self.handle 64 | .schedule_read(&self.listener, self.listener_token); 65 | } 66 | 67 | pub fn schedule(&mut self) { 68 | match self.scheduler.get_token_noblock() { 69 | Some((token, avail)) if token == self.listener_token => { 70 | self.accept_connection(avail); 71 | } 72 | Some((token, available)) => { 73 | self.handle_data(token, available); 74 | } 75 | _ => {} 76 | } 77 | } 78 | 79 | fn accept_connection(&mut self, available: Available) { 80 | if available & READ != 0 { 81 | // Make sure we have something to accept 82 | if let Ok((stream, addr)) = self.listener.accept() { 83 | let token = self.next_token; 84 | self.next_token += 1; 85 | stream.set_nonblocking(true).unwrap(); 86 | let stream_fd = stream.as_raw_fd(); 87 | self.connections.insert( 88 | token, 89 | T::new( 90 | addr, 91 | stream, 92 | IOScheduler::new(self.scheduler.new_poll_handle(), stream_fd, token), 93 | ), 94 | ); 95 | // Add to some sort of hashmap. 96 | } 97 | } 98 | self.listen(); 99 | } 100 | 101 | fn handle_data(&mut self, token: Token, available: Available) { 102 | let preserve = { 103 | match self.connections.get_mut(&token) { 104 | Some(connection) => { 105 | if available & READ != 0 { 106 | connection.handle_read_ready() 107 | } else if available & WRITE != 0 { 108 | connection.handle_write_ready() 109 | } else if available & HUP != 0 { 110 | connection.handle_hup() 111 | } else { 112 | true 113 | } 114 | } 115 | None => { 116 | // FIXME: Record 117 | true 118 | } 119 | } 120 | }; 121 | 122 | if !preserve { 123 | self.connections.remove(&token); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /examples/acl-fw/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate fnv; 2 | #[macro_use] 3 | extern crate lazy_static; 4 | extern crate netbricks; 5 | use fnv::FnvHasher; 6 | use netbricks::allocators::CacheAligned; 7 | use netbricks::common::Result; 8 | use netbricks::config::load_config; 9 | use netbricks::interface::*; 10 | use netbricks::operators::{Batch, ReceiveBatch}; 11 | use netbricks::packets::ip::v4::Ipv4; 12 | use netbricks::packets::ip::Flow; 13 | use netbricks::packets::{Ethernet, Packet, Udp}; 14 | use netbricks::runtime::Runtime; 15 | use netbricks::scheduler::Scheduler; 16 | use netbricks::utils::cidr::v4::Ipv4Cidr; 17 | use netbricks::utils::cidr::Cidr; 18 | use std::collections::HashSet; 19 | use std::hash::BuildHasherDefault; 20 | use std::net::IpAddr; 21 | use std::net::Ipv4Addr; 22 | use std::sync::Arc; 23 | use std::sync::RwLock; 24 | 25 | type FnvHash = BuildHasherDefault; 26 | 27 | lazy_static! { 28 | static ref FLOW_CACHE: Arc>> = { 29 | let m = HashSet::with_hasher(Default::default()); 30 | Arc::new(RwLock::new(m)) 31 | }; 32 | } 33 | 34 | lazy_static! { 35 | static ref ACLS: Arc>> = { 36 | let acl = vec![Acl { 37 | src_prefix: Some(Ipv4Cidr::new(Ipv4Addr::new(0, 0, 0, 0), 0).unwrap()), 38 | dst_prefix: None, 39 | src_port: None, 40 | dst_port: None, 41 | established: None, 42 | drop: false, 43 | }]; 44 | Arc::new(RwLock::new(acl)) 45 | }; 46 | } 47 | 48 | #[derive(Clone)] 49 | pub struct Acl { 50 | pub src_prefix: Option, 51 | pub dst_prefix: Option, 52 | pub src_port: Option, 53 | pub dst_port: Option, 54 | pub established: Option, 55 | // TODO: Related not complete 56 | pub drop: bool, 57 | } 58 | 59 | impl Acl { 60 | fn contains(&self, ip: IpAddr) -> bool { 61 | if let Some(ref prefix) = self.src_prefix { 62 | prefix.contains_ip(ip) 63 | } else { 64 | true 65 | } 66 | } 67 | 68 | fn matches(&self, flow: &Flow) -> bool { 69 | if self.contains(flow.src_ip()) 70 | && self.contains(flow.dst_ip()) 71 | && (self.src_port.is_none() || flow.src_port() == self.src_port.unwrap()) 72 | && (self.dst_port.is_none() || flow.dst_port() == self.dst_port.unwrap()) 73 | { 74 | if let Some(established) = self.established { 75 | let rev_flow = flow.reverse(); 76 | (FLOW_CACHE.read().unwrap().contains(flow) 77 | || FLOW_CACHE.read().unwrap().contains(&rev_flow)) 78 | == established 79 | } else { 80 | true 81 | } 82 | } else { 83 | false 84 | } 85 | } 86 | } 87 | 88 | fn install(ports: Vec>, sched: &mut S) { 89 | for port in &ports { 90 | println!( 91 | "Receiving port {} rxq {} txq {}", 92 | port.mac_address(), 93 | port.rxq(), 94 | port.txq() 95 | ); 96 | } 97 | 98 | let pipelines: Vec<_> = ports 99 | .iter() 100 | .map(|port| { 101 | ReceiveBatch::new(port.clone()) 102 | .map(|p| { 103 | let mut ethernet = p.parse::()?; 104 | ethernet.swap_addresses(); 105 | let v4 = ethernet.parse::()?; 106 | let udp = v4.parse::>()?; 107 | Ok(udp) 108 | }) 109 | .filter(|p| acl_match(p)) 110 | .send(port.clone()) 111 | }) 112 | .collect(); 113 | 114 | println!("Running {} pipelines", pipelines.len()); 115 | for pipeline in pipelines { 116 | sched.add_task(pipeline).unwrap(); 117 | } 118 | } 119 | 120 | fn acl_match(p: &Udp) -> bool { 121 | let flow = p.flow(); 122 | let acls = ACLS.read().unwrap(); 123 | let matches = acls.iter().find(|ref acl| acl.matches(&flow)); 124 | 125 | if let Some(acl) = matches { 126 | if !acl.drop { 127 | FLOW_CACHE.write().unwrap().insert(flow); 128 | } 129 | true 130 | } else { 131 | false 132 | } 133 | } 134 | 135 | fn main() -> Result<()> { 136 | let configuration = load_config()?; 137 | println!("{}", configuration); 138 | let mut runtime = Runtime::init(&configuration)?; 139 | runtime.add_pipeline_to_run(install); 140 | runtime.execute() 141 | } 142 | --------------------------------------------------------------------------------