From c26c06d62c816a043f58080788fddd6678e425bf Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Thu, 27 Jul 2023 13:57:25 -0400 Subject: [PATCH] Setting up buildsystem to allow for BPF builds --- .cargo/cross-images/.gitignore | 1 + .../bpfex-unknown-none.dockerfile | 11 + .vscode/settings.json | 2 +- .vscode/tasks.json | 32 ++- Cargo.toml | 111 ++++----- Cross.toml | 5 + Makefile | 51 +++- protomask-ebpf/Cargo.toml | 5 + protomask-ebpf/src/main.rs | 5 + protomask-ebpf/src/panic.rs | 6 + protomask-tun/Cargo.toml | 23 -- protomask-tun/README.md | 3 - protomask-tun/src/error.rs | 10 - protomask-tun/src/lib.rs | 5 - protomask-tun/src/tun.rs | 226 ------------------ protomask/Cargo.toml | 18 ++ protomask/src/main.rs | 5 + rust-toolchain.toml | 15 ++ 18 files changed, 193 insertions(+), 341 deletions(-) create mode 100644 .cargo/cross-images/.gitignore create mode 100644 .cargo/cross-images/bpfex-unknown-none.dockerfile create mode 100644 Cross.toml create mode 100644 protomask-ebpf/Cargo.toml create mode 100644 protomask-ebpf/src/main.rs create mode 100644 protomask-ebpf/src/panic.rs delete mode 100644 protomask-tun/Cargo.toml delete mode 100644 protomask-tun/README.md delete mode 100644 protomask-tun/src/error.rs delete mode 100644 protomask-tun/src/lib.rs delete mode 100644 protomask-tun/src/tun.rs create mode 100644 protomask/Cargo.toml create mode 100644 protomask/src/main.rs create mode 100644 rust-toolchain.toml diff --git a/.cargo/cross-images/.gitignore b/.cargo/cross-images/.gitignore new file mode 100644 index 0000000..196bdf2 --- /dev/null +++ b/.cargo/cross-images/.gitignore @@ -0,0 +1 @@ +/*.dockerfile.timestamp \ No newline at end of file diff --git a/.cargo/cross-images/bpfex-unknown-none.dockerfile b/.cargo/cross-images/bpfex-unknown-none.dockerfile new file mode 100644 index 0000000..de8b42d --- /dev/null +++ b/.cargo/cross-images/bpfex-unknown-none.dockerfile @@ -0,0 +1,11 @@ +# Cross image for building bpfel and bpfeb targets + +FROM rust:latest +ARG TOOLCHAIN_CHANNEL + +# Install the needed toolcahin components +RUN rustup install ${TOOLCHAIN_CHANNEL} +RUN rustup component add rust-src --toolchain ${TOOLCHAIN_CHANNEL} + +# Install the BFP linker +RUN cargo install bpf-linker diff --git a/.vscode/settings.json b/.vscode/settings.json index 17b3803..70d6904 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,5 @@ "pnet", "Protomask", "rtnetlink" - ] + ], } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2270824..0505982 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,24 +2,40 @@ "version": "2.0.0", "tasks": [ { - "type": "cargo", - "command": "build", + "type": "shell", + "command": "make", + "args": [ + "target/x86_64-unknown-linux-musl/debug/protomask" + ], "problemMatcher": [ "$rustc", "$rust-panic" ], "group": "build", - "label": "rust: cargo build" + "label": "rust: build [target: x86_64-unknown-linux-musl]" }, { - "type": "cargo", - "command": "test", + "type": "shell", + "command": "make", + "args": [ + "target/x86_64-unknown-linux-musl/release/protomask" + ], "problemMatcher": [ "$rustc", "$rust-panic" ], - "group": "test", - "label": "rust: cargo test" - } + "group": "build", + "label": "rust: build [target: x86_64-unknown-linux-musl] [profile: release]" + }, + // { + // "type": "cargo", + // "command": "test", + // "problemMatcher": [ + // "$rustc", + // "$rust-panic" + // ], + // "group": "test", + // "label": "rust: cargo test" + // } ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 37458a9..257c8a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,56 +1,57 @@ -[package] -name = "protomask" -version = "0.2.0" -authors = ["Evan Pratten "] -edition = "2021" -description = "A user space NAT64 implementation" -readme = "README.md" -homepage = "https://github.com/ewpratten/protomask" -documentation = "https://docs.rs/protomask" -repository = "https://github.com/ewpratten/protomask" -license = "GPL-3.0" -keywords = [] -categories = [] +# [package] +# name = "protomask" +# version = "0.2.0" +# authors = ["Evan Pratten "] +# edition = "2021" +# description = "A user space NAT64 implementation" +# readme = "README.md" +# homepage = "https://github.com/ewpratten/protomask" +# documentation = "https://docs.rs/protomask" +# repository = "https://github.com/ewpratten/protomask" +# license = "GPL-3.0" +# keywords = [] +# categories = [] +# # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +# [dependencies] +# protomask-tun = { path = "protomask-tun", version = "0.1.0" } +# tokio = { version = "1.29.1", features = [ +# "macros", +# "rt-multi-thread", +# # "process", +# "sync" +# ] } +# clap = { version = "4.3.11", features = ["derive"] } +# serde = { version = "1.0.171", features = ["derive"] } +# ipnet = { version = "2.8.0", features = ["serde"] } +# hyper = { version = "0.14.27", features = ["server", "http1", "tcp"] } +# owo-colors = { version = "3.5.0", features = ["supports-colors"] } +# toml = "0.7.6" +# log = "0.4.19" +# fern = "0.6.2" +# serde_path_to_error = "0.1.13" +# thiserror = "1.0.43" +# tun-tap = "0.1.3" +# bimap = "0.6.3" +# pnet_packet = "0.34.0" +# rtnetlink = "0.13.0" +# futures = "0.3.28" +# prometheus = "0.13.3" +# lazy_static = "1.4.0" +# [[bin]] +# name = "protomask" +# path = "src/cli/main.rs" +# [package.metadata.deb] +# section = "network" +# assets = [ +# ["target/release/protomask", "/usr/local/bin/protomask", "755"], +# ["./protomask.toml", "/etc/protomask.toml", "644"], +# ["README.md", "usr/share/doc/protomask/README.md", "644"] +# ] +# conf-files = ["/etc/protomask.toml"] +# depends = [] +# maintainer-scripts = "./debian/" +# systemd-units = { enable = false } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -protomask-tun = { path = "protomask-tun", version = "0.1.0" } -tokio = { version = "1.29.1", features = [ - "macros", - "rt-multi-thread", - # "process", - "sync" -] } -clap = { version = "4.3.11", features = ["derive"] } -serde = { version = "1.0.171", features = ["derive"] } -ipnet = { version = "2.8.0", features = ["serde"] } -hyper = { version = "0.14.27", features = ["server", "http1", "tcp"] } -owo-colors = { version = "3.5.0", features = ["supports-colors"] } -toml = "0.7.6" -log = "0.4.19" -fern = "0.6.2" -serde_path_to_error = "0.1.13" -thiserror = "1.0.43" -tun-tap = "0.1.3" -bimap = "0.6.3" -pnet_packet = "0.34.0" -rtnetlink = "0.13.0" -futures = "0.3.28" -prometheus = "0.13.3" -lazy_static = "1.4.0" - -[[bin]] -name = "protomask" -path = "src/cli/main.rs" - -[package.metadata.deb] -section = "network" -assets = [ - ["target/release/protomask", "/usr/local/bin/protomask", "755"], - ["./protomask.toml", "/etc/protomask.toml", "644"], - ["README.md", "usr/share/doc/protomask/README.md", "644"] -] -conf-files = ["/etc/protomask.toml"] -depends = [] -maintainer-scripts = "./debian/" -systemd-units = { enable = false } \ No newline at end of file +[workspace] +resolver = "2" +members = ["protomask", "protomask-ebpf"] diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..2f76165 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,5 @@ +[target.bpfel-unknown-none] +image = "protomask/bpfex-unknown-none" + +[target.bpfeb-unknown-none] +image = "protomask/bpfex-unknown-none" \ No newline at end of file diff --git a/Makefile b/Makefile index f2b9397..77a0c20 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,48 @@ # All sources used to build the protomask binary -SRC = Cargo.toml $(shell find src/ -type f -name '*.rs') $(shell find protomask-tun/src/ -type f -name '*.rs') +PROTOMASK_SRC = protomask/Cargo.toml $(shell find protomask/src/ -type f -name '*.rs') +PROTOMASK_EBPF_SRC = protomask-ebpf/Cargo.toml $(shell find protomask-ebpf/src/ -type f -name '*.rs') # Used to auto-version things -CRATE_VERSION = $(shell sed -n -r "s/^version = \"([0-9\.]+)\"/\1/p" Cargo.toml) +CRATE_VERSION = $(shell sed -n -r "s/^version = \"([0-9\.]+)\"/\1/p" protomask/Cargo.toml) +TOOLCHAIN_CHANNEL = $(shell sed -n -r "s/^channel = \"(.+)\"/\1/p" rust-toolchain.toml) -target/x86_64-unknown-linux-musl/release/protomask: $(SRC) - cross build --target x86_64-unknown-linux-musl --release +.PHONY: clean -target/aarch64-unknown-linux-musl/release/protomask: $(SRC) - cross build --target aarch64-unknown-linux-musl --release +# x64 Protomask binary (Debug) +target/x86_64-unknown-linux-musl/debug/protomask: $(PROTOMASK_SRC) target/bpfel-unknown-none/debug/protomask-ebpf + cross build --target x86_64-unknown-linux-musl --bin protomask -target/x86_64-unknown-linux-musl/debian/protomask_${CRATE_VERSION}_amd64.deb: target/x86_64-unknown-linux-musl/release/protomask - cargo deb --target x86_64-unknown-linux-musl --no-build +# x64 Protomask binary (Release) +target/x86_64-unknown-linux-musl/release/protomask: $(PROTOMASK_SRC) target/bpfel-unknown-none/release/protomask-ebpf + cross build --target x86_64-unknown-linux-musl --bin protomask --release -target/aarch64-unknown-linux-musl/debian/protomask_${CRATE_VERSION}_arm64.deb: target/aarch64-unknown-linux-musl/release/protomask - cargo deb --target aarch64-unknown-linux-musl --no-build +# Docker image used for building bpfel and bpfeb images +.cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp: .cargo/cross-images/bpfex-unknown-none.dockerfile + docker build -t protomask/bpfex-unknown-none -f $< --build-arg TOOLCHAIN_CHANNEL=$(TOOLCHAIN_CHANNEL) .cargo/cross-images + touch .cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp + +# Little-Endian BPF (Debug) +target/bpfel-unknown-none/debug/protomask-ebpf: $(PROTOMASK_EBPF_SRC) .cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp + cross build -Z build-std=core --target bpfel-unknown-none --bin protomask-ebpf + +# Little-Endian BPF (Release) +target/bpfel-unknown-none/release/protomask-ebpf: $(PROTOMASK_EBPF_SRC) .cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp + cargo build -Z build-std=core --target bpfel-unknown-none --bin protomask-ebpf --release + +# Big-Endian BPF (Debug) +target/bpfeb-unknown-none/debug/protomask-ebpf: $(PROTOMASK_EBPF_SRC) .cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp + cross build -Z build-std=core --target bpfeb-unknown-none --bin protomask-ebpf + +# Big-Endian BPF (Release) +target/bpfeb-unknown-none/release/protomask-ebpf: $(PROTOMASK_EBPF_SRC) .cargo/cross-images/bpfex-unknown-none.dockerfile.timestamp + cargo build -Z build-std=core --target bpfeb-unknown-none --bin protomask-ebpf --release + +# Cleanup task +clean: + cargo clean + +# target/x86_64-unknown-linux-musl/debian/protomask_${CRATE_VERSION}_amd64.deb: target/x86_64-unknown-linux-musl/release/protomask +# cargo deb --target x86_64-unknown-linux-musl --no-build + +# target/aarch64-unknown-linux-musl/debian/protomask_${CRATE_VERSION}_arm64.deb: target/aarch64-unknown-linux-musl/release/protomask +# cargo deb --target aarch64-unknown-linux-musl --no-build diff --git a/protomask-ebpf/Cargo.toml b/protomask-ebpf/Cargo.toml new file mode 100644 index 0000000..926112c --- /dev/null +++ b/protomask-ebpf/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "protomask-ebpf" +publish = false +version = "0.0.0" +edition = "2021" diff --git a/protomask-ebpf/src/main.rs b/protomask-ebpf/src/main.rs new file mode 100644 index 0000000..b46c75d --- /dev/null +++ b/protomask-ebpf/src/main.rs @@ -0,0 +1,5 @@ +#![no_std] +#![no_main] + +mod panic; + diff --git a/protomask-ebpf/src/panic.rs b/protomask-ebpf/src/panic.rs new file mode 100644 index 0000000..2412fa9 --- /dev/null +++ b/protomask-ebpf/src/panic.rs @@ -0,0 +1,6 @@ +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + unsafe { core::hint::unreachable_unchecked() } +} \ No newline at end of file diff --git a/protomask-tun/Cargo.toml b/protomask-tun/Cargo.toml deleted file mode 100644 index a2a24aa..0000000 --- a/protomask-tun/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "protomask-tun" -version = "0.1.0" -authors = ["Evan Pratten "] -edition = "2021" -description = "An async interface to Linux TUN devices" -readme = "README.md" -homepage = "https://github.com/ewpratten/protomask/protomask-tun" -documentation = "https://docs.rs/protomask-tun" -repository = "https://github.com/ewpratten/protomask" -license = "GPL-3.0" -keywords = [] -categories = [] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -tokio = { version = "1.29.1", features = ["sync", "rt"] } -log = "0.4.19" -thiserror = "1.0.43" -tun-tap = "0.1.3" -rtnetlink = "0.13.0" -futures = "0.3.28" -ipnet = "^2.8.0" diff --git a/protomask-tun/README.md b/protomask-tun/README.md deleted file mode 100644 index 2f77949..0000000 --- a/protomask-tun/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# protomask-tun - -An async interface to Linux TUN devices. Support library for `protomask`. diff --git a/protomask-tun/src/error.rs b/protomask-tun/src/error.rs deleted file mode 100644 index ed62a8e..0000000 --- a/protomask-tun/src/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - IoError(#[from] std::io::Error), - - #[error(transparent)] - NetlinkError(#[from] rtnetlink::Error), -} - -pub type Result = std::result::Result; \ No newline at end of file diff --git a/protomask-tun/src/lib.rs b/protomask-tun/src/lib.rs deleted file mode 100644 index b75b6df..0000000 --- a/protomask-tun/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod error; -mod tun; - -pub use error::{Error, Result}; -pub use tun::TunDevice; diff --git a/protomask-tun/src/tun.rs b/protomask-tun/src/tun.rs deleted file mode 100644 index 79511b5..0000000 --- a/protomask-tun/src/tun.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::{ - io::{Read, Write}, - net::IpAddr, - os::fd::{AsRawFd, FromRawFd}, -}; - -use futures::TryStreamExt; -use ipnet::IpNet; -use tokio::{ - sync::{broadcast, mpsc}, - task, -}; -use tun_tap::Mode; - -use crate::Result; - -#[derive(Debug)] -pub struct TunDevice { - device: tun_tap::Iface, - rt_handle: rtnetlink::Handle, - link_index: u32, - mtu: usize, -} - -impl TunDevice { - /// Create and bring up a new TUN device - /// - /// ## Name format - /// - /// The name field can be any string. If `%d` is present in the string, - /// it will be replaced with a unique number. - pub async fn new(name: &str) -> Result { - // Bring up an rtnetlink connection - let (rt_connection, rt_handle, _) = rtnetlink::new_connection().map_err(|err| { - log::error!("Failed to open rtnetlink connection"); - log::error!("{}", err); - err - })?; - tokio::spawn(rt_connection); - - // Create the TUN device - let tun_device = tun_tap::Iface::without_packet_info(name, Mode::Tun)?; - log::debug!("Created new TUN device: {}", tun_device.name()); - - // Get access to the link through rtnetlink - // NOTE: I don't think there is any way this can fail, so `except` is probably OK - let tun_link = rt_handle - .link() - .get() - .match_name(tun_device.name().to_owned()) - .execute() - .try_next() - .await? - .expect("Failed to access newly created TUN device"); - - // Bring the link up - rt_handle - .link() - .set(tun_link.header.index) - .up() - .execute() - .await - .map_err(|err| { - log::error!("Failed to bring up link"); - log::error!("{}", err); - err - })?; - log::debug!("Brought {} up", tun_device.name()); - - // Read the link MTU - let mtu: usize = - std::fs::read_to_string(format!("/sys/class/net/{}/mtu", tun_device.name())) - .expect("Failed to read link MTU") - .strip_suffix("\n") - .unwrap() - .parse() - .unwrap(); - - Ok(Self { - device: tun_device, - rt_handle, - link_index: tun_link.header.index, - mtu, - }) - } - - /// Add an IP address to this device - pub async fn add_address(&mut self, ip_address: IpAddr, prefix_len: u8) -> Result<()> { - self.rt_handle - .address() - .add(self.link_index, ip_address, prefix_len) - .execute() - .await - .map_err(|err| { - log::error!("Failed to add address {} to link", ip_address); - log::error!("{}", err); - err - })?; - - Ok(()) - } - - /// Remove an IP address from this device - pub async fn remove_address(&mut self, ip_address: IpAddr, prefix_len: u8) -> Result<()> { - // Find the address message that matches the given address - if let Some(address_message) = self - .rt_handle - .address() - .get() - .set_link_index_filter(self.link_index) - .set_address_filter(ip_address) - .set_prefix_length_filter(prefix_len) - .execute() - .try_next() - .await - .map_err(|err| { - log::error!("Failed to find address {} on link", ip_address); - log::error!("{}", err); - err - })? - { - // Delete the address - self.rt_handle - .address() - .del(address_message) - .execute() - .await - .map_err(|err| { - log::error!("Failed to remove address {} from link", ip_address); - log::error!("{}", err); - err - })?; - } - - Ok(()) - } - - /// Add a route to this device - pub async fn add_route(&mut self, destination: IpNet) -> Result<()> { - match destination { - IpNet::V4(destination) => { - self.rt_handle - .route() - .add() - .v4() - .output_interface(self.link_index) - .destination_prefix(destination.addr(), destination.prefix_len()) - .execute() - .await - .map_err(|err| { - log::error!("Failed to add route {} to link", destination); - log::error!("{}", err); - err - })?; - } - IpNet::V6(destination) => { - self.rt_handle - .route() - .add() - .v6() - .output_interface(self.link_index) - .destination_prefix(destination.addr(), destination.prefix_len()) - .execute() - .await - .map_err(|err| { - log::error!("Failed to add route {} to link", destination); - log::error!("{}", err); - err - })?; - } - } - - Ok(()) - } - - /// Spawns worker threads, and returns a tx/rx pair for the caller to interact with them - pub async fn spawn_worker(&self) -> (mpsc::Sender>, broadcast::Receiver>) { - // Create a channel for packets to be sent to the caller - let (tx_to_caller, rx_from_worker) = broadcast::channel(65535); - - // Create a channel for packets being received from the caller - let (tx_to_worker, mut rx_from_caller) = mpsc::channel(65535); - - // Clone some values for use in worker threads - let mtu = self.mtu; - let device_fd = self.device.as_raw_fd(); - - // Create a task that broadcasts all incoming packets - let _rx_task = task::spawn_blocking(move || { - // Build a buffer to read packets into - let mut buffer = vec![0u8; mtu]; - - // Create a file to access the TUN device - let mut device = unsafe { std::fs::File::from_raw_fd(device_fd) }; - - loop { - // Read a packet from the TUN device - let packet_len = device.read(&mut buffer[..]).unwrap(); - let packet = buffer[..packet_len].to_vec(); - - // Broadcast the packet to all listeners - tx_to_caller.send(packet).unwrap(); - } - }); - - // Create a task that sends all outgoing packets - let _tx_task = task::spawn(async move { - // Create a file to access the TUN device - let mut device = unsafe { std::fs::File::from_raw_fd(device_fd) }; - - loop { - // Wait for a packet to be sent - let packet: Vec = rx_from_caller.recv().await.unwrap(); - - // Write the packet to the TUN device - device.write_all(&packet[..]).unwrap(); - } - }); - - // Create a task that sends all outgoing packets - let _tx_task = task::spawn_blocking(|| {}); - - // Return an rx/tx pair for the caller to interact with the workers - (tx_to_worker, rx_from_worker) - } -} diff --git a/protomask/Cargo.toml b/protomask/Cargo.toml new file mode 100644 index 0000000..e6161a9 --- /dev/null +++ b/protomask/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "protomask" +version = "0.2.0" +authors = ["Evan Pratten "] +edition = "2021" +description = "A user space NAT64 implementation" +readme = "README.md" +homepage = "https://github.com/ewpratten/protomask" +documentation = "https://docs.rs/protomask" +repository = "https://github.com/ewpratten/protomask" +license = "GPL-3.0" +keywords = [] +categories = [] + +[dependencies] +tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "sync"] } +clap = { version = "4.3.11", features = ["derive"] } + diff --git a/protomask/src/main.rs b/protomask/src/main.rs new file mode 100644 index 0000000..9d38cbe --- /dev/null +++ b/protomask/src/main.rs @@ -0,0 +1,5 @@ + +#[tokio::main] +pub async fn main(){ + +} \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5c73935 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,15 @@ +[toolchain] +channel = "nightly-2023-07-26" +# components = [ +# "cargo", +# "clippy", +# "rust-docs", +# "rust-src", +# "rust-std", +# "rustc", +# "rustfmt", +# ] +# targets = [ +# "bpfel-unknown-none", +# "bpfeb-unknown-none", +# ]