mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2024-11-22 08:16:25 -06:00
Add libpcap format support and ability to download a snapshot of the last 10 seconds in pcap format for Wireshark (or other tool) analysis.
This commit is contained in:
parent
8288bb3f9b
commit
17100415dd
104
src/rust/Cargo.lock
generated
104
src/rust/Cargo.lock
generated
@ -733,6 +733,18 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "educe"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4"
|
||||
dependencies = [
|
||||
"enum-ordinalize",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
@ -748,6 +760,20 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-ordinalize"
|
||||
version = "3.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
@ -1453,6 +1479,7 @@ dependencies = [
|
||||
"nix",
|
||||
"once_cell",
|
||||
"rocket",
|
||||
"rocket-download-response",
|
||||
"rocket_async_compression",
|
||||
"sysinfo",
|
||||
]
|
||||
@ -1630,6 +1657,16 @@ version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@ -1736,6 +1773,27 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@ -2218,6 +2276,19 @@ dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket-download-response"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98c0a60b1b9d018d3cd73b4b69a1f49656a4adbb1552a997e18de30d7413c6ce"
|
||||
dependencies = [
|
||||
"educe",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"rocket",
|
||||
"url-escape",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_async_compression"
|
||||
version = "0.2.0"
|
||||
@ -2281,6 +2352,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.9"
|
||||
@ -2328,6 +2408,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.153"
|
||||
@ -2940,6 +3026,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
@ -2980,6 +3075,15 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url-escape"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
|
@ -139,6 +139,9 @@ pub enum BusRequest {
|
||||
/// Give me a dump of the last 10 seconds of packet headers
|
||||
GetPacketHeaderDump(String),
|
||||
|
||||
/// Give me a libpcap format packet dump (shortened) of the last 10 seconds
|
||||
GetPcapDump,
|
||||
|
||||
/// If running on Equinix (the `equinix_test` feature is enabled),
|
||||
/// display a "run bandwidht test" link.
|
||||
#[cfg(feature = "equinix_tests")]
|
||||
|
@ -92,4 +92,7 @@ pub enum BusResponse {
|
||||
|
||||
/// Packet header dump
|
||||
PacketDump(Vec<PacketHeader>),
|
||||
|
||||
/// Pcap format dump
|
||||
PcapDump(Vec<u8>),
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ pub use config::{HeimdallMode, HeimdalConfig};
|
||||
mod flows;
|
||||
pub use flows::{expire_heimdall_flows, get_flow_stats};
|
||||
mod timeline;
|
||||
pub use timeline::ten_second_packet_dump;
|
||||
pub use timeline::{ten_second_packet_dump, ten_second_pcap};
|
||||
mod pcap;
|
||||
|
50
src/rust/lqos_heimdall/src/pcap.rs
Normal file
50
src/rust/lqos_heimdall/src/pcap.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::time::Duration;
|
||||
use zerocopy::AsBytes;
|
||||
use crate::perf_interface::HeimdallEvent;
|
||||
|
||||
#[derive(AsBytes)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PcapFileHeader {
|
||||
magic: u32,
|
||||
version_major: u16,
|
||||
version_minor: u16,
|
||||
thiszone: i32,
|
||||
sigfigs: u32,
|
||||
snaplen: u32,
|
||||
link_type: u32,
|
||||
}
|
||||
|
||||
impl PcapFileHeader {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
magic: 0xa1b2c3d4,
|
||||
version_major: 2,
|
||||
version_minor: 4,
|
||||
thiszone: 0,
|
||||
sigfigs: 0,
|
||||
snaplen: 64,
|
||||
link_type: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsBytes)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PcapPacketHeader {
|
||||
ts_sec: u32,
|
||||
ts_usec: u32,
|
||||
inc_len: u32, // Octets included
|
||||
orig_len: u32, // Length the packet used to be
|
||||
}
|
||||
|
||||
impl PcapPacketHeader {
|
||||
pub(crate) fn from_heimdall(event: &HeimdallEvent) -> Self {
|
||||
let timestamp_nanos = Duration::from_nanos(event.timestamp);
|
||||
Self {
|
||||
ts_sec: timestamp_nanos.as_secs() as u32,
|
||||
ts_usec: timestamp_nanos.subsec_micros(),
|
||||
inc_len: u32::min(64, event.size),
|
||||
orig_len: event.size
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ pub struct HeimdallEvent {
|
||||
pub tcp_window: u16,
|
||||
pub tcp_tsval: u32,
|
||||
pub tcp_tsecr: u32,
|
||||
pub packet_data: [u8; 64],
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3,7 +3,8 @@ use dashmap::DashSet;
|
||||
use lqos_bus::{PacketHeader, tos_parser};
|
||||
use lqos_utils::{unix_time::time_since_boot, XdpIpAddress};
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::perf_interface::HeimdallEvent;
|
||||
use zerocopy::AsBytes;
|
||||
use crate::{perf_interface::HeimdallEvent, pcap::{PcapFileHeader, PcapPacketHeader}};
|
||||
|
||||
impl HeimdallEvent {
|
||||
fn as_header(&self) -> PacketHeader {
|
||||
@ -58,3 +59,17 @@ pub fn ten_second_packet_dump(ip: XdpIpAddress) -> Vec<PacketHeader> {
|
||||
.map(|e| e.as_header())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn ten_second_pcap() -> Vec<u8> {
|
||||
let mut bytes : Vec<u8> = Vec::new();
|
||||
let file_header = PcapFileHeader::new();
|
||||
bytes.extend(file_header.as_bytes());
|
||||
let mut packets: Vec<HeimdallEvent> = TIMELINE.data.iter().map(|e| e.clone()).collect();
|
||||
packets.sort_by(|a,b| a.timestamp.cmp(&b.timestamp));
|
||||
packets.iter().for_each(|p| {
|
||||
let packet_header = PcapPacketHeader::from_heimdall(p);
|
||||
bytes.extend(packet_header.as_bytes());
|
||||
bytes.extend(p.packet_data);
|
||||
});
|
||||
bytes
|
||||
}
|
@ -18,7 +18,8 @@ sysinfo = "0"
|
||||
default-net = "0"
|
||||
nix = "0"
|
||||
once_cell = "1"
|
||||
rocket-download-response = "0.5"
|
||||
|
||||
# Support JemAlloc on supported platforms
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
jemallocator = "0.5"
|
||||
jemallocator = "0.5"
|
||||
|
@ -70,6 +70,7 @@ fn rocket() -> _ {
|
||||
queue_info::watch_circuit,
|
||||
queue_info::flow_stats,
|
||||
queue_info::packet_dump,
|
||||
queue_info::pcap,
|
||||
config_control::get_nic_list,
|
||||
config_control::get_current_python_config,
|
||||
config_control::get_current_lqosd_config,
|
||||
|
@ -2,9 +2,11 @@ use crate::auth_guard::AuthGuard;
|
||||
use crate::cache_control::NoCache;
|
||||
use crate::tracker::SHAPED_DEVICES;
|
||||
use lqos_bus::{bus_request, BusRequest, BusResponse, FlowTransport, PacketHeader};
|
||||
use rocket::http::Status;
|
||||
use rocket::response::content::RawJson;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket_download_response::DownloadResponse;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
@ -123,6 +125,17 @@ pub async fn packet_dump(ip: String, _auth: AuthGuard) -> NoCache<Json<Vec<Packe
|
||||
NoCache::new(Json(result))
|
||||
}
|
||||
|
||||
#[get("/api/pcap")]
|
||||
pub async fn pcap() -> Result<DownloadResponse, Status> {
|
||||
for r in bus_request(vec![BusRequest::GetPcapDump]).await.unwrap() {
|
||||
if let BusResponse::PcapDump(bytes) = r {
|
||||
return Ok(DownloadResponse::from_vec(bytes, Some("capture.pcap"), None));
|
||||
}
|
||||
}
|
||||
|
||||
Err(Status::NoContent)
|
||||
}
|
||||
|
||||
#[cfg(feature = "equinix_tests")]
|
||||
#[get("/api/run_btest")]
|
||||
pub async fn run_btest() -> NoCache<RawJson<String>> {
|
||||
|
@ -564,11 +564,12 @@
|
||||
for (let i=0; i<ips.length; ++i) {
|
||||
ip_list += ips[i] + ",";
|
||||
if (circuit_info != null) {
|
||||
ip_btns += "<a href='/ip_dump?ip=" + ips[i] + "&dn=" + circuit_info.capacity[0] + "&up=" + circuit_info.capacity[1] + "' class='btn btn-info'>Packet Dump: " + ips[i] + "</a>"
|
||||
ip_btns += "<a href='/ip_dump?ip=" + ips[i] + "&dn=" + circuit_info.capacity[0] + "&up=" + circuit_info.capacity[1] + "' class='btn btn-info'>Packet Dump: " + ips[i] + "</a> "
|
||||
}
|
||||
}
|
||||
ip_btns += "<br />";
|
||||
if (!madeButtons && ips.length > 0 && circuit_info != null) {
|
||||
ip_btns += "<a href='/api/pcap' class='btn btn-warning'>Download PCAP Dump</a>";
|
||||
ip_btns += "<br />";
|
||||
madeButtons = true;
|
||||
$("#packetButtons").html(ip_btns);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ struct heimdall_event {
|
||||
__u16 tcp_window;
|
||||
__u32 tsval;
|
||||
__u32 tsecr;
|
||||
__u8 dump[64];
|
||||
};
|
||||
|
||||
static __always_inline __u8 get_heimdall_mode()
|
||||
@ -99,6 +100,9 @@ static __always_inline void update_heimdall(struct dissector_t *dissector, __u32
|
||||
event.tcp_window = dissector->window;
|
||||
event.tsval = dissector->tsval;
|
||||
event.tsecr = dissector->tsecr;
|
||||
if (size > 64 && ((char *)dissector->start + 64 < dissector->end)) {
|
||||
__builtin_memcpy(&event.dump, dissector->start, 64);
|
||||
}
|
||||
long err = bpf_ringbuf_output(&heimdall_events, &event, sizeof(event), 0);
|
||||
if (err != 0) {
|
||||
bpf_debug("Failed to send perf event %d", err);
|
||||
|
@ -205,6 +205,9 @@ fn handle_bus_requests(
|
||||
BusResponse::Fail("Invalid IP".to_string())
|
||||
}
|
||||
}
|
||||
BusRequest::GetPcapDump => {
|
||||
BusResponse::PcapDump(lqos_heimdall::ten_second_pcap())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user