mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Issue 291 capture time (#293)
* Make packet capture time user configurable. * Add `packet_capture_time` to /etc/lqos.conf as a number (seconds) * Rework the capture logic to obtain the capture time and wait for the specified period. * Rename some functions that specified ten seconds in the name. Relates to ISSUE #291 * Remove a dangling dashboard link * Change libpcap download filename to <capture-circuit_id.pcap> Relates to ISSUE #291 * Pass the circuit_id to the ip_dump page. * Include the circuit_id in the downloaded packet capture filename. * Add starting timestamp to capture filename Adds the starting timestamp (in ns) to the capture filename when you download a libcap dump. Ending timestamp isn't included; the starting stamp is almost certainly unique. Relates to ISSUE #291 and fixes the parts that I intend to touch.
This commit is contained in:
parent
a32949c76d
commit
25801b6445
@ -4,6 +4,7 @@
|
||||
# Where is LibreQoS installed?
|
||||
lqos_directory = '/opt/libreqos/src'
|
||||
queue_check_period_ms = 1000
|
||||
packet_capture_time = 10 # Number of seconds to capture packets in an analysis session
|
||||
|
||||
[usage_stats]
|
||||
send_anonymous = true
|
||||
|
1
src/rust/Cargo.lock
generated
1
src/rust/Cargo.lock
generated
@ -1436,6 +1436,7 @@ dependencies = [
|
||||
"dashmap",
|
||||
"log",
|
||||
"lqos_bus",
|
||||
"lqos_config",
|
||||
"lqos_sys",
|
||||
"lqos_utils",
|
||||
"once_cell",
|
||||
|
@ -91,7 +91,12 @@ pub enum BusResponse {
|
||||
FlowData(Vec<(FlowTransport, Option<FlowTransport>)>),
|
||||
|
||||
/// The index of the new packet collection session
|
||||
PacketCollectionSession(usize),
|
||||
PacketCollectionSession{
|
||||
/// The identifier of the capture session
|
||||
session_id: usize,
|
||||
/// Number of seconds for which data will be captured
|
||||
countdown: usize
|
||||
},
|
||||
|
||||
/// Packet header dump
|
||||
PacketDump(Option<Vec<PacketHeader>>),
|
||||
|
@ -30,6 +30,11 @@ pub struct EtcLqos {
|
||||
|
||||
/// If present, defined anonymous usage stat sending
|
||||
pub usage_stats: Option<UsageStats>,
|
||||
|
||||
/// Defines for how many seconds a libpcap compatible capture should
|
||||
/// run. Short times are good, there's a real performance penalty to
|
||||
/// capturing high-throughput streams. Defaults to 10 seconds.
|
||||
pub packet_capture_time: Option<usize>,
|
||||
}
|
||||
|
||||
/// Represents a set of `sysctl` and `ethtool` tweaks that may be
|
||||
|
@ -8,6 +8,7 @@ license = "GPL-2.0-only"
|
||||
lqos_utils = { path = "../lqos_utils" }
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
lqos_sys = { path = "../lqos_sys" }
|
||||
lqos_config = { path = "../lqos_config" }
|
||||
log = "0"
|
||||
zerocopy = {version = "0.6.1", features = [ "simd" ] }
|
||||
once_cell = "1.17.1"
|
||||
|
@ -9,7 +9,7 @@ pub use config::{HeimdalConfig, HeimdallMode};
|
||||
mod flows;
|
||||
pub use flows::{expire_heimdall_flows, get_flow_stats};
|
||||
mod timeline;
|
||||
pub use timeline::{ten_second_packet_dump, ten_second_pcap, hyperfocus_on_target};
|
||||
pub use timeline::{n_second_packet_dump, n_second_pcap, hyperfocus_on_target};
|
||||
mod pcap;
|
||||
mod watchlist;
|
||||
use lqos_utils::fdtimer::periodic;
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
};
|
||||
use dashmap::{DashMap, DashSet};
|
||||
use lqos_bus::{tos_parser, PacketHeader};
|
||||
use lqos_config::EtcLqos;
|
||||
use lqos_utils::{unix_time::time_since_boot, XdpIpAddress};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
@ -99,7 +100,7 @@ static FOCUS_SESSIONS: Lazy<DashMap<usize, FocusSession>> =
|
||||
///
|
||||
/// * Either `None` or...
|
||||
/// * The id number of the collection session for analysis.
|
||||
pub fn hyperfocus_on_target(ip: XdpIpAddress) -> Option<usize> {
|
||||
pub fn hyperfocus_on_target(ip: XdpIpAddress) -> Option<(usize, usize)> {
|
||||
if HYPERFOCUSED.compare_exchange(
|
||||
false,
|
||||
true,
|
||||
@ -107,10 +108,17 @@ pub fn hyperfocus_on_target(ip: XdpIpAddress) -> Option<usize> {
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
) == Ok(false)
|
||||
{
|
||||
// If explicitly set, obtain the capture time. Otherwise, default to
|
||||
// a reasonable 10 seconds.
|
||||
let capture_time = if let Ok(cfg) = EtcLqos::load() {
|
||||
cfg.packet_capture_time.unwrap_or(10)
|
||||
} else {
|
||||
10
|
||||
};
|
||||
let new_id =
|
||||
FOCUS_SESSION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
std::thread::spawn(move || {
|
||||
for _ in 0..10 {
|
||||
for _ in 0..capture_time {
|
||||
let _ = set_heimdall_mode(HeimdallMode::Analysis);
|
||||
heimdall_watch_ip(ip);
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
@ -133,7 +141,7 @@ pub fn hyperfocus_on_target(ip: XdpIpAddress) -> Option<usize> {
|
||||
|
||||
HYPERFOCUSED.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
});
|
||||
Some(new_id)
|
||||
Some((new_id, capture_time))
|
||||
} else {
|
||||
log::warn!(
|
||||
"Heimdall was busy and won't start another collection session."
|
||||
@ -142,7 +150,7 @@ pub fn hyperfocus_on_target(ip: XdpIpAddress) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ten_second_packet_dump(session_id: usize) -> Option<Vec<PacketHeader>> {
|
||||
pub fn n_second_packet_dump(session_id: usize) -> Option<Vec<PacketHeader>> {
|
||||
if let Some(session) = FOCUS_SESSIONS.get(&session_id) {
|
||||
Some(session.data.iter().map(|e| e.as_header()).collect())
|
||||
} else {
|
||||
@ -150,7 +158,7 @@ pub fn ten_second_packet_dump(session_id: usize) -> Option<Vec<PacketHeader>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ten_second_pcap(session_id: usize) -> Option<String> {
|
||||
pub fn n_second_pcap(session_id: usize) -> Option<String> {
|
||||
if let Some(mut session) = FOCUS_SESSIONS.get_mut(&session_id) {
|
||||
let filename = format!("/tmp/cap_sess_{session_id}");
|
||||
session.dump_filename = Some(filename.clone());
|
||||
|
@ -118,14 +118,14 @@ pub async fn flow_stats(ip_list: String, _auth: AuthGuard) -> NoCache<Json<Vec<(
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub enum RequestAnalysisResult {
|
||||
Fail,
|
||||
Ok(usize)
|
||||
Ok{ session_id: usize, countdown: usize }
|
||||
}
|
||||
|
||||
#[get("/api/request_analysis/<ip>")]
|
||||
pub async fn request_analysis(ip: String) -> NoCache<Json<RequestAnalysisResult>> {
|
||||
for r in bus_request(vec![BusRequest::GatherPacketData(ip)]).await.unwrap() {
|
||||
if let BusResponse::PacketCollectionSession(id) = r {
|
||||
return NoCache::new(Json(RequestAnalysisResult::Ok(id)));
|
||||
if let BusResponse::PacketCollectionSession{session_id, countdown} = r {
|
||||
return NoCache::new(Json(RequestAnalysisResult::Ok{session_id, countdown}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,8 +143,11 @@ pub async fn packet_dump(id: usize, _auth: AuthGuard) -> NoCache<Json<Vec<Packet
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[get("/api/pcap/<id>/capture.pcap")]
|
||||
pub async fn pcap(id: usize) -> Result<NoCache<NamedFile>, Status> {
|
||||
#[allow(unused_variables)]
|
||||
#[get("/api/pcap/<id>/<filename>")]
|
||||
pub async fn pcap(id: usize, filename: String) -> Result<NoCache<NamedFile>, Status> {
|
||||
// The unusued _filename parameter is there to allow the changing of the
|
||||
// filename on the client side. See Github issue 291.
|
||||
for r in bus_request(vec![BusRequest::GetPcapDump(id)]).await.unwrap() {
|
||||
if let BusResponse::PcapDump(Some(filename)) = r {
|
||||
return Ok(NoCache::new(NamedFile::open(filename).await.unwrap()));
|
||||
|
@ -195,7 +195,7 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fa fa-bar-chart"></i> Flows (Last 30 Seconds)</h5>
|
||||
<p class="alert alert-warning" role="alert">
|
||||
<i class="fa fa-warning"></i> Gathering packet data can cause high CPU load during the 10 second capture window.
|
||||
<i class="fa fa-warning"></i> Gathering packet data can cause high CPU load during the capture window.
|
||||
</p>
|
||||
<div id="packetButtons"></div>
|
||||
<div id="flowList"></div>
|
||||
@ -595,9 +595,9 @@
|
||||
alert("Heimdall is busy serving other customers. Your desire is important to him, please try again later.")
|
||||
return;
|
||||
}
|
||||
analysisId = data.Ok;
|
||||
analysisId = data.Ok.session_id;
|
||||
analysisBtn = "#dumpBtn_" + id;
|
||||
analysisTimer = 10;
|
||||
analysisTimer = data.Ok.countdown;
|
||||
analyzeTick();
|
||||
});
|
||||
}
|
||||
@ -608,7 +608,7 @@
|
||||
if (analysisTimer > -1) {
|
||||
setTimeout(analyzeTick, 1000);
|
||||
} else {
|
||||
window.location.href = "/ip_dump?id=" + analysisId;
|
||||
window.location.href = "/ip_dump?id=" + analysisId + "&circuit_id=" + encodeURI(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,6 @@
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fa fa-home"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/tree?parent=0"><i class="fa fa-tree"></i> Tree</a>
|
||||
</li>
|
||||
@ -130,7 +127,7 @@ if (hdr->cwr) flags |= 128;
|
||||
}
|
||||
|
||||
function paginator(active) {
|
||||
let paginator = "<a href='/api/pcap/" + target + "/capture.pcap' class='btn btn-warning'>Download PCAP Dump</a> ";
|
||||
let paginator = "<a href='/api/pcap/" + target + "/capture-" + circuit_id + "-" + starting_timestamp + ".pcap' class='btn btn-warning'>Download PCAP Dump</a> ";
|
||||
paginator += "<a href='#' class='btn btn-info' onClick='zoomIn();'>Zoom In</a> ";
|
||||
paginator += "<a href='#' class='btn btn-info' onClick='zoomOut();'>Zoom Out</a> (ℹ️ Or drag an area of the graph) <br />";
|
||||
|
||||
@ -198,7 +195,10 @@ if (hdr->cwr) flags |= 128;
|
||||
{x: x_axis, y:y2_axis, name: 'Upload', type: 'scatter', mode: 'markers', error_x: { type: 'percent', value: capacity[1], symetric: false, valueminus: 0 }},
|
||||
];
|
||||
Plotly.newPlot(graph, data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true, title: 'Bytes' }, xaxis: {automargin: true, title: "Nanoseconds"} }, { responsive: true });
|
||||
}
|
||||
}
|
||||
|
||||
let circuit_id = null;
|
||||
let starting_timestamp = null;
|
||||
|
||||
function start() {
|
||||
colorReloadButton();
|
||||
@ -206,6 +206,7 @@ if (hdr->cwr) flags |= 128;
|
||||
const params = new Proxy(new URLSearchParams(window.location.search), {
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
circuit_id = params.circuit_id;
|
||||
|
||||
capacity = [ params.dn, params.up ]; // Bits per second
|
||||
capacity = [ capacity[0] / 8, capacity[1] / 8 ]; // Bytes per second
|
||||
@ -226,6 +227,7 @@ if (hdr->cwr) flags |= 128;
|
||||
}
|
||||
packets = data;
|
||||
pages = Math.ceil((packets.length / PAGE_SIZE));
|
||||
starting_timestamp = min_ts;
|
||||
paginator(0);
|
||||
viewPage(0);
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ use anyhow::Result;
|
||||
use log::{info, warn};
|
||||
use lqos_bus::{BusRequest, BusResponse, UnixSocketServer};
|
||||
use lqos_config::LibreQoSConfig;
|
||||
use lqos_heimdall::{ten_second_packet_dump, perf_interface::heimdall_handle_events, start_heimdall};
|
||||
use lqos_heimdall::{n_second_packet_dump, perf_interface::heimdall_handle_events, start_heimdall};
|
||||
use lqos_queue_tracker::{
|
||||
add_watched_queue, get_raw_circuit_data, spawn_queue_monitor,
|
||||
spawn_queue_structure_monitor,
|
||||
@ -197,16 +197,16 @@ fn handle_bus_requests(
|
||||
}
|
||||
BusRequest::GetFlowStats(ip) => get_flow_stats(ip),
|
||||
BusRequest::GetPacketHeaderDump(id) => {
|
||||
BusResponse::PacketDump(ten_second_packet_dump(*id))
|
||||
BusResponse::PacketDump(n_second_packet_dump(*id))
|
||||
}
|
||||
BusRequest::GetPcapDump(id) => {
|
||||
BusResponse::PcapDump(lqos_heimdall::ten_second_pcap(*id))
|
||||
BusResponse::PcapDump(lqos_heimdall::n_second_pcap(*id))
|
||||
}
|
||||
BusRequest::GatherPacketData(ip) => {
|
||||
let ip = ip.parse::<IpAddr>();
|
||||
if let Ok(ip) = ip {
|
||||
if let Some(id) = lqos_heimdall::hyperfocus_on_target(ip.into()) {
|
||||
BusResponse::PacketCollectionSession(id)
|
||||
if let Some((session_id, countdown)) = lqos_heimdall::hyperfocus_on_target(ip.into()) {
|
||||
BusResponse::PacketCollectionSession{session_id, countdown}
|
||||
} else {
|
||||
BusResponse::Fail("Busy".to_string())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user