blob: a244065fba8d54b53e4b9a067f15997a88808e55 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::{format_err, Error},
fuchsia_async as fasync,
helper::*,
net_types::ip::IpVersion,
std::net::{SocketAddr, UdpSocket},
};
const PACKET_COUNT: usize = 10;
const PAYLOAD_SIZE: usize = 100; // octets.
const BIG_PAYLOAD_SIZE: usize = 1000; // octets.
fn main() -> Result<(), Error> {
// `filter_integration` is too long for endpoint_names.
const TEST_NAME: &str = "filter";
let mut env = TestEnvironment::new(fasync::Executor::new()?, TEST_NAME)?;
test_case!(timeout_test, &mut env);
test_case!(bad_filters_test, &mut env);
test_case!(positive_filter_test, &mut env);
test_case!(negative_filter_test, &mut env);
test_case!(long_filter_string_test, &mut env);
Ok(())
}
// A test that specifying a timeout causes netdump to quit by itself.
// The timeout is only intended to be best-effort. Capture exiting successfully
// is the only criterion for the test to pass.
fn timeout_test(env: &mut TestEnvironment) -> Result<(), Error> {
let mut args = env.new_args(EndpointType::RX).insert_timeout(0);
let mut output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_ok());
args = env.new_args(EndpointType::RX).insert_timeout(1);
output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_ok());
Ok(())
}
// A test of bad filter strings where packet capture exits immediately.
// Not intending to test every syntax error as that should be unit tested.
fn bad_filters_test(env: &mut TestEnvironment) -> Result<(), Error> {
// Empty filter string.
let mut args = env.new_args(EndpointType::RX).insert_filter("");
let mut output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_err());
// Invalid keyword.
args = env.new_args(EndpointType::RX).insert_filter("this is magic");
output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_err());
// Invalid parenthesis.
args = env.new_args(EndpointType::RX).insert_filter("udp and ( ip4 tcp port 80");
output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_err());
// Invalid port name.
args = env.new_args(EndpointType::RX).insert_filter("udp and ip4 tcp port htp");
output = env.run_test_case_no_packets(args.into())?;
assert!(output.ok().is_err());
Ok(())
}
// Test the effect of a positive filter (no leading "not") on packet capture.
// Should expect only the packets accepted by the filter are captured.
fn positive_filter_test(env: &mut TestEnvironment) -> Result<(), Error> {
let args = env
.new_args(EndpointType::RX)
.insert_packet_count(PACKET_COUNT)
.insert_write_to_dumpfile(DEFAULT_DUMPFILE)
.insert_filter(&format!("greater {}", BIG_PAYLOAD_SIZE));
let socket_tx = UdpSocket::bind(EndpointType::TX.default_socket_addr(IpVersion::V4))?;
let send_packets_fut = async move {
// Interleave sending of big and small packets.
for _ in 0..PACKET_COUNT {
send_udp_packets(
socket_tx.try_clone()?,
EndpointType::RX.default_socket_addr(IpVersion::V4),
PAYLOAD_SIZE,
1,
)
.await?;
send_udp_packets(
socket_tx.try_clone()?,
EndpointType::RX.default_socket_addr(IpVersion::V4),
BIG_PAYLOAD_SIZE,
1,
)
.await?;
}
Ok(())
};
let output = env.run_test_case(args.into(), send_packets_fut, DEFAULT_DUMPFILE)?;
output.ok()?;
let output_stdout = output_string(&output.stdout);
// Obtained headers.
let packets: Vec<String> = output_stdout.lines().map(String::from).collect();
assert_eq!(packets.len(), PACKET_COUNT);
// Expected data.
let exp_length = format!("{}", 20 + 8 + BIG_PAYLOAD_SIZE); // IPv4 + UDP + Payload.
for packet in packets {
if !packet.contains(&exp_length) {
return Err(format_err!("Unfiltered packet found! {}", packet));
}
}
Ok(())
}
// Test the effect of a negative filter in packet capture.
// Should expect only the packets accepted by the filter are captured.
fn negative_filter_test(env: &mut TestEnvironment) -> Result<(), Error> {
const ALT_PORT: u16 = 3333;
let args = env
.new_args(EndpointType::RX)
.insert_packet_count(PACKET_COUNT)
.insert_write_to_dumpfile(DEFAULT_DUMPFILE)
.insert_filter(&format!("not port {}", EndpointType::TX.default_port()));
let socket_tx = UdpSocket::bind(EndpointType::TX.default_socket_addr(IpVersion::V4))?;
let alt_socket_addr =
SocketAddr::new(EndpointType::TX.default_ip_addr(IpVersion::V4), ALT_PORT);
let alt_socket_tx = UdpSocket::bind(alt_socket_addr)?;
let send_packets_fut = async move {
// Interleave sending from different ports.
for _ in 0..PACKET_COUNT {
send_udp_packets(
socket_tx.try_clone()?,
EndpointType::RX.default_socket_addr(IpVersion::V4),
PAYLOAD_SIZE,
1,
)
.await?;
send_udp_packets(
alt_socket_tx.try_clone()?,
EndpointType::RX.default_socket_addr(IpVersion::V4),
PAYLOAD_SIZE,
1,
)
.await?;
}
Ok(())
};
let output = env.run_test_case(args.into(), send_packets_fut, DEFAULT_DUMPFILE)?;
output.ok()?;
let output_stdout = output_string(&output.stdout);
// Obtained headers.
let packets: Vec<String> = output_stdout.lines().map(String::from).collect();
assert_eq!(packets.len(), PACKET_COUNT);
for packet in packets {
if packet.contains(&EndpointType::TX.default_port().to_string()) {
return Err(format_err!("Unfiltered packet found! {}", packet));
}
}
Ok(())
}
// Test that a long filter string works.
// Only testing a subcomponent of the filter.
fn long_filter_string_test(env: &mut TestEnvironment) -> Result<(), Error> {
const ALT_PORT: u16 = 2345;
const FILTER_STR: &str = "not ( port 65026,65268,ssh or dst port 8083 or ip6 dst port \
33330-33341 or proto udp dst port 2345 or ip4 udp dst port 1900 )";
let args = env
.new_args(EndpointType::RX)
.insert_packet_count(PACKET_COUNT)
.insert_write_to_dumpfile(DEFAULT_DUMPFILE)
.insert_filter(FILTER_STR);
let socket_tx = UdpSocket::bind(EndpointType::TX.default_socket_addr(IpVersion::V4))?;
let alt_socket_addr =
SocketAddr::new(EndpointType::RX.default_ip_addr(IpVersion::V4), ALT_PORT);
let send_packets_fut = async move {
// Interleave sending from different ports.
for _ in 0..PACKET_COUNT {
send_udp_packets(
socket_tx.try_clone()?,
EndpointType::RX.default_socket_addr(IpVersion::V4),
PAYLOAD_SIZE,
1,
)
.await?;
send_udp_packets(socket_tx.try_clone()?, alt_socket_addr, PAYLOAD_SIZE, 1).await?;
}
Ok(())
};
let output = env.run_test_case(args.into(), send_packets_fut, DEFAULT_DUMPFILE)?;
output.ok()?;
let output_stdout = output_string(&output.stdout);
// Obtained headers.
let packets: Vec<String> = output_stdout.lines().map(String::from).collect();
assert_eq!(packets.len(), PACKET_COUNT);
for packet in packets {
if packet.contains("2345") {
return Err(format_err!("Unfiltered packet found! {}", packet));
}
}
Ok(())
}