base commit
This commit is contained in:
commit
1394b5d76c
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
### https://raw.github.com/github/gitignore/master/Rust.gitignore
|
||||
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
2142
Cargo.lock
generated
Normal file
2142
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["xtask", "net-logger", "net-logger-common"]
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# net2elk
|
||||
## network tool for generate logs in elasticsearch as tcpdump
|
||||
|
||||
Project developed exclusively for educational purposes, use at your own risk
|
||||
|
||||
To configure the elasticsearch acces edit the file _net-logger/src/elk/elasticsearch.rs_
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Install bpf-linker: `cargo install bpf-linker`
|
||||
|
||||
## Build eBPF
|
||||
|
||||
```bash
|
||||
cargo xtask build-ebpf
|
||||
```
|
||||
|
||||
To perform a release build you can use the `--release` flag.
|
||||
You may also change the target architecture with the `--target` flag.
|
||||
|
||||
## Build Userspace
|
||||
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
||||
## Build eBPF and Userspace
|
||||
|
||||
```bash
|
||||
cargo xtask build
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
RUST_LOG=info cargo xtask run
|
||||
```
|
||||
|
||||
## elasticsearch
|
||||
|
||||
To configure the elasticsearch acces edit the file _net-logger/src/elk/elasticsearch.rs_
|
15
net-logger-common/Cargo.toml
Normal file
15
net-logger-common/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "net-logger-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
user = ["aya"]
|
||||
|
||||
[dependencies]
|
||||
aya = { version = "0.12", optional = true }
|
||||
network-types = "0.0.6"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
18
net-logger-common/src/lib.rs
Normal file
18
net-logger-common/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
||||
#![no_std]
|
||||
use network_types::ip::{in6_addr, IpProto};
|
||||
use network_types::eth::EtherType;
|
||||
use core::net::Ipv6Addr;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Event {
|
||||
pub ipv: EtherType,
|
||||
pub source_port: u16,
|
||||
pub source_ipv4: u32,
|
||||
pub source_ipv6: Ipv6Addr,
|
||||
pub dest_port: u16,
|
||||
pub dest_ipv4: u32,
|
||||
pub dest_ipv6: Ipv6Addr,
|
||||
pub len: u16,
|
||||
pub proto: IpProto,
|
||||
}
|
6
net-logger-ebpf/.cargo/config.toml
Normal file
6
net-logger-ebpf/.cargo/config.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[build]
|
||||
target-dir = "../target"
|
||||
target = "bpfel-unknown-none"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core"]
|
2
net-logger-ebpf/.helix/config.toml
Normal file
2
net-logger-ebpf/.helix/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[editor]
|
||||
workspace-lsp-roots = []
|
4
net-logger-ebpf/.vim/coc-settings.json
Normal file
4
net-logger-ebpf/.vim/coc-settings.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"rust-analyzer.cargo.target": "bpfel-unknown-none",
|
||||
"rust-analyzer.checkOnSave.allTargets": false
|
||||
}
|
4
net-logger-ebpf/.vscode/settings.json
vendored
Normal file
4
net-logger-ebpf/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"rust-analyzer.cargo.target": "bpfel-unknown-none",
|
||||
"rust-analyzer.checkOnSave.allTargets": false
|
||||
}
|
197
net-logger-ebpf/Cargo.lock
generated
Normal file
197
net-logger-ebpf/Cargo.lock
generated
Normal file
@ -0,0 +1,197 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aya-ebpf"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7318de0c49a17873182763831cb22f74fb30d04e2eb7e6d7b7e9b7d86d70ed3"
|
||||
dependencies = [
|
||||
"aya-ebpf-bindings",
|
||||
"aya-ebpf-cty",
|
||||
"aya-ebpf-macros",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-ebpf-bindings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8536b7e39b232ecd854e587f473ba15640c09afc3e08408fc28144a7404ae75"
|
||||
dependencies = [
|
||||
"aya-ebpf-cty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-ebpf-cty"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5c130d898322b9698937465b3b749095dae85dba0da4ee648235947eb95738d"
|
||||
|
||||
[[package]]
|
||||
name = "aya-ebpf-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce7820cc83547582284a140ffbdd46ab527d7ee2d9d0cfedf3f184fad3f8e15c"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-log-common"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d38a351ee2d5dc24e04cac6184b1b39408642d9a8b585892c99146f8dd4edb"
|
||||
dependencies = [
|
||||
"num_enum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-log-ebpf"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a10bbadd0829895a91eb1cd2bb02d7af145704087f03812bed60cb9fe65dbb3"
|
||||
dependencies = [
|
||||
"aya-ebpf",
|
||||
"aya-log-common",
|
||||
"aya-log-ebpf-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-log-ebpf-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6d8251a75f56077db51892041aa6b77c70ef2723845d7a210979700b2f01bc4"
|
||||
dependencies = [
|
||||
"aya-log-common",
|
||||
"aya-log-parser",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aya-log-parser"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b102eb5c88c9aa0b49102d3fbcee08ecb0dfa81014f39b373311de7a7032cb"
|
||||
dependencies = [
|
||||
"aya-log-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net-logger-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"network-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net-logger-ebpf"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aya-ebpf",
|
||||
"aya-log-ebpf",
|
||||
"net-logger-common",
|
||||
"network-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "network-types"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41b13eba62f530cd2ea031938ac4472b9b649694baa1e587c2a2fadc07844d3c"
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
34
net-logger-ebpf/Cargo.toml
Normal file
34
net-logger-ebpf/Cargo.toml
Normal file
@ -0,0 +1,34 @@
|
||||
cargo-features = ["profile-rustflags"]
|
||||
[package]
|
||||
name = "net-logger-ebpf"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aya-ebpf = "0.1.0"
|
||||
aya-log-ebpf = "0.1.0"
|
||||
net-logger-common = { path = "../net-logger-common" }
|
||||
network-types = "0.0.6"
|
||||
|
||||
[[bin]]
|
||||
name = "net-logger"
|
||||
path = "src/main.rs"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
lto = true
|
||||
panic = "abort"
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
rpath = false
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace]
|
||||
members = []
|
13
net-logger-ebpf/rust-toolchain.toml
Normal file
13
net-logger-ebpf/rust-toolchain.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
# The source code of rustc, provided by the rust-src component, is needed for
|
||||
# building eBPF programs.
|
||||
components = [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"rust-docs",
|
||||
"rust-src",
|
||||
"rust-std",
|
||||
"rustc",
|
||||
"rustfmt",
|
||||
]
|
23
net-logger-ebpf/src/main.rs
Normal file
23
net-logger-ebpf/src/main.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
|
||||
use aya_log_ebpf::info;
|
||||
pub mod stract_data;
|
||||
#[xdp]
|
||||
pub fn net_logger(ctx: XdpContext) -> u32 {
|
||||
match stract_data::trafic_router(ctx) {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => xdp_action::XDP_ABORTED,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_net_logger(ctx: XdpContext) -> Result<u32, u32> {
|
||||
info!(&ctx, "received a packet");
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
}
|
113
net-logger-ebpf/src/stract_data.rs
Normal file
113
net-logger-ebpf/src/stract_data.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use aya_ebpf::{
|
||||
bindings::xdp_action,
|
||||
macros::map,
|
||||
programs::XdpContext,
|
||||
maps::PerfEventArray,
|
||||
maps::PerCpuArray,
|
||||
};
|
||||
use aya_log_ebpf::info;
|
||||
use net_logger_common::Event;
|
||||
use core::mem;
|
||||
use network_types::{
|
||||
eth::{EthHdr, EtherType},
|
||||
ip::{IpProto, Ipv4Hdr, Ipv6Hdr, in6_addr},
|
||||
tcp::TcpHdr,
|
||||
udp::UdpHdr,
|
||||
icmp::IcmpHdr,
|
||||
};
|
||||
|
||||
#[map]
|
||||
pub static mut SCRATCH: PerCpuArray<Event> = PerCpuArray::with_max_entries(1, 0); // per-cpu
|
||||
|
||||
#[map]
|
||||
pub static mut EVENTS: PerfEventArray<Event> = PerfEventArray::with_max_entries(0, 0);
|
||||
|
||||
#[inline(always)] //
|
||||
fn ptr_at<T>(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> {
|
||||
let start = ctx.data();
|
||||
let end = ctx.data_end();
|
||||
let len = mem::size_of::<T>();
|
||||
|
||||
if start + offset + len > end {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok((start + offset) as *const T)
|
||||
}
|
||||
|
||||
pub fn trafic_router(ctx: XdpContext) -> Result<u32, ()> {
|
||||
let ethhdr: *const EthHdr = ptr_at(&ctx, 0)?; //
|
||||
match unsafe { (*ethhdr).ether_type } {
|
||||
EtherType::Ipv4 => process_v4(ctx),
|
||||
EtherType::Ipv6 => process_v6(ctx),
|
||||
_ => Ok(xdp_action::XDP_PASS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ports(ctx: &XdpContext, protocol: IpProto, ip_hdr_len: usize) -> Result<(u16, u16),()> {
|
||||
let source_port;
|
||||
let dest_port;
|
||||
match protocol {
|
||||
IpProto::Tcp => {
|
||||
let tcphdr: *const TcpHdr =
|
||||
ptr_at(ctx, EthHdr::LEN + ip_hdr_len)?;
|
||||
source_port = u16::from_be(unsafe { (*tcphdr).source });
|
||||
dest_port = u16::from_be(unsafe { (*tcphdr).dest });
|
||||
}
|
||||
IpProto::Udp => {
|
||||
let udphdr: *const UdpHdr =
|
||||
ptr_at(ctx, EthHdr::LEN + ip_hdr_len)?;
|
||||
source_port = u16::from_be(unsafe { (*udphdr).source });
|
||||
dest_port = u16::from_be(unsafe { (*udphdr).dest });
|
||||
}
|
||||
_ => {
|
||||
source_port = 0;
|
||||
dest_port = 0;
|
||||
},
|
||||
};
|
||||
Ok((source_port, dest_port))
|
||||
}
|
||||
|
||||
pub fn process_v6(ctx: XdpContext) -> Result<u32, ()> {
|
||||
let ipv6hdr: *const Ipv6Hdr = ptr_at(&ctx, EthHdr::LEN)?;
|
||||
let source_addr = unsafe { (*ipv6hdr).src_addr() };
|
||||
let dest_addr = unsafe { (*ipv6hdr).dst_addr() };
|
||||
let protocol = unsafe { (*ipv6hdr).next_hdr };
|
||||
let pack_len = unsafe { (*ipv6hdr).payload_len };
|
||||
let ports = get_ports(&ctx, protocol, Ipv6Hdr::LEN)?;
|
||||
|
||||
let mut event = unsafe { *SCRATCH.get_ptr_mut(0).ok_or(())? };
|
||||
event.ipv = EtherType::Ipv6;
|
||||
event.source_port = ports.0;
|
||||
event.source_ipv6 = source_addr;
|
||||
event.dest_port = ports.1;
|
||||
event.dest_ipv6 = dest_addr;
|
||||
event.proto = protocol;
|
||||
event.len = pack_len;
|
||||
unsafe { EVENTS.output(&ctx, &mut event, 0); }
|
||||
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
||||
|
||||
pub fn process_v4(ctx: XdpContext) -> Result<u32, ()> {
|
||||
|
||||
let ipv4hdr: *const Ipv4Hdr = ptr_at(&ctx, EthHdr::LEN)?;
|
||||
let source_addr = u32::from_be(unsafe { (*ipv4hdr).src_addr });
|
||||
let dest_addr = u32::from_be(unsafe { (*ipv4hdr).dst_addr });
|
||||
let protocol = unsafe { (*ipv4hdr).proto };
|
||||
let pack_len = unsafe { (*ipv4hdr).tot_len };
|
||||
let ports = get_ports(&ctx, protocol, Ipv4Hdr::LEN)?;
|
||||
|
||||
let mut event = unsafe { *SCRATCH.get_ptr_mut(0).ok_or(())? };
|
||||
|
||||
event.ipv = EtherType::Ipv4;
|
||||
event.source_port = ports.0;
|
||||
event.source_ipv4 = source_addr;
|
||||
event.dest_port = ports.1;
|
||||
event.dest_ipv4 = dest_addr;
|
||||
event.proto = protocol;
|
||||
event.len = pack_len;
|
||||
unsafe { EVENTS.output(&ctx, &mut event, 0); }
|
||||
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
32
net-logger/Cargo.toml
Normal file
32
net-logger/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "net-logger"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
aya = "0.12"
|
||||
aya-log = "0.2"
|
||||
clap = { version = "4.1", features = ["derive"] }
|
||||
net-logger-common = { path = "../net-logger-common", features = ["user"] }
|
||||
anyhow = "1"
|
||||
env_logger = "0.10"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
tokio = { version = "1.25", features = [
|
||||
"macros",
|
||||
"rt",
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
"signal",
|
||||
] }
|
||||
bytes = "*"
|
||||
network-types = "0.0.6"
|
||||
elasticsearch = "8.17.0-alpha.1"
|
||||
serde_json = "*"
|
||||
serde = "*"
|
||||
chrono = "*"
|
||||
|
||||
[[bin]]
|
||||
name = "net-logger"
|
||||
path = "src/main.rs"
|
252
net-logger/src/clasificator/ip_group.rs
Normal file
252
net-logger/src/clasificator/ip_group.rs
Normal file
@ -0,0 +1,252 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use crate::utils::ip::print_proto;
|
||||
use network_types::ip::{IpProto, Ipv4Hdr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ConectionCoreAtom {
|
||||
pub len: u128,
|
||||
pub ip4_list: Vec<IpAtom<Ipv4Addr>>,
|
||||
pub ip6_list: Vec<IpAtom<Ipv6Addr>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct IpAtom<IpType> {
|
||||
len: u128,
|
||||
ip_local: IpType,
|
||||
port_local: u16,
|
||||
ip_remote: IpType,
|
||||
port_remote: u16,
|
||||
//pub proto_len: HashMap<u8, u128>
|
||||
pub proto_len: Vec<(u8, u128)>,
|
||||
}
|
||||
|
||||
impl ConectionCoreAtom {
|
||||
pub fn new() -> Self {
|
||||
ConectionCoreAtom {
|
||||
len: 0,
|
||||
ip4_list: Vec::new(),
|
||||
ip6_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addv4(
|
||||
&mut self,
|
||||
source_ip: u32,
|
||||
dest_ip: u32,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
protocol: IpProto,
|
||||
size: u16,
|
||||
) {
|
||||
self.len += size as u128;
|
||||
let source_ip_format = Ipv4Addr::from_bits(source_ip);
|
||||
let dest_ip_format = Ipv4Addr::from_bits(dest_ip);
|
||||
let local_ip;
|
||||
let remote_ip;
|
||||
let local_port;
|
||||
let remote_port;
|
||||
if source_ip_format.is_private() {
|
||||
local_ip = source_ip_format;
|
||||
remote_ip = dest_ip_format;
|
||||
local_port = source_port;
|
||||
remote_port = dest_port;
|
||||
} else {
|
||||
local_ip = dest_ip_format;
|
||||
remote_ip = source_ip_format;
|
||||
local_port = dest_port;
|
||||
remote_port = dest_port;
|
||||
}
|
||||
match self.check_packagev4(local_ip, remote_ip, local_port, remote_port) {
|
||||
Some(ip_atom) => {
|
||||
ip_atom.add(protocol, size as u128);
|
||||
}
|
||||
None => {
|
||||
let mut ip_atom = IpAtom::new_ip(local_ip, remote_ip, local_port, remote_port);
|
||||
ip_atom.add(protocol, size as u128);
|
||||
self.ip4_list.push(ip_atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addv6(
|
||||
&mut self,
|
||||
source_ip: Ipv6Addr,
|
||||
dest_ip: Ipv6Addr,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
protocol: IpProto,
|
||||
size: u16,
|
||||
) {
|
||||
self.len += size as u128;
|
||||
let local_ip;
|
||||
let remote_ip;
|
||||
let local_port;
|
||||
let remote_port;
|
||||
if source_ip.is_unique_local() {
|
||||
local_ip = source_ip;
|
||||
local_port = source_port;
|
||||
remote_ip = dest_ip;
|
||||
remote_port = dest_port;
|
||||
} else {
|
||||
local_ip = dest_ip;
|
||||
local_port = dest_port;
|
||||
remote_ip = source_ip;
|
||||
remote_port = source_port;
|
||||
}
|
||||
match self.check_packagev6(local_ip, remote_ip, local_port, remote_port) {
|
||||
Some(ip_atom) => {
|
||||
ip_atom.add(protocol, size as u128);
|
||||
}
|
||||
None => {
|
||||
let mut ip_atom = IpAtom::new_ip(local_ip, remote_ip, local_port, remote_port);
|
||||
ip_atom.add(protocol, size as u128);
|
||||
self.ip6_list.push(ip_atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_packagev4(
|
||||
&mut self,
|
||||
local_ip: Ipv4Addr,
|
||||
remote_ip: Ipv4Addr,
|
||||
local_port: u16,
|
||||
remote_port: u16,
|
||||
) -> Option<&mut IpAtom<Ipv4Addr>> {
|
||||
let mut index = 0;
|
||||
loop {
|
||||
match self.ip4_list.get(index) {
|
||||
Some(ip) => {
|
||||
if ip.eq_splited(local_ip, remote_ip, local_port, remote_port) {
|
||||
return self.ip4_list.get_mut(index);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_packagev6(
|
||||
&mut self,
|
||||
local_ip: Ipv6Addr,
|
||||
remote_ip: Ipv6Addr,
|
||||
local_port: u16,
|
||||
remote_port: u16,
|
||||
) -> Option<&mut IpAtom<Ipv6Addr>> {
|
||||
let index = 0;
|
||||
loop {
|
||||
match self.ip6_list.get(index) {
|
||||
Some(ip) => {
|
||||
if ip.eq_splited(local_ip, remote_ip, local_port, remote_port) {
|
||||
return self.ip6_list.get_mut(index);
|
||||
}
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_json(&self, timestamp: String) -> Value {
|
||||
json!({
|
||||
"@timestamp" : timestamp,
|
||||
"ips_v4" : self.ip4_list,
|
||||
"ips_v6" : self.ip6_list,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Self {
|
||||
let old_len = self.len;
|
||||
self.len = 0;
|
||||
Self {
|
||||
len: old_len,
|
||||
ip4_list: self.ip4_list.drain(..).collect(),
|
||||
ip6_list: self.ip6_list.drain(..).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/*fn create_ipv4(&mut self, source_ip: u32, dest_ip: u32) {
|
||||
let source_ip_format = Ipv4Addr::from_bits(source_ip);
|
||||
let dest_ip_format = Ipv4Addr::from_bits(dest_ip);
|
||||
self.ip4_list.push(if source_ip_format.is_private() {
|
||||
IpAtom::new_ip(source_ip_format, dest_ip_format)
|
||||
} else {
|
||||
IpAtom::new_ip(dest_ip_format, source_ip_format)
|
||||
})
|
||||
}
|
||||
|
||||
fn create_ipv6(&mut self, source_ip: Ipv6Addr, dest_ip: Ipv6Addr) {
|
||||
self.ip6_list.push(if source_ip.is_unique_local() {
|
||||
IpAtom::new_ip(source_ip, dest_ip)
|
||||
} else {
|
||||
IpAtom::new_ip(dest_ip, source_ip)
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
impl<IpType: Eq> IpAtom<IpType> {
|
||||
pub fn new_ip(local_ip: IpType, remote_ip: IpType, local_port: u16, remote_port: u16) -> Self {
|
||||
IpAtom {
|
||||
len: 0,
|
||||
ip_local: local_ip,
|
||||
port_local: local_port,
|
||||
ip_remote: remote_ip,
|
||||
port_remote: remote_port,
|
||||
proto_len: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
//hashmap can be less efficient
|
||||
/*pub fn add(&mut self,protocol: IpProto, size: u128) {
|
||||
self.len+=size;
|
||||
match self.proto_len.get_mut(&(protocol as u8)) {
|
||||
Some(l) => {
|
||||
*l+=size;
|
||||
},
|
||||
None => {
|
||||
self.proto_len.insert(protocol as u8,size);
|
||||
},
|
||||
};
|
||||
}*/
|
||||
|
||||
pub fn add(&mut self, protocol: IpProto, size: u128) {
|
||||
self.len += size;
|
||||
let mut index = 0;
|
||||
loop {
|
||||
match self.proto_len.get_mut(index) {
|
||||
Some(proto) => {
|
||||
if proto.0 == protocol as u8 {
|
||||
proto.1 += size;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.proto_len.push((protocol as u8, size));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_splited(
|
||||
&self,
|
||||
local_ip: IpType,
|
||||
remote_ip: IpType,
|
||||
local_port: u16,
|
||||
remote_port: u16,
|
||||
) -> bool {
|
||||
((self.ip_local == local_ip && self.port_local == local_port)
|
||||
&& (self.ip_remote == remote_ip && self.port_remote == remote_port))
|
||||
|| ((self.ip_local == remote_ip && self.port_local == remote_port)
|
||||
&& (self.ip_remote == local_ip && self.port_remote == local_port))
|
||||
}
|
||||
}
|
50
net-logger/src/clasificator/ip_wrapper.rs
Normal file
50
net-logger/src/clasificator/ip_wrapper.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
pub struct Ip4Wrapper {
|
||||
raw: Ipv4Addr,
|
||||
}
|
||||
|
||||
pub struct Ip6Wrapper {
|
||||
raw: Ipv6Addr,
|
||||
}
|
||||
|
||||
pub trait IpWrapper<IpType> {
|
||||
fn is_private(&self) -> bool;
|
||||
fn get_raw(&self) -> IpType;
|
||||
}
|
||||
|
||||
impl Ip4Wrapper {
|
||||
pub fn new(ip: u32) -> Self {
|
||||
Self {
|
||||
raw: Ipv4Addr::from_bits(ip),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ip6Wrapper {
|
||||
pub fn new(ip: Ipv6Addr) -> Self {
|
||||
Self {
|
||||
raw: ip,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IpWrapper<Ipv4Addr> for Ip4Wrapper {
|
||||
fn is_private(&self) -> bool {
|
||||
self.raw.is_private()
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> Ipv4Addr {
|
||||
self.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl IpWrapper<Ipv6Addr> for Ip6Wrapper {
|
||||
fn is_private(&self) -> bool {
|
||||
self.raw.is_unique_local()
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> Ipv6Addr {
|
||||
self.raw
|
||||
}
|
||||
}
|
3
net-logger/src/clasificator/mod.rs
Normal file
3
net-logger/src/clasificator/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod ip_group;
|
||||
mod ip_wrapper;
|
||||
pub mod store;
|
96
net-logger/src/clasificator/store.rs
Normal file
96
net-logger/src/clasificator/store.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use network_types::ip::IpProto;
|
||||
|
||||
use crate::elk::elasticsearch::ElasticConection;
|
||||
|
||||
use super::ip_group::ConectionCoreAtom;
|
||||
use std::{
|
||||
future::IntoFuture,
|
||||
net::Ipv6Addr,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub struct net_stats_storage {
|
||||
connections_store: ConectionCoreAtom,
|
||||
elastic_connection: ElasticConection,
|
||||
old_connections: Vec<ConectionCoreAtom>,
|
||||
last_insert: SystemTime,
|
||||
}
|
||||
|
||||
impl net_stats_storage {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
connections_store: ConectionCoreAtom::new(),
|
||||
elastic_connection: ElasticConection::new().unwrap(),
|
||||
old_connections: Vec::new(),
|
||||
last_insert: SystemTime::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn addv4(
|
||||
&mut self,
|
||||
source_ip: u32,
|
||||
dest_ip: u32,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
protocol: IpProto,
|
||||
size: u16,
|
||||
) {
|
||||
self.store_or_not().await;
|
||||
|
||||
self.connections_store
|
||||
.addv4(source_ip, dest_ip, source_port, dest_port, protocol, size);
|
||||
}
|
||||
|
||||
pub async fn addv6(
|
||||
&mut self,
|
||||
source_ip: Ipv6Addr,
|
||||
dest_ip: Ipv6Addr,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
protocol: IpProto,
|
||||
size: u16,
|
||||
) {
|
||||
self.store_or_not().await;
|
||||
|
||||
self.connections_store
|
||||
.addv6(source_ip, dest_ip, source_port, dest_port, protocol, size);
|
||||
}
|
||||
|
||||
pub async fn store_or_not(&mut self) {
|
||||
if self
|
||||
.last_insert
|
||||
.duration_since(SystemTime::now())
|
||||
.unwrap_or(Duration::from_secs(2))
|
||||
.as_secs()
|
||||
> 2
|
||||
|| self.connections_store.len > 200000
|
||||
{
|
||||
self.store_in_elastic().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn store_in_elastic(&mut self) {
|
||||
self.old_connections.push(self.connections_store.reset());
|
||||
self.connections_store = ConectionCoreAtom::new();
|
||||
println!("entra:{}", self.old_connections.len());
|
||||
for con in &self.old_connections {
|
||||
println!(
|
||||
"datos:{}",
|
||||
chrono::offset::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
|
||||
);
|
||||
match self
|
||||
.elastic_connection
|
||||
.send(con.generate_json(
|
||||
chrono::offset::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(_) => continue,
|
||||
Err(_) => {
|
||||
println!("No va");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
net-logger/src/elk/elasticsearch.rs
Normal file
71
net-logger/src/elk/elasticsearch.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use elasticsearch::auth::Credentials;
|
||||
use elasticsearch::http::transport::SingleNodeConnectionPool;
|
||||
use elasticsearch::http::transport::TransportBuilder;
|
||||
use elasticsearch::http::Url;
|
||||
use elasticsearch::Elasticsearch;
|
||||
use elasticsearch::IndexParts;
|
||||
use serde_json::Value;
|
||||
use std::fmt;
|
||||
|
||||
pub struct ElasticConection {
|
||||
conection: Elasticsearch,
|
||||
}
|
||||
|
||||
pub struct ElasticConErr {
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl ElasticConection {
|
||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let url = Url::parse("http://elastic_ip:9200")?;
|
||||
let credentials = Credentials::Basic("elastic".into(), "password".into());
|
||||
let connection_pool = SingleNodeConnectionPool::new(url);
|
||||
let transport = TransportBuilder::new(connection_pool)
|
||||
.auth(credentials)
|
||||
.build()?;
|
||||
Ok(Self {
|
||||
conection: Elasticsearch::new(transport),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send(&self, data: Value) -> Result<(), ElasticConErr> {
|
||||
println!("aquí tambi'en entra");
|
||||
let raw_response = self
|
||||
.conection
|
||||
.index(IndexParts::Index("netlogger-0.1"))
|
||||
.body(data)
|
||||
.send()
|
||||
.await;
|
||||
let response = match raw_response {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Err(ElasticConErr {
|
||||
content: e.to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let status_code = response.status_code();
|
||||
match status_code.clone().is_success() {
|
||||
true => Ok(()),
|
||||
false => {
|
||||
let err_ret = match response.text().await {
|
||||
Ok(ret) => ret,
|
||||
Err(_e) => status_code.as_str().to_string(),
|
||||
};
|
||||
Err(ElasticConErr { content: err_ret })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ElasticConErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.content)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ElasticConErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.content)
|
||||
}
|
||||
}
|
1
net-logger/src/elk/mod.rs
Normal file
1
net-logger/src/elk/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod elasticsearch;
|
159
net-logger/src/main.rs
Normal file
159
net-logger/src/main.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use anyhow::Context;
|
||||
use aya::maps::AsyncPerfEventArray;
|
||||
use aya::programs::{Xdp, XdpFlags};
|
||||
use aya::util::online_cpus;
|
||||
use aya::{include_bytes_aligned, Bpf};
|
||||
use aya_log::BpfLogger;
|
||||
use aya_log::Formatter;
|
||||
use bytes::BytesMut;
|
||||
use clap::Parser;
|
||||
use log::{debug, info, warn};
|
||||
use net_logger_common::Event;
|
||||
use network_types::eth::EtherType;
|
||||
use tokio::signal;
|
||||
mod clasificator;
|
||||
mod elk;
|
||||
mod utils;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Opt {
|
||||
#[clap(short, long, default_value = "lo")]
|
||||
iface: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
env_logger::init();
|
||||
|
||||
// Bump the memlock rlimit. This is needed for older kernels that don't use the
|
||||
// new memcg based accounting, see https://lwn.net/Articles/837122/
|
||||
let rlim = libc::rlimit {
|
||||
rlim_cur: libc::RLIM_INFINITY,
|
||||
rlim_max: libc::RLIM_INFINITY,
|
||||
};
|
||||
let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) };
|
||||
if ret != 0 {
|
||||
debug!("remove limit on locked memory failed, ret is: {}", ret);
|
||||
}
|
||||
|
||||
if let Err(e) = load_bpf().await {
|
||||
eprintln!("error: {:#}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn load_bpf() -> Result<(), aya::BpfError> {
|
||||
// This will include your eBPF object file as raw bytes at compile-time and load it at
|
||||
// runtime. This approach is recommended for most real-world use cases. If you would
|
||||
// like to specify the eBPF program at runtime rather than at compile-time, you can
|
||||
// reach for `Bpf::load_file` instead.
|
||||
#[cfg(debug_assertions)]
|
||||
let mut bpf = Bpf::load(include_bytes_aligned!(
|
||||
"../../target/bpfel-unknown-none/debug/net-logger"
|
||||
))?;
|
||||
#[cfg(not(debug_assertions))]
|
||||
let mut bpf = Bpf::load(include_bytes_aligned!(
|
||||
"../../target/bpfel-unknown-none/release/net-logger"
|
||||
))?;
|
||||
if let Err(e) = BpfLogger::init(&mut bpf) {
|
||||
// This can happen if you remove all log statements from your eBPF program.
|
||||
warn!("failed to initialize eBPF logger: {}", e);
|
||||
}
|
||||
|
||||
proces_events_xdp(bpf).await
|
||||
}
|
||||
|
||||
async fn proces_events_xdp(mut bpf: Bpf) -> Result<(), aya::BpfError> {
|
||||
let opt = Opt::parse();
|
||||
|
||||
// This will include your eBPF object file as raw bytes at compile-time and load it at
|
||||
// runtime. This approach is recommended for most real-world use cases. If you would
|
||||
// like to specify the eBPF program at runtime rather than at compile-time, you can
|
||||
// reach for `Bpf::load_file` instead.
|
||||
|
||||
//let program : &mut SockOps = bpf.program_mut("get_package_data").unwrap().try_into().unwrap();
|
||||
//program.load().unwrap();
|
||||
let program: &mut Xdp = bpf.program_mut("net_logger").unwrap().try_into().unwrap();
|
||||
program.load().unwrap();
|
||||
program.attach(&opt.iface, XdpFlags::default())
|
||||
.context("failed to attach the XDP program with default flags - try changing XdpFlags::default() to XdpFlags::SKB_MODE").unwrap();
|
||||
let cpus = online_cpus().unwrap();
|
||||
let num_cpus = cpus.len();
|
||||
//let events_raw = bpf.map_mut("EVENTS");
|
||||
let mut events: AsyncPerfEventArray<_> = bpf
|
||||
.take_map("EVENTS")
|
||||
.context("failed to map QUERY_RING")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
for cpu in cpus {
|
||||
let mut buf = events.open(cpu, None).unwrap();
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let mut buffers = (0..num_cpus)
|
||||
.map(|_| BytesMut::with_capacity(10240))
|
||||
.collect::<Vec<_>>();
|
||||
let mut con = clasificator::store::net_stats_storage::new();
|
||||
loop {
|
||||
let events = buf.read_events(&mut buffers).await.unwrap();
|
||||
for i in 0..events.read {
|
||||
// get timestamp
|
||||
//let now = Local::now();
|
||||
|
||||
// read the event
|
||||
let buf = &mut buffers[i];
|
||||
let ptr = buf.as_ptr() as *const Event;
|
||||
let data = unsafe { ptr.read_unaligned() };
|
||||
// parse out the data
|
||||
match data.ipv {
|
||||
EtherType::Ipv4 => {
|
||||
println!("source ip:{}, source port:{}, dest ip:{}, dest port:{}, proto:{}, size: {}",
|
||||
aya_log::Ipv4Formatter::format(data.source_ipv4),
|
||||
data.source_port,
|
||||
aya_log::Ipv4Formatter::format(data.dest_ipv4),
|
||||
data.dest_port,
|
||||
utils::ip::print_proto(data.proto),
|
||||
data.len);
|
||||
con.addv4(
|
||||
data.source_ipv4,
|
||||
data.dest_ipv4,
|
||||
data.source_port,
|
||||
data.dest_port,
|
||||
data.proto,
|
||||
data.len,
|
||||
)
|
||||
.await;
|
||||
//println!("package size:{}, ips:{}", con.len, con.ip4_list.get(con.ip4_list.len()-1).unwrap().proto_len.get(0).unwrap_or(&(0,0)).1);
|
||||
}
|
||||
EtherType::Ipv6 => {
|
||||
println!("source ip:{}, source port:{}, dest ip:{}, dest port:{}, proto:{}, size: {}",
|
||||
aya_log::Ipv6Formatter::format(data.source_ipv6),
|
||||
data.source_port,
|
||||
aya_log::Ipv6Formatter::format(data.dest_ipv6),
|
||||
data.dest_port,
|
||||
utils::ip::print_proto(data.proto),
|
||||
data.len);
|
||||
con.addv6(
|
||||
data.source_ipv6,
|
||||
data.dest_ipv6,
|
||||
data.source_port,
|
||||
data.dest_port,
|
||||
data.proto,
|
||||
data.len,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
_ => println!("result not coverd"),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
info!("Waiting for Ctrl-C...");
|
||||
signal::ctrl_c().await.unwrap();
|
||||
info!("Exiting...");
|
||||
|
||||
Ok(())
|
||||
}
|
154
net-logger/src/utils/ip.rs
Normal file
154
net-logger/src/utils/ip.rs
Normal file
@ -0,0 +1,154 @@
|
||||
use network_types::ip::IpProto;
|
||||
|
||||
pub fn print_proto(proto: IpProto) -> String {
|
||||
String::from(match proto {
|
||||
IpProto::HopOpt => "HopOpt",
|
||||
IpProto::Icmp => "Icmp",
|
||||
IpProto::Igmp => "Igmp",
|
||||
IpProto::Ggp => "Ggp",
|
||||
IpProto::Ipv4 => "Ipv4",
|
||||
IpProto::Stream => "Stream",
|
||||
IpProto::Tcp => "Tcp",
|
||||
IpProto::Cbt => "Cbt",
|
||||
IpProto::Egp => "Egp",
|
||||
IpProto::Igp => "Igp",
|
||||
IpProto::BbnRccMon => "BbnRccMon",
|
||||
IpProto::NvpII => "NvpII",
|
||||
IpProto::Pup => "Pup",
|
||||
IpProto::Argus => "Argus",
|
||||
IpProto::Emcon => "Emcon",
|
||||
IpProto::Xnet => "Xnet",
|
||||
IpProto::Chaos => "Chaos",
|
||||
IpProto::Udp => "Udp",
|
||||
IpProto::Mux => "Mux",
|
||||
IpProto::DcnMeas => "DcnMeas",
|
||||
IpProto::Hmp => "Hmp",
|
||||
IpProto::Prm => "Prm",
|
||||
IpProto::Idp => "Idp",
|
||||
IpProto::Trunk1 => "Trunk1",
|
||||
IpProto::Trunk2 => "Trunk2",
|
||||
IpProto::Leaf1 => "Leaf1",
|
||||
IpProto::Leaf2 => "Leaf2",
|
||||
IpProto::Rdp => "Rdp",
|
||||
IpProto::Irtp => "Irtp",
|
||||
IpProto::Tp4 => "Tp4",
|
||||
IpProto::Netblt => "Netblt",
|
||||
IpProto::MfeNsp => "MfeNsp",
|
||||
IpProto::MeritInp => "MeritInp",
|
||||
IpProto::Dccp => "Dccp",
|
||||
IpProto::ThirdPartyConnect => "ThirdPartyConnect",
|
||||
IpProto::Idpr => "Idpr",
|
||||
IpProto::Xtp => "Xtp",
|
||||
IpProto::Ddp => "Ddp",
|
||||
IpProto::IdprCmtp => "IdprCmtp",
|
||||
IpProto::TpPlusPlus => "TpPlusPlus",
|
||||
IpProto::Il => "Il",
|
||||
IpProto::Ipv6 => "Ipv6",
|
||||
IpProto::Sdrp => "Sdrp",
|
||||
IpProto::Ipv6Route => "Ipv6Route",
|
||||
IpProto::Ipv6Frag => "Ipv6Frag",
|
||||
IpProto::Idrp => "Idrp",
|
||||
IpProto::Rsvp => "Rsvp",
|
||||
IpProto::Gre => "Gre",
|
||||
IpProto::Dsr => "Dsr",
|
||||
IpProto::Bna => "Bna",
|
||||
IpProto::Esp => "Esp",
|
||||
IpProto::Ah => "Ah",
|
||||
IpProto::Inlsp => "Inlsp",
|
||||
IpProto::Swipe => "Swipe",
|
||||
IpProto::Narp => "Narp",
|
||||
IpProto::Mobile => "Mobile",
|
||||
IpProto::Tlsp => "Tlsp",
|
||||
IpProto::Skip => "Skip",
|
||||
IpProto::Ipv6Icmp => "Ipv6Icmp",
|
||||
IpProto::Ipv6NoNxt => "Ipv6NoNxt",
|
||||
IpProto::Ipv6Opts => "Ipv6Opts",
|
||||
IpProto::AnyHostInternal => "AnyHostInternal",
|
||||
IpProto::Cftp => "Cftp",
|
||||
IpProto::AnyLocalNetwork => "AnyLocalNetwork",
|
||||
IpProto::SatExpak => "SatExpak",
|
||||
IpProto::Kryptolan => "Kryptolan",
|
||||
IpProto::Rvd => "Rvd",
|
||||
IpProto::Ippc => "Ippc",
|
||||
IpProto::AnyDistributedFileSystem => "AnyDistributedFileSystem",
|
||||
IpProto::SatMon => "SatMon",
|
||||
IpProto::Visa => "Visa",
|
||||
IpProto::Ipcv => "Ipcv",
|
||||
IpProto::Cpnx => "Cpnx",
|
||||
IpProto::Cphb => "Cphb",
|
||||
IpProto::Wsn => "Wsn",
|
||||
IpProto::Pvp => "Pvp",
|
||||
IpProto::BrSatMon => "BrSatMon",
|
||||
IpProto::SunNd => "SunNd",
|
||||
IpProto::WbMon => "WbMon",
|
||||
IpProto::WbExpak => "WbExpak",
|
||||
IpProto::IsoIp => "IsoIp",
|
||||
IpProto::Vmtp => "Vmtp",
|
||||
IpProto::SecureVmtp => "SecureVmtp",
|
||||
IpProto::Vines => "Vines",
|
||||
IpProto::Ttp => "Ttp",
|
||||
IpProto::NsfnetIgp => "NsfnetIgp",
|
||||
IpProto::Dgp => "Dgp",
|
||||
IpProto::Tcf => "Tcf",
|
||||
IpProto::Eigrp => "Eigrp",
|
||||
IpProto::Ospfigp => "Ospfigp",
|
||||
IpProto::SpriteRpc => "SpriteRpc",
|
||||
IpProto::Larp => "Larp",
|
||||
IpProto::Mtp => "Mtp",
|
||||
IpProto::Ax25 => "Ax25",
|
||||
IpProto::Ipip => "Ipip",
|
||||
IpProto::Micp => "Micp",
|
||||
IpProto::SccSp => "SccSp",
|
||||
IpProto::Etherip => "Etherip",
|
||||
IpProto::Encap => "Encap",
|
||||
IpProto::AnyPrivateEncryptionScheme => "AnyPrivateEncryptionScheme",
|
||||
IpProto::Gmtp => "Gmtp",
|
||||
IpProto::Ifmp => "Ifmp",
|
||||
IpProto::Pnni => "Pnni",
|
||||
IpProto::Pim => "Pim",
|
||||
IpProto::Aris => "Aris",
|
||||
IpProto::Scps => "Scps",
|
||||
IpProto::Qnx => "Qnx",
|
||||
IpProto::ActiveNetworks => "ActiveNetworks",
|
||||
IpProto::IpComp => "IpComp",
|
||||
IpProto::Snp => "Snp",
|
||||
IpProto::CompaqPeer => "CompaqPeer",
|
||||
IpProto::IpxInIp => "IpxInIp",
|
||||
IpProto::Vrrp => "Vrrp",
|
||||
IpProto::Pgm => "Pgm",
|
||||
IpProto::AnyZeroHopProtocol => "AnyZeroHopProtocol",
|
||||
IpProto::L2tp => "L2tp",
|
||||
IpProto::Ddx => "Ddx",
|
||||
IpProto::Iatp => "Iatp",
|
||||
IpProto::Stp => "Stp",
|
||||
IpProto::Srp => "Srp",
|
||||
IpProto::Uti => "Uti",
|
||||
IpProto::Smp => "Smp",
|
||||
IpProto::Sm => "Sm",
|
||||
IpProto::Ptp => "Ptp",
|
||||
IpProto::IsisOverIpv4 => "IsisOverIpv4",
|
||||
IpProto::Fire => "Fire",
|
||||
IpProto::Crtp => "Crtp",
|
||||
IpProto::Crudp => "Crudp",
|
||||
IpProto::Sscopmce => "Sscopmce",
|
||||
IpProto::Iplt => "Iplt",
|
||||
IpProto::Sps => "Sps",
|
||||
IpProto::Pipe => "Pipe",
|
||||
IpProto::Sctp => "Sctp",
|
||||
IpProto::Fc => "Fc",
|
||||
IpProto::RsvpE2eIgnore => "RsvpE2eIgnore",
|
||||
IpProto::MobilityHeader => "MobilityHeader",
|
||||
IpProto::UdpLite => "UdpLite",
|
||||
IpProto::Mpls => "Mpls",
|
||||
IpProto::Manet => "Manet",
|
||||
IpProto::Hip => "Hip",
|
||||
IpProto::Shim6 => "Shim6",
|
||||
IpProto::Wesp => "Wesp",
|
||||
IpProto::Rohc => "Rohc",
|
||||
IpProto::EthernetInIpv4 => "EthernetInIpv4",
|
||||
IpProto::Aggfrag => "Aggfrag",
|
||||
IpProto::Test1 => "Test1",
|
||||
IpProto::Test2 => "Test2",
|
||||
IpProto::Reserved => "Reserved",
|
||||
})
|
||||
}
|
1
net-logger/src/utils/mod.rs
Normal file
1
net-logger/src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod ip;
|
8
xtask/Cargo.toml
Normal file
8
xtask/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
clap = { version = "4.1", features = ["derive"] }
|
42
xtask/src/build.rs
Normal file
42
xtask/src/build.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use clap::Parser;
|
||||
|
||||
use crate::build_ebpf::{build_ebpf, Architecture, Options as BuildOptions};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Options {
|
||||
/// Set the endianness of the BPF target
|
||||
#[clap(default_value = "bpfel-unknown-none", long)]
|
||||
pub bpf_target: Architecture,
|
||||
/// Build and run the release target
|
||||
#[clap(long)]
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
/// Build the project
|
||||
fn build_project(opts: &Options) -> Result<(), anyhow::Error> {
|
||||
let mut args = vec!["build"];
|
||||
if opts.release {
|
||||
args.push("--release")
|
||||
}
|
||||
let status = Command::new("cargo")
|
||||
.args(&args)
|
||||
.status()
|
||||
.expect("failed to build userspace");
|
||||
assert!(status.success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build our ebpf program and the project
|
||||
pub fn build(opts: Options) -> Result<(), anyhow::Error> {
|
||||
// build our ebpf program followed by our application
|
||||
build_ebpf(BuildOptions {
|
||||
target: opts.bpf_target,
|
||||
release: opts.release,
|
||||
})
|
||||
.context("Error while building eBPF program")?;
|
||||
build_project(&opts).context("Error while building userspace application")?;
|
||||
Ok(())
|
||||
}
|
67
xtask/src/build_ebpf.rs
Normal file
67
xtask/src/build_ebpf.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use std::{path::PathBuf, process::Command};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Architecture {
|
||||
BpfEl,
|
||||
BpfEb,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Architecture {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"bpfel-unknown-none" => Architecture::BpfEl,
|
||||
"bpfeb-unknown-none" => Architecture::BpfEb,
|
||||
_ => return Err("invalid target".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Architecture {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Architecture::BpfEl => "bpfel-unknown-none",
|
||||
Architecture::BpfEb => "bpfeb-unknown-none",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Options {
|
||||
/// Set the endianness of the BPF target
|
||||
#[clap(default_value = "bpfel-unknown-none", long)]
|
||||
pub target: Architecture,
|
||||
/// Build the release target
|
||||
#[clap(long)]
|
||||
pub release: bool,
|
||||
}
|
||||
|
||||
pub fn build_ebpf(opts: Options) -> Result<(), anyhow::Error> {
|
||||
let dir = PathBuf::from("net-logger-ebpf");
|
||||
let target = format!("--target={}", opts.target);
|
||||
let mut args = vec![
|
||||
"build",
|
||||
target.as_str(),
|
||||
"-Z",
|
||||
"build-std=core",
|
||||
];
|
||||
if opts.release {
|
||||
args.push("--release")
|
||||
}
|
||||
|
||||
// Command::new creates a child process which inherits all env variables. This means env
|
||||
// vars set by the cargo xtask command are also inherited. RUSTUP_TOOLCHAIN is removed
|
||||
// so the rust-toolchain.toml file in the -ebpf folder is honored.
|
||||
|
||||
let status = Command::new("cargo")
|
||||
.current_dir(dir)
|
||||
.env_remove("RUSTUP_TOOLCHAIN")
|
||||
.args(&args)
|
||||
.status()
|
||||
.expect("failed to build bpf program");
|
||||
assert!(status.success());
|
||||
Ok(())
|
||||
}
|
36
xtask/src/main.rs
Normal file
36
xtask/src/main.rs
Normal file
@ -0,0 +1,36 @@
|
||||
mod build_ebpf;
|
||||
mod build;
|
||||
mod run;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Options {
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
enum Command {
|
||||
BuildEbpf(build_ebpf::Options),
|
||||
Build(build::Options),
|
||||
Run(run::Options),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts = Options::parse();
|
||||
|
||||
use Command::*;
|
||||
let ret = match opts.command {
|
||||
BuildEbpf(opts) => build_ebpf::build_ebpf(opts),
|
||||
Run(opts) => run::run(opts),
|
||||
Build(opts) => build::build(opts),
|
||||
};
|
||||
|
||||
if let Err(e) = ret {
|
||||
eprintln!("{e:#}");
|
||||
exit(1);
|
||||
}
|
||||
}
|
55
xtask/src/run.rs
Normal file
55
xtask/src/run.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use clap::Parser;
|
||||
|
||||
use crate::{build::{build, Options as BuildOptions}, build_ebpf::Architecture};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Options {
|
||||
/// Set the endianness of the BPF target
|
||||
#[clap(default_value = "bpfel-unknown-none", long)]
|
||||
pub bpf_target: Architecture,
|
||||
/// Build and run the release target
|
||||
#[clap(long)]
|
||||
pub release: bool,
|
||||
/// The command used to wrap your application
|
||||
#[clap(short, long, default_value = "sudo -E")]
|
||||
pub runner: String,
|
||||
/// Arguments to pass to your application
|
||||
#[clap(name = "args", last = true)]
|
||||
pub run_args: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
/// Build and run the project
|
||||
pub fn run(opts: Options) -> Result<(), anyhow::Error> {
|
||||
// Build our ebpf program and the project
|
||||
build(BuildOptions{
|
||||
bpf_target: opts.bpf_target,
|
||||
release: opts.release,
|
||||
}).context("Error while building project")?;
|
||||
|
||||
// profile we are building (release or debug)
|
||||
let profile = if opts.release { "release" } else { "debug" };
|
||||
let bin_path = format!("target/{profile}/net-logger");
|
||||
|
||||
// arguments to pass to the application
|
||||
let mut run_args: Vec<_> = opts.run_args.iter().map(String::as_str).collect();
|
||||
|
||||
// configure args
|
||||
let mut args: Vec<_> = opts.runner.trim().split_terminator(' ').collect();
|
||||
args.push(bin_path.as_str());
|
||||
args.append(&mut run_args);
|
||||
|
||||
// run the command
|
||||
let status = Command::new(args.first().expect("No first argument"))
|
||||
.args(args.iter().skip(1))
|
||||
.status()
|
||||
.expect("failed to run the command");
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("Failed to run `{}`", args.join(" "));
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user