Only show unknown IPs from the last 5 minutes.

IMPORTANT: run "remove_pinned_maps.sh" before you run the new
version of lqosd. The eBPF map structure has changed.

1) Add a "last_seen" value to the map_traffic eBPF map.
2) Whenever traffic is seen, update "last_seen" to equal the
   result of a bpf_ktime_get_boot_ns() call.
3) When retrieving the unknown IP list, perform a syscall to
   obtain the time since boot in nanoseconds and subtract
   five minutes.
4) Filter out any unknown IPs that exceed the 5 minute window.

This should solve the problem with "unknown IPs" filling up
on start, and gradually accumulating.
This commit is contained in:
Herbert Wolverson
2023-01-13 17:48:20 +00:00
parent 25234cc28a
commit fecb27c6bc
8 changed files with 28 additions and 17 deletions

13
src/rust/Cargo.lock generated
View File

@@ -399,17 +399,6 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cow_struct"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780363852334e8717416de3477ac2ce707bdf91c086daae09b80f0b354b5a6b5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cpufeatures"
version = "0.2.5"
@@ -1281,13 +1270,13 @@ name = "lqosd"
version = "0.1.0"
dependencies = [
"anyhow",
"cow_struct",
"env_logger",
"lazy_static",
"log",
"lqos_bus",
"lqos_config",
"lqos_sys",
"nix",
"notify",
"parking_lot 0.12.1",
"serde",

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
nix = "0.25"
nix = "0"
libbpf-sys = "1"
anyhow = "1"
byteorder = "1.4"

View File

@@ -15,6 +15,7 @@ struct host_counter {
__u64 download_packets;
__u64 upload_packets;
__u32 tc_handle;
__u64 last_seen;
};
// Pinned map storing counters per host. its an LRU structure: if it
@@ -38,20 +39,21 @@ static __always_inline void track_traffic(
struct host_counter * counter =
(struct host_counter *)bpf_map_lookup_elem(&map_traffic, key);
if (counter) {
counter->last_seen = bpf_ktime_get_boot_ns();
counter->tc_handle = tc_handle;
if (direction == 1) {
// Download
counter->download_packets += 1;
counter->download_bytes += size;
counter->tc_handle = tc_handle;
} else {
// Upload
counter->upload_packets += 1;
counter->upload_bytes += size;
counter->tc_handle = tc_handle;
}
} else {
struct host_counter new_host = {0};
new_host.tc_handle = tc_handle;
new_host.last_seen = bpf_ktime_get_boot_ns();
if (direction == 1) {
new_host.download_packets = 1;
new_host.download_bytes = size;

View File

@@ -19,6 +19,9 @@ pub struct HostCounter {
/// Mapped TC handle, 0 if there isn't one.
pub tc_handle: u32,
/// Time last seen, in nanoseconds since kernel boot
pub last_seen: u64,
}
impl Default for HostCounter {
@@ -29,11 +32,14 @@ impl Default for HostCounter {
upload_bytes: 0,
upload_packets: 0,
tc_handle: 0,
last_seen: 0,
}
}
}
/// Queries the underlying `map_traffic` eBPF pinned map, and returns every entry.
pub fn get_throughput_map() -> Result<Vec<(XdpIpAddress, Vec<HostCounter>)>> {
Ok(BpfPerCpuMap::<XdpIpAddress, HostCounter>::from_path("/sys/fs/bpf/map_traffic")?.dump_vec())
let result = BpfPerCpuMap::<XdpIpAddress, HostCounter>::from_path("/sys/fs/bpf/map_traffic")?.dump_vec();
//println!("{:#?}", result);
Ok(result)
}

View File

@@ -21,4 +21,4 @@ serde = { version = "1.0", features = ["derive"] }
notify = { version = "5.0.0", default-features = false, feature=["macos_kqueue"] } # Not using crossbeam because of Tokio
env_logger = "0"
log = "0"
cow_struct = "0"
nix = "0"

View File

@@ -229,12 +229,21 @@ pub fn host_counts() -> BusResponse {
}
pub fn all_unknown_ips() -> BusResponse {
let boot_time = nix::time::clock_gettime(nix::time::ClockId::CLOCK_BOOTTIME)
.expect("Unable to obtain kernel time.");
let time_since_boot = Duration::from(boot_time);
let five_minutes_ago = time_since_boot - Duration::from_secs(300);
let five_minutes_ago_nanoseconds = five_minutes_ago.as_nanos();
let mut full_list: Vec<(XdpIpAddress, (u64, u64), (u64, u64), f32, TcHandle, u64)> = {
let tp = THROUGHPUT_TRACKER.read();
tp.raw_data
.iter()
.filter(|(ip, _)| !ip.as_ip().is_loopback())
.filter(|(_, d)| d.tc_handle.as_u32() == 0)
.filter(|(_, d)| {
d.last_seen as u128 > five_minutes_ago_nanoseconds
})
.map(|(ip, te)| {
(
*ip,

View File

@@ -13,6 +13,7 @@ pub(crate) struct ThroughputEntry {
pub(crate) tc_handle: TcHandle,
pub(crate) recent_rtt_data: [u32; 60],
pub(crate) last_fresh_rtt_data_cycle: u64,
pub(crate) last_seen: u64, // Last seen in kernel time since boot
}
impl ThroughputEntry {

View File

@@ -63,6 +63,9 @@ impl ThroughputTracker {
if c.tc_handle != 0 {
entry.tc_handle = TcHandle::from_u32(c.tc_handle);
}
if c.last_seen != 0 {
entry.last_seen = c.last_seen;
}
}
if entry.packets != entry.prev_packets {
entry.most_recent_cycle = self.cycle;
@@ -80,6 +83,7 @@ impl ThroughputTracker {
tc_handle: TcHandle::zero(),
recent_rtt_data: [0; 60],
last_fresh_rtt_data_cycle: 0,
last_seen: 0,
};
for c in counts {
entry.bytes.0 += c.download_bytes;