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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions bin/opteadm/src/bin/opteadm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ use opte::api::MAJOR_VERSION;
use opte::api::MacAddr;
use opte::api::MulticastUnderlay;
use opte::api::Vni;
use opte::api::DumpLayerResp;
use opte::print::collect_prop_rows;
use opte::print::print_layer;
use opte::print::print_list_layers;
use opte::print::print_props;
use opte::print::print_props_parseable;
use opte::print::print_tcp_flows;
use opte::print::print_uft;
use opte_ioctl::OpteHdl;
Expand Down Expand Up @@ -100,6 +104,36 @@ enum Command {
name: String,
},

/// Show K/V configuration properties exposed by rule actions.
///
/// Like `dladm show-linkprop`. With no filters, lists every property
/// of every rule on every layer.
ShowProp {
#[arg(short)]
port: String,

/// Restrict output to a single layer.
#[arg(short = 'l', long)]
layer: Option<String>,

/// Restrict output to a single direction.
#[arg(short = 'd', long = "dir")]
direction: Option<Direction>,

/// Restrict output to a single rule id.
#[arg(short = 'r', long)]
rule_id: Option<u64>,

/// Comma-separated list of property names to show.
#[arg(short = 'p', long, value_delimiter = ',')]
prop: Vec<String>,

/// Emit `LAYER:DIR:RULE:PROPERTY:VALUE` lines, with `:` and `\`
/// in values backslash-escaped.
#[arg(short = 'c')]
parseable: bool,
},

/// Clear all entries from the Unified Flow Table.
ClearUft {
#[arg(short)]
Expand Down Expand Up @@ -766,6 +800,40 @@ fn main() -> anyhow::Result<()> {
print_layer(resp)?;
}

Command::ShowProp {
port,
layer,
direction,
rule_id,
prop,
parseable,
} => {
let layers: Vec<DumpLayerResp> = match &layer {
Some(name) => vec![hdl.dump_layer(&port, name)?],
None => hdl
.list_layers(&port)?
.layers
.iter()
.map(|l| hdl.dump_layer(&port, &l.name))
.collect::<Result<_, _>>()?,
};

let prop_filter: Option<&[String]> =
if prop.is_empty() { None } else { Some(&prop) };
let rows = collect_prop_rows(
&layers,
direction,
rule_id,
prop_filter,
);

if parseable {
print_props_parseable(&rows)?;
} else {
print_props(&rows)?;
}
}

Command::ClearUft { port } => {
hdl.clear_uft(&port)?;
}
Expand Down
15 changes: 15 additions & 0 deletions crates/opte-api/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,4 +487,19 @@ pub struct RuleDump {
pub predicates: Vec<String>,
pub data_predicates: Vec<String>,
pub action: String,
/// Read-only diagnostic properties exposed by the rule's [`Action`],
/// modeled after `dladm show-linkprop`. Populated by implementations
/// of the `ActionProperties` trait.
pub action_properties: Vec<ActionProperty>,
}

/// A single key/value diagnostic property of an [`Action`] (or another
/// `NetworkImpl`-defined object).
///
/// Properties are immutable once the owning object is constructed and
/// are intended for human and machine readers (e.g. `opteadm show-prop`).
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ActionProperty {
pub name: String,
pub value: String,
}
2 changes: 1 addition & 1 deletion crates/opte-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub use ulp::*;
///
/// We rely on CI and the check-api-version.sh script to verify that
/// this number is incremented anytime the oxide-api code changes.
pub const API_VERSION: u64 = 40;
pub const API_VERSION: u64 = 41;

/// Major version of the OPTE package.
pub const MAJOR_VERSION: u64 = 0;
Expand Down
16 changes: 16 additions & 0 deletions lib/opte/src/engine/dhcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,22 @@ impl Display for DhcpAction {
}
}

impl crate::engine::props::ActionProperties for DhcpAction {
fn property_names(&self) -> &'static [&'static str] {
&["client_mac", "client_ip", "gw_mac", "gw_ip", "reply_type"]
}
fn get_property(&self, name: &str) -> Option<String> {
match name {
"client_mac" => Some(self.client_mac.to_string()),
"client_ip" => Some(self.client_ip.to_string()),
"gw_mac" => Some(self.gw_mac.to_string()),
"gw_ip" => Some(self.gw_ip.to_string()),
"reply_type" => Some(self.reply_type.to_string()),
_ => None,
}
}
}

// XXX I read up just enough on DHCP to get initial lease working.
// However, I imagine there could be post-lease messages between
// client/server and those might be unicast, at which point these
Expand Down
19 changes: 19 additions & 0 deletions lib/opte/src/engine/dhcpv6/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,25 @@ impl Display for Dhcpv6Action {
}
}

impl crate::engine::props::ActionProperties for Dhcpv6Action {
fn property_names(&self) -> &'static [&'static str] {
&["client_mac", "server_mac", "addresses"]
}
fn get_property(&self, name: &str) -> Option<alloc::string::String> {
match name {
"client_mac" => Some(self.client_mac.to_string()),
"server_mac" => Some(self.server_mac.to_string()),
"addresses" => Some(
self.addresses()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(","),
),
_ => None,
}
}
}

/// A lifetime describes the duration over which data such as addresses are
/// valid. These are encoded in messages as a u32.
#[derive(Clone, Copy, Debug, PartialEq)]
Expand Down
16 changes: 16 additions & 0 deletions lib/opte/src/engine/icmp/v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ use opte::engine::Checksum as OpteCsum;
pub use opte_api::ip::IcmpEchoReply;
use smoltcp::wire;

