├── src ├── ebpf │ ├── ghostwire-ebpf │ │ ├── src │ │ │ ├── utils │ │ │ │ ├── mod.rs │ │ │ │ └── ptr_at.rs │ │ │ ├── handlers │ │ │ │ ├── mod.rs │ │ │ │ ├── egress.rs │ │ │ │ └── ingress.rs │ │ │ └── main.rs │ │ ├── .helix │ │ │ └── config.toml │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── .vim │ │ │ └── coc-settings.json │ │ ├── .vscode │ │ │ └── settings.json │ │ ├── rust-toolchain.toml │ │ ├── Cargo.toml │ │ └── Cargo.lock │ ├── Cargo.toml │ ├── ghostwire │ │ ├── src │ │ │ ├── utils │ │ │ │ ├── mod.rs │ │ │ │ ├── map_management.rs │ │ │ │ ├── bootloader.rs │ │ │ │ ├── state.rs │ │ │ │ ├── ebpf.rs │ │ │ │ ├── prometheus.rs │ │ │ │ └── socket.rs │ │ │ └── main.rs │ │ └── Cargo.toml │ ├── xtask │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── main.rs │ │ │ ├── clippy.rs │ │ │ ├── build.rs │ │ │ ├── run.rs │ │ │ └── build_ebpf.rs │ ├── ghostwire-common │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── Cargo.lock ├── cli │ ├── src │ │ ├── utils │ │ │ ├── mod.rs │ │ │ ├── console.rs │ │ │ ├── socket.rs │ │ │ ├── handler.rs │ │ │ └── yaml.rs │ │ └── main.rs │ ├── Cargo.toml │ └── Cargo.lock └── types │ ├── Cargo.toml │ ├── Cargo.lock │ └── src │ └── lib.rs ├── rustfmt.toml ├── .gitignore ├── LICENSE ├── .github └── workflows │ ├── cli.yml │ └── ebpf.yml ├── doc.config.yml ├── scripts └── install.sh └── README.md /src/ebpf/ghostwire-ebpf/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ptr_at; 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_layout = "Vertical" 2 | imports_granularity = "Crate" 3 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/.helix/config.toml: -------------------------------------------------------------------------------- 1 | [editor] 2 | workspace-lsp-roots = [] 3 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/src/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod egress; 2 | pub mod ingress; 3 | -------------------------------------------------------------------------------- /src/cli/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod console; 2 | pub mod handler; 3 | pub mod socket; 4 | pub mod yaml; 5 | -------------------------------------------------------------------------------- /src/ebpf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["xtask", "ghostwire", "ghostwire-common"] 4 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ebpf; 2 | pub mod map_management; 3 | pub mod prometheus; 4 | pub mod socket; 5 | pub mod state; 6 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../target" 3 | target = "bpfel-unknown-none" 4 | 5 | [unstable] 6 | build-std = ["core"] 7 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "bpfel-unknown-none", 3 | "rust-analyzer.checkOnSave.allTargets": false 4 | } 5 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "bpfel-unknown-none", 3 | "rust-analyzer.checkOnSave.allTargets": false 4 | } 5 | -------------------------------------------------------------------------------- /src/ebpf/xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | clap = { version = "4.1", features = ["derive"] } 9 | -------------------------------------------------------------------------------- /src/types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ghostwire_types" 3 | version = "0.2.1" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = {version = "1.0.210", features = ["derive"]} 8 | serde_json = "1.0.64" 9 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ghostwire-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | default = [] 8 | user = ["aya"] 9 | 10 | [dependencies] 11 | aya = { version = "0.12", optional = true } 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | -------------------------------------------------------------------------------- /src/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.89" 8 | clap = "4.5.18" 9 | serde_json = "1.0.128" 10 | ghostwire_types = { path = "../types" } 11 | colored = "2.1.0" 12 | serde_yaml = "0.9.34" 13 | serde = {version="1.0.210", features=["derive"]} 14 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | # The source code of rustc, provided by the rust-src component, is needed for 4 | # building eBPF programs. 5 | components = [ 6 | "cargo", 7 | "clippy", 8 | "rust-docs", 9 | "rust-src", 10 | "rust-std", 11 | "rustc", 12 | "rustfmt", 13 | ] 14 | -------------------------------------------------------------------------------- /src/cli/src/utils/console.rs: -------------------------------------------------------------------------------- 1 | use colored::*; 2 | 3 | /// Print a success message to the console. 4 | pub fn print_success(message: &str) { 5 | println!( 6 | "{} {} {}", 7 | ">>>".bold().blue(), 8 | message.bold().green(), 9 | "<<<".bold().blue() 10 | ); 11 | } 12 | 13 | /// Print a success message to the console. 14 | pub fn print_error(message: &str) { 15 | println!( 16 | "{} {} {}", 17 | ">>>".bold().blue(), 18 | message.bold().red(), 19 | "<<<".bold().blue() 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ghostwire-ebpf" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | aya-ebpf = "0.1.0" 8 | aya-log-ebpf = "0.1.0" 9 | ghostwire-common = { path = "../ghostwire-common" } 10 | network-types = "0.0.7" 11 | 12 | [[bin]] 13 | name = "ghostwire" 14 | path = "src/main.rs" 15 | 16 | [profile.dev] 17 | opt-level = 3 18 | debug = false 19 | debug-assertions = false 20 | overflow-checks = false 21 | lto = true 22 | panic = "abort" 23 | incremental = false 24 | codegen-units = 1 25 | rpath = false 26 | 27 | [profile.release] 28 | lto = true 29 | panic = "abort" 30 | codegen-units = 1 31 | 32 | [workspace] 33 | members = [] 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | #Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ 22 | -------------------------------------------------------------------------------- /src/ebpf/xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | mod build; 2 | mod build_ebpf; 3 | mod clippy; 4 | mod run; 5 | 6 | use std::process::exit; 7 | 8 | use clap::Parser; 9 | 10 | #[derive(Debug, Parser)] 11 | pub struct Options { 12 | #[clap(subcommand)] 13 | command: Command, 14 | } 15 | 16 | #[derive(Debug, Parser)] 17 | enum Command { 18 | BuildEbpf(build_ebpf::Options), 19 | Build(build::Options), 20 | Run(run::Options), 21 | Clippy(clippy::Options), 22 | } 23 | 24 | fn main() { 25 | let opts = Options::parse(); 26 | 27 | use Command::*; 28 | let ret = match opts.command { 29 | BuildEbpf(opts) => build_ebpf::build_ebpf(opts), 30 | Run(opts) => run::run(opts), 31 | Build(opts) => build::build(opts), 32 | Clippy(opts) => clippy::run_clippy(opts), 33 | }; 34 | 35 | if let Err(e) = ret { 36 | eprintln!("{e:#}"); 37 | exit(1); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{ 2 | Arg, 3 | Command, 4 | }; 5 | use utils::{ 6 | console::print_error, 7 | handler::handle_arguments, 8 | }; 9 | 10 | mod utils; 11 | 12 | /// Core CLI handler 13 | fn main() { 14 | let matches = Command::new("ghostwire") 15 | .name("ghostwire") 16 | .version("0.1") 17 | .author("Whole Lotta Heart, Corp.") 18 | .about("Ghostwire is a stateful XDP firewall") 19 | .subcommands([ 20 | Command::new("status").about("Gets the current status of the firewall"), 21 | Command::new("disable").about("Disable the firewall"), 22 | Command::new("load") 23 | .about("Load the firewall rules from a configuration file") 24 | .args([Arg::new("file").required(true)]), 25 | ]) 26 | .arg_required_else_help(true) 27 | .get_matches(); 28 | 29 | if let Err(e) = handle_arguments(matches) { 30 | print_error(&e.to_string()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ghostwire" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | aya = "0.12" 9 | aya-log = "=0.2.0" 10 | clap = { version = "4.1", features = ["derive"] } 11 | ghostwire-common = { path = "../ghostwire-common", features = ["user"] } 12 | anyhow = "1" 13 | env_logger = "0.10" 14 | libc = "0.2" 15 | log = "0.4" 16 | tokio = { version = "1.25", features = [ 17 | "macros", 18 | "rt", 19 | "rt-multi-thread", 20 | "net", 21 | "signal", 22 | "sync", 23 | ] } 24 | tracing = "0.1.40" 25 | serde_json = "1.0.128" 26 | ghostwire_types = { path = "../../types" } 27 | serde = { version = "1.0.210", features = ["derive"] } 28 | prometheus = "0.13.4" 29 | tokio_schedule = "0.3.2" 30 | http-body-util = { version = "0.1" } 31 | hyper = { version = "1", features = ["http1"] } 32 | hyper-util = { version = "0.1.5", features = ["full"] } 33 | bytes = "1.6.0" 34 | yaml-rust = "0.4.5" 35 | tracing-subscriber = "0.3.18" 36 | lazy_static = "1.5.0" 37 | 38 | [[bin]] 39 | name = "ghostwire" 40 | path = "src/main.rs" 41 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/map_management.rs: -------------------------------------------------------------------------------- 1 | use crate::OVERALL_STATE; 2 | 3 | /// Function to manage eBPF maps in the background, such as the ratelimiter. Designed to be run in a task. 4 | pub async fn manage_maps() { 5 | let overall_state = OVERALL_STATE.read().await; 6 | 7 | // Read the state and determine if an eBPF program is loaded. 8 | if let Some(state) = &overall_state.state { 9 | // Acquire the write lock to the ratelimit map and purge it. 10 | let mut rule_map = state.rule_ratelimit_map.write().await; 11 | 12 | let keys = rule_map.keys().collect::>(); 13 | 14 | for key in keys { 15 | match key { 16 | Ok(t) => { 17 | if let Err(e) = rule_map.remove(&t) { 18 | tracing::error!("Failed to remove key from ratelimit map: {}", e); 19 | }; 20 | } 21 | Err(e) => { 22 | tracing::error!("Failed to iterate over keys in ratelimit map: {}", e); 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Packetware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/bootloader.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * A TODO is to load the previous state of the firewall on startup, probably over a oneshot. This 3 | * file is not included as a module yet for that reason. 4 | * 5 | * (random code proceeds) 6 | * 7 | * 8 | let (tx, rx) = oneshot::channel::<(Vec, String)>(); 9 | 10 | { 11 | let mut overall_state = OVERALL_STATE.write().await; 12 | 13 | overall_state.oneshot_send = Some(tx) 14 | } 15 | // block until we get the initial rules or a startup message 16 | let (initial_rules, interface) = rx.await?; 17 | 18 | // ok, we're ready to start 19 | if initial_rules.is_empty() { 20 | tracing::warn!( 21 | "Starting firewall with no rules - all inbound new connections will be dropped!" 22 | ) 23 | } 24 | 25 | // load the eBPF into the kernel 26 | let state = Arc::new(load_ebpf(counters.clone(), initial_rules, interface)?);* // update the CLI with the new state 27 | { 28 | let state = Arc::clone(&state); 29 | let mut overall_state = OVERALL_STATE.write().await; 30 | 31 | overall_state.enabled = true; 32 | overall_state.state = Some(state); 33 | } 34 | */ 35 | -------------------------------------------------------------------------------- /.github/workflows/cli.yml: -------------------------------------------------------------------------------- 1 | # Inspired by aya-template 2 | # @see https://github.com/aya-rs/aya-template/blob/main/.github/workflows/ci.yml 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | schedule: 14 | - cron: 00 4 * * * 15 | 16 | env: 17 | CARGO_TERM_COLOR: always 18 | 19 | jobs: 20 | cli: 21 | runs-on: ubuntu-22.04 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: switch to nightly 27 | run: | 28 | rustup default nightly 29 | rustup component add rustfmt --toolchain nightly 30 | rustup component add clippy --toolchain nightly 31 | rustup component add rust-src --toolchain nightly 32 | 33 | - name: rustfmt 34 | run: | 35 | rustfmt --config-path rustfmt.toml --edition 2021 src/cli/src/main.rs 36 | rustfmt --config-path rustfmt.toml --edition 2021 src/cli/src/utils/*.rs 37 | git diff --exit-code 38 | 39 | - name: cargo build 40 | working-directory: src/cli 41 | run: cargo build 42 | 43 | - name: cargo clippy 44 | working-directory: src/cli 45 | run: cargo clippy 46 | -------------------------------------------------------------------------------- /src/ebpf/xtask/src/clippy.rs: -------------------------------------------------------------------------------- 1 | // custom xtask command to run clippy 2 | 3 | use clap::Parser; 4 | use std::process::Command; 5 | 6 | use crate::build_ebpf::Architecture; 7 | 8 | #[derive(Debug, Parser)] 9 | pub struct Options { 10 | /// Set the endianness of the BPF target 11 | #[clap(default_value = "bpfel-unknown-none", long)] 12 | pub bpf_target: Architecture, 13 | /// Clippy will fix the issues 14 | #[clap(long)] 15 | pub fix: bool, 16 | /// Clippy will ignore if the directory has uncommitted changes 17 | #[clap(long)] 18 | pub allow_dirty: bool, 19 | /// Clippy will fix staged files 20 | #[clap(long)] 21 | pub allow_staged: bool, 22 | } 23 | 24 | /// Run clippy on the program 25 | pub fn run_clippy(opts: Options) -> Result<(), anyhow::Error> { 26 | let mut args = vec!["clippy"]; 27 | if opts.fix { 28 | args.push("--fix") 29 | } 30 | if opts.allow_dirty { 31 | args.push("--allow-dirty") 32 | } 33 | if opts.allow_staged { 34 | args.push("--allow-staged") 35 | } 36 | let status = Command::new("cargo") 37 | .current_dir("ghostwire-ebpf") 38 | .args(&args) 39 | .status() 40 | .expect("failed to build userspace"); 41 | assert!(status.success()); 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /src/cli/src/utils/socket.rs: -------------------------------------------------------------------------------- 1 | use ghostwire_types::{ 2 | ClientMessage, 3 | ServerMessage, 4 | }; 5 | use std::{ 6 | io::{ 7 | Read, 8 | Write, 9 | }, 10 | os::unix::net::UnixStream, 11 | }; 12 | 13 | /// Send a message to the firewall, erroring if unsuccessful 14 | pub fn send_message(client_message: ClientMessage) -> anyhow::Result { 15 | // Connect to the socket. 16 | let mut stream = UnixStream::connect("/tmp/ghostwire.sock") 17 | .map_err(|_| anyhow::anyhow!("couldn't connect to the ghostwire server, is it online?"))?; 18 | 19 | // Serialize the client message. 20 | let serialized = serde_json::to_string(&client_message)?; 21 | 22 | // Send the message over the wire. 23 | stream.write_all(serialized.as_bytes())?; 24 | 25 | let mut buffer = [0; 1024]; 26 | 27 | // Read the response. 28 | let bytes_read = stream.read(&mut buffer)?; 29 | 30 | let response = std::str::from_utf8(&buffer[..bytes_read])?; 31 | 32 | // Deserialize the response. 33 | let server_response: ServerMessage = serde_json::from_str(response)?; 34 | 35 | if server_response.request_success { 36 | Ok(server_response.message) 37 | } else { 38 | anyhow::bail!( 39 | "The server responded with an error: {}", 40 | server_response.message 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ebpf/xtask/src/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use anyhow::Context as _; 4 | use clap::Parser; 5 | 6 | use crate::build_ebpf::{ 7 | build_ebpf, 8 | Architecture, 9 | Options as BuildOptions, 10 | }; 11 | 12 | #[derive(Debug, Parser)] 13 | pub struct Options { 14 | /// Set the endianness of the BPF target 15 | #[clap(default_value = "bpfel-unknown-none", long)] 16 | pub bpf_target: Architecture, 17 | /// Build and run the release target 18 | #[clap(long)] 19 | pub release: bool, 20 | } 21 | 22 | /// Build the project 23 | fn build_project(opts: &Options) -> Result<(), anyhow::Error> { 24 | let mut args = vec!["build"]; 25 | if opts.release { 26 | args.push("--release") 27 | } 28 | let status = Command::new("cargo") 29 | .args(&args) 30 | .status() 31 | .expect("failed to build userspace"); 32 | assert!(status.success()); 33 | Ok(()) 34 | } 35 | 36 | /// Build our ebpf program and the project 37 | pub fn build(opts: Options) -> Result<(), anyhow::Error> { 38 | // build our ebpf program followed by our application 39 | build_ebpf(BuildOptions { 40 | target: opts.bpf_target, 41 | release: opts.release, 42 | }) 43 | .context("Error while building eBPF program")?; 44 | build_project(&opts).context("Error while building userspace application")?; 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /doc.config.yml: -------------------------------------------------------------------------------- 1 | # API reference for the YAML fields 2 | 3 | # The interface to run the XDP on 4 | interface: "eth0" 5 | # Whether the firewall is persistent across reboots. 6 | # Not currently implemented. 7 | # persistent: true 8 | 9 | # Whether the Prometheus exporter is enabled. 10 | # Not currently implemented. Prometheus always listens on :4343. 11 | # prometheus: true 12 | # The local port Prometheus should listen on. 13 | # prometheus_port: 4242 14 | 15 | # The firewall rules you'd like to define. 16 | # The firewall drops traffic like TCP and UDP by default, rules whitelist traffic 17 | rules: 18 | # Define each rule individually 19 | - rule: 20 | # The source IP range this rule will apply to. For example, 23.133.104.69/32, or 23.133.104.0/24. 21 | # To allow traffic from any IP, use 0.0.0.0/0 22 | source_ip_range: 0.0.0.0/0 23 | # The destination IP range this rule will apply to. 24 | # To allow traffic to go to any IP assigned with this server, use 0.0.0.0/0. 25 | destination_ip_range: 0.0.0.0/0 26 | # The IP protocol to allow. 27 | # Current allowed values are: TCP, UDP, ICMP, ALL. 28 | protocol: "TCP" 29 | # The port to allow the traffic to. Only applicable to TCP and UDP. 30 | # Omit or enter 0 to allow any port. 31 | port: 22 32 | # Limit the amount of packets sent to this service per source IP. Runs over 1 minute. 33 | # Enter to zero to disable ratelimiting. 34 | ratelimit: 0 35 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/src/utils/ptr_at.rs: -------------------------------------------------------------------------------- 1 | use aya_ebpf::programs::{ 2 | TcContext, 3 | XdpContext, 4 | }; 5 | 6 | /// Get the pointer from an index to the end the desired type length for an XDP packet. This will help us break up the packet into the different 7 | /// headers and payloads. 8 | pub fn xdp_ptr_at_fallible(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> { 9 | // get the reference at the beginning of the packet 10 | let start = ctx.data(); 11 | // get the length of the packet 12 | let end = ctx.data_end(); 13 | // the length of the desired type 14 | let length = core::mem::size_of::(); 15 | 16 | // make sure we don't exceed the bounds of the packet 17 | if start + offset + length > end { 18 | return Err(()); 19 | } 20 | 21 | // return the reference to the data, parsing the bytes as the desired type 22 | Ok((start + offset) as *const T) 23 | } 24 | 25 | /// Get the pointer from an index to the end the desired type length for a traffic control packet. This will help us break up the packet into the different 26 | /// headers and payloads. 27 | pub fn tc_ptr_at_fallible(ctx: &TcContext, offset: usize) -> Result<*const T, ()> { 28 | // get the reference at the beginning of the packet 29 | let start = ctx.data(); 30 | // get the length of the packet 31 | let end = ctx.data_end(); 32 | // the length of the desired type 33 | let length = core::mem::size_of::(); 34 | 35 | // make sure we don't exceed the bounds of the packet 36 | if start + offset + length > end { 37 | return Err(()); 38 | } 39 | 40 | // return the reference to the data, parsing the bytes as the desired type 41 | Ok((start + offset) as *const T) 42 | } 43 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | /// A firewall rule in C format 6 | pub struct Rule { 7 | /// The ID of this rule with what the API identifies it as. This will also be the key of the 8 | /// ratelimiting map if ratelimiting is enabled for this rule. 9 | pub id: u32, 10 | /// The start source IP address in big endian 11 | pub source_start_ip: u32, 12 | /// The end source IP address in big endian 13 | pub source_end_ip: u32, 14 | /// The start destination IP address in big endian. If this rule applies everywhere, all bytes 15 | /// will show 0 16 | pub destination_start_ip: u32, 17 | /// The end destination IP address in big endian. If this rule applies everywhere, all bytes 18 | /// will show 0 19 | pub destination_end_ip: u32, 20 | /// Protocol number (currently limited to either 1, 6, 17 for ICMP, TCP, and UDP respectively. 21 | /// if this rule applies to all protocols, this will be zero) 22 | pub protocol_number: u8, 23 | /// The port if TCP or UDP (if not, 0) 24 | pub port_number: u16, 25 | /// If the rule is a ratelimiting one, represent the amount of traffic allowed per IP over 10 26 | /// seconds. If there's no ratelimiting rule, this is 0. 27 | pub ratelimiting: u32, 28 | } 29 | 30 | #[repr(C)] 31 | #[derive(Debug, Clone, Copy)] 32 | /// Analytics for each rule 33 | pub struct RuleAnalytics { 34 | /// The ID of the rule this is referencing 35 | pub rule_id: u32, 36 | /// Number of times this rule was evaluated 37 | pub evaluated: u128, 38 | /// Number of times traffic passed this rule 39 | pub passed: u128, 40 | } 41 | 42 | #[cfg(feature = "user")] 43 | unsafe impl aya::Pod for Rule {} 44 | #[cfg(feature = "user")] 45 | unsafe impl aya::Pod for RuleAnalytics {} 46 | -------------------------------------------------------------------------------- /src/ebpf/xtask/src/run.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use anyhow::Context as _; 4 | use clap::Parser; 5 | 6 | use crate::{ 7 | build::{ 8 | build, 9 | Options as BuildOptions, 10 | }, 11 | build_ebpf::Architecture, 12 | }; 13 | 14 | #[derive(Debug, Parser)] 15 | pub struct Options { 16 | /// Set the endianness of the BPF target 17 | #[clap(default_value = "bpfel-unknown-none", long)] 18 | pub bpf_target: Architecture, 19 | /// Build and run the release target 20 | #[clap(long)] 21 | pub release: bool, 22 | /// The command used to wrap your application 23 | #[clap(short, long, default_value = "sudo -E")] 24 | pub runner: String, 25 | /// Arguments to pass to your application 26 | #[clap(name = "args", last = true)] 27 | pub run_args: Vec, 28 | } 29 | 30 | /// Build and run the project 31 | pub fn run(opts: Options) -> Result<(), anyhow::Error> { 32 | // Build our ebpf program and the project 33 | build(BuildOptions { 34 | bpf_target: opts.bpf_target, 35 | release: opts.release, 36 | }) 37 | .context("Error while building project")?; 38 | 39 | // profile we are building (release or debug) 40 | let profile = if opts.release { "release" } else { "debug" }; 41 | let bin_path = format!("target/{profile}/ghostwire"); 42 | 43 | // arguments to pass to the application 44 | let mut run_args: Vec<_> = opts.run_args.iter().map(String::as_str).collect(); 45 | 46 | // configure args 47 | let mut args: Vec<_> = opts.runner.trim().split_terminator(' ').collect(); 48 | args.push(bin_path.as_str()); 49 | args.append(&mut run_args); 50 | 51 | // run the command 52 | let status = Command::new(args.first().expect("No first argument")) 53 | .args(args.iter().skip(1)) 54 | .status() 55 | .expect("failed to run the command"); 56 | 57 | if !status.success() { 58 | anyhow::bail!("Failed to run `{}`", args.join(" ")); 59 | } 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /src/cli/src/utils/handler.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | console::print_success, 3 | yaml::parse_yaml, 4 | }; 5 | use crate::utils::socket::send_message; 6 | use anyhow::{ 7 | Context, 8 | Result, 9 | }; 10 | use clap::ArgMatches; 11 | use ghostwire_types::{ 12 | ClientMessage, 13 | ClientReqType, 14 | }; 15 | use std::fs; 16 | 17 | /// Handle the CLI commands. 18 | pub fn handle_arguments(matches: ArgMatches) -> Result<()> { 19 | let resp = match matches.subcommand() { 20 | Some(("status", _)) => send_message(ClientMessage { 21 | req_type: ClientReqType::STATUS, 22 | interface: None, 23 | rules: None, 24 | }), 25 | /* Currently disabled due to lack of implementation for persistence. 26 | Some(("enable", enable_matches)) => { 27 | let interface = enable_matches 28 | .get_one::("interface") 29 | .context("No interface provided")?; 30 | send_message(ClientMessage { 31 | req_type: ClientReqType::ENABLE, 32 | interface: Some(interface.to_string()), 33 | rules: None, 34 | }) 35 | }*/ 36 | Some(("disable", _)) => send_message(ClientMessage { 37 | req_type: ClientReqType::DISABLE, 38 | interface: None, 39 | rules: None, 40 | }), 41 | Some(("load", file_matches)) => { 42 | let file = file_matches 43 | .get_one::("file") 44 | .context("No file provided")?; 45 | let (rules, interface) = parse_yaml(fs::read_to_string(file)?)?; 46 | 47 | send_message(ClientMessage { 48 | req_type: ClientReqType::RULES, 49 | interface: Some(interface), 50 | rules: Some(rules), 51 | }) 52 | } 53 | _ => { 54 | anyhow::bail!("No subcommand provided"); 55 | } 56 | }?; 57 | 58 | print_success(&resp); 59 | 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ghostwire install script 4 | # Currently, only tested on Ubuntu 5 | 6 | set -eu 7 | 8 | repo="https://github.com/packetware/ghostwire" 9 | arch=$(uname -m) 10 | os=$(uname -s) 11 | 12 | echo "🤗 Finding the right binary for your platform..." 13 | 14 | if [ "$os" == "Linux" ]; then 15 | if [ "$arch" == "x86_64" ]; then 16 | target="linux-x86" 17 | elif [ "$arch" == "aarch64" | "$arch" == "arm64" ]; then 18 | target="linux-arm4" 19 | else 20 | echo "😩 Sorry, we don't have binaries for your platform: '$os $arch'" 21 | echo " You can build from source or submit an issue at $repo/issues" 22 | exit 1 23 | fi 24 | else 25 | echo "😩 Sorry, we don't have binaries for your platform: '$os $arch'. Currently, only Linux is supported." 26 | exit 1 27 | fi 28 | 29 | # Download the server 30 | echo "📦 Downloading latest ghostwire server to /opt/ghostwire" 31 | mkdir -p /opt/ghostwire 32 | sudo curl --fail --location --progress-bar $repo/releases/latest/download/ghostwire-server-$target -o /opt/ghostwire/ghostwire 33 | chmod +x /opt/ghostwire/ghostwire 34 | 35 | # Start the systemd service 36 | echo "🛠️ Creating systemd service for Ghostwire server" 37 | sudo tee /etc/systemd/system/ghostwire.service > /dev/null < = RwLock::new(OverallState { enabled: false, state: None, counters: create_prometheus_counters().expect("infallible prometheus counter generation failed") }); 26 | } 27 | 28 | mod utils; 29 | 30 | #[tokio::main] 31 | async fn main() -> Result<(), anyhow::Error> { 32 | println!("Starting Ghostwire ..."); 33 | 34 | // Set our own tracing subscriber. 35 | tracing_subscriber::fmt::Subscriber::builder() 36 | .pretty() 37 | .with_max_level(tracing::Level::TRACE) 38 | .init(); 39 | 40 | // TODO: read the previous state on startup (@see utils/bootloader.rs) 41 | 42 | // Start the UNIX socket server. 43 | task::spawn(socket_server()); 44 | 45 | // Start the Prometheus metrics task. 46 | task::spawn(every(10).seconds().perform(|| async { 47 | prometheus_metrics().await; 48 | })); 49 | 50 | // Begin to manage the eBPF maps. 51 | task::spawn(every(1).minute().perform(|| async { 52 | manage_maps().await; 53 | })); 54 | 55 | // Start the Prometheus HTTP listener. 56 | task::spawn(handle_prom_listener()); 57 | 58 | // Allow the user to unload the eBPF program and the socket server with Ctrl-C. 59 | tracing::info!("Waiting for Ctrl-C..."); 60 | 61 | signal::ctrl_c().await?; 62 | 63 | tracing::info!("Exiting..."); 64 | 65 | // Emperically, the process doesn't close when `main` returns Ok(()), so we exit manually. 66 | exit(0); 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /src/ebpf/xtask/src/build_ebpf.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::PathBuf, 3 | process::Command, 4 | }; 5 | 6 | use clap::Parser; 7 | 8 | #[derive(Debug, Copy, Clone)] 9 | pub enum Architecture { 10 | BpfEl, 11 | BpfEb, 12 | } 13 | 14 | impl std::str::FromStr for Architecture { 15 | type Err = String; 16 | 17 | fn from_str(s: &str) -> Result { 18 | Ok(match s { 19 | "bpfel-unknown-none" => Architecture::BpfEl, 20 | "bpfeb-unknown-none" => Architecture::BpfEb, 21 | _ => return Err("invalid target".to_owned()), 22 | }) 23 | } 24 | } 25 | 26 | impl std::fmt::Display for Architecture { 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | f.write_str(match self { 29 | Architecture::BpfEl => "bpfel-unknown-none", 30 | Architecture::BpfEb => "bpfeb-unknown-none", 31 | }) 32 | } 33 | } 34 | 35 | #[derive(Debug, Parser)] 36 | pub struct Options { 37 | /// Set the endianness of the BPF target 38 | #[clap(default_value = "bpfel-unknown-none", long)] 39 | pub target: Architecture, 40 | /// Build the release target 41 | #[clap(long)] 42 | pub release: bool, 43 | } 44 | 45 | pub fn build_ebpf(opts: Options) -> Result<(), anyhow::Error> { 46 | let dir = PathBuf::from("ghostwire-ebpf"); 47 | let target = format!("--target={}", opts.target); 48 | let mut args = vec!["build", target.as_str(), "-Z", "build-std=core"]; 49 | if opts.release { 50 | args.push("--release") 51 | } 52 | 53 | // Command::new creates a child process which inherits all env variables. This means env 54 | // vars set by the cargo xtask command are also inherited. RUSTUP_TOOLCHAIN is removed 55 | // so the rust-toolchain.toml file in the -ebpf folder is honored. 56 | 57 | let status = Command::new("cargo") 58 | .current_dir(dir) 59 | .env_remove("RUSTUP_TOOLCHAIN") 60 | .args(&args) 61 | .status() 62 | .expect("failed to build bpf program"); 63 | assert!(status.success()); 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/ebpf.yml: -------------------------------------------------------------------------------- 1 | # Inspired by aya-template 2 | # @see https://github.com/aya-rs/aya-template/blob/main/.github/workflows/ci.yml 3 | 4 | name: ebpf 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | pull_request: 12 | branches: 13 | - main 14 | 15 | schedule: 16 | - cron: 00 4 * * * 17 | 18 | env: 19 | CARGO_TERM_COLOR: always 20 | 21 | jobs: 22 | ebpf: 23 | runs-on: ubuntu-22.04 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: switch to nightly 29 | run: | 30 | rustup default nightly 31 | rustup component add rustfmt --toolchain nightly 32 | rustup component add clippy --toolchain nightly 33 | rustup component add rust-src --toolchain nightly 34 | 35 | - uses: taiki-e/install-action@v2 36 | with: 37 | tool: bpf-linker 38 | 39 | - name: rustfmt 40 | run: | 41 | rustfmt --config-path rustfmt.toml --edition 2021 src/ebpf/ghostwire-ebpf/src/main.rs 42 | rustfmt --config-path rustfmt.toml --edition 2021 src/ebpf/ghostwire-ebpf/src/utils/* 43 | rustfmt --config-path rustfmt.toml --edition 2021 src/ebpf/ghostwire/src/main.rs 44 | rustfmt --config-path rustfmt.toml --edition 2021 src/ebpf/ghostwire/src/utils/* 45 | rustfmt --config-path rustfmt.toml --edition 2021 src/ebpf/ghostwire-common/src/* 46 | git diff --exit-code 47 | 48 | - name: cargo xtask build 49 | working-directory: src/ebpf 50 | run: cargo run --package xtask -- build 51 | 52 | - name: cargo xtask clippy 53 | working-directory: src/ebpf 54 | run: cargo run --package xtask -- clippy 55 | 56 | - name: cargo socket server build 57 | working-directory: src/ebpf/ghostwire 58 | run: cargo build 59 | 60 | - name: cargo socket server clippy 61 | working-directory: src/ebpf/ghostwire 62 | run: cargo clippy 63 | 64 | - name: cargo common build 65 | working-directory: src/ebpf/ghostwire-common 66 | run: cargo build 67 | 68 | - name: cargo common clippy 69 | working-directory: src/ebpf/ghostwire-common 70 | run: cargo clippy 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ghostwire 2 | An (experimental) stateful XDP firewall for Linux. 3 | 4 | We built this to use it internally at [Packetware](https://packetware.net), a global content delivery network, to protect our infrastructure. 5 | We found performance with IPtables is a joke for systems where performance is must, and unintuitive to manage persistence for. 6 | 7 | For stateless "stupid" filtering, Ghostwire is approximately 5.5x more capable (in handled packets per second) than IPtables's fastest PREROUTING table. 8 | For stateful filtering, Ghostwire destroys IPtables' conntrack (our initial benchmarks show at least 9.5x more packets per second). 9 | It's controlled through simple YAML configuration files, no BS. 10 | 11 | Some features are: 12 | - Stateful holepunch-based filtering 13 | - Rate limiting 14 | - Simple YAML syntax 15 | - UNIX socket API 16 | - Exports Prometheus metrics 17 | 18 | We'd like to add: 19 | - Block IP UNIX socket endpoint (much more performant ipset) 20 | - More complex rate limiting 21 | - Installation support for more systems 22 | 23 | This is currently in Alpha state, I wouldn't recommend using it in production just yet. 24 | 25 | ## Installation 26 | Ghostwire is tested on Ubuntu 24.04 LTS internally, but this installation script should work on any systemd-based system. 27 | 28 | ```bash 29 | curl -s https://raw.githubusercontent.com/packetware/ghostwire/main/scripts/install.sh | sudo bash 30 | ``` 31 | 32 | Then, add the rules you'd like. 33 | 34 | Start the firewall: 35 | ```bash 36 | gw load config.yml 37 | ``` 38 | 39 | See the status: 40 | ```bash 41 | gw status 42 | ``` 43 | 44 | Stop the firewall: 45 | ```bash 46 | gw disable 47 | ``` 48 | 49 | ## Configuration 50 | Ghostwire is configured through YAML files. Here's an example configuration file: 51 | 52 | ```yaml 53 | interface: "eth0" 54 | # The firewall rules you'd like to define. 55 | # The firewall drops traffic like TCP and UDP by default, rules whitelist traffic 56 | rules: 57 | # Define each rule individually 58 | - rule: 59 | # The source IP range this rule will apply to. For example, 23.133.104.69/32, or 23.133.104.0/24. 60 | # To allow traffic from any IP, use 0.0.0.0/0 61 | source_ip_range: 0.0.0.0/0 62 | # The destination IP range this rule will apply to. 63 | # To allow traffic to go to any IP assigned with this server, use 0.0.0.0/0. 64 | destination_ip_range: 0.0.0.0/0 65 | # The IP protocol to allow. 66 | # Current allowed values are: TCP, UDP, ICMP, ALL. 67 | protocol: "TCP" 68 | # The port to allow the traffic to. Only applicable to TCP and UDP. 69 | # Omit or enter 0 to allow any port. 70 | port: 22 71 | # Limit the amount of packets sent to this service per source IP. Runs over 1 minute. 72 | # Enter to zero to disable ratelimiting. 73 | ratelimit: 100 74 | ``` 75 | -------------------------------------------------------------------------------- /src/types/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ghostwire_types" 7 | version = "0.2.1" 8 | dependencies = [ 9 | "serde", 10 | "serde_json", 11 | ] 12 | 13 | [[package]] 14 | name = "itoa" 15 | version = "1.0.11" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 18 | 19 | [[package]] 20 | name = "memchr" 21 | version = "2.7.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 24 | 25 | [[package]] 26 | name = "proc-macro2" 27 | version = "1.0.86" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 30 | dependencies = [ 31 | "unicode-ident", 32 | ] 33 | 34 | [[package]] 35 | name = "quote" 36 | version = "1.0.37" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 39 | dependencies = [ 40 | "proc-macro2", 41 | ] 42 | 43 | [[package]] 44 | name = "ryu" 45 | version = "1.0.18" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 48 | 49 | [[package]] 50 | name = "serde" 51 | version = "1.0.210" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 54 | dependencies = [ 55 | "serde_derive", 56 | ] 57 | 58 | [[package]] 59 | name = "serde_derive" 60 | version = "1.0.210" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 63 | dependencies = [ 64 | "proc-macro2", 65 | "quote", 66 | "syn", 67 | ] 68 | 69 | [[package]] 70 | name = "serde_json" 71 | version = "1.0.128" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 74 | dependencies = [ 75 | "itoa", 76 | "memchr", 77 | "ryu", 78 | "serde", 79 | ] 80 | 81 | [[package]] 82 | name = "syn" 83 | version = "2.0.79" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 86 | dependencies = [ 87 | "proc-macro2", 88 | "quote", 89 | "unicode-ident", 90 | ] 91 | 92 | [[package]] 93 | name = "unicode-ident" 94 | version = "1.0.13" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 97 | -------------------------------------------------------------------------------- /src/types/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | 3 | use serde::{ 4 | Deserialize, 5 | Serialize, 6 | }; 7 | 8 | // Types for firewall rules, messages 9 | 10 | /// A message between server and client 11 | #[derive(Serialize, Deserialize, Debug)] 12 | pub struct ClientMessage { 13 | pub req_type: ClientReqType, 14 | /// Optional rules to send to the server on a RULES request 15 | pub rules: Option>, 16 | /// Optional interface to send to the server on a RULES request 17 | pub interface: Option, 18 | } 19 | 20 | /// What the client is requesting from the server 21 | #[derive(Serialize, Deserialize, Debug)] 22 | pub enum ClientReqType { 23 | /// Client is asking for the current status of the firewall 24 | STATUS, 25 | /// Client is providing new rules and the interface to listen on 26 | RULES, 27 | /// Client is asking to enable the firewall 28 | ENABLE, 29 | /// Client is asking to disable the firewall 30 | DISABLE, 31 | } 32 | 33 | /// A response from the server 34 | #[derive(Serialize, Deserialize, Debug)] 35 | pub struct ServerMessage { 36 | pub request_success: bool, 37 | pub message: String, 38 | } 39 | 40 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 41 | /// A firewall rule in C format, where fields are expected to be in big endian, or network byte order 42 | /// You may have noticed this also exists in the ghostwire_types crate. This is because that's the specific type 43 | /// that is used in the eBPF program. 44 | pub struct Rule { 45 | /// The ID of this rule with what the API identifies it as. This will also be the key of the 46 | /// ratelimiting map if ratelimiting is enabled for this rule. 47 | pub id: u32, 48 | /// The start source IP address in big endian 49 | pub source_start_ip: u32, 50 | /// The end source IP address in big endian 51 | pub source_end_ip: u32, 52 | /// The start destination IP address in big endian. If this rule applies everywhere, all bytes 53 | /// will show 0 54 | pub destination_start_ip: u32, 55 | /// The end destination IP address in big endian. If this rule applies everywhere, all bytes 56 | /// will show 0 57 | pub destination_end_ip: u32, 58 | /// Protocol number (currently limited to either 1, 6, 17 for ICMP, TCP, and UDP respectively. 59 | /// if this rule applies to all protocols, this will be zero) 60 | pub protocol_number: u8, 61 | /// The port if TCP or UDP (if not, 0) 62 | pub port_number: u16, 63 | /// If the rule is a ratelimiting one, represent the amount of traffic allowed per IP over 10 64 | /// seconds. If there's no ratelimiting rule, this is 0. 65 | pub ratelimiting: u32, 66 | } 67 | 68 | /// A network protocol. This is used in the Rule struct to determine what protocol the rule applies to. 69 | #[derive(Serialize, Deserialize, Debug)] 70 | pub enum Protocol { 71 | TCP, 72 | UDP, 73 | ICMP, 74 | } 75 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/state.rs: -------------------------------------------------------------------------------- 1 | use aya::{ 2 | maps::{ 3 | HashMap, 4 | MapData, 5 | }, 6 | Bpf, 7 | }; 8 | use ghostwire_common::{ 9 | Rule, 10 | RuleAnalytics, 11 | }; 12 | use prometheus::{ 13 | IntCounterVec, 14 | Registry, 15 | }; 16 | use std::sync::Arc; 17 | use tokio::sync::RwLock; 18 | 19 | /// The overall state of the firewall, to be exposed to the CLI 20 | pub struct OverallState { 21 | pub enabled: bool, 22 | /// The state of the firewall when active 23 | pub state: Option>, 24 | /// The Prometheus counters to update from the maps 25 | pub counters: PromCounters, 26 | } 27 | 28 | /// The state of the firewall when active 29 | pub struct State { 30 | /// Reference to the eBPF program. Held to avoid dropping the program. 31 | pub _ebpf: RwLock, 32 | /// The interface to apply the XDP hook to 33 | pub interface: String, 34 | /// The applied rules 35 | pub rule_map: RwLock>, 36 | /// The rule metrics 37 | pub rule_analytic_map: HashMap, 38 | /// The ratelimit metrics 39 | pub rule_ratelimit_map: RwLock>, 40 | /// The aggregate XDP metrics 41 | pub xdp_analytic_map: HashMap, 42 | /// The aggregate traffic control metrics 43 | pub tc_analytic_map: HashMap, 44 | } 45 | 46 | /// The state of the Prometheus counters 47 | pub struct PromCounters { 48 | /// The prometheus registry 49 | pub registry: Registry, 50 | /// The number of times a rule was evaluated 51 | pub rule_evaluated: IntCounterVec, 52 | /// The number of times a rule allowed traffic 53 | pub rule_passed: IntCounterVec, 54 | /// The number of times an XDP action was taken 55 | pub xdp_action: IntCounterVec, 56 | /// The number of times a TC action was taken 57 | pub tc_action: IntCounterVec, 58 | } 59 | 60 | impl OverallState { 61 | /// Implement format for OverallState that shows the overall status of the application. Not a trait 62 | /// because we access the rule map async and blocking reads are a bad practice. 63 | pub async fn fmt(&self) -> String { 64 | let mut str = String::new(); 65 | 66 | if self.enabled { 67 | str.push_str("Ghostwire is enabled"); 68 | } else { 69 | str.push_str("Ghostwire is disabled"); 70 | } 71 | 72 | // summarize the rules 73 | if let Some(state) = self.state.as_ref() { 74 | str.push_str(&format!(" on interface {}", state.interface)); 75 | let rule_map = state.rule_map.read().await; 76 | // eBPF maps don't have a length method 77 | str.push_str(&format!( 78 | " with {} rules", 79 | rule_map.iter().collect::>().len() 80 | )); 81 | } 82 | 83 | str 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/cli/src/utils/yaml.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | /// This file is dedicated to the YAML chief Dobri. 3 | use ghostwire_types::Rule; 4 | use serde::Deserialize; 5 | use std::net::Ipv4Addr; 6 | 7 | /// Convert the YAML into firewall rules. Returns the rule and the correct interface. 8 | pub fn parse_yaml(yaml: String) -> anyhow::Result<(Vec, String)> { 9 | let parsed: serde_yaml::Value = serde_yaml::from_str(&yaml)?; 10 | let rules: Vec = serde_yaml::from_value(parsed["rules"].clone())?; 11 | 12 | let parsed_rules: Vec = rules 13 | .into_iter() 14 | .enumerate() 15 | .map(|(id, yaml_rule)| convert_to_rule(yaml_rule, id as u32)) 16 | .collect::, anyhow::Error>>()?; 17 | 18 | Ok(( 19 | parsed_rules, 20 | parsed["interface"] 21 | .as_str() 22 | .ok_or(anyhow::anyhow!("interface not provided"))? 23 | .to_string(), 24 | )) 25 | } 26 | 27 | /// Convert a YAML rule into a firewall rule. 28 | fn convert_to_rule(yaml_rule: YamlRule, id: u32) -> anyhow::Result { 29 | let (source_start_ip, source_end_ip) = parse_ip_range(&yaml_rule.source_ip_range)?; 30 | let (destination_start_ip, destination_end_ip) = 31 | parse_ip_range(&yaml_rule.destination_ip_range)?; 32 | 33 | let protocol_number = match yaml_rule.protocol.to_lowercase().as_str() { 34 | "icmp" => 1, 35 | "tcp" => 6, 36 | "udp" => 17, 37 | _ => anyhow::bail!("Invalid protocol"), 38 | }; 39 | 40 | Ok(Rule { 41 | id, 42 | source_start_ip, 43 | source_end_ip, 44 | destination_start_ip, 45 | destination_end_ip, 46 | protocol_number: u8::to_be(protocol_number), 47 | port_number: u16::to_be(yaml_rule.port), 48 | ratelimiting: yaml_rule.ratelimit, 49 | }) 50 | } 51 | 52 | #[derive(Debug, Deserialize)] 53 | /// A rule in the YAML format. 54 | struct YamlRule { 55 | source_ip_range: String, 56 | destination_ip_range: String, 57 | protocol: String, 58 | port: u16, 59 | ratelimit: u32, 60 | } 61 | 62 | /// Parse an IP range in CIDR notation to two big endian numbers: the start and end of the range. 63 | fn parse_ip_range(ip_range: &str) -> anyhow::Result<(u32, u32)> { 64 | if ip_range == "0.0.0.0/0" { 65 | return Ok((0, u32::MAX)); 66 | } 67 | 68 | // Break up the subnet from the IP. 69 | let parts: Vec<&str> = ip_range.split('/').collect(); 70 | // Parse the IPv4 part. 71 | let ip: Ipv4Addr = parts[0].parse().context("Invalid IP address")?; 72 | // The user didn't provide a prefix length. Assume it's a single ip (/32). 73 | let prefix_length: u8 = if parts.len() > 1 { 74 | parts[1].parse().context("Invalid prefix length")? 75 | } else { 76 | 32 77 | }; 78 | let mask = !((1u32 << (32 - prefix_length)) - 1); 79 | 80 | let start_ip = u32::from(ip) & mask; 81 | let end_ip = start_ip | !mask; 82 | 83 | Ok((start_ip.to_be(), end_ip.to_be())) 84 | } 85 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use aya_ebpf::{ 5 | bindings::{ 6 | xdp_action, 7 | TC_ACT_SHOT, 8 | }, 9 | macros::{ 10 | classifier, 11 | map, 12 | xdp, 13 | }, 14 | maps::{ 15 | HashMap, 16 | LruHashMap, 17 | }, 18 | programs::{ 19 | TcContext, 20 | XdpContext, 21 | }, 22 | }; 23 | use ghostwire_common::{ 24 | Rule, 25 | RuleAnalytics, 26 | }; 27 | 28 | mod handlers; 29 | mod utils; 30 | 31 | use crate::handlers::{ 32 | egress::ghostwire_egress_fallible, 33 | ingress::ghostwire_ingress_fallible, 34 | }; 35 | 36 | #[map] 37 | /// The map which holds the firewall rules. Key is the index. Arrays in eBPF are immutable, so we're using a HashMap as a pseudo array 38 | pub static RULES: HashMap = HashMap::::with_max_entries(100, 0); 39 | 40 | #[map] 41 | /// The map which holds the ratelimiting metrics for ratelimiting-based rules. Key is a combination 42 | /// of IP address and rule ID. 43 | pub static RATELIMITING: LruHashMap = 44 | LruHashMap::::with_max_entries(1_000_000, 0); 45 | 46 | #[map] 47 | /// The map which holds the analytics for each firewall rule. Key is the rule ID. 48 | pub static RULE_ANALYTICS: HashMap = 49 | HashMap::::with_max_entries(1024, 0); 50 | 51 | #[map] 52 | /// The holepunched connections (leaving the server). Key is source IP + source port + destination 53 | /// IP + destination port. Value is the time the last time there was traffic over this connection. 54 | pub static HOLEPUNCHED: LruHashMap = 55 | LruHashMap::::with_max_entries(1_000_000, 0); 56 | 57 | #[map] 58 | /// Whenever an action is completed IN XDP, like DROP, PASS, or ABORT, report that in this map. Designed 59 | /// to be an overall statistic 60 | pub static XDP_ACTION_ANALYTICS: HashMap = 61 | HashMap::::with_max_entries(100, 0); 62 | 63 | #[map] 64 | /// Whenever an action is completed, like TC_ACT_SHOT or TC_ACT_PIPE report that in this map. Designed 65 | /// to be an overall statistic 66 | pub static TC_ACTION_ANALYTICS: HashMap = HashMap::::with_max_entries(100, 0); 67 | 68 | #[xdp] 69 | /// The infallible XDP hook for all incoming traffic. 70 | pub fn ghostwire_xdp(ctx: XdpContext) -> u32 { 71 | unsafe { 72 | let result = match ghostwire_ingress_fallible(ctx) { 73 | Ok(ret) => ret, 74 | Err(_) => xdp_action::XDP_ABORTED, 75 | }; 76 | 77 | // Record the XDP action taken. 78 | match XDP_ACTION_ANALYTICS.get_ptr_mut(&result) { 79 | Some(val) => *val += 1, 80 | None => { 81 | let _ = XDP_ACTION_ANALYTICS.insert(&result, &1, 0); 82 | } 83 | } 84 | 85 | result 86 | } 87 | } 88 | 89 | #[classifier] 90 | /// The infallible TC hook for all outgoing traffic. 91 | pub fn ghostwire_tc(tc: TcContext) -> i32 { 92 | unsafe { 93 | let result = match ghostwire_egress_fallible(tc) { 94 | Ok(ret) => ret, 95 | Err(_) => TC_ACT_SHOT, 96 | }; 97 | 98 | // Record the TC action taken. 99 | match TC_ACTION_ANALYTICS.get_ptr_mut(&result) { 100 | Some(val) => *val += 1, 101 | None => { 102 | let _ = TC_ACTION_ANALYTICS.insert(&result, &1, 0); 103 | } 104 | } 105 | 106 | result 107 | } 108 | } 109 | 110 | #[panic_handler] 111 | fn panic(_info: &core::panic::PanicInfo) -> ! { 112 | unsafe { core::hint::unreachable_unchecked() } 113 | } 114 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/src/handlers/egress.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::ptr_at::tc_ptr_at_fallible, 3 | HOLEPUNCHED, 4 | }; 5 | use aya_ebpf::{ 6 | bindings::TC_ACT_PIPE, 7 | helpers::bpf_ktime_get_ns, 8 | programs::TcContext, 9 | }; 10 | use network_types::{ 11 | eth::EthHdr, 12 | ip::{ 13 | IpProto::{ 14 | Icmp, 15 | Tcp, 16 | Udp, 17 | }, 18 | Ipv4Hdr, 19 | }, 20 | tcp::TcpHdr, 21 | udp::UdpHdr, 22 | }; 23 | 24 | /// The function is called whenever a packet is leaving the server through the traffic control 25 | /// hook. It should: 26 | /// 1. Parse the packet; 27 | /// - Get the source & destination IP addresses and the port (if the protocol is portless, like ICMP, both ports will be 0) 28 | /// 2. Update the holepunched LRU map; 29 | /// - Key is the source IP + source port + destination IP + destination port 30 | /// - If the connection is already in the map, update the timestamp 31 | /// - If the connection is not in the map, add it 32 | /// - If the connection is a TCP connection and the FIN / RST flags are set, remove from the map 33 | pub unsafe fn ghostwire_egress_fallible(tc: TcContext) -> Result { 34 | // Skip the ethernet header, that's not providing us with any value right now. 35 | // Attempt to parse the IP header. 36 | let ip_header: *const Ipv4Hdr = tc_ptr_at_fallible(&tc, EthHdr::LEN).map_err(|_| ())?; 37 | 38 | // Pull the source and destination IP addresses and the protocol. 39 | let src_ip = unsafe { (*ip_header).src_addr }; 40 | let dst_ip = unsafe { (*ip_header).dst_addr }; 41 | let protocol = unsafe { (*ip_header).proto }; 42 | // Store whether the connection should be removed from the holepunched map, instead of 43 | // appended. 44 | let mut remove = false; 45 | let (src_port, dst_port) = match protocol { 46 | Tcp => { 47 | // Parse the TCP header. 48 | let tcp_header: *const TcpHdr = 49 | tc_ptr_at_fallible::(&tc, EthHdr::LEN + Ipv4Hdr::LEN).map_err(|_| ())?; 50 | 51 | // Check if the connection is being closed. 52 | // Currently, this is limited to the RST flag. The problem with FIN is the server will 53 | // keep waiting for the generic ACK to close the connection, which will never come if 54 | // we remove it from the map. A potential solution is to create another map for pending 55 | // closing connections. 56 | if unsafe { (*tcp_header)._bitfield_1.get(3, 1) } != 0 { 57 | remove = true; 58 | } 59 | 60 | // Get the source and destination ports. 61 | ((*tcp_header).source, (*tcp_header).dest) 62 | } 63 | Udp => { 64 | // Parse the UDP header. 65 | let udp_header: *const UdpHdr = 66 | tc_ptr_at_fallible::(&tc, EthHdr::LEN + Ipv4Hdr::LEN).map_err(|_| ())?; 67 | 68 | // Get the source and destination ports. 69 | ((*udp_header).source, (*udp_header).dest) 70 | } 71 | Icmp => (0, 0), 72 | _ => return Ok(TC_ACT_PIPE), 73 | }; 74 | 75 | // Get the key for the holepunched map, upgrading each type to u64 to avoid overflow. 76 | let key = src_ip as u64 + src_port as u64 + dst_ip as u64 + dst_port as u64; 77 | 78 | match remove { 79 | true => { 80 | // Remove the connection from the holepunched map. 81 | let _ = HOLEPUNCHED.remove(&key); 82 | } 83 | false => { 84 | // Update the holepunched map with the latest connection time. 85 | let _ = HOLEPUNCHED.insert(&key, &bpf_ktime_get_ns(), 0); 86 | } 87 | } 88 | 89 | // Let traffic go through. 90 | Ok(TC_ACT_PIPE) 91 | } 92 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/ebpf.rs: -------------------------------------------------------------------------------- 1 | use super::state::State; 2 | use crate::OVERALL_STATE; 3 | use anyhow::Context; 4 | use aya::{ 5 | include_bytes_aligned, 6 | maps::HashMap, 7 | programs::{ 8 | tc, 9 | SchedClassifier, 10 | TcAttachType, 11 | Xdp, 12 | XdpFlags, 13 | }, 14 | Bpf, 15 | }; 16 | use aya_log::BpfLogger; 17 | use ghostwire_common::{ 18 | Rule, 19 | RuleAnalytics, 20 | }; 21 | use std::sync::Arc; 22 | use tokio::sync::RwLock; 23 | 24 | pub async fn load_ebpf(initial_rules: Vec, interface: String) -> anyhow::Result<()> { 25 | match load_ebpf_fallible(initial_rules.clone(), interface.clone(), false).await { 26 | Ok(_) => Ok(()), 27 | Err(e) => { 28 | tracing::warn!( 29 | "failed to load eBPF with default flags, trying again in SKB: {}", 30 | e 31 | ); 32 | load_ebpf_fallible(initial_rules, interface.clone(), true) 33 | .await 34 | .map_err(|_| { 35 | anyhow::anyhow!( 36 | "failed to load eBPF program. is {} the correct interface?", 37 | interface 38 | ) 39 | }) 40 | } 41 | } 42 | } 43 | 44 | /// Load the eBPF program, fetching the maps and creating state from partial arguments 45 | async fn load_ebpf_fallible( 46 | initial_rules: Vec, 47 | interface: String, 48 | skb: bool, 49 | ) -> anyhow::Result<()> { 50 | // Bump the memlock rlimit. This is needed for older kernels that don't use the 51 | // new memcg based accounting, see https://lwn.net/Articles/837122/ 52 | let rlim = libc::rlimit { 53 | rlim_cur: libc::RLIM_INFINITY, 54 | rlim_max: libc::RLIM_INFINITY, 55 | }; 56 | let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; 57 | if ret != 0 { 58 | tracing::debug!("remove limit on locked memory failed, ret is: {}", ret); 59 | } 60 | 61 | #[cfg(debug_assertions)] 62 | let mut bpf = Bpf::load(include_bytes_aligned!( 63 | "../../../target/bpfel-unknown-none/debug/ghostwire" 64 | ))?; 65 | #[cfg(not(debug_assertions))] 66 | let mut bpf = Bpf::load(include_bytes_aligned!( 67 | "../../../target/bpfel-unknown-none/release/ghostwire" 68 | ))?; 69 | 70 | if let Err(e) = BpfLogger::init(&mut bpf) { 71 | // If the logger doesn't intialize, we probably weren't logging anything. 72 | tracing::info!("didn't initialize eBPF logger: {}", e); 73 | } 74 | 75 | let program: &mut Xdp = bpf.program_mut("ghostwire_xdp").unwrap().try_into()?; 76 | program.load().unwrap(); 77 | program 78 | .attach( 79 | &interface, 80 | match skb { 81 | true => XdpFlags::SKB_MODE, 82 | false => XdpFlags::default(), 83 | }, 84 | ) 85 | .context("failed to attach XDP. trying with SKB next...")?; 86 | let _ = tc::qdisc_add_clsact(&interface); 87 | 88 | let program: &mut SchedClassifier = bpf.program_mut("ghostwire_tc").unwrap().try_into()?; 89 | program.load()?; 90 | program.attach(&interface, TcAttachType::Egress)?; 91 | 92 | // Fetch the eBPF maps. 93 | let mut rule_map: HashMap<_, u32, Rule> = HashMap::try_from(bpf.take_map("RULES").unwrap())?; 94 | 95 | for (i, rule) in initial_rules.iter().enumerate() { 96 | rule_map.insert(i as u32, rule, 0)?; 97 | } 98 | 99 | let rule_ratelimit_map: HashMap<_, u64, u64> = 100 | HashMap::try_from(bpf.take_map("RATELIMITING").unwrap())?; 101 | 102 | let rule_analytic_map: HashMap<_, u32, RuleAnalytics> = 103 | HashMap::try_from(bpf.take_map("RULE_ANALYTICS").unwrap())?; 104 | 105 | let xdp_analytic_map: HashMap<_, u32, u128> = 106 | HashMap::try_from(bpf.take_map("XDP_ACTION_ANALYTICS").unwrap())?; 107 | 108 | let tc_analytic_map: HashMap<_, i32, u128> = 109 | HashMap::try_from(bpf.take_map("TC_ACTION_ANALYTICS").unwrap())?; 110 | 111 | let state = Arc::new(State { 112 | interface, 113 | _ebpf: RwLock::new(bpf), 114 | rule_map: RwLock::new(rule_map), 115 | rule_ratelimit_map: RwLock::new(rule_ratelimit_map), 116 | rule_analytic_map, 117 | xdp_analytic_map, 118 | tc_analytic_map, 119 | }); 120 | 121 | // Load the state. 122 | let mut write = OVERALL_STATE.write().await; 123 | 124 | write.state = Some(state); 125 | 126 | Ok(()) 127 | } 128 | 129 | /// Unload the eBPF program 130 | pub async fn unload_ebpf() { 131 | let mut write = OVERALL_STATE.write().await; 132 | 133 | // writing None to the state necessarily will drop the eBPF program 134 | // @see https://aya-rs.dev/book/aya/lifecycle/#populating-our-map-from-userspace 135 | // a critical assumption is that the state is not being used anywhere else in the program (this 136 | // assumption is currently correct, this is the only reference persistently held to the state) 137 | write.state = None; 138 | } 139 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aya-ebpf" 7 | version = "0.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b7318de0c49a17873182763831cb22f74fb30d04e2eb7e6d7b7e9b7d86d70ed3" 10 | dependencies = [ 11 | "aya-ebpf-bindings", 12 | "aya-ebpf-cty", 13 | "aya-ebpf-macros", 14 | "rustversion", 15 | ] 16 | 17 | [[package]] 18 | name = "aya-ebpf-bindings" 19 | version = "0.1.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "a8536b7e39b232ecd854e587f473ba15640c09afc3e08408fc28144a7404ae75" 22 | dependencies = [ 23 | "aya-ebpf-cty", 24 | ] 25 | 26 | [[package]] 27 | name = "aya-ebpf-cty" 28 | version = "0.2.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d5c130d898322b9698937465b3b749095dae85dba0da4ee648235947eb95738d" 31 | 32 | [[package]] 33 | name = "aya-ebpf-macros" 34 | version = "0.1.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "ce7820cc83547582284a140ffbdd46ab527d7ee2d9d0cfedf3f184fad3f8e15c" 37 | dependencies = [ 38 | "proc-macro-error", 39 | "proc-macro2", 40 | "quote", 41 | "syn", 42 | ] 43 | 44 | [[package]] 45 | name = "aya-log-common" 46 | version = "0.1.14" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "b6d38a351ee2d5dc24e04cac6184b1b39408642d9a8b585892c99146f8dd4edb" 49 | dependencies = [ 50 | "num_enum", 51 | ] 52 | 53 | [[package]] 54 | name = "aya-log-ebpf" 55 | version = "0.1.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "2a10bbadd0829895a91eb1cd2bb02d7af145704087f03812bed60cb9fe65dbb3" 58 | dependencies = [ 59 | "aya-ebpf", 60 | "aya-log-common", 61 | "aya-log-ebpf-macros", 62 | ] 63 | 64 | [[package]] 65 | name = "aya-log-ebpf-macros" 66 | version = "0.1.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "f6d8251a75f56077db51892041aa6b77c70ef2723845d7a210979700b2f01bc4" 69 | dependencies = [ 70 | "aya-log-common", 71 | "aya-log-parser", 72 | "proc-macro2", 73 | "quote", 74 | "syn", 75 | ] 76 | 77 | [[package]] 78 | name = "aya-log-parser" 79 | version = "0.1.13" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "14b102eb5c88c9aa0b49102d3fbcee08ecb0dfa81014f39b373311de7a7032cb" 82 | dependencies = [ 83 | "aya-log-common", 84 | ] 85 | 86 | [[package]] 87 | name = "ghostwire-common" 88 | version = "0.1.0" 89 | 90 | [[package]] 91 | name = "ghostwire-ebpf" 92 | version = "0.1.0" 93 | dependencies = [ 94 | "aya-ebpf", 95 | "aya-log-ebpf", 96 | "ghostwire-common", 97 | "network-types", 98 | ] 99 | 100 | [[package]] 101 | name = "network-types" 102 | version = "0.0.7" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "e82e9f64c09f56aa7c80c3fa087997bd99a913f91d9c74d36cf5fd75dd5773e6" 105 | 106 | [[package]] 107 | name = "num_enum" 108 | version = "0.7.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 111 | dependencies = [ 112 | "num_enum_derive", 113 | ] 114 | 115 | [[package]] 116 | name = "num_enum_derive" 117 | version = "0.7.3" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 120 | dependencies = [ 121 | "proc-macro2", 122 | "quote", 123 | "syn", 124 | ] 125 | 126 | [[package]] 127 | name = "proc-macro-error" 128 | version = "1.0.4" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 131 | dependencies = [ 132 | "proc-macro-error-attr", 133 | "proc-macro2", 134 | "quote", 135 | "version_check", 136 | ] 137 | 138 | [[package]] 139 | name = "proc-macro-error-attr" 140 | version = "1.0.4" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 143 | dependencies = [ 144 | "proc-macro2", 145 | "quote", 146 | "version_check", 147 | ] 148 | 149 | [[package]] 150 | name = "proc-macro2" 151 | version = "1.0.86" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 154 | dependencies = [ 155 | "unicode-ident", 156 | ] 157 | 158 | [[package]] 159 | name = "quote" 160 | version = "1.0.37" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 163 | dependencies = [ 164 | "proc-macro2", 165 | ] 166 | 167 | [[package]] 168 | name = "rustversion" 169 | version = "1.0.17" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 172 | 173 | [[package]] 174 | name = "syn" 175 | version = "2.0.79" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 178 | dependencies = [ 179 | "proc-macro2", 180 | "quote", 181 | "unicode-ident", 182 | ] 183 | 184 | [[package]] 185 | name = "unicode-ident" 186 | version = "1.0.13" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 189 | 190 | [[package]] 191 | name = "version_check" 192 | version = "0.9.5" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 195 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/prometheus.rs: -------------------------------------------------------------------------------- 1 | use std::convert::Infallible; 2 | 3 | use crate::OVERALL_STATE; 4 | use anyhow::Context; 5 | use bytes::Bytes; 6 | use http_body_util::Full; 7 | use hyper::{ 8 | server::conn::http1, 9 | service::service_fn, 10 | Request, 11 | Response, 12 | StatusCode, 13 | }; 14 | use hyper_util::rt::TokioIo; 15 | use prometheus::{ 16 | Encoder, 17 | IntCounterVec, 18 | Registry, 19 | TextEncoder, 20 | }; 21 | use std::net::SocketAddr; 22 | use tokio::net::TcpListener; 23 | 24 | use super::state::PromCounters; 25 | 26 | /// Create the Prometheus counters and Registry. 27 | pub fn create_prometheus_counters() -> anyhow::Result { 28 | let registry = Registry::new(); 29 | 30 | let rule_evaluated = IntCounterVec::new( 31 | prometheus::Opts::new( 32 | "gw_rule_evaluated", 33 | "The number of times a rule was evaluated", 34 | ), 35 | &["rule_id"], 36 | )?; 37 | registry.register(Box::new(rule_evaluated.clone()))?; 38 | 39 | let rule_passed = IntCounterVec::new( 40 | prometheus::Opts::new( 41 | "gw_rule_passed", 42 | "The number of times a rule allowed traffic", 43 | ), 44 | &["rule_id"], 45 | )?; 46 | registry.register(Box::new(rule_passed.clone()))?; 47 | 48 | let xdp_action = IntCounterVec::new( 49 | prometheus::Opts::new( 50 | "gw_xdp_action", 51 | "The number of times an XDP action was taken", 52 | ), 53 | &["action"], 54 | )?; 55 | registry.register(Box::new(xdp_action.clone()))?; 56 | 57 | let tc_action = IntCounterVec::new( 58 | prometheus::Opts::new("gw_tc_action", "The number of times a TC action was taken"), 59 | &["action"], 60 | )?; 61 | registry.register(Box::new(tc_action.clone()))?; 62 | 63 | Ok(PromCounters { 64 | registry, 65 | rule_evaluated, 66 | rule_passed, 67 | xdp_action, 68 | tc_action, 69 | }) 70 | } 71 | 72 | /// Pull the maps and update the Prometheus metrics. Designed to be run as a task. 73 | pub async fn prometheus_metrics() { 74 | let overall_state = OVERALL_STATE.read().await; 75 | 76 | if let Some(state) = &overall_state.state { 77 | for (key, value) in state.rule_analytic_map.iter().flatten() { 78 | let evaluated_diff = value.evaluated 79 | - overall_state 80 | .counters 81 | .rule_evaluated 82 | .with_label_values(&[&key.to_string()]) 83 | .get() as u128; 84 | 85 | overall_state 86 | .counters 87 | .rule_evaluated 88 | .with_label_values(&[&key.to_string()]) 89 | .inc_by(evaluated_diff as u64); 90 | 91 | let passed_diff = value.passed 92 | - overall_state 93 | .counters 94 | .rule_passed 95 | .with_label_values(&[&key.to_string()]) 96 | .get() as u128; 97 | 98 | overall_state 99 | .counters 100 | .rule_passed 101 | .with_label_values(&[&key.to_string()]) 102 | .inc_by(passed_diff as u64); 103 | } 104 | 105 | for (key, value) in state.xdp_analytic_map.iter().flatten() { 106 | overall_state 107 | .counters 108 | .xdp_action 109 | .with_label_values(&[xdp_action_to_string(key)]) 110 | .inc_by(value as u64); 111 | } 112 | 113 | for (key, value) in state.tc_analytic_map.iter().flatten() { 114 | overall_state 115 | .counters 116 | .tc_action 117 | .with_label_values(&[tc_action_to_string(key)]) 118 | .inc_by(value as u64); 119 | } 120 | } 121 | } 122 | 123 | /// Convert an XDP action to a string. 124 | /// @see https://docs.aya-rs.dev/aya_ebpf/bindings/xdp_action/ 125 | fn xdp_action_to_string(action: u32) -> &'static str { 126 | match action { 127 | 0 => "XDP_ABORTED", 128 | 1 => "XDP_DROP", 129 | 2 => "XDP_PASS", 130 | 3 => "XDP_TX", 131 | 4 => "XDP_REDIRECT", 132 | _ => "Unknown", 133 | } 134 | } 135 | 136 | /// Convert a TC action to a string. 137 | fn tc_action_to_string(action: i32) -> &'static str { 138 | match action { 139 | 0 => "TC_ACT_OK", 140 | 2 => "TC_ACT_SHOT", 141 | 3 => "TC_ACT_PIPE", 142 | _ => "Unknown", 143 | } 144 | } 145 | 146 | pub async fn handle_prom_listener() -> anyhow::Result<()> { 147 | // TODO: implement different ports 148 | let socket_addr: SocketAddr = "127.0.0.1:4343".parse()?; 149 | 150 | let listener = TcpListener::bind(socket_addr) 151 | .await 152 | .context("could not listen on port 4343")?; 153 | 154 | tracing::info!("Prometheus is starting on port 4343"); 155 | 156 | loop { 157 | let (stream, _ip) = listener.accept().await?; 158 | 159 | let io = TokioIo::new(stream); 160 | 161 | tokio::task::spawn(async move { 162 | if let Err(err) = http1::Builder::new() 163 | .keep_alive(false) 164 | .serve_connection( 165 | io, 166 | service_fn(|req: Request| prom_http(req)), 167 | ) 168 | .await 169 | { 170 | tracing::warn!("error serving connection: {:?}", err); 171 | } 172 | }); 173 | } 174 | } 175 | 176 | /// Expose Prometheus metrics 177 | pub async fn prom_http( 178 | _: Request, 179 | ) -> Result>, Infallible> { 180 | let overall_state = OVERALL_STATE.read().await; 181 | let metric_families = overall_state.counters.registry.gather(); 182 | // Drop the reference to the state, as we don't need it for the preceeding IO. 183 | drop(overall_state); 184 | 185 | let encoder = TextEncoder::new(); 186 | let mut buffer = vec![]; 187 | 188 | match encoder.encode(&metric_families, &mut buffer) { 189 | Ok(()) => Ok(Response::new(Full::new(Bytes::from(buffer)))), 190 | Err(err) => { 191 | tracing::warn!("Failed to encode prometheus metrics: {err:?}"); 192 | 193 | let mut err_resp = Response::new(Full::new(Bytes::from( 194 | "Failed to encode prometheus metrics", 195 | ))); 196 | 197 | *err_resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; 198 | 199 | Ok(err_resp) 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire/src/utils/socket.rs: -------------------------------------------------------------------------------- 1 | use super::ebpf::{ 2 | load_ebpf, 3 | unload_ebpf, 4 | }; 5 | use crate::OVERALL_STATE; 6 | use ghostwire_types::{ 7 | ClientMessage, 8 | ClientReqType, 9 | Rule, 10 | ServerMessage, 11 | }; 12 | use std::{ 13 | io::{ 14 | Read, 15 | Write, 16 | }, 17 | os::unix::net::{ 18 | UnixListener, 19 | UnixStream, 20 | }, 21 | }; 22 | 23 | /// Listen on the socket for client requests from the CLI 24 | pub async fn socket_server() -> anyhow::Result<()> { 25 | // delete a socket that could exist currently 26 | let _ = std::fs::remove_file("/tmp/ghostwire.sock"); 27 | 28 | let listener = UnixListener::bind("/tmp/ghostwire.sock").expect("Failed to bind socket"); 29 | 30 | for stream in listener.incoming() { 31 | match stream { 32 | Ok(stream) => { 33 | if let Err(e) = handle_stream(stream).await { 34 | tracing::error!("Failed to handle stream: {:?}", e); 35 | }; 36 | } 37 | Err(err) => { 38 | tracing::error!("Failed to accept connection: {:?}", err); 39 | } 40 | } 41 | } 42 | 43 | Ok(()) 44 | } 45 | 46 | /// Run the listener and handle new messages 47 | async fn handle_stream(mut stream: UnixStream) -> anyhow::Result<()> { 48 | let mut buffer = [0; 1024]; 49 | 50 | match stream.read(&mut buffer) { 51 | Ok(size) => { 52 | let received_data = &buffer[..size]; 53 | match serde_json::from_slice::(received_data) { 54 | Ok(message) => { 55 | handle_server_request(message, stream).await?; 56 | } 57 | Err(err) => { 58 | anyhow::bail!("Failed to parse JSON: {}", err); 59 | } 60 | } 61 | } 62 | Err(err) => { 63 | anyhow::bail!("Failed to read from socket: {}", err); 64 | } 65 | } 66 | 67 | Ok(()) 68 | } 69 | 70 | /// Handle a message from the socket client. 71 | async fn handle_server_request( 72 | message: ClientMessage, 73 | mut stream: UnixStream, 74 | ) -> anyhow::Result<()> { 75 | let resp = match handle_server_request_fallible(message).await { 76 | Ok(t) => t, 77 | Err(e) => ServerMessage { 78 | request_success: false, 79 | message: format!("{}", e), 80 | }, 81 | }; 82 | 83 | let response_data = serde_json::to_vec(&resp)?; 84 | stream.write_all(&response_data)?; 85 | 86 | Ok(()) 87 | } 88 | 89 | /// Once parsed to a ClientMessage, handle the request 90 | async fn handle_server_request_fallible(message: ClientMessage) -> anyhow::Result { 91 | match message.req_type { 92 | ClientReqType::STATUS => handle_status_request().await, 93 | ClientReqType::RULES => { 94 | handle_load( 95 | message.rules.ok_or(anyhow::anyhow!( 96 | "request to change rules didn't include rules" 97 | ))?, 98 | message.interface.ok_or(anyhow::anyhow!( 99 | "request to change rules didn't include the interface" 100 | ))?, 101 | ) 102 | .await 103 | } 104 | ClientReqType::ENABLE => { 105 | handle_enable(message.interface.ok_or(anyhow::anyhow!( 106 | "enable message didn't include the interface" 107 | ))?) 108 | .await 109 | } 110 | ClientReqType::DISABLE => handle_disable().await, 111 | } 112 | } 113 | 114 | /// Handle a status request from the client 115 | async fn handle_status_request() -> anyhow::Result { 116 | let overall_status = OVERALL_STATE.read().await; 117 | 118 | Ok(ServerMessage { 119 | request_success: true, 120 | message: overall_status.fmt().await, 121 | }) 122 | } 123 | 124 | /// Handle the modification of rules. The client will send the full list of rules, to which we will 125 | /// replace the map. 126 | async fn handle_load(rules: Vec, interface: String) -> anyhow::Result { 127 | // Find if the firewall is enabled. 128 | let mut enabled = false; 129 | 130 | { 131 | enabled = OVERALL_STATE.read().await.enabled; 132 | } 133 | 134 | if !enabled { 135 | handle_enable(interface).await?; 136 | } 137 | 138 | let overall_status = OVERALL_STATE.read().await; 139 | 140 | match &overall_status.state { 141 | Some(state) => { 142 | // eBPF maps are super limited in what they can do in comparison to a HashMap from the standard 143 | // library, so instead of being able to clear the map, 144 | // we'll have to sauce it up 145 | let mut map = state.rule_map.write().await; 146 | 147 | let map_len = map.iter().collect::>().len(); 148 | 149 | // we use index as key 150 | for i in 0..map_len { 151 | map.remove(&(i as u32))?; 152 | } 153 | 154 | // insert the new rules 155 | for (i, rule) in rules.iter().enumerate() { 156 | map.insert(i as u32, convert_rule(*rule), 0)?; 157 | } 158 | 159 | Ok(ServerMessage { 160 | request_success: true, 161 | message: "Rules updated".to_string(), 162 | }) 163 | } 164 | None => { 165 | anyhow::bail!("Firewall is not enabled"); 166 | } 167 | } 168 | } 169 | 170 | /// Handle the enabling of the firewall. 171 | async fn handle_enable(interface: String) -> anyhow::Result { 172 | { 173 | let overall_status = OVERALL_STATE.read().await; 174 | 175 | if overall_status.enabled || overall_status.state.is_some() { 176 | anyhow::bail!("Firewall is already enabled"); 177 | } 178 | } 179 | 180 | load_ebpf(vec![], interface).await?; 181 | 182 | { 183 | let mut overall_status = OVERALL_STATE.write().await; 184 | 185 | overall_status.enabled = true; 186 | } 187 | 188 | Ok(ServerMessage { 189 | request_success: true, 190 | message: "Firewall enabled".to_string(), 191 | }) 192 | } 193 | 194 | /// Handle the disabling of the firewall. 195 | async fn handle_disable() -> anyhow::Result { 196 | { 197 | let overall_status = OVERALL_STATE.read().await; 198 | 199 | if !overall_status.enabled || overall_status.state.is_none() { 200 | anyhow::bail!("Firewall is already disabled"); 201 | } 202 | } 203 | 204 | unload_ebpf().await; 205 | 206 | { 207 | let mut overall_status = OVERALL_STATE.write().await; 208 | 209 | overall_status.enabled = false; 210 | } 211 | 212 | Ok(ServerMessage { 213 | request_success: true, 214 | message: "Firewall disabled".to_string(), 215 | }) 216 | } 217 | 218 | /// Convert a rule from the common format to the eBPF format for insertion into the map. 219 | fn convert_rule(rule: Rule) -> ghostwire_common::Rule { 220 | ghostwire_common::Rule { 221 | id: rule.id, 222 | source_start_ip: rule.source_start_ip, 223 | source_end_ip: rule.source_end_ip, 224 | destination_start_ip: rule.destination_start_ip, 225 | destination_end_ip: rule.destination_end_ip, 226 | protocol_number: rule.protocol_number, 227 | port_number: rule.port_number, 228 | ratelimiting: rule.ratelimiting, 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/ebpf/ghostwire-ebpf/src/handlers/ingress.rs: -------------------------------------------------------------------------------- 1 | use aya_ebpf::{ 2 | bindings::xdp_action::{ 3 | XDP_ABORTED, 4 | XDP_DROP, 5 | XDP_PASS, 6 | }, 7 | helpers::bpf_ktime_get_ns, 8 | programs::XdpContext, 9 | }; 10 | use network_types::{ 11 | eth::EthHdr, 12 | ip::{ 13 | IpProto::{ 14 | Icmp, 15 | Tcp, 16 | Udp, 17 | }, 18 | Ipv4Hdr, 19 | }, 20 | tcp::TcpHdr, 21 | udp::UdpHdr, 22 | }; 23 | 24 | use crate::{ 25 | utils::ptr_at::xdp_ptr_at_fallible, 26 | HOLEPUNCHED, 27 | RATELIMITING, 28 | RULES, 29 | RULE_ANALYTICS, 30 | }; 31 | use ghostwire_common::RuleAnalytics; 32 | 33 | /// The function called whenever a packet enters through the wire. This should: 34 | /// 1. Parse the packet; 35 | /// - Letting the packet through if it's an internal protocol (like ARP) 36 | /// - Dropping or rejecting clearly malformed traffic 37 | /// 2. Look for rules; 38 | /// - Evaluating rules to see if they're applicable to this rule 39 | /// - Performing ratelimiting if the rule has it enabled 40 | /// 3. Look for entries that are holepunched; 41 | /// - Since we're a stateful firewall, look for when we established a connection outbound and allow that traffic back in 42 | /// - When connections are terminated (like if the client sends a FIN or RST to the port), remove from the holepunched map 43 | /// 4. Drop traffic 44 | /// - When traffic has made it to this point, it's not whitelisted or holepunched. Since we're (at least currently) a default-drop firewall, drop it. 45 | pub unsafe fn ghostwire_ingress_fallible(ctx: XdpContext) -> Result { 46 | // skip the ethernet header, that's not providing us with any value right now 47 | // attempt to parse the ip header 48 | let ip_header: *const Ipv4Hdr = 49 | xdp_ptr_at_fallible(&ctx, EthHdr::LEN).map_err(|_| XDP_ABORTED)?; 50 | 51 | // pull the source and destination IP addresses and the protocol 52 | let src_ip = unsafe { (*ip_header).src_addr }; 53 | let dst_ip = unsafe { (*ip_header).dst_addr }; 54 | let protocol = unsafe { (*ip_header).proto }; 55 | let (src_port, dst_port) = match protocol { 56 | Tcp => { 57 | // parse the TCP header 58 | let tcp_header: *const TcpHdr = 59 | xdp_ptr_at_fallible::(&ctx, EthHdr::LEN + Ipv4Hdr::LEN) 60 | .map_err(|_| XDP_ABORTED)?; 61 | 62 | // get the source and destination ports 63 | ((*tcp_header).source, (*tcp_header).dest) 64 | } 65 | Udp => { 66 | // parse the UDP header 67 | let udp_header: *const UdpHdr = 68 | xdp_ptr_at_fallible::(&ctx, EthHdr::LEN + Ipv4Hdr::LEN) 69 | .map_err(|_| XDP_ABORTED)?; 70 | 71 | // get the source and destination ports 72 | ((*udp_header).source, (*udp_header).dest) 73 | } 74 | Icmp => (0, 0), 75 | // for now, we're only supporting TCP and UDP 76 | // let everything else in 77 | _ => return Ok(XDP_PASS), 78 | }; 79 | 80 | // the index of where we are in the map 81 | // we're using maps and not an array because arrays are immutable, meanwhile we can update maps 82 | // on the fly 83 | for index in 0..100 { 84 | if let Some(rule) = RULES.get(&index) { 85 | if src_ip >= rule.source_start_ip && src_ip <= rule.source_end_ip { 86 | // Determine if should perform a protocol check. 87 | if rule.protocol_number != 0 { 88 | if rule.protocol_number != protocol as u8 { 89 | continue; 90 | } 91 | 92 | // Compare port if relevant. 93 | if rule.port_number != 0 && rule.port_number != dst_port { 94 | continue; 95 | } 96 | } 97 | 98 | // Indicate we have evaulated this rule. 99 | match RULE_ANALYTICS.get_ptr_mut(&rule.id) { 100 | Some(analytics) => { 101 | (*analytics).evaluated += 1; 102 | } 103 | None => { 104 | let _ = RULE_ANALYTICS.insert( 105 | &rule.id, 106 | &RuleAnalytics { 107 | rule_id: rule.id, 108 | evaluated: 1, 109 | passed: 0, 110 | }, 111 | 0, 112 | ); 113 | } 114 | } 115 | 116 | // Determine if we should perform ratelimiting. 117 | if rule.ratelimiting != 0 { 118 | // Create a ratelimit key. This is a combination of the source IP and the rule ID. 119 | let key = (src_ip + rule.id) as u64; 120 | // Fetch and increment ratelimit value for this key. 121 | let current_value = match RATELIMITING.get_ptr_mut(&key) { 122 | Some(value) => { 123 | *value += 1; 124 | *value - 1 125 | } 126 | None => { 127 | let _ = RATELIMITING.insert(&key, &1, 0); 128 | 0 129 | } 130 | }; 131 | 132 | // If we've exceeded the ratelimiting, drop the packet, and record the action 133 | if rule.ratelimiting as u64 >= current_value { 134 | match RULE_ANALYTICS.get_ptr_mut(&rule.id) { 135 | Some(analytics) => { 136 | (*analytics).passed += 1; 137 | } 138 | None => { 139 | let _ = RULE_ANALYTICS.insert( 140 | &rule.id, 141 | &RuleAnalytics { 142 | rule_id: rule.id, 143 | evaluated: 0, 144 | passed: 1, 145 | }, 146 | 0, 147 | ); 148 | } 149 | } 150 | } else { 151 | return Ok(XDP_DROP); 152 | } 153 | } 154 | 155 | match RULE_ANALYTICS.get_ptr_mut(&rule.id) { 156 | Some(analytics) => { 157 | (*analytics).passed += 1; 158 | } 159 | None => { 160 | let _ = RULE_ANALYTICS.insert( 161 | &rule.id, 162 | &RuleAnalytics { 163 | rule_id: rule.id, 164 | evaluated: 0, 165 | passed: 1, 166 | }, 167 | 0, 168 | ); 169 | } 170 | } 171 | 172 | // Packet passed protocol conformity checks and ratelimit (if enabled) 173 | return Ok(XDP_PASS); 174 | } 175 | } else { 176 | break; 177 | } 178 | } 179 | 180 | // Create a key for the holepunched map, upgrading the type to u64 to avoid overflow 181 | let key = src_ip as u64 + src_port as u64 + dst_ip as u64 + dst_port as u64; 182 | 183 | match HOLEPUNCHED.get_ptr_mut(&key) { 184 | Some(last_time) => { 185 | // Update the time of the connection. 186 | (*last_time) = bpf_ktime_get_ns(); 187 | 188 | Ok(XDP_PASS) 189 | } 190 | None => { 191 | // Drop the connection if no other case is met. 192 | Ok(XDP_DROP) 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/cli/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.8" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 40 | dependencies = [ 41 | "windows-sys 0.52.0", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.4" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 49 | dependencies = [ 50 | "anstyle", 51 | "windows-sys 0.52.0", 52 | ] 53 | 54 | [[package]] 55 | name = "anyhow" 56 | version = "1.0.89" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 59 | 60 | [[package]] 61 | name = "clap" 62 | version = "4.5.18" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" 65 | dependencies = [ 66 | "clap_builder", 67 | ] 68 | 69 | [[package]] 70 | name = "clap_builder" 71 | version = "4.5.18" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" 74 | dependencies = [ 75 | "anstream", 76 | "anstyle", 77 | "clap_lex", 78 | "strsim", 79 | ] 80 | 81 | [[package]] 82 | name = "clap_lex" 83 | version = "0.7.2" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 86 | 87 | [[package]] 88 | name = "cli" 89 | version = "0.1.0" 90 | dependencies = [ 91 | "anyhow", 92 | "clap", 93 | "colored", 94 | "ghostwire_types", 95 | "serde", 96 | "serde_json", 97 | "serde_yaml", 98 | ] 99 | 100 | [[package]] 101 | name = "colorchoice" 102 | version = "1.0.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 105 | 106 | [[package]] 107 | name = "colored" 108 | version = "2.1.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 111 | dependencies = [ 112 | "lazy_static", 113 | "windows-sys 0.48.0", 114 | ] 115 | 116 | [[package]] 117 | name = "equivalent" 118 | version = "1.0.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 121 | 122 | [[package]] 123 | name = "ghostwire_types" 124 | version = "0.2.1" 125 | dependencies = [ 126 | "serde", 127 | "serde_json", 128 | ] 129 | 130 | [[package]] 131 | name = "hashbrown" 132 | version = "0.15.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 135 | 136 | [[package]] 137 | name = "indexmap" 138 | version = "2.6.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 141 | dependencies = [ 142 | "equivalent", 143 | "hashbrown", 144 | ] 145 | 146 | [[package]] 147 | name = "is_terminal_polyfill" 148 | version = "1.70.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 151 | 152 | [[package]] 153 | name = "itoa" 154 | version = "1.0.11" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 157 | 158 | [[package]] 159 | name = "lazy_static" 160 | version = "1.5.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 163 | 164 | [[package]] 165 | name = "memchr" 166 | version = "2.7.4" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 169 | 170 | [[package]] 171 | name = "proc-macro2" 172 | version = "1.0.86" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 175 | dependencies = [ 176 | "unicode-ident", 177 | ] 178 | 179 | [[package]] 180 | name = "quote" 181 | version = "1.0.37" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 184 | dependencies = [ 185 | "proc-macro2", 186 | ] 187 | 188 | [[package]] 189 | name = "ryu" 190 | version = "1.0.18" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 193 | 194 | [[package]] 195 | name = "serde" 196 | version = "1.0.210" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 199 | dependencies = [ 200 | "serde_derive", 201 | ] 202 | 203 | [[package]] 204 | name = "serde_derive" 205 | version = "1.0.210" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 208 | dependencies = [ 209 | "proc-macro2", 210 | "quote", 211 | "syn", 212 | ] 213 | 214 | [[package]] 215 | name = "serde_json" 216 | version = "1.0.128" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 219 | dependencies = [ 220 | "itoa", 221 | "memchr", 222 | "ryu", 223 | "serde", 224 | ] 225 | 226 | [[package]] 227 | name = "serde_yaml" 228 | version = "0.9.34+deprecated" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 231 | dependencies = [ 232 | "indexmap", 233 | "itoa", 234 | "ryu", 235 | "serde", 236 | "unsafe-libyaml", 237 | ] 238 | 239 | [[package]] 240 | name = "strsim" 241 | version = "0.11.1" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 244 | 245 | [[package]] 246 | name = "syn" 247 | version = "2.0.79" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 250 | dependencies = [ 251 | "proc-macro2", 252 | "quote", 253 | "unicode-ident", 254 | ] 255 | 256 | [[package]] 257 | name = "unicode-ident" 258 | version = "1.0.13" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 261 | 262 | [[package]] 263 | name = "unsafe-libyaml" 264 | version = "0.2.11" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 267 | 268 | [[package]] 269 | name = "utf8parse" 270 | version = "0.2.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 273 | 274 | [[package]] 275 | name = "windows-sys" 276 | version = "0.48.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 279 | dependencies = [ 280 | "windows-targets 0.48.5", 281 | ] 282 | 283 | [[package]] 284 | name = "windows-sys" 285 | version = "0.52.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 288 | dependencies = [ 289 | "windows-targets 0.52.6", 290 | ] 291 | 292 | [[package]] 293 | name = "windows-targets" 294 | version = "0.48.5" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 297 | dependencies = [ 298 | "windows_aarch64_gnullvm 0.48.5", 299 | "windows_aarch64_msvc 0.48.5", 300 | "windows_i686_gnu 0.48.5", 301 | "windows_i686_msvc 0.48.5", 302 | "windows_x86_64_gnu 0.48.5", 303 | "windows_x86_64_gnullvm 0.48.5", 304 | "windows_x86_64_msvc 0.48.5", 305 | ] 306 | 307 | [[package]] 308 | name = "windows-targets" 309 | version = "0.52.6" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 312 | dependencies = [ 313 | "windows_aarch64_gnullvm 0.52.6", 314 | "windows_aarch64_msvc 0.52.6", 315 | "windows_i686_gnu 0.52.6", 316 | "windows_i686_gnullvm", 317 | "windows_i686_msvc 0.52.6", 318 | "windows_x86_64_gnu 0.52.6", 319 | "windows_x86_64_gnullvm 0.52.6", 320 | "windows_x86_64_msvc 0.52.6", 321 | ] 322 | 323 | [[package]] 324 | name = "windows_aarch64_gnullvm" 325 | version = "0.48.5" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 328 | 329 | [[package]] 330 | name = "windows_aarch64_gnullvm" 331 | version = "0.52.6" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 334 | 335 | [[package]] 336 | name = "windows_aarch64_msvc" 337 | version = "0.48.5" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 340 | 341 | [[package]] 342 | name = "windows_aarch64_msvc" 343 | version = "0.52.6" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 346 | 347 | [[package]] 348 | name = "windows_i686_gnu" 349 | version = "0.48.5" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 352 | 353 | [[package]] 354 | name = "windows_i686_gnu" 355 | version = "0.52.6" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 358 | 359 | [[package]] 360 | name = "windows_i686_gnullvm" 361 | version = "0.52.6" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 364 | 365 | [[package]] 366 | name = "windows_i686_msvc" 367 | version = "0.48.5" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 370 | 371 | [[package]] 372 | name = "windows_i686_msvc" 373 | version = "0.52.6" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 376 | 377 | [[package]] 378 | name = "windows_x86_64_gnu" 379 | version = "0.48.5" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 382 | 383 | [[package]] 384 | name = "windows_x86_64_gnu" 385 | version = "0.52.6" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 388 | 389 | [[package]] 390 | name = "windows_x86_64_gnullvm" 391 | version = "0.48.5" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 394 | 395 | [[package]] 396 | name = "windows_x86_64_gnullvm" 397 | version = "0.52.6" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 400 | 401 | [[package]] 402 | name = "windows_x86_64_msvc" 403 | version = "0.48.5" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 406 | 407 | [[package]] 408 | name = "windows_x86_64_msvc" 409 | version = "0.52.6" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 412 | -------------------------------------------------------------------------------- /src/ebpf/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "allocator-api2" 43 | version = "0.2.18" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 46 | 47 | [[package]] 48 | name = "android-tzdata" 49 | version = "0.1.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 52 | 53 | [[package]] 54 | name = "android_system_properties" 55 | version = "0.1.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 58 | dependencies = [ 59 | "libc", 60 | ] 61 | 62 | [[package]] 63 | name = "anstream" 64 | version = "0.6.15" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 67 | dependencies = [ 68 | "anstyle", 69 | "anstyle-parse", 70 | "anstyle-query", 71 | "anstyle-wincon", 72 | "colorchoice", 73 | "is_terminal_polyfill", 74 | "utf8parse", 75 | ] 76 | 77 | [[package]] 78 | name = "anstyle" 79 | version = "1.0.8" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 82 | 83 | [[package]] 84 | name = "anstyle-parse" 85 | version = "0.2.5" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 88 | dependencies = [ 89 | "utf8parse", 90 | ] 91 | 92 | [[package]] 93 | name = "anstyle-query" 94 | version = "1.1.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 97 | dependencies = [ 98 | "windows-sys 0.52.0", 99 | ] 100 | 101 | [[package]] 102 | name = "anstyle-wincon" 103 | version = "3.0.4" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 106 | dependencies = [ 107 | "anstyle", 108 | "windows-sys 0.52.0", 109 | ] 110 | 111 | [[package]] 112 | name = "anyhow" 113 | version = "1.0.89" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 116 | 117 | [[package]] 118 | name = "assert_matches" 119 | version = "1.5.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" 122 | 123 | [[package]] 124 | name = "atomic-waker" 125 | version = "1.1.2" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 128 | 129 | [[package]] 130 | name = "autocfg" 131 | version = "1.4.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 134 | 135 | [[package]] 136 | name = "aya" 137 | version = "0.12.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "90eea657cc8028447cbda5068f4e10c4fadba0131624f4f7dd1a9c46ffc8d81f" 140 | dependencies = [ 141 | "assert_matches", 142 | "aya-obj", 143 | "bitflags", 144 | "bytes", 145 | "lazy_static", 146 | "libc", 147 | "log", 148 | "object 0.32.2", 149 | "thiserror", 150 | "tokio", 151 | ] 152 | 153 | [[package]] 154 | name = "aya-log" 155 | version = "0.2.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1f11a92f305b983e9f53433457dede617a4ad0aa22e4702220092f39e844c1a2" 158 | dependencies = [ 159 | "aya", 160 | "aya-log-common", 161 | "bytes", 162 | "log", 163 | "thiserror", 164 | "tokio", 165 | ] 166 | 167 | [[package]] 168 | name = "aya-log-common" 169 | version = "0.1.14" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "b6d38a351ee2d5dc24e04cac6184b1b39408642d9a8b585892c99146f8dd4edb" 172 | dependencies = [ 173 | "num_enum", 174 | ] 175 | 176 | [[package]] 177 | name = "aya-obj" 178 | version = "0.1.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "2c02024a307161cf3d1f052161958fd13b1a33e3e038083e58082c0700fdab85" 181 | dependencies = [ 182 | "bytes", 183 | "core-error", 184 | "hashbrown 0.14.5", 185 | "log", 186 | "object 0.32.2", 187 | "thiserror", 188 | ] 189 | 190 | [[package]] 191 | name = "backtrace" 192 | version = "0.3.74" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 195 | dependencies = [ 196 | "addr2line", 197 | "cfg-if", 198 | "libc", 199 | "miniz_oxide", 200 | "object 0.36.4", 201 | "rustc-demangle", 202 | "windows-targets", 203 | ] 204 | 205 | [[package]] 206 | name = "bitflags" 207 | version = "2.6.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 210 | 211 | [[package]] 212 | name = "bumpalo" 213 | version = "3.16.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 216 | 217 | [[package]] 218 | name = "bytes" 219 | version = "1.7.2" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" 222 | 223 | [[package]] 224 | name = "cc" 225 | version = "1.1.25" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" 228 | dependencies = [ 229 | "shlex", 230 | ] 231 | 232 | [[package]] 233 | name = "cfg-if" 234 | version = "1.0.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 237 | 238 | [[package]] 239 | name = "chrono" 240 | version = "0.4.38" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 243 | dependencies = [ 244 | "android-tzdata", 245 | "iana-time-zone", 246 | "js-sys", 247 | "num-traits", 248 | "wasm-bindgen", 249 | "windows-targets", 250 | ] 251 | 252 | [[package]] 253 | name = "clap" 254 | version = "4.5.18" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" 257 | dependencies = [ 258 | "clap_builder", 259 | "clap_derive", 260 | ] 261 | 262 | [[package]] 263 | name = "clap_builder" 264 | version = "4.5.18" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" 267 | dependencies = [ 268 | "anstream", 269 | "anstyle", 270 | "clap_lex", 271 | "strsim", 272 | ] 273 | 274 | [[package]] 275 | name = "clap_derive" 276 | version = "4.5.18" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 279 | dependencies = [ 280 | "heck", 281 | "proc-macro2", 282 | "quote", 283 | "syn", 284 | ] 285 | 286 | [[package]] 287 | name = "clap_lex" 288 | version = "0.7.2" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 291 | 292 | [[package]] 293 | name = "colorchoice" 294 | version = "1.0.2" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 297 | 298 | [[package]] 299 | name = "core-error" 300 | version = "0.0.0" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "efcdb2972eb64230b4c50646d8498ff73f5128d196a90c7236eec4cbe8619b8f" 303 | dependencies = [ 304 | "version_check", 305 | ] 306 | 307 | [[package]] 308 | name = "core-foundation-sys" 309 | version = "0.8.7" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 312 | 313 | [[package]] 314 | name = "env_logger" 315 | version = "0.10.2" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" 318 | dependencies = [ 319 | "humantime", 320 | "is-terminal", 321 | "log", 322 | "regex", 323 | "termcolor", 324 | ] 325 | 326 | [[package]] 327 | name = "equivalent" 328 | version = "1.0.1" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 331 | 332 | [[package]] 333 | name = "fnv" 334 | version = "1.0.7" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 337 | 338 | [[package]] 339 | name = "futures-channel" 340 | version = "0.3.31" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 343 | dependencies = [ 344 | "futures-core", 345 | ] 346 | 347 | [[package]] 348 | name = "futures-core" 349 | version = "0.3.31" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 352 | 353 | [[package]] 354 | name = "futures-sink" 355 | version = "0.3.31" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 358 | 359 | [[package]] 360 | name = "futures-task" 361 | version = "0.3.31" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 364 | 365 | [[package]] 366 | name = "futures-util" 367 | version = "0.3.31" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 370 | dependencies = [ 371 | "futures-core", 372 | "futures-task", 373 | "pin-project-lite", 374 | "pin-utils", 375 | ] 376 | 377 | [[package]] 378 | name = "ghostwire" 379 | version = "0.1.0" 380 | dependencies = [ 381 | "anyhow", 382 | "aya", 383 | "aya-log", 384 | "bytes", 385 | "clap", 386 | "env_logger", 387 | "ghostwire-common", 388 | "ghostwire_types", 389 | "http-body-util", 390 | "hyper", 391 | "hyper-util", 392 | "lazy_static", 393 | "libc", 394 | "log", 395 | "prometheus", 396 | "serde", 397 | "serde_json", 398 | "tokio", 399 | "tokio_schedule", 400 | "tracing", 401 | "tracing-subscriber", 402 | "yaml-rust", 403 | ] 404 | 405 | [[package]] 406 | name = "ghostwire-common" 407 | version = "0.1.0" 408 | dependencies = [ 409 | "aya", 410 | ] 411 | 412 | [[package]] 413 | name = "ghostwire_types" 414 | version = "0.2.1" 415 | dependencies = [ 416 | "serde", 417 | "serde_json", 418 | ] 419 | 420 | [[package]] 421 | name = "gimli" 422 | version = "0.31.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" 425 | 426 | [[package]] 427 | name = "h2" 428 | version = "0.4.6" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" 431 | dependencies = [ 432 | "atomic-waker", 433 | "bytes", 434 | "fnv", 435 | "futures-core", 436 | "futures-sink", 437 | "http", 438 | "indexmap", 439 | "slab", 440 | "tokio", 441 | "tokio-util", 442 | "tracing", 443 | ] 444 | 445 | [[package]] 446 | name = "hashbrown" 447 | version = "0.14.5" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 450 | dependencies = [ 451 | "ahash", 452 | "allocator-api2", 453 | ] 454 | 455 | [[package]] 456 | name = "hashbrown" 457 | version = "0.15.0" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 460 | 461 | [[package]] 462 | name = "heck" 463 | version = "0.5.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 466 | 467 | [[package]] 468 | name = "hermit-abi" 469 | version = "0.3.9" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 472 | 473 | [[package]] 474 | name = "hermit-abi" 475 | version = "0.4.0" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 478 | 479 | [[package]] 480 | name = "http" 481 | version = "1.1.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 484 | dependencies = [ 485 | "bytes", 486 | "fnv", 487 | "itoa", 488 | ] 489 | 490 | [[package]] 491 | name = "http-body" 492 | version = "1.0.1" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 495 | dependencies = [ 496 | "bytes", 497 | "http", 498 | ] 499 | 500 | [[package]] 501 | name = "http-body-util" 502 | version = "0.1.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 505 | dependencies = [ 506 | "bytes", 507 | "futures-util", 508 | "http", 509 | "http-body", 510 | "pin-project-lite", 511 | ] 512 | 513 | [[package]] 514 | name = "httparse" 515 | version = "1.9.5" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 518 | 519 | [[package]] 520 | name = "httpdate" 521 | version = "1.0.3" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 524 | 525 | [[package]] 526 | name = "humantime" 527 | version = "2.1.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 530 | 531 | [[package]] 532 | name = "hyper" 533 | version = "1.4.1" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" 536 | dependencies = [ 537 | "bytes", 538 | "futures-channel", 539 | "futures-util", 540 | "h2", 541 | "http", 542 | "http-body", 543 | "httparse", 544 | "httpdate", 545 | "itoa", 546 | "pin-project-lite", 547 | "smallvec", 548 | "tokio", 549 | "want", 550 | ] 551 | 552 | [[package]] 553 | name = "hyper-util" 554 | version = "0.1.9" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" 557 | dependencies = [ 558 | "bytes", 559 | "futures-channel", 560 | "futures-util", 561 | "http", 562 | "http-body", 563 | "hyper", 564 | "pin-project-lite", 565 | "socket2", 566 | "tokio", 567 | "tower-service", 568 | "tracing", 569 | ] 570 | 571 | [[package]] 572 | name = "iana-time-zone" 573 | version = "0.1.61" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 576 | dependencies = [ 577 | "android_system_properties", 578 | "core-foundation-sys", 579 | "iana-time-zone-haiku", 580 | "js-sys", 581 | "wasm-bindgen", 582 | "windows-core", 583 | ] 584 | 585 | [[package]] 586 | name = "iana-time-zone-haiku" 587 | version = "0.1.2" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 590 | dependencies = [ 591 | "cc", 592 | ] 593 | 594 | [[package]] 595 | name = "indexmap" 596 | version = "2.6.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 599 | dependencies = [ 600 | "equivalent", 601 | "hashbrown 0.15.0", 602 | ] 603 | 604 | [[package]] 605 | name = "is-terminal" 606 | version = "0.4.13" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" 609 | dependencies = [ 610 | "hermit-abi 0.4.0", 611 | "libc", 612 | "windows-sys 0.52.0", 613 | ] 614 | 615 | [[package]] 616 | name = "is_terminal_polyfill" 617 | version = "1.70.1" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 620 | 621 | [[package]] 622 | name = "itoa" 623 | version = "1.0.11" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 626 | 627 | [[package]] 628 | name = "js-sys" 629 | version = "0.3.70" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 632 | dependencies = [ 633 | "wasm-bindgen", 634 | ] 635 | 636 | [[package]] 637 | name = "lazy_static" 638 | version = "1.5.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 641 | 642 | [[package]] 643 | name = "libc" 644 | version = "0.2.159" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" 647 | 648 | [[package]] 649 | name = "linked-hash-map" 650 | version = "0.5.6" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 653 | 654 | [[package]] 655 | name = "lock_api" 656 | version = "0.4.12" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 659 | dependencies = [ 660 | "autocfg", 661 | "scopeguard", 662 | ] 663 | 664 | [[package]] 665 | name = "log" 666 | version = "0.4.22" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 669 | 670 | [[package]] 671 | name = "memchr" 672 | version = "2.7.4" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 675 | 676 | [[package]] 677 | name = "miniz_oxide" 678 | version = "0.8.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 681 | dependencies = [ 682 | "adler2", 683 | ] 684 | 685 | [[package]] 686 | name = "mio" 687 | version = "1.0.2" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 690 | dependencies = [ 691 | "hermit-abi 0.3.9", 692 | "libc", 693 | "wasi", 694 | "windows-sys 0.52.0", 695 | ] 696 | 697 | [[package]] 698 | name = "nu-ansi-term" 699 | version = "0.46.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 702 | dependencies = [ 703 | "overload", 704 | "winapi", 705 | ] 706 | 707 | [[package]] 708 | name = "num-traits" 709 | version = "0.2.19" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 712 | dependencies = [ 713 | "autocfg", 714 | ] 715 | 716 | [[package]] 717 | name = "num_enum" 718 | version = "0.7.3" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 721 | dependencies = [ 722 | "num_enum_derive", 723 | ] 724 | 725 | [[package]] 726 | name = "num_enum_derive" 727 | version = "0.7.3" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 730 | dependencies = [ 731 | "proc-macro2", 732 | "quote", 733 | "syn", 734 | ] 735 | 736 | [[package]] 737 | name = "object" 738 | version = "0.32.2" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 741 | dependencies = [ 742 | "memchr", 743 | ] 744 | 745 | [[package]] 746 | name = "object" 747 | version = "0.36.4" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" 750 | dependencies = [ 751 | "memchr", 752 | ] 753 | 754 | [[package]] 755 | name = "once_cell" 756 | version = "1.20.1" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" 759 | dependencies = [ 760 | "portable-atomic", 761 | ] 762 | 763 | [[package]] 764 | name = "overload" 765 | version = "0.1.1" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 768 | 769 | [[package]] 770 | name = "parking_lot" 771 | version = "0.12.3" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 774 | dependencies = [ 775 | "lock_api", 776 | "parking_lot_core", 777 | ] 778 | 779 | [[package]] 780 | name = "parking_lot_core" 781 | version = "0.9.10" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 784 | dependencies = [ 785 | "cfg-if", 786 | "libc", 787 | "redox_syscall", 788 | "smallvec", 789 | "windows-targets", 790 | ] 791 | 792 | [[package]] 793 | name = "pin-project-lite" 794 | version = "0.2.14" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 797 | 798 | [[package]] 799 | name = "pin-utils" 800 | version = "0.1.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 803 | 804 | [[package]] 805 | name = "portable-atomic" 806 | version = "1.9.0" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" 809 | 810 | [[package]] 811 | name = "proc-macro2" 812 | version = "1.0.86" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 815 | dependencies = [ 816 | "unicode-ident", 817 | ] 818 | 819 | [[package]] 820 | name = "prometheus" 821 | version = "0.13.4" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" 824 | dependencies = [ 825 | "cfg-if", 826 | "fnv", 827 | "lazy_static", 828 | "memchr", 829 | "parking_lot", 830 | "protobuf", 831 | "thiserror", 832 | ] 833 | 834 | [[package]] 835 | name = "protobuf" 836 | version = "2.28.0" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" 839 | 840 | [[package]] 841 | name = "quote" 842 | version = "1.0.37" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 845 | dependencies = [ 846 | "proc-macro2", 847 | ] 848 | 849 | [[package]] 850 | name = "redox_syscall" 851 | version = "0.5.7" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 854 | dependencies = [ 855 | "bitflags", 856 | ] 857 | 858 | [[package]] 859 | name = "regex" 860 | version = "1.11.0" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" 863 | dependencies = [ 864 | "aho-corasick", 865 | "memchr", 866 | "regex-automata", 867 | "regex-syntax", 868 | ] 869 | 870 | [[package]] 871 | name = "regex-automata" 872 | version = "0.4.8" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 875 | dependencies = [ 876 | "aho-corasick", 877 | "memchr", 878 | "regex-syntax", 879 | ] 880 | 881 | [[package]] 882 | name = "regex-syntax" 883 | version = "0.8.5" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 886 | 887 | [[package]] 888 | name = "rustc-demangle" 889 | version = "0.1.24" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 892 | 893 | [[package]] 894 | name = "ryu" 895 | version = "1.0.18" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 898 | 899 | [[package]] 900 | name = "scopeguard" 901 | version = "1.2.0" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 904 | 905 | [[package]] 906 | name = "serde" 907 | version = "1.0.210" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 910 | dependencies = [ 911 | "serde_derive", 912 | ] 913 | 914 | [[package]] 915 | name = "serde_derive" 916 | version = "1.0.210" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 919 | dependencies = [ 920 | "proc-macro2", 921 | "quote", 922 | "syn", 923 | ] 924 | 925 | [[package]] 926 | name = "serde_json" 927 | version = "1.0.128" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 930 | dependencies = [ 931 | "itoa", 932 | "memchr", 933 | "ryu", 934 | "serde", 935 | ] 936 | 937 | [[package]] 938 | name = "sharded-slab" 939 | version = "0.1.7" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 942 | dependencies = [ 943 | "lazy_static", 944 | ] 945 | 946 | [[package]] 947 | name = "shlex" 948 | version = "1.3.0" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 951 | 952 | [[package]] 953 | name = "signal-hook-registry" 954 | version = "1.4.2" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 957 | dependencies = [ 958 | "libc", 959 | ] 960 | 961 | [[package]] 962 | name = "slab" 963 | version = "0.4.9" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 966 | dependencies = [ 967 | "autocfg", 968 | ] 969 | 970 | [[package]] 971 | name = "smallvec" 972 | version = "1.13.2" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 975 | 976 | [[package]] 977 | name = "socket2" 978 | version = "0.5.7" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 981 | dependencies = [ 982 | "libc", 983 | "windows-sys 0.52.0", 984 | ] 985 | 986 | [[package]] 987 | name = "strsim" 988 | version = "0.11.1" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 991 | 992 | [[package]] 993 | name = "syn" 994 | version = "2.0.79" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 997 | dependencies = [ 998 | "proc-macro2", 999 | "quote", 1000 | "unicode-ident", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "termcolor" 1005 | version = "1.4.1" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1008 | dependencies = [ 1009 | "winapi-util", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "thiserror" 1014 | version = "1.0.64" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 1017 | dependencies = [ 1018 | "thiserror-impl", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "thiserror-impl" 1023 | version = "1.0.64" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 1026 | dependencies = [ 1027 | "proc-macro2", 1028 | "quote", 1029 | "syn", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "thread_local" 1034 | version = "1.1.8" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1037 | dependencies = [ 1038 | "cfg-if", 1039 | "once_cell", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "tokio" 1044 | version = "1.40.0" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 1047 | dependencies = [ 1048 | "backtrace", 1049 | "bytes", 1050 | "libc", 1051 | "mio", 1052 | "pin-project-lite", 1053 | "signal-hook-registry", 1054 | "socket2", 1055 | "tokio-macros", 1056 | "windows-sys 0.52.0", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "tokio-macros" 1061 | version = "2.4.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1064 | dependencies = [ 1065 | "proc-macro2", 1066 | "quote", 1067 | "syn", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "tokio-util" 1072 | version = "0.7.12" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 1075 | dependencies = [ 1076 | "bytes", 1077 | "futures-core", 1078 | "futures-sink", 1079 | "pin-project-lite", 1080 | "tokio", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "tokio_schedule" 1085 | version = "0.3.2" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "61c291c554da3518d6ef69c76ea35aabc78f736185a16b6017f6d1c224dac2e0" 1088 | dependencies = [ 1089 | "chrono", 1090 | "tokio", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "tower-service" 1095 | version = "0.3.3" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1098 | 1099 | [[package]] 1100 | name = "tracing" 1101 | version = "0.1.40" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1104 | dependencies = [ 1105 | "pin-project-lite", 1106 | "tracing-attributes", 1107 | "tracing-core", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "tracing-attributes" 1112 | version = "0.1.27" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1115 | dependencies = [ 1116 | "proc-macro2", 1117 | "quote", 1118 | "syn", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "tracing-core" 1123 | version = "0.1.32" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1126 | dependencies = [ 1127 | "once_cell", 1128 | "valuable", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "tracing-log" 1133 | version = "0.2.0" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1136 | dependencies = [ 1137 | "log", 1138 | "once_cell", 1139 | "tracing-core", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "tracing-subscriber" 1144 | version = "0.3.18" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 1147 | dependencies = [ 1148 | "nu-ansi-term", 1149 | "sharded-slab", 1150 | "smallvec", 1151 | "thread_local", 1152 | "tracing-core", 1153 | "tracing-log", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "try-lock" 1158 | version = "0.2.5" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1161 | 1162 | [[package]] 1163 | name = "unicode-ident" 1164 | version = "1.0.13" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1167 | 1168 | [[package]] 1169 | name = "utf8parse" 1170 | version = "0.2.2" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1173 | 1174 | [[package]] 1175 | name = "valuable" 1176 | version = "0.1.0" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1179 | 1180 | [[package]] 1181 | name = "version_check" 1182 | version = "0.9.5" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1185 | 1186 | [[package]] 1187 | name = "want" 1188 | version = "0.3.1" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1191 | dependencies = [ 1192 | "try-lock", 1193 | ] 1194 | 1195 | [[package]] 1196 | name = "wasi" 1197 | version = "0.11.0+wasi-snapshot-preview1" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1200 | 1201 | [[package]] 1202 | name = "wasm-bindgen" 1203 | version = "0.2.93" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 1206 | dependencies = [ 1207 | "cfg-if", 1208 | "once_cell", 1209 | "wasm-bindgen-macro", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "wasm-bindgen-backend" 1214 | version = "0.2.93" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 1217 | dependencies = [ 1218 | "bumpalo", 1219 | "log", 1220 | "once_cell", 1221 | "proc-macro2", 1222 | "quote", 1223 | "syn", 1224 | "wasm-bindgen-shared", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "wasm-bindgen-macro" 1229 | version = "0.2.93" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 1232 | dependencies = [ 1233 | "quote", 1234 | "wasm-bindgen-macro-support", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "wasm-bindgen-macro-support" 1239 | version = "0.2.93" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 1242 | dependencies = [ 1243 | "proc-macro2", 1244 | "quote", 1245 | "syn", 1246 | "wasm-bindgen-backend", 1247 | "wasm-bindgen-shared", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "wasm-bindgen-shared" 1252 | version = "0.2.93" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 1255 | 1256 | [[package]] 1257 | name = "winapi" 1258 | version = "0.3.9" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1261 | dependencies = [ 1262 | "winapi-i686-pc-windows-gnu", 1263 | "winapi-x86_64-pc-windows-gnu", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "winapi-i686-pc-windows-gnu" 1268 | version = "0.4.0" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1271 | 1272 | [[package]] 1273 | name = "winapi-util" 1274 | version = "0.1.9" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1277 | dependencies = [ 1278 | "windows-sys 0.59.0", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "winapi-x86_64-pc-windows-gnu" 1283 | version = "0.4.0" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1286 | 1287 | [[package]] 1288 | name = "windows-core" 1289 | version = "0.52.0" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1292 | dependencies = [ 1293 | "windows-targets", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "windows-sys" 1298 | version = "0.52.0" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1301 | dependencies = [ 1302 | "windows-targets", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "windows-sys" 1307 | version = "0.59.0" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1310 | dependencies = [ 1311 | "windows-targets", 1312 | ] 1313 | 1314 | [[package]] 1315 | name = "windows-targets" 1316 | version = "0.52.6" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1319 | dependencies = [ 1320 | "windows_aarch64_gnullvm", 1321 | "windows_aarch64_msvc", 1322 | "windows_i686_gnu", 1323 | "windows_i686_gnullvm", 1324 | "windows_i686_msvc", 1325 | "windows_x86_64_gnu", 1326 | "windows_x86_64_gnullvm", 1327 | "windows_x86_64_msvc", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "windows_aarch64_gnullvm" 1332 | version = "0.52.6" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1335 | 1336 | [[package]] 1337 | name = "windows_aarch64_msvc" 1338 | version = "0.52.6" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1341 | 1342 | [[package]] 1343 | name = "windows_i686_gnu" 1344 | version = "0.52.6" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1347 | 1348 | [[package]] 1349 | name = "windows_i686_gnullvm" 1350 | version = "0.52.6" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1353 | 1354 | [[package]] 1355 | name = "windows_i686_msvc" 1356 | version = "0.52.6" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1359 | 1360 | [[package]] 1361 | name = "windows_x86_64_gnu" 1362 | version = "0.52.6" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1365 | 1366 | [[package]] 1367 | name = "windows_x86_64_gnullvm" 1368 | version = "0.52.6" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1371 | 1372 | [[package]] 1373 | name = "windows_x86_64_msvc" 1374 | version = "0.52.6" 1375 | source = "registry+https://github.com/rust-lang/crates.io-index" 1376 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1377 | 1378 | [[package]] 1379 | name = "xtask" 1380 | version = "0.1.0" 1381 | dependencies = [ 1382 | "anyhow", 1383 | "clap", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "yaml-rust" 1388 | version = "0.4.5" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1391 | dependencies = [ 1392 | "linked-hash-map", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "zerocopy" 1397 | version = "0.7.35" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1400 | dependencies = [ 1401 | "zerocopy-derive", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "zerocopy-derive" 1406 | version = "0.7.35" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1409 | dependencies = [ 1410 | "proc-macro2", 1411 | "quote", 1412 | "syn", 1413 | ] 1414 | --------------------------------------------------------------------------------