Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Implement defender metrics #70

Merged
merged 22 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7fc002b
Initial structure of defender metrics
KennethKnudsen97 Dec 23, 2024
ae63c48
metric structure and tests
KennethKnudsen97 Dec 27, 2024
f28e3d1
generic custom metric
KennethKnudsen97 Jan 2, 2025
d76a514
Change Custom metric to use references
KennethKnudsen97 Jan 6, 2025
f631a8c
aws types with references
KennethKnudsen97 Jan 6, 2025
a58a660
impl tuple for Version
KennethKnudsen97 Jan 6, 2025
09ef37a
remove timestamp as argument for function in custom metric
KennethKnudsen97 Jan 6, 2025
817caf7
include aws metrics and use bon crate for building metric struct
KennethKnudsen97 Jan 6, 2025
825e5a0
error handling
KennethKnudsen97 Jan 14, 2025
2707cd7
error handling
KennethKnudsen97 Jan 16, 2025
8418ee6
error handling
KennethKnudsen97 Jan 21, 2025
3f9fe06
Merge branch 'feature/async' into feature/defender_metrics
KennethKnudsen97 Jan 21, 2025
042db28
feature flag for cbor and temp fix for Header serialize
KennethKnudsen97 Feb 3, 2025
0089ca9
smal changes
KennethKnudsen97 Feb 4, 2025
985ca38
String list example
KennethKnudsen97 Feb 5, 2025
ca5f8ce
Metric integration test
KennethKnudsen97 Feb 5, 2025
092ea92
Update src/defender_metrics/data_types.rs
KennethKnudsen97 Feb 7, 2025
bcdab34
Cargo clippy and unit test
KennethKnudsen97 Feb 7, 2025
fcce0f3
Merge branch 'feature/defender_metrics' of github.com:BlackbirdHQ/rus…
KennethKnudsen97 Feb 7, 2025
b1e6c35
cargo clippy fix
KennethKnudsen97 Feb 7, 2025
fa66a9c
fixed unit test and version serialization
KennethKnudsen97 Feb 7, 2025
caec287
fix test
KennethKnudsen97 Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.allTargets": true,
"rust-analyzer.cargo.features": ["log"],
"rust-analyzer.cargo.target": "x86_64-unknown-linux-gnu"
"rust-analyzer.cargo.target": "x86_64-unknown-linux-gnu",

}
13 changes: 9 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ embassy-futures = "0.1"

log = { version = "0.4", default-features = false, optional = true }
defmt = { version = "0.3", optional = true }
bon = { version = "3.3.2", default-features = false }

[dev-dependencies]
native-tls = { version = "0.2" }
Expand All @@ -62,16 +63,19 @@ tokio = { version = "1.33", default-features = false, features = [
] }
tokio-native-tls = { version = "0.3.1" }
embassy-futures = { version = "0.1.0" }
embassy-time = { version = "0.4", features = ["log", "std", "generic-queue"] }
embassy-time = { version = "0.4", features = ["log", "std", "generic-queue-8"] }
embedded-io-adapters = { version = "0.6.0", features = ["tokio-1"] }

ecdsa = { version = "0.16", features = ["pkcs8", "pem"] }
p256 = "0.13"
pkcs8 = { version = "0.10", features = ["encryption", "pem"] }
hex = { version = "0.4.3", features = ["alloc"] }


[features]
default = ["ota_mqtt_data", "provision_cbor"]
default = ["ota_mqtt_data", "metric_cbor", "provision_cbor"]

metric_cbor = ["dep:minicbor", "dep:minicbor-serde"]

provision_cbor = ["dep:minicbor", "dep:minicbor-serde"]

Expand All @@ -89,5 +93,6 @@ defmt = [
]
log = ["dep:log", "embedded-mqtt/log"]

# [patch."ssh://[email protected]/FactbirdHQ/embedded-mqtt"]
# embedded-mqtt = { path = "../embedded-mqtt" }

