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
19 changes: 11 additions & 8 deletions .github/buildomat/common.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#!/bin/bash

# The tofino2 has 20 stages, and the current sidecar.p4 needs all 20 of them.
# Specifying the number of stages isn't strictly necessary, but it allows us to
# track when we exceed the current ceiling. The underlying intention is to grow
# deliberately and thoughtfully, given the limited space on the ASIC.
# The tofino2 has 20 stages. The base sidecar.p4 builds at 16 stages, and,
# with multicast enabled, at 19. Specifying the number of stages isn't
# strictly necessary, but it allows us to track when we exceed the current
# ceiling. The underlying intention is to grow deliberately and thoughtfully,
# given the limited space on the ASIC.
#
# Note: this now seems silly since we have maxed out the number of stages, but
# we want to leave this check and note in place should we ever find a way to
# reduce our footprint below 20 stages.
TOFINO_STAGES=20
# Note: p4c does multiple placement rounds. table_summary.log reports each
# round. The first (unconstrained) is informational, and the final (at the
# bottom of the log) is what the binary actually uses. If
# --num-stages-override cannot be satisfied, the assembler errors out and
# no binary is produced.
TOFINO_STAGES=16

# These describe which version of the SDE to download and where to find it
SDE_COMMIT=2a6b33211c9675996dcb99fe939045506667ae94
Expand Down
8 changes: 7 additions & 1 deletion .github/buildomat/packet-test-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ if [ x$MULTICAST == x ]; then
CODEGEN_FEATURES=--multicast
SWADM_FEATURES=--features=multicast
fi


if [ x$MULTICAST == x ]; then
TOFINO_STAGES=16
else
TOFINO_STAGES=19
fi

function cleanup {
set +o errexit
set +o pipefail
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ To build the p4 program:
$ cargo xtask codegen [ -n <p4 program name> ] [--sde <sde directory> ]
```

*Note*: The final stage count for the binary is reported in
`target/proto/opt/oxide/dendrite/sidecar/pipe/logs/table_summary.log`.
If the requested stage allotment cannot be met, the assembler errors
out and no binary is produced.

The Tofino model is not yet available for `illumos`/`helios`. To run the
compiled p4 program on the Tofino model on a Linux system:

Expand Down
70 changes: 66 additions & 4 deletions asic/src/softnpu/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ impl TableOps<Handle> for Table {
trace!(hdl.log, "match_data:\n{:#?}", match_data);
trace!(hdl.log, "action_data:\n{:#?}", action_data);

// Route tables are idx-only in sidecar-lite and route_ttl_is_1
// is ignored here.
//
// TODO: remove compat once https://github.com/oxidecomputer/sidecar-lite/pull/152
// is merged and sidecar-lite updates route keys/actions accordingly.
let is_route_table = matches!(
self.type_,
TableType::RouteFwdIpv4 | TableType::RouteFwdIpv6
);
if is_route_table {
if route_ttl_is_1(&match_data.fields) {
trace!(hdl.log, "skipping ttl==1 route entry for {name}");
return Ok(());
}
if action_data.action == "ttl_exceeded" {
trace!(hdl.log, "skipping ttl_exceeded action for {name}");
return Ok(());
}
}

let keyset_data = keyset_data(match_data.fields, self.type_);

let (action, parameter_data) = match (
Expand Down Expand Up @@ -428,6 +448,22 @@ impl TableOps<Handle> for Table {
}
("rewrite", params)
}
#[cfg(feature = "multicast")]
(TableType::PortMacAddressMcast, "rewrite") => {
let mut params = Vec::new();
for arg in action_data.args {
match arg.value {
ValueTypes::U64(v) => {
let mac = v.to_le_bytes();
params.extend_from_slice(&mac[0..6]);
}
ValueTypes::Ptr(v) => {
params.extend_from_slice(v.as_slice());
}
}
}
("rewrite", params)
}
(TableType::NatIngressIpv4, "forward_ipv4_to")
| (TableType::NatIngressIpv6, "forward_ipv6_to")
| (TableType::AttachedSubnetIpv4, "forward_to_v4")
Expand Down Expand Up @@ -573,6 +609,15 @@ impl TableOps<Handle> for Table {
trace!(hdl.log, "table: {name}");
trace!(hdl.log, "match_data:\n{:#?}", match_data);

let is_route_table = matches!(
self.type_,
TableType::RouteFwdIpv4 | TableType::RouteFwdIpv6
);
if is_route_table && route_ttl_is_1(&match_data.fields) {
trace!(hdl.log, "skipping ttl==1 route entry delete for {name}");
return Ok(());
}

let keyset_data = keyset_data(match_data.fields, self.type_);

trace!(hdl.log, "sending request to softnpu");
Expand Down Expand Up @@ -632,10 +677,12 @@ fn keyset_data(match_data: Vec<MatchEntryField>, table: TableType) -> Vec<u8> {
serialize_value_type(&x, &mut data);
keyset_data.extend_from_slice(&data[..2]);
}
TableType::RouteIdxIpv4 => {
// "idx" => exact => bit<16>
serialize_value_type(&x, &mut data);
keyset_data.extend_from_slice(&data[..2]);
TableType::RouteFwdIpv4 | TableType::RouteFwdIpv6 => {
// sidecar-lite route keys are idx-only.
if m.name == "idx" {
serialize_value_type(&x, &mut data);
keyset_data.extend_from_slice(&data[..2]);
}
}
TableType::NatIngressIpv4 => {
// "dst_addr" => hdr.ipv4.dst: exact => bit<32>
Expand Down Expand Up @@ -725,3 +772,18 @@ fn serialize_value_type_be(x: &ValueTypes, data: &mut Vec<u8>) {
}
}
}

fn route_ttl_is_1(fields: &[MatchEntryField]) -> bool {
fields.iter().any(|field| {
if field.name != "route_ttl_is_1" {
return false;
}
match &field.value {
MatchEntryValue::Value(ValueTypes::U64(v)) => *v != 0,
MatchEntryValue::Value(ValueTypes::Ptr(v)) => {
v.first().is_some_and(|b| *b != 0)
}
_ => false,
}
})
}
31 changes: 16 additions & 15 deletions asic/src/tofino_common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ pub mod ports;
fn table_name(type_: TableType) -> &'static str {
match type_ {
TableType::RouteIdxIpv4 => {
"pipe.Ingress.l3_router.Router4.lookup_idx.lookup"
"pipe.Ingress.l3_router.router4.lookup_idx.lookup"
}
TableType::RouteFwdIpv4 => {
"pipe.Ingress.l3_router.Router4.lookup_idx.route"
"pipe.Ingress.l3_router.router4.lookup_idx.route"
}
TableType::RouteIdxIpv6 => {
"pipe.Ingress.l3_router.Router6.lookup_idx.lookup"
"pipe.Ingress.l3_router.router6.lookup_idx.lookup"
}
TableType::RouteFwdIpv6 => {
"pipe.Ingress.l3_router.Router6.lookup_idx.route"
"pipe.Ingress.l3_router.router6.lookup_idx.route"
}
#[cfg(feature = "multicast")]
TableType::RouteIpv4Mcast => {
Expand All @@ -45,13 +45,15 @@ fn table_name(type_: TableType) -> &'static str {
}
TableType::ArpIpv4 => "pipe.Ingress.l3_router.Arp.tbl",
TableType::NeighborIpv6 => "pipe.Ingress.l3_router.Ndp.tbl",
TableType::PortMacAddress => "pipe.Ingress.mac_rewrite.mac_rewrite",
TableType::PortMacAddress => {
"pipe.Egress.unicast_mac_rewrite.mac_rewrite"
}
TableType::PortAddrIpv4 => "pipe.Ingress.filter.switch_ipv4_addr",
TableType::PortAddrIpv6 => "pipe.Ingress.filter.switch_ipv6_addr",
TableType::NatIngressIpv4 => "pipe.Ingress.nat_ingress.ingress_ipv4",
TableType::NatIngressIpv6 => "pipe.Ingress.nat_ingress.ingress_ipv6",
TableType::UplinkIngress => "pipe.Ingress.filter.uplink_ports",
TableType::UplinkEgress => "pipe.Ingress.egress_filter.egress_filter",
TableType::UplinkEgress => "pipe.Egress.egress_filter.egress_filter",
TableType::AttachedSubnetIpv4 => {
"pipe.Ingress.attached_subnet_ingress.attached_subnets_v4"
}
Expand Down Expand Up @@ -79,7 +81,9 @@ fn table_name(type_: TableType) -> &'static str {
"pipe.Ingress.nat_ingress.ingress_ipv6_mcast"
}
#[cfg(feature = "multicast")]
TableType::PortMacAddressMcast => "pipe.Egress.mac_rewrite.mac_rewrite",
TableType::PortMacAddressMcast => {
"pipe.Egress.mcast_mac_rewrite.mac_rewrite"
}
#[cfg(feature = "multicast")]
TableType::McastEgressDecapPorts => {
"pipe.Egress.mcast_egress.tbl_decap_ports"
Expand All @@ -97,9 +101,13 @@ fn counter_table_name(id: CounterId) -> &'static str {
CounterId::Service => "pipe.Ingress.services.service_ctr",
CounterId::Ingress => "pipe.Ingress.ingress_ctr",
CounterId::Packet => "pipe.Ingress.packet_ctr",
CounterId::Egress => "pipe.Ingress.egress_ctr",
CounterId::DropPort => "pipe.Ingress.drop_port_ctr",
CounterId::DropReason => "pipe.Ingress.drop_reason_ctr",
CounterId::Forwarded => "pipe.Egress.forwarded_ctr",
CounterId::Unicast => "pipe.Egress.unicast_ctr",
CounterId::MulticastLL => "pipe.Egress.link_local_mcast_ctr",
CounterId::EgressDropPort => "pipe.Egress.drop_port_ctr",
CounterId::EgressDropReason => "pipe.Egress.drop_reason_ctr",
#[cfg(feature = "multicast")]
CounterId::Multicast(id) => mulitcast_counter_table_name(id),
}
Expand All @@ -108,16 +116,9 @@ fn counter_table_name(id: CounterId) -> &'static str {
#[cfg(feature = "multicast")]
fn mulitcast_counter_table_name(id: MulticastCounterId) -> &'static str {
match id {
MulticastCounterId::EgressDropPort => "pipe.Egress.drop_port_ctr",
MulticastCounterId::EgressDropReason => "pipe.Egress.drop_reason_ctr",
MulticastCounterId::Unicast => "pipe.Egress.unicast_ctr",
MulticastCounterId::Multicast => "pipe.Egress.mcast_ctr",
MulticastCounterId::MulticastExt => "pipe.Egress.external_mcast_ctr",
MulticastCounterId::MulticastLL => "pipe.Egress.link_local_mcast_ctr",
MulticastCounterId::MulticastUL => "pipe.Egress.underlay_mcast_ctr",
MulticastCounterId::MulticastDrop => {
"pipe.Ingress.filter.drop_mcast_ctr"
}
}
}

Expand Down
46 changes: 18 additions & 28 deletions common/src/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,16 @@ pub struct FecRSCounters {
pub enum CounterId {
Service,
Ingress,
Egress,
Packet,
DropPort,
DropReason,
Forwarded,
Unicast,
/// Link-local IPv6 multicast (ff02::/16). Not feature-gated because
/// link-local forwarding uses standard routing, not replication groups.
MulticastLL,
EgressDropPort,
EgressDropReason,
#[cfg(feature = "multicast")]
Multicast(MulticastCounterId),
}
Expand All @@ -227,14 +233,9 @@ pub enum CounterId {
)]
#[cfg(feature = "multicast")]
pub enum MulticastCounterId {
EgressDropPort,
EgressDropReason,
Unicast,
Multicast,
MulticastExt,
MulticastLL,
MulticastUL,
MulticastDrop,
}

impl fmt::Display for CounterId {
Expand All @@ -245,10 +246,14 @@ impl fmt::Display for CounterId {
match self {
CounterId::Service => "Service".to_string(),
CounterId::Ingress => "Ingress".to_string(),
CounterId::Egress => "Egress".to_string(),
CounterId::Packet => "Packet".to_string(),
CounterId::DropPort => "Ingress_Drop_Port".to_string(),
CounterId::DropReason => "Ingress_Drop_Reason".to_string(),
CounterId::Forwarded => "Forwarded".to_string(),
CounterId::Unicast => "Unicast".to_string(),
CounterId::MulticastLL => "Multicast_Link_Local".to_string(),
CounterId::EgressDropPort => "Egress_Drop_Port".to_string(),
CounterId::EgressDropReason => "Egress_Drop_Reason".to_string(),
#[cfg(feature = "multicast")]
CounterId::Multicast(id) => id.to_string(),
}
Expand All @@ -263,58 +268,43 @@ impl std::str::FromStr for CounterId {
match s.to_lowercase().replace(['_'], "").as_str() {
"service" => Ok(CounterId::Service),
"ingress" => Ok(CounterId::Ingress),
"egress" => Ok(CounterId::Egress),
"packet" => Ok(CounterId::Packet),
"ingressdropport" => Ok(CounterId::DropPort),
"ingressdropreason" => Ok(CounterId::DropReason),
"forwarded" => Ok(CounterId::Forwarded),
"unicast" => Ok(CounterId::Unicast),
"multicastll" | "multicastlinklocal" => Ok(CounterId::MulticastLL),
"egressdropport" => Ok(CounterId::EgressDropPort),
"egressdropreason" => Ok(CounterId::EgressDropReason),
#[cfg(feature = "multicast")]
x => match x {
"egressdropport" => {
Ok(CounterId::Multicast(MulticastCounterId::EgressDropPort))
}
"egressdropreason" => Ok(CounterId::Multicast(
MulticastCounterId::EgressDropReason,
)),
"unicast" => {
Ok(CounterId::Multicast(MulticastCounterId::Unicast))
}
"multicast" => {
Ok(CounterId::Multicast(MulticastCounterId::Multicast))
}
"multicastext" | "multicastexternal" => {
Ok(CounterId::Multicast(MulticastCounterId::MulticastExt))
}
"multicastll" | "multicastlinklocal" => {
Ok(CounterId::Multicast(MulticastCounterId::MulticastLL))
}
"multicastul" | "multicastunderlay" => {
Ok(CounterId::Multicast(MulticastCounterId::MulticastUL))
}
"multicastdrop" => {
Ok(CounterId::Multicast(MulticastCounterId::MulticastDrop))
}
x => Err(format!("No such counter: {x}")),
},
#[cfg(not(feature = "multicast"))]
x => Err(format!("No such counter: {x}")),
}
}
}

#[cfg(feature = "multicast")]
impl fmt::Display for MulticastCounterId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
MulticastCounterId::EgressDropPort => "Egress_Drop_Port",
MulticastCounterId::EgressDropReason => "Egress_Drop_Reason",
MulticastCounterId::Unicast => "Unicast",
MulticastCounterId::Multicast => "Multicast",
MulticastCounterId::MulticastExt => "Multicast_External",
MulticastCounterId::MulticastLL => "Multicast_Link_Local",
MulticastCounterId::MulticastUL => "Multicast_Underlay",
MulticastCounterId::MulticastDrop => "Multicast_Drop",
}
)
}
Expand Down
2 changes: 1 addition & 1 deletion common/src/illumos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/
//
// Copyright 2025 Oxide Computer Company
// Copyright 2026 Oxide Computer Company

//! Illumos-specific common modules and operations.

Expand Down
7 changes: 6 additions & 1 deletion dpd-client/tests/integration_tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const SHOW_HEX: u8 = 0x02;

/// Admin-local IPv6 multicast prefix (ff04::/16, scope 4).
pub const ADMIN_LOCAL_MULTICAST_PREFIX: u16 = 0xFF04;
pub const DEFAULT_TEST_TAG: &str = "test";
pub const NON_MATCHING_TEST_TAG: &str = "failed";

// Timeout set on `Pcap` objects.
//
Expand Down Expand Up @@ -254,7 +256,8 @@ impl Switch {
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).build().fuse();
let log = slog::Logger::root(drain, slog::o!());
let client_state = ClientState { tag: String::from("test"), log };
let client_state =
ClientState { tag: String::from(DEFAULT_TEST_TAG), log };
let client = Client::new(
&format!("http://{ctrl_host}:{ctrl_port}"),
client_state,
Expand Down Expand Up @@ -1443,7 +1446,9 @@ pub fn gen_arp_reply(src: Endpoint, tgt: Endpoint) -> Packet {

pub mod prelude {
pub use super::ADMIN_LOCAL_MULTICAST_PREFIX;
pub use super::DEFAULT_TEST_TAG;
pub use super::NO_PORT;
pub use super::NON_MATCHING_TEST_TAG;
pub use super::PhysPort;
pub use super::SERVICE_PORT;
pub use super::Switch;
Expand Down
Loading