mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Cleanup ICMP tracking, add documentation to source code.
This should make the Heimdall branch usable.
This commit is contained in:
parent
d3feb64911
commit
7d055d9796
@ -67,24 +67,43 @@ pub struct XdpPpingResult {
|
||||
pub samples: u32,
|
||||
}
|
||||
|
||||
/// Defines an IP protocol for display in the flow
|
||||
/// tracking (Heimdall) system.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum FlowProto {
|
||||
TCP, UDP, ICMP
|
||||
/// A TCP flow
|
||||
TCP,
|
||||
/// A UDP flow
|
||||
UDP,
|
||||
/// An ICMP flow
|
||||
ICMP
|
||||
}
|
||||
|
||||
/// Defines the display data for a flow in Heimdall.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct FlowTransport {
|
||||
/// The Source IP address
|
||||
pub src: String,
|
||||
/// The Destination IP address
|
||||
pub dst: String,
|
||||
/// The flow protocol (see `FlowProto`)
|
||||
pub proto: FlowProto,
|
||||
/// The source port, which is overridden to ICMP code on ICMP flows.
|
||||
pub src_port: u16,
|
||||
/// The destination port, which isn't useful at all on ICMP flows.
|
||||
pub dst_port: u16,
|
||||
/// The number of bytes since we started tracking this flow.
|
||||
pub bytes: u64,
|
||||
/// The number of packets since we started tracking this flow.
|
||||
pub packets: u64,
|
||||
/// Detected DSCP code if any
|
||||
pub dscp: u8,
|
||||
/// Detected ECN bit status (0-3)
|
||||
pub ecn: u8,
|
||||
}
|
||||
|
||||
/// Extract the 6-bit DSCP and 2-bit ECN code from a TOS field
|
||||
/// in an IP header.
|
||||
pub fn tos_parser(tos: u8) -> (u8, u8) {
|
||||
// Format: 2 bits of ECN, 6 bits of DSCP
|
||||
const ECN: u8 = 0b00000011;
|
||||
|
@ -549,6 +549,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
function icmpType(n) {
|
||||
switch (n) {
|
||||
case 0: return "ECHO REPLY";
|
||||
case 3: return "DESTINATION UNREACHABLE";
|
||||
case 4: return "SOURCE QUENCH";
|
||||
case 8: return "ECHO REQUEST";
|
||||
case 11: return "TIME EXCEEDED";
|
||||
case 12: return "PARAMETER PROBLEM";
|
||||
case 13: return "TIMESTAMP REQUEST";
|
||||
case 14: return "TIMESTAMP REPLY";
|
||||
case 15: return "INFO REQUEST";
|
||||
case 16: return "INFO REPLY";
|
||||
case 17: return "ADDRESS REQUEST";
|
||||
case 18: return "ADDRESS REPLY";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
function getFlows() {
|
||||
let ip_list = "";
|
||||
for (let i=0; i<ips.length; ++i) {
|
||||
@ -587,9 +605,21 @@
|
||||
html += "<tr>";
|
||||
html += "<td>" + data[i][0].proto + "</td>";
|
||||
html += "<td>" + data[i][0].src + "</td>";
|
||||
html += "<td>" + data[i][0].src_port + "</td>";
|
||||
if (data[i][0].proto == "ICMP") {
|
||||
html += "<td>" + icmpType(data[i][0].src_port) + "</td>";
|
||||
} else {
|
||||
html += "<td>" + data[i][0].src_port + "</td>";
|
||||
}
|
||||
html += "<td>" + data[i][0].dst + "</td>";
|
||||
html += "<td>" + data[i][0].dst_port + "</td>";
|
||||
if (data[i][0].proto == "ICMP") {
|
||||
if (data[i][1] != null) {
|
||||
html += "<td>" + icmpType(data[i][1].src_port) + "</td>";
|
||||
} else {
|
||||
html += "<td></td>";
|
||||
}
|
||||
} else {
|
||||
html += "<td>" + data[i][0].dst_port + "</td>";
|
||||
}
|
||||
html += "<td>" + data[i][0].packets + "</td>";
|
||||
html += "<td>" + rpackets + "</td>";
|
||||
html += "<td>" + scaleNumber(data[i][0].bytes) + "</td>";
|
||||
|
@ -346,12 +346,13 @@ static __always_inline void snoop(struct dissector_t *dissector)
|
||||
struct icmphdr *hdr = get_icmp_header(dissector);
|
||||
if (hdr != NULL)
|
||||
{
|
||||
if (hdr + 1 > dissector->end)
|
||||
if ((char *)hdr + sizeof(struct icmphdr) > dissector->end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
dissector->src_port = hdr->type;
|
||||
dissector->dst_port = hdr->code;
|
||||
dissector->ip_protocol = 1;
|
||||
dissector->src_port = bpf_ntohs(hdr->type);
|
||||
dissector->dst_port = bpf_ntohs(hdr->type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -8,28 +8,31 @@
|
||||
#include "dissector.h"
|
||||
|
||||
// Array containing one element, the Heimdall configuration
|
||||
struct heimdall_config_t {
|
||||
struct heimdall_config_t
|
||||
{
|
||||
__u32 monitor_mode; // 0 = Off, 1 = Targets only, 2 = Analysis Mode
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, struct heimdall_config_t);
|
||||
__uint(max_entries, 2);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
struct
|
||||
{
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, struct heimdall_config_t);
|
||||
__uint(max_entries, 2);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} heimdall_config SEC(".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__type(key, struct in6_addr);
|
||||
__type(value, __u32);
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__type(key, struct in6_addr);
|
||||
__type(value, __u32);
|
||||
__uint(max_entries, 64);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} heimdall_watching SEC(".maps");
|
||||
|
||||
struct heimdall_key {
|
||||
struct heimdall_key
|
||||
{
|
||||
struct in6_addr src;
|
||||
struct in6_addr dst;
|
||||
__u8 ip_protocol;
|
||||
@ -37,7 +40,8 @@ struct heimdall_key {
|
||||
__u16 dst_port;
|
||||
};
|
||||
|
||||
struct heimdall_data {
|
||||
struct heimdall_data
|
||||
{
|
||||
__u64 last_seen;
|
||||
__u64 bytes;
|
||||
__u64 packets;
|
||||
@ -47,48 +51,64 @@ struct heimdall_data {
|
||||
|
||||
struct
|
||||
{
|
||||
__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
|
||||
__type(key, struct heimdall_key);
|
||||
__type(value, struct heimdall_data);
|
||||
__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
|
||||
__type(key, struct heimdall_key);
|
||||
__type(value, struct heimdall_data);
|
||||
__uint(max_entries, MAX_FLOWS);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} heimdall SEC(".maps");
|
||||
|
||||
static __always_inline __u8 get_heimdall_mode() {
|
||||
static __always_inline __u8 get_heimdall_mode()
|
||||
{
|
||||
__u32 index = 0;
|
||||
struct heimdall_config_t * cfg = (struct heimdall_config_t *)bpf_map_lookup_elem(&heimdall_config, &index);
|
||||
if (cfg) {
|
||||
struct heimdall_config_t *cfg = (struct heimdall_config_t *)bpf_map_lookup_elem(&heimdall_config, &index);
|
||||
if (cfg)
|
||||
{
|
||||
return cfg->monitor_mode;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline bool is_heimdall_watching(struct dissector_t *dissector) {
|
||||
__u32 * watching = bpf_map_lookup_elem(&heimdall_watching, &dissector->src_ip);
|
||||
if (watching) return true;
|
||||
static __always_inline bool is_heimdall_watching(struct dissector_t *dissector)
|
||||
{
|
||||
__u32 *watching = bpf_map_lookup_elem(&heimdall_watching, &dissector->src_ip);
|
||||
if (watching)
|
||||
return true;
|
||||
watching = bpf_map_lookup_elem(&heimdall_watching, &dissector->dst_ip);
|
||||
if (watching) return true;
|
||||
if (watching)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline void update_heimdall(struct dissector_t * dissector, __u32 size, int dir) {
|
||||
if (dissector->src_port == 0 || dissector->dst_port == 0) return;
|
||||
static __always_inline void update_heimdall(struct dissector_t *dissector, __u32 size, int dir)
|
||||
{
|
||||
// Don't report any non-ICMP without ports
|
||||
if (dissector->ip_protocol != 1 && (dissector->src_port == 0 || dissector->dst_port == 0))
|
||||
return;
|
||||
// Don't report ICMP with invalid numbers
|
||||
if (dissector->ip_protocol == 1 && dissector->src_port > 18) return;
|
||||
struct heimdall_key key = {0};
|
||||
key.src = dissector->src_ip;
|
||||
key.dst = dissector->dst_ip;
|
||||
key.ip_protocol = dissector->ip_protocol;
|
||||
key.src_port = bpf_ntohs(dissector->src_port);
|
||||
key.dst_port = bpf_ntohs(dissector->dst_port);
|
||||
struct heimdall_data * counter = (struct heimdall_data *)bpf_map_lookup_elem(&heimdall, &key);
|
||||
if (counter) {
|
||||
struct heimdall_data *counter = (struct heimdall_data *)bpf_map_lookup_elem(&heimdall, &key);
|
||||
if (counter)
|
||||
{
|
||||
counter->last_seen = bpf_ktime_get_boot_ns();
|
||||
counter->packets += 1;
|
||||
counter->bytes += size;
|
||||
if (dissector->tos != 0) {
|
||||
if (dissector->tos != 0)
|
||||
{
|
||||
counter->tos = dissector->tos;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
struct heimdall_data counter = {0};
|
||||
counter.last_seen = bpf_ktime_get_boot_ns();
|
||||
counter.bytes = size;
|
||||
@ -97,7 +117,8 @@ static __always_inline void update_heimdall(struct dissector_t * dissector, __u3
|
||||
counter.reserved[0] = 0;
|
||||
counter.reserved[1] = 0;
|
||||
counter.reserved[2] = 0;
|
||||
if (bpf_map_update_elem(&heimdall, &key, &counter, BPF_NOEXIST) != 0) {
|
||||
if (bpf_map_update_elem(&heimdall, &key, &counter, BPF_NOEXIST) != 0)
|
||||
{
|
||||
bpf_debug("Failed to insert tracking");
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,35 @@ use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{bpf_per_cpu_map::BpfPerCpuMap, XdpIpAddress, bpf_map::BpfMap};
|
||||
|
||||
/// Representation of the eBPF `heimdall_key` type.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct HeimdallKey {
|
||||
/// Mapped `XdpIpAddress` source for the flow.
|
||||
pub src_ip: XdpIpAddress,
|
||||
/// Mapped `XdpIpAddress` destination for the flow
|
||||
pub dst_ip: XdpIpAddress,
|
||||
/// IP protocol (see the Linux kernel!)
|
||||
pub ip_protocol: u8,
|
||||
/// Source port number, or ICMP type.
|
||||
pub src_port: u16,
|
||||
/// Destination port number.
|
||||
pub dst_port: u16,
|
||||
}
|
||||
|
||||
/// Mapped representation of the eBPF `heimdall_data` type.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[repr(C)]
|
||||
pub struct HeimdallData {
|
||||
/// Last seen, in nanoseconds (since boot time).
|
||||
pub last_seen: u64,
|
||||
/// Number of bytes since the flow started being tracked
|
||||
pub bytes: u64,
|
||||
/// Number of packets since the flow started being tracked
|
||||
pub packets: u64,
|
||||
/// IP header TOS value
|
||||
pub tos: u8,
|
||||
/// Reserved to pad the structure
|
||||
pub reserved: [u8; 3],
|
||||
}
|
||||
|
||||
@ -37,10 +49,15 @@ pub fn heimdall_for_each(
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently unused, represents the current operation mode of the Heimdall
|
||||
/// sub-system. Defaults to 1.
|
||||
#[repr(u8)]
|
||||
pub enum HeimdallMode {
|
||||
/// Do not monitor
|
||||
Off = 0,
|
||||
/// Only look at flows on hosts we are watching via the circuit monitor
|
||||
WatchOnly = 1,
|
||||
/// Capture everything (this may set your CPU on fire)
|
||||
Analysis = 2,
|
||||
}
|
||||
|
||||
@ -87,6 +104,8 @@ impl HeimdallWatching {
|
||||
|
||||
static HEIMDALL_WATCH_LIST: Lazy<DashMap<XdpIpAddress, HeimdallWatching>> = Lazy::new(DashMap::new);
|
||||
|
||||
/// Run this periodically (once per second) to expire any watched traffic
|
||||
/// flows that haven't received traffic in the last 30 seconds.
|
||||
pub fn heimdall_expire() {
|
||||
if let Ok(now) = time_since_boot() {
|
||||
let now = Duration::from(now).as_nanos();
|
||||
@ -99,6 +118,9 @@ pub fn heimdall_expire() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Instruct Heimdall to start watching an IP address.
|
||||
/// You want to call this when you refresh a flow; it will auto-expire
|
||||
/// in 30 seconds.
|
||||
pub fn heimdall_watch_ip(ip: XdpIpAddress) {
|
||||
if HEIMDALL_WATCH_LIST.contains_key(&ip) {
|
||||
return;
|
||||
|
@ -131,6 +131,9 @@ pub fn get_flow_stats(ip: &str) -> BusResponse {
|
||||
}
|
||||
}
|
||||
|
||||
result.sort_by(|a,b| {
|
||||
b.0.bytes.cmp(&a.0.bytes)
|
||||
});
|
||||
|
||||
return BusResponse::FlowData(result);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user