[patch."ssh://git@github.com/FactbirdHQ/embedded-mqtt"]
embedded-mqtt = { path = "../embedded-mqtt" }
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[toolchain]
channel = "nightly-2024-07-17"
channel = "nightly-2024-09-06"
components = ["rust-src", "rustfmt", "llvm-tools"]
targets = [
"x86_64-unknown-linux-gnu",
Expand Down
24 changes: 11 additions & 13 deletions shadow_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ fn create_assigners(fields: &Vec<Field>) -> Vec<proc_macro2::TokenStream> {
if field
.attrs
.iter()
.find(|a| a.path.is_ident("static_shadow_field"))
.is_some()
.any(|a| a.path.is_ident("static_shadow_field"))
{
None
} else {
Expand All @@ -133,7 +132,7 @@ fn create_assigners(fields: &Vec<Field>) -> Vec<proc_macro2::TokenStream> {
.collect::<Vec<_>>()
}

fn create_optional_fields(fields: &Vec<Field>) -> Vec<proc_macro2::TokenStream> {
fn create_optional_fields(fields: &[Field]) -> Vec<proc_macro2::TokenStream> {
fields
.iter()
.filter_map(|field| {
Expand All @@ -153,8 +152,7 @@ fn create_optional_fields(fields: &Vec<Field>) -> Vec<proc_macro2::TokenStream>
if field
.attrs
.iter()
.find(|a| a.path.is_ident("static_shadow_field"))
.is_some()
.any(|a| a.path.is_ident("static_shadow_field"))
{
None
} else {
Expand Down Expand Up @@ -183,13 +181,13 @@ fn generate_shadow_state(input: &StructParseInput) -> proc_macro2::TokenStream {
None => quote! { None },
};

return quote! {
quote! {
#[automatically_derived]
impl #impl_generics rustot::shadows::ShadowState for #ident #ty_generics #where_clause {
const NAME: Option<&'static str> = #name;
// const MAX_PAYLOAD_SIZE: usize = 512;
}
};
}
}

fn generate_shadow_patch_struct(input: &StructParseInput) -> proc_macro2::TokenStream {
Expand All @@ -205,10 +203,10 @@ fn generate_shadow_patch_struct(input: &StructParseInput) -> proc_macro2::TokenS

let optional_ident = format_ident!("Patch{}", ident);

let assigners = create_assigners(&shadow_fields);
let optional_fields = create_optional_fields(&shadow_fields);
let assigners = create_assigners(shadow_fields);
let optional_fields = create_optional_fields(shadow_fields);

return quote! {
quote! {
#[automatically_derived]
#[derive(Default, Clone, ::serde::Deserialize, ::serde::Serialize)]
#(#copy_attrs)*
Expand All @@ -228,7 +226,7 @@ fn generate_shadow_patch_struct(input: &StructParseInput) -> proc_macro2::TokenS
)*
}
}
};
}
}

fn generate_shadow_patch_enum(input: &EnumParseInput) -> proc_macro2::TokenStream {
Expand All @@ -238,7 +236,7 @@ fn generate_shadow_patch_enum(input: &EnumParseInput) -> proc_macro2::TokenStrea

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

return quote! {
quote! {
#[automatically_derived]
impl #impl_generics rustot::shadows::ShadowPatch for #ident #ty_generics #where_clause {
type PatchState = #ident #ty_generics;
Expand All @@ -247,5 +245,5 @@ fn generate_shadow_patch_enum(input: &EnumParseInput) -> proc_macro2::TokenStrea
*self = opt;
}
}
};
}
}
81 changes: 81 additions & 0 deletions src/defender_metrics/aws_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct TcpConnections<'a> {
#[serde(rename = "ec")]
pub established_connections: Option<&'a EstablishedConnections<'a>>,
}

#[derive(Debug, Serialize)]
pub struct EstablishedConnections<'a> {
#[serde(rename = "cs")]
pub connections: Option<&'a [&'a Connection<'a>]>,

#[serde(rename = "t")]
pub total: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct Connection<'a> {
#[serde(rename = "rad")]
pub remote_addr: &'a str,

/// Port number, must be >= 0
#[serde(rename = "lp")]
pub local_port: Option<u16>,

