1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ProcessState {
8 Running,
10 Sleeping,
12 DiskSleep,
14 Stopped,
16 Traced,
18 Zombie,
20 Dead,
22 Unknown(i64),
24}
25
26impl ProcessState {
27 pub fn from_raw(value: i64) -> Self {
29 match value {
30 0 => Self::Running,
31 1 => Self::Sleeping,
32 2 => Self::DiskSleep,
33 4 => Self::Stopped,
34 8 => Self::Traced,
35 16 => Self::Dead,
36 32 => Self::Zombie,
37 _ => Self::Unknown(value),
38 }
39 }
40}
41
42impl fmt::Display for ProcessState {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::Running => write!(f, "R (running)"),
46 Self::Sleeping => write!(f, "S (sleeping)"),
47 Self::DiskSleep => write!(f, "D (disk sleep)"),
48 Self::Stopped => write!(f, "T (stopped)"),
49 Self::Traced => write!(f, "t (traced)"),
50 Self::Zombie => write!(f, "Z (zombie)"),
51 Self::Dead => write!(f, "X (dead)"),
52 Self::Unknown(v) => write!(f, "? ({v})"),
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct ProcessInfo {
60 pub pid: u64,
62 pub ppid: u64,
64 pub comm: String,
66 pub state: ProcessState,
68 pub vaddr: u64,
70 pub cr3: Option<u64>,
72 pub start_time: u64,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum Protocol {
82 Tcp,
84 Udp,
86 Tcp6,
88 Udp6,
90 Unix,
92 Raw,
94}
95
96impl fmt::Display for Protocol {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::Tcp => write!(f, "TCP"),
100 Self::Udp => write!(f, "UDP"),
101 Self::Tcp6 => write!(f, "TCP6"),
102 Self::Udp6 => write!(f, "UDP6"),
103 Self::Unix => write!(f, "UNIX"),
104 Self::Raw => write!(f, "RAW"),
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum ConnectionState {
112 Established,
114 SynSent,
116 SynRecv,
118 FinWait1,
120 FinWait2,
122 TimeWait,
124 Close,
126 CloseWait,
128 LastAck,
130 Listen,
132 Closing,
134 Unknown(u8),
136}
137
138impl ConnectionState {
139 pub fn from_raw(value: u8) -> Self {
141 match value {
142 1 => Self::Established,
143 2 => Self::SynSent,
144 3 => Self::SynRecv,
145 4 => Self::FinWait1,
146 5 => Self::FinWait2,
147 6 => Self::TimeWait,
148 7 => Self::Close,
149 8 => Self::CloseWait,
150 9 => Self::LastAck,
151 10 => Self::Listen,
152 11 => Self::Closing,
153 _ => Self::Unknown(value),
154 }
155 }
156}
157
158impl fmt::Display for ConnectionState {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 match self {
161 Self::Established => write!(f, "ESTABLISHED"),
162 Self::SynSent => write!(f, "SYN_SENT"),
163 Self::SynRecv => write!(f, "SYN_RECV"),
164 Self::FinWait1 => write!(f, "FIN_WAIT1"),
165 Self::FinWait2 => write!(f, "FIN_WAIT2"),
166 Self::TimeWait => write!(f, "TIME_WAIT"),
167 Self::Close => write!(f, "CLOSE"),
168 Self::CloseWait => write!(f, "CLOSE_WAIT"),
169 Self::LastAck => write!(f, "LAST_ACK"),
170 Self::Listen => write!(f, "LISTEN"),
171 Self::Closing => write!(f, "CLOSING"),
172 Self::Unknown(v) => write!(f, "UNKNOWN({v})"),
173 }
174 }
175}
176
177#[derive(Debug, Clone)]
179pub struct ConnectionInfo {
180 pub protocol: Protocol,
182 pub local_addr: String,
184 pub local_port: u16,
186 pub remote_addr: String,
188 pub remote_port: u16,
190 pub state: ConnectionState,
192 pub pid: Option<u64>,
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198pub enum ModuleState {
199 Live,
201 Coming,
203 Going,
205 Unformed,
207 Unknown(u32),
209}
210
211impl ModuleState {
212 pub fn from_raw(value: u32) -> Self {
214 match value {
215 0 => Self::Live,
216 1 => Self::Coming,
217 2 => Self::Going,
218 3 => Self::Unformed,
219 _ => Self::Unknown(value),
220 }
221 }
222}
223
224impl fmt::Display for ModuleState {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 Self::Live => write!(f, "Live"),
228 Self::Coming => write!(f, "Coming"),
229 Self::Going => write!(f, "Going"),
230 Self::Unformed => write!(f, "Unformed"),
231 Self::Unknown(v) => write!(f, "Unknown({v})"),
232 }
233 }
234}
235
236#[derive(Debug, Clone)]
238pub struct ModuleInfo {
239 pub name: String,
241 pub base_addr: u64,
243 pub size: u64,
245 pub state: ModuleState,
247}
248
249#[derive(Debug, Clone)]
258pub struct PsTreeEntry {
259 pub process: ProcessInfo,
261 pub depth: u32,
263}
264
265#[derive(Debug, Clone)]
275pub struct ThreadInfo {
276 pub tgid: u64,
278 pub tid: u64,
280 pub comm: String,
282 pub state: ProcessState,
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292#[allow(clippy::struct_excessive_bools)]
293pub struct VmaFlags {
294 pub read: bool,
296 pub write: bool,
298 pub exec: bool,
300 pub shared: bool,
302}
303
304impl VmaFlags {
305 pub fn from_raw(flags: u64) -> Self {
307 Self {
308 read: flags & 0x1 != 0,
309 write: flags & 0x2 != 0,
310 exec: flags & 0x4 != 0,
311 shared: flags & 0x8 != 0,
312 }
313 }
314}
315
316impl fmt::Display for VmaFlags {
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 write!(
319 f,
320 "{}{}{}{}",
321 if self.read { 'r' } else { '-' },
322 if self.write { 'w' } else { '-' },
323 if self.exec { 'x' } else { '-' },
324 if self.shared { 's' } else { 'p' },
325 )
326 }
327}
328
329#[derive(Debug, Clone)]
331pub struct VmaInfo {
332 pub pid: u64,
334 pub comm: String,
336 pub start: u64,
338 pub end: u64,
340 pub flags: VmaFlags,
342 pub pgoff: u64,
344 pub file_backed: bool,
346}
347
348#[derive(Debug, Clone)]
354pub struct FileDescriptorInfo {
355 pub pid: u64,
357 pub comm: String,
359 pub fd: u32,
361 pub path: String,
363 pub inode: Option<u64>,
365 pub pos: u64,
367}
368
369#[derive(Debug, Clone, PartialEq, Eq)]
375pub struct EnvVarInfo {
376 pub pid: u64,
378 pub comm: String,
380 pub key: String,
382 pub value: String,
384}
385
386#[derive(Debug, Clone, PartialEq, Eq)]
396pub struct CmdlineInfo {
397 pub pid: u64,
399 pub comm: String,
401 pub cmdline: String,
403}
404
405#[derive(Debug, Clone)]
411pub struct MalfindInfo {
412 pub pid: u64,
414 pub comm: String,
416 pub start: u64,
418 pub end: u64,
420 pub flags: VmaFlags,
422 pub reason: String,
424 pub header_bytes: Vec<u8>,
426}
427
428#[derive(Debug, Clone)]
434pub struct MountInfo {
435 pub dev_name: String,
437 pub mount_point: String,
439 pub fs_type: String,
441}
442
443#[derive(Debug, Clone, PartialEq, Eq)]
453pub struct BashHistoryInfo {
454 pub pid: u64,
456 pub comm: String,
458 pub command: String,
460 pub timestamp: Option<i64>,
462 pub index: u64,
464}
465
466#[derive(Debug, Clone)]
472pub struct PsxViewInfo {
473 pub pid: u64,
475 pub comm: String,
477 pub in_task_list: bool,
479 pub in_pid_hash: bool,
481}
482
483#[derive(Debug, Clone)]
489pub struct TtyCheckInfo {
490 pub name: String,
492 pub operation: String,
494 pub handler: u64,
496 pub hooked: bool,
498}
499
500#[derive(Debug, Clone)]
506pub struct KernelHookInfo {
507 pub symbol: String,
509 pub address: u64,
511 pub hook_type: String,
513 pub target: Option<u64>,
515 pub suspicious: bool,
517}
518
519#[derive(Debug, Clone, Copy, PartialEq, Eq)]
525pub enum ElfType {
526 None,
528 Relocatable,
530 Executable,
532 SharedObject,
534 Core,
536 Unknown(u16),
538}
539
540impl ElfType {
541 pub fn from_raw(value: u16) -> Self {
543 match value {
544 0 => Self::None,
545 1 => Self::Relocatable,
546 2 => Self::Executable,
547 3 => Self::SharedObject,
548 4 => Self::Core,
549 _ => Self::Unknown(value),
550 }
551 }
552}
553
554impl fmt::Display for ElfType {
555 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
556 match self {
557 Self::None => write!(f, "NONE"),
558 Self::Relocatable => write!(f, "REL"),
559 Self::Executable => write!(f, "EXEC"),
560 Self::SharedObject => write!(f, "DYN"),
561 Self::Core => write!(f, "CORE"),
562 Self::Unknown(v) => write!(f, "UNKNOWN({v})"),
563 }
564 }
565}
566
567#[derive(Debug, Clone)]
569pub struct ElfInfo {
570 pub pid: u64,
572 pub comm: String,
574 pub vma_start: u64,
576 pub elf_type: ElfType,
578 pub machine: u16,
580 pub entry_point: u64,
582}
583
584#[derive(Debug, Clone)]
590pub struct HiddenModuleInfo {
591 pub name: String,
593 pub base_addr: u64,
595 pub size: u64,
597 pub in_modules_list: bool,
599 pub in_sysfs: bool,
601}
602
603#[derive(Debug, Clone)]
609pub struct SyscallInfo {
610 pub number: u64,
612 pub handler: u64,
614 pub hooked: bool,
616 pub expected_name: Option<String>,
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq)]
626pub enum BootTimeSource {
627 Timekeeper,
629 UserProvided,
631}
632
633impl std::fmt::Display for BootTimeSource {
634 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
635 match self {
636 Self::Timekeeper => write!(f, "timekeeper"),
637 Self::UserProvided => write!(f, "user-provided"),
638 }
639 }
640}
641
642#[derive(Debug, Clone)]
644pub struct BootTimeEstimate {
645 pub source: BootTimeSource,
647 pub boot_epoch_secs: i64,
649}
650
651#[derive(Debug, Clone)]
657pub struct BootTimeInfo {
658 pub best_estimate: Option<i64>,
660 pub estimates: Vec<BootTimeEstimate>,
662 pub inconsistent: bool,
664 pub max_drift_secs: i64,
666}
667
668const BOOT_TIME_DRIFT_THRESHOLD: i64 = 60;
670
671impl BootTimeInfo {
672 pub fn from_estimates(estimates: Vec<BootTimeEstimate>) -> Self {
678 let best_estimate = estimates.first().map(|e| e.boot_epoch_secs);
679
680 let mut max_drift: i64 = 0;
681 for i in 0..estimates.len() {
682 for j in (i + 1)..estimates.len() {
683 let drift = (estimates[i].boot_epoch_secs - estimates[j].boot_epoch_secs).abs();
684 if drift > max_drift {
685 max_drift = drift;
686 }
687 }
688 }
689
690 Self {
691 best_estimate,
692 estimates,
693 inconsistent: max_drift > BOOT_TIME_DRIFT_THRESHOLD,
694 max_drift_secs: max_drift,
695 }
696 }
697
698 pub fn absolute_secs(&self, boot_ns: u64) -> Option<i64> {
702 self.best_estimate.map(|epoch| {
703 let boot_secs = i64::try_from(boot_ns / 1_000_000_000).unwrap_or(i64::MAX);
704 epoch + boot_secs
705 })
706 }
707}
708
709#[derive(Debug, Clone, Copy, PartialEq, Eq)]
715pub enum NeighState {
716 Incomplete,
718 Reachable,
720 Stale,
722 Delay,
724 Probe,
726 Failed,
728 Permanent,
730 Unknown(u8),
732}
733
734impl NeighState {
735 pub fn from_raw(value: u8) -> Self {
737 match value {
738 0x01 => Self::Incomplete,
739 0x02 => Self::Reachable,
740 0x04 => Self::Stale,
741 0x08 => Self::Delay,
742 0x10 => Self::Probe,
743 0x20 => Self::Failed,
744 0x80 => Self::Permanent,
745 _ => Self::Unknown(value),
746 }
747 }
748}
749
750impl std::fmt::Display for NeighState {
751 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752 match self {
753 Self::Incomplete => write!(f, "INCOMPLETE"),
754 Self::Reachable => write!(f, "REACHABLE"),
755 Self::Stale => write!(f, "STALE"),
756 Self::Delay => write!(f, "DELAY"),
757 Self::Probe => write!(f, "PROBE"),
758 Self::Failed => write!(f, "FAILED"),
759 Self::Permanent => write!(f, "PERMANENT"),
760 Self::Unknown(v) => write!(f, "UNKNOWN(0x{v:02x})"),
761 }
762 }
763}
764
765#[derive(Debug, Clone)]
767pub struct ArpEntryInfo {
768 pub ip_addr: String,
770 pub mac_addr: String,
772 pub dev_name: String,
774 pub state: NeighState,
776}
777
778#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
784pub struct NetfilterRuleInfo {
785 pub table: String,
787 pub chain: String,
789 pub target: String,
791 pub protocol: String,
793 pub source: Option<String>,
795 pub destination: Option<String>,
797}
798
799#[derive(Debug, Clone, PartialEq, Eq)]
805pub struct CrontabEntry {
806 pub pid: u64,
808 pub comm: String,
810 pub line: String,
812}
813
814#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
820pub enum SshKeyType {
821 Rsa,
823 Ed25519,
825 Dsa,
827 Ecdsa256,
829 Ecdsa384,
831 Ecdsa521,
833 Unknown,
835}
836
837impl SshKeyType {
838 pub fn from_prefix(prefix: &str) -> Self {
842 match prefix {
843 "ssh-rsa" => Self::Rsa,
844 "ssh-ed25519" => Self::Ed25519,
845 "ssh-dss" => Self::Dsa,
846 "ecdsa-sha2-nistp256" => Self::Ecdsa256,
847 "ecdsa-sha2-nistp384" => Self::Ecdsa384,
848 "ecdsa-sha2-nistp521" => Self::Ecdsa521,
849 _ => Self::Unknown,
850 }
851 }
852}
853
854impl fmt::Display for SshKeyType {
855 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
856 match self {
857 Self::Rsa => write!(f, "ssh-rsa"),
858 Self::Ed25519 => write!(f, "ssh-ed25519"),
859 Self::Dsa => write!(f, "ssh-dss"),
860 Self::Ecdsa256 => write!(f, "ecdsa-sha2-nistp256"),
861 Self::Ecdsa384 => write!(f, "ecdsa-sha2-nistp384"),
862 Self::Ecdsa521 => write!(f, "ecdsa-sha2-nistp521"),
863 Self::Unknown => write!(f, "unknown"),
864 }
865 }
866}
867
868#[derive(Debug, Clone, serde::Serialize)]
870pub struct SshKeyInfo {
871 pub pid: u64,
873 pub key_type: SshKeyType,
875 pub key_data: String,
877 pub comment: String,
879}
880
881#[derive(Debug, Clone, serde::Serialize)]
887pub struct HiddenProcessInfo {
888 pub pid: u64,
890 pub comm: String,
892 pub present_in_pid_ns: bool,
894 pub present_in_task_list: bool,
896 pub present_in_pid_hash: bool,
898}
899
900#[derive(Debug, Clone, serde::Serialize)]
902pub struct VdsoTamperInfo {
903 pub pid: u64,
905 pub comm: String,
907 pub vdso_base: u64,
909 pub vdso_size: u64,
911 pub differs_from_canonical: bool,
913 pub diff_byte_count: usize,
915}
916
917#[derive(Debug, Clone, serde::Serialize)]
919pub struct UserNsEscalationInfo {
920 pub pid: u64,
922 pub comm: String,
924 pub ns_depth: u32,
926 pub owner_uid: u32,
928 pub process_uid: u32,
930 pub has_cap_sys_admin: bool,
932 pub is_suspicious: bool,
934}
935
936#[derive(Debug, Clone, serde::Serialize)]
938pub struct AuditTamperInfo {
939 pub audit_enabled: bool,
941 pub backlog_limit: u32,
943 pub suppressed_pids: Vec<u64>,
945 pub suppressed_uids: Vec<u32>,
947 pub audit_globally_disabled: bool,
949}
950
951#[derive(Debug, Clone, serde::Serialize)]
953pub struct CpuPinningInfo {
954 pub pid: u64,
956 pub comm: String,
958 pub pinned_cpu_count: u32,
960 pub total_cpu_count: u32,
962 pub sched_policy: u32,
964 pub cpu_time_ns: u64,
966}
967
968#[allow(clippy::struct_excessive_bools)]
974#[derive(Debug, Clone, serde::Serialize)]
975pub struct ContainerEscapeCorrelateInfo {
976 pub pid: u64,
978 pub comm: String,
980 pub pid_ns_differs_from_cgroup_ns: bool,
982 pub has_host_mounts: bool,
984 pub cap_sys_admin: bool,
986 pub cap_sys_ptrace: bool,
988 pub in_non_init_pid_ns: bool,
990}
991
992#[derive(Debug, Clone, PartialEq, serde::Serialize)]
994pub enum FdAbuseType {
995 TimerFd,
997 SignalFd,
999 EventFd,
1001}
1002
1003#[derive(Debug, Clone, serde::Serialize)]
1005pub struct FdAbuseInfo {
1006 pub pid: u64,
1008 pub comm: String,
1010 pub fd_type: FdAbuseType,
1012 pub signal_mask: u64,
1014 pub interval_ns: u64,
1016 pub is_cross_process_shared: bool,
1018}
1019
1020#[allow(clippy::struct_excessive_bools)]
1022#[derive(Debug, Clone, serde::Serialize)]
1023pub struct SharedMemAnomalyInfo {
1024 pub pid: u64,
1026 pub comm: String,
1028 pub shm_base: u64,
1030 pub shm_size: u64,
1032 pub is_memfd: bool,
1034 pub is_executable: bool,
1036 pub is_cross_uid: bool,
1038 pub has_elf_header: bool,
1040}
1041
1042#[derive(Debug, Clone, serde::Serialize)]
1044pub struct FuseAbuseInfo {
1045 pub pid: u64,
1047 pub comm: String,
1049 pub mount_point: String,
1051 pub is_over_sensitive_path: bool,
1053 pub daemon_is_root: bool,
1055 pub allow_other: bool,
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061 use super::*;
1062
1063 #[test]
1064 fn neigh_state_from_raw() {
1065 assert_eq!(NeighState::from_raw(0x01), NeighState::Incomplete);
1066 assert_eq!(NeighState::from_raw(0x02), NeighState::Reachable);
1067 assert_eq!(NeighState::from_raw(0x04), NeighState::Stale);
1068 assert_eq!(NeighState::from_raw(0x08), NeighState::Delay);
1069 assert_eq!(NeighState::from_raw(0x10), NeighState::Probe);
1070 assert_eq!(NeighState::from_raw(0x20), NeighState::Failed);
1071 assert_eq!(NeighState::from_raw(0x80), NeighState::Permanent);
1072 assert!(matches!(
1073 NeighState::from_raw(0xFF),
1074 NeighState::Unknown(0xFF)
1075 ));
1076 }
1077
1078 #[test]
1079 fn neigh_state_display() {
1080 assert_eq!(NeighState::Reachable.to_string(), "REACHABLE");
1081 assert_eq!(NeighState::Stale.to_string(), "STALE");
1082 assert_eq!(NeighState::Permanent.to_string(), "PERMANENT");
1083 assert_eq!(NeighState::Unknown(0x42).to_string(), "UNKNOWN(0x42)");
1084 }
1085
1086 #[test]
1087 fn vma_flags_from_raw() {
1088 let f = VmaFlags::from_raw(0x5); assert!(f.read);
1090 assert!(!f.write);
1091 assert!(f.exec);
1092 assert!(!f.shared);
1093 }
1094
1095 #[test]
1096 fn vma_flags_display() {
1097 assert_eq!(VmaFlags::from_raw(0x7).to_string(), "rwxp"); assert_eq!(VmaFlags::from_raw(0x1).to_string(), "r--p");
1099 assert_eq!(VmaFlags::from_raw(0xF).to_string(), "rwxs"); assert_eq!(VmaFlags::from_raw(0x0).to_string(), "---p");
1101 }
1102
1103 #[test]
1104 fn process_state_from_raw() {
1105 assert_eq!(ProcessState::from_raw(0), ProcessState::Running);
1106 assert_eq!(ProcessState::from_raw(1), ProcessState::Sleeping);
1107 assert_eq!(ProcessState::from_raw(2), ProcessState::DiskSleep);
1108 assert_eq!(ProcessState::from_raw(4), ProcessState::Stopped);
1109 assert_eq!(ProcessState::from_raw(8), ProcessState::Traced);
1110 assert_eq!(ProcessState::from_raw(16), ProcessState::Dead);
1111 assert_eq!(ProcessState::from_raw(32), ProcessState::Zombie);
1112 assert!(matches!(
1113 ProcessState::from_raw(99),
1114 ProcessState::Unknown(99)
1115 ));
1116 }
1117
1118 #[test]
1119 fn process_state_display() {
1120 assert_eq!(ProcessState::Running.to_string(), "R (running)");
1121 assert_eq!(ProcessState::Sleeping.to_string(), "S (sleeping)");
1122 assert_eq!(ProcessState::DiskSleep.to_string(), "D (disk sleep)");
1123 assert_eq!(ProcessState::Stopped.to_string(), "T (stopped)");
1124 assert_eq!(ProcessState::Traced.to_string(), "t (traced)");
1125 assert_eq!(ProcessState::Dead.to_string(), "X (dead)");
1126 assert_eq!(ProcessState::Zombie.to_string(), "Z (zombie)");
1127 assert_eq!(ProcessState::Unknown(42).to_string(), "? (42)");
1128 }
1129
1130 #[test]
1131 fn connection_state_from_raw() {
1132 assert_eq!(ConnectionState::from_raw(1), ConnectionState::Established);
1133 assert_eq!(ConnectionState::from_raw(2), ConnectionState::SynSent);
1134 assert_eq!(ConnectionState::from_raw(3), ConnectionState::SynRecv);
1135 assert_eq!(ConnectionState::from_raw(4), ConnectionState::FinWait1);
1136 assert_eq!(ConnectionState::from_raw(5), ConnectionState::FinWait2);
1137 assert_eq!(ConnectionState::from_raw(6), ConnectionState::TimeWait);
1138 assert_eq!(ConnectionState::from_raw(7), ConnectionState::Close);
1139 assert_eq!(ConnectionState::from_raw(8), ConnectionState::CloseWait);
1140 assert_eq!(ConnectionState::from_raw(9), ConnectionState::LastAck);
1141 assert_eq!(ConnectionState::from_raw(10), ConnectionState::Listen);
1142 assert_eq!(ConnectionState::from_raw(11), ConnectionState::Closing);
1143 assert!(matches!(
1144 ConnectionState::from_raw(99),
1145 ConnectionState::Unknown(99)
1146 ));
1147 }
1148
1149 #[test]
1150 fn connection_state_display() {
1151 assert_eq!(ConnectionState::Established.to_string(), "ESTABLISHED");
1152 assert_eq!(ConnectionState::SynSent.to_string(), "SYN_SENT");
1153 assert_eq!(ConnectionState::SynRecv.to_string(), "SYN_RECV");
1154 assert_eq!(ConnectionState::FinWait1.to_string(), "FIN_WAIT1");
1155 assert_eq!(ConnectionState::FinWait2.to_string(), "FIN_WAIT2");
1156 assert_eq!(ConnectionState::TimeWait.to_string(), "TIME_WAIT");
1157 assert_eq!(ConnectionState::Close.to_string(), "CLOSE");
1158 assert_eq!(ConnectionState::CloseWait.to_string(), "CLOSE_WAIT");
1159 assert_eq!(ConnectionState::LastAck.to_string(), "LAST_ACK");
1160 assert_eq!(ConnectionState::Listen.to_string(), "LISTEN");
1161 assert_eq!(ConnectionState::Closing.to_string(), "CLOSING");
1162 assert_eq!(ConnectionState::Unknown(42).to_string(), "UNKNOWN(42)");
1163 }
1164
1165 #[test]
1166 fn module_state_from_raw() {
1167 assert_eq!(ModuleState::from_raw(0), ModuleState::Live);
1168 assert_eq!(ModuleState::from_raw(1), ModuleState::Coming);
1169 assert_eq!(ModuleState::from_raw(2), ModuleState::Going);
1170 assert_eq!(ModuleState::from_raw(3), ModuleState::Unformed);
1171 assert!(matches!(
1172 ModuleState::from_raw(99),
1173 ModuleState::Unknown(99)
1174 ));
1175 }
1176
1177 #[test]
1178 fn module_state_display() {
1179 assert_eq!(ModuleState::Live.to_string(), "Live");
1180 assert_eq!(ModuleState::Coming.to_string(), "Coming");
1181 assert_eq!(ModuleState::Going.to_string(), "Going");
1182 assert_eq!(ModuleState::Unformed.to_string(), "Unformed");
1183 assert_eq!(ModuleState::Unknown(42).to_string(), "Unknown(42)");
1184 }
1185
1186 #[test]
1187 fn protocol_display() {
1188 assert_eq!(Protocol::Tcp.to_string(), "TCP");
1189 assert_eq!(Protocol::Udp.to_string(), "UDP");
1190 assert_eq!(Protocol::Tcp6.to_string(), "TCP6");
1191 assert_eq!(Protocol::Udp6.to_string(), "UDP6");
1192 assert_eq!(Protocol::Unix.to_string(), "UNIX");
1193 assert_eq!(Protocol::Raw.to_string(), "RAW");
1194 }
1195
1196 #[test]
1197 fn elf_type_from_raw() {
1198 assert_eq!(ElfType::from_raw(0), ElfType::None);
1199 assert_eq!(ElfType::from_raw(1), ElfType::Relocatable);
1200 assert_eq!(ElfType::from_raw(2), ElfType::Executable);
1201 assert_eq!(ElfType::from_raw(3), ElfType::SharedObject);
1202 assert_eq!(ElfType::from_raw(4), ElfType::Core);
1203 assert!(matches!(ElfType::from_raw(99), ElfType::Unknown(99)));
1204 }
1205
1206 #[test]
1207 fn elf_type_display() {
1208 assert_eq!(ElfType::None.to_string(), "NONE");
1209 assert_eq!(ElfType::Relocatable.to_string(), "REL");
1210 assert_eq!(ElfType::Executable.to_string(), "EXEC");
1211 assert_eq!(ElfType::SharedObject.to_string(), "DYN");
1212 assert_eq!(ElfType::Core.to_string(), "CORE");
1213 assert_eq!(ElfType::Unknown(42).to_string(), "UNKNOWN(42)");
1214 }
1215
1216 #[test]
1219 fn boot_time_source_display() {
1220 assert_eq!(BootTimeSource::Timekeeper.to_string(), "timekeeper");
1221 assert_eq!(BootTimeSource::UserProvided.to_string(), "user-provided");
1222 }
1223
1224 #[test]
1225 fn from_estimates_empty_has_no_best() {
1226 let info = BootTimeInfo::from_estimates(vec![]);
1227 assert_eq!(info.best_estimate, None);
1228 assert!(!info.inconsistent);
1229 assert_eq!(info.max_drift_secs, 0);
1230 }
1231
1232 #[test]
1233 fn from_estimates_single_source() {
1234 let info = BootTimeInfo::from_estimates(vec![BootTimeEstimate {
1235 source: BootTimeSource::Timekeeper,
1236 boot_epoch_secs: 1_712_000_000,
1237 }]);
1238 assert_eq!(info.best_estimate, Some(1_712_000_000));
1239 assert!(!info.inconsistent);
1240 assert_eq!(info.max_drift_secs, 0);
1241 }
1242
1243 #[test]
1244 fn from_estimates_consistent_sources() {
1245 let info = BootTimeInfo::from_estimates(vec![
1246 BootTimeEstimate {
1247 source: BootTimeSource::Timekeeper,
1248 boot_epoch_secs: 1_712_000_000,
1249 },
1250 BootTimeEstimate {
1251 source: BootTimeSource::UserProvided,
1252 boot_epoch_secs: 1_712_000_030, },
1254 ]);
1255 assert_eq!(info.best_estimate, Some(1_712_000_000));
1256 assert!(!info.inconsistent);
1257 assert_eq!(info.max_drift_secs, 30);
1258 }
1259
1260 #[test]
1261 fn from_estimates_inconsistent_sources() {
1262 let info = BootTimeInfo::from_estimates(vec![
1263 BootTimeEstimate {
1264 source: BootTimeSource::Timekeeper,
1265 boot_epoch_secs: 1_712_000_000,
1266 },
1267 BootTimeEstimate {
1268 source: BootTimeSource::UserProvided,
1269 boot_epoch_secs: 1_712_000_120, },
1271 ]);
1272 assert_eq!(info.best_estimate, Some(1_712_000_000));
1273 assert!(info.inconsistent);
1274 assert_eq!(info.max_drift_secs, 120);
1275 }
1276
1277 #[test]
1278 fn absolute_secs_with_boot_epoch() {
1279 let info = BootTimeInfo::from_estimates(vec![BootTimeEstimate {
1280 source: BootTimeSource::UserProvided,
1281 boot_epoch_secs: 1_712_000_000,
1282 }]);
1283 assert_eq!(info.absolute_secs(500_000_000), Some(1_712_000_000));
1285 assert_eq!(info.absolute_secs(3_600_000_000_000), Some(1_712_003_600));
1287 }
1288
1289 #[test]
1290 fn absolute_secs_without_boot_epoch() {
1291 let info = BootTimeInfo::from_estimates(vec![]);
1292 assert_eq!(info.absolute_secs(500_000_000), None);
1293 }
1294
1295 #[test]
1298 fn ssh_key_type_from_prefix() {
1299 assert_eq!(SshKeyType::from_prefix("ssh-rsa"), SshKeyType::Rsa);
1300 assert_eq!(SshKeyType::from_prefix("ssh-ed25519"), SshKeyType::Ed25519);
1301 assert_eq!(SshKeyType::from_prefix("ssh-dss"), SshKeyType::Dsa);
1302 assert_eq!(
1303 SshKeyType::from_prefix("ecdsa-sha2-nistp256"),
1304 SshKeyType::Ecdsa256
1305 );
1306 assert_eq!(
1307 SshKeyType::from_prefix("ecdsa-sha2-nistp384"),
1308 SshKeyType::Ecdsa384
1309 );
1310 assert_eq!(
1311 SshKeyType::from_prefix("ecdsa-sha2-nistp521"),
1312 SshKeyType::Ecdsa521
1313 );
1314 assert_eq!(SshKeyType::from_prefix("bogus"), SshKeyType::Unknown);
1315 assert_eq!(SshKeyType::from_prefix(""), SshKeyType::Unknown);
1316 }
1317
1318 #[test]
1319 fn ssh_key_type_display() {
1320 assert_eq!(SshKeyType::Rsa.to_string(), "ssh-rsa");
1321 assert_eq!(SshKeyType::Ed25519.to_string(), "ssh-ed25519");
1322 assert_eq!(SshKeyType::Dsa.to_string(), "ssh-dss");
1323 assert_eq!(SshKeyType::Ecdsa256.to_string(), "ecdsa-sha2-nistp256");
1324 assert_eq!(SshKeyType::Ecdsa384.to_string(), "ecdsa-sha2-nistp384");
1325 assert_eq!(SshKeyType::Ecdsa521.to_string(), "ecdsa-sha2-nistp521");
1326 assert_eq!(SshKeyType::Unknown.to_string(), "unknown");
1327 }
1328}