mirror of
https://github.com/DaLaw2/NetGuardia.git
synced 2026-08-24 14:10:28 +09:00
Feat/ml api (#13)
* wip: implement ml inference api * wip: adjust code * feat: Add ML alert api components * add: Add private ip analysis * fix: Fix compile error * refactor: Change compilation place of frontend * refactor: Change compilation place of frontend * feat: Using cic2018 models and fix feature extraction * feat: Using the torch models * wip: Continuous optimization model * wip: Add TODO * wip: Add csv record * wip: Continuously improve the inference * fix: use actual bulk duration for Fwd/Bwd Avg Bulk Rate * chore: adjust code
This commit is contained in:
parent
55edf01113
commit
26f2e1851b
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,7 +10,7 @@ target/
|
||||
|
||||
.idea
|
||||
logs
|
||||
.env
|
||||
TODO
|
||||
.log
|
||||
.txt
|
||||
net-guardia/static/web
|
||||
|
||||
331
Cargo.lock
generated
331
Cargo.lock
generated
@ -61,9 +61,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.11.1"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cceded2fb55f3c4b67068fa64962e2ca59614edc5b03167de9ff82ae803da0"
|
||||
checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
@ -110,9 +110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-router"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
|
||||
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
|
||||
dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
@ -173,9 +173,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.11.0"
|
||||
version = "4.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea"
|
||||
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
@ -208,7 +208,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2 0.5.8",
|
||||
"socket2 0.6.0",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
@ -228,16 +228,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-ws"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3a1fb4f9f2794b0aadaf2ba5f14a6f034c7e86957b458c506a8cb75953f2d99"
|
||||
checksum = "decf53c3cdd63dd6f289980b430238f9a2f6d19f8bce8e418272e08d3da43f0f"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"bytestring",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -251,15 +253,6 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
@ -465,21 +458,6 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@ -596,31 +574,14 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-util-schemas"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-untagged",
|
||||
"serde-value",
|
||||
"thiserror 2.0.16",
|
||||
"toml 0.8.20",
|
||||
"unicode-xid",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.22.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3f56c207c76c07652489840ff98687dcf213de178ac0974660d6fefeaf5ec6"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"cargo-util-schemas",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -920,16 +881,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
@ -1116,12 +1067,6 @@ dependencies = [
|
||||
"wasi 0.14.3+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
@ -1130,9 +1075,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -1370,12 +1315,12 @@ checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1389,17 +1334,6 @@ dependencies = [
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.21.1"
|
||||
@ -1503,7 +1437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1800,7 +1734,7 @@ dependencies = [
|
||||
"thiserror 2.0.16",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"toml 0.9.5",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
"tracing-subscriber",
|
||||
@ -1967,15 +1901,6 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
@ -1996,7 +1921,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.5.8",
|
||||
"smallvec",
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2359,12 +2284,6 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
@ -2455,39 +2374,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-untagged"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
|
||||
dependencies = [
|
||||
"erased-serde",
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-value"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||
dependencies = [
|
||||
"ordered-float",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2508,20 +2416,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2681,9 +2580,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.37.0"
|
||||
version = "0.38.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
|
||||
checksum = "1efc19935b4b66baa6f654ac7924c192f55b175c00a7ab72410fc24284dacda8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memchr",
|
||||
@ -2812,29 +2711,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.47.1"
|
||||
version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2 0.6.0",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2862,32 +2758,22 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"hashbrown 0.14.5",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.20"
|
||||
version = "1.0.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned 0.6.8",
|
||||
"toml_datetime 0.6.8",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
|
||||
checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned 1.0.0",
|
||||
"toml_datetime 0.7.0",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
@ -2895,49 +2781,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
version = "1.0.0+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned 0.6.8",
|
||||
"toml_datetime 0.6.8",
|
||||
"winnow",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.2"
|
||||
version = "1.0.9+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.2"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
|
||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@ -3183,12 +3047,6 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@ -3345,55 +3203,54 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
version = "0.62.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
checksum = "49e6c4a1f363c8210c6f77ba24f645c61c6fb941eccf013da691f7e09515b8ac"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link 0.1.3",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
checksum = "123e712f464a8a60ce1a13f4c446d2d43ab06464cb5842ff68f5c71b6fb7852e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
version = "0.62.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
checksum = "68f3db6b24b120200d649cd4811b4947188ed3a8d2626f7075146c5d178a9a4a"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3402,21 +3259,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
@ -3425,30 +3276,30 @@ checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3469,6 +3320,15 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@ -3487,11 +3347,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
checksum = "ab47f085ad6932defa48855254c758cdd0e2f2d48e62a34118a268d8f345e118"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3547,9 +3407,6 @@ name = "winnow"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
|
||||
@ -7,8 +7,8 @@ default-members = ["net-guardia", "common"]
|
||||
aya = { version = "0.13.1", default-features = false }
|
||||
aya-ebpf = { version = "0.1.1", default-features = false }
|
||||
aya-log = { version = "0.2.1", default-features = false }
|
||||
aya-log-ebpf = { version = "0.1.1", default-features = false }
|
||||
cargo_metadata = { version = "0.22.0", default-features = false }
|
||||
aya-log-ebpf = { version = "0.1.0", default-features = false }
|
||||
cargo_metadata = { version = "0.23.1", default-features = false }
|
||||
libc = { version = "0.2.159", default-features = false }
|
||||
network-types = "0.1.0"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
||||
@ -7,4 +7,5 @@ pub mod ingress {
|
||||
|
||||
pub mod egress {
|
||||
pub const STATISTICS: u32 = 0;
|
||||
pub const TRANSMISSION: u32 = 1;
|
||||
}
|
||||
|
||||
@ -44,28 +44,28 @@ unsafe fn parse_ipv4_packet(start: usize, end: usize, target: *mut Event) -> Res
|
||||
core::ptr::write(ipv4_data_ptr as *mut IpProto, ipv4.proto);
|
||||
core::ptr::copy_nonoverlapping(
|
||||
ipv4.src_addr.as_ptr(),
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, source_ip)),
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, src_ip)),
|
||||
4,
|
||||
);
|
||||
core::ptr::copy_nonoverlapping(
|
||||
ipv4.dst_addr.as_ptr(),
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, destination_ip)),
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, dst_ip)),
|
||||
4,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, source_port)) as *mut u16,
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, src_port)) as *mut u16,
|
||||
source_port,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, destination_port)) as *mut u16,
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, dst_port)) as *mut u16,
|
||||
destination_port,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, len)) as *mut u32,
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, packet_length)) as *mut u32,
|
||||
(end - start) as u32,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, timestamp)) as *mut u64,
|
||||
ipv4_data_ptr.add(core::mem::offset_of!(IPv4Event, timestamp_us)) as *mut u64,
|
||||
bpf_ktime_get_ns(),
|
||||
);
|
||||
|
||||
@ -95,28 +95,28 @@ unsafe fn parse_ipv6_packet(start: usize, end: usize, target: *mut Event) -> Res
|
||||
core::ptr::write(ipv6_data_ptr as *mut IpProto, ipv6.next_hdr);
|
||||
core::ptr::copy_nonoverlapping(
|
||||
ipv6.src_addr.as_ptr(),
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, source_ip)),
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, src_ip)),
|
||||
16,
|
||||
);
|
||||
core::ptr::copy_nonoverlapping(
|
||||
ipv6.dst_addr.as_ptr(),
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, destination_ip)),
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, dst_ip)),
|
||||
16,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, source_port)) as *mut u16,
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, src_port)) as *mut u16,
|
||||
source_port,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, destination_port)) as *mut u16,
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, dst_port)) as *mut u16,
|
||||
destination_port,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, len)) as *mut u32,
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, packet_length)) as *mut u32,
|
||||
(end - start) as u32,
|
||||
);
|
||||
core::ptr::write(
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, timestamp)) as *mut u64,
|
||||
ipv6_data_ptr.add(core::mem::offset_of!(IPv6Event, timestamp_us)) as *mut u64,
|
||||
bpf_ktime_get_ns(),
|
||||
);
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use network_types::ip::IpProto;
|
||||
|
||||
use crate::model::ip_address::{AddrPortV4, AddrPortV6};
|
||||
@ -10,6 +9,13 @@ pub enum Event {
|
||||
IPv6(IPv6Event),
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Clone)]
|
||||
pub enum RawIp {
|
||||
V4(u32),
|
||||
V6(u128),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn timestamp_us(&self) -> u64 {
|
||||
match self {
|
||||
@ -67,25 +73,17 @@ impl Event {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn src_ip(&self) -> IpAddr {
|
||||
pub fn src_ip(&self) -> RawIp {
|
||||
match self {
|
||||
Event::IPv4(e) => {
|
||||
IpAddr::V4(Ipv4Addr::from(e.src_ip))
|
||||
}
|
||||
Event::IPv6(e) => {
|
||||
IpAddr::V6(Ipv6Addr::from(e.src_ip))
|
||||
}
|
||||
Event::IPv4(e) => RawIp::V4(e.src_ip),
|
||||
Event::IPv6(e) => RawIp::V6(e.src_ip),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dst_ip(&self) -> IpAddr {
|
||||
pub fn dst_ip(&self) -> RawIp {
|
||||
match self {
|
||||
Event::IPv4(e) => {
|
||||
IpAddr::V4(Ipv4Addr::from(e.dst_ip))
|
||||
}
|
||||
Event::IPv6(e) => {
|
||||
IpAddr::V6(Ipv6Addr::from(e.dst_ip))
|
||||
}
|
||||
Event::IPv4(e) => RawIp::V4(e.dst_ip),
|
||||
Event::IPv6(e) => RawIp::V6(e.dst_ip),
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,6 +168,7 @@ impl IPv6Event {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TcpFlags {
|
||||
pub fin: bool,
|
||||
|
||||
10
config.toml
10
config.toml
@ -3,8 +3,7 @@ ingress_ifname = "enp4s0f1" # Ingress NIC Name
|
||||
egress_ifname = "enp4s0f0" # Egress NIC Name
|
||||
geoip_db_name = "GeoLite2-City.mmdb"
|
||||
deep_autoencoder_name = "deep_autoencoder.onnx"
|
||||
random_forest_name = "random_forest.onnx"
|
||||
mlp_name = "mlp.onnx"
|
||||
classifier_name = "classifier.onnx"
|
||||
models_config_name = "inference_config.json"
|
||||
combined_queue_count = 8 # NIC Combined Queue Count (ethtool -l <NIC>)
|
||||
channel_size = 4096
|
||||
@ -19,4 +18,9 @@ refresh_interval = 5 # Statistics Refresh Time
|
||||
|
||||
max_concurrent_flows = 10000 # max_flows: track up to 10000 concurrent flows
|
||||
min_packets_for_inference = 5 # min_packets: minimum 10 packets per flow for inference
|
||||
inference_interval_secs = 5 # interval_secs: run inference every 5 seconds
|
||||
inference_interval_secs = 5 # interval_secs: run inference every 5 seconds
|
||||
aggregator_window_secs = 30
|
||||
inference_batch_size = 200
|
||||
|
||||
traffic_logging_mode = true # When true, disables ML inference and records all ingress/egress packets to CSV
|
||||
traffic_log_csv_path = "traffic_log.csv" # Output CSV file path for traffic logging mode
|
||||
@ -60,14 +60,14 @@ pub fn ipv6_update_stats(event: &IPv6Event) {
|
||||
unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &AddrPortV4, event: &IPv4Event) {
|
||||
unsafe {
|
||||
if let Some(status) = map.get_ptr_mut(key) {
|
||||
(*status).bytes += event.len as u64;
|
||||
(*status).bytes += event.packet_length as u64;
|
||||
(*status).packets += 1;
|
||||
(*status).last_seen = event.timestamp;
|
||||
(*status).last_seen = event.timestamp_us;
|
||||
} else {
|
||||
let new_stats = FlowStats {
|
||||
bytes: event.len as u64,
|
||||
bytes: event.packet_length as u64,
|
||||
packets: 1,
|
||||
last_seen: event.timestamp,
|
||||
last_seen: event.timestamp_us,
|
||||
};
|
||||
let _ = map.insert(key, &new_stats, 0);
|
||||
}
|
||||
@ -78,14 +78,14 @@ unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &
|
||||
unsafe fn ipv6_update_flow_status(map: &LruHashMap<AddrPortV6, FlowStats>, key: &AddrPortV6, event: &IPv6Event) {
|
||||
unsafe {
|
||||
if let Some(status) = map.get_ptr_mut(key) {
|
||||
(*status).bytes += event.len as u64;
|
||||
(*status).bytes += event.packet_length as u64;
|
||||
(*status).packets += 1;
|
||||
(*status).last_seen = event.timestamp;
|
||||
(*status).last_seen = event.timestamp_us;
|
||||
} else {
|
||||
let new_stats = FlowStats {
|
||||
bytes: event.len as u64,
|
||||
bytes: event.packet_length as u64,
|
||||
packets: 1,
|
||||
last_seen: event.timestamp,
|
||||
last_seen: event.timestamp_us,
|
||||
};
|
||||
let _ = map.insert(key, &new_stats, 0);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ mod action;
|
||||
use action::statistics;
|
||||
use aya_ebpf::bindings::xdp_action;
|
||||
use aya_ebpf::macros::{map, xdp};
|
||||
use aya_ebpf::maps::{PerCpuArray, ProgramArray};
|
||||
use aya_ebpf::maps::{PerCpuArray, ProgramArray, XskMap};
|
||||
use aya_ebpf::programs::XdpContext;
|
||||
#[allow(unused_imports)]
|
||||
use aya_log_ebpf::info;
|
||||
@ -16,6 +16,8 @@ use common::{ebpf::parsing, model::event::Event};
|
||||
static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
|
||||
#[map]
|
||||
static PARSED_PACKET: PerCpuArray<Event> = PerCpuArray::with_max_entries(1, 0);
|
||||
#[map]
|
||||
static EGRESS_XSKS_MAP: XskMap = XskMap::pinned(64, 0);
|
||||
|
||||
#[xdp]
|
||||
pub fn net_guardia(ctx: XdpContext) -> u32 {
|
||||
@ -39,12 +41,12 @@ unsafe fn packet_intake(ctx: XdpContext) -> Result<u32, ()> {
|
||||
#[xdp]
|
||||
pub fn statistics(ctx: XdpContext) -> u32 {
|
||||
unsafe {
|
||||
let _ = try_statistics(ctx);
|
||||
let _ = try_statistics(&ctx);
|
||||
xdp_action::XDP_PASS
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn try_statistics(_: XdpContext) -> Result<u32, ()> {
|
||||
unsafe fn try_statistics(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
unsafe {
|
||||
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
|
||||
let parsed_packet = &*ptr;
|
||||
@ -56,10 +58,20 @@ unsafe fn try_statistics(_: XdpContext) -> Result<u32, ()> {
|
||||
statistics::ipv6_update_stats(event);
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, TRANSMISSION);
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
||||
}
|
||||
|
||||
#[xdp]
|
||||
pub fn transmission(ctx: XdpContext) -> u32 {
|
||||
let queue_id = unsafe { (*ctx.ctx).rx_queue_index };
|
||||
match EGRESS_XSKS_MAP.redirect(queue_id, 0) {
|
||||
Ok(action) => action,
|
||||
Err(_) => xdp_action::XDP_PASS,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
|
||||
@ -23,13 +23,13 @@ static IPV6_DST_BLACKLIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> = HashMap::with
|
||||
|
||||
pub fn ipv4_is_whitelisted(event: &IPv4Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = IPV4_SRC_WHITELIST.get(&event.source_ip) {
|
||||
if is_port_exist(ports, event.source_port) {
|
||||
if let Some(ports) = IPV4_SRC_WHITELIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = IPV4_DST_WHITELIST.get(&event.destination_ip) {
|
||||
if is_port_exist(ports, event.destination_port) {
|
||||
if let Some(ports) = IPV4_DST_WHITELIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -39,13 +39,13 @@ pub fn ipv4_is_whitelisted(event: &IPv4Event) -> bool {
|
||||
|
||||
pub fn ipv6_is_whitelisted(event: &IPv6Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = IPV6_SRC_WHITELIST.get(&event.source_ip) {
|
||||
if is_port_exist(ports, event.source_port) {
|
||||
if let Some(ports) = IPV6_SRC_WHITELIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = IPV6_DST_WHITELIST.get(&event.destination_ip) {
|
||||
if is_port_exist(ports, event.destination_port) {
|
||||
if let Some(ports) = IPV6_DST_WHITELIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -55,13 +55,13 @@ pub fn ipv6_is_whitelisted(event: &IPv6Event) -> bool {
|
||||
|
||||
pub fn ipv4_is_blacklisted(event: &IPv4Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = IPV4_SRC_BLACKLIST.get(&event.source_ip) {
|
||||
if is_port_exist(ports, event.source_port) {
|
||||
if let Some(ports) = IPV4_SRC_BLACKLIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = IPV4_DST_BLACKLIST.get(&event.destination_ip) {
|
||||
if is_port_exist(ports, event.destination_port) {
|
||||
if let Some(ports) = IPV4_DST_BLACKLIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -71,13 +71,13 @@ pub fn ipv4_is_blacklisted(event: &IPv4Event) -> bool {
|
||||
|
||||
pub fn ipv6_is_blacklisted(event: &IPv6Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = IPV6_SRC_BLACKLIST.get(&event.source_ip) {
|
||||
if is_port_exist(ports, event.source_port) {
|
||||
if let Some(ports) = IPV6_SRC_BLACKLIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = IPV6_DST_BLACKLIST.get(&event.destination_ip) {
|
||||
if is_port_exist(ports, event.destination_port) {
|
||||
if let Some(ports) = IPV6_DST_BLACKLIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,14 +60,14 @@ pub fn ipv6_update_stats(event: &IPv6Event) {
|
||||
unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &AddrPortV4, event: &IPv4Event) {
|
||||
unsafe {
|
||||
if let Some(status) = map.get_ptr_mut(key) {
|
||||
(*status).bytes += event.len as u64;
|
||||
(*status).bytes += event.packet_length as u64;
|
||||
(*status).packets += 1;
|
||||
(*status).last_seen = event.timestamp;
|
||||
(*status).last_seen = event.timestamp_us;
|
||||
} else {
|
||||
let new_stats = FlowStats {
|
||||
bytes: event.len as u64,
|
||||
bytes: event.packet_length as u64,
|
||||
packets: 1,
|
||||
last_seen: event.timestamp,
|
||||
last_seen: event.timestamp_us,
|
||||
};
|
||||
let _ = map.insert(key, &new_stats, 0);
|
||||
}
|
||||
@ -78,14 +78,14 @@ unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &
|
||||
unsafe fn ipv6_update_flow_status(map: &LruHashMap<AddrPortV6, FlowStats>, key: &AddrPortV6, event: &IPv6Event) {
|
||||
unsafe {
|
||||
if let Some(status) = map.get_ptr_mut(key) {
|
||||
(*status).bytes += event.len as u64;
|
||||
(*status).bytes += event.packet_length as u64;
|
||||
(*status).packets += 1;
|
||||
(*status).last_seen = event.timestamp;
|
||||
(*status).last_seen = event.timestamp_us;
|
||||
} else {
|
||||
let new_stats = FlowStats {
|
||||
bytes: event.len as u64,
|
||||
bytes: event.packet_length as u64,
|
||||
packets: 1,
|
||||
last_seen: event.timestamp,
|
||||
last_seen: event.timestamp_us,
|
||||
};
|
||||
let _ = map.insert(key, &new_stats, 0);
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
|
||||
#[map]
|
||||
static PARSED_PACKET: PerCpuArray<Event> = PerCpuArray::with_max_entries(1, 0);
|
||||
#[map]
|
||||
static XSKS_MAP: XskMap = XskMap::pinned(64, 0);
|
||||
static INGRESS_XSKS_MAP: XskMap = XskMap::pinned(64, 0);
|
||||
|
||||
#[xdp]
|
||||
pub fn net_guardia(ctx: XdpContext) -> u32 {
|
||||
@ -151,7 +151,7 @@ unsafe fn try_statistics(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
#[xdp]
|
||||
pub fn transmission(ctx: XdpContext) -> u32 {
|
||||
let queue_id = unsafe { (*ctx.ctx).rx_queue_index };
|
||||
match XSKS_MAP.redirect(queue_id, 0) {
|
||||
match INGRESS_XSKS_MAP.redirect(queue_id, 0) {
|
||||
Ok(action) => action,
|
||||
Err(_) => xdp_action::XDP_PASS,
|
||||
}
|
||||
|
||||
BIN
models/classifier.onnx
Normal file
BIN
models/classifier.onnx
Normal file
Binary file not shown.
Binary file not shown.
@ -1,577 +1,277 @@
|
||||
{
|
||||
"created_at": "2026-01-27T13:05:39.258903",
|
||||
"created_at": "2026-03-05T14:04:08.736531",
|
||||
"framework": "PyTorch",
|
||||
"model": {
|
||||
"deep_autoencoder": {
|
||||
"file": "deep_autoencoder.onnx",
|
||||
"input_dim": 78,
|
||||
"encoding_dim": 16
|
||||
"input_dim": 27,
|
||||
"encoding_dim": 16,
|
||||
"ae_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
"bwd_packets",
|
||||
"fwd_bytes",
|
||||
"bwd_bytes",
|
||||
"flow_bytes_per_sec",
|
||||
"flow_pkts_per_sec",
|
||||
"fwd_win_bytes",
|
||||
"bwd_win_bytes",
|
||||
"fwd_pkt_len_mean",
|
||||
"bwd_pkt_len_mean",
|
||||
"fwd_iat_mean",
|
||||
"bwd_iat_mean",
|
||||
"flow_iat_mean",
|
||||
"pkt_len_mean",
|
||||
"dst_port",
|
||||
"protocol",
|
||||
"psh_flag_cnt",
|
||||
"ack_flag_cnt",
|
||||
"syn_flag_cnt",
|
||||
"fin_flag_cnt",
|
||||
"rst_flag_cnt",
|
||||
"pkt_len_std",
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
],
|
||||
"ae_threshold": 0.17166012525558472
|
||||
},
|
||||
"random_forest": {
|
||||
"file": "random_forest.onnx",
|
||||
"n_estimators": 100,
|
||||
"n_features": 78
|
||||
},
|
||||
"mlp_classifier": {
|
||||
"file": "mlp.onnx",
|
||||
"input_dim": 78,
|
||||
"n_classes": 10
|
||||
"classifier": {
|
||||
"file": "classifier.onnx",
|
||||
"type": "ResNet MLP",
|
||||
"n_features": 28,
|
||||
"n_classes": 5,
|
||||
"classifier_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
"bwd_packets",
|
||||
"fwd_bytes",
|
||||
"bwd_bytes",
|
||||
"flow_bytes_per_sec",
|
||||
"flow_pkts_per_sec",
|
||||
"fwd_win_bytes",
|
||||
"bwd_win_bytes",
|
||||
"fwd_pkt_len_mean",
|
||||
"bwd_pkt_len_mean",
|
||||
"fwd_iat_mean",
|
||||
"bwd_iat_mean",
|
||||
"flow_iat_mean",
|
||||
"pkt_len_mean",
|
||||
"dst_port",
|
||||
"protocol",
|
||||
"psh_flag_cnt",
|
||||
"ack_flag_cnt",
|
||||
"syn_flag_cnt",
|
||||
"fin_flag_cnt",
|
||||
"rst_flag_cnt",
|
||||
"pkt_len_std",
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts",
|
||||
"ae_anomaly_score"
|
||||
]
|
||||
}
|
||||
},
|
||||
"preprocessing": {
|
||||
"clip_params": {
|
||||
"Destination Port": {
|
||||
"lower": 22.0,
|
||||
"upper": 63734.0
|
||||
},
|
||||
"Flow Duration": {
|
||||
"ae_clip_params": {
|
||||
"flow_duration": {
|
||||
"lower": 1.0,
|
||||
"upper": 118756600.72000001
|
||||
"upper": 118485085.19000001
|
||||
},
|
||||
"Total Fwd Packets": {
|
||||
"lower": 1.0,
|
||||
"upper": 98.0
|
||||
},
|
||||
"Total Backward Packets": {
|
||||
"fwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 126.0
|
||||
"upper": 54.0
|
||||
},
|
||||
"Total Length of Fwd Packets": {
|
||||
"bwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 13119.520000000019
|
||||
"upper": 82.0
|
||||
},
|
||||
"Total Length of Bwd Packets": {
|
||||
"fwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 186394.64000000013
|
||||
"upper": 6960.0
|
||||
},
|
||||
"Fwd Packet Length Max": {
|
||||
"bwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 5840.0
|
||||
"upper": 89558.6950000003
|
||||
},
|
||||
"Fwd Packet Length Min": {
|
||||
"flow_bytes_per_sec": {
|
||||
"lower": 0.0,
|
||||
"upper": 98.0
|
||||
"upper": 12500000.0
|
||||
},
|
||||
"Fwd Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1932.5
|
||||
},
|
||||
"Fwd Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 2376.49858
|
||||
},
|
||||
"Bwd Packet Length Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 4380.0
|
||||
},
|
||||
"Bwd Packet Length Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 308.0
|
||||
},
|
||||
"Bwd Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1715.628244226656
|
||||
},
|
||||
"Bwd Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1167.9936145600002
|
||||
},
|
||||
"Flow Bytes\/s": {
|
||||
"lower": 0.0,
|
||||
"upper": 37000000.0
|
||||
},
|
||||
"Flow Packets\/s": {
|
||||
"lower": 0.041637660508,
|
||||
"flow_pkts_per_sec": {
|
||||
"lower": 0.0226829350855,
|
||||
"upper": 2000000.0
|
||||
},
|
||||
"Flow IAT Mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 30800000.0
|
||||
},
|
||||
"Flow IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 44253994.319940574
|
||||
},
|
||||
"Flow IAT Max": {
|
||||
"lower": 1.0,
|
||||
"upper": 96199268.00000003
|
||||
},
|
||||
"Flow IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 1999969.04
|
||||
},
|
||||
"Fwd IAT Total": {
|
||||
"lower": 0.0,
|
||||
"upper": 119000000.0
|
||||
},
|
||||
"Fwd IAT Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 74300000.0
|
||||
},
|
||||
"Fwd IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 35894248.669783354
|
||||
},
|
||||
"Fwd IAT Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 97800000.0
|
||||
},
|
||||
"Fwd IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 74300000.0
|
||||
},
|
||||
"Bwd IAT Total": {
|
||||
"lower": 0.0,
|
||||
"upper": 118000000.0
|
||||
},
|
||||
"Bwd IAT Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 73734410.76000024
|
||||
},
|
||||
"Bwd IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 30100000.0
|
||||
},
|
||||
"Bwd IAT Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 93800000.0
|
||||
},
|
||||
"Bwd IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 73734410.76000024
|
||||
},
|
||||
"Fwd PSH Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"Bwd PSH Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd URG Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd URG Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Header Length": {
|
||||
"lower": 20.0,
|
||||
"upper": 2376.0
|
||||
},
|
||||
"Bwd Header Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 3312.0
|
||||
},
|
||||
"Fwd Packets\/s": {
|
||||
"lower": 0.02417299188,
|
||||
"upper": 2000000.0
|
||||
},
|
||||
"Bwd Packets\/s": {
|
||||
"lower": 0.0,
|
||||
"upper": 142857.1429
|
||||
},
|
||||
"Min Packet Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 89.0
|
||||
},
|
||||
"Max Packet Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 5840.0
|
||||
},
|
||||
"Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1153.4362532
|
||||
},
|
||||
"Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1845.154148000012
|
||||
},
|
||||
"Packet Length Variance": {
|
||||
"lower": 0.0,
|
||||
"upper": 3404593.931120044
|
||||
},
|
||||
"FIN Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"SYN Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"RST Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"PSH Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"ACK Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"URG Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"CWE Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"ECE Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Down\/Up Ratio": {
|
||||
"lower": 0.0,
|
||||
"upper": 5.0
|
||||
},
|
||||
"Average Packet Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1182.362785582229
|
||||
},
|
||||
"Avg Fwd Segment Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1932.5
|
||||
},
|
||||
"Avg Bwd Segment Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1715.628244226656
|
||||
},
|
||||
"Fwd Header Length.1": {
|
||||
"lower": 20.0,
|
||||
"upper": 2376.0
|
||||
},
|
||||
"Fwd Avg Bytes\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Avg Packets\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Avg Bulk Rate": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Bytes\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Packets\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Bulk Rate": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Subflow Fwd Packets": {
|
||||
"lower": 1.0,
|
||||
"upper": 98.0
|
||||
},
|
||||
"Subflow Fwd Bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 13119.520000000019
|
||||
},
|
||||
"Subflow Bwd Packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 126.0
|
||||
},
|
||||
"Subflow Bwd Bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 186394.64000000013
|
||||
},
|
||||
"Init_Win_bytes_forward": {
|
||||
"fwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
},
|
||||
"Init_Win_bytes_backward": {
|
||||
"bwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
"upper": 64000.0
|
||||
},
|
||||
"act_data_pkt_fwd": {
|
||||
"fwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 49.0
|
||||
"upper": 744.7272727272729
|
||||
},
|
||||
"min_seg_size_forward": {
|
||||
"lower": 20.0,
|
||||
"bwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1393.9601153594992
|
||||
},
|
||||
"fwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 86366722.035
|
||||
},
|
||||
"bwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 21700000.0
|
||||
},
|
||||
"flow_iat_mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 86353400.64500001
|
||||
},
|
||||
"pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 945.0294113974604
|
||||
},
|
||||
"dst_port": {
|
||||
"lower": 0.0,
|
||||
"upper": 64499.0
|
||||
},
|
||||
"protocol": {
|
||||
"lower": 0.0,
|
||||
"upper": 17.0
|
||||
},
|
||||
"psh_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 240.0
|
||||
},
|
||||
"ack_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 655.0
|
||||
},
|
||||
"syn_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 4.0
|
||||
},
|
||||
"fin_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 2.0
|
||||
},
|
||||
"rst_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 943.9221842600019
|
||||
},
|
||||
"fwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 721.4199648708689
|
||||
},
|
||||
"bwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 911.0272448420104
|
||||
},
|
||||
"fwd_seg_size_min": {
|
||||
"lower": 0.0,
|
||||
"upper": 40.0
|
||||
},
|
||||
"Active Mean": {
|
||||
"fwd_act_data_pkts": {
|
||||
"lower": 0.0,
|
||||
"upper": 3410152.040000004
|
||||
},
|
||||
"Active Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1944723.8431600018
|
||||
},
|
||||
"Active Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 5717825.840000004
|
||||
},
|
||||
"Active Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 2918887.7600000002
|
||||
},
|
||||
"Idle Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 94100000.0
|
||||
},
|
||||
"Idle Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 20712312.015200634
|
||||
},
|
||||
"Idle Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 94800000.0
|
||||
},
|
||||
"Idle Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 94100000.0
|
||||
"upper": 58.0
|
||||
}
|
||||
},
|
||||
"scaler": {
|
||||
"ae_scaler": {
|
||||
"mean": [
|
||||
9415.502043247605,
|
||||
11217541.457604988,
|
||||
5.41509711200182,
|
||||
5.010050165039152,
|
||||
508.74335117243135,
|
||||
3302.166099044608,
|
||||
217.24886707430434,
|
||||
20.105687086824716,
|
||||
65.40745127720037,
|
||||
68.76070937185386,
|
||||
394.97690859650953,
|
||||
49.88046220640826,
|
||||
159.85952962743096,
|
||||
121.94653847258593,
|
||||
872411.6556228747,
|
||||
64701.11201063558,
|
||||
840255.3525863544,
|
||||
1516373.8724808302,
|
||||
4422301.697900706,
|
||||
24666.656195771673,
|
||||
10897580.686685171,
|
||||
1785372.7881935516,
|
||||
1206278.3042178946,
|
||||
4276119.216091527,
|
||||
1025069.5899462275,
|
||||
10158901.07420493,
|
||||
1666490.9735317046,
|
||||
939816.6614413982,
|
||||
3689910.1546691586,
|
||||
1003727.4123986625,
|
||||
0.054886351088404936,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
140.54050707030981,
|
||||
133.63606216540694,
|
||||
58358.168874140014,
|
||||
4983.791329731815,
|
||||
19.812641079549177,
|
||||
480.4855221752525,
|
||||
108.29833052637488,
|
||||
144.4437888623996,
|
||||
99406.03379366906,
|
||||
0.01811977227544623,
|
||||
0.054886351088404936,
|
||||
0.0,
|
||||
0.25800834720207716,
|
||||
0.28694244020382764,
|
||||
0.11596161536441252,
|
||||
0.0,
|
||||
0.0,
|
||||
0.6969992921551522,
|
||||
122.46836156918589,
|
||||
65.40745127720037,
|
||||
159.85952962743843,
|
||||
140.54050707030981,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
5.41509711200182,
|
||||
508.74335117243135,
|
||||
5.010050165039152,
|
||||
3302.166099044608,
|
||||
6891.551141020379,
|
||||
2404.6474695096604,
|
||||
2.6520584031389776,
|
||||
25.79473555242033,
|
||||
61328.50855670946,
|
||||
36311.697575423306,
|
||||
137445.81057140988,
|
||||
37549.65545208145,
|
||||
3689065.3106244486,
|
||||
167385.3556173429,
|
||||
3883594.4613142335,
|
||||
3458258.8992590285
|
||||
9783470.902880548,
|
||||
3.6532605393470496,
|
||||
3.2219949045990472,
|
||||
311.35281195367173,
|
||||
1407.508354811673,
|
||||
298997.28705051675,
|
||||
32378.36735569276,
|
||||
6158.3256759444475,
|
||||
6125.023325143747,
|
||||
58.1683929498305,
|
||||
149.72168204849987,
|
||||
2991407.86700246,
|
||||
543337.1970583026,
|
||||
2727511.340181723,
|
||||
105.03829359866097,
|
||||
13463.473246959382,
|
||||
8.920585011866685,
|
||||
5.457814320208867,
|
||||
12.214631683524651,
|
||||
0.4885976379312313,
|
||||
0.246598391240437,
|
||||
0.08478201266637703,
|
||||
151.71809799252043,
|
||||
76.89649795414182,
|
||||
143.21014925828936,
|
||||
21.447338538177014,
|
||||
3.2607463735155715
|
||||
],
|
||||
"std": [
|
||||
19743.81786383566,
|
||||
30111390.85802201,
|
||||
10.612016675700064,
|
||||
12.872110268702288,
|
||||
1653.7982707262067,
|
||||
17387.56157755931,
|
||||
645.437387791972,
|
||||
22.356844266679282,
|
||||
193.61621129020307,
|
||||
244.137250250046,
|
||||
801.628533600607,
|
||||
65.65797274491328,
|
||||
277.7559653593753,
|
||||
269.57514761088515,
|
||||
3622907.235127058,
|
||||
237431.11489053545,
|
||||
3293477.440659972,
|
||||
5309087.889319858,
|
||||
14143198.815741453,
|
||||
160458.44923675407,
|
||||
29985211.254190512,
|
||||
8130184.699376457,
|
||||
4247601.6170286415,
|
||||
14271787.10745024,
|
||||
7769581.505106356,
|
||||
29154202.839787327,
|
||||
8031691.1024753135,
|
||||
3622760.030182759,
|
||||
13368925.151392205,
|
||||
7684129.1959599955,
|
||||
0.22775829195136954,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
271.11142624961946,
|
||||
342.17520432711024,
|
||||
231133.09437362824,
|
||||
14026.707464402314,
|
||||
22.005832334437958,
|
||||
970.4322795025132,
|
||||
179.9690556845557,
|
||||
280.2535015764328,
|
||||
343919.1355100174,
|
||||
0.13338457979891152,
|
||||
0.22775829195136954,
|
||||
1.0,
|
||||
0.4375386154114052,
|
||||
0.4523344738284939,
|
||||
0.32017888613474904,
|
||||
1.0,
|
||||
1.0,
|
||||
0.6456077980707177,
|
||||
185.26118186235436,
|
||||
193.61621129020307,
|
||||
277.7559653594075,
|
||||
271.11142624961946,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
10.612016675700064,
|
||||
1653.7982707262067,
|
||||
12.872110268702288,
|
||||
17387.56157755931,
|
||||
14922.88749872485,
|
||||
9322.158869764009,
|
||||
5.784504103070997,
|
||||
6.252894021045679,
|
||||
322010.01591284445,
|
||||
201975.98444339563,
|
||||
641116.7578408231,
|
||||
245957.32066446543,
|
||||
13068297.83388166,
|
||||
1617503.1921646637,
|
||||
13724469.425904194,
|
||||
12765671.772576509
|
||||
27895851.239715174,
|
||||
6.4263677035863696,
|
||||
8.326275195918974,
|
||||
739.2548064174216,
|
||||
7898.711708881367,
|
||||
1187804.3239195002,
|
||||
190280.1548511611,
|
||||
15556.664474188405,
|
||||
17012.235322987635,
|
||||
98.89458775902833,
|
||||
242.79522892109904,
|
||||
13146157.964503227,
|
||||
2242915.166631539,
|
||||
12878527.145353919,
|
||||
151.95316919115754,
|
||||
21264.915159327567,
|
||||
4.943209145195101,
|
||||
23.02750953458606,
|
||||
57.0633472601567,
|
||||
1.2662749497077417,
|
||||
0.4553262784517569,
|
||||
0.278557037237646,
|
||||
211.1373807979287,
|
||||
143.47790203327,
|
||||
228.24531573376805,
|
||||
9.323458649060111,
|
||||
7.228913366949205
|
||||
],
|
||||
"feature_names": [
|
||||
"Destination Port",
|
||||
"Flow Duration",
|
||||
"Total Fwd Packets",
|
||||
"Total Backward Packets",
|
||||
"Total Length of Fwd Packets",
|
||||
"Total Length of Bwd Packets",
|
||||
"Fwd Packet Length Max",
|
||||
"Fwd Packet Length Min",
|
||||
"Fwd Packet Length Mean",
|
||||
"Fwd Packet Length Std",
|
||||
"Bwd Packet Length Max",
|
||||
"Bwd Packet Length Min",
|
||||
"Bwd Packet Length Mean",
|
||||
"Bwd Packet Length Std",
|
||||
"Flow Bytes\/s",
|
||||
"Flow Packets\/s",
|
||||
"Flow IAT Mean",
|
||||
"Flow IAT Std",
|
||||
"Flow IAT Max",
|
||||
"Flow IAT Min",
|
||||
"Fwd IAT Total",
|
||||
"Fwd IAT Mean",
|
||||
"Fwd IAT Std",
|
||||
"Fwd IAT Max",
|
||||
"Fwd IAT Min",
|
||||
"Bwd IAT Total",
|
||||
"Bwd IAT Mean",
|
||||
"Bwd IAT Std",
|
||||
"Bwd IAT Max",
|
||||
"Bwd IAT Min",
|
||||
"Fwd PSH Flags",
|
||||
"Bwd PSH Flags",
|
||||
"Fwd URG Flags",
|
||||
"Bwd URG Flags",
|
||||
"Fwd Header Length",
|
||||
"Bwd Header Length",
|
||||
"Fwd Packets\/s",
|
||||
"Bwd Packets\/s",
|
||||
"Min Packet Length",
|
||||
"Max Packet Length",
|
||||
"Packet Length Mean",
|
||||
"Packet Length Std",
|
||||
"Packet Length Variance",
|
||||
"FIN Flag Count",
|
||||
"SYN Flag Count",
|
||||
"RST Flag Count",
|
||||
"PSH Flag Count",
|
||||
"ACK Flag Count",
|
||||
"URG Flag Count",
|
||||
"CWE Flag Count",
|
||||
"ECE Flag Count",
|
||||
"Down\/Up Ratio",
|
||||
"Average Packet Size",
|
||||
"Avg Fwd Segment Size",
|
||||
"Avg Bwd Segment Size",
|
||||
"Fwd Header Length.1",
|
||||
"Fwd Avg Bytes\/Bulk",
|
||||
"Fwd Avg Packets\/Bulk",
|
||||
"Fwd Avg Bulk Rate",
|
||||
"Bwd Avg Bytes\/Bulk",
|
||||
"Bwd Avg Packets\/Bulk",
|
||||
"Bwd Avg Bulk Rate",
|
||||
"Subflow Fwd Packets",
|
||||
"Subflow Fwd Bytes",
|
||||
"Subflow Bwd Packets",
|
||||
"Subflow Bwd Bytes",
|
||||
"Init_Win_bytes_forward",
|
||||
"Init_Win_bytes_backward",
|
||||
"act_data_pkt_fwd",
|
||||
"min_seg_size_forward",
|
||||
"Active Mean",
|
||||
"Active Std",
|
||||
"Active Max",
|
||||
"Active Min",
|
||||
"Idle Mean",
|
||||
"Idle Std",
|
||||
"Idle Max",
|
||||
"Idle Min"
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
"bwd_packets",
|
||||
"fwd_bytes",
|
||||
"bwd_bytes",
|
||||
"flow_bytes_per_sec",
|
||||
"flow_pkts_per_sec",
|
||||
"fwd_win_bytes",
|
||||
"bwd_win_bytes",
|
||||
"fwd_pkt_len_mean",
|
||||
"bwd_pkt_len_mean",
|
||||
"fwd_iat_mean",
|
||||
"bwd_iat_mean",
|
||||
"flow_iat_mean",
|
||||
"pkt_len_mean",
|
||||
"dst_port",
|
||||
"protocol",
|
||||
"psh_flag_cnt",
|
||||
"ack_flag_cnt",
|
||||
"syn_flag_cnt",
|
||||
"fin_flag_cnt",
|
||||
"rst_flag_cnt",
|
||||
"pkt_len_std",
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
]
|
||||
},
|
||||
"post_scaling_clip": {
|
||||
@ -579,114 +279,11 @@
|
||||
"max": 5.0
|
||||
}
|
||||
},
|
||||
"ensemble": {
|
||||
"strategy_name": "Max",
|
||||
"threshold": 0.3603835832054485,
|
||||
"tpr": 0.9985636048676042,
|
||||
"fpr": 0.0029998719808261593,
|
||||
"precision": 0.9879023673677936,
|
||||
"f1": 0.9932043770233031
|
||||
},
|
||||
"ae_normalization": {
|
||||
"min": 1.480184057280627e-5,
|
||||
"max": 1.1978646574008398,
|
||||
"mean": 0.0023598657051511107,
|
||||
"std": 0.008826375460685048,
|
||||
"median": 0.00031163828850514576,
|
||||
"p90": 0.004909278753678756,
|
||||
"p95": 0.010077479060604477,
|
||||
"p99": 0.03354822433745485
|
||||
},
|
||||
"attack_labels": {
|
||||
"0": "Bot",
|
||||
"0": "Brute Force",
|
||||
"1": "DDoS",
|
||||
"2": "DoS GoldenEye",
|
||||
"3": "DoS Hulk",
|
||||
"4": "DoS Slowhttptest",
|
||||
"5": "DoS slowloris",
|
||||
"6": "FTP-Patator",
|
||||
"7": "PortScan",
|
||||
"8": "SSH-Patator",
|
||||
"9": "Web Attack"
|
||||
},
|
||||
"feature_order": [
|
||||
"Destination Port",
|
||||
"Flow Duration",
|
||||
"Total Fwd Packets",
|
||||
"Total Backward Packets",
|
||||
"Total Length of Fwd Packets",
|
||||
"Total Length of Bwd Packets",
|
||||
"Fwd Packet Length Max",
|
||||
"Fwd Packet Length Min",
|
||||
"Fwd Packet Length Mean",
|
||||
"Fwd Packet Length Std",
|
||||
"Bwd Packet Length Max",
|
||||
"Bwd Packet Length Min",
|
||||
"Bwd Packet Length Mean",
|
||||
"Bwd Packet Length Std",
|
||||
"Flow Bytes\/s",
|
||||
"Flow Packets\/s",
|
||||
"Flow IAT Mean",
|
||||
"Flow IAT Std",
|
||||
"Flow IAT Max",
|
||||
"Flow IAT Min",
|
||||
"Fwd IAT Total",
|
||||
"Fwd IAT Mean",
|
||||
"Fwd IAT Std",
|
||||
"Fwd IAT Max",
|
||||
"Fwd IAT Min",
|
||||
"Bwd IAT Total",
|
||||
"Bwd IAT Mean",
|
||||
"Bwd IAT Std",
|
||||
"Bwd IAT Max",
|
||||
"Bwd IAT Min",
|
||||
"Fwd PSH Flags",
|
||||
"Bwd PSH Flags",
|
||||
"Fwd URG Flags",
|
||||
"Bwd URG Flags",
|
||||
"Fwd Header Length",
|
||||
"Bwd Header Length",
|
||||
"Fwd Packets\/s",
|
||||
"Bwd Packets\/s",
|
||||
"Min Packet Length",
|
||||
"Max Packet Length",
|
||||
"Packet Length Mean",
|
||||
"Packet Length Std",
|
||||
"Packet Length Variance",
|
||||
"FIN Flag Count",
|
||||
"SYN Flag Count",
|
||||
"RST Flag Count",
|
||||
"PSH Flag Count",
|
||||
"ACK Flag Count",
|
||||
"URG Flag Count",
|
||||
"CWE Flag Count",
|
||||
"ECE Flag Count",
|
||||
"Down\/Up Ratio",
|
||||
"Average Packet Size",
|
||||
"Avg Fwd Segment Size",
|
||||
"Avg Bwd Segment Size",
|
||||
"Fwd Header Length.1",
|
||||
"Fwd Avg Bytes\/Bulk",
|
||||
"Fwd Avg Packets\/Bulk",
|
||||
"Fwd Avg Bulk Rate",
|
||||
"Bwd Avg Bytes\/Bulk",
|
||||
"Bwd Avg Packets\/Bulk",
|
||||
"Bwd Avg Bulk Rate",
|
||||
"Subflow Fwd Packets",
|
||||
"Subflow Fwd Bytes",
|
||||
"Subflow Bwd Packets",
|
||||
"Subflow Bwd Bytes",
|
||||
"Init_Win_bytes_forward",
|
||||
"Init_Win_bytes_backward",
|
||||
"act_data_pkt_fwd",
|
||||
"min_seg_size_forward",
|
||||
"Active Mean",
|
||||
"Active Std",
|
||||
"Active Max",
|
||||
"Active Min",
|
||||
"Idle Mean",
|
||||
"Idle Std",
|
||||
"Idle Max",
|
||||
"Idle Min"
|
||||
]
|
||||
"2": "DoS",
|
||||
"3": "Exploitation",
|
||||
"4": "Reconnaissance"
|
||||
}
|
||||
}
|
||||
@ -1,582 +1,239 @@
|
||||
{
|
||||
"threshold": 0.3603835832054485,
|
||||
"strategy_name": "Max",
|
||||
"clip_params": {
|
||||
"Destination Port": {
|
||||
"lower": 22.0,
|
||||
"upper": 63734.0
|
||||
},
|
||||
"Flow Duration": {
|
||||
"ae_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
"bwd_packets",
|
||||
"fwd_bytes",
|
||||
"bwd_bytes",
|
||||
"flow_bytes_per_sec",
|
||||
"flow_pkts_per_sec",
|
||||
"fwd_win_bytes",
|
||||
"bwd_win_bytes",
|
||||
"fwd_pkt_len_mean",
|
||||
"bwd_pkt_len_mean",
|
||||
"fwd_iat_mean",
|
||||
"bwd_iat_mean",
|
||||
"flow_iat_mean",
|
||||
"pkt_len_mean",
|
||||
"dst_port",
|
||||
"protocol",
|
||||
"psh_flag_cnt",
|
||||
"ack_flag_cnt",
|
||||
"syn_flag_cnt",
|
||||
"fin_flag_cnt",
|
||||
"rst_flag_cnt",
|
||||
"pkt_len_std",
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
],
|
||||
"ae_clip_params": {
|
||||
"flow_duration": {
|
||||
"lower": 1.0,
|
||||
"upper": 118756600.72000001
|
||||
"upper": 118485085.19000001
|
||||
},
|
||||
"Total Fwd Packets": {
|
||||
"lower": 1.0,
|
||||
"upper": 98.0
|
||||
},
|
||||
"Total Backward Packets": {
|
||||
"fwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 126.0
|
||||
"upper": 54.0
|
||||
},
|
||||
"Total Length of Fwd Packets": {
|
||||
"bwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 13119.520000000019
|
||||
"upper": 82.0
|
||||
},
|
||||
"Total Length of Bwd Packets": {
|
||||
"fwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 186394.64000000013
|
||||
"upper": 6960.0
|
||||
},
|
||||
"Fwd Packet Length Max": {
|
||||
"bwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 5840.0
|
||||
"upper": 89558.6950000003
|
||||
},
|
||||
"Fwd Packet Length Min": {
|
||||
"flow_bytes_per_sec": {
|
||||
"lower": 0.0,
|
||||
"upper": 98.0
|
||||
"upper": 12500000.0
|
||||
},
|
||||
"Fwd Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1932.5
|
||||
},
|
||||
"Fwd Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 2376.49858
|
||||
},
|
||||
"Bwd Packet Length Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 4380.0
|
||||
},
|
||||
"Bwd Packet Length Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 308.0
|
||||
},
|
||||
"Bwd Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1715.628244226656
|
||||
},
|
||||
"Bwd Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1167.9936145600002
|
||||
},
|
||||
"Flow Bytes\/s": {
|
||||
"lower": 0.0,
|
||||
"upper": 37000000.0
|
||||
},
|
||||
"Flow Packets\/s": {
|
||||
"lower": 0.041637660508,
|
||||
"flow_pkts_per_sec": {
|
||||
"lower": 0.0226829350855,
|
||||
"upper": 2000000.0
|
||||
},
|
||||
"Flow IAT Mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 30800000.0
|
||||
},
|
||||
"Flow IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 44253994.319940574
|
||||
},
|
||||
"Flow IAT Max": {
|
||||
"lower": 1.0,
|
||||
"upper": 96199268.00000003
|
||||
},
|
||||
"Flow IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 1999969.04
|
||||
},
|
||||
"Fwd IAT Total": {
|
||||
"lower": 0.0,
|
||||
"upper": 119000000.0
|
||||
},
|
||||
"Fwd IAT Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 74300000.0
|
||||
},
|
||||
"Fwd IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 35894248.669783354
|
||||
},
|
||||
"Fwd IAT Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 97800000.0
|
||||
},
|
||||
"Fwd IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 74300000.0
|
||||
},
|
||||
"Bwd IAT Total": {
|
||||
"lower": 0.0,
|
||||
"upper": 118000000.0
|
||||
},
|
||||
"Bwd IAT Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 73734410.76000024
|
||||
},
|
||||
"Bwd IAT Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 30100000.0
|
||||
},
|
||||
"Bwd IAT Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 93800000.0
|
||||
},
|
||||
"Bwd IAT Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 73734410.76000024
|
||||
},
|
||||
"Fwd PSH Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"Bwd PSH Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd URG Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd URG Flags": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Header Length": {
|
||||
"lower": 20.0,
|
||||
"upper": 2376.0
|
||||
},
|
||||
"Bwd Header Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 3312.0
|
||||
},
|
||||
"Fwd Packets\/s": {
|
||||
"lower": 0.02417299188,
|
||||
"upper": 2000000.0
|
||||
},
|
||||
"Bwd Packets\/s": {
|
||||
"lower": 0.0,
|
||||
"upper": 142857.1429
|
||||
},
|
||||
"Min Packet Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 89.0
|
||||
},
|
||||
"Max Packet Length": {
|
||||
"lower": 0.0,
|
||||
"upper": 5840.0
|
||||
},
|
||||
"Packet Length Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1153.4362532
|
||||
},
|
||||
"Packet Length Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1845.154148000012
|
||||
},
|
||||
"Packet Length Variance": {
|
||||
"lower": 0.0,
|
||||
"upper": 3404593.931120044
|
||||
},
|
||||
"FIN Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"SYN Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"RST Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"PSH Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"ACK Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"URG Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"CWE Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"ECE Flag Count": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Down\/Up Ratio": {
|
||||
"lower": 0.0,
|
||||
"upper": 5.0
|
||||
},
|
||||
"Average Packet Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1182.362785582229
|
||||
},
|
||||
"Avg Fwd Segment Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1932.5
|
||||
},
|
||||
"Avg Bwd Segment Size": {
|
||||
"lower": 0.0,
|
||||
"upper": 1715.628244226656
|
||||
},
|
||||
"Fwd Header Length.1": {
|
||||
"lower": 20.0,
|
||||
"upper": 2376.0
|
||||
},
|
||||
"Fwd Avg Bytes\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Avg Packets\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Fwd Avg Bulk Rate": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Bytes\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Packets\/Bulk": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Bwd Avg Bulk Rate": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
},
|
||||
"Subflow Fwd Packets": {
|
||||
"lower": 1.0,
|
||||
"upper": 98.0
|
||||
},
|
||||
"Subflow Fwd Bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 13119.520000000019
|
||||
},
|
||||
"Subflow Bwd Packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 126.0
|
||||
},
|
||||
"Subflow Bwd Bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 186394.64000000013
|
||||
},
|
||||
"Init_Win_bytes_forward": {
|
||||
"fwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
},
|
||||
"Init_Win_bytes_backward": {
|
||||
"bwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
"upper": 64000.0
|
||||
},
|
||||
"act_data_pkt_fwd": {
|
||||
"fwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 49.0
|
||||
"upper": 744.7272727272729
|
||||
},
|
||||
"min_seg_size_forward": {
|
||||
"lower": 20.0,
|
||||
"bwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1393.9601153594992
|
||||
},
|
||||
"fwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 86366722.035
|
||||
},
|
||||
"bwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 21700000.0
|
||||
},
|
||||
"flow_iat_mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 86353400.64500001
|
||||
},
|
||||
"pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 945.0294113974604
|
||||
},
|
||||
"dst_port": {
|
||||
"lower": 0.0,
|
||||
"upper": 64499.0
|
||||
},
|
||||
"protocol": {
|
||||
"lower": 0.0,
|
||||
"upper": 17.0
|
||||
},
|
||||
"psh_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 240.0
|
||||
},
|
||||
"ack_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 655.0
|
||||
},
|
||||
"syn_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 4.0
|
||||
},
|
||||
"fin_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 2.0
|
||||
},
|
||||
"rst_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 943.9221842600019
|
||||
},
|
||||
"fwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 721.4199648708689
|
||||
},
|
||||
"bwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 911.0272448420104
|
||||
},
|
||||
"fwd_seg_size_min": {
|
||||
"lower": 0.0,
|
||||
"upper": 40.0
|
||||
},
|
||||
"Active Mean": {
|
||||
"fwd_act_data_pkts": {
|
||||
"lower": 0.0,
|
||||
"upper": 3410152.040000004
|
||||
},
|
||||
"Active Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 1944723.8431600018
|
||||
},
|
||||
"Active Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 5717825.840000004
|
||||
},
|
||||
"Active Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 2918887.7600000002
|
||||
},
|
||||
"Idle Mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 94100000.0
|
||||
},
|
||||
"Idle Std": {
|
||||
"lower": 0.0,
|
||||
"upper": 20712312.015200634
|
||||
},
|
||||
"Idle Max": {
|
||||
"lower": 0.0,
|
||||
"upper": 94800000.0
|
||||
},
|
||||
"Idle Min": {
|
||||
"lower": 0.0,
|
||||
"upper": 94100000.0
|
||||
"upper": 58.0
|
||||
}
|
||||
},
|
||||
"scaler_mean": [
|
||||
9415.502043247605,
|
||||
11217541.457604988,
|
||||
5.41509711200182,
|
||||
5.010050165039152,
|
||||
508.74335117243135,
|
||||
3302.166099044608,
|
||||
217.24886707430434,
|
||||
20.105687086824716,
|
||||
65.40745127720037,
|
||||
68.76070937185386,
|
||||
394.97690859650953,
|
||||
49.88046220640826,
|
||||
159.85952962743096,
|
||||
121.94653847258593,
|
||||
872411.6556228747,
|
||||
64701.11201063558,
|
||||
840255.3525863544,
|
||||
1516373.8724808302,
|
||||
4422301.697900706,
|
||||
24666.656195771673,
|
||||
10897580.686685171,
|
||||
1785372.7881935516,
|
||||
1206278.3042178946,
|
||||
4276119.216091527,
|
||||
1025069.5899462275,
|
||||
10158901.07420493,
|
||||
1666490.9735317046,
|
||||
939816.6614413982,
|
||||
3689910.1546691586,
|
||||
1003727.4123986625,
|
||||
0.054886351088404936,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
140.54050707030981,
|
||||
133.63606216540694,
|
||||
58358.168874140014,
|
||||
4983.791329731815,
|
||||
19.812641079549177,
|
||||
480.4855221752525,
|
||||
108.29833052637488,
|
||||
144.4437888623996,
|
||||
99406.03379366906,
|
||||
0.01811977227544623,
|
||||
0.054886351088404936,
|
||||
0.0,
|
||||
0.25800834720207716,
|
||||
0.28694244020382764,
|
||||
0.11596161536441252,
|
||||
0.0,
|
||||
0.0,
|
||||
0.6969992921551522,
|
||||
122.46836156918589,
|
||||
65.40745127720037,
|
||||
159.85952962743843,
|
||||
140.54050707030981,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
5.41509711200182,
|
||||
508.74335117243135,
|
||||
5.010050165039152,
|
||||
3302.166099044608,
|
||||
6891.551141020379,
|
||||
2404.6474695096604,
|
||||
2.6520584031389776,
|
||||
25.79473555242033,
|
||||
61328.50855670946,
|
||||
36311.697575423306,
|
||||
137445.81057140988,
|
||||
37549.65545208145,
|
||||
3689065.3106244486,
|
||||
167385.3556173429,
|
||||
3883594.4613142335,
|
||||
3458258.8992590285
|
||||
"ae_scaler_mean": [
|
||||
9783470.902880548,
|
||||
3.6532605393470496,
|
||||
3.2219949045990472,
|
||||
311.35281195367173,
|
||||
1407.508354811673,
|
||||
298997.28705051675,
|
||||
32378.36735569276,
|
||||
6158.3256759444475,
|
||||
6125.023325143747,
|
||||
58.1683929498305,
|
||||
149.72168204849987,
|
||||
2991407.86700246,
|
||||
543337.1970583026,
|
||||
2727511.340181723,
|
||||
105.03829359866097,
|
||||
13463.473246959382,
|
||||
8.920585011866685,
|
||||
5.457814320208867,
|
||||
12.214631683524651,
|
||||
0.4885976379312313,
|
||||
0.246598391240437,
|
||||
0.08478201266637703,
|
||||
151.71809799252043,
|
||||
76.89649795414182,
|
||||
143.21014925828936,
|
||||
21.447338538177014,
|
||||
3.2607463735155715
|
||||
],
|
||||
"scaler_std": [
|
||||
19743.81786383566,
|
||||
30111390.85802201,
|
||||
10.612016675700064,
|
||||
12.872110268702288,
|
||||
1653.7982707262067,
|
||||
17387.56157755931,
|
||||
645.437387791972,
|
||||
22.356844266679282,
|
||||
193.61621129020307,
|
||||
244.137250250046,
|
||||
801.628533600607,
|
||||
65.65797274491328,
|
||||
277.7559653593753,
|
||||
269.57514761088515,
|
||||
3622907.235127058,
|
||||
237431.11489053545,
|
||||
3293477.440659972,
|
||||
5309087.889319858,
|
||||
14143198.815741453,
|
||||
160458.44923675407,
|
||||
29985211.254190512,
|
||||
8130184.699376457,
|
||||
4247601.6170286415,
|
||||
14271787.10745024,
|
||||
7769581.505106356,
|
||||
29154202.839787327,
|
||||
8031691.1024753135,
|
||||
3622760.030182759,
|
||||
13368925.151392205,
|
||||
7684129.1959599955,
|
||||
0.22775829195136954,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
271.11142624961946,
|
||||
342.17520432711024,
|
||||
231133.09437362824,
|
||||
14026.707464402314,
|
||||
22.005832334437958,
|
||||
970.4322795025132,
|
||||
179.9690556845557,
|
||||
280.2535015764328,
|
||||
343919.1355100174,
|
||||
0.13338457979891152,
|
||||
0.22775829195136954,
|
||||
1.0,
|
||||
0.4375386154114052,
|
||||
0.4523344738284939,
|
||||
0.32017888613474904,
|
||||
1.0,
|
||||
1.0,
|
||||
0.6456077980707177,
|
||||
185.26118186235436,
|
||||
193.61621129020307,
|
||||
277.7559653594075,
|
||||
271.11142624961946,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
10.612016675700064,
|
||||
1653.7982707262067,
|
||||
12.872110268702288,
|
||||
17387.56157755931,
|
||||
14922.88749872485,
|
||||
9322.158869764009,
|
||||
5.784504103070997,
|
||||
6.252894021045679,
|
||||
322010.01591284445,
|
||||
201975.98444339563,
|
||||
641116.7578408231,
|
||||
245957.32066446543,
|
||||
13068297.83388166,
|
||||
1617503.1921646637,
|
||||
13724469.425904194,
|
||||
12765671.772576509
|
||||
"ae_scaler_std": [
|
||||
27895851.239715174,
|
||||
6.4263677035863696,
|
||||
8.326275195918974,
|
||||
739.2548064174216,
|
||||
7898.711708881367,
|
||||
1187804.3239195002,
|
||||
190280.1548511611,
|
||||
15556.664474188405,
|
||||
17012.235322987635,
|
||||
98.89458775902833,
|
||||
242.79522892109904,
|
||||
13146157.964503227,
|
||||
2242915.166631539,
|
||||
12878527.145353919,
|
||||
151.95316919115754,
|
||||
21264.915159327567,
|
||||
4.943209145195101,
|
||||
23.02750953458606,
|
||||
57.0633472601567,
|
||||
1.2662749497077417,
|
||||
0.4553262784517569,
|
||||
0.278557037237646,
|
||||
211.1373807979287,
|
||||
143.47790203327,
|
||||
228.24531573376805,
|
||||
9.323458649060111,
|
||||
7.228913366949205
|
||||
],
|
||||
"ae_post_clip_min": -5.0,
|
||||
"ae_post_clip_max": 5.0,
|
||||
"ae_threshold": 0.17166012525558472,
|
||||
"classifier_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
"bwd_packets",
|
||||
"fwd_bytes",
|
||||
"bwd_bytes",
|
||||
"flow_bytes_per_sec",
|
||||
"flow_pkts_per_sec",
|
||||
"fwd_win_bytes",
|
||||
"bwd_win_bytes",
|
||||
"fwd_pkt_len_mean",
|
||||
"bwd_pkt_len_mean",
|
||||
"fwd_iat_mean",
|
||||
"bwd_iat_mean",
|
||||
"flow_iat_mean",
|
||||
"pkt_len_mean",
|
||||
"dst_port",
|
||||
"protocol",
|
||||
"psh_flag_cnt",
|
||||
"ack_flag_cnt",
|
||||
"syn_flag_cnt",
|
||||
"fin_flag_cnt",
|
||||
"rst_flag_cnt",
|
||||
"pkt_len_std",
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts",
|
||||
"ae_anomaly_score"
|
||||
],
|
||||
"post_clip_min": -5.0,
|
||||
"post_clip_max": 5.0,
|
||||
"ae_normalization": {
|
||||
"min": 1.480184057280627e-5,
|
||||
"max": 1.1978646574008398,
|
||||
"mean": 0.0023598657051511107,
|
||||
"std": 0.008826375460685048,
|
||||
"median": 0.00031163828850514576,
|
||||
"p90": 0.004909278753678756,
|
||||
"p95": 0.010077479060604477,
|
||||
"p99": 0.03354822433745485
|
||||
},
|
||||
"attack_labels": {
|
||||
"0": "Bot",
|
||||
"0": "Brute Force",
|
||||
"1": "DDoS",
|
||||
"2": "DoS GoldenEye",
|
||||
"3": "DoS Hulk",
|
||||
"4": "DoS Slowhttptest",
|
||||
"5": "DoS slowloris",
|
||||
"6": "FTP-Patator",
|
||||
"7": "PortScan",
|
||||
"8": "SSH-Patator",
|
||||
"9": "Web Attack"
|
||||
},
|
||||
"feature_names": [
|
||||
"Destination Port",
|
||||
"Flow Duration",
|
||||
"Total Fwd Packets",
|
||||
"Total Backward Packets",
|
||||
"Total Length of Fwd Packets",
|
||||
"Total Length of Bwd Packets",
|
||||
"Fwd Packet Length Max",
|
||||
"Fwd Packet Length Min",
|
||||
"Fwd Packet Length Mean",
|
||||
"Fwd Packet Length Std",
|
||||
"Bwd Packet Length Max",
|
||||
"Bwd Packet Length Min",
|
||||
"Bwd Packet Length Mean",
|
||||
"Bwd Packet Length Std",
|
||||
"Flow Bytes\/s",
|
||||
"Flow Packets\/s",
|
||||
"Flow IAT Mean",
|
||||
"Flow IAT Std",
|
||||
"Flow IAT Max",
|
||||
"Flow IAT Min",
|
||||
"Fwd IAT Total",
|
||||
"Fwd IAT Mean",
|
||||
"Fwd IAT Std",
|
||||
"Fwd IAT Max",
|
||||
"Fwd IAT Min",
|
||||
"Bwd IAT Total",
|
||||
"Bwd IAT Mean",
|
||||
"Bwd IAT Std",
|
||||
"Bwd IAT Max",
|
||||
"Bwd IAT Min",
|
||||
"Fwd PSH Flags",
|
||||
"Bwd PSH Flags",
|
||||
"Fwd URG Flags",
|
||||
"Bwd URG Flags",
|
||||
"Fwd Header Length",
|
||||
"Bwd Header Length",
|
||||
"Fwd Packets\/s",
|
||||
"Bwd Packets\/s",
|
||||
"Min Packet Length",
|
||||
"Max Packet Length",
|
||||
"Packet Length Mean",
|
||||
"Packet Length Std",
|
||||
"Packet Length Variance",
|
||||
"FIN Flag Count",
|
||||
"SYN Flag Count",
|
||||
"RST Flag Count",
|
||||
"PSH Flag Count",
|
||||
"ACK Flag Count",
|
||||
"URG Flag Count",
|
||||
"CWE Flag Count",
|
||||
"ECE Flag Count",
|
||||
"Down\/Up Ratio",
|
||||
"Average Packet Size",
|
||||
"Avg Fwd Segment Size",
|
||||
"Avg Bwd Segment Size",
|
||||
"Fwd Header Length.1",
|
||||
"Fwd Avg Bytes\/Bulk",
|
||||
"Fwd Avg Packets\/Bulk",
|
||||
"Fwd Avg Bulk Rate",
|
||||
"Bwd Avg Bytes\/Bulk",
|
||||
"Bwd Avg Packets\/Bulk",
|
||||
"Bwd Avg Bulk Rate",
|
||||
"Subflow Fwd Packets",
|
||||
"Subflow Fwd Bytes",
|
||||
"Subflow Bwd Packets",
|
||||
"Subflow Bwd Bytes",
|
||||
"Init_Win_bytes_forward",
|
||||
"Init_Win_bytes_backward",
|
||||
"act_data_pkt_fwd",
|
||||
"min_seg_size_forward",
|
||||
"Active Mean",
|
||||
"Active Std",
|
||||
"Active Max",
|
||||
"Active Min",
|
||||
"Idle Mean",
|
||||
"Idle Std",
|
||||
"Idle Max",
|
||||
"Idle Min"
|
||||
]
|
||||
"2": "DoS",
|
||||
"3": "Exploitation",
|
||||
"4": "Reconnaissance"
|
||||
}
|
||||
}
|
||||
BIN
models/mlp.onnx
BIN
models/mlp.onnx
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit bf27f315f2536025fa9068b9027a151a160bb70d
|
||||
Subproject commit 55da05e710cb00bbc9bef85804667624b0ddf377
|
||||
@ -10,7 +10,7 @@ macros = { path = "../macros" }
|
||||
actix = "0.13.5"
|
||||
actix-cors = "0.7.1"
|
||||
actix-web = "4.11.0"
|
||||
actix-ws = "0.3.0"
|
||||
actix-ws = "0.4.0"
|
||||
aya = { workspace = true }
|
||||
aya-log = { workspace = true }
|
||||
network-types = { workspace = true }
|
||||
@ -22,11 +22,11 @@ parking_lot = "0.12.5"
|
||||
rust-embed = "8.7.2"
|
||||
serde = { workspace = true }
|
||||
serde_json = "1.0.143"
|
||||
sysinfo = "0.37.0"
|
||||
sysinfo = "0.38.2"
|
||||
thiserror = "2.0.3"
|
||||
tokio = { version = "1.40.0", features = ["full", "macros"] }
|
||||
tokio-tungstenite = "0.28.0"
|
||||
toml = "0.9.5"
|
||||
toml = "1.0.3"
|
||||
tracing = "0.1.41"
|
||||
tracing-appender = "0.2.3"
|
||||
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
|
||||
@ -36,6 +36,8 @@ maxminddb = "0.27.1"
|
||||
lru = "0.16.2"
|
||||
futures = "0.3.31"
|
||||
tract-onnx = "0.22.0"
|
||||
#csv = "1.4.0"
|
||||
#anyhow = "1.0.100"
|
||||
|
||||
[build-dependencies]
|
||||
cargo_metadata = { workspace = true }
|
||||
|
||||
@ -255,13 +255,18 @@ fn build_egress_ebpf() {
|
||||
fn build_frontend() {
|
||||
let _ = dotenvy::dotenv();
|
||||
|
||||
let Some(frontend_dir) = env::var_os("FRONTEND_DIR") else {
|
||||
panic!("FRONTEND_DIR environment variable is required but not set");
|
||||
};
|
||||
// let Some(frontend_dir) = env::var_os("FRONTEND_DIR") else {
|
||||
// panic!("FRONTEND_DIR environment variable is required but not set");
|
||||
// };
|
||||
|
||||
let project_root = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let static_dir = PathBuf::from(project_root).join("static").join("web");
|
||||
let frontend_dir = PathBuf::from(frontend_dir);
|
||||
let project_root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let static_dir = project_root.join("static").join("web");
|
||||
|
||||
let project_name = project_root.file_name().unwrap().to_string_lossy();
|
||||
let frontend_dir = project_root
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(format!("{}-frontend", project_name));
|
||||
|
||||
if !frontend_dir.exists() {
|
||||
panic!("Frontend directory {:?} does not exist", frontend_dir);
|
||||
|
||||
@ -4,7 +4,6 @@ pub mod statistics;
|
||||
pub mod xsk_manager;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use aya::Ebpf;
|
||||
use crossbeam::queue::SegQueue;
|
||||
@ -15,7 +14,6 @@ use crate::core::ebpf::access_control::AccessControl;
|
||||
use crate::core::ebpf::service::Service;
|
||||
use crate::core::ebpf::statistics::Statistics;
|
||||
use crate::core::ebpf::xsk_manager::XskManager;
|
||||
use crate::core::infrastructure::health::SystemHealth;
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::model::error::system::SystemError;
|
||||
use crate::model::error::Error;
|
||||
@ -26,8 +24,7 @@ pub struct EbpfServices {
|
||||
pub access_control: Arc<AccessControl>,
|
||||
pub service: Arc<Service>,
|
||||
pub statistics: Arc<Statistics>,
|
||||
pub health: Arc<SystemHealth>,
|
||||
shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||
pub shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
impl EbpfServices {
|
||||
@ -36,9 +33,8 @@ impl EbpfServices {
|
||||
ingress_ebpf: &mut Ebpf,
|
||||
egress_ebpf: &mut Ebpf,
|
||||
) -> Result<Self, Error> {
|
||||
let xsk_manager = XskManager::new(app_config.clone(), ingress_ebpf)?;
|
||||
let xsk_manager = XskManager::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||
let access_control = AccessControl::new(ingress_ebpf)?;
|
||||
let health = SystemHealth::new(app_config.clone())?;
|
||||
let service = Service::new(ingress_ebpf)?;
|
||||
let statistics = Statistics::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||
let ebpf_services = Self {
|
||||
@ -46,7 +42,6 @@ impl EbpfServices {
|
||||
access_control: Arc::new(access_control),
|
||||
service: Arc::new(service),
|
||||
statistics: Arc::new(statistics),
|
||||
health: Arc::new(health),
|
||||
shutdowns: SegQueue::new(),
|
||||
};
|
||||
Ok(ebpf_services)
|
||||
@ -55,20 +50,16 @@ impl EbpfServices {
|
||||
pub async fn run(self: Arc<Self>, ml_engine: Arc<Engine>) -> Result<(), Error> {
|
||||
let xsk_manager = self.xsk_manager.clone();
|
||||
let statistics = self.statistics.clone();
|
||||
let health = self.health.clone();
|
||||
|
||||
xsk_manager.run(Some(ml_engine))?;
|
||||
xsk_manager.run(Some(ml_engine), &self.shutdowns)?;
|
||||
|
||||
let statistics_shutdown = statistics.run().await;
|
||||
let health_shutdown = health.run(Duration::from_secs(3)).await;
|
||||
|
||||
self.shutdowns.push(statistics_shutdown);
|
||||
self.shutdowns.push(health_shutdown);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn terminate(self: Arc<Self>) {
|
||||
self.xsk_manager.shutdown();
|
||||
while let Some(shutdown) = self.shutdowns.pop() {
|
||||
if shutdown.send(()).is_err() {
|
||||
log!(SystemError::ShutdownSignalFailed);
|
||||
|
||||
@ -6,9 +6,9 @@ use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use aya::Ebpf;
|
||||
use aya::maps::{MapData, XskMap};
|
||||
use crossbeam::channel::{Receiver, Sender, bounded};
|
||||
use aya::Ebpf;
|
||||
use crossbeam::channel::{bounded, Receiver, Sender};
|
||||
use crossbeam::queue::SegQueue;
|
||||
use macros::log;
|
||||
use parking_lot::Mutex;
|
||||
@ -17,36 +17,38 @@ use xsk_rs::config::{BindFlags, FrameSize, Interface, LibxdpFlags, QueueSize, So
|
||||
use xsk_rs::{CompQueue, FillQueue, FrameDesc, RxQueue, Socket, TxQueue, Umem};
|
||||
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::model::direction::Direction;
|
||||
use crate::ml::engine::{Engine, PacketProcessor};
|
||||
use crate::model::config::Config;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::error::system::SystemError;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::log::ebpf::EbpfLog;
|
||||
use crate::ml::engine::{Engine, PacketProcessor};
|
||||
|
||||
pub struct XskManager {
|
||||
app_config: Arc<AppConfig>,
|
||||
xsk_map: Mutex<XskMap<MapData>>,
|
||||
shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||
egress_xsk_map: Mutex<XskMap<MapData>>,
|
||||
}
|
||||
|
||||
impl XskManager {
|
||||
pub fn new(
|
||||
app_config: Arc<AppConfig>,
|
||||
ebpf: &mut Ebpf,
|
||||
) -> Result<Self, Error> {
|
||||
let map = ebpf.take_map("XSKS_MAP").ok_or(EbpfError::MapNotFound)?;
|
||||
pub fn new(app_config: Arc<AppConfig>, ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||
let map = ingress_ebpf
|
||||
.take_map("INGRESS_XSKS_MAP")
|
||||
.ok_or(EbpfError::MapNotFound)?;
|
||||
let xsk_map = XskMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
||||
|
||||
let egress_map = egress_ebpf.take_map("EGRESS_XSKS_MAP").ok_or(EbpfError::MapNotFound)?;
|
||||
let egress_xsk_map = XskMap::try_from(egress_map).map_err(EbpfError::MapOperationError)?;
|
||||
|
||||
Ok(Self {
|
||||
app_config,
|
||||
xsk_map: Mutex::new(xsk_map),
|
||||
shutdowns: SegQueue::new(),
|
||||
egress_xsk_map: Mutex::new(egress_xsk_map),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(&self, ml_engine: Option<Arc<Engine>>) -> Result<(), Error> {
|
||||
pub fn run(&self, ml_engine: Option<Arc<Engine>>, shutdowns: &SegQueue<oneshot::Sender<()>>) -> Result<(), Error> {
|
||||
let config = self.app_config.config.clone();
|
||||
let combined_queue_count = config.combined_queue_count;
|
||||
|
||||
@ -71,7 +73,7 @@ impl XskManager {
|
||||
&config.egress_ifname,
|
||||
&config.ingress_ifname,
|
||||
Direction::Egress,
|
||||
None,
|
||||
packet_processor.clone(),
|
||||
)?;
|
||||
|
||||
let mut xsk_map = self.xsk_map.lock();
|
||||
@ -81,25 +83,24 @@ impl XskManager {
|
||||
.map_err(EbpfError::AfXdpSetFailed)?;
|
||||
drop(xsk_map);
|
||||
|
||||
let mut egress_xsk_map = self.egress_xsk_map.lock();
|
||||
let egress_fd = egress_xsk.rx.fd().as_raw_fd();
|
||||
egress_xsk_map
|
||||
.set(queue_id, egress_fd, 0)
|
||||
.map_err(EbpfError::AfXdpSetFailed)?;
|
||||
drop(egress_xsk_map);
|
||||
|
||||
let ingress_shutdown = ingress_xsk.run(ingress_to_egress_tx, egress_to_ingress_rx)?;
|
||||
self.shutdowns.push(ingress_shutdown);
|
||||
shutdowns.push(ingress_shutdown);
|
||||
|
||||
let egress_shutdown = egress_xsk.run(egress_to_ingress_tx, ingress_to_egress_rx)?;
|
||||
self.shutdowns.push(egress_shutdown);
|
||||
shutdowns.push(egress_shutdown);
|
||||
|
||||
log!(EbpfLog::QueuePairStarted(queue_id));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) {
|
||||
while let Some(sender) = self.shutdowns.pop() {
|
||||
if sender.send(()).is_err() {
|
||||
log!(SystemError::ShutdownSignalFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XskPair {
|
||||
@ -109,7 +110,7 @@ pub struct XskPair {
|
||||
comp_queue: CompQueue,
|
||||
tx: TxQueue,
|
||||
rx: RxQueue,
|
||||
frame_pool: Arc<Mutex<Vec<FrameDesc>>>,
|
||||
frame_pool: Arc<Mutex<Vec<FrameDesc>>>, // SegQueue
|
||||
packet_processor: Option<Arc<PacketProcessor>>,
|
||||
}
|
||||
|
||||
@ -150,10 +151,8 @@ impl XskPair {
|
||||
|
||||
let interface = Interface::new(rx_ifname_c);
|
||||
|
||||
let (tx, rx, queue) = unsafe {
|
||||
Socket::new(socket_config, &umem, &interface, queue_id)
|
||||
.map_err(EbpfError::SocketSetFailed)?
|
||||
};
|
||||
let (tx, rx, queue) =
|
||||
unsafe { Socket::new(socket_config, &umem, &interface, queue_id).map_err(EbpfError::SocketSetFailed)? };
|
||||
|
||||
let (mut fill_queue, comp_queue) = queue.ok_or(EbpfError::UnknownError)?;
|
||||
|
||||
@ -278,7 +277,7 @@ impl XskPair {
|
||||
let packet_data = data.contents()[..packet_len].to_vec();
|
||||
|
||||
if let Some(ref processor) = self.packet_processor {
|
||||
processor.process(&packet_data);
|
||||
processor.process(&packet_data, self.direction == Direction::Ingress);
|
||||
}
|
||||
|
||||
if let Err(e) = forward_tx.try_send(packet_data) {
|
||||
@ -366,4 +365,4 @@ impl XskPair {
|
||||
|
||||
Ok(nb_submitted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,9 @@ use tokio::sync::RwLock;
|
||||
use lru::LruCache;
|
||||
use std::num::NonZeroUsize;
|
||||
use tokio::task;
|
||||
use crate::model::geo_stats::GeoLocation;
|
||||
|
||||
use crate::model::geo_stats::GeoLocation;
|
||||
use crate::utils::ip_address;
|
||||
|
||||
pub struct GeoIpService {
|
||||
reader: Arc<Reader<Vec<u8>>>,
|
||||
@ -36,6 +37,17 @@ impl GeoIpService {
|
||||
}
|
||||
|
||||
pub async fn lookup(&self, ip: IpAddr) -> Result<Option<GeoLocation>, MaxMindDbError> {
|
||||
if ip_address::is_private_ip(&ip) {
|
||||
return Ok(Some(GeoLocation {
|
||||
country: Some("Local IP".into()),
|
||||
country_code: Some("Local".into()),
|
||||
city: None,
|
||||
latitude: None,
|
||||
longitude: None,
|
||||
timezone: None,
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
let cache = self.cache.read().await;
|
||||
if let Some(cached) = cache.peek(&ip) {
|
||||
|
||||
@ -2,14 +2,25 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::Serialize;
|
||||
use sysinfo::{Components, Networks, System};
|
||||
use tokio::sync::{broadcast, oneshot, RwLock};
|
||||
use tokio::time::interval;
|
||||
use tracing::{info, error, warn};
|
||||
use macros::log;
|
||||
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::model::log::health::Health;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::health::{
|
||||
ConfiguredNetworkStats,
|
||||
CpuCoreInfo,
|
||||
CpuDetails,
|
||||
LoadAverage,
|
||||
MemoryUsage,
|
||||
NetworkStats,
|
||||
SystemHealthMetrics,
|
||||
SystemHealthStatus,
|
||||
SystemInfo
|
||||
};
|
||||
|
||||
pub struct SystemHealth {
|
||||
system: RwLock<System>,
|
||||
@ -21,85 +32,6 @@ pub struct SystemHealth {
|
||||
// management_interface: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemHealthMetrics {
|
||||
pub timestamp: u64,
|
||||
pub boot_time: u64,
|
||||
pub uptime_seconds: u64,
|
||||
pub system_info: SystemInfo,
|
||||
pub cpu_details: CpuDetails,
|
||||
pub memory_usage: MemoryUsage,
|
||||
pub network_stats: ConfiguredNetworkStats,
|
||||
pub load_average: Option<LoadAverage>,
|
||||
pub temperature: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemInfo {
|
||||
pub kernel_version: Option<String>,
|
||||
pub os_name: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub architecture: String,
|
||||
pub total_processes: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CpuDetails {
|
||||
pub cpu_brand: String,
|
||||
pub core_count: usize,
|
||||
pub cpu_usage: f32,
|
||||
pub cpu_frequency: u64,
|
||||
pub cores: Vec<CpuCoreInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CpuCoreInfo {
|
||||
pub core_id: usize,
|
||||
pub usage_percent: f32,
|
||||
pub frequency: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MemoryUsage {
|
||||
pub total: u64,
|
||||
pub used: u64,
|
||||
pub available: u64,
|
||||
pub usage_percent: f32,
|
||||
pub swap_total: u64,
|
||||
pub swap_used: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ConfiguredNetworkStats {
|
||||
pub ingress: Option<NetworkStats>,
|
||||
pub egress: Option<NetworkStats>,
|
||||
// pub management: Option<NetworkStats>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct NetworkStats {
|
||||
pub interface: String,
|
||||
pub bytes_received: u64,
|
||||
pub bytes_transmitted: u64,
|
||||
pub packets_received: u64,
|
||||
pub packets_transmitted: u64,
|
||||
pub errors_received: u64,
|
||||
pub errors_transmitted: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LoadAverage {
|
||||
pub one_minute: f64,
|
||||
pub five_minute: f64,
|
||||
pub fifteen_minute: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemHealthStatus {
|
||||
pub overall_healthy: bool,
|
||||
pub issues: Vec<String>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
impl SystemHealth {
|
||||
pub fn new(config: Arc<AppConfig>) -> Result<Self, Error> {
|
||||
@ -115,11 +47,6 @@ impl SystemHealth {
|
||||
// management_interface: config.management_ifindex.clone(),
|
||||
};
|
||||
|
||||
info!(
|
||||
"System health monitoring initialized with interfaces: ingress={}, egress={}",
|
||||
health.ingress_interface, health.egress_interface
|
||||
);
|
||||
|
||||
Ok(health)
|
||||
}
|
||||
|
||||
@ -147,12 +74,10 @@ impl SystemHealth {
|
||||
}
|
||||
|
||||
async fn refresh_and_broadcast(&self) {
|
||||
// Refresh all system information
|
||||
self.system.write().await.refresh_all();
|
||||
self.networks.write().await.refresh(true);
|
||||
self.components.write().await.refresh(true);
|
||||
|
||||
// Collect metrics
|
||||
let system = self.system.read().await;
|
||||
let networks = self.networks.read().await;
|
||||
let components = self.components.read().await;
|
||||
@ -170,10 +95,9 @@ impl SystemHealth {
|
||||
drop(networks);
|
||||
drop(components);
|
||||
|
||||
// Broadcast metrics if there are subscribers
|
||||
if self.broadcast_tx.receiver_count() > 0 {
|
||||
if let Err(e) = self.broadcast_tx.send(metrics) {
|
||||
error!("Failed to broadcast system health metrics: {}", e);
|
||||
log!(Health::BroadcastFailed(e.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,10 +237,10 @@ impl SystemHealth {
|
||||
// let management = create_network_stats(management_interface);
|
||||
|
||||
if ingress.is_none() {
|
||||
warn!("Ingress interface '{}' not found", ingress_interface);
|
||||
log!(Health::InterfaceNotFound("Ingress".to_string(), ingress_interface.to_string()));
|
||||
}
|
||||
if egress.is_none() {
|
||||
warn!("Egress interface '{}' not found", egress_interface);
|
||||
log!(Health::InterfaceNotFound("Egress".to_string(), egress_interface.to_string()));
|
||||
}
|
||||
// if management.is_none() {
|
||||
// warn!("Management interface '{}' not found", management_interface);
|
||||
|
||||
80
net-guardia/src/core/infrastructure/ml_alert.rs
Normal file
80
net-guardia/src/core/infrastructure/ml_alert.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use serde::Serialize;
|
||||
use tokio::sync::broadcast;
|
||||
use tracing::error;
|
||||
|
||||
use crate::model::ml_detection::DetectionResult;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct AlertMessage {
|
||||
pub timestamp: u64,
|
||||
pub flow_key: String,
|
||||
pub src_ip: String,
|
||||
pub dst_ip: String,
|
||||
pub src_port: u16,
|
||||
pub dst_port: u16,
|
||||
pub protocol: u8,
|
||||
pub is_attack: bool,
|
||||
pub attack_type: Option<String>,
|
||||
pub confidence: f32,
|
||||
pub ae_score: f32,
|
||||
}
|
||||
|
||||
impl AlertMessage {
|
||||
pub fn from_detection_result(result: &DetectionResult) -> Self {
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
Self {
|
||||
timestamp,
|
||||
flow_key: result.flow_key.clone(),
|
||||
src_ip: result.flow_key_raw.src_ip.clone(),
|
||||
dst_ip: result.flow_key_raw.dst_ip.clone(),
|
||||
src_port: result.flow_key_raw.src_port,
|
||||
dst_port: result.flow_key_raw.dst_port,
|
||||
protocol: result.flow_key_raw.protocol,
|
||||
is_attack: result.is_attack,
|
||||
attack_type: result.attack_type.clone(),
|
||||
confidence: result.confidence,
|
||||
ae_score: result.ae_score,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MLAlert {
|
||||
broadcast_tx: broadcast::Sender<AlertMessage>,
|
||||
}
|
||||
|
||||
impl MLAlert {
|
||||
pub fn new() -> Self {
|
||||
let (broadcast_tx, _) = broadcast::channel(100);
|
||||
|
||||
MLAlert {
|
||||
broadcast_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe_to_alerts(&self) -> broadcast::Receiver<AlertMessage> {
|
||||
self.broadcast_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn broadcast_alert(&self, result: &DetectionResult) {
|
||||
if self.broadcast_tx.receiver_count() > 0 {
|
||||
let alert = AlertMessage::from_detection_result(result);
|
||||
if let Err(e) = self.broadcast_tx.send(alert) {
|
||||
error!("Failed to broadcast ML alert: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_subscribers(&self) -> bool {
|
||||
self.broadcast_tx.receiver_count() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MLAlert {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,95 @@
|
||||
pub mod app_config;
|
||||
pub mod health;
|
||||
pub mod geoip;
|
||||
pub mod ml_alert;
|
||||
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam::queue::SegQueue;
|
||||
use macros::log;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::core::infrastructure::health::SystemHealth;
|
||||
use crate::core::infrastructure::ml_alert::MLAlert;
|
||||
use crate::ml::config_loader::InferenceConfig;
|
||||
use crate::ml::engine::Engine;
|
||||
use crate::ml::feature_extractor::FlowFeatures;
|
||||
use crate::ml::model_loader::MLModels;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::error::system::SystemError;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::log::system::SystemLog;
|
||||
use crate::ml::traffic_logger::TrafficLogger;
|
||||
|
||||
pub struct AppServices {
|
||||
pub health: Arc<SystemHealth>,
|
||||
pub ml_alert: Arc<MLAlert>,
|
||||
pub ml_models: Arc<MLModels>,
|
||||
pub ml_engine: Arc<Engine>,
|
||||
shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
impl AppServices {
|
||||
pub fn new(app_config: Arc<AppConfig>, inference_config: Arc<InferenceConfig>) -> Result<Self, Error> {
|
||||
let health = SystemHealth::new(app_config.clone())?;
|
||||
|
||||
let ml_models = Arc::new(MLModels::load_models(&app_config, &inference_config)?);
|
||||
let ml_alert = Arc::new(MLAlert::new());
|
||||
|
||||
let traffic_logger = if app_config.traffic_logging_mode {
|
||||
let csv_path = app_config.traffic_log_csv_path.clone();
|
||||
let mut header = FlowFeatures::all_feature_names_owned();
|
||||
header.push("Label".to_string());
|
||||
let logger = TrafficLogger::new(&csv_path, header)
|
||||
.map_err(|e| MiscError::TrafficLogCreateError(csv_path.clone(), e.to_string()))?;
|
||||
log!(SystemLog::TrafficLoggingEnabled(csv_path));
|
||||
Some(Arc::new(logger))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ml_engine = Arc::new(Engine::new(
|
||||
ml_models.clone(),
|
||||
inference_config.clone(),
|
||||
ml_alert.clone(),
|
||||
app_config.max_concurrent_flows,
|
||||
app_config.min_packets_for_inference,
|
||||
app_config.inference_batch_size,
|
||||
app_config.inference_interval_secs,
|
||||
app_config.aggregator_window_secs,
|
||||
traffic_logger,
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
health: Arc::new(health),
|
||||
ml_alert,
|
||||
ml_models,
|
||||
ml_engine,
|
||||
shutdowns: SegQueue::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn run(&self) -> Result<(), Error> {
|
||||
let health = self.health.clone();
|
||||
let ml_engine = self.ml_engine.clone();
|
||||
|
||||
let health_shutdown = health.run(Duration::from_secs(3)).await;
|
||||
self.shutdowns.push(health_shutdown);
|
||||
|
||||
let ml_shutdown = ml_engine.run().await;
|
||||
self.shutdowns.push(ml_shutdown);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn terminate(&self) {
|
||||
while let Some(shutdown) = self.shutdowns.pop() {
|
||||
if shutdown.send(()).is_err() {
|
||||
log!(SystemError::ShutdownSignalFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,8 @@ use macros::log;
|
||||
|
||||
use crate::core::ebpf::EbpfServices;
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::core::infrastructure::AppServices;
|
||||
use crate::ml::config_loader::InferenceConfig;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
@ -18,19 +20,15 @@ use crate::model::error::Error;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::log::system::SystemLog;
|
||||
use crate::utils::logging::Logging;
|
||||
use crate::web::api::{control, default, misc};
|
||||
use crate::ml::model_loader::MLModels;
|
||||
use crate::ml::config_loader::InferenceConfig;
|
||||
use crate::ml::engine::Engine;
|
||||
use crate::web::api::{control, default, health, misc, ml_alert};
|
||||
|
||||
pub struct System {
|
||||
pub app_config: Arc<AppConfig>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub ebpf_services: Arc<EbpfServices>,
|
||||
pub app_services: Arc<AppServices>,
|
||||
pub ingress_ebpf: Ebpf,
|
||||
pub egress_ebpf: Ebpf,
|
||||
pub ml_models: Arc<MLModels>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub ml_engine: Arc<Engine>,
|
||||
#[allow(dead_code)]
|
||||
ingress_program_array: ProgramArray<MapData>,
|
||||
#[allow(dead_code)]
|
||||
@ -45,36 +43,21 @@ impl System {
|
||||
|
||||
let inference_config = Arc::new(InferenceConfig::load_file(&app_config.models_config_name)?);
|
||||
|
||||
let ml_models = Arc::new(MLModels::load_models(&app_config, inference_config.num_features())?);
|
||||
|
||||
let ebpf_services = Arc::new(EbpfServices::new(
|
||||
app_config.clone(),
|
||||
&mut ingress_ebpf,
|
||||
&mut egress_ebpf,
|
||||
)?);
|
||||
|
||||
let ml_engine = Arc::new(Engine::new(
|
||||
ml_models.clone(),
|
||||
inference_config.clone(),
|
||||
app_config.max_concurrent_flows,
|
||||
app_config.min_packets_for_inference,
|
||||
app_config.inference_interval_secs,
|
||||
));
|
||||
|
||||
log!(MLLog::EngineStarted {
|
||||
max_flows: app_config.max_concurrent_flows,
|
||||
min_packets: app_config.min_packets_for_inference,
|
||||
interval_secs: app_config.inference_interval_secs
|
||||
});
|
||||
let app_services = Arc::new(AppServices::new(app_config.clone(), inference_config.clone())?);
|
||||
|
||||
let system = System {
|
||||
app_config,
|
||||
inference_config,
|
||||
ebpf_services,
|
||||
app_services,
|
||||
ingress_ebpf,
|
||||
egress_ebpf,
|
||||
ml_models,
|
||||
inference_config,
|
||||
ml_engine,
|
||||
ingress_program_array,
|
||||
egress_program_array,
|
||||
};
|
||||
@ -83,15 +66,19 @@ impl System {
|
||||
|
||||
pub async fn run(&mut self) -> Result<(), Error> {
|
||||
let ebpf_services = self.ebpf_services.clone();
|
||||
let app_services = self.app_services.clone();
|
||||
Logging::initialize()?;
|
||||
log!(SystemLog::Initializing);
|
||||
|
||||
log!(MLLog::ModelsLoaded { info: self.ml_models.get_model_info("deep_autoencoder") });
|
||||
log!(MLLog::ModelsLoaded { info: self.ml_models.get_model_info("random_forest") });
|
||||
log!(MLLog::ModelsLoaded { info: self.ml_models.get_model_info("mlp") });
|
||||
log!(MLLog::ModelsLoaded(
|
||||
self.app_services.ml_models.get_model_info("deep_autoencoder")
|
||||
));
|
||||
log!(MLLog::ModelsLoaded(
|
||||
self.app_services.ml_models.get_model_info("classifier")
|
||||
));
|
||||
|
||||
log!(MLLog::ConfigLoaded {
|
||||
features: self.inference_config.num_features(),
|
||||
features: self.inference_config.num_ae_features(),
|
||||
attacks: self.inference_config.num_attack_types()
|
||||
});
|
||||
|
||||
@ -99,19 +86,19 @@ impl System {
|
||||
log!(SystemLog::InitializeComplete);
|
||||
self.attach_ebpf()?;
|
||||
|
||||
|
||||
let _ml_handle = self.ml_engine.clone().start();
|
||||
|
||||
ebpf_services.run(self.ml_engine.clone()).await?;
|
||||
ebpf_services.run(app_services.ml_engine.clone()).await?;
|
||||
app_services.run().await?;
|
||||
self.run_http_server().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn terminate(&self) -> Result<(), Error> {
|
||||
let ebpf_services = self.ebpf_services.clone();
|
||||
let app_services = self.app_services.clone();
|
||||
log!(SystemLog::Terminating);
|
||||
|
||||
ebpf_services.terminate();
|
||||
app_services.terminate();
|
||||
log!(SystemLog::TerminateComplete);
|
||||
Ok(())
|
||||
}
|
||||
@ -152,12 +139,12 @@ impl System {
|
||||
|
||||
async fn run_http_server(&self) -> Result<(), Error> {
|
||||
let app_config = self.app_config.clone();
|
||||
let inference_config = self.inference_config.clone();
|
||||
let access_control = self.ebpf_services.access_control.clone();
|
||||
let service = self.ebpf_services.service.clone();
|
||||
let statistics = self.ebpf_services.statistics.clone();
|
||||
let health = self.ebpf_services.health.clone();
|
||||
let ml_models = self.ml_models.clone();
|
||||
let inference_config = self.inference_config.clone();
|
||||
let health = self.app_services.health.clone();
|
||||
let ml_alert = self.app_services.ml_alert.clone();
|
||||
let port = self.app_config.http_server_bind_port;
|
||||
HttpServer::new(move || {
|
||||
let cors = actix_cors::Cors::default()
|
||||
@ -168,21 +155,23 @@ impl System {
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.app_data(web::Data::from(app_config.clone()))
|
||||
.app_data(web::Data::from(inference_config.clone()))
|
||||
.app_data(web::Data::from(access_control.clone()))
|
||||
.app_data(web::Data::from(service.clone()))
|
||||
.app_data(web::Data::from(statistics.clone()))
|
||||
.app_data(web::Data::from(health.clone()))
|
||||
.app_data(web::Data::from(ml_models.clone()))
|
||||
.app_data(web::Data::from(inference_config.clone()))
|
||||
.app_data(web::Data::from(ml_alert.clone()))
|
||||
.service(control::initialize())
|
||||
.service(ml_alert::initialize())
|
||||
.service(health::initialize())
|
||||
.service(misc::initialize())
|
||||
.default_service(route().to(default::default_route))
|
||||
})
|
||||
.bind(format!("0.0.0.0:{}", port))
|
||||
.map_err(HttpError::BindPortError)?
|
||||
.run()
|
||||
.await
|
||||
.map_err(HttpError::ServerPanic)?;
|
||||
.bind(format!("0.0.0.0:{}", port))
|
||||
.map_err(HttpError::BindPortError)?
|
||||
.run()
|
||||
.await
|
||||
.map_err(HttpError::ServerPanic)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -191,13 +180,23 @@ impl System {
|
||||
env!("OUT_DIR"),
|
||||
"/net-guardia-ingress"
|
||||
)))
|
||||
.map_err(EbpfError::EbpfNotFound)?;
|
||||
.map_err(EbpfError::EbpfNotFound)?;
|
||||
let program_array = ingress_ebpf.take_map("PROGRAM_ARRAY").ok_or(EbpfError::MapNotFound)?;
|
||||
let mut program_array = ProgramArray::try_from(program_array).map_err(EbpfError::MapOperationError)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "access_control", ingress::ACCESS_CONTROL)?;
|
||||
Self::load_program(
|
||||
&mut ingress_ebpf,
|
||||
&mut program_array,
|
||||
"access_control",
|
||||
ingress::ACCESS_CONTROL,
|
||||
)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "service", ingress::SERVICE)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "statistics", ingress::STATISTICS)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "transmission", ingress::TRANSMISSION)?;
|
||||
Self::load_program(
|
||||
&mut ingress_ebpf,
|
||||
&mut program_array,
|
||||
"transmission",
|
||||
ingress::TRANSMISSION,
|
||||
)?;
|
||||
Ok((ingress_ebpf, program_array))
|
||||
}
|
||||
|
||||
@ -206,10 +205,16 @@ impl System {
|
||||
env!("OUT_DIR"),
|
||||
"/net-guardia-egress"
|
||||
)))
|
||||
.map_err(EbpfError::EbpfNotFound)?;
|
||||
.map_err(EbpfError::EbpfNotFound)?;
|
||||
let program_array = egress_ebpf.take_map("PROGRAM_ARRAY").ok_or(EbpfError::MapNotFound)?;
|
||||
let mut program_array = ProgramArray::try_from(program_array).map_err(EbpfError::MapOperationError)?;
|
||||
Self::load_program(&mut egress_ebpf, &mut program_array, "statistics", egress::STATISTICS)?;
|
||||
Self::load_program(
|
||||
&mut egress_ebpf,
|
||||
&mut program_array,
|
||||
"transmission",
|
||||
egress::TRANSMISSION,
|
||||
)?;
|
||||
Ok((egress_ebpf, program_array))
|
||||
}
|
||||
|
||||
@ -241,4 +246,4 @@ impl System {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,12 +20,12 @@ impl AttackAggregator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_alert(&mut self, flow_key: &FlowKey, ensemble_score: f32, threshold: f32) -> bool {
|
||||
pub fn should_alert(&mut self, flow_key: &FlowKey, score: f32, threshold: f32) -> bool {
|
||||
let now = Instant::now();
|
||||
|
||||
let detections = self.detections.entry(flow_key.clone()).or_default();
|
||||
detections.retain(|(time, _)| now.duration_since(*time) < self.window_duration);
|
||||
detections.push((now, ensemble_score));
|
||||
detections.push((now, score));
|
||||
|
||||
if detections.len() >= self.min_detections {
|
||||
let avg_score: f32 =
|
||||
|
||||
@ -4,20 +4,19 @@ use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::error::ml::MLError;
|
||||
use crate::model::ml_detection::{AENormalization, ClipParams};
|
||||
use crate::model::ml_detection::{AENormalization, ClipParams, PrecisionLevels};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InferenceConfig {
|
||||
pub threshold: f64,
|
||||
pub strategy_name: String,
|
||||
pub clip_params: HashMap<String, ClipParams>,
|
||||
pub scaler_mean: Vec<f64>,
|
||||
pub scaler_std: Vec<f64>,
|
||||
pub post_clip_min: f64,
|
||||
pub post_clip_max: f64,
|
||||
pub ae_normalization: AENormalization,
|
||||
pub ae_feature_names: Vec<String>,
|
||||
pub ae_clip_params: HashMap<String, ClipParams>,
|
||||
pub ae_scaler_mean: Vec<f64>,
|
||||
pub ae_scaler_std: Vec<f64>,
|
||||
pub ae_post_clip_min: f64,
|
||||
pub ae_post_clip_max: f64,
|
||||
pub ae_threshold: f32,
|
||||
pub classifier_feature_names: Vec<String>,
|
||||
pub attack_labels: HashMap<String, String>,
|
||||
pub feature_names: Vec<String>,
|
||||
}
|
||||
|
||||
impl InferenceConfig {
|
||||
@ -30,8 +29,12 @@ impl InferenceConfig {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn num_features(&self) -> usize {
|
||||
self.feature_names.len()
|
||||
pub fn num_ae_features(&self) -> usize {
|
||||
self.ae_feature_names.len()
|
||||
}
|
||||
|
||||
pub fn num_classifier_features(&self) -> usize {
|
||||
self.classifier_feature_names.len()
|
||||
}
|
||||
|
||||
pub fn num_attack_types(&self) -> usize {
|
||||
|
||||
@ -1,140 +1,154 @@
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use macros::log;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time::interval;
|
||||
|
||||
use super::aggregator::AttackAggregator;
|
||||
use super::config_loader::InferenceConfig;
|
||||
use super::feature_extractor::FlowFeatures;
|
||||
use super::flow_tracker::FlowTracker;
|
||||
use super::inference::Inference;
|
||||
use super::model_loader::MLModels;
|
||||
use super::aggregator::AttackAggregator;
|
||||
use super::traffic_logger::TrafficLogger;
|
||||
|
||||
use crate::utils::packet_parser::parse_packet;
|
||||
use crate::core::infrastructure::ml_alert::MLAlert;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::ml_detection::{EngineStats, InferenceStats};
|
||||
use crate::utils::packet_parser::parse_packet;
|
||||
|
||||
pub struct Engine {
|
||||
flow_tracker: Arc<FlowTracker>,
|
||||
inference_pipeline: Arc<Inference>,
|
||||
aggregator: Arc<Mutex<AttackAggregator>>,
|
||||
ml_alert: Arc<MLAlert>,
|
||||
min_packets: usize,
|
||||
batch_size: usize,
|
||||
inference_interval_secs: u64,
|
||||
traffic_logger: Option<Arc<TrafficLogger>>,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new(
|
||||
models: Arc<MLModels>,
|
||||
config: Arc<InferenceConfig>,
|
||||
ml_alert: Arc<MLAlert>,
|
||||
max_flows: usize,
|
||||
min_packets: usize,
|
||||
batch_size: usize,
|
||||
interval_secs: u64,
|
||||
window_secs: u64,
|
||||
traffic_logger: Option<Arc<TrafficLogger>>,
|
||||
) -> Self {
|
||||
let flow_tracker = Arc::new(FlowTracker::new(max_flows));
|
||||
let inference_pipeline = Arc::new(Inference::new(models, config));
|
||||
|
||||
let aggregator = Arc::new(Mutex::new(AttackAggregator::new(30, 10)));
|
||||
let min_detections = ((window_secs / interval_secs) / 2).max(1) as usize;
|
||||
let aggregator = Arc::new(Mutex::new(AttackAggregator::new(window_secs, min_detections)));
|
||||
|
||||
Self {
|
||||
flow_tracker,
|
||||
inference_pipeline,
|
||||
aggregator,
|
||||
ml_alert,
|
||||
min_packets,
|
||||
batch_size,
|
||||
inference_interval_secs: interval_secs,
|
||||
traffic_logger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(self: Arc<Self>) -> tokio::task::JoinHandle<()> {
|
||||
pub async fn run(self: Arc<Self>) -> oneshot::Sender<()> {
|
||||
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||
tokio::spawn(async move {
|
||||
self.run_inference_loop().await;
|
||||
})
|
||||
self.run_inference_loop(shutdown_rx).await;
|
||||
});
|
||||
shutdown_tx
|
||||
}
|
||||
|
||||
pub fn get_flow_tracker(&self) -> Arc<FlowTracker> {
|
||||
self.flow_tracker.clone()
|
||||
}
|
||||
|
||||
async fn run_inference_loop(&self) {
|
||||
async fn run_inference_loop(&self, mut shutdown_rx: oneshot::Receiver<()>) {
|
||||
let mut ticker = interval(Duration::from_secs(self.inference_interval_secs));
|
||||
|
||||
loop {
|
||||
ticker.tick().await;
|
||||
|
||||
let start = Instant::now();
|
||||
tokio::select! {
|
||||
_ = &mut shutdown_rx => break,
|
||||
_ = ticker.tick() => {}
|
||||
}
|
||||
|
||||
let total_flows = self.flow_tracker.flow_count();
|
||||
let all_flows = self.flow_tracker.get_flows_snapshot();
|
||||
let packet_counts: Vec<usize> = all_flows.iter().map(|f| f.packet_count()).collect();
|
||||
|
||||
let flows = self
|
||||
.flow_tracker
|
||||
.get_flows_for_inference(self.min_packets);
|
||||
let flows = self.flow_tracker.get_flows_for_inference(self.min_packets);
|
||||
|
||||
log!(
|
||||
MLLog::FlowStats(
|
||||
total_flows,
|
||||
flows.len(),
|
||||
self.min_packets,
|
||||
format!("{:?}", packet_counts)
|
||||
)
|
||||
);
|
||||
log!(MLLog::FlowStats(
|
||||
total_flows,
|
||||
flows.len(),
|
||||
self.min_packets,
|
||||
format!("{:?}", packet_counts)
|
||||
));
|
||||
|
||||
if flows.is_empty() {
|
||||
log!(
|
||||
MLLog::InferenceSkipped(
|
||||
format!(
|
||||
"No flows with sufficient packets (total flows: {}, min packets: {})",
|
||||
total_flows,
|
||||
self.min_packets
|
||||
)
|
||||
)
|
||||
);
|
||||
log!(MLLog::InferenceSkipped(format!(
|
||||
"No flows with sufficient packets (total flows: {}, min packets: {})",
|
||||
total_flows, self.min_packets
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
|
||||
let batch_size = flows.len().min(200);
|
||||
let batch = &flows[..batch_size];
|
||||
|
||||
log!(MLLog::RunningInference(batch_size));
|
||||
|
||||
let results = self.inference_pipeline.infer_batch(batch);
|
||||
|
||||
let elapsed_us = start.elapsed().as_micros() as u64;
|
||||
let stats = InferenceStats::from_results(&results, elapsed_us);
|
||||
|
||||
if results.len() != batch_size {
|
||||
log!(MLLog::InferenceResults(batch_size, results.len()));
|
||||
if let Some(ref logger) = self.traffic_logger {
|
||||
let feature_names = FlowFeatures::all_feature_names_owned();
|
||||
for flow in &flows {
|
||||
let features = FlowFeatures::extract(flow, &feature_names);
|
||||
logger.log_row(features.to_csv_record());
|
||||
}
|
||||
self.flow_tracker.cleanup_old_flows(60_000_000);
|
||||
continue;
|
||||
}
|
||||
|
||||
log!(
|
||||
MLLog::InferenceCompleted(
|
||||
stats.total_flows,
|
||||
stats.malicious_flows,
|
||||
stats.benign_flows,(elapsed_us as f64 / 1000.0) as u32,
|
||||
stats.flows_per_second
|
||||
)
|
||||
);
|
||||
let batch = &flows[..flows.len().min(self.batch_size)];
|
||||
|
||||
log!(MLLog::RunningInference(batch.len()));
|
||||
|
||||
let start = Instant::now();
|
||||
let results = self.inference_pipeline.infer_batch(batch);
|
||||
let elapsed_us = start.elapsed().as_micros() as u64;
|
||||
|
||||
let stats = InferenceStats::from_results(&results, elapsed_us);
|
||||
|
||||
if results.len() != batch.len() {
|
||||
log!(MLLog::InferenceResults(batch.len(), results.len()));
|
||||
}
|
||||
|
||||
log!(MLLog::InferenceCompleted(
|
||||
stats.total_flows,
|
||||
stats.malicious_flows,
|
||||
stats.benign_flows,
|
||||
(elapsed_us as f64 / 1000.0) as u32,
|
||||
stats.flows_per_second
|
||||
));
|
||||
|
||||
if let Ok(mut aggregator) = self.aggregator.lock() {
|
||||
for result in &results {
|
||||
if result.is_attack {
|
||||
let should_alert = aggregator.should_alert(
|
||||
&result.flow_key_raw,
|
||||
result.ensemble_score,
|
||||
self.inference_pipeline.config.threshold as f32,
|
||||
);
|
||||
let should_alert =
|
||||
aggregator.should_alert(&result.flow_key_raw, result.ae_score, result.threshold);
|
||||
|
||||
if should_alert {
|
||||
log!(
|
||||
MLLog::ThreatDetected(
|
||||
result.flow_key.clone(),
|
||||
result.attack_type.clone().unwrap_or_else(|| "UNKNOWN".to_string()),
|
||||
result.confidence,
|
||||
result.ae_score,
|
||||
result.rf_score,
|
||||
result.ensemble_score,
|
||||
)
|
||||
);
|
||||
log!(MLLog::ThreatDetected(
|
||||
format!("{:?}", result.direction),
|
||||
result.flow_key.clone(),
|
||||
result.attack_type.clone().unwrap_or_else(|| "UNKNOWN".to_string()),
|
||||
result.confidence,
|
||||
result.ae_score,
|
||||
));
|
||||
|
||||
self.ml_alert.broadcast_alert(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,14 +160,13 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_packet(&self, packet_data: &[u8]) {
|
||||
pub fn process_packet(&self, packet_data: &[u8], is_ingress: bool) {
|
||||
match parse_packet(packet_data) {
|
||||
Some(packet_info) => {
|
||||
self.flow_tracker.process_packet(packet_info);
|
||||
}
|
||||
None => {
|
||||
log!(MLLog::ParsePacketFailed(packet_data.len()))
|
||||
Some((packet_info, payload_start)) => {
|
||||
let payload = packet_data.get(payload_start..).unwrap_or(&[]);
|
||||
self.flow_tracker.process_packet(packet_info, is_ingress, payload);
|
||||
}
|
||||
None => log!(MLLog::ParsePacketFailed(packet_data.len())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,13 +186,13 @@ impl PacketProcessor {
|
||||
Self { ml_engine }
|
||||
}
|
||||
|
||||
pub fn process(&self, packet_data: &[u8]) {
|
||||
self.ml_engine.process_packet(packet_data);
|
||||
pub fn process(&self, packet_data: &[u8], is_ingress: bool) {
|
||||
self.ml_engine.process_packet(packet_data, is_ingress);
|
||||
}
|
||||
|
||||
pub fn process_batch(&self, packets: &[Vec<u8>]) {
|
||||
pub fn process_batch(&self, packets: &[Vec<u8>], is_ingress: bool) {
|
||||
for packet in packets {
|
||||
self.process(packet);
|
||||
self.process(packet, is_ingress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::model::ml_detection::{PacketData, ClipParams};
|
||||
|
||||
use super::flow_tracker::FlowData;
|
||||
use crate::model::ml_detection::{ClipParams, PacketData};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FlowFeatures {
|
||||
@ -46,17 +47,17 @@ impl FlowFeatures {
|
||||
|
||||
// 16-19
|
||||
let flow_iats = compute_flow_iats(&flow.fwd_packets, &flow.bwd_packets);
|
||||
let (flow_iat_mean, flow_iat_std, flow_iat_max, flow_iat_min) = compute_stats(&flow_iats);
|
||||
let (flow_iat_max, flow_iat_min, flow_iat_mean, flow_iat_std) = compute_stats(&flow_iats);
|
||||
|
||||
// 20-24
|
||||
let fwd_iats = compute_iats(&flow.fwd_packets);
|
||||
let fwd_iat_total: f64 = fwd_iats.iter().sum();
|
||||
let (fwd_iat_mean, fwd_iat_std, fwd_iat_max, fwd_iat_min) = compute_stats(&fwd_iats);
|
||||
let (fwd_iat_max, fwd_iat_min, fwd_iat_mean, fwd_iat_std) = compute_stats(&fwd_iats);
|
||||
|
||||
// 25-29
|
||||
let bwd_iats = compute_iats(&flow.bwd_packets);
|
||||
let bwd_iat_total: f64 = bwd_iats.iter().sum();
|
||||
let (bwd_iat_mean, bwd_iat_std, bwd_iat_max, bwd_iat_min) = compute_stats(&bwd_iats);
|
||||
let (bwd_iat_max, bwd_iat_min, bwd_iat_mean, bwd_iat_std) = compute_stats(&bwd_iats);
|
||||
|
||||
// 30-37
|
||||
let fwd_psh = flow.fwd_packets.iter().filter(|p| p.flags.psh).count() as f64;
|
||||
@ -79,58 +80,50 @@ impl FlowFeatures {
|
||||
let bwd_bulk = &flow.bwd_bulk_state;
|
||||
|
||||
// 68-69
|
||||
let fwd_seg_sizes: Vec<f64> = flow.fwd_packets
|
||||
let fwd_seg_sizes: Vec<f64> = flow
|
||||
.fwd_packets
|
||||
.iter()
|
||||
.filter(|p| p.payload_length > 0)
|
||||
.map(|p| p.header_length as f64)
|
||||
.collect();
|
||||
|
||||
// 70-73
|
||||
let (active_mean, active_std, active_max, active_min) = compute_stats(
|
||||
&flow
|
||||
.active_periods
|
||||
.iter()
|
||||
.map(|&x| x as f64)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
let (active_max, active_min, active_mean, active_std) =
|
||||
compute_stats(&flow.active_periods.iter().map(|&x| x as f64).collect::<Vec<_>>());
|
||||
|
||||
// 74-77
|
||||
let (idle_mean, idle_std, idle_max, idle_min) = compute_stats(
|
||||
&flow
|
||||
.idle_periods
|
||||
.iter()
|
||||
.map(|&x| x as f64)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
let (idle_max, idle_min, idle_mean, idle_std) =
|
||||
compute_stats(&flow.idle_periods.iter().map(|&x| x as f64).collect::<Vec<_>>());
|
||||
|
||||
match feature_name {
|
||||
"Destination Port" => flow.flow_key.dst_port as f64,
|
||||
"Flow Duration" => duration_us,
|
||||
"Total Fwd Packets" => fwd_count,
|
||||
"Total Backward Packets" => bwd_count,
|
||||
"Total Length of Fwd Packets" => flow.fwd_total_bytes as f64,
|
||||
"Total Length of Bwd Packets" => flow.bwd_total_bytes as f64,
|
||||
"Destination Port" | "Dst Port" | "dst_port" => flow.flow_key.dst_port as f64,
|
||||
"Protocol" | "protocol" => flow.flow_key.protocol as f64,
|
||||
"Flow Duration" | "flow_duration" => duration_us,
|
||||
"Total Fwd Packets" | "Tot Fwd Pkts" | "fwd_packets" => fwd_count,
|
||||
"Total Backward Packets" | "Tot Bwd Pkts" | "bwd_packets" => bwd_count,
|
||||
"Total Length of Fwd Packets" | "TotLen Fwd Pkts" | "fwd_bytes" => flow.fwd_total_bytes as f64,
|
||||
"Total Length of Bwd Packets" | "TotLen Bwd Pkts" | "bwd_bytes" => flow.bwd_total_bytes as f64,
|
||||
"Fwd Packet Length Max" => fwd_max,
|
||||
"Fwd Packet Length Min" => fwd_min,
|
||||
"Fwd Packet Length Mean" => fwd_mean,
|
||||
"Fwd Packet Length Std" => fwd_std,
|
||||
"Fwd Packet Length Mean" | "Fwd Pkt Len Mean" | "fwd_pkt_len_mean" => fwd_mean,
|
||||
"Fwd Packet Length Std" | "Fwd Pkt Len Std" | "fwd_pkt_len_std" => fwd_std,
|
||||
"Bwd Packet Length Max" => bwd_max,
|
||||
"Bwd Packet Length Min" => bwd_min,
|
||||
"Bwd Packet Length Mean" => bwd_mean,
|
||||
"Bwd Packet Length Std" => bwd_std,
|
||||
"Flow Bytes/s" => safe_div(total_bytes, duration_s),
|
||||
"Flow Packets/s" => safe_div(total_count, duration_s),
|
||||
"Flow IAT Mean" => flow_iat_mean,
|
||||
"Bwd Packet Length Mean" | "Bwd Pkt Len Mean" | "bwd_pkt_len_mean" => bwd_mean,
|
||||
"Bwd Packet Length Std" | "Bwd Pkt Len Std" | "bwd_pkt_len_std" => bwd_std,
|
||||
"Flow Bytes/s" | "Flow Byts/s" | "flow_bytes_per_sec" => safe_div(total_bytes, duration_s),
|
||||
"Flow Packets/s" | "Flow Pkts/s" | "flow_pkts_per_sec" => safe_div(total_count, duration_s),
|
||||
"Flow IAT Mean" | "flow_iat_mean" => flow_iat_mean,
|
||||
"Flow IAT Std" => flow_iat_std,
|
||||
"Flow IAT Max" => flow_iat_max,
|
||||
"Flow IAT Min" => flow_iat_min,
|
||||
"Fwd IAT Total" => fwd_iat_total,
|
||||
"Fwd IAT Mean" => fwd_iat_mean,
|
||||
"Fwd IAT Mean" | "fwd_iat_mean" => fwd_iat_mean,
|
||||
"Fwd IAT Std" => fwd_iat_std,
|
||||
"Fwd IAT Max" => fwd_iat_max,
|
||||
"Fwd IAT Min" => fwd_iat_min,
|
||||
"Bwd IAT Total" => bwd_iat_total,
|
||||
"Bwd IAT Mean" => bwd_iat_mean,
|
||||
"Bwd IAT Mean" | "bwd_iat_mean" => bwd_iat_mean,
|
||||
"Bwd IAT Std" => bwd_iat_std,
|
||||
"Bwd IAT Max" => bwd_iat_max,
|
||||
"Bwd IAT Min" => bwd_iat_min,
|
||||
@ -144,14 +137,14 @@ impl FlowFeatures {
|
||||
"Bwd Packets/s" => safe_div(bwd_count, duration_s),
|
||||
"Min Packet Length" => min_len,
|
||||
"Max Packet Length" => max_len,
|
||||
"Packet Length Mean" => mean_len,
|
||||
"Packet Length Std" => std_len,
|
||||
"Packet Length Mean" | "Pkt Len Mean" | "pkt_len_mean" => mean_len,
|
||||
"Packet Length Std" | "Pkt Len Std" | "pkt_len_std" => std_len,
|
||||
"Packet Length Variance" => std_len * std_len,
|
||||
"FIN Flag Count" => flow.fin_count as f64,
|
||||
"SYN Flag Count" => flow.syn_count as f64,
|
||||
"RST Flag Count" => flow.rst_count as f64,
|
||||
"PSH Flag Count" => flow.psh_count as f64,
|
||||
"ACK Flag Count" => flow.ack_count as f64,
|
||||
"FIN Flag Count" | "FIN Flag Cnt" | "fin_flag_cnt" => flow.fin_count as f64,
|
||||
"SYN Flag Count" | "SYN Flag Cnt" | "syn_flag_cnt" => flow.syn_count as f64,
|
||||
"RST Flag Count" | "RST Flag Cnt" | "rst_flag_cnt" => flow.rst_count as f64,
|
||||
"PSH Flag Count" | "PSH Flag Cnt" | "psh_flag_cnt" => flow.psh_count as f64,
|
||||
"ACK Flag Count" | "ACK Flag Cnt" | "ack_flag_cnt" => flow.ack_count as f64,
|
||||
"URG Flag Count" => flow.urg_count as f64,
|
||||
"CWE Flag Count" => flow.cwe_count as f64,
|
||||
"ECE Flag Count" => flow.ece_count as f64,
|
||||
@ -162,18 +155,25 @@ impl FlowFeatures {
|
||||
"Fwd Header Length.1" => flow.fwd_header_bytes as f64,
|
||||
"Fwd Avg Bytes/Bulk" => safe_div(fwd_bulk.total_bytes as f64, fwd_bulk.bulk_count as f64),
|
||||
"Fwd Avg Packets/Bulk" => safe_div(fwd_bulk.total_packets as f64, fwd_bulk.bulk_count as f64),
|
||||
"Fwd Avg Bulk Rate" => safe_div(fwd_bulk.total_bytes as f64, duration_s),
|
||||
"Fwd Avg Bulk Rate" => safe_div(
|
||||
fwd_bulk.total_bytes as f64,
|
||||
fwd_bulk.total_duration_us as f64 / 1_000_000.0,
|
||||
),
|
||||
"Bwd Avg Bytes/Bulk" => safe_div(bwd_bulk.total_bytes as f64, bwd_bulk.bulk_count as f64),
|
||||
"Bwd Avg Packets/Bulk" => safe_div(bwd_bulk.total_packets as f64, bwd_bulk.bulk_count as f64),
|
||||
"Bwd Avg Bulk Rate" => safe_div(bwd_bulk.total_bytes as f64, duration_s),
|
||||
"Bwd Avg Bulk Rate" => safe_div(
|
||||
bwd_bulk.total_bytes as f64,
|
||||
bwd_bulk.total_duration_us as f64 / 1_000_000.0,
|
||||
),
|
||||
"Subflow Fwd Packets" => fwd_count,
|
||||
"Subflow Fwd Bytes" => flow.fwd_total_bytes as f64,
|
||||
"Subflow Bwd Packets" => bwd_count,
|
||||
"Subflow Bwd Bytes" => flow.bwd_total_bytes as f64,
|
||||
"Init_Win_bytes_forward" => flow.init_win_bytes_fwd as f64,
|
||||
"Init_Win_bytes_backward" => flow.init_win_bytes_bwd as f64,
|
||||
"act_data_pkt_fwd" => fwd_seg_sizes.len() as f64,
|
||||
"min_seg_size_forward" => fwd_seg_sizes.iter()
|
||||
"Init_Win_bytes_forward" | "Init Fwd Win Byts" | "fwd_win_bytes" => flow.init_win_bytes_fwd as f64,
|
||||
"Init_Win_bytes_backward" | "Init Bwd Win Byts" | "bwd_win_bytes" => flow.init_win_bytes_bwd as f64,
|
||||
"act_data_pkt_fwd" | "Fwd Act Data Pkts" | "fwd_act_data_pkts" => fwd_seg_sizes.len() as f64,
|
||||
"min_seg_size_forward" | "Fwd Seg Size Min" | "fwd_seg_size_min" => fwd_seg_sizes
|
||||
.iter()
|
||||
.min_by(|a, b| a.total_cmp(b))
|
||||
.copied()
|
||||
.unwrap_or(0.0),
|
||||
@ -186,9 +186,7 @@ impl FlowFeatures {
|
||||
"Idle Max" => idle_max,
|
||||
"Idle Min" => idle_min,
|
||||
|
||||
_ => {
|
||||
0.0
|
||||
}
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +206,6 @@ impl FlowFeatures {
|
||||
}
|
||||
}
|
||||
|
||||
/// Winsorization: Clip each feature according to its specific bounds from clip_params
|
||||
pub fn winsorize(&mut self, clip_params: &HashMap<String, ClipParams>, feature_names: &[String]) {
|
||||
for (i, feature_name) in feature_names.iter().enumerate() {
|
||||
if i < self.feature_num {
|
||||
@ -218,6 +215,104 @@ impl FlowFeatures {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_features_content(&self) -> &Vec<f64> {
|
||||
&self.features
|
||||
}
|
||||
|
||||
pub fn all_feature_names() -> Vec<&'static str> {
|
||||
vec![
|
||||
"Destination Port",
|
||||
"Protocol",
|
||||
"Flow Duration",
|
||||
"Total Fwd Packets",
|
||||
"Total Backward Packets",
|
||||
"Total Length of Fwd Packets",
|
||||
"Total Length of Bwd Packets",
|
||||
"Fwd Packet Length Max",
|
||||
"Fwd Packet Length Min",
|
||||
"Fwd Packet Length Mean",
|
||||
"Fwd Packet Length Std",
|
||||
"Bwd Packet Length Max",
|
||||
"Bwd Packet Length Min",
|
||||
"Bwd Packet Length Mean",
|
||||
"Bwd Packet Length Std",
|
||||
"Flow Bytes/s",
|
||||
"Flow Packets/s",
|
||||
"Flow IAT Mean",
|
||||
"Flow IAT Std",
|
||||
"Flow IAT Max",
|
||||
"Flow IAT Min",
|
||||
"Fwd IAT Total",
|
||||
"Fwd IAT Mean",
|
||||
"Fwd IAT Std",
|
||||
"Fwd IAT Max",
|
||||
"Fwd IAT Min",
|
||||
"Bwd IAT Total",
|
||||
"Bwd IAT Mean",
|
||||
"Bwd IAT Std",
|
||||
"Bwd IAT Max",
|
||||
"Bwd IAT Min",
|
||||
"Fwd PSH Flags",
|
||||
"Bwd PSH Flags",
|
||||
"Fwd URG Flags",
|
||||
"Bwd URG Flags",
|
||||
"Fwd Header Length",
|
||||
"Bwd Header Length",
|
||||
"Fwd Packets/s",
|
||||
"Bwd Packets/s",
|
||||
"Min Packet Length",
|
||||
"Max Packet Length",
|
||||
"Packet Length Mean",
|
||||
"Packet Length Std",
|
||||
"Packet Length Variance",
|
||||
"FIN Flag Count",
|
||||
"SYN Flag Count",
|
||||
"RST Flag Count",
|
||||
"PSH Flag Count",
|
||||
"ACK Flag Count",
|
||||
"URG Flag Count",
|
||||
"CWE Flag Count",
|
||||
"ECE Flag Count",
|
||||
"Down/Up Ratio",
|
||||
"Average Packet Size",
|
||||
"Avg Fwd Segment Size",
|
||||
"Avg Bwd Segment Size",
|
||||
"Fwd Header Length.1",
|
||||
"Fwd Avg Bytes/Bulk",
|
||||
"Fwd Avg Packets/Bulk",
|
||||
"Fwd Avg Bulk Rate",
|
||||
"Bwd Avg Bytes/Bulk",
|
||||
"Bwd Avg Packets/Bulk",
|
||||
"Bwd Avg Bulk Rate",
|
||||
"Subflow Fwd Packets",
|
||||
"Subflow Fwd Bytes",
|
||||
"Subflow Bwd Packets",
|
||||
"Subflow Bwd Bytes",
|
||||
"Init_Win_bytes_forward",
|
||||
"Init_Win_bytes_backward",
|
||||
"act_data_pkt_fwd",
|
||||
"min_seg_size_forward",
|
||||
"Active Mean",
|
||||
"Active Std",
|
||||
"Active Max",
|
||||
"Active Min",
|
||||
"Idle Mean",
|
||||
"Idle Std",
|
||||
"Idle Max",
|
||||
"Idle Min",
|
||||
]
|
||||
}
|
||||
|
||||
pub fn all_feature_names_owned() -> Vec<String> {
|
||||
Self::all_feature_names().iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
pub fn to_csv_record(&self) -> Vec<String> {
|
||||
let mut record: Vec<String> = self.features.iter().map(|f| f.to_string()).collect();
|
||||
record.push("BENIGN".to_string());
|
||||
record
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_stats(values: &[f64]) -> (f64, f64, f64, f64) {
|
||||
@ -261,4 +356,4 @@ fn compute_flow_iats(fwd_packets: &[PacketData], bwd_packets: &[PacketData]) ->
|
||||
.windows(2)
|
||||
.map(|w| (w[1].timestamp_us - w[0].timestamp_us) as f64)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,15 @@ use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time;
|
||||
|
||||
use crate::model::ml_detection::{BulkState, FlowKey, PacketData};
|
||||
use common::model::event::Event;
|
||||
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::ml_detection::{BulkState, FlowKey, PacketData};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FlowData {
|
||||
pub flow_key: FlowKey,
|
||||
pub direction: Direction,
|
||||
pub start_time_us: u64,
|
||||
pub last_time_us: u64,
|
||||
pub fwd_packets: Vec<PacketData>,
|
||||
@ -34,9 +37,10 @@ pub struct FlowData {
|
||||
}
|
||||
|
||||
impl FlowData {
|
||||
pub fn new(flow_key: FlowKey, first_packet: &Event) -> Self {
|
||||
pub fn new(flow_key: FlowKey, first_packet: &Event, direction: Direction) -> Self {
|
||||
Self {
|
||||
flow_key,
|
||||
direction,
|
||||
start_time_us: first_packet.timestamp_us(),
|
||||
last_time_us: first_packet.timestamp_us(),
|
||||
fwd_packets: Vec::new(),
|
||||
@ -149,9 +153,12 @@ impl FlowData {
|
||||
bulk_state.in_bulk = true;
|
||||
bulk_state.last_bulk_bytes = packet.length as u64;
|
||||
bulk_state.last_bulk_packets = 1;
|
||||
bulk_state.last_bulk_start_us = packet.timestamp_us;
|
||||
bulk_state.last_bulk_packet_us = packet.timestamp_us;
|
||||
} else {
|
||||
bulk_state.last_bulk_bytes += packet.length as u64;
|
||||
bulk_state.last_bulk_packets += 1;
|
||||
bulk_state.last_bulk_packet_us = packet.timestamp_us;
|
||||
}
|
||||
} else {
|
||||
if bulk_state.in_bulk
|
||||
@ -161,10 +168,15 @@ impl FlowData {
|
||||
bulk_state.bulk_count += 1;
|
||||
bulk_state.total_bytes += bulk_state.last_bulk_bytes;
|
||||
bulk_state.total_packets += bulk_state.last_bulk_packets;
|
||||
bulk_state.total_duration_us += bulk_state
|
||||
.last_bulk_packet_us
|
||||
.saturating_sub(bulk_state.last_bulk_start_us);
|
||||
}
|
||||
bulk_state.in_bulk = false;
|
||||
bulk_state.last_bulk_bytes = 0;
|
||||
bulk_state.last_bulk_packets = 0;
|
||||
bulk_state.last_bulk_start_us = 0;
|
||||
bulk_state.last_bulk_packet_us = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,27 +202,61 @@ impl FlowTracker {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_packet(&self, mut packet: Event) {
|
||||
let flow_key = FlowKey::from_packet(&packet);
|
||||
let reverse_key = flow_key.reverse();
|
||||
pub fn process_packet(&self, mut packet: Event, is_ingress: bool, payload: &[u8]) {
|
||||
let direction = if is_ingress {
|
||||
Direction::Ingress
|
||||
} else {
|
||||
Direction::Egress
|
||||
};
|
||||
let packet_key = FlowKey::from_packet(&packet);
|
||||
let proto = packet_key.protocol;
|
||||
let src_port = packet_key.src_port;
|
||||
let dst_port = packet_key.dst_port;
|
||||
let reversed_key = packet_key.clone().reverse();
|
||||
|
||||
let Ok(mut flows) = self.flows.lock() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let (actual_key, is_forward) = if flows.contains_key(&flow_key) {
|
||||
(flow_key, true)
|
||||
} else if flows.contains_key(&reverse_key) {
|
||||
(reverse_key, false)
|
||||
// Try-both: canonical key is whichever orientation already exists in the flow table.
|
||||
// For new flows, identify the initiator using (in priority order):
|
||||
// 1. TCP SYN / SYN+ACK flags
|
||||
// 2. DPI: TLS ClientHello/ServerHello, HTTP request/response, DNS QR bit
|
||||
// 3. Best effort: use packet as-is
|
||||
let (actual_key, is_forward) = if flows.contains_key(&packet_key) {
|
||||
(packet_key, true)
|
||||
} else if flows.contains_key(&reversed_key) {
|
||||
(reversed_key, false)
|
||||
} else {
|
||||
(flow_key, true)
|
||||
let flags = packet.tcp_flags();
|
||||
if flags.syn && flags.ack {
|
||||
// Normal: Server (egress side) sends SYN+ACK, packet arrives on ingress → reverse
|
||||
// Bot attack: Client (egress side) sends SYN+ACK, packet arrives on egress → keep as-is
|
||||
if is_ingress {
|
||||
(reversed_key, false)
|
||||
} else {
|
||||
(packet_key, true)
|
||||
}
|
||||
} else if flags.syn {
|
||||
(packet_key, true)
|
||||
} else {
|
||||
match detect_initiator(payload, proto, src_port, dst_port) {
|
||||
Some(true) => (packet_key, true),
|
||||
Some(false) => (reversed_key, false),
|
||||
None => (packet_key, true),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
packet.set_is_forward(is_forward);
|
||||
|
||||
let flow = flows.entry(actual_key.clone()).or_insert_with(|| {
|
||||
FlowData::new(actual_key, &packet)
|
||||
});
|
||||
// `direction` should reflect the initiator's interface.
|
||||
// If this packet is backward (is_forward = false), the initiator is on the opposite side.
|
||||
let initiator_direction = if is_forward { direction } else { direction.flip() };
|
||||
|
||||
let flow = flows
|
||||
.entry(actual_key.clone())
|
||||
.or_insert_with(|| FlowData::new(actual_key, &packet, initiator_direction));
|
||||
|
||||
flow.add_packet(&packet);
|
||||
|
||||
@ -248,9 +294,7 @@ impl FlowTracker {
|
||||
let Ok(mut flows) = self.flows.lock() else {
|
||||
return;
|
||||
};
|
||||
flows.retain(|_, flow| {
|
||||
now.saturating_sub(flow.last_time_us) < max_age_us
|
||||
});
|
||||
flows.retain(|_, flow| now.saturating_sub(flow.last_time_us) < max_age_us);
|
||||
}
|
||||
|
||||
pub fn flow_count(&self) -> usize {
|
||||
@ -259,4 +303,50 @@ impl FlowTracker {
|
||||
};
|
||||
flows.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inspect payload bytes to determine which side is the flow initiator.
|
||||
/// Returns Some(true) if this packet is from the initiator, Some(false) if from the responder,
|
||||
/// or None if the payload gives no useful signal.
|
||||
fn detect_initiator(payload: &[u8], protocol: u8, src_port: u16, dst_port: u16) -> Option<bool> {
|
||||
if payload.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// TLS: record type 0x16 (Handshake), byte 5 = handshake type
|
||||
// 0x01 = ClientHello → this side is the initiator
|
||||
// 0x02 = ServerHello → this side is the responder
|
||||
if payload.len() >= 6 && payload[0] == 0x16 {
|
||||
return match payload[5] {
|
||||
0x01 => Some(true),
|
||||
0x02 => Some(false),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
// HTTP: request line starts with a method verb (initiator),
|
||||
// response starts with "HTTP/" (responder)
|
||||
if payload.len() >= 5 {
|
||||
if payload.starts_with(b"GET ")
|
||||
|| payload.starts_with(b"POST ")
|
||||
|| payload.starts_with(b"PUT ")
|
||||
|| payload.starts_with(b"HEAD ")
|
||||
|| payload.starts_with(b"DELETE ")
|
||||
|| payload.starts_with(b"OPTIONS ")
|
||||
|| payload.starts_with(b"PATCH ")
|
||||
{
|
||||
return Some(true);
|
||||
}
|
||||
if payload.starts_with(b"HTTP/") {
|
||||
return Some(false);
|
||||
}
|
||||
}
|
||||
|
||||
// DNS over UDP (port 53): flags byte 2, MSB = QR bit
|
||||
// 0 = query (initiator), 1 = response (responder)
|
||||
if protocol == 17 && (src_port == 53 || dst_port == 53) && payload.len() >= 3 {
|
||||
return Some((payload[2] >> 7) == 0);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use tract_onnx::prelude::*;
|
||||
use macros::log;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use macros::log;
|
||||
use tract_onnx::prelude::*;
|
||||
|
||||
use super::flow_tracker::FlowData;
|
||||
use super::config_loader::InferenceConfig;
|
||||
use super::feature_extractor::FlowFeatures;
|
||||
use super::flow_tracker::FlowData;
|
||||
use super::model_loader::MLModels;
|
||||
|
||||
use crate::model::ml_detection::DetectionResult;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::ml_detection::DetectionResult;
|
||||
|
||||
pub struct Inference {
|
||||
pub models: Arc<MLModels>,
|
||||
@ -21,31 +21,16 @@ impl Inference {
|
||||
}
|
||||
|
||||
pub fn infer_batch(&self, flows: &[FlowData]) -> Vec<DetectionResult> {
|
||||
flows
|
||||
.iter()
|
||||
.filter_map(|flow| self.infer_single(flow))
|
||||
.collect()
|
||||
flows.iter().filter_map(|flow| self.infer_single(flow)).collect()
|
||||
}
|
||||
|
||||
pub fn infer_single(&self, flow: &FlowData) -> Option<DetectionResult> {
|
||||
// extract
|
||||
let mut features = FlowFeatures::extract(
|
||||
flow,
|
||||
&self.config.feature_names
|
||||
);
|
||||
let ae_features = self.preprocess_ae_features(flow);
|
||||
|
||||
// pre-process
|
||||
features.winsorize(&self.config.clip_params, &self.config.feature_names);
|
||||
features.normalize(&self.config.scaler_mean, &self.config.scaler_std);
|
||||
features.clip(self.config.post_clip_min, self.config.post_clip_max);
|
||||
|
||||
// input tensor
|
||||
let input = tract_ndarray::Array2::from_shape_fn((1, self.config.num_features()), |(_, j)| {
|
||||
features.features[j] as f32
|
||||
});
|
||||
|
||||
// Deep Autoencoder
|
||||
let ae_score = match self.run_autoencoder(&input) {
|
||||
// 2. Deep Autoencoder
|
||||
let ae_input = Self::vec_to_array2(&ae_features);
|
||||
let ae_score = match self.run_autoencoder(&ae_input) {
|
||||
Ok(score) => score,
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("DeepAutoEncoder".to_string(), e.to_string()));
|
||||
@ -53,129 +38,83 @@ impl Inference {
|
||||
}
|
||||
};
|
||||
|
||||
// Random Forest
|
||||
let rf_score = match self.run_random_forest(&input) {
|
||||
Ok(score) => score,
|
||||
let cls_input = self.build_classifier_input(&ae_features, ae_score);
|
||||
|
||||
let (attack_type, confidence) = match self.run_classifier(&cls_input) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("RandomForest".to_string(), e.to_string()));
|
||||
log!(MLLog::InferenceFailed("LightGBM".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Ensemble score
|
||||
let ensemble_score = self.compute_ensemble_score(ae_score, rf_score);
|
||||
|
||||
let is_anomaly = ensemble_score > self.config.threshold as f32;
|
||||
let is_attack = ae_score >= self.config.ae_threshold;
|
||||
|
||||
let flow_key = format!(
|
||||
"{}:{} -> {}:{} (proto {})",
|
||||
"{}:{} -> {}:{} (proto {}) [{}]",
|
||||
flow.flow_key.src_ip,
|
||||
flow.flow_key.src_port,
|
||||
flow.flow_key.dst_ip,
|
||||
flow.flow_key.dst_port,
|
||||
flow.flow_key.protocol
|
||||
flow.flow_key.protocol,
|
||||
flow.direction
|
||||
);
|
||||
|
||||
let flow_key_raw = flow.flow_key.clone();
|
||||
Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw: flow.flow_key.clone(),
|
||||
direction: flow.direction,
|
||||
is_attack,
|
||||
attack_type: if is_attack { Some(attack_type) } else { None },
|
||||
confidence,
|
||||
ae_score,
|
||||
threshold: self.config.ae_threshold,
|
||||
})
|
||||
}
|
||||
|
||||
if is_anomaly {
|
||||
// MLP
|
||||
let (attack_type, confidence) = match self.run_mlp(&input) {
|
||||
Ok((attack_type, conf)) => (attack_type, conf),
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("MLP".to_string(), e.to_string()));
|
||||
("UNKNOWN".to_string(), ensemble_score)
|
||||
}
|
||||
};
|
||||
fn preprocess_ae_features(&self, flow: &FlowData) -> Vec<f32> {
|
||||
let mut features = FlowFeatures::extract(flow, &self.config.ae_feature_names);
|
||||
features.winsorize(&self.config.ae_clip_params, &self.config.ae_feature_names);
|
||||
features.normalize(&self.config.ae_scaler_mean, &self.config.ae_scaler_std);
|
||||
features.clip(self.config.ae_post_clip_min, self.config.ae_post_clip_max);
|
||||
features.features.iter().map(|&x| x as f32).collect()
|
||||
}
|
||||
|
||||
if confidence < 0.75 {
|
||||
return Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw,
|
||||
is_attack: false,
|
||||
attack_type: None,
|
||||
confidence: 1.0 - ensemble_score,
|
||||
ae_score,
|
||||
rf_score,
|
||||
ensemble_score,
|
||||
});
|
||||
fn vec_to_array2(v: &[f32]) -> tract_ndarray::Array2<f32> {
|
||||
tract_ndarray::Array2::from_shape_fn((1, v.len()), |(_, j)| v[j])
|
||||
}
|
||||
|
||||
/// Classifier 輸入 = 已預處理的 ae_features ++ [ae_anomaly_score]
|
||||
fn build_classifier_input(&self, ae_features: &[f32], ae_score: f32) -> tract_ndarray::Array2<f32> {
|
||||
let n = ae_features.len() + 1;
|
||||
tract_ndarray::Array2::from_shape_fn((1, n), |(_, j)| {
|
||||
if j < ae_features.len() {
|
||||
ae_features[j]
|
||||
} else {
|
||||
ae_score
|
||||
}
|
||||
|
||||
Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw,
|
||||
is_attack: true,
|
||||
attack_type: Some(attack_type),
|
||||
confidence,
|
||||
ae_score,
|
||||
rf_score,
|
||||
ensemble_score,
|
||||
})
|
||||
} else {
|
||||
Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw,
|
||||
is_attack: false,
|
||||
attack_type: None,
|
||||
confidence: 1.0 - ensemble_score,
|
||||
ae_score,
|
||||
rf_score,
|
||||
ensemble_score,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn run_autoencoder(&self, input: &tract_ndarray::Array2<f32>) -> TractResult<f32> {
|
||||
let input_tensor = input.clone().into_tensor();
|
||||
|
||||
let result = self
|
||||
.models
|
||||
.deep_autoencoder
|
||||
.run(tvec![input_tensor.into()])?;
|
||||
.run(tvec![input.clone().into_tensor().into()])?;
|
||||
|
||||
let output = result[0]
|
||||
.to_array_view::<f32>()?
|
||||
.into_dimensionality::<tract_ndarray::Ix2>()?;
|
||||
|
||||
let diff = input - &output;
|
||||
let squared_errors = &diff * &diff;
|
||||
let mse = squared_errors.sum() / self.config.num_features() as f32;
|
||||
let mse = (&diff * &diff).sum() / self.config.ae_feature_names.len() as f32;
|
||||
|
||||
let ae_norm = &self.config.ae_normalization;
|
||||
let ae_score = (mse - ae_norm.min as f32) / (ae_norm.max as f32 - ae_norm.min as f32 + 1e-10);
|
||||
let ae_score = ae_score.clamp(0.0, 1.0);
|
||||
|
||||
Ok(ae_score)
|
||||
Ok(mse)
|
||||
}
|
||||
|
||||
fn run_random_forest(&self, input: &tract_ndarray::Array2<f32>) -> TractResult<f32> {
|
||||
fn run_classifier(&self, input: &tract_ndarray::Array2<f32>) -> TractResult<(String, f32)> {
|
||||
let input_tensor = input.clone().into_tensor();
|
||||
|
||||
let result = self.models.random_forest.run(tvec![input_tensor.into()])?;
|
||||
|
||||
// output[0] = output_label (i64)
|
||||
// output[1] = output_probability (sequence of maps)
|
||||
|
||||
if result.len() > 1 {
|
||||
if let Ok(proba) = result[1].to_array_view::<f32>() {
|
||||
if proba.len() > 1 {
|
||||
return Ok(proba.iter().nth(1).copied().unwrap_or(0.0));
|
||||
} else if proba.len() == 1 {
|
||||
return Ok(proba.iter().next().copied().unwrap_or(0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let label = result[0].to_array_view::<i64>()?;
|
||||
let prediction = label.iter().next().copied().unwrap_or(0);
|
||||
|
||||
Ok(if prediction != 0 { 1.0 } else { 0.0 })
|
||||
}
|
||||
|
||||
fn run_mlp(&self, input: &tract_ndarray::Array2<f32>) -> TractResult<(String, f32)> {
|
||||
let input_tensor = input.clone().into_tensor();
|
||||
|
||||
let result = self.models.mlp.run(tvec![input_tensor.into()])?;
|
||||
let result = self.models.classifier.run(tvec![input_tensor.into()])?;
|
||||
|
||||
let output = result[0].to_array_view::<f32>()?;
|
||||
|
||||
@ -189,7 +128,8 @@ impl Inference {
|
||||
}
|
||||
}
|
||||
|
||||
let attack_type = self.config
|
||||
let attack_type = self
|
||||
.config
|
||||
.attack_labels
|
||||
.get(&predicted_class.to_string())
|
||||
.cloned()
|
||||
@ -197,30 +137,4 @@ impl Inference {
|
||||
|
||||
Ok((attack_type, max_prob))
|
||||
}
|
||||
|
||||
fn compute_ensemble_score(&self, ae_score: f32, rf_score: f32) -> f32 {
|
||||
let strategy = &self.config.strategy_name;
|
||||
|
||||
if strategy.starts_with("W_") {
|
||||
if let Some(weights_str) = strategy.strip_prefix("W_") {
|
||||
let parts: Vec<&str> = weights_str.split(':').collect();
|
||||
if parts.len() == 2 {
|
||||
if let (Ok(w1), Ok(w2)) = (parts[0].parse::<f32>(), parts[1].parse::<f32>()) {
|
||||
let w1 = w1 / 10.0;
|
||||
let w2 = w2 / 10.0;
|
||||
return w1 * ae_score + w2 * rf_score;
|
||||
}
|
||||
}
|
||||
}
|
||||
(ae_score + rf_score) / 2.0
|
||||
} else {
|
||||
match strategy.as_str() {
|
||||
"Average" => (ae_score + rf_score) / 2.0,
|
||||
"Max" => ae_score.max(rf_score),
|
||||
"Min" => ae_score.min(rf_score),
|
||||
"Product" => ae_score * rf_score,
|
||||
_ => (ae_score + rf_score) / 2.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,4 +4,5 @@ pub mod flow_tracker;
|
||||
pub mod feature_extractor;
|
||||
pub mod inference;
|
||||
pub mod engine;
|
||||
pub mod aggregator;
|
||||
pub mod aggregator;
|
||||
pub mod traffic_logger;
|
||||
@ -5,17 +5,17 @@ use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::model::error::ml::MLError;
|
||||
use crate::model::ml_detection::RunnableModel;
|
||||
|
||||
use super::config_loader::InferenceConfig;
|
||||
|
||||
pub struct MLModels {
|
||||
pub deep_autoencoder: RunnableModel,
|
||||
pub random_forest: RunnableModel,
|
||||
pub mlp: RunnableModel,
|
||||
pub classifier: RunnableModel,
|
||||
}
|
||||
impl MLModels {
|
||||
pub fn load_models(app_config: &Arc<AppConfig>, features: usize) -> Result<Self, MLError> {
|
||||
pub fn load_models(app_config: &Arc<AppConfig>, inference_config: &Arc<InferenceConfig>) -> Result<Self, MLError> {
|
||||
Ok(Self {
|
||||
deep_autoencoder: Self::loader(&app_config.deep_autoencoder_name, features)?,
|
||||
random_forest: Self::loader(&app_config.random_forest_name, features)?,
|
||||
mlp: Self::loader(&app_config.mlp_name, features)?,
|
||||
deep_autoencoder: Self::loader(&app_config.deep_autoencoder_name, inference_config.num_ae_features())?,
|
||||
classifier: Self::loader(&app_config.classifier_name, inference_config.num_classifier_features())?
|
||||
})
|
||||
}
|
||||
|
||||
@ -49,8 +49,7 @@ impl MLModels {
|
||||
pub fn get_model_info(&self, name: &str) -> String {
|
||||
let model = match name {
|
||||
"deep_autoencoder" => &self.deep_autoencoder,
|
||||
"random_forest" => &self.random_forest,
|
||||
"mlp" => &self.mlp,
|
||||
"classifier" => &self.classifier,
|
||||
_ => return "unknown model".to_string(),
|
||||
};
|
||||
|
||||
|
||||
46
net-guardia/src/ml/traffic_logger.rs
Normal file
46
net-guardia/src/ml/traffic_logger.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::thread;
|
||||
|
||||
use crossbeam::channel::{bounded, Sender, TrySendError};
|
||||
|
||||
pub struct TrafficLogger {
|
||||
sender: Sender<Vec<String>>,
|
||||
}
|
||||
|
||||
impl TrafficLogger {
|
||||
pub fn new(csv_path: &str, header: Vec<String>) -> Result<Self, std::io::Error> {
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(csv_path)?;
|
||||
|
||||
let mut writer = BufWriter::new(file);
|
||||
writeln!(writer, "{}", header.join(","))?;
|
||||
writer.flush()?;
|
||||
|
||||
let (sender, receiver) = bounded::<Vec<String>>(65536);
|
||||
|
||||
thread::Builder::new()
|
||||
.name("traffic-logger".to_string())
|
||||
.spawn(move || {
|
||||
for record in receiver {
|
||||
if let Err(e) = writeln!(writer, "{}", record.join(",")) {
|
||||
eprintln!("[traffic-logger] write error: {}", e);
|
||||
}
|
||||
}
|
||||
let _ = writer.flush();
|
||||
})?;
|
||||
|
||||
Ok(Self { sender })
|
||||
}
|
||||
|
||||
pub fn log_row(&self, record: Vec<String>) {
|
||||
match self.sender.try_send(record) {
|
||||
Ok(_) => {}
|
||||
Err(TrySendError::Full(_)) => {}
|
||||
Err(TrySendError::Disconnected(_)) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,7 @@ pub struct Config {
|
||||
pub egress_ifname: String,
|
||||
pub geoip_db_name: String,
|
||||
pub deep_autoencoder_name: String,
|
||||
pub random_forest_name: String,
|
||||
pub mlp_name: String,
|
||||
pub classifier_name: String,
|
||||
pub models_config_name: String,
|
||||
pub combined_queue_count: u32,
|
||||
pub channel_size: usize,
|
||||
@ -28,4 +27,8 @@ pub struct Config {
|
||||
pub max_concurrent_flows: usize,
|
||||
pub min_packets_for_inference: usize,
|
||||
pub inference_interval_secs: u64,
|
||||
}
|
||||
pub aggregator_window_secs: u64,
|
||||
pub inference_batch_size: usize,
|
||||
pub traffic_logging_mode: bool,
|
||||
pub traffic_log_csv_path: String,
|
||||
}
|
||||
@ -7,6 +7,24 @@ pub enum Direction {
|
||||
Egress,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn flip(self) -> Self {
|
||||
match self {
|
||||
Direction::Ingress => Direction::Egress,
|
||||
Direction::Egress => Direction::Ingress,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Direction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Direction::Ingress => write!(f, "Ingress"),
|
||||
Direction::Egress => write!(f, "Egress"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FlowDirection {
|
||||
|
||||
@ -21,5 +21,9 @@ traceable! {
|
||||
|
||||
#[error("Invalid GeoIP configuration")]
|
||||
InvalidGeoIPConfiguration => tracing::Level::ERROR,
|
||||
|
||||
#[no_source]
|
||||
#[error("Failed to create traffic log file '{path}': {reason}")]
|
||||
TrafficLogCreateError { path: String, reason: String } => tracing::Level::ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
81
net-guardia/src/model/health.rs
Normal file
81
net-guardia/src/model/health.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemHealthMetrics {
|
||||
pub timestamp: u64,
|
||||
pub boot_time: u64,
|
||||
pub uptime_seconds: u64,
|
||||
pub system_info: SystemInfo,
|
||||
pub cpu_details: CpuDetails,
|
||||
pub memory_usage: MemoryUsage,
|
||||
pub network_stats: ConfiguredNetworkStats,
|
||||
pub load_average: Option<LoadAverage>,
|
||||
pub temperature: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemInfo {
|
||||
pub kernel_version: Option<String>,
|
||||
pub os_name: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub architecture: String,
|
||||
pub total_processes: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CpuDetails {
|
||||
pub cpu_brand: String,
|
||||
pub core_count: usize,
|
||||
pub cpu_usage: f32,
|
||||
pub cpu_frequency: u64,
|
||||
pub cores: Vec<CpuCoreInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CpuCoreInfo {
|
||||
pub core_id: usize,
|
||||
pub usage_percent: f32,
|
||||
pub frequency: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MemoryUsage {
|
||||
pub total: u64,
|
||||
pub used: u64,
|
||||
pub available: u64,
|
||||
pub usage_percent: f32,
|
||||
pub swap_total: u64,
|
||||
pub swap_used: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ConfiguredNetworkStats {
|
||||
pub ingress: Option<NetworkStats>,
|
||||
pub egress: Option<NetworkStats>,
|
||||
// pub management: Option<NetworkStats>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct NetworkStats {
|
||||
pub interface: String,
|
||||
pub bytes_received: u64,
|
||||
pub bytes_transmitted: u64,
|
||||
pub packets_received: u64,
|
||||
pub packets_transmitted: u64,
|
||||
pub errors_received: u64,
|
||||
pub errors_transmitted: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LoadAverage {
|
||||
pub one_minute: f64,
|
||||
pub five_minute: f64,
|
||||
pub fifteen_minute: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemHealthStatus {
|
||||
pub overall_healthy: bool,
|
||||
pub issues: Vec<String>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
11
net-guardia/src/model/log/health.rs
Normal file
11
net-guardia/src/model/log/health.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use macros::loggable;
|
||||
|
||||
loggable! {
|
||||
Health {
|
||||
#[error("{ifname} interface '{interface}' not found")]
|
||||
InterfaceNotFound { ifname: String, interface: String } => tracing::Level::WARN,
|
||||
|
||||
#[error("Failed to broadcast system health metrics: {error}")]
|
||||
BroadcastFailed { error: String } => tracing::Level::ERROR,
|
||||
}
|
||||
}
|
||||
@ -18,9 +18,6 @@ loggable! {
|
||||
#[error("Machine Learning detection ready")]
|
||||
Ready => tracing::Level::INFO,
|
||||
|
||||
#[error("Machine Learning detection shutdown")]
|
||||
Shutdown => tracing::Level::INFO,
|
||||
|
||||
#[error("Machine Learning channel disconnected")]
|
||||
ChannelDisconnected => tracing::Level::WARN,
|
||||
|
||||
@ -48,17 +45,14 @@ loggable! {
|
||||
#[error("Inference configuration loaded: {features} features, {attacks} attack types")]
|
||||
ConfigLoaded { features: usize, attacks: usize } => tracing::Level::INFO,
|
||||
|
||||
#[error("ML Engine started: max_flows={max_flows}, min_packets={min_packets}, interval={interval_secs}s")]
|
||||
EngineStarted { max_flows: usize, min_packets: usize, interval_secs: u64 } => tracing::Level::INFO,
|
||||
|
||||
#[error("Inference completed: {total_flows} flows ({anomaly} anomaly, {benign} benign) in {duration_ms}ms ({throughput:.1} flows/s)")]
|
||||
InferenceCompleted { total_flows: usize, anomaly: usize, benign: usize, duration_ms: u32, throughput: f32 } => tracing::Level::INFO,
|
||||
|
||||
#[error("Inference skipped: {reason}")]
|
||||
InferenceSkipped { reason: String } => tracing::Level::INFO,
|
||||
|
||||
#[error("Threat detected: {flow} -> {attack_type} (confidence: {confidence:.2}, ae_score: {ae_score:.4}, rf_score: {rf_score:.4}, ensemble: {ensemble_score:.4})")]
|
||||
ThreatDetected { flow: String, attack_type: String, confidence: f32, ae_score: f32, rf_score: f32, ensemble_score: f32 } => tracing::Level::WARN,
|
||||
#[error("Threat detected [{direction}]: {flow} -> {attack_type} (confidence: {confidence:.2}, ae_score: {ae_score:.4})")]
|
||||
ThreatDetected { direction: String, flow: String, attack_type: String, confidence: f32, ae_score: f32 } => tracing::Level::WARN,
|
||||
|
||||
#[error("Flow stats: total={total_flows}, qualified={flows_len}, min_packets={min_packets}, packet_counts: {counts}")]
|
||||
FlowStats { total_flows: usize, flows_len: usize, min_packets: usize, counts: String } => tracing::Level::INFO,
|
||||
|
||||
@ -3,3 +3,4 @@ pub mod http;
|
||||
pub mod ml;
|
||||
pub mod system;
|
||||
pub mod misc;
|
||||
pub mod health;
|
||||
|
||||
@ -23,5 +23,8 @@ loggable! {
|
||||
|
||||
#[error("Configuration not found")]
|
||||
ConfigNotFound => tracing::Level::INFO,
|
||||
|
||||
#[error("Traffic logging mode enabled — writing packets to: {path}")]
|
||||
TrafficLoggingEnabled { path: String } => tracing::Level::INFO,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
use common::model::event::{Event, TcpFlags};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tract_onnx::prelude::{Graph, SimplePlan, TypedFact, TypedOp};
|
||||
use common::model::event::{Event, TcpFlags};
|
||||
|
||||
use crate::model::direction::Direction;
|
||||
use crate::utils::packet_parser::{format_ipv4, format_ipv6};
|
||||
|
||||
pub type RunnableModel = SimplePlan<TypedFact, Box<dyn TypedOp>, Graph<TypedFact, Box<dyn TypedOp>>>;
|
||||
@ -15,6 +17,7 @@ pub struct ClipParams {
|
||||
pub struct AENormalization {
|
||||
pub min: f64,
|
||||
pub max: f64,
|
||||
pub norm_max: f64,
|
||||
pub mean: f64,
|
||||
pub std: f64,
|
||||
pub median: f64,
|
||||
@ -23,7 +26,13 @@ pub struct AENormalization {
|
||||
pub p99: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PrecisionLevels {
|
||||
pub threshold: f64,
|
||||
pub precision: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlowKey {
|
||||
pub src_ip: String,
|
||||
pub dst_ip: String,
|
||||
@ -77,8 +86,11 @@ pub struct BulkState {
|
||||
pub bulk_count: u32,
|
||||
pub total_bytes: u64,
|
||||
pub total_packets: u64,
|
||||
pub total_duration_us: u64,
|
||||
pub last_bulk_bytes: u64,
|
||||
pub last_bulk_packets: u64,
|
||||
pub last_bulk_start_us: u64,
|
||||
pub last_bulk_packet_us: u64,
|
||||
pub in_bulk: bool,
|
||||
}
|
||||
|
||||
@ -86,12 +98,12 @@ pub struct BulkState {
|
||||
pub struct DetectionResult {
|
||||
pub flow_key: String,
|
||||
pub flow_key_raw: FlowKey,
|
||||
pub direction: Direction,
|
||||
pub is_attack: bool,
|
||||
pub attack_type: Option<String>,
|
||||
pub confidence: f32,
|
||||
pub ae_score: f32,
|
||||
pub rf_score: f32,
|
||||
pub ensemble_score: f32,
|
||||
pub threshold: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@ -128,4 +140,4 @@ impl InferenceStats {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EngineStats {
|
||||
pub active_flows: usize,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,3 +7,4 @@ pub mod list_type;
|
||||
pub mod log;
|
||||
pub mod time_type;
|
||||
pub mod ml_detection;
|
||||
pub mod health;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use common::define::setting::MAX_RULES_PORT;
|
||||
use common::model::ip_address::Port;
|
||||
|
||||
@ -8,3 +10,20 @@ pub fn convert_ports_to_vec(ports: [u16; MAX_RULES_PORT]) -> Vec<Port> {
|
||||
}
|
||||
filtered_ports
|
||||
}
|
||||
|
||||
pub fn is_private_ip(ip: &IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => {
|
||||
v4.is_private()
|
||||
|| v4.is_loopback()
|
||||
|| v4.is_link_local()
|
||||
|| v4.is_broadcast()
|
||||
}
|
||||
IpAddr::V6(v6) => {
|
||||
v6.is_loopback()
|
||||
|| v6.is_unique_local() // fc00::/7
|
||||
|| v6.is_unspecified()
|
||||
|| v6.is_multicast()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
use std::time;
|
||||
use std::mem;
|
||||
use std::time;
|
||||
|
||||
use common::model::event::{Event, IPv4Event, IPv6Event, TcpFlags};
|
||||
use network_types::ip::IpProto;
|
||||
|
||||
pub fn parse_packet(packet_data: &[u8]) -> Option<Event> {
|
||||
pub fn parse_packet(packet_data: &[u8]) -> Option<(Event, usize)> {
|
||||
if packet_data.len() < 14 {
|
||||
return None;
|
||||
}
|
||||
@ -23,7 +23,7 @@ pub fn parse_packet(packet_data: &[u8]) -> Option<Event> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ipv4(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
fn parse_ipv4(packet_data: &[u8], timestamp_us: u64) -> Option<(Event, usize)> {
|
||||
if packet_data.len() < 34 {
|
||||
return None;
|
||||
}
|
||||
@ -36,6 +36,10 @@ fn parse_ipv4(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
let src_ip = u32::from_be_bytes([ip_header[12], ip_header[13], ip_header[14], ip_header[15]]);
|
||||
let dst_ip = u32::from_be_bytes([ip_header[16], ip_header[17], ip_header[18], ip_header[19]]);
|
||||
|
||||
if protocol_byte != 6 && protocol_byte != 17 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ihl = (ip_header[0] & 0x0F) as usize * 4;
|
||||
let total_len = u16::from_be_bytes([ip_header[2], ip_header[3]]) as u32;
|
||||
|
||||
@ -64,6 +68,7 @@ fn parse_ipv4(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
};
|
||||
|
||||
let payload_length = total_len.saturating_sub(ihl as u32 + header_length as u32);
|
||||
let payload_start = 14 + ihl + header_length as usize;
|
||||
|
||||
let event = IPv4Event {
|
||||
protocol,
|
||||
@ -80,10 +85,10 @@ fn parse_ipv4(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
is_forward: false,
|
||||
};
|
||||
|
||||
Some(Event::IPv4(event))
|
||||
Some((Event::IPv4(event), payload_start))
|
||||
}
|
||||
|
||||
fn parse_ipv6(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
fn parse_ipv6(packet_data: &[u8], timestamp_us: u64) -> Option<(Event, usize)> {
|
||||
if packet_data.len() < 54 {
|
||||
return None;
|
||||
}
|
||||
@ -101,6 +106,10 @@ fn parse_ipv6(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
dest_ip_bytes.copy_from_slice(&ip_header[24..40]);
|
||||
let dst_ip = u128::from_be_bytes(dest_ip_bytes);
|
||||
|
||||
if protocol_byte != 6 && protocol_byte != 17 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let payload_len = u16::from_be_bytes([ip_header[4], ip_header[5]]) as u32;
|
||||
let total_len = payload_len + 40;
|
||||
|
||||
@ -129,6 +138,7 @@ fn parse_ipv6(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
};
|
||||
|
||||
let payload_length = total_len.saturating_sub(40 + header_length as u32);
|
||||
let payload_start = 14 + 40 + header_length as usize;
|
||||
|
||||
let event = IPv6Event {
|
||||
protocol,
|
||||
@ -145,24 +155,33 @@ fn parse_ipv6(packet_data: &[u8], timestamp_us: u64) -> Option<Event> {
|
||||
is_forward: false,
|
||||
};
|
||||
|
||||
Some(Event::IPv6(event))
|
||||
Some((Event::IPv6(event), payload_start))
|
||||
}
|
||||
|
||||
pub fn format_ipv4(addr: u32) -> String {
|
||||
let bytes = addr.to_be_bytes();
|
||||
format!(
|
||||
"{}.{}.{}.{}",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3],
|
||||
)
|
||||
format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3],)
|
||||
}
|
||||
|
||||
pub fn format_ipv6(addr: u128) -> String {
|
||||
let bytes = addr.to_be_bytes();
|
||||
format!(
|
||||
"{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3],
|
||||
bytes[4], bytes[5], bytes[6], bytes[7],
|
||||
bytes[8], bytes[9], bytes[10], bytes[11],
|
||||
bytes[12], bytes[13], bytes[14], bytes[15]
|
||||
bytes[0],
|
||||
bytes[1],
|
||||
bytes[2],
|
||||
bytes[3],
|
||||
bytes[4],
|
||||
bytes[5],
|
||||
bytes[6],
|
||||
bytes[7],
|
||||
bytes[8],
|
||||
bytes[9],
|
||||
bytes[10],
|
||||
bytes[11],
|
||||
bytes[12],
|
||||
bytes[13],
|
||||
bytes[14],
|
||||
bytes[15]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
pub mod access_control;
|
||||
pub mod service;
|
||||
pub mod statistics;
|
||||
pub mod health;
|
||||
|
||||
use actix_web::{web, Scope};
|
||||
|
||||
@ -10,5 +9,4 @@ pub fn initialize() -> Scope {
|
||||
.service(access_control::initialize())
|
||||
.service(service::initialize())
|
||||
.service(statistics::initialize())
|
||||
.service(health::initialize())
|
||||
}
|
||||
|
||||
22
net-guardia/src/web/api/ml_alert.rs
Normal file
22
net-guardia/src/web/api/ml_alert.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::core::infrastructure::ml_alert::MLAlert;
|
||||
use crate::web::websocket::alert_websocket;
|
||||
use actix_web::{get, web, HttpRequest, HttpResponse, Responder, Scope};
|
||||
|
||||
pub fn initialize() -> Scope {
|
||||
web::scope("/ml")
|
||||
.service(websocket_alert)
|
||||
}
|
||||
|
||||
#[get("/websocket/alert")]
|
||||
async fn websocket_alert(
|
||||
req: HttpRequest,
|
||||
stream: web::Payload,
|
||||
ai: web::Data<MLAlert>,
|
||||
) -> impl Responder {
|
||||
match alert_websocket::websocket_alert(req, stream, ai).await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
HttpResponse::InternalServerError().body(format!("WebSocket error: {}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
pub mod control;
|
||||
pub mod default;
|
||||
pub mod misc;
|
||||
pub mod ml_alert;
|
||||
pub mod health;
|
||||
|
||||
90
net-guardia/src/web/websocket/alert_websocket.rs
Normal file
90
net-guardia/src/web/websocket/alert_websocket.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use actix_web::{web, HttpRequest, HttpResponse, Result};
|
||||
use actix_ws::{handle, Message, MessageStream, Session};
|
||||
use futures_util::StreamExt;
|
||||
use macros::log;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
use crate::core::infrastructure::ml_alert::{MLAlert, AlertMessage};
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::log::http::HttpLog;
|
||||
|
||||
pub async fn websocket_alert(
|
||||
req: HttpRequest,
|
||||
body: web::Payload,
|
||||
ai: web::Data<MLAlert>,
|
||||
) -> Result<HttpResponse> {
|
||||
let (response, session, msg_stream) = handle(&req, body)?;
|
||||
|
||||
let broadcast_rx = ai.subscribe_to_alerts();
|
||||
|
||||
actix_web::rt::spawn(async move {
|
||||
handle_alert_connection(session, msg_stream, broadcast_rx).await;
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn handle_alert_connection(
|
||||
mut session: Session,
|
||||
mut msg_stream: MessageStream,
|
||||
mut broadcast_rx: broadcast::Receiver<AlertMessage>,
|
||||
) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
msg_result = msg_stream.next() => {
|
||||
if !handle_client_message(&mut session, msg_result).await {
|
||||
break;
|
||||
}
|
||||
},
|
||||
broadcast_result = broadcast_rx.recv() => {
|
||||
match broadcast_result {
|
||||
Ok(alert) => {
|
||||
if !send_alert(&mut session, &alert).await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(broadcast::error::RecvError::Lagged(skipped)) => {
|
||||
log!(HttpLog::WebSocketLaged(skipped));
|
||||
continue;
|
||||
}
|
||||
Err(broadcast::error::RecvError::Closed) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let _ = session.close(None).await;
|
||||
}
|
||||
|
||||
async fn handle_client_message(
|
||||
session: &mut Session,
|
||||
msg_result: Option<Result<Message, actix_ws::ProtocolError>>,
|
||||
) -> bool {
|
||||
match msg_result {
|
||||
Some(Ok(Message::Text(_))) => true,
|
||||
Some(Ok(Message::Ping(bytes))) => session.pong(&bytes).await.is_ok(),
|
||||
Some(Ok(Message::Close(reason))) => {
|
||||
let _ = (session.clone()).close(reason).await;
|
||||
false
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
log!(HttpError::WebSocketError(err));
|
||||
false
|
||||
}
|
||||
None => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_alert(session: &mut Session, alert: &AlertMessage) -> bool {
|
||||
match serde_json::to_string(alert) {
|
||||
Ok(json) => session.text(json).await.is_ok(),
|
||||
Err(err) => {
|
||||
log!(MiscError::SerializeError(err));
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,10 +4,11 @@ use futures_util::StreamExt;
|
||||
use macros::log;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
use crate::core::infrastructure::health::{SystemHealth, SystemHealthMetrics};
|
||||
use crate::core::infrastructure::health::SystemHealth;
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::log::http::HttpLog;
|
||||
use crate::model::health::SystemHealthMetrics;
|
||||
|
||||
pub async fn websocket_system_health(
|
||||
req: HttpRequest,
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
pub mod flow_websocket;
|
||||
pub mod health_websocket;
|
||||
pub mod alert_websocket;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user