impl crate::engine::props::ActionProperties for IcmpEchoReply {
fn property_names(&self) -> &'static [&'static str] {
&["echo_src_mac", "echo_src_ip", "echo_dst_mac", "echo_dst_ip"]
}
fn get_property(&self, name: &str) -> Option<alloc::string::String> {
use alloc::string::ToString;
match name {
"echo_src_mac" => Some(self.echo_src_mac.to_string()),
"echo_src_ip" => Some(self.echo_src_ip.to_string()),
"echo_dst_mac" => Some(self.echo_dst_mac.to_string()),
"echo_dst_ip" => Some(self.echo_dst_ip.to_string()),
_ => None,
}
}
}

impl HairpinAction for IcmpEchoReply {
fn implicit_preds(&self) -> (Vec<Predicate>, Vec<DataPredicate>) {
let hdr_preds = vec![
Expand Down
49 changes: 49 additions & 0 deletions lib/opte/src/engine/icmp/v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ impl Display for MessageType {
}
}

impl crate::engine::props::ActionProperties for Icmpv6EchoReply {
fn property_names(&self) -> &'static [&'static str] {
&["src_mac", "src_ip", "dst_mac", "dst_ip"]
}
fn get_property(&self, name: &str) -> Option<alloc::string::String> {
use alloc::string::ToString;
match name {
"src_mac" => Some(self.src_mac.to_string()),
"src_ip" => Some(self.src_ip.to_string()),
"dst_mac" => Some(self.dst_mac.to_string()),
"dst_ip" => Some(self.dst_ip.to_string()),
_ => None,
}
}
}

impl HairpinAction for Icmpv6EchoReply {
fn implicit_preds(&self) -> (Vec<Predicate>, Vec<DataPredicate>) {
let hdr_preds = vec![
Expand Down Expand Up @@ -199,6 +215,22 @@ impl HairpinAction for Icmpv6EchoReply {
}
}

impl crate::engine::props::ActionProperties for RouterAdvertisement {
fn property_names(&self) -> &'static [&'static str] {
&["src_mac", "mac", "ip", "managed_cfg"]
}
fn get_property(&self, name: &str) -> Option<alloc::string::String> {
use alloc::string::ToString;
match name {
"src_mac" => Some(self.src_mac.to_string()),
"mac" => Some(self.mac.to_string()),
"ip" => Some(self.ip().to_string()),
"managed_cfg" => Some(self.managed_cfg.to_string()),
_ => None,
}
}
}

impl HairpinAction for RouterAdvertisement {
fn implicit_preds(&self) -> (Vec<Predicate>, Vec<DataPredicate>) {
const ALL_ROUTERS_MAC: MacAddr =
Expand Down Expand Up @@ -502,6 +534,23 @@ fn construct_neighbor_advert<'a>(
))
}

impl crate::engine::props::ActionProperties for NeighborAdvertisement {
fn property_names(&self) -> &'static [&'static str] {
&["src_mac", "mac", "ip", "is_router", "allow_unspec"]
}
fn get_property(&self, name: &str) -> Option<alloc::string::String> {
use alloc::string::ToString;
match name {
"src_mac" => Some(self.src_mac.to_string()),
"mac" => Some(self.mac.to_string()),
"ip" => Some(self.ip().to_string()),
"is_router" => Some(self.is_router.to_string()),
"allow_unspec" => Some(self.allow_unspec.to_string()),
_ => None,
}
}
}

impl HairpinAction for NeighborAdvertisement {
fn implicit_preds(&self) -> (Vec<Predicate>, Vec<DataPredicate>) {
// The source IP must be a link-local IPv6 address, or, if
Expand Down
1 change: 1 addition & 0 deletions lib/opte/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod packet;
pub mod parse;
pub mod port;
pub mod predicate;
pub mod props;
pub mod rule;
pub mod snat;
#[macro_use]
Expand Down
30 changes: 30 additions & 0 deletions lib/opte/src/engine/nat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use super::port::meta::ActionMeta;
use super::port::meta::ActionMetaValue;
use super::predicate::DataPredicate;
use super::predicate::Predicate;
use super::props::ActionProperties;
use super::rule;
use super::rule::ActionDesc;
use super::rule::AllowOrDeny;
Expand Down Expand Up @@ -105,6 +106,21 @@ impl fmt::Display for OutboundNat {
}
}

impl ActionProperties for OutboundNat {
fn property_names(&self) -> &'static [&'static str] {
&["priv_ip", "external_ips"]
}
fn get_property(&self, name: &str) -> Option<String> {
match name {
"priv_ip" => Some(self.priv_ip.to_string()),
"external_ips" => {
Some(self.external_ips.iter().format(",").to_string())
}
_ => None,
}
}
}

impl StatefulAction for OutboundNat {
fn gen_desc(
&self,
Expand Down Expand Up @@ -168,6 +184,18 @@ impl fmt::Display for InboundNat {
}
}

impl ActionProperties for InboundNat {
fn property_names(&self) -> &'static [&'static str] {
&["priv_ip"]
}
fn get_property(&self, name: &str) -> Option<String> {
match name {
"priv_ip" => Some(self.priv_ip.to_string()),
_ => None,
}
}
}

impl StatefulAction for InboundNat {
fn gen_desc(
&self,
Expand Down Expand Up @@ -401,6 +429,8 @@ impl fmt::Display for ExternalIpTagger {
}
}

impl ActionProperties for ExternalIpTagger {}

impl MetaAction for ExternalIpTagger {
fn implicit_preds(&self) -> (Vec<Predicate>, Vec<DataPredicate>) {
(vec![], vec![])
Expand Down
Loading