/// Interface name
#[serde(rename = "li")]
pub local_interface: Option<&'a str>,
}

#[derive(Debug, Serialize)]
pub struct ListeningTcpPorts<'a> {
#[serde(rename = "pts")]
pub ports: Option<&'a [&'a TcpPort<'a>]>,

#[serde(rename = "t")]
pub total: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct TcpPort<'a> {
#[serde(rename = "pt")]
pub port: u16,

#[serde(rename = "if")]
pub interface: Option<&'a str>,
}

#[derive(Debug, Serialize)]
pub struct ListeningUdpPorts<'a> {
#[serde(rename = "pts")]
pub ports: Option<&'a [&'a UdpPort<'a>]>,

#[serde(rename = "t")]
pub total: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct UdpPort<'a> {
#[serde(rename = "pt")]
pub port: u16,

#[serde(rename = "if")]
pub interface: Option<&'a str>,
}

#[derive(Debug, Serialize)]
pub struct NetworkStats {
#[serde(rename = "bi")]
pub bytes_in: Option<u64>,

#[serde(rename = "bo")]
pub bytes_out: Option<u64>,

#[serde(rename = "pi")]
pub packets_in: Option<u64>,

#[serde(rename = "po")]
pub packets_out: Option<u64>,
}
84 changes: 84 additions & 0 deletions src/defender_metrics/data_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use core::fmt::{Display, Write};

use bon::Builder;
use embassy_time::Instant;
use serde::{ser::SerializeStruct, Deserialize, Serialize};

use super::aws_types::{ListeningTcpPorts, ListeningUdpPorts, NetworkStats, TcpConnections};

#[derive(Debug, Serialize, Builder)]
pub struct Metric<'a, C: Serialize> {
#[serde(rename = "hed")]
pub header: Header,

#[serde(rename = "met")]
pub metrics: Option<Metrics<'a>>,

#[serde(rename = "cmet")]
pub custom_metrics: Option<C>,
}

#[derive(Debug, Serialize)]
pub struct Metrics<'a> {
listening_tcp_ports: Option<ListeningTcpPorts<'a>>,
listening_udp_ports: Option<ListeningUdpPorts<'a>>,
network_stats: Option<NetworkStats>,
tcp_connections: Option<TcpConnections<'a>>,
}

#[derive(Debug, Serialize)]
pub struct Header {
/// Monotonically increasing value. Epoch timestamp recommended.
#[serde(rename = "rid")]
pub report_id: i64,

/// Version in Major.Minor format.
#[serde(rename = "v")]
pub version: Version,
}

impl Default for Header {
fn default() -> Self {
Self {
report_id: Instant::now().as_millis() as i64,
version: Default::default(),
}
}
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum CustomMetric<'a> {
Number(i64),
NumberList(&'a [u64]),
StringList(&'a [&'a str]),
IpList(&'a [&'a str]),
}

/// Format is `Version(Major, Minor)`
#[derive(Debug, PartialEq, Deserialize)]
pub struct Version(pub u8, pub u8);

impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut st: heapless::String<7> = heapless::String::new();
st.write_fmt(format_args!("{}.{}", self.0, self.1)).ok();

serializer.serialize_str(&st)
}
}

impl Display for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}.{}", self.0, self.1,)
}
}

impl Default for Version {
fn default() -> Self {
Self(1, 0)
}
}
29 changes: 29 additions & 0 deletions src/defender_metrics/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ErrorResponse<'a> {
#[serde(rename = "thingName")]
pub thing_name: &'a str,
pub status: &'a str,
#[serde(rename = "statusDetails")]
pub status_details: StatusDetails<'a>,
pub timestamp: i64,
}
#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StatusDetails<'a> {
#[serde(rename = "ErrorCode")]
pub error_code: MetricError,
#[serde(rename = "ErrorMessage")]
pub error_message: Option<&'a str>,
}
#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MetricError {
Malformed,
InvalidPayload,
Throttled,
MissingHeader,
Other,
}
Loading
Loading