#[cfg(all(feature = "tokio", feature = "http"))]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use flowscope::http::{HttpMessage, HttpParser};
use futures::StreamExt;
use netring::flow::SessionEvent;
use netring::flow::extract::FiveTuple;
use netring::{AsyncCapture, BpfFilter};
use std::time::{Duration, Instant};
let iface = std::env::args().nth(1).unwrap_or_else(|| "lo".into());
let seconds: u64 = std::env::args()
.nth(2)
.and_then(|s| s.parse().ok())
.unwrap_or(30);
let filter = BpfFilter::builder().tcp().ports([80, 8080]).build()?;
eprintln!(
"[http] watching {iface} for {seconds}s (TCP/80, TCP/8080)\n\
BPF filter: {} instructions",
filter.len()
);
let cap = AsyncCapture::open_with_filter(&iface, filter)?;
let mut stream = cap
.flow_stream(FiveTuple::bidirectional())
.session_stream(HttpParser::default());
let deadline = Instant::now() + Duration::from_secs(seconds);
let mut req_count = 0u64;
let mut resp_count = 0u64;
let mut flow_count = 0u64;
while Instant::now() < deadline
&& let Some(evt) = stream.next().await
{
match evt? {
SessionEvent::Started { key, .. } => {
flow_count += 1;
println!("+ flow {a} <-> {b}", a = key.a, b = key.b);
}
SessionEvent::Application { message, .. } => match message {
HttpMessage::Request(req) => {
req_count += 1;
println!(
" → {method} {path} {ver:?}",
method = String::from_utf8_lossy(&req.method),
path = String::from_utf8_lossy(&req.path),
ver = req.version
);
}
HttpMessage::Response(resp) => {
resp_count += 1;
println!(
" ← {status} {reason} {len} bytes",
status = resp.status,
reason = String::from_utf8_lossy(&resp.reason),
len = resp.body.len()
);
}
_ => {}
},
SessionEvent::Closed { key, reason, .. } => {
println!("- flow {a} <-> {b} {reason:?}", a = key.a, b = key.b);
}
SessionEvent::FlowAnomaly { kind, .. } => {
eprintln!("! flow anomaly: {kind:?}");
}
SessionEvent::TrackerAnomaly { kind, .. } => {
eprintln!("! tracker anomaly: {kind:?}");
}
_ => {}
}
}
eprintln!("[done] {flow_count} flows seen, {req_count} requests, {resp_count} responses");
Ok(())
}
#[cfg(not(all(feature = "tokio", feature = "http")))]
fn main() {
eprintln!("Build with --features tokio,http");
}