Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions dpd-client/tests/integration_tests/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,20 @@ use dpd_client::ClientInfo;
use dpd_client::types::Ipv4Entry;
use dpd_client::types::Ipv6Entry;

// Run a single drop test. This sends a packet that we expect to be dropped,
// and verifies that the expected drop counter is bumped by one. If the test
// runs to completion we return the counter evaluation as a boolean rather than
// an Error. This allows us to trigger the error in the test routine, which
// gives us a more specific test failure message.
async fn one_drop_test(
/// Process a single packet, asserting that a given counter is incremented by
/// one and a packet is appropriately transformed (or dropped).
///
/// If the test runs to completion we return the counter evaluation as a
/// boolean rather than an Error. This allows us to trigger the error in
/// the test routine, which gives us a more specific test failure message
pub async fn one_counted_test(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that this is somewhat similar to check_counter_incremented in https://github.com/oxidecomputer/dendrite/pull/362/changes#diff-892387f02bc3907dbe15941e9c76aecc0b8ca4b85d61f6a6130686aaba5b77ca

Which was moved from a helper that already exists in main. Could we easily achieve the same end with only one of these?

async fn check_counter_incremented(
switch: &Switch,
counter_name: &str,
baseline: u64,
expected_increment: u64,
client_name: Option<&str>,
) -> anyhow::Result<u64> {
let mut new_value = 0;
// Poll for the counter value (with timeout)
for _i in 0..20 {
std::thread::sleep(std::time::Duration::from_millis(100));
new_value =
switch.get_counter(counter_name, client_name).await.unwrap();
if new_value == baseline + expected_increment {
return Ok(new_value);
}
}
// Counter didn't increment as expected
Err(anyhow!(
"Counter '{}' expected to increase by {} (from {} to {}), but only reached {}",
counter_name,
expected_increment,
baseline,
baseline + expected_increment,
new_value
))
}

switch: &Switch,
port: PhysPort,
packet: packet::Packet,
packet: TestPacket,
packet_after: Option<TestPacket>,
counter: &str,
) -> anyhow::Result<bool> {
let send = TestPacket { packet: Arc::new(packet), port };

let old = switch.get_counter(counter, None).await?;
switch.packet_test(vec![send], Vec::new())?;
switch.packet_test(vec![packet], packet_after.into_iter().collect())?;

let mut new = 0;
for _i in 0..20 {
Expand All @@ -45,7 +44,7 @@ async fn one_drop_test(
// simulator and when that updated value is available to the SDE. To
// avoid a long pointless delay here, we try multiple times with a short
// sleep rather than once with a long sleep.
std::thread::sleep(std::time::Duration::from_millis(100));
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
new = switch.get_counter(counter, None).await?;
if old + 1 == new {
break;
Expand All @@ -55,6 +54,27 @@ async fn one_drop_test(
Ok(old + 1 == new)
}

/// Run a single drop test. This sends a packet that we expect to be dropped,
/// and verifies that the expected drop counter is bumped by one.
///
/// If the test runs to completion we return the counter evaluation as a
/// boolean rather than an Error. This allows us to trigger the error in
/// the test routine, which gives us a more specific test failure message.
async fn one_drop_test(
switch: &Switch,
port: PhysPort,
packet: packet::Packet,
counter: &str,
) -> anyhow::Result<bool> {
one_counted_test(
switch,
TestPacket { packet: Arc::new(packet), port },
None,
counter,
)
.await
}

// Add an IPv4 address to a port on the switch
async fn add_switch_ipv4(switch: &Switch, port: PhysPort, addr: &str) {
let (port_id, link_id) = switch.link_id(port).unwrap();
Expand Down
29 changes: 25 additions & 4 deletions dpd-client/tests/integration_tests/route_ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use reqwest::StatusCode;

use crate::integration_tests::common;
use crate::integration_tests::common::prelude::*;
use crate::integration_tests::counters::one_counted_test;
use packet::Endpoint;
use packet::eth::EthQHdr;

Expand Down Expand Up @@ -159,7 +160,12 @@ async fn test_deleted_route_ipv4_impl(switch: &Switch) -> TestResult {
common::set_icmp_unreachable(switch, &mut to_recv, ingress);
let expected = TestPacket { packet: Arc::new(to_recv), port: egress };

switch.packet_test(vec![send], vec![expected])
assert!(
one_counted_test(switch, send, Some(expected), "ipv4_unrouteable")
.await?
);

Ok(())
}

#[tokio::test]
Expand Down Expand Up @@ -262,7 +268,12 @@ async fn test_route_ipv4_unrouteable() -> TestResult {
common::set_icmp_unreachable(switch, &mut to_recv, ingress);
let expected = TestPacket { packet: Arc::new(to_recv), port: egress };

switch.packet_test(vec![send], vec![expected])
assert!(
one_counted_test(switch, send, Some(expected), "ipv4_unrouteable")
.await?
);

Ok(())
}

#[tokio::test]
Expand Down Expand Up @@ -934,6 +945,7 @@ async fn test_v4_over_v6() -> TestResult {
.expect_err("route should be gone after deleting its only target");

// Verify that packets to the deleted route produce ICMP unreachable.
// This must *also* increment the relevant drop counter.
let to_send = common::gen_udp_packet(
Endpoint::parse("e0:d5:5e:67:89:ab", "10.10.10.10", 3333).unwrap(),
Endpoint::parse("e0:d5:5e:67:89:ac", "10.10.10.11", 4444).unwrap(),
Expand All @@ -943,7 +955,11 @@ async fn test_v4_over_v6() -> TestResult {

let send = TestPacket { packet: Arc::new(to_send), port: ingress };
let expected = TestPacket { packet: Arc::new(to_recv), port: SERVICE_PORT };
switch.packet_test(vec![send], vec![expected])?;

assert!(
one_counted_test(switch, send, Some(expected), "ipv4_unrouteable")
.await?
);

// Re-add the route and verify forwarding works again.
common::set_route_ipv4_over_ipv6(
Expand Down Expand Up @@ -979,7 +995,12 @@ async fn test_v4_over_v6() -> TestResult {

let send = TestPacket { packet: Arc::new(to_send), port: ingress };
let expected = TestPacket { packet: Arc::new(to_recv), port: SERVICE_PORT };
switch.packet_test(vec![send], vec![expected])
assert!(
one_counted_test(switch, send, Some(expected), "ipv4_unrouteable")
.await?
);

Ok(())
}

/// Test that individual v4 and v6 targets can be deleted from a mixed
Expand Down
14 changes: 12 additions & 2 deletions dpd-client/tests/integration_tests/route_ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use packet::{Endpoint, ipv6, sidecar};

use crate::integration_tests::common;
use crate::integration_tests::common::prelude::*;
use crate::integration_tests::counters::one_counted_test;
use packet::eth::EthQHdr;

use dpd_client::types;
Expand Down Expand Up @@ -201,7 +202,12 @@ async fn test_deleted_unicast_impl(switch: &Switch) -> TestResult {

common::set_icmp6_unreachable(switch, &mut to_recv, ingress);
let expected = TestPacket { packet: Arc::new(to_recv), port: egress };
switch.packet_test(vec![send], vec![expected])
assert!(
one_counted_test(switch, send, Some(expected), "ipv6_unrouteable")
.await?
);

Ok(())
}

#[tokio::test]
Expand Down Expand Up @@ -279,8 +285,12 @@ async fn test_unrouteable() -> TestResult {

common::set_icmp6_unreachable(switch, &mut to_recv, ingress);
let expected = TestPacket { packet: Arc::new(to_recv), port: egress };
assert!(
one_counted_test(switch, send, Some(expected), "ipv6_unrouteable")
.await?
);

switch.packet_test(vec![send], vec![expected])
Ok(())
}

// Define two routes, one with a 56-bit prefix and one with a 64-bit prefix.
Expand Down
12 changes: 9 additions & 3 deletions dpd/p4/sidecar.p4
Original file line number Diff line number Diff line change
Expand Up @@ -2187,11 +2187,17 @@ control Ingress(
}
}

if (meta.dropped) {
// Handle dropped packets
ig_dprsr_md.drop_ctl = 1;
// Drops recorded in the L3 router need to be counted, but
// we do not convert them to _actual_ packet drops in the
// deparser. We forward such packets to the host to generate an
// ICMP response in our stead.
if (meta.drop_reason != 0) {
drop_port_ctr.count(ig_intr_md.ingress_port);
drop_reason_ctr.count(meta.drop_reason);
}

if (meta.dropped) {
ig_dprsr_md.drop_ctl = 1;
Comment on lines +2197 to +2200

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these are all the cases this affects. There's some duplication for the multicast blocks. Is it worth adding checks for the TTL, ARP, and NDP cases?

dendrite/dpd/p4/sidecar.p4

Lines 1141 to 1143 in 9ffd5a1

meta.drop_reason = DROP_ARP_MISS;
// Dont set meta.dropped because we want an error packet
// to go up to the switch.

dendrite/dpd/p4/sidecar.p4

Lines 1189 to 1191 in 9ffd5a1

meta.drop_reason = DROP_NDP_MISS;
// Dont set meta.dropped because we want an error packet
// to go up to the switch.

dendrite/dpd/p4/sidecar.p4

Lines 1247 to 1249 in 9ffd5a1

// Dont set meta.dropped because we want an error packet
// to go out.
meta.drop_reason = DROP_IPV4_UNROUTEABLE;

dendrite/dpd/p4/sidecar.p4

Lines 1252 to 1254 in 9ffd5a1

// Dont set meta.dropped because we want an error packet
// to go out.
meta.drop_reason = DROP_IPV4_TTL_EXCEEDED;

dendrite/dpd/p4/sidecar.p4

Lines 1329 to 1331 in 9ffd5a1

meta.drop_reason = DROP_IPV4_UNROUTEABLE;
// Dont set meta.dropped because we want an error packet
// to go out.

dendrite/dpd/p4/sidecar.p4

Lines 1334 to 1336 in 9ffd5a1

meta.drop_reason = DROP_IPV4_TTL_INVALID;
// Dont set meta.dropped because we want an error packet
// to go out.

dendrite/dpd/p4/sidecar.p4

Lines 1386 to 1388 in 9ffd5a1

meta.drop_reason = DROP_IPV6_UNROUTEABLE;
// Dont set meta.dropped because we want an error packet
// to go out.

dendrite/dpd/p4/sidecar.p4

Lines 1391 to 1393 in 9ffd5a1

meta.drop_reason = DROP_IPV6_TTL_EXCEEDED;
// Dont set meta.dropped because we want an error packet
// to go out.

dendrite/dpd/p4/sidecar.p4

Lines 1465 to 1467 in 9ffd5a1

meta.drop_reason = DROP_IPV6_UNROUTEABLE;
// Dont set meta.dropped because we want an error packet
// to go out.

dendrite/dpd/p4/sidecar.p4

Lines 1470 to 1472 in 9ffd5a1

meta.drop_reason = DROP_IPV6_TTL_EXCEEDED;
// Dont set meta.dropped because we want an error packet
// to go out.

} else if (!meta.is_mcast) {
egress_ctr.count(ig_tm_md.ucast_egress_port);
if (ig_tm_md.ucast_egress_port != USER_SPACE_SERVICE_PORT) {
Expand Down
Loading