diff --git a/aal/src/lib.rs b/aal/src/lib.rs index 8106d293..a1a01388 100644 --- a/aal/src/lib.rs +++ b/aal/src/lib.rs @@ -12,6 +12,7 @@ use serde::Serialize; use thiserror::Error; use common::ports::{PortFec, PortMedia, PortPrbsMode, PortSpeed, TxEq}; +use common::table::TableType; mod fuse; pub use fuse::*; @@ -264,7 +265,7 @@ pub trait AsicOps { /// the intermediate representation to the ASIC-specific format expected by the /// underlying hardware or emulator. pub trait TableOps { - fn new(hdl: &H, name: &str) -> AsicResult + fn new(hdl: &H, type_: TableType) -> AsicResult where Self: Sized; diff --git a/asic/Cargo.toml b/asic/Cargo.toml index 39dc3cbe..f4965feb 100644 --- a/asic/Cargo.toml +++ b/asic/Cargo.toml @@ -13,7 +13,7 @@ tofino_asic = [ tofino_stub = [] softnpu = ["softnpu-lib", "dep:propolis"] chaos = [] -multicast = ["aal/multicast"] +multicast = ["aal/multicast", "common/multicast"] [lib] # The genpd.rs code generated by bindgen causes the doctest to fail diff --git a/asic/src/chaos/mod.rs b/asic/src/chaos/mod.rs index d06ee84c..53385fee 100644 --- a/asic/src/chaos/mod.rs +++ b/asic/src/chaos/mod.rs @@ -18,6 +18,7 @@ use aal::{ SidecarIdentifiers, }; use common::ports::{PortFec, PortId, PortMedia, PortPrbsMode, PortSpeed}; +use common::table::TableType; use crate::Identifiers; pub use crate::faux_fsm::FsmState; @@ -68,7 +69,7 @@ impl Chaos { #[derive(Default, Debug, Serialize, Deserialize, Clone)] pub struct TableChaos { /// Track a set of chaos probabilities keyed by strings. - pub values: HashMap, + pub values: HashMap, } /// A convenience function for creating chaos tables. @@ -84,31 +85,54 @@ macro_rules! table_chaos { } impl TableChaos { + fn add_tables(&mut self, ids: Vec, prob: f64) { + for id in ids { + self.values.insert(id, prob); + } + } + /// Create a new chaos table with all known dendrite table identifiers, /// assigning each a uniform chaos value. pub fn uniform(v: f64) -> Self { - table_chaos!( - (table::ROUTE_IPV4, v), - (table::ROUTE_IPV6, v), - (table::ARP_IPV4, v), - (table::NEIGHBOR_IPV6, v), - (table::MAC_REWRITE, v), - (table::SWITCH_IPV4_ADDR, v), - (table::SWITCH_IPV6_ADDR, v), - (table::NAT_INGRESS_IPV4, v), - (table::NAT_INGRESS_IPV6, v), - (table::MCAST_NAT_INGRESS_IPV4, v), - (table::MCAST_NAT_INGRESS_IPV6, v), - (table::MCAST_REPLICATION_IPV4, v), - (table::MCAST_REPLICATION_IPV6, v), - (table::MCAST_SRC_FILTER_IPV4, v), - (table::MCAST_SRC_FILTER_IPV6, v), - (table::MCAST_ROUTE_IPV4, v), - (table::MCAST_ROUTE_IPV6, v), - (table::MCAST_MAC_REWRITE, v), - (table::MCAST_DECAP_PORTS, v), - (table::MCAST_PORT_ID_MAPPING, v) - ) + let mut tc = TableChaos { values: HashMap::new() }; + + tc.add_tables( + vec![ + TableType::RouteIdxIpv4, + TableType::RouteFwdIpv4, + TableType::RouteIdxIpv6, + TableType::RouteFwdIpv6, + TableType::ArpIpv4, + TableType::NeighborIpv6, + TableType::PortMacAddress, + TableType::PortAddrIpv4, + TableType::PortAddrIpv6, + TableType::NatIngressIpv4, + TableType::NatIngressIpv6, + TableType::UplinkIngress, + TableType::UplinkEgress, + TableType::AttachedSubnetIpv4, + TableType::AttachedSubnetIpv6, + ], + v, + ); + #[cfg(feature = "multicast")] + tc.add_tables( + vec![ + TableType::RouteIpv4Mcast, + TableType::RouteIpv6Mcast, + TableType::McastIpv6, + TableType::McastIpv4SrcFilter, + TableType::McastIpv6SrcFilter, + TableType::NatIngressIpv4Mcast, + TableType::NatIngressIpv6Mcast, + TableType::PortMacAddressMcast, + TableType::McastEgressDecapPorts, + TableType::McastEgressPortMapping, + ], + v, + ); + tc } /// Return a chaos error according to the underlying probability value for @@ -116,10 +140,10 @@ impl TableChaos { pub fn unfurled( &self, log: &Logger, - id: &str, + id: TableType, message: &str, ) -> AsicResult<()> { - if let Some(value) = self.values.get(id) + if let Some(value) = self.values.get(&id) && *value >= random() { slog::error!(log, "chaos table error: {}", message); diff --git a/asic/src/chaos/table.rs b/asic/src/chaos/table.rs index 1cb7030d..36515270 100644 --- a/asic/src/chaos/table.rs +++ b/asic/src/chaos/table.rs @@ -14,49 +14,17 @@ use crate::chaos::{Handle, table_unfurl}; use aal::{ ActionParse, AsicError, AsicResult, CounterData, MatchParse, TableOps, }; - -// These names line up with the table names in the sidecar P4 program. -pub const ROUTE_IPV4: &str = "pipe.Ingress.l3_router.routes_ipv4"; -pub const ROUTE_IPV6: &str = "pipe.Ingress.l3_router.routes_ipv6"; -pub const ARP_IPV4: &str = "pipe.Ingress.l3_router.arp_ipv4"; -pub const NEIGHBOR_IPV6: &str = "pipe.Ingress.l3_router.neighbor_ipv6"; -pub const MAC_REWRITE: &str = "pipe.Ingress.mac_rewrite.mac_rewrite"; -pub const SWITCH_IPV4_ADDR: &str = "pipe.Ingress.filter.switch_ipv4_addr"; -pub const SWITCH_IPV6_ADDR: &str = "pipe.Ingress.filter.switch_ipv6_addr"; -pub const NAT_INGRESS_IPV4: &str = "pipe.Ingress.nat_ingress.ingress_ipv4"; -pub const NAT_INGRESS_IPV6: &str = "pipe.Ingress.nat_ingress.ingress_ipv6"; -pub(crate) const MCAST_NAT_INGRESS_IPV4: &str = - "pipe.Ingress.nat_ingress.ingress_ipv4_mcast"; -pub(crate) const MCAST_NAT_INGRESS_IPV6: &str = - "pipe.Ingress.nat_ingress.ingress_ipv6_mcast"; -pub(crate) const MCAST_REPLICATION_IPV4: &str = - "pipe.Ingress.mcast_ingress.mcast_replication_ipv4"; -pub(crate) const MCAST_REPLICATION_IPV6: &str = - "pipe.Ingress.mcast_ingress.mcast_replication_ipv6"; -pub(crate) const MCAST_SRC_FILTER_IPV4: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4"; -pub(crate) const MCAST_SRC_FILTER_IPV6: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6"; -pub(crate) const MCAST_ROUTE_IPV4: &str = - "pipe.Ingress.l3_router.MulticastRouter4.tbl"; -pub(crate) const MCAST_ROUTE_IPV6: &str = - "pipe.Ingress.l3_router.MulticastRouter6.tbl"; -pub(crate) const MCAST_MAC_REWRITE: &str = - "pipe.Egress.mac_rewrite.mac_rewrite"; -pub(crate) const MCAST_DECAP_PORTS: &str = - "pipe.Egress.mcast_egress.tbl_decap_ports"; -pub(crate) const MCAST_PORT_ID_MAPPING: &str = - "pipe.Egress.mcast_egress.asic_id_to_port"; +use common::table::TableType; pub struct Table { - name: String, + type_: TableType, keys: Mutex>, } impl TableOps for Table { - fn new(hdl: &Handle, name: &str) -> AsicResult { - table_unfurl!(hdl, name, table_new); - Ok(Table { name: name.into(), keys: Mutex::new(HashSet::new()) }) + fn new(hdl: &Handle, type_: TableType) -> AsicResult
{ + table_unfurl!(hdl, type_, table_new); + Ok(Table { type_, keys: Mutex::new(HashSet::new()) }) } fn size(&self) -> usize { @@ -69,7 +37,7 @@ impl TableOps for Table { } fn clear(&self, hdl: &Handle) -> AsicResult<()> { - table_unfurl!(hdl, &self.name, table_clear); + table_unfurl!(hdl, self.type_, table_clear); let mut keys = self.keys.lock().unwrap(); *keys = HashSet::new(); Ok(()) @@ -89,7 +57,7 @@ impl TableOps for Table { if keys.contains(&x) { return Err(AsicError::Exists); } - table_unfurl!(hdl, &self.name, table_entry_add); + table_unfurl!(hdl, self.type_, table_entry_add); keys.insert(x); Ok(()) } @@ -110,7 +78,7 @@ impl TableOps for Table { "table entry not found".to_string(), )); } - table_unfurl!(hdl, &self.name, table_entry_update); + table_unfurl!(hdl, self.type_, table_entry_update); Ok(()) } @@ -129,7 +97,7 @@ impl TableOps for Table { "table entry not found".to_string(), )); } - table_unfurl!(hdl, &self.name, table_entry_del); + table_unfurl!(hdl, self.type_, table_entry_del); keys.remove(&x); Ok(()) } diff --git a/asic/src/softnpu/table.rs b/asic/src/softnpu/table.rs index a356afee..56d9c1fe 100644 --- a/asic/src/softnpu/table.rs +++ b/asic/src/softnpu/table.rs @@ -12,99 +12,77 @@ use aal::{ ActionParse, AsicError, AsicResult, CounterData, MatchEntryField, MatchEntryValue, MatchParse, TableOps, ValueTypes, }; +use common::table::TableType; /// Represents a handle to a SoftNPU ASIC table. The `id` member corresponds to /// the table path in the P4 program. Well known sidecar-lite.p4 paths follow /// below. pub struct Table { - id: Option, - dpd_id: Option, + type_: TableType, + implemented: bool, size: usize, } -// soft-npu table names -const ROUTER_V4_RT: &str = "ingress.router.v4_route.rtr"; -const ROUTER_V4_IDX: &str = "ingress.router.v4_idx.rtr"; -const ROUTER_V6_RT: &str = "ingress.router.v6_route.rtr"; -const ROUTER_V6_IDX: &str = "ingress.router.v6_idx.rtr"; -const LOCAL_V6: &str = "ingress.local.local_v6"; -const LOCAL_V4: &str = "ingress.local.local_v4"; -const NAT_V4: &str = "ingress.nat.nat_v4"; -const NAT_V6: &str = "ingress.nat.nat_v6"; -const ATTACHED_SUBNET_V4: &str = "ingress.attached.attached_subnet_v4"; -const ATTACHED_SUBNET_V6: &str = "ingress.attached.attached_subnet_v6"; -const _NAT_ICMP_V6: &str = "ingress.nat.nat_icmp_v6"; -const _NAT_ICMP_V4: &str = "ingress.nat.nat_icmp_v4"; -const RESOLVER_V4: &str = "ingress.resolver.resolver_v4"; -const RESOLVER_V6: &str = "ingress.resolver.resolver_v6"; -const MAC_REWRITE: &str = "ingress.mac.mac_rewrite"; -const _PROXY_ARP: &str = "ingress.pxarp.proxy_arp"; - -// sidecar.p4 table names -const SWITCH_ADDR4: &str = "pipe.Ingress.filter.switch_ipv4_addr"; -const SWITCH_ADDR6: &str = "pipe.Ingress.filter.switch_ipv6_addr"; -const ROUTER4_LOOKUP_RT: &str = - "pipe.Ingress.l3_router.Router4.lookup_idx.route"; -const ROUTER4_LOOKUP_IDX: &str = - "pipe.Ingress.l3_router.Router4.lookup_idx.lookup"; -const ROUTER6_LOOKUP_RT: &str = - "pipe.Ingress.l3_router.Router6.lookup_idx.route"; -const ROUTER6_LOOKUP_IDX: &str = - "pipe.Ingress.l3_router.Router6.lookup_idx.lookup"; -const NDP: &str = "pipe.Ingress.l3_router.Ndp.tbl"; -const ARP: &str = "pipe.Ingress.l3_router.Arp.tbl"; -const DPD_MAC_REWRITE: &str = "pipe.Ingress.mac_rewrite.mac_rewrite"; -const NAT_INGRESS4: &str = "pipe.Ingress.nat_ingress.ingress_ipv4"; -const NAT_INGRESS6: &str = "pipe.Ingress.nat_ingress.ingress_ipv6"; -const ATTACHED_SUBNET_INGRESS4: &str = - "pipe.Ingress.attached_subnet_ingress.attached_subnets_v4"; -const ATTACHED_SUBNET_INGRESS6: &str = - "pipe.Ingress.attached_subnet_ingress.attached_subnets_v6"; +impl Table { + pub fn softnpu_table_name(&self) -> Option<&'static str> { + if self.implemented { + match self.type_ { + TableType::RouteFwdIpv4 => Some("ingress.router.v4_route.rtr"), + TableType::RouteIdxIpv4 => Some("ingress.router.v4_idx.rtr"), + TableType::RouteFwdIpv6 => Some("ingress.router.v6_route.rtr"), + TableType::RouteIdxIpv6 => Some("ingress.router.v6_idx.rtr"), + TableType::PortAddrIpv6 => Some("ingress.local.local_v6"), + TableType::PortAddrIpv4 => Some("ingress.local.local_v4"), + TableType::NatIngressIpv4 => Some("ingress.nat.nat_v4"), + TableType::NatIngressIpv6 => Some("ingress.nat.nat_v6"), + TableType::AttachedSubnetIpv4 => { + Some("ingress.attached.attached_subnet_v4") + } + TableType::AttachedSubnetIpv6 => { + Some("ingress.attached.attached_subnet_v6") + } + TableType::ArpIpv4 => Some("ingress.resolver.resolver_v4"), + TableType::NeighborIpv6 => Some("ingress.resolver.resolver_v6"), + TableType::PortMacAddress => Some("ingress.mac.mac_rewrite"), + _ => panic!( + "implemented table {} has no softnpu table", + self.type_ + ), + } + } else { + None + } + } +} // All tables are defined to be 1024 entries deep const TABLE_SIZE: usize = 4096; impl TableOps for Table { - fn new(hdl: &Handle, name: &str) -> AsicResult
{ + fn new(hdl: &Handle, type_: TableType) -> AsicResult
{ // TODO just mapping sidecar.p4 things onto simplified sidecar-lite.p4 // things to get started. - let (id, dpd_id) = match name { - ROUTER4_LOOKUP_RT => { - (Some(ROUTER_V4_RT.into()), Some(ROUTER4_LOOKUP_RT.into())) - } - ROUTER4_LOOKUP_IDX => { - (Some(ROUTER_V4_IDX.into()), Some(ROUTER4_LOOKUP_IDX.into())) - } - ROUTER6_LOOKUP_RT => { - (Some(ROUTER_V6_RT.into()), Some(ROUTER6_LOOKUP_RT.into())) - } - ROUTER6_LOOKUP_IDX => { - (Some(ROUTER_V6_IDX.into()), Some(ROUTER6_LOOKUP_IDX.into())) - } - SWITCH_ADDR4 => (Some(LOCAL_V4.into()), Some(SWITCH_ADDR4.into())), - SWITCH_ADDR6 => (Some(LOCAL_V6.into()), Some(SWITCH_ADDR6.into())), - NDP => (Some(RESOLVER_V6.into()), Some(NDP.into())), - ARP => (Some(RESOLVER_V4.into()), Some(ARP.into())), - DPD_MAC_REWRITE => { - (Some(MAC_REWRITE.into()), Some(DPD_MAC_REWRITE.into())) - } - NAT_INGRESS4 => (Some(NAT_V4.into()), Some(NAT_INGRESS4.into())), - NAT_INGRESS6 => (Some(NAT_V6.into()), Some(NAT_INGRESS6.into())), - ATTACHED_SUBNET_INGRESS4 => ( - Some(ATTACHED_SUBNET_V4.into()), - Some(ATTACHED_SUBNET_INGRESS4.into()), - ), - ATTACHED_SUBNET_INGRESS6 => ( - Some(ATTACHED_SUBNET_V6.into()), - Some(ATTACHED_SUBNET_INGRESS6.into()), - ), + let implemented = match type_ { + TableType::RouteIdxIpv4 + | TableType::RouteFwdIpv4 + | TableType::RouteIdxIpv6 + | TableType::RouteFwdIpv6 + | TableType::PortAddrIpv4 + | TableType::PortAddrIpv6 + | TableType::ArpIpv4 + | TableType::NeighborIpv6 + | TableType::PortMacAddress + | TableType::NatIngressIpv4 + | TableType::NatIngressIpv6 + | TableType::AttachedSubnetIpv4 + | TableType::AttachedSubnetIpv6 => true, x => { error!(hdl.log, "TABLE NOT HANDLED {x}"); - (None, None) + false } }; - Ok(Table { id, dpd_id, size: TABLE_SIZE }) + Ok(Table { type_, implemented, size: TABLE_SIZE }) } fn size(&self) -> usize { @@ -122,33 +100,28 @@ impl TableOps for Table { key: &M, data: &A, ) -> AsicResult<()> { - let table = match &self.id { - None => return Ok(()), - Some(id) => id.clone(), - }; - let dpd_table = match &self.dpd_id { - None => return Ok(()), - Some(id) => id.clone(), + let Some(table) = self.softnpu_table_name() else { + return Ok(()); }; - + let name = self.type_.to_string(); let match_data = key.key_to_ir().unwrap(); let action_data = data.action_to_ir().unwrap(); trace!(hdl.log, "entry_add called"); - trace!(hdl.log, "table: {}", table); + trace!(hdl.log, "table: {name}"); trace!(hdl.log, "match_data:\n{:#?}", match_data); trace!(hdl.log, "action_data:\n{:#?}", action_data); - let keyset_data = keyset_data(match_data.fields, &table); + let keyset_data = keyset_data(match_data.fields, self.type_); let (action, parameter_data) = match ( - dpd_table.as_str(), + self.type_, action_data.action.as_str(), ) { // TODO: implement mappings for natv6 actions - (SWITCH_ADDR4, "claimv4") => ("local", Vec::new()), - (SWITCH_ADDR6, "claimv6") => ("local", Vec::new()), - (ROUTER4_LOOKUP_IDX, "index") => { + (TableType::PortAddrIpv4, "claimv4") => ("local", Vec::new()), + (TableType::PortAddrIpv6, "claimv6") => ("local", Vec::new()), + (TableType::RouteIdxIpv4, "index") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -167,7 +140,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::index {x}" + "unexpected parameter: {name}::index {x}" ) } } @@ -179,7 +152,7 @@ impl TableOps for Table { } ("index", params) } - (ROUTER4_LOOKUP_RT, "forward") => { + (TableType::RouteFwdIpv4, "forward") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -200,7 +173,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward {x}" + "unexpected parameter: {name}::forward {x}" ) } } @@ -212,7 +185,7 @@ impl TableOps for Table { } ("forward", params) } - (ROUTER4_LOOKUP_RT, "forward_v6") => { + (TableType::RouteFwdIpv4, "forward_v6") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -227,7 +200,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward {x}" + "unexpected parameter: {name}::forward {x}" ) } } @@ -241,7 +214,7 @@ impl TableOps for Table { } ("forward_v6", params) } - (ROUTER4_LOOKUP_RT, "forward_vlan") => { + (TableType::RouteFwdIpv4, "forward_vlan") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -268,7 +241,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward_vlan {x}" + "unexpected parameter: {name}::forward_vlan {x}" ) } } @@ -280,7 +253,7 @@ impl TableOps for Table { } ("forward_vlan", params) } - (ROUTER4_LOOKUP_RT, "forward_vlan_v6") => { + (TableType::RouteFwdIpv4, "forward_vlan_v6") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -301,7 +274,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward_vlan {x}" + "unexpected parameter: {name}::forward_vlan {x}" ) } } @@ -315,7 +288,7 @@ impl TableOps for Table { } ("forward_vlan_v6", params) } - (ROUTER6_LOOKUP_IDX, "index") => { + (TableType::RouteIdxIpv6, "index") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -334,7 +307,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::index {x}" + "unexpected parameter: {name}::index {x}" ) } } @@ -346,7 +319,7 @@ impl TableOps for Table { } ("index", params) } - (ROUTER6_LOOKUP_RT, "forward") => { + (TableType::RouteFwdIpv6, "forward") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -361,7 +334,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward {x}" + "unexpected parameter: {name}::forward {x}" ) } } @@ -375,7 +348,7 @@ impl TableOps for Table { } ("forward", params) } - (ROUTER6_LOOKUP_RT, "forward_vlan") => { + (TableType::RouteFwdIpv6, "forward_vlan") => { let mut params = Vec::new(); for arg in action_data.args.iter() { match &arg.value { @@ -396,7 +369,7 @@ impl TableOps for Table { x => { error!( hdl.log, - "unexpected parameter: {dpd_table}::forward_vlan {x}" + "unexpected parameter: {name}::forward_vlan {x}" ) } } @@ -410,7 +383,7 @@ impl TableOps for Table { } ("forward_vlan", params) } - (ARP, "rewrite") => { + (TableType::ArpIpv4, "rewrite") => { let mut params = Vec::new(); for arg in action_data.args { match arg.value { @@ -425,7 +398,7 @@ impl TableOps for Table { } ("rewrite_dst", params) } - (NDP, "rewrite") => { + (TableType::NeighborIpv6, "rewrite") => { let mut params = Vec::new(); for arg in action_data.args { match arg.value { @@ -440,7 +413,7 @@ impl TableOps for Table { } ("rewrite_dst", params) } - (DPD_MAC_REWRITE, "rewrite") => { + (TableType::PortMacAddress, "rewrite") => { let mut params = Vec::new(); for arg in action_data.args { match arg.value { @@ -455,10 +428,10 @@ impl TableOps for Table { } ("rewrite", params) } - (NAT_INGRESS4, "forward_ipv4_to") - | (NAT_INGRESS6, "forward_ipv6_to") - | (ATTACHED_SUBNET_INGRESS4, "forward_to_v4") - | (ATTACHED_SUBNET_INGRESS6, "forward_to_v6") => { + (TableType::NatIngressIpv4, "forward_ipv4_to") + | (TableType::NatIngressIpv6, "forward_ipv6_to") + | (TableType::AttachedSubnetIpv4, "forward_to_v4") + | (TableType::AttachedSubnetIpv6, "forward_to_v6") => { let mut target = Vec::new(); let mut vni = Vec::new(); let mut mac = Vec::new(); @@ -538,20 +511,20 @@ impl TableOps for Table { params.extend_from_slice(mac.as_slice()); ("forward_to_sled", params) } - (tbl, x) => { - error!(hdl.log, "ACTION NOT HANDLED {tbl} {x}"); + (_, x) => { + error!(hdl.log, "ACTION NOT HANDLED {name} {x}"); return Ok(()); } }; let action = action.to_string(); trace!(hdl.log, "sending request to softnpu"); - trace!(hdl.log, "table: {}", table); + trace!(hdl.log, "table: {name}"); trace!(hdl.log, "action: {:#?}", action); trace!(hdl.log, "keyset_data:\n{:#?}", keyset_data); trace!(hdl.log, "parameter_data:\n{:#?}", parameter_data); let msg = ManagementRequest::TableAdd(TableAdd { - table, + table: table.to_string(), action, keyset_data, parameter_data, @@ -568,15 +541,16 @@ impl TableOps for Table { key: &M, data: &A, ) -> AsicResult<()> { - let table = match &self.id { - None => return Ok(()), - Some(id) => id.clone(), + let Some(_table) = self.softnpu_table_name() else { + return Ok(()); }; + let name = self.type_.to_string(); + let match_data = key.key_to_ir().unwrap(); let action_data = data.action_to_ir().unwrap(); trace!(hdl.log, "entry_update called"); - trace!(hdl.log, "table: {}", table); + trace!(hdl.log, "table: {name}"); trace!(hdl.log, "match_data:\n{:#?}", match_data); trace!(hdl.log, "action_data:\n{:#?}", action_data); @@ -589,24 +563,26 @@ impl TableOps for Table { hdl: &Handle, key: &M, ) -> AsicResult<()> { - let table = match &self.id { - None => return Ok(()), - Some(id) => id.clone(), + let Some(table) = self.softnpu_table_name() else { + return Ok(()); }; + let name = self.type_.to_string(); let match_data = key.key_to_ir().unwrap(); trace!(hdl.log, "entry_del called"); - trace!(hdl.log, "table: {}", table); + trace!(hdl.log, "table: {name}"); trace!(hdl.log, "match_data:\n{:#?}", match_data); - let keyset_data = keyset_data(match_data.fields, &table); + let keyset_data = keyset_data(match_data.fields, self.type_); trace!(hdl.log, "sending request to softnpu"); - trace!(hdl.log, "table: {}", table); + trace!(hdl.log, "table: {name}"); trace!(hdl.log, "keyset_data:\n{:#?}", keyset_data); - let msg = - ManagementRequest::TableRemove(TableRemove { keyset_data, table }); + let msg = ManagementRequest::TableRemove(TableRemove { + keyset_data, + table: table.to_string(), + }); crate::softnpu::mgmt::write(msg, &hdl.mgmt_config); @@ -632,7 +608,7 @@ impl TableOps for Table { /// Extract keys from `match_data` and ensure that they are /// in a data structure with the correct length -fn keyset_data(match_data: Vec, table: &str) -> Vec { +fn keyset_data(match_data: Vec, table: TableType) -> Vec { let mut keyset_data: Vec = Vec::new(); for m in match_data { match m.value { @@ -640,40 +616,40 @@ fn keyset_data(match_data: Vec, table: &str) -> Vec { MatchEntryValue::Value(x) => { let mut data: Vec = Vec::new(); match table { - RESOLVER_V4 => { + TableType::ArpIpv4 => { // "nexthop_ipv4" => bit<32> serialize_value_type(&x, &mut data); keyset_data.extend_from_slice(&data[..4]); } - RESOLVER_V6 => { + TableType::NeighborIpv6 => { // "nexthop_ipv4" => bit<128> let mut buf = Vec::new(); serialize_value_type(&x, &mut buf); buf.reverse(); keyset_data.extend_from_slice(&buf); } - MAC_REWRITE => { + TableType::PortMacAddress => { serialize_value_type(&x, &mut data); keyset_data.extend_from_slice(&data[..2]); } - ROUTER_V4_RT => { + TableType::RouteIdxIpv4 => { // "idx" => exact => bit<16> serialize_value_type(&x, &mut data); keyset_data.extend_from_slice(&data[..2]); } - NAT_V4 => { + TableType::NatIngressIpv4 => { // "dst_addr" => hdr.ipv4.dst: exact => bit<32> serialize_value_type(&x, &mut data); keyset_data.extend_from_slice(&data[..4]); } - NAT_V6 => { + TableType::NatIngressIpv6 => { // "dst_addr" => hdr.ipv6.dst: exact => bit<128> let mut buf = Vec::new(); serialize_value_type(&x, &mut buf); buf.reverse(); keyset_data.extend_from_slice(&buf); } - LOCAL_V6 => { + TableType::PortAddrIpv6 => { let mut buf = Vec::new(); serialize_value_type(&x, &mut buf); buf.reverse(); @@ -688,7 +664,7 @@ fn keyset_data(match_data: Vec, table: &str) -> Vec { MatchEntryValue::Lpm(x) => { let mut data: Vec = Vec::new(); match table { - ROUTER_V4_IDX | ATTACHED_SUBNET_V4 => { + TableType::RouteIdxIpv4 | TableType::AttachedSubnetIpv4 => { // prefix for longest prefix match operation // "dst_addr" => hdr.ipv4.dst: lpm => bit<32> serialize_value_type_be(&x.prefix, &mut data); @@ -705,7 +681,7 @@ fn keyset_data(match_data: Vec, table: &str) -> Vec { // Ranges (i.e. port ranges) MatchEntryValue::Range(x) => { match table { - NAT_V4 | NAT_V6 => { + TableType::NatIngressIpv4 | TableType::NatIngressIpv6 => { // "l4_dst_port" => ingress.nat_id: range => bit<16> let low = &x.low.to_le_bytes(); let high = &x.high.to_le_bytes(); diff --git a/asic/src/tofino_asic/table.rs b/asic/src/tofino_asic/table.rs index c129fbcc..98f48888 100644 --- a/asic/src/tofino_asic/table.rs +++ b/asic/src/tofino_asic/table.rs @@ -17,21 +17,8 @@ use std::sync::{Condvar, Mutex}; use super::bf_wrapper::*; use super::*; use crate::tofino_asic::genpd::*; -use aal::ActionArg; -use aal::ActionData; -use aal::ActionParse; -use aal::AsicError; -use aal::AsicResult; -use aal::CounterData; -use aal::MatchData; -use aal::MatchEntryField; -use aal::MatchEntryValue; -use aal::MatchLpm; -use aal::MatchMask; -use aal::MatchParse; -use aal::MatchRange; -use aal::MatchType; -use aal::ValueTypes; +use aal::*; +use common::table::TableType; // Refreshing the counters is a relatively expensive operation, so we try not // to do it too frequently. This defines how many milliseconds we wait between @@ -694,23 +681,26 @@ impl TofinoTableOps for Table { } impl aal::TableOps for Table { - fn new(hdl: &Handle, name: &str) -> AsicResult
{ - let mut info = tofino_common::TableInfo::new(&hdl.rt, name)?; - slog::debug!(hdl.log, "table {name}\n{info:#?}"); + fn new(hdl: &Handle, type_: TableType) -> AsicResult
{ + let mut info = tofino_common::TableInfo::new(&hdl.rt, type_)?; + slog::debug!(hdl.log, "initted {info:#?}"); let bf = hdl.bf_get(); let mut rt_hdl: *const bf_rt_table_hdl = ptr::null_mut(); unsafe { - let tmp = CString::new(name).unwrap(); + let tmp = CString::new(info.name.clone()).unwrap(); bf_rt_table_from_name_get(bf.rt_info, tmp.as_ptr(), &mut rt_hdl) } - .check_error(&format!("fetching handle for table {name}")) + .check_error(&format!("fetching handle for table {}", info.name)) .log_error(hdl)?; info.size = unsafe { let mut size = 0; bf_rt_table_size_get(rt_hdl, bf.rt_sess, &TGT, &mut size) - .check_error(&format!("fetching actual size of table {name}")) + .check_error(&format!( + "fetching actual size of table {}", + info.name + )) .log_error(hdl)?; size }; diff --git a/asic/src/tofino_common/mod.rs b/asic/src/tofino_common/mod.rs index 6d3f751d..0729a089 100644 --- a/asic/src/tofino_common/mod.rs +++ b/asic/src/tofino_common/mod.rs @@ -13,9 +13,113 @@ use serde::Deserialize; use aal::AsicError; use aal::AsicResult; use aal::MatchType; +use common::counters::CounterId; +#[cfg(feature = "multicast")] +use common::counters::MulticastCounterId; +use common::table::TableType; pub mod ports; +fn table_name(type_: TableType) -> &'static str { + match type_ { + TableType::RouteIdxIpv4 => { + "pipe.Ingress.l3_router.Router4.lookup_idx.lookup" + } + TableType::RouteFwdIpv4 => { + "pipe.Ingress.l3_router.Router4.lookup_idx.route" + } + TableType::RouteIdxIpv6 => { + "pipe.Ingress.l3_router.Router6.lookup_idx.lookup" + } + TableType::RouteFwdIpv6 => { + "pipe.Ingress.l3_router.Router6.lookup_idx.route" + } + #[cfg(feature = "multicast")] + TableType::RouteIpv4Mcast => { + "pipe.Ingress.l3_router.MulticastRouter4.tbl" + } + #[cfg(feature = "multicast")] + TableType::RouteIpv6Mcast => { + "pipe.Ingress.l3_router.MulticastRouter6.tbl" + } + 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::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::AttachedSubnetIpv4 => { + "pipe.Ingress.attached_subnet_ingress.attached_subnets_v4" + } + TableType::AttachedSubnetIpv6 => { + "pipe.Ingress.attached_subnet_ingress.attached_subnets_v6" + } + #[cfg(feature = "multicast")] + TableType::McastIpv6 => { + "pipe.Ingress.mcast_ingress.mcast_replication_ipv6" + } + #[cfg(feature = "multicast")] + TableType::McastIpv4SrcFilter => { + "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4" + } + #[cfg(feature = "multicast")] + TableType::McastIpv6SrcFilter => { + "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6" + } + #[cfg(feature = "multicast")] + TableType::NatIngressIpv4Mcast => { + "pipe.Ingress.nat_ingress.ingress_ipv4_mcast" + } + #[cfg(feature = "multicast")] + TableType::NatIngressIpv6Mcast => { + "pipe.Ingress.nat_ingress.ingress_ipv6_mcast" + } + #[cfg(feature = "multicast")] + TableType::PortMacAddressMcast => "pipe.Egress.mac_rewrite.mac_rewrite", + #[cfg(feature = "multicast")] + TableType::McastEgressDecapPorts => { + "pipe.Egress.mcast_egress.tbl_decap_ports" + } + #[cfg(feature = "multicast")] + TableType::McastEgressPortMapping => { + "pipe.Egress.mcast_egress.asic_id_to_port" + } + TableType::Counter(c) => counter_table_name(c), + } +} + +fn counter_table_name(id: CounterId) -> &'static str { + match id { + 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", + #[cfg(feature = "multicast")] + CounterId::Multicast(id) => mulitcast_counter_table_name(id), + } +} + +#[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" + } + } +} + #[derive(Debug)] // Allow clippy to pass with tofino_stub #[cfg_attr(not(feature = "tofino_asic"), allow(dead_code))] @@ -49,6 +153,7 @@ pub type DataMap = HashMap; // Allow clippy to pass with tofino_stub #[cfg_attr(not(feature = "tofino_asic"), allow(dead_code))] pub struct TableInfo { + pub name: String, pub keys: KeyMap, pub actions: ActionMap, pub data: DataMap, @@ -61,10 +166,17 @@ fn missing(what: &str, which: &str) -> AsicError { } impl TableInfo { - pub fn new(bfrt: &BfRt, name: &str) -> AsicResult { + pub fn new(bfrt: &BfRt, type_: TableType) -> AsicResult { + let name = table_name(type_); let (keys, actions, data, size) = bfrt.get_table(name)?; - Ok(TableInfo { keys, actions, data, size: size as usize }) + Ok(TableInfo { + name: name.to_string(), + keys, + actions, + data, + size: size as usize, + }) } #[cfg(feature = "tofino_asic")] diff --git a/asic/src/tofino_stub/table.rs b/asic/src/tofino_stub/table.rs index e0ae718a..1ab31f60 100644 --- a/asic/src/tofino_stub/table.rs +++ b/asic/src/tofino_stub/table.rs @@ -57,8 +57,11 @@ impl DataHdl { } impl TableOps for Table { - fn new(hdl: &StubHandle, name: &str) -> AsicResult
{ - let info = TableInfo::new(&hdl.rt, name)?; + fn new( + hdl: &StubHandle, + type_: common::table::TableType, + ) -> AsicResult
{ + let info = TableInfo::new(&hdl.rt, type_)?; Ok(Table { info }) } diff --git a/common/Cargo.toml b/common/Cargo.toml index 9f4b59db..d7a7bc0a 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["Nils Nieuwejaar "] edition = "2024" +[features] +multicast = [] + [dependencies] anyhow.workspace = true chrono.workspace = true diff --git a/common/src/counters.rs b/common/src/counters.rs index 441ba16b..be4d19c3 100644 --- a/common/src/counters.rs +++ b/common/src/counters.rs @@ -2,7 +2,9 @@ // 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 + +use std::fmt; use schemars::JsonSchema; use serde::Deserialize; @@ -186,3 +188,134 @@ pub struct FecRSCounters { /// FEC symbol errors on lane 7 pub fec_ser_lane_7: u32, } + +/// sidecar.p4 defines the following set of indirect counters. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, +)] +pub enum CounterId { + Service, + Ingress, + Egress, + Packet, + DropPort, + DropReason, + #[cfg(feature = "multicast")] + Multicast(MulticastCounterId), +} + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, +)] +#[cfg(feature = "multicast")] +pub enum MulticastCounterId { + EgressDropPort, + EgressDropReason, + Unicast, + Multicast, + MulticastExt, + MulticastLL, + MulticastUL, + MulticastDrop, +} + +impl fmt::Display for CounterId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + 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(), + #[cfg(feature = "multicast")] + CounterId::Multicast(id) => id.to_string(), + } + ) + } +} + +impl std::str::FromStr for CounterId { + type Err = String; + + fn from_str(s: &str) -> Result { + 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), + #[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", + } + ) + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 42940064..e83fcde3 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -17,6 +17,7 @@ pub mod logging; pub mod nat; pub mod network; pub mod ports; +pub mod table; #[cfg(target_os = "illumos")] pub mod illumos; diff --git a/common/src/table.rs b/common/src/table.rs new file mode 100644 index 00000000..edafbb8a --- /dev/null +++ b/common/src/table.rs @@ -0,0 +1,295 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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 2026 Oxide Computer Company + +use std::fmt; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Error, Debug, Clone)] +pub enum TableError { + #[error("No such table: {0}")] + NoSuchTable(String), +} + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, +)] +pub enum TableType { + RouteIdxIpv4, + RouteFwdIpv4, + RouteIdxIpv6, + RouteFwdIpv6, + #[cfg(feature = "multicast")] + RouteIpv4Mcast, + #[cfg(feature = "multicast")] + RouteIpv6Mcast, + ArpIpv4, + NeighborIpv6, + PortMacAddress, + PortAddrIpv4, + PortAddrIpv6, + NatIngressIpv4, + NatIngressIpv6, + UplinkIngress, + UplinkEgress, + AttachedSubnetIpv4, + AttachedSubnetIpv6, + #[cfg(feature = "multicast")] + McastIpv6, + #[cfg(feature = "multicast")] + McastIpv4SrcFilter, + #[cfg(feature = "multicast")] + McastIpv6SrcFilter, + #[cfg(feature = "multicast")] + NatIngressIpv4Mcast, + #[cfg(feature = "multicast")] + NatIngressIpv6Mcast, + #[cfg(feature = "multicast")] + PortMacAddressMcast, + #[cfg(feature = "multicast")] + McastEgressDecapPorts, + #[cfg(feature = "multicast")] + McastEgressPortMapping, + Counter(crate::counters::CounterId), +} + +/// Returns a vec of all the normal table types. This will not include the +/// tables used to collect counter data, which are managed separately. +pub fn get_table_types() -> Vec { + let mut base_tables = vec![ + TableType::RouteIdxIpv4, + TableType::RouteFwdIpv4, + TableType::RouteIdxIpv6, + TableType::RouteFwdIpv6, + TableType::ArpIpv4, + TableType::NeighborIpv6, + TableType::PortMacAddress, + TableType::PortAddrIpv4, + TableType::PortAddrIpv6, + TableType::NatIngressIpv4, + TableType::NatIngressIpv6, + TableType::UplinkIngress, + TableType::UplinkEgress, + TableType::AttachedSubnetIpv4, + TableType::AttachedSubnetIpv6, + ]; + + let mut multicast_tables; + #[cfg(feature = "multicast")] + { + multicast_tables = vec![ + TableType::RouteIpv4Mcast, + TableType::RouteIpv6Mcast, + TableType::McastIpv6, + TableType::McastIpv4SrcFilter, + TableType::McastIpv6SrcFilter, + TableType::NatIngressIpv4Mcast, + TableType::NatIngressIpv6Mcast, + TableType::PortMacAddressMcast, + TableType::McastEgressDecapPorts, + TableType::McastEgressPortMapping, + ]; + } + #[cfg(not(feature = "multicast"))] + { + multicast_tables = Vec::new(); + } + base_tables.append(&mut multicast_tables); + base_tables +} + +// This is the name that will be displayed in the log and presented to the user. +// It is similar to the name used in the p4 code, to simplify debugging, but +// isn't required to be identical. +impl fmt::Display for TableType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + TableType::RouteIdxIpv4 => { + "Ingress.l3_router.Router4.lookup_idx.lookup".to_string() + } + TableType::RouteFwdIpv4 => { + "Ingress.l3_router.Router4.lookup_idx.route".to_string() + } + TableType::RouteIdxIpv6 => { + "Ingress.l3_router.Router6.lookup_idx.lookup".to_string() + } + TableType::RouteFwdIpv6 => { + "Ingress.l3_router.Router6.lookup_idx.route".to_string() + } + #[cfg(feature = "multicast")] + TableType::RouteIpv4Mcast => { + "Ingress.l3_router.MulticastRouter4.tbl".to_string() + } + #[cfg(feature = "multicast")] + TableType::RouteIpv6Mcast => { + "Ingress.l3_router.MulticastRouter6.tbl".to_string() + } + TableType::ArpIpv4 => "Ingress.l3_router.Arp.tbl".to_string(), + TableType::NeighborIpv6 => + "Ingress.l3_router.Ndp.tbl".to_string(), + TableType::PortMacAddress => + "Ingress.mac_rewrite.mac_rewrite".to_string(), + TableType::PortAddrIpv4 => + "Ingress.filter.switch_ipv4_addr".to_string(), + TableType::PortAddrIpv6 => + "Ingress.filter.switch_ipv6_addr".to_string(), + TableType::NatIngressIpv4 => + "Ingress.nat_ingress.ingress_ipv4".to_string(), + TableType::NatIngressIpv6 => + "Ingress.nat_ingress.ingress_ipv6".to_string(), + TableType::UplinkIngress => + "Ingress.filter.uplink_ports".to_string(), + TableType::UplinkEgress => + "Ingress.egress_filter.egress_filter".to_string(), + TableType::AttachedSubnetIpv4 => { + "Ingress.attached_subnet_ingress.attached_subnets_v4" + .to_string() + } + TableType::AttachedSubnetIpv6 => { + "Ingress.attached_subnet_ingress.attached_subnets_v6" + .to_string() + } + #[cfg(feature = "multicast")] + TableType::McastIpv6 => { + "Ingress.mcast_ingress.mcast_replication_ipv6".to_string() + } + #[cfg(feature = "multicast")] + TableType::McastIpv4SrcFilter => { + "Ingress.mcast_ingress.mcast_source_filter_ipv4".to_string() + } + #[cfg(feature = "multicast")] + TableType::McastIpv6SrcFilter => { + "Ingress.mcast_ingress.mcast_source_filter_ipv6".to_string() + } + #[cfg(feature = "multicast")] + TableType::NatIngressIpv4Mcast => { + "Ingress.nat_ingress.ingress_ipv4_mcast".to_string() + } + #[cfg(feature = "multicast")] + TableType::NatIngressIpv6Mcast => { + "Ingress.nat_ingress.ingress_ipv6_mcast".to_string() + } + #[cfg(feature = "multicast")] + TableType::PortMacAddressMcast => + "Egress.mac_rewrite.mac_rewrite".to_string(), + #[cfg(feature = "multicast")] + TableType::McastEgressDecapPorts => { + "Egress.mcast_egress.tbl_decap_ports".to_string() + } + #[cfg(feature = "multicast")] + TableType::McastEgressPortMapping => { + "Egress.mcast_egress.asic_id_to_port".to_string() + } + TableType::Counter(c) => format!("Counter({c})"), + } + ) + } +} + +impl TryFrom<&str> for TableType { + type Error = TableError; + + fn try_from(name: &str) -> Result { + let name = name.to_lowercase(); + match name.as_str() { + "ingress.l3_router.router4.lookup_idx.lookup" => { + Ok(TableType::RouteIdxIpv4) + } + + "ingress.l3_router.router4.lookup_idx.route" => { + Ok(TableType::RouteFwdIpv4) + } + + "ingress.l3_router.router6.lookup_idx.lookup" => { + Ok(TableType::RouteIdxIpv6) + } + + "ingress.l3_router.router6.lookup_idx.route" => { + Ok(TableType::RouteFwdIpv6) + } + #[cfg(feature = "multicast")] + "ingress.l3_router.multicastrouter4.tbl" => { + Ok(TableType::RouteIpv4Mcast) + } + + #[cfg(feature = "multicast")] + "ingress.l3_router.multicastrouter6.tbl" => { + Ok(TableType::RouteIpv6Mcast) + } + "ingress.l3_router.arp.tbl" => Ok(TableType::ArpIpv4), + "ingress.l3_router.ndp.tbl" => Ok(TableType::NeighborIpv6), + "ingress.mac_rewrite.mac_rewrite" => Ok(TableType::PortMacAddress), + "ingress.filter.switch_ipv4_addr" => Ok(TableType::PortAddrIpv4), + "ingress.filter.switch_ipv6_addr" => Ok(TableType::PortAddrIpv6), + "ingress.nat_ingress.ingress_ipv4" => Ok(TableType::NatIngressIpv4), + "ingress.nat_ingress.ingress_ipv6" => Ok(TableType::NatIngressIpv6), + "ingress.filter.uplink_ports" => Ok(TableType::UplinkIngress), + "ingress.egress_filter.egress_filter" => { + Ok(TableType::UplinkEgress) + } + "ingress.attached_subnet_ingress.attached_subnets_v4" => { + Ok(TableType::AttachedSubnetIpv4) + } + + "ingress.attached_subnet_ingress.attached_subnets_v6" => { + Ok(TableType::AttachedSubnetIpv6) + } + + #[cfg(feature = "multicast")] + "ingress.mcast_ingress.mcast_replication_ipv6" => { + Ok(TableType::McastIpv6) + } + + #[cfg(feature = "multicast")] + "ingress.mcast_ingress.mcast_source_filter_ipv4" => { + Ok(TableType::McastIpv4SrcFilter) + } + + #[cfg(feature = "multicast")] + "ingress.mcast_ingress.mcast_source_filter_ipv6" => { + Ok(TableType::McastIpv6SrcFilter) + } + + #[cfg(feature = "multicast")] + "ingress.nat_ingress.ingress_ipv4_mcast" => { + Ok(TableType::NatIngressIpv4Mcast) + } + + #[cfg(feature = "multicast")] + "ingress.nat_ingress.ingress_ipv6_mcast" => { + Ok(TableType::NatIngressIpv6Mcast) + } + + #[cfg(feature = "multicast")] + "egress.mac_rewrite.mac_rewrite" => { + Ok(TableType::PortMacAddressMcast) + } + #[cfg(feature = "multicast")] + "egress.mcast_egress.tbl_decap_ports" => { + Ok(TableType::McastEgressDecapPorts) + } + + #[cfg(feature = "multicast")] + "egress.mcast_egress.asic_id_to_port" => { + Ok(TableType::McastEgressPortMapping) + } + _ => Err(TableError::NoSuchTable(name)), + } + } +} diff --git a/dpd-client/tests/chaos_tests/port_settings.rs b/dpd-client/tests/chaos_tests/port_settings.rs index 73d0b66a..a3dfebcb 100644 --- a/dpd-client/tests/chaos_tests/port_settings.rs +++ b/dpd-client/tests/chaos_tests/port_settings.rs @@ -9,8 +9,9 @@ use super::harness::{ new_dpd_client, run_dpd, }; use super::util::{link_list_ipv4, link_list_ipv6}; -use asic::chaos::{AsicConfig, Chaos, TableChaos, table}; +use asic::chaos::{AsicConfig, Chaos, TableChaos}; use asic::table_chaos; +use common::table::TableType; use dpd_client::types::{ LinkCreate, LinkId, LinkSettings, PortFec, PortId, PortSettings, PortSpeed, }; @@ -109,7 +110,7 @@ async fn test_port_settings_addr_fail_1() -> anyhow::Result<()> { // for the switch IPv4 address table. let config = AsicConfig { radix: TESTING_RADIX, - table_entry_add: table_chaos!((table::SWITCH_IPV4_ADDR, 1.0)), + table_entry_add: table_chaos!((TableType::PortAddrIpv4, 1.0)), ..Default::default() }; diff --git a/dpd-client/tests/integration_tests/mcast.rs b/dpd-client/tests/integration_tests/mcast.rs index 46ed1d8b..1a97c6e2 100644 --- a/dpd-client/tests/integration_tests/mcast.rs +++ b/dpd-client/tests/integration_tests/mcast.rs @@ -808,7 +808,7 @@ async fn test_vlan_propagation_to_internal() -> TestResult { // handled by NAT ingress tables. let route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should dump IPv4 route table") .into_inner(); @@ -837,7 +837,7 @@ async fn test_vlan_propagation_to_internal() -> TestResult { // !hdr.geneve.isValid() guard, so one entry per group suffices. let nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should dump NAT ingress table") .into_inner(); @@ -851,7 +851,7 @@ async fn test_vlan_propagation_to_internal() -> TestResult { // reference. let bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should clean up internal group") .into_inner(); @@ -929,7 +929,7 @@ async fn test_group_api_lifecycle() { // Route table: 1 entry (dst_addr only, VLAN via action) let route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should dump route table") .into_inner(); @@ -947,7 +947,7 @@ async fn test_group_api_lifecycle() { // NAT table: 1 entry per group with exact VLAN match let nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should dump NAT table") .into_inner(); @@ -3683,7 +3683,7 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Note: Only IPv6 has a replication table; IPv4 uses different mechanisms let ipv6_repl_table_before = switch .client - .table_dump("pipe.Ingress.mcast_ingress.mcast_replication_ipv6", false) + .table_dump("Ingress.mcast_ingress.mcast_replication_ipv6", false) .await .expect("Should be able to dump IPv6 replication table"); @@ -3695,13 +3695,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check route tables let ipv4_route_table_before = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump IPv4 route table"); let ipv6_route_table_before = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter6.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter6.tbl", false) .await .expect("Should be able to dump IPv6 route table"); @@ -3717,13 +3717,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check NAT tables let ipv4_nat_table_before = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump IPv4 NAT table"); let ipv6_nat_table_before = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv6_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv6_mcast", false) .await .expect("Should be able to dump IPv6 NAT table"); @@ -3739,19 +3739,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check source filter tables let ipv4_src_filter_table_before = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv4", false) .await .expect("Should be able to dump IPv4 source filter table"); let ipv6_src_filter_table_before = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv6", false) .await .expect("Should be able to dump IPv6 source filter table"); @@ -3777,7 +3771,7 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Note: Only IPv6 has a replication table; IPv4 uses different mechanisms let ipv6_repl_table_after = switch .client - .table_dump("pipe.Ingress.mcast_ingress.mcast_replication_ipv6", false) + .table_dump("Ingress.mcast_ingress.mcast_replication_ipv6", false) .await .expect("Should be able to dump IPv6 replication table"); @@ -3789,13 +3783,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check route tables after reset let ipv4_route_table_after = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump IPv4 route table"); let ipv6_route_table_after = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter6.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter6.tbl", false) .await .expect("Should be able to dump IPv6 route table"); @@ -3811,13 +3805,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check NAT tables after reset let ipv4_nat_table_after = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump IPv4 NAT table"); let ipv6_nat_table_after = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv6_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv6_mcast", false) .await .expect("Should be able to dump IPv6 NAT table"); @@ -3833,19 +3827,13 @@ async fn test_multicast_reset_all_tables() -> TestResult { // Check source filter tables after reset let ipv4_src_filter_table_after = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv4", false) .await .expect("Should be able to dump IPv4 source filter table"); let ipv6_src_filter_table_after = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv6", false) .await .expect("Should be able to dump IPv6 source filter table"); @@ -4724,7 +4712,7 @@ async fn test_multicast_empty_then_add_members_ipv6() -> TestResult { // Verify bitmap table is initially empty for both group IDs let bitmap_table_initial = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table initially"); // Should have no bitmap entries when groups are empty @@ -4880,7 +4868,7 @@ async fn test_multicast_empty_then_add_members_ipv6() -> TestResult { // Verify bitmap table now has entry for external group ID only (1 entry with bitmap of 2 ports) let bitmap_table_with_members = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table with members"); assert_eq!( @@ -4932,7 +4920,7 @@ async fn test_multicast_empty_then_add_members_ipv6() -> TestResult { // Verify bitmap table is empty again after removing all members let bitmap_table_final = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table after emptying"); // Should have no bitmap entries when group is empty again @@ -5121,7 +5109,7 @@ async fn test_multicast_empty_then_add_members_ipv4() -> TestResult { // Verify bitmap table is initially empty for both group IDs let bitmap_table_initial = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table initially"); // Should have no bitmap entries when groups are empty @@ -5272,7 +5260,7 @@ async fn test_multicast_empty_then_add_members_ipv4() -> TestResult { // Verify bitmap table now has entry for external group ID only (underlay doesn't need decap bitmap) let bitmap_table_with_members = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table with members"); // Should have bitmap entry for external group ID only (underlay doesn't need decap bitmap) @@ -5324,7 +5312,7 @@ async fn test_multicast_empty_then_add_members_ipv4() -> TestResult { // Verify bitmap table is empty again after removing all members let bitmap_table_final = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table after emptying"); // Should have no bitmap entries when group is empty again @@ -5390,17 +5378,17 @@ async fn test_multicast_rollback_external_group_creation_failure() -> TestResult // Get initial table states let initial_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table"); let initial_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table"); let initial_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table"); let initial_src_filter_table = switch @@ -5472,7 +5460,7 @@ async fn test_multicast_rollback_external_group_creation_failure() -> TestResult // Table states should be unchanged let post_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table"); @@ -5484,7 +5472,7 @@ async fn test_multicast_rollback_external_group_creation_failure() -> TestResult let post_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table"); @@ -5496,7 +5484,7 @@ async fn test_multicast_rollback_external_group_creation_failure() -> TestResult let post_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table"); @@ -5673,7 +5661,7 @@ async fn test_multicast_rollback_nat_transition_failure() -> TestResult { // Get initial NAT table state let initial_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table"); @@ -5739,7 +5727,7 @@ async fn test_multicast_rollback_nat_transition_failure() -> TestResult { // Verify NAT table state is unchanged let post_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table"); @@ -5788,7 +5776,7 @@ async fn test_multicast_rollback_vlan_propagation_consistency() { // Get initial bitmap table state let _initial_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table"); @@ -5809,12 +5797,12 @@ async fn test_multicast_rollback_vlan_propagation_consistency() { // Get initial table states before attempting creation let initial_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table"); let initial_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table"); @@ -5841,12 +5829,12 @@ async fn test_multicast_rollback_vlan_propagation_consistency() { // Verify rollback worked - tables should remain unchanged let post_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table after rollback"); let post_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table after rollback"); @@ -5932,10 +5920,7 @@ async fn test_multicast_rollback_source_filter_update() -> TestResult { // Get initial source filter table state let initial_src_table = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv4", false) .await .expect("Should be able to dump source filter table"); @@ -5987,10 +5972,7 @@ async fn test_multicast_rollback_source_filter_update() -> TestResult { // Verify source filter table is back to initial state (rollback worked) let post_rollback_src_table = switch .client - .table_dump( - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4", - false, - ) + .table_dump("Ingress.mcast_ingress.mcast_source_filter_ipv4", false) .await .expect("Should be able to dump source filter table after rollback"); @@ -6151,17 +6133,17 @@ async fn test_multicast_rollback_table_operation_failure() { // Get table states after internal group deletion but before external group attempt let initial_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table"); let initial_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table"); let initial_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table"); @@ -6198,19 +6180,19 @@ async fn test_multicast_rollback_table_operation_failure() { // Verify table rollback worked - all tables should be unchanged let post_route_table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await .expect("Should be able to dump route table after rollback"); let post_nat_table = switch .client - .table_dump("pipe.Ingress.nat_ingress.ingress_ipv4_mcast", false) + .table_dump("Ingress.nat_ingress.ingress_ipv4_mcast", false) .await .expect("Should be able to dump NAT table after rollback"); let post_bitmap_table = switch .client - .table_dump("pipe.Egress.mcast_egress.tbl_decap_ports", false) + .table_dump("Egress.mcast_egress.tbl_decap_ports", false) .await .expect("Should be able to dump bitmap table after rollback"); @@ -6335,9 +6317,9 @@ async fn test_multicast_group_get_underlay() -> TestResult { } const SOURCE_FILTER_IPV4_TABLE: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4"; + "Ingress.mcast_ingress.mcast_source_filter_ipv4"; const SOURCE_FILTER_IPV6_TABLE: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6"; + "Ingress.mcast_ingress.mcast_source_filter_ipv6"; /// Test that when `IpSrc::Any` is present in the sources list, only a single /// /0 entry is added to the source filter table (not individual entries for @@ -7180,7 +7162,7 @@ async fn test_vlan_lifecycle_route_entries() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await? .into_inner(); assert!( @@ -7210,7 +7192,7 @@ async fn test_vlan_lifecycle_route_entries() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await? .into_inner(); assert!( @@ -7244,7 +7226,7 @@ async fn test_vlan_lifecycle_route_entries() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter4.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter4.tbl", false) .await? .into_inner(); assert!( @@ -7329,7 +7311,7 @@ async fn test_vlan_lifecycle_route_entries_ipv6() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter6.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter6.tbl", false) .await? .into_inner(); assert!( @@ -7359,7 +7341,7 @@ async fn test_vlan_lifecycle_route_entries_ipv6() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter6.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter6.tbl", false) .await? .into_inner(); assert!( @@ -7393,7 +7375,7 @@ async fn test_vlan_lifecycle_route_entries_ipv6() -> TestResult { let table = switch .client - .table_dump("pipe.Ingress.l3_router.MulticastRouter6.tbl", false) + .table_dump("Ingress.l3_router.MulticastRouter6.tbl", false) .await? .into_inner(); assert!( diff --git a/dpd/src/api_server.rs b/dpd/src/api_server.rs index ce1870dd..80825a1b 100644 --- a/dpd/src/api_server.rs +++ b/dpd/src/api_server.rs @@ -1940,10 +1940,12 @@ impl DpdApi for DpdApiImpl { async fn counter_list( _rqctx: RequestContext>, ) -> Result>, HttpError> { - match counters::get_counter_names() { - Err(e) => Err(e.into()), - Ok(counters) => Ok(HttpResponseOk(counters)), - } + Ok(HttpResponseOk( + counters::get_counter_ids() + .iter() + .map(|id| id.to_string()) + .collect(), + )) } async fn counter_reset( diff --git a/dpd/src/counters.rs b/dpd/src/counters.rs index 2c8e42a4..71ffc0a5 100644 --- a/dpd/src/counters.rs +++ b/dpd/src/counters.rs @@ -10,7 +10,6 @@ /// differ from regular match-action tables in that they are accessed with an /// index number rather than a match-key, and they have no actions associated /// with them. -/// use std::collections::BTreeMap; use std::convert::TryFrom; use std::convert::TryInto; @@ -23,6 +22,10 @@ use crate::types::{DpdError, DpdResult}; use aal::MatchParse; use aal_macros::*; use asic::Handle; +use common::counters::CounterId; +#[cfg(feature = "multicast")] +use common::counters::MulticastCounterId; +use common::table::TableType; use anyhow::Context; use dpd_types::views; @@ -38,160 +41,52 @@ struct IndexKey { /// Represents an indirect counter in a P4 program. pub struct Counter { - /// The CounterId assigned to this counter at build time. - id: CounterId, /// The asic-layer Table structure used to identify the indirect counter in /// the program. table: table::Table, } -/// sidecar.p4 defines the following set of indirect counters. -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(u8)] -enum CounterId { - Service, - Ingress, - Egress, - Packet, - DropPort, - DropReason, - #[cfg(feature = "multicast")] - EgressDropPort, - #[cfg(feature = "multicast")] - EgressDropReason, - #[cfg(feature = "multicast")] - Unicast, +pub fn get_counter_ids() -> Vec { + let mut base = vec![ + CounterId::Service, + CounterId::Ingress, + CounterId::Egress, + CounterId::Packet, + CounterId::DropPort, + CounterId::DropReason, + ]; + let mut multicast; #[cfg(feature = "multicast")] - Multicast, - #[cfg(feature = "multicast")] - MulticastExt, - #[cfg(feature = "multicast")] - MulticastLL, - #[cfg(feature = "multicast")] - MulticastUL, - #[cfg(feature = "multicast")] - MulticastDrop, -} - -impl From for u8 { - fn from(c: CounterId) -> u8 { - c as u8 + { + multicast = vec![ + CounterId::Multicast(MulticastCounterId::EgressDropPort), + CounterId::Multicast(MulticastCounterId::EgressDropReason), + CounterId::Multicast(MulticastCounterId::Unicast), + CounterId::Multicast(MulticastCounterId::Multicast), + CounterId::Multicast(MulticastCounterId::MulticastExt), + CounterId::Multicast(MulticastCounterId::MulticastLL), + CounterId::Multicast(MulticastCounterId::MulticastUL), + CounterId::Multicast(MulticastCounterId::MulticastDrop), + ]; } -} - -/// Each indirect counter is identified by different names at different places -/// in the code flow. This structure is used to define all the different names -/// used by each table in a single place. -struct CounterDescription { - // Each counter is assigned an ID, which lets us associate the static - // definitions with the Counter state maintained in the switch - // structure. - id: CounterId, - // The name by which clients will identify a table - client_name: &'static str, - // The name assigned to the table in the compiled p4 program - p4_name: &'static str, -} - -const BASE_COUNTERS: [CounterDescription; 6] = [ - CounterDescription { - id: CounterId::Service, - client_name: "Service", - p4_name: "pipe.Ingress.services.service_ctr", - }, - CounterDescription { - id: CounterId::Ingress, - client_name: "Ingress", - p4_name: "pipe.Ingress.ingress_ctr", - }, - CounterDescription { - id: CounterId::Packet, - client_name: "Packet", - p4_name: "pipe.Ingress.packet_ctr", - }, - CounterDescription { - id: CounterId::Egress, - client_name: "Egress", - p4_name: "pipe.Ingress.egress_ctr", - }, - CounterDescription { - id: CounterId::DropPort, - client_name: "Ingress_Drop_Port", - p4_name: "pipe.Ingress.drop_port_ctr", - }, - CounterDescription { - id: CounterId::DropReason, - client_name: "Ingress_Drop_Reason", - p4_name: "pipe.Ingress.drop_reason_ctr", - }, -]; - -#[cfg(not(feature = "multicast"))] -const MULTICAST_COUNTERS: [CounterDescription; 0] = []; -#[cfg(feature = "multicast")] -const MULTICAST_COUNTERS: [CounterDescription; 8] = [ - CounterDescription { - id: CounterId::EgressDropPort, - client_name: "Egress_Drop_Port", - p4_name: "pipe.Egress.drop_port_ctr", - }, - CounterDescription { - id: CounterId::EgressDropReason, - client_name: "Egress_Drop_Reason", - p4_name: "pipe.Egress.drop_reason_ctr", - }, - CounterDescription { - id: CounterId::Unicast, - client_name: "Unicast", - p4_name: "pipe.Egress.unicast_ctr", - }, - CounterDescription { - id: CounterId::Multicast, - client_name: "Multicast", - p4_name: "pipe.Egress.mcast_ctr", - }, - CounterDescription { - id: CounterId::MulticastExt, - client_name: "Multicast_External", - p4_name: "pipe.Egress.external_mcast_ctr", - }, - CounterDescription { - id: CounterId::MulticastLL, - client_name: "Multicast_Link_Local", - p4_name: "pipe.Egress.link_local_mcast_ctr", - }, - CounterDescription { - id: CounterId::MulticastUL, - client_name: "Multicast_Underlay", - p4_name: "pipe.Egress.underlay_mcast_ctr", - }, - CounterDescription { - id: CounterId::MulticastDrop, - client_name: "Multicast_Drop", - p4_name: "pipe.Ingress.filter.drop_mcast_ctr", - }, -]; - -/// Get the list of names by which end users can refer to a counter. -pub fn get_counter_names() -> DpdResult> { - Ok(BASE_COUNTERS - .iter() - .chain(MULTICAST_COUNTERS.iter()) - .map(|c| c.client_name.to_string()) - .collect()) + #[cfg(not(feature = "multicast"))] + { + multicast = Vec::new(); + } + base.append(&mut multicast); + base } /// Fetch a counter by name from the switch's list of counters. This call /// returns a pointer to the mutex that controls access to the counter, and the /// caller is responsible for locking the mutex. -fn get_counter<'a>( - switch: &'a Switch, - name: &str, -) -> DpdResult<&'a Mutex> { - let name = name.to_lowercase(); +fn get_counter( + switch: &Switch, + counter: CounterId, +) -> DpdResult<&Mutex> { switch .counters - .get(&name) + .get(&counter) .ok_or(DpdError::Invalid("no such counter".to_string())) } @@ -403,22 +298,14 @@ pub async fn get_values( force_sync: bool, counter_name: String, ) -> DpdResult> { - let counter_id = { - // While we are grabbing the CounterId here, the primary purpose of this - // little dance is to verify that the counter exists before referencing it - // in the closure below. We can't just pass the counter into the closure, - // because the compiler is worried that the Counter lifetime will exceed - // that of the Switch that contains it. - get_counter(switch, &counter_name)?.lock().unwrap().id - }; + let counter_id = counter_name.parse::()?; let counters = { let switch = switch.clone(); - let name = counter_name.clone(); // Because this call may initiate an ASIC->memory sync operation it can // be long-running, so we run it in a new task. tokio::task::spawn_blocking(move || { - get_counter(&switch, &name) + get_counter(&switch, counter_id) .expect("verified that the table exists in the outer function") .lock() .unwrap() @@ -440,15 +327,19 @@ pub async fn get_values( } CounterId::DropReason => reason_label(idx.idx as u8)?, #[cfg(feature = "multicast")] - CounterId::EgressDropPort - | CounterId::Unicast - | CounterId::Multicast - | CounterId::MulticastExt - | CounterId::MulticastLL - | CounterId::MulticastUL - | CounterId::MulticastDrop => port_label(switch, idx.idx).await, + CounterId::Multicast(MulticastCounterId::EgressDropPort) + | CounterId::Multicast(MulticastCounterId::Unicast) + | CounterId::Multicast(MulticastCounterId::Multicast) + | CounterId::Multicast(MulticastCounterId::MulticastExt) + | CounterId::Multicast(MulticastCounterId::MulticastLL) + | CounterId::Multicast(MulticastCounterId::MulticastUL) + | CounterId::Multicast(MulticastCounterId::MulticastDrop) => { + port_label(switch, idx.idx).await + } #[cfg(feature = "multicast")] - CounterId::EgressDropReason => reason_label(idx.idx as u8)?, + CounterId::Multicast(MulticastCounterId::EgressDropReason) => { + reason_label(idx.idx as u8)? + } }; if let Some(key) = key { @@ -473,22 +364,24 @@ pub async fn get_values( } pub fn reset(switch: &Switch, counter_name: String) -> DpdResult<()> { - let mut counter = get_counter(switch, &counter_name)?.lock().unwrap(); + let id = counter_name.parse::()?; + let mut counter = get_counter(switch, id)?.lock().unwrap(); counter.table.clear(&switch.asic_hdl) } /// Create internal structures for managing the counters built into sidecar.p4 -pub fn init(hdl: &Handle) -> anyhow::Result>> { +pub fn init( + hdl: &Handle, +) -> anyhow::Result>> { let mut counters = BTreeMap::new(); - for c in BASE_COUNTERS.iter().chain(MULTICAST_COUNTERS.iter()) { + for id in get_counter_ids() { + let name = id.to_string(); counters.insert( - c.client_name.to_string().to_lowercase(), + id, Mutex::new(Counter { - id: c.id, - table: table::Table::new(hdl, c.p4_name).with_context( - || format!("creating {} counter", c.client_name), - )?, + table: table::Table::new(hdl, TableType::Counter(id)) + .with_context(|| format!("creating {name} counter"))?, }), ); } diff --git a/dpd/src/link.rs b/dpd/src/link.rs index 4dadb56d..94a18d85 100644 --- a/dpd/src/link.rs +++ b/dpd/src/link.rs @@ -13,11 +13,10 @@ use crate::fault::Faultable; use crate::fault::LinkUpTracker; use crate::ports::AdminEvent; use crate::ports::Event; -use crate::table::MacOps; +use crate::table::mac; #[cfg(feature = "multicast")] use crate::table::mcast; use crate::table::port_ip; -use crate::table::port_mac; use crate::table::uplink; use crate::transceivers::qsfp_xcvr_mpn; use crate::types::DpdError; @@ -1565,26 +1564,22 @@ fn set_mac_config( asic_id: AsicId, mac: MacAddr, ) -> DpdResult<()> { - MacOps::::mac_set(switch, asic_id, mac)?; + mac::mac_set(switch, asic_id, mac)?; #[cfg(feature = "multicast")] { - MacOps::::mac_set( - switch, asic_id, mac, - )?; + mac::mcast_mac_set(switch, asic_id, mac)?; mcast::mcast_egress::add_port_mapping_entry(switch, asic_id)?; } Ok(()) } fn clear_mac_config(switch: &Switch, asic_id: AsicId) -> DpdResult<()> { - MacOps::::mac_clear(switch, asic_id)?; + mac::mac_clear(switch, asic_id)?; #[cfg(feature = "multicast")] { - MacOps::::mac_clear( - switch, asic_id, - )?; + mac::mcast_mac_clear(switch, asic_id)?; mcast::mcast_egress::del_port_mapping_entry(switch, asic_id)?; } Ok(()) diff --git a/dpd/src/macaddrs.rs b/dpd/src/macaddrs.rs index ecf48d94..b095c6ac 100644 --- a/dpd/src/macaddrs.rs +++ b/dpd/src/macaddrs.rs @@ -11,6 +11,8 @@ use slog::debug; use slog::o; use crate::Switch; +#[cfg(feature = "tofino_asic")] +use crate::table::mac; use crate::types::DpdError; use crate::types::DpdResult; use common::network::MacAddr; @@ -24,8 +26,6 @@ cfg_if::cfg_if! { use std::convert::TryFrom; #[cfg(feature = "multicast")] use crate::table::mcast; - use crate::table::port_mac; - use crate::table::MacOps; use common::ports::PortFec; use common::ports::PortSpeed; use common::ports::InternalPort; @@ -428,10 +428,10 @@ impl Switch { } // Reset ingress and egress MAC tables and Port ID table(s). - MacOps::::reset(self)?; + mac::reset(self)?; #[cfg(feature = "multicast")] { - MacOps::::reset(self)?; + mac::mcast_reset(self)?; mcast::mcast_egress::reset_bitmap_table(self)?; } diff --git a/dpd/src/main.rs b/dpd/src/main.rs index 22436893..04af8169 100644 --- a/dpd/src/main.rs +++ b/dpd/src/main.rs @@ -40,8 +40,10 @@ use crate::port_map::SidecarRevision; use crate::rpw::WorkflowServer; use crate::switch_port::SwitchPorts; use aal::{ActionParse, AsicError, MatchParse}; +use common::counters::CounterId; use common::network::MacAddr; use common::ports::PortId; +use common::table::TableType; use dpd_types::switch_identifiers::SwitchIdentifiers; use table::Table; use types::*; @@ -185,8 +187,8 @@ pub struct Switch { pub config: Mutex, pub log: slog::Logger, pub asic_hdl: asic::Handle, - pub tables: BTreeMap>, - pub counters: BTreeMap>, + pub tables: BTreeMap>, + pub counters: BTreeMap>, pub links: Mutex, pub routes: TokioMutex, pub arp: Mutex, @@ -321,10 +323,7 @@ impl Switch { } /// Get exclusive access to a table of the requested type, if it exists. - pub fn table_get( - &self, - id: table::TableType, - ) -> DpdResult> { + pub fn table_get(&self, id: TableType) -> DpdResult> { match self.tables.get(&id) { Some(table) => Ok(table.lock().unwrap()), None => Err("no such table".into()), @@ -332,19 +331,15 @@ impl Switch { } // Add a new table to the internal list of tables. - fn table_add( - &mut self, - name: &str, - id: table::TableType, - ) -> anyhow::Result<()> { - let t = table::Table::new(&self.asic_hdl, name) + fn table_add(&mut self, id: TableType) -> anyhow::Result<()> { + let t = table::Table::new(&self.asic_hdl, id) .with_context(|| format!("creating {id:?} table"))?; self.tables.insert(id, Mutex::new(t)); Ok(()) } /// Returns the number of entries the table can hold - pub fn table_size(&self, table_type: table::TableType) -> DpdResult { + pub fn table_size(&self, table_type: TableType) -> DpdResult { let t = self.table_get(table_type)?; Ok(t.size()) } @@ -352,7 +347,7 @@ impl Switch { /// Add a single entry to the requested table. pub fn table_entry_add( &self, - table_type: table::TableType, + table_type: TableType, key: &M, data: &A, ) -> DpdResult<()> { @@ -369,7 +364,7 @@ impl Switch { /// Update a single table entry. pub fn table_entry_update( &self, - table_type: table::TableType, + table_type: TableType, key: &M, data: &A, ) -> DpdResult<()> { @@ -386,7 +381,7 @@ impl Switch { /// Delete a single table entry. pub fn table_entry_del( &self, - table_type: table::TableType, + table_type: TableType, key: &M, ) -> DpdResult<()> { let mut t = self.table_get(table_type)?; @@ -402,19 +397,19 @@ impl Switch { /// Fetch all of the entries in a P4 table and return them pub fn table_dump( &self, - t: table::TableType, + t: TableType, from_hardware: bool, ) -> DpdResult { let t = self.table_get(t)?; Ok(dpd_types::views::Table { - name: t.name.to_string(), + name: t.type_.to_string(), size: t.usage.size as usize, entries: t .get_entries::(&self.asic_hdl, from_hardware) .map_err(|e| { error!(self.log, "failed to get table contents"; - "table" => t.name.to_string(), + "table" => t.type_.to_string(), "error" => %e); e }) @@ -432,14 +427,14 @@ impl Switch { pub fn counter_fetch( &self, force_sync: bool, - t: table::TableType, + t: TableType, ) -> DpdResult> { let t = self.table_get(t)?; t.get_counters::(&self.asic_hdl, force_sync) .map_err(|e| { error!(self.log, "failed to get counter data"; - "table" => t.name.to_string(), + "table" => t.type_.to_string(), "error" => %e); e }) @@ -453,7 +448,7 @@ impl Switch { } /// Completely clear the requested table. - pub fn table_clear(&self, t: table::TableType) -> DpdResult<()> { + pub fn table_clear(&self, t: TableType) -> DpdResult<()> { let mut t = self.table_get(t)?; t.clear(&self.asic_hdl) } diff --git a/dpd/src/oxstats.rs b/dpd/src/oxstats.rs index 5eabe43e..7727ab01 100644 --- a/dpd/src/oxstats.rs +++ b/dpd/src/oxstats.rs @@ -11,6 +11,7 @@ use std::time::{Duration, Instant}; use chrono::Utc; use common::ports::PortId; +use common::table::TableType; use dpd_types::link::{LinkId, LinkState}; use dpd_types::oxstats::{OximeterConfig, OximeterMetadata}; use dpd_types::switch_identifiers::SwitchIdentifiers; @@ -29,7 +30,6 @@ use oximeter::{MetricsError, Producer}; use crate::DpdResult; use crate::Switch; -use crate::table; use aal::PortHdl; use asic::AsicLinkStats; use asic::FsmStats; @@ -208,7 +208,7 @@ impl TableStats { } // Update this link's metrics using the latest observations - pub fn update_stats(&mut self, switch: &Switch, id: table::TableType) { + pub fn update_stats(&mut self, switch: &Switch, id: TableType) { let table = switch.table_get(id).expect("table population is fixed"); let usage = &table.usage.clone(); self.capacity.datum = u64::from(usage.size); @@ -291,7 +291,7 @@ struct Oxstats { /// Stats for each link link_stats: Mutex>, /// Statistics for each table - table_stats: BTreeMap, + table_stats: BTreeMap, } impl std::fmt::Debug for Oxstats { @@ -319,7 +319,7 @@ impl Oxstats { // The population of tables doesn't change over time, so we can safely // allocate the TableStats structures at init. let mut table_stats = BTreeMap::new(); - for (id, table) in switch.tables.iter() { + for id in switch.tables.keys() { let table = SwitchTable { rack_id: sled_identifiers.rack_id, sled_id: sled_identifiers.sled_id, @@ -343,7 +343,7 @@ impl Oxstats { .wafer_loc .map(|[_, y]| y) .unwrap_or(0), - table: table.lock().unwrap().name.clone().into(), + table: id.to_string().into(), }; table_stats.insert(*id, TableStats::new(table)); } diff --git a/dpd/src/route.rs b/dpd/src/route.rs index 5cbe8b50..e6421ca2 100644 --- a/dpd/src/route.rs +++ b/dpd/src/route.rs @@ -120,6 +120,7 @@ use crate::freemap; use crate::types::{DpdError, DpdResult}; use crate::{Switch, table}; use common::ports::PortId; +use common::table::TableType; use oxnet::{IpNet, Ipv4Net, Ipv6Net}; // These are the largest numbers of targets supported for a single route @@ -519,14 +520,14 @@ fn add_route_locked( let max_targets; if subnet.is_ipv4() { max_targets = MAX_TARGETS_IPV4; - route_data.v4_freemap.maybe_init( - switch.table_size(table::TableType::RouteFwdIpv4)? as u16, - ); + route_data + .v4_freemap + .maybe_init(switch.table_size(TableType::RouteFwdIpv4)? as u16); } else { max_targets = MAX_TARGETS_IPV6; - route_data.v6_freemap.maybe_init( - switch.table_size(table::TableType::RouteFwdIpv6)? as u16, - ); + route_data + .v6_freemap + .maybe_init(switch.table_size(TableType::RouteFwdIpv6)? as u16); } // Get the old set of targets that we'll be adding to diff --git a/dpd/src/table/arp_ipv4.rs b/dpd/src/table/arp_ipv4.rs index 963ec673..2824014e 100644 --- a/dpd/src/table/arp_ipv4.rs +++ b/dpd/src/table/arp_ipv4.rs @@ -14,8 +14,6 @@ use common::network::MacAddr; use aal::{ActionParse, MatchParse}; -pub const TABLE_NAME: &str = "pipe.Ingress.l3_router.Arp.tbl"; - #[derive(MatchParse, Hash)] struct MatchKey { #[match_xlate(name = "nexthop_ipv4")] diff --git a/dpd/src/table/attached_subnet_v4.rs b/dpd/src/table/attached_subnet_v4.rs index 967bc67a..dedc2e98 100644 --- a/dpd/src/table/attached_subnet_v4.rs +++ b/dpd/src/table/attached_subnet_v4.rs @@ -17,9 +17,6 @@ use crate::Switch; use crate::table::*; use common::network::{InstanceTarget, MacAddr}; -pub const EXT_SUBNET_IPV4_TABLE_NAME: &str = - "pipe.Ingress.attached_subnet_ingress.attached_subnets_v4"; - // Used to identify entries in the external subnet table #[derive(MatchParse, Hash, Debug)] struct AttachedSubnetV4MatchKey { diff --git a/dpd/src/table/attached_subnet_v6.rs b/dpd/src/table/attached_subnet_v6.rs index d38b352c..743f4895 100644 --- a/dpd/src/table/attached_subnet_v6.rs +++ b/dpd/src/table/attached_subnet_v6.rs @@ -16,9 +16,6 @@ use crate::Switch; use crate::table::*; use common::network::{InstanceTarget, MacAddr}; -pub const EXT_SUBNET_IPV6_TABLE_NAME: &str = - "pipe.Ingress.attached_subnet_ingress.attached_subnets_v6"; - // Used to identify entries in the external subnet table #[derive(MatchParse, Hash, Debug)] struct AttachedSubnetV6MatchKey { diff --git a/dpd/src/table/mac.rs b/dpd/src/table/mac.rs new file mode 100644 index 00000000..0627f037 --- /dev/null +++ b/dpd/src/table/mac.rs @@ -0,0 +1,140 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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 2026 Oxide Computer Company + +use slog::error; +use slog::info; + +use crate::Switch; +use crate::types::*; +use aal::ActionParse; +use aal::MatchParse; +use aal_macros::ActionParse; +use aal_macros::MatchParse; +use common::network::MacAddr; +use common::table::TableType; +use dpd_types::views; + +#[derive(MatchParse, Debug, Hash)] +struct MacMatchKey { + port: u16, +} + +#[derive(ActionParse, Debug)] +enum MacAction { + #[action_xlate(name = "rewrite")] + Rewrite { mac: MacAddr }, +} + +fn mac_set_common( + s: &Switch, + type_: TableType, + port: u16, + mac: MacAddr, +) -> DpdResult<()> { + let match_key = MacMatchKey { port }; + let action_data = MacAction::Rewrite { mac }; + + match s.table_entry_add(type_, &match_key, &action_data) { + Ok(_) => { + info!(s.log, "set mac on {port} in table {type_}: {mac}",); + Ok(()) + } + Err(e) => { + error!( + s.log, + "set mac on {port} in table {type_}: {mac} failed: {e:?}", + ); + Err(e) + } + } +} + +fn mac_clear_common(s: &Switch, type_: TableType, port: u16) -> DpdResult<()> { + let match_key = MacMatchKey { port }; + + match s.table_entry_del(type_, &match_key) { + Ok(_) => { + info!(s.log, "cleared mac on {port} in table {type_}",); + Ok(()) + } + Err(e) => { + error!(s.log, "clear mac on {port} in table {type_} failed: {e:?}",); + Err(e) + } + } +} + +/// Add a new entry to the MAC table. +/// +/// An error is returned if the entry already exists. Use `mac_update` instead. +pub fn mac_set(s: &Switch, port: u16, mac: MacAddr) -> DpdResult<()> { + mac_set_common(s, TableType::PortMacAddress, port, mac) +} + +/// Remove an entry from the MAC table. +pub fn mac_clear(s: &Switch, port: u16) -> DpdResult<()> { + mac_clear_common(s, TableType::PortMacAddress, port) +} + +pub fn table_dump(s: &Switch, from_hardware: bool) -> DpdResult { + s.table_dump::( + TableType::PortMacAddress, + from_hardware, + ) +} + +pub fn counter_fetch( + s: &Switch, + force_sync: bool, +) -> DpdResult> { + s.counter_fetch::(force_sync, TableType::PortMacAddress) +} + +/// Remove all entries from the MAC table. +#[cfg(feature = "tofino_asic")] +pub fn reset(s: &Switch) -> DpdResult<()> { + s.table_clear(TableType::PortMacAddress) +} + +/// Add a new entry to the MAC table. +/// +/// An error is returned if the entry already exists. Use `mac_update` instead. +#[cfg(feature = "multicast")] +pub fn mcast_mac_set(s: &Switch, port: u16, mac: MacAddr) -> DpdResult<()> { + mac_set_common(s, TableType::PortMacAddressMcast, port, mac) +} + +/// Remove an entry from the MAC table. +#[cfg(feature = "multicast")] +pub fn mcast_mac_clear(s: &Switch, port: u16) -> DpdResult<()> { + mac_clear_common(s, TableType::PortMacAddressMcast, port) +} + +#[cfg(feature = "multicast")] +pub fn mcast_table_dump( + s: &Switch, + from_hardware: bool, +) -> DpdResult { + s.table_dump::( + TableType::PortMacAddressMcast, + from_hardware, + ) +} + +#[cfg(feature = "multicast")] +pub fn mcast_counter_fetch( + s: &Switch, + force_sync: bool, +) -> DpdResult> { + s.counter_fetch::(force_sync, TableType::PortMacAddressMcast) +} + +/// Remove all entries from the MAC table. +#[cfg(feature = "multicast")] +#[cfg(feature = "tofino_asic")] +pub fn mcast_reset(s: &Switch) -> DpdResult<()> { + s.table_clear(TableType::PortMacAddressMcast) +} diff --git a/dpd/src/table/mcast/mcast_egress.rs b/dpd/src/table/mcast/mcast_egress.rs index b9d1355c..8a432846 100644 --- a/dpd/src/table/mcast/mcast_egress.rs +++ b/dpd/src/table/mcast/mcast_egress.rs @@ -15,15 +15,6 @@ use aal_macros::*; use dpd_types::mcast::MulticastGroupId; use slog::debug; -/// Table for multicast egress entries matching the multicast group ID -/// and setting which ports to possibly decap. -pub(crate) const DECAP_PORTS_TABLE_NAME: &str = - "pipe.Egress.mcast_egress.tbl_decap_ports"; - -/// Table for multicast egress entries matching the replication group ID. -pub(crate) const PORT_ID_TABLE_NAME: &str = - "pipe.Egress.mcast_egress.asic_id_to_port"; - #[derive(MatchParse, Hash)] struct MatchKeyDecapPorts { #[match_xlate(name = "egress_rid")] diff --git a/dpd/src/table/mcast/mcast_nat.rs b/dpd/src/table/mcast/mcast_nat.rs index 0ccc999e..b9e307b6 100644 --- a/dpd/src/table/mcast/mcast_nat.rs +++ b/dpd/src/table/mcast/mcast_nat.rs @@ -21,13 +21,6 @@ use aal_macros::*; use common::network::{MacAddr, NatTarget}; use slog::debug; -/// IPv4 Table for multicast NAT entries. -pub(crate) const IPV4_TABLE_NAME: &str = - "pipe.Ingress.nat_ingress.ingress_ipv4_mcast"; -/// IPv6 Table for multicast NAT entries. -pub(crate) const IPV6_TABLE_NAME: &str = - "pipe.Ingress.nat_ingress.ingress_ipv6_mcast"; - #[derive(ActionParse, Debug)] enum Ipv4Action { #[action_xlate(name = "mcast_forward_ipv4_to")] diff --git a/dpd/src/table/mcast/mcast_port_mac.rs b/dpd/src/table/mcast/mcast_port_mac.rs deleted file mode 100644 index 8099724a..00000000 --- a/dpd/src/table/mcast/mcast_port_mac.rs +++ /dev/null @@ -1,25 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// 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 2026 Oxide Computer Company - -//! Table operations for multicast port MAC entries. - -use crate::table::{MacTable, TableType}; - -/// Table for multicast port MAC entries. -pub const TABLE_NAME: &str = "pipe.Egress.mac_rewrite.mac_rewrite"; - -/// Table for multicast port MAC entries. -pub struct PortMacTable; - -impl MacTable for PortMacTable { - fn table_type() -> TableType { - TableType::PortMacMcast - } - - fn table_name() -> &'static str { - TABLE_NAME - } -} diff --git a/dpd/src/table/mcast/mcast_replication.rs b/dpd/src/table/mcast/mcast_replication.rs index 44c97f8d..50815c8d 100644 --- a/dpd/src/table/mcast/mcast_replication.rs +++ b/dpd/src/table/mcast/mcast_replication.rs @@ -17,10 +17,6 @@ use aal_macros::*; use dpd_types::mcast::MulticastGroupId; use slog::debug; -/// IPv6 Table for multicast replication entries and group membership. -pub(crate) const IPV6_TABLE_NAME: &str = - "pipe.Ingress.mcast_ingress.mcast_replication_ipv6"; - #[derive(ActionParse, Debug)] enum Ipv6Action { #[action_xlate(name = "configure_mcastv6")] diff --git a/dpd/src/table/mcast/mcast_route.rs b/dpd/src/table/mcast/mcast_route.rs index 5a8447e0..e383f459 100644 --- a/dpd/src/table/mcast/mcast_route.rs +++ b/dpd/src/table/mcast/mcast_route.rs @@ -23,13 +23,6 @@ use slog::debug; use super::{Ipv4MatchKey, Ipv6MatchKey}; use crate::{Switch, table::*}; -/// IPv4 Table for multicast routing entries. -pub(crate) const IPV4_TABLE_NAME: &str = - "pipe.Ingress.l3_router.MulticastRouter4.tbl"; -/// IPv6 Table for multicast routing entries. -pub(crate) const IPV6_TABLE_NAME: &str = - "pipe.Ingress.l3_router.MulticastRouter6.tbl"; - #[derive(ActionParse, Debug)] enum Ipv4Action { #[action_xlate(name = "forward")] diff --git a/dpd/src/table/mcast/mcast_src_filter.rs b/dpd/src/table/mcast/mcast_src_filter.rs index 13f3bd5c..775f17c0 100644 --- a/dpd/src/table/mcast/mcast_src_filter.rs +++ b/dpd/src/table/mcast/mcast_src_filter.rs @@ -18,13 +18,6 @@ use aal_macros::*; use oxnet::{Ipv4Net, Ipv6Net}; use slog::debug; -/// IPv4 Table for multicast source filter entries. -pub(crate) const IPV4_TABLE_NAME: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4"; -/// IPv6 Table for multicast source filter entries. -pub(crate) const IPV6_TABLE_NAME: &str = - "pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6"; - #[derive(MatchParse, Hash)] struct Ipv4MatchKey { #[match_xlate(name = "src_addr", type = "lpm")] diff --git a/dpd/src/table/mcast/mod.rs b/dpd/src/table/mcast/mod.rs index 02802625..5630d016 100644 --- a/dpd/src/table/mcast/mod.rs +++ b/dpd/src/table/mcast/mod.rs @@ -17,7 +17,6 @@ use aal_macros::*; pub(crate) mod mcast_egress; pub(crate) mod mcast_nat; -pub(crate) mod mcast_port_mac; pub(crate) mod mcast_replication; pub(crate) mod mcast_route; pub(crate) mod mcast_src_filter; diff --git a/dpd/src/table/mod.rs b/dpd/src/table/mod.rs index 83ca9fed..8abcd957 100644 --- a/dpd/src/table/mod.rs +++ b/dpd/src/table/mod.rs @@ -7,76 +7,30 @@ use std::convert::TryFrom; use std::hash::Hash; -use slog::{debug, error, info}; +use common::table; +use slog::debug; use crate::Switch; use crate::types::*; use aal::ActionParse; use aal::MatchParse; use aal::TableOps; -use common::network::MacAddr; +use common::table::TableType; use dpd_types::views; pub mod arp_ipv4; pub mod attached_subnet_v4; pub mod attached_subnet_v6; +pub mod mac; #[cfg(feature = "multicast")] pub mod mcast; pub mod nat; pub mod neighbor_ipv6; pub mod port_ip; -pub mod port_mac; pub mod route_ipv4; pub mod route_ipv6; pub mod uplink; -const BASE_TABLES: [(&str, TableType); 15] = [ - (route_ipv4::INDEX_TABLE_NAME, TableType::RouteIdxIpv4), - (route_ipv4::FORWARD_TABLE_NAME, TableType::RouteFwdIpv4), - (route_ipv6::INDEX_TABLE_NAME, TableType::RouteIdxIpv6), - (route_ipv6::FORWARD_TABLE_NAME, TableType::RouteFwdIpv6), - (arp_ipv4::TABLE_NAME, TableType::ArpIpv4), - (neighbor_ipv6::TABLE_NAME, TableType::NeighborIpv6), - (port_mac::TABLE_NAME, TableType::PortMac), - (port_ip::IPV4_TABLE_NAME, TableType::PortIpv4), - (port_ip::IPV6_TABLE_NAME, TableType::PortIpv6), - (nat::IPV4_TABLE_NAME, TableType::NatIngressIpv4), - (nat::IPV6_TABLE_NAME, TableType::NatIngressIpv6), - (uplink::INGRESS_TABLE_NAME, TableType::UplinkIngress), - (uplink::EGRESS_TABLE_NAME, TableType::UplinkEgress), - ( - attached_subnet_v4::EXT_SUBNET_IPV4_TABLE_NAME, - TableType::AttachedSubnetIpv4, - ), - ( - attached_subnet_v6::EXT_SUBNET_IPV6_TABLE_NAME, - TableType::AttachedSubnetIpv6, - ), -]; - -#[cfg(feature = "multicast")] -const MCAST_TABLES: [(&str, TableType); 10] = [ - (mcast::mcast_replication::IPV6_TABLE_NAME, TableType::McastIpv6), - (mcast::mcast_src_filter::IPV4_TABLE_NAME, TableType::McastIpv4SrcFilter), - (mcast::mcast_src_filter::IPV6_TABLE_NAME, TableType::McastIpv6SrcFilter), - (mcast::mcast_nat::IPV4_TABLE_NAME, TableType::NatIngressIpv4Mcast), - (mcast::mcast_nat::IPV6_TABLE_NAME, TableType::NatIngressIpv6Mcast), - (mcast::mcast_route::IPV4_TABLE_NAME, TableType::RouteIpv4Mcast), - (mcast::mcast_route::IPV6_TABLE_NAME, TableType::RouteIpv6Mcast), - (mcast::mcast_port_mac::TABLE_NAME, TableType::PortMacMcast), - ( - mcast::mcast_egress::DECAP_PORTS_TABLE_NAME, - TableType::McastEgressDecapPorts, - ), - ( - mcast::mcast_egress::PORT_ID_TABLE_NAME, - TableType::McastEgressPortMapping, - ), -]; - -#[cfg(not(feature = "multicast"))] -const MCAST_TABLES: [(&str, TableType); 0] = []; - /// Basic statistics about p4 table usage #[derive(Clone, Debug, Default)] pub struct TableUsage { @@ -120,7 +74,7 @@ impl TableUsage { /// A P4 table. pub struct Table { /// Name of the table - pub name: String, + pub type_: TableType, /// Basic capacity and usage statistics pub usage: TableUsage, /// asic-specific data @@ -129,17 +83,13 @@ pub struct Table { impl Table { /// Allocate a new ASIC table with an empty usage structure - pub fn new(hdl: &asic::Handle, name: &str) -> DpdResult + pub fn new(hdl: &asic::Handle, type_: TableType) -> DpdResult where Self: Sized, { - let asic_data = asic::Table::new(hdl, name)?; + let asic_data = asic::Table::new(hdl, type_)?; let size = asic_data.size(); - Ok(Table { - name: name.to_string(), - usage: TableUsage::new(size as u32), - asic_data, - }) + Ok(Table { type_, usage: TableUsage::new(size as u32), asic_data }) } /// Returns the number of entries the table can hold @@ -169,7 +119,7 @@ impl Table { { if self.usage.occupancy == self.usage.size { self.usage.exhaustion += 1; - Err(DpdError::TableFull(self.name.clone())) + Err(DpdError::TableFull(self.type_.to_string())) } else { self.asic_data .entry_add(hdl, key, data) @@ -255,7 +205,7 @@ impl Table { } pub fn list(switch: &Switch) -> Vec { - switch.tables.values().map(|t| t.lock().unwrap().name.to_string()).collect() + switch.tables.keys().map(|t| t.to_string()).collect() } /// Given the name of a table, call into the table-specific code to get the @@ -294,11 +244,13 @@ pub fn get_entries( TableType::AttachedSubnetIpv6 => { attached_subnet_v6::table_dump(switch, from_hardware) } - TableType::PortIpv4 => port_ip::ipv4_table_dump(switch, from_hardware), - TableType::PortIpv6 => port_ip::ipv6_table_dump(switch, from_hardware), - TableType::PortMac => { - MacOps::::table_dump(switch, from_hardware) + TableType::PortAddrIpv4 => { + port_ip::ipv4_table_dump(switch, from_hardware) + } + TableType::PortAddrIpv6 => { + port_ip::ipv6_table_dump(switch, from_hardware) } + TableType::PortMacAddress => mac::table_dump(switch, from_hardware), TableType::UplinkEgress => { uplink::egress_table_dump(switch, from_hardware) } @@ -334,11 +286,8 @@ pub fn get_entries( mcast::mcast_route::ipv6_table_dump(switch, from_hardware) } #[cfg(feature = "multicast")] - TableType::PortMacMcast => { - MacOps::::table_dump( - switch, - from_hardware, - ) + TableType::PortMacAddressMcast => { + mac::mcast_table_dump(switch, from_hardware) } #[cfg(feature = "multicast")] TableType::McastEgressDecapPorts => { @@ -348,6 +297,9 @@ pub fn get_entries( TableType::McastEgressPortMapping => { mcast::mcast_egress::port_mapping_table_dump(switch, from_hardware) } + x => Err(DpdError::Other(format!( + "table {x} has no associated table entries" + ))), } } @@ -375,17 +327,19 @@ pub fn get_counters( neighbor_ipv6::counter_fetch(switch, force_sync) } TableType::ArpIpv4 => arp_ipv4::counter_fetch(switch, force_sync), - TableType::PortMac => { - MacOps::::counter_fetch(switch, force_sync) - } + TableType::PortMacAddress => mac::counter_fetch(switch, force_sync), TableType::NatIngressIpv4 => { nat::ipv4_counter_fetch(switch, force_sync) } TableType::NatIngressIpv6 => { nat::ipv6_counter_fetch(switch, force_sync) } - TableType::PortIpv4 => port_ip::ipv4_counter_fetch(switch, force_sync), - TableType::PortIpv6 => port_ip::ipv6_counter_fetch(switch, force_sync), + TableType::PortAddrIpv4 => { + port_ip::ipv4_counter_fetch(switch, force_sync) + } + TableType::PortAddrIpv6 => { + port_ip::ipv6_counter_fetch(switch, force_sync) + } TableType::AttachedSubnetIpv4 => { attached_subnet_v4::counter_fetch(switch, force_sync) } @@ -435,217 +389,21 @@ pub fn get_counters( mcast::mcast_egress::port_mapping_counter_fetch(switch, force_sync) } #[cfg(feature = "multicast")] - TableType::PortMacMcast => { - MacOps::::counter_fetch( - switch, force_sync, - ) + TableType::PortMacAddressMcast => { + mac::mcast_counter_fetch(switch, force_sync) } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum TableType { - RouteIdxIpv4, - RouteFwdIpv4, - RouteIdxIpv6, - RouteFwdIpv6, - #[cfg(feature = "multicast")] - RouteIpv4Mcast, - #[cfg(feature = "multicast")] - RouteIpv6Mcast, - ArpIpv4, - NeighborIpv6, - PortMac, - PortIpv4, - PortIpv6, - NatIngressIpv4, - NatIngressIpv6, - UplinkIngress, - UplinkEgress, - AttachedSubnetIpv4, - AttachedSubnetIpv6, - #[cfg(feature = "multicast")] - McastIpv6, - #[cfg(feature = "multicast")] - McastIpv4SrcFilter, - #[cfg(feature = "multicast")] - McastIpv6SrcFilter, - #[cfg(feature = "multicast")] - NatIngressIpv4Mcast, - #[cfg(feature = "multicast")] - NatIngressIpv6Mcast, - #[cfg(feature = "multicast")] - PortMacMcast, - #[cfg(feature = "multicast")] - McastEgressDecapPorts, - #[cfg(feature = "multicast")] - McastEgressPortMapping, -} - -fn name_to_type() -> impl Iterator { - BASE_TABLES.iter().chain(MCAST_TABLES.iter()) -} - -impl TryFrom<&str> for TableType { - type Error = DpdError; - - fn try_from(name: &str) -> Result { - let name = name.to_lowercase(); - for (table_name, table_type) in name_to_type() { - if table_name.to_lowercase() == name { - return Ok(*table_type); - } - } - Err(DpdError::NoSuchTable(name)) + x => Err(DpdError::Other(format!( + "table {x} has no associated counters" + ))), } } pub fn init(switch: &mut Switch) -> anyhow::Result<()> { debug!(switch.log, "initializing tables"); - for (name, table_type) in name_to_type() { - switch.table_add(name, *table_type)?; + for table_type in table::get_table_types() { + switch.table_add(table_type)?; } Ok(()) } - -// Common trait for Mac-related table rewriting -pub trait MacTable { - // The table type identifier - fn table_type() -> TableType; - fn table_name() -> &'static str; -} - -#[derive(aal_macros::MatchParse, Debug, Hash)] -struct MacMatchKey { - port: u16, -} - -#[derive(aal_macros::ActionParse, Debug)] -enum MacAction { - #[action_xlate(name = "rewrite")] - Rewrite { mac: MacAddr }, -} - -// Generic MAC operations that work with any table that implements MacTable -pub struct MacOps { - _phantom: std::marker::PhantomData, -} - -impl MacOps { - /// Update an _existing_ entry in the MAC table. - #[allow(dead_code)] - pub(crate) fn mac_update( - s: &Switch, - port: u16, - mac: MacAddr, - ) -> DpdResult<()> { - let match_key = MacMatchKey { port }; - let action_data = MacAction::Rewrite { mac }; - - match s.table_entry_update(T::table_type(), &match_key, &action_data) { - Ok(_) => { - info!( - s.log, - "update mac on {} in table {}: {}", - port, - T::table_name(), - mac - ); - Ok(()) - } - Err(e) => { - error!( - s.log, - "update mac on {} in table {}: {} failed: {:?}", - port, - T::table_name(), - mac, - e - ); - Err(e) - } - } - } - - /// Add a new entry to the MAC table. - /// - /// An error is returned if the entry already exists. Use `mac_update` instead. - pub fn mac_set(s: &Switch, port: u16, mac: MacAddr) -> DpdResult<()> { - let match_key = MacMatchKey { port }; - let action_data = MacAction::Rewrite { mac }; - - match s.table_entry_add(T::table_type(), &match_key, &action_data) { - Ok(_) => { - info!( - s.log, - "set mac on {} in table {}: {}", - port, - T::table_name(), - mac - ); - Ok(()) - } - Err(e) => { - error!( - s.log, - "set mac on {} in table {}: {} failed: {:?}", - port, - T::table_name(), - mac, - e - ); - Err(e) - } - } - } - - /// Remove an entry from the MAC table. - pub fn mac_clear(s: &Switch, port: u16) -> DpdResult<()> { - let match_key = MacMatchKey { port }; - - match s.table_entry_del(T::table_type(), &match_key) { - Ok(_) => { - info!( - s.log, - "cleared mac on {} in table {}", - port, - T::table_name() - ); - Ok(()) - } - Err(e) => { - error!( - s.log, - "clear mac on {} in table {} failed: {:?}", - port, - T::table_name(), - e - ); - Err(e) - } - } - } - - pub fn table_dump( - s: &Switch, - from_hardware: bool, - ) -> DpdResult { - s.table_dump::(T::table_type(), from_hardware) - } - - pub fn counter_fetch( - s: &Switch, - force_sync: bool, - ) -> DpdResult> { - s.counter_fetch::(force_sync, T::table_type()) - } - - /// Remove all entries from the MAC table. - #[cfg_attr(not(feature = "tofino_asic"), allow(dead_code))] - pub fn reset(s: &Switch) -> DpdResult<()> { - info!(s.log, "reset port macs in table {}", T::table_name()); - s.table_clear(T::table_type()) - } -} diff --git a/dpd/src/table/nat.rs b/dpd/src/table/nat.rs index 63d6e0ad..cb652e2d 100644 --- a/dpd/src/table/nat.rs +++ b/dpd/src/table/nat.rs @@ -17,9 +17,6 @@ use crate::Switch; use crate::table::*; use common::network::{MacAddr, NatTarget}; -pub const IPV4_TABLE_NAME: &str = "pipe.Ingress.nat_ingress.ingress_ipv4"; -pub const IPV6_TABLE_NAME: &str = "pipe.Ingress.nat_ingress.ingress_ipv6"; - #[derive(MatchParse, Hash)] struct Ipv6MatchKey { dst_addr: Ipv6Addr, diff --git a/dpd/src/table/neighbor_ipv6.rs b/dpd/src/table/neighbor_ipv6.rs index 1ee72082..0d89d5f5 100644 --- a/dpd/src/table/neighbor_ipv6.rs +++ b/dpd/src/table/neighbor_ipv6.rs @@ -16,8 +16,6 @@ use crate::Switch; use crate::table::*; use common::network::MacAddr; -pub const TABLE_NAME: &str = "pipe.Ingress.l3_router.Ndp.tbl"; - #[derive(MatchParse, Hash)] struct MatchKey { #[match_xlate(name = "nexthop_ipv6")] diff --git a/dpd/src/table/port_ip.rs b/dpd/src/table/port_ip.rs index 656f7b77..04a004fe 100644 --- a/dpd/src/table/port_ip.rs +++ b/dpd/src/table/port_ip.rs @@ -18,9 +18,6 @@ use aal::MatchMask; use aal::MatchParse; use aal_macros::*; -pub const IPV4_TABLE_NAME: &str = "pipe.Ingress.filter.switch_ipv4_addr"; -pub const IPV6_TABLE_NAME: &str = "pipe.Ingress.filter.switch_ipv6_addr"; - #[derive(MatchParse, Hash)] struct Ipv4MatchKey { #[match_xlate(name = "orig_dst_ipv4")] @@ -98,7 +95,7 @@ pub fn loopback_ipv4_add(s: &Switch, ipv4: Ipv4Addr) -> DpdResult<()> { dst_addr: ipv4, in_port: MatchMask { val: 0u16.into(), mask: 0u16.into() }, }; - s.table_entry_add(TableType::PortIpv4, &claim_key, &ActionV4::ClaimIpv4) + s.table_entry_add(TableType::PortAddrIpv4, &claim_key, &ActionV4::ClaimIpv4) .map(|_| info!(s.log, "added ipv4 loopback"; "addr" => %ipv4)) .inspect_err(|e| { error!(s.log, "failed to add ipv4 loopback"; @@ -112,7 +109,7 @@ pub fn loopback_ipv4_delete(s: &Switch, ipv4: Ipv4Addr) -> DpdResult<()> { dst_addr: ipv4, in_port: MatchMask { val: 0u16.into(), mask: 0u16.into() }, }; - s.table_entry_del(TableType::PortIpv4, &claim_key) + s.table_entry_del(TableType::PortAddrIpv4, &claim_key) .map(|_| info!(s.log, "deleted ipv4 loopback"; "addr" => %ipv4)) .inspect_err(|e| { error!(s.log, "failed to delete ipv4 loopback"; @@ -126,7 +123,7 @@ pub fn loopback_ipv6_add(s: &Switch, ipv6: Ipv6Addr) -> DpdResult<()> { dst_addr: ipv6, in_port: MatchMask { val: 0u16.into(), mask: 0u16.into() }, }; - s.table_entry_add(TableType::PortIpv6, &claim_key, &ActionV6::ClaimIpv6) + s.table_entry_add(TableType::PortAddrIpv6, &claim_key, &ActionV6::ClaimIpv6) .map(|_| info!(s.log, "added ipv6 loopback"; "addr" => %ipv6)) .inspect_err(|e| { error!(s.log, "failed to add ipv6 loopback"; @@ -140,7 +137,7 @@ pub fn loopback_ipv6_delete(s: &Switch, ipv6: Ipv6Addr) -> DpdResult<()> { dst_addr: ipv6, in_port: MatchMask { val: 0u16.into(), mask: 0u16.into() }, }; - s.table_entry_del(TableType::PortIpv6, &claim_key) + s.table_entry_del(TableType::PortAddrIpv6, &claim_key) .map(|_| info!(s.log, "deleted ipv6 loopback"; "addr" => %ipv6)) .inspect_err(|e| { error!(s.log, "failed to delete ipv6 loopback"; @@ -170,13 +167,17 @@ fn endeavour_to_repair( fn ipv4_add_work(s: &Switch, port: u16, ipv4: Ipv4Addr) -> DpdResult<()> { let (claim_key, drop_key) = match_keys_ipv4(ipv4, port); - s.table_entry_add(TableType::PortIpv4, &claim_key, &ActionV4::ClaimIpv4)?; - s.table_entry_add(TableType::PortIpv4, &drop_key, &ActionV4::DropIpv4) + s.table_entry_add( + TableType::PortAddrIpv4, + &claim_key, + &ActionV4::ClaimIpv4, + )?; + s.table_entry_add(TableType::PortAddrIpv4, &drop_key, &ActionV4::DropIpv4) .inspect_err(|_| { endeavour_to_repair( s, format!("ipv4 address {ipv4} only half added"), - || s.table_entry_del(TableType::PortIpv4, &claim_key), + || s.table_entry_del(TableType::PortAddrIpv4, &claim_key), ); }) } @@ -200,14 +201,14 @@ pub fn ipv4_add(s: &Switch, port: u16, ipv4: Ipv4Addr) -> DpdResult<()> { fn ipv4_delete_work(s: &Switch, port: u16, ipv4: Ipv4Addr) -> DpdResult<()> { let (claim_key, drop_key) = match_keys_ipv4(ipv4, port); - s.table_entry_del(TableType::PortIpv4, &claim_key)?; - s.table_entry_del(TableType::PortIpv4, &drop_key).inspect_err(|_| { + s.table_entry_del(TableType::PortAddrIpv4, &claim_key)?; + s.table_entry_del(TableType::PortAddrIpv4, &drop_key).inspect_err(|_| { endeavour_to_repair( s, format!("ipv4 address {ipv4} only half deleted"), || { s.table_entry_add( - TableType::PortIpv4, + TableType::PortAddrIpv4, &claim_key, &ActionV4::ClaimIpv4, ) @@ -247,13 +248,17 @@ pub fn ipv4_delete_many<'a>( fn ipv6_add_work(s: &Switch, port: u16, ipv6: Ipv6Addr) -> DpdResult<()> { let (claim_key, drop_key) = match_keys_ipv6(ipv6, port); - s.table_entry_add(TableType::PortIpv6, &claim_key, &ActionV6::ClaimIpv6)?; - s.table_entry_add(TableType::PortIpv6, &drop_key, &ActionV6::DropIpv6) + s.table_entry_add( + TableType::PortAddrIpv6, + &claim_key, + &ActionV6::ClaimIpv6, + )?; + s.table_entry_add(TableType::PortAddrIpv6, &drop_key, &ActionV6::DropIpv6) .inspect_err(|_| { endeavour_to_repair( s, format!("ipv6 address {ipv6} only half added"), - || s.table_entry_del(TableType::PortIpv6, &claim_key), + || s.table_entry_del(TableType::PortAddrIpv6, &claim_key), ); }) } @@ -277,14 +282,14 @@ pub fn ipv6_add(s: &Switch, port: u16, ipv6: Ipv6Addr) -> DpdResult<()> { fn ipv6_delete_work(s: &Switch, port: u16, ipv6: Ipv6Addr) -> DpdResult<()> { let (claim_key, drop_key) = match_keys_ipv6(ipv6, port); - s.table_entry_del(TableType::PortIpv6, &claim_key)?; - s.table_entry_del(TableType::PortIpv6, &drop_key).inspect_err(|_| { + s.table_entry_del(TableType::PortAddrIpv6, &claim_key)?; + s.table_entry_del(TableType::PortAddrIpv6, &drop_key).inspect_err(|_| { endeavour_to_repair( s, format!("ipv6 address {ipv6} only half deleted"), || { s.table_entry_add( - TableType::PortIpv6, + TableType::PortAddrIpv6, &claim_key, &ActionV6::ClaimIpv6, ) @@ -313,36 +318,42 @@ pub fn ipv4_table_dump( s: &Switch, from_hardware: bool, ) -> DpdResult { - s.table_dump::(TableType::PortIpv4, from_hardware) + s.table_dump::( + TableType::PortAddrIpv4, + from_hardware, + ) } pub fn ipv6_table_dump( s: &Switch, from_hardware: bool, ) -> DpdResult { - s.table_dump::(TableType::PortIpv6, from_hardware) + s.table_dump::( + TableType::PortAddrIpv6, + from_hardware, + ) } pub fn ipv4_table_clear(s: &Switch) -> DpdResult<()> { - s.table_clear(TableType::PortIpv4) + s.table_clear(TableType::PortAddrIpv4) } pub fn ipv6_table_clear(s: &Switch) -> DpdResult<()> { - s.table_clear(TableType::PortIpv6) + s.table_clear(TableType::PortAddrIpv6) } pub fn ipv4_counter_fetch( s: &Switch, force_sync: bool, ) -> DpdResult> { - s.counter_fetch::(force_sync, TableType::PortIpv4) + s.counter_fetch::(force_sync, TableType::PortAddrIpv4) } pub fn ipv6_counter_fetch( s: &Switch, force_sync: bool, ) -> DpdResult> { - s.counter_fetch::(force_sync, TableType::PortIpv6) + s.counter_fetch::(force_sync, TableType::PortAddrIpv6) } /// Delete many IPv6 address from the ASIC tables. diff --git a/dpd/src/table/port_mac.rs b/dpd/src/table/port_mac.rs deleted file mode 100644 index 267a12c9..00000000 --- a/dpd/src/table/port_mac.rs +++ /dev/null @@ -1,21 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// 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 - -use super::{MacTable, TableType}; - -pub const TABLE_NAME: &str = "pipe.Ingress.mac_rewrite.mac_rewrite"; - -pub struct PortMacTable; - -impl MacTable for PortMacTable { - fn table_type() -> TableType { - TableType::PortMac - } - - fn table_name() -> &'static str { - TABLE_NAME - } -} diff --git a/dpd/src/table/route_ipv4.rs b/dpd/src/table/route_ipv4.rs index 75c3ddc8..60564ede 100644 --- a/dpd/src/table/route_ipv4.rs +++ b/dpd/src/table/route_ipv4.rs @@ -17,11 +17,6 @@ use oxnet::Ipv4Net; use slog::error; use slog::info; -pub const INDEX_TABLE_NAME: &str = - "pipe.Ingress.l3_router.Router4.lookup_idx.lookup"; -pub const FORWARD_TABLE_NAME: &str = - "pipe.Ingress.l3_router.Router4.lookup_idx.route"; - // Used for indentifying entries in the index->route_data table #[derive(MatchParse, Hash, Debug)] struct IndexKey { diff --git a/dpd/src/table/route_ipv6.rs b/dpd/src/table/route_ipv6.rs index 21427ebc..28c6bb3a 100644 --- a/dpd/src/table/route_ipv6.rs +++ b/dpd/src/table/route_ipv6.rs @@ -16,11 +16,6 @@ use oxnet::Ipv6Net; use slog::error; use slog::info; -pub const INDEX_TABLE_NAME: &str = - "pipe.Ingress.l3_router.Router6.lookup_idx.lookup"; -pub const FORWARD_TABLE_NAME: &str = - "pipe.Ingress.l3_router.Router6.lookup_idx.route"; - // Used for indentifying entries in the index->route_data table #[derive(MatchParse, Hash, Debug)] struct IndexKey { diff --git a/dpd/src/table/uplink.rs b/dpd/src/table/uplink.rs index 58b38f7f..f858d228 100644 --- a/dpd/src/table/uplink.rs +++ b/dpd/src/table/uplink.rs @@ -13,9 +13,6 @@ use crate::table::*; use aal::{ActionParse, MatchParse}; use aal_macros::*; -pub const INGRESS_TABLE_NAME: &str = "pipe.Ingress.filter.uplink_ports"; -pub const EGRESS_TABLE_NAME: &str = "pipe.Ingress.egress_filter.egress_filter"; - #[derive(MatchParse, Debug, Hash)] struct IngressMatchKey { #[match_xlate(name = "ingress_port")] diff --git a/dpd/src/types.rs b/dpd/src/types.rs index ecee5c8d..2626b8d7 100644 --- a/dpd/src/types.rs +++ b/dpd/src/types.rs @@ -321,3 +321,13 @@ impl convert::From for DpdError { DpdError::Invalid(err.to_string()) } } + +impl convert::From for DpdError { + fn from(err: common::table::TableError) -> Self { + match err { + common::table::TableError::NoSuchTable(x) => { + DpdError::NoSuchTable(x) + } + } + } +} diff --git a/tools/run_dpd.sh b/tools/run_dpd.sh index e55f6c4e..c64f5df7 100755 --- a/tools/run_dpd.sh +++ b/tools/run_dpd.sh @@ -15,14 +15,15 @@ LISTEN_ADDRESS="--listen-addresses [::1]:12224" BOARD_REV="b" MAC_BASE="--mac-base a8:40:25:00:00:02" GDB="" +BIN_SUBDIR=target/debug function usage() { echo "Usage: $0 [-gh] [-b ] [-d [-m ]" - echo "\t[-l ] [-L ] [ -M ]" + echo "\t[-l ] [-L ] [ -M ] [ -r ]" echo "\t[-4 ] [ -p ] [ -x ]" } -while getopts b:d:ghl:m:p:r:t:x:4:L: opt +while getopts b:d:ghl:m:p:rt:x:4:L: opt do if [ $opt == "b" ]; then BIN_NAME=$OPTARG @@ -43,6 +44,8 @@ do else PORT_CONFIG="--port-config $OPTARG" fi + elif [ $opt == "r" ]; then + BIN_SUBDIR=target/release elif [ $opt == "x" ]; then XCVR_CONFIG="--transceiver-interface $OPTARG" elif [ $opt == "4" ]; then @@ -71,7 +74,7 @@ if [ x${WS} == "x" ]; then export ZLOG_CFG=/opt/oxide/dendrite/misc/zlog-cfg else # assume we're running in a workspace - export BIN_DIR=${WS}/target/debug + export BIN_DIR=${WS}/${BIN_SUBDIR} export ZLOG_CFG=${WS}/dpd/misc/zlog-cfg fi