From bf327dd2e3e8604913ba5419a5d83a541e81b272 Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Thu, 24 Apr 2025 11:57:12 +0100 Subject: [PATCH 1/5] Move all direct use of handle to OpteHdl. --- bin/opteadm/src/bin/opteadm.rs | 15 +- bin/opteadm/src/lib.rs | 320 ++----------------------------- crates/opte-api/src/cmd.rs | 159 ++++++++++++++- crates/opte-api/src/lib.rs | 4 +- lib/opte-ioctl/src/lib.rs | 146 +++++++++++++- lib/opte/src/engine/ioctl.rs | 185 ------------------ lib/opte/src/engine/layer.rs | 22 +-- lib/opte/src/engine/mod.rs | 1 - lib/opte/src/engine/port/mod.rs | 44 +++-- lib/opte/src/engine/print.rs | 34 ++-- lib/opte/src/engine/rule.rs | 13 +- lib/opte/src/engine/tcp_state.rs | 5 +- xde-tests/src/lib.rs | 14 +- xde/src/xde.rs | 4 +- 14 files changed, 386 insertions(+), 580 deletions(-) delete mode 100644 lib/opte/src/engine/ioctl.rs diff --git a/bin/opteadm/src/bin/opteadm.rs b/bin/opteadm/src/bin/opteadm.rs index cdd07ac5..fe74d27e 100644 --- a/bin/opteadm/src/bin/opteadm.rs +++ b/bin/opteadm/src/bin/opteadm.rs @@ -23,6 +23,7 @@ use opte::engine::print::print_tcp_flows; use opte::engine::print::print_uft; use opteadm::COMMIT_COUNT; use opteadm::OpteAdm; +use oxide_vpc::api::AddFwRuleReq; use oxide_vpc::api::AddRouterEntryReq; use oxide_vpc::api::Address; use oxide_vpc::api::ClearVirt2BoundaryReq; @@ -48,6 +49,7 @@ use oxide_vpc::api::RouterTarget; use oxide_vpc::api::SNat4Cfg; use oxide_vpc::api::SNat6Cfg; use oxide_vpc::api::SetExternalIpsReq; +use oxide_vpc::api::SetFwRulesReq; use oxide_vpc::api::SetVirt2BoundaryReq; use oxide_vpc::api::SetVirt2PhysReq; use oxide_vpc::api::TunnelEndpoint; @@ -565,7 +567,7 @@ fn print_port(t: &mut impl Write, pi: PortInfo) -> std::io::Result<()> { fn main() -> anyhow::Result<()> { let cmd = Command::parse(); - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; + let hdl = OpteAdm::open()?; match cmd { Command::ListPorts => { @@ -595,7 +597,7 @@ fn main() -> anyhow::Result<()> { } Command::DumpLayer { port, name } => { - let resp = &hdl.get_layer_by_name(&port, &name)?; + let resp = &hdl.dump_layer(&port, &name)?; print!("Port {port} - "); print_layer(resp)?; } @@ -605,7 +607,6 @@ fn main() -> anyhow::Result<()> { } Command::ClearLft { port, layer } => { - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; hdl.clear_lft(&port, &layer)?; } @@ -622,7 +623,6 @@ fn main() -> anyhow::Result<()> { } Command::DumpV2B => { - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; print_v2b(&hdl.dump_v2b()?)?; } @@ -633,7 +633,7 @@ fn main() -> anyhow::Result<()> { action, priority, }; - hdl.add_firewall_rule(&port, &rule)?; + hdl.add_firewall_rule(&AddFwRuleReq { port_name: port, rule })?; } Command::SetFwRules { port } => { @@ -645,8 +645,7 @@ fn main() -> anyhow::Result<()> { rules.push(r); } - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; - hdl.set_firewall_rules(&port, rules)?; + hdl.set_firewall_rules(&SetFwRulesReq { port_name: port, rules })?; } Command::CreateXde { @@ -742,7 +741,6 @@ fn main() -> anyhow::Result<()> { } Command::SetV2B { prefix, tunnel_endpoint } => { - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; let tep = tunnel_endpoint .into_iter() .map(|ip| TunnelEndpoint { @@ -755,7 +753,6 @@ fn main() -> anyhow::Result<()> { } Command::ClearV2B { prefix, tunnel_endpoint } => { - let hdl = opteadm::OpteAdm::open(OpteAdm::XDE_CTL)?; let tep = tunnel_endpoint .into_iter() .map(|ip| TunnelEndpoint { diff --git a/bin/opteadm/src/lib.rs b/bin/opteadm/src/lib.rs index f2e8a560..abab3811 100644 --- a/bin/opteadm/src/lib.rs +++ b/bin/opteadm/src/lib.rs @@ -2,328 +2,32 @@ // 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 2024 Oxide Computer Company +// Copyright 2025 Oxide Computer Company //! OPTE driver administration library -use opte::api::ClearXdeUnderlayReq; -use opte::api::Direction; -use opte::api::IpCidr; -use opte::api::NoResp; -use opte::api::OpteCmd; -use opte::api::SetXdeUnderlayReq; -use opte::engine::ioctl::{self as api}; use opte_ioctl::Error; -use opte_ioctl::run_cmd_ioctl; -use oxide_vpc::api::AddFwRuleReq; -use oxide_vpc::api::AddRouterEntryReq; -use oxide_vpc::api::AllowCidrReq; -use oxide_vpc::api::ClearVirt2BoundaryReq; -use oxide_vpc::api::ClearVirt2PhysReq; -use oxide_vpc::api::CreateXdeReq; -use oxide_vpc::api::DelRouterEntryReq; -use oxide_vpc::api::DelRouterEntryResp; -use oxide_vpc::api::DeleteXdeReq; -use oxide_vpc::api::DhcpCfg; -use oxide_vpc::api::DumpVirt2BoundaryReq; -use oxide_vpc::api::DumpVirt2BoundaryResp; -use oxide_vpc::api::DumpVirt2PhysReq; -use oxide_vpc::api::DumpVirt2PhysResp; -use oxide_vpc::api::FirewallRule; -use oxide_vpc::api::ListPortsResp; -use oxide_vpc::api::RemFwRuleReq; -use oxide_vpc::api::RemoveCidrReq; -use oxide_vpc::api::RemoveCidrResp; -use oxide_vpc::api::SetExternalIpsReq; -use oxide_vpc::api::SetFwRulesReq; -use oxide_vpc::api::SetVirt2BoundaryReq; -use oxide_vpc::api::SetVirt2PhysReq; -use oxide_vpc::api::VpcCfg; -use std::fs::File; -use std::fs::OpenOptions; -use std::os::unix::io::AsRawFd; +use opte_ioctl::OpteHdl; +use std::ops::Deref; include!(concat!(env!("OUT_DIR"), "/gen.rs")); /// The handle used to send administration commands to the OPTE /// control node. #[derive(Debug)] -pub struct OpteAdm { - device: File, -} - -impl OpteAdm { - pub const XDE_CTL: &'static str = "/dev/xde"; - - /// Add xde device - pub fn create_xde( - &self, - name: &str, - cfg: VpcCfg, - dhcp: DhcpCfg, - passthrough: bool, - ) -> Result { - use libnet::link; - - let linkid = link::create_link_id( - name, - libnet::LinkClass::Misc, - libnet::LinkFlags::Active, - )?; - - let xde_devname = name.into(); - let cmd = OpteCmd::CreateXde; - let req = CreateXdeReq { xde_devname, linkid, cfg, dhcp, passthrough }; - let res = run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)); - - if res.is_err() { - let _ = link::delete_link_id(linkid, libnet::LinkFlags::Active); - } - - res - } - - /// Delete xde device - pub fn delete_xde(&self, name: &str) -> Result { - let link_id = libnet::LinkHandle::Name(name.into()).id()?; - let req = DeleteXdeReq { xde_devname: name.into() }; - let cmd = OpteCmd::DeleteXde; - let resp = run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req))?; - libnet::link::delete_link_id(link_id, libnet::LinkFlags::Active)?; - Ok(resp) - } - - /// Set xde underlay devices - pub fn set_xde_underlay( - &self, - u1: &str, - u2: &str, - ) -> Result { - let req = SetXdeUnderlayReq { u1: u1.into(), u2: u2.into() }; - let cmd = OpteCmd::SetXdeUnderlay; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - /// Clear xde underlay devices - pub fn clear_xde_underlay(&self) -> Result { - let req = ClearXdeUnderlayReq { _unused: 0 }; - let cmd = OpteCmd::ClearXdeUnderlay; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - /// Add a firewall rule - pub fn add_firewall_rule( - &self, - port_name: &str, - rule: &FirewallRule, - ) -> Result { - let cmd = OpteCmd::AddFwRule; - let req = AddFwRuleReq { - port_name: port_name.to_string(), - rule: rule.clone(), - }; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn set_firewall_rules( - &self, - port_name: &str, - rules: Vec, - ) -> Result { - let cmd = OpteCmd::SetFwRules; - let req = SetFwRulesReq { port_name: port_name.to_string(), rules }; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - /// Return the contents of an OPTE layer. - pub fn get_layer_by_name( - &self, - port_name: &str, - name: &str, - ) -> Result { - let cmd = OpteCmd::DumpLayer; - let req = api::DumpLayerReq { - port_name: port_name.to_string(), - name: name.to_string(), - }; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } +pub struct OpteAdm(OpteHdl); - /// List all the ports. - pub fn list_ports(&self) -> Result { - run_cmd_ioctl(self.device.as_raw_fd(), OpteCmd::ListPorts, None::<&()>) - } +impl Deref for OpteAdm { + type Target = OpteHdl; - pub fn list_layers( - &self, - port: &str, - ) -> Result { - let cmd = OpteCmd::ListLayers; - run_cmd_ioctl::( - self.device.as_raw_fd(), - cmd, - Some(&api::ListLayersReq { port_name: port.to_string() }), - ) + fn deref(&self) -> &Self::Target { + &self.0 } +} +impl OpteAdm { /// Create a new handle to the OPTE control node. - pub fn open(what: &str) -> Result { - Ok(OpteAdm { - device: OpenOptions::new().read(true).write(true).open(what)?, - }) - } - - /// Remove a firewall rule. - pub fn remove_firewall_rule( - &self, - req: &RemFwRuleReq, - ) -> Result { - let cmd = OpteCmd::RemFwRule; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(req)) - } - - /// Return the TCP flows. - pub fn dump_tcp_flows( - &self, - port_name: &str, - ) -> Result { - let cmd = OpteCmd::DumpTcpFlows; - run_cmd_ioctl::( - self.device.as_raw_fd(), - cmd, - Some(&api::DumpTcpFlowsReq { port_name: port_name.to_string() }), - ) - } - - /// Clear all entries from the Unified Flow Table (UFT). - pub fn clear_uft(&self, port_name: &str) -> Result { - let cmd = OpteCmd::ClearUft; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&api::ClearUftReq { port_name: port_name.to_string() }), - ) - } - - /// Clear all entries from the given Layer's Flow Table (LFT). - pub fn clear_lft( - &self, - port_name: &str, - layer_name: &str, - ) -> Result { - let cmd = OpteCmd::ClearLft; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&api::ClearLftReq { - port_name: port_name.to_string(), - layer_name: layer_name.to_string(), - }), - ) - } - - /// Return the Unified Flow Table (UFT). - pub fn dump_uft(&self, port_name: &str) -> Result { - let cmd = OpteCmd::DumpUft; - run_cmd_ioctl::( - self.device.as_raw_fd(), - cmd, - Some(&api::DumpUftReq { port_name: port_name.to_string() }), - ) - } - - pub fn set_v2p(&self, req: &SetVirt2PhysReq) -> Result { - let cmd = OpteCmd::SetVirt2Phys; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn clear_v2p(&self, req: &ClearVirt2PhysReq) -> Result { - let cmd = OpteCmd::ClearVirt2Phys; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - /// Dump the Virtual-to-Physical mappings. - pub fn dump_v2p(&self) -> Result { - let cmd = OpteCmd::DumpVirt2Phys; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&DumpVirt2PhysReq { unused: 99 }), - ) - } - - pub fn set_v2b(&self, req: &SetVirt2BoundaryReq) -> Result { - let cmd = OpteCmd::SetVirt2Boundary; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn clear_v2b( - &self, - req: &ClearVirt2BoundaryReq, - ) -> Result { - let cmd = OpteCmd::ClearVirt2Boundary; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - /// Dump the Virtual-to-Boundary mappings. - pub fn dump_v2b(&self) -> Result { - let cmd = OpteCmd::DumpVirt2Boundary; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&DumpVirt2BoundaryReq { unused: 99 }), - ) - } - - pub fn add_router_entry( - &self, - req: &AddRouterEntryReq, - ) -> Result { - let cmd = OpteCmd::AddRouterEntry; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn del_router_entry( - &self, - req: &DelRouterEntryReq, - ) -> Result { - let cmd = OpteCmd::DelRouterEntry; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn set_external_ips( - &self, - req: &SetExternalIpsReq, - ) -> Result { - let cmd = OpteCmd::SetExternalIps; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) - } - - pub fn allow_cidr( - &self, - port_name: &str, - cidr: IpCidr, - dir: Direction, - ) -> Result { - let cmd = OpteCmd::AllowCidr; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&AllowCidrReq { cidr, port_name: port_name.into(), dir }), - ) - } - - pub fn remove_cidr( - &self, - port_name: &str, - cidr: IpCidr, - dir: Direction, - ) -> Result { - let cmd = OpteCmd::RemoveCidr; - run_cmd_ioctl( - self.device.as_raw_fd(), - cmd, - Some(&RemoveCidrReq { cidr, port_name: port_name.into(), dir }), - ) + pub fn open() -> Result { + OpteHdl::open().map(Self) } } diff --git a/crates/opte-api/src/cmd.rs b/crates/opte-api/src/cmd.rs index fe4ed737..33be6b40 100644 --- a/crates/opte-api/src/cmd.rs +++ b/crates/opte-api/src/cmd.rs @@ -2,18 +2,23 @@ // 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 2024 Oxide Computer Company +// Copyright 2025 Oxide Computer Company use super::API_VERSION; +use super::TcpState; use super::encap::Vni; use super::ip::IpCidr; use super::mac::MacAddr; use alloc::string::String; +use alloc::vec::Vec; +use core::fmt::Debug; use illumos_sys_hdrs::c_int; use illumos_sys_hdrs::size_t; use serde::Deserialize; use serde::Serialize; +pub type RuleId = u64; + pub const XDE_IOC: u32 = 0xde777700; pub const XDE_IOC_OPTE_CMD: i32 = XDE_IOC as i32 | 0x01; @@ -244,7 +249,7 @@ impl OpteError { /// A marker trait indicating a success response type that is returned /// from a command and may be passed across the ioctl/API boundary. -pub trait CmdOk: core::fmt::Debug + Serialize {} +pub trait CmdOk: Debug + Serialize {} impl CmdOk for () {} @@ -255,3 +260,153 @@ pub struct NoResp { } impl CmdOk for NoResp {} + +/// Dump various information about a layer, for use in debugging or +/// administrative purposes. +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpLayerReq { + /// The name of the port whose layer you want to dump. + pub port_name: String, + /// The name of the layer to dump. + pub name: String, +} + +/// The response to a [`DumpLayerReq`]. +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpLayerResp { + /// The name of the layer. + pub name: String, + /// The inbound rules. + pub rules_in: Vec, + /// The outbound rules. + pub rules_out: Vec, + /// The default inbound action. + pub default_in: String, + /// The number of times the default inbound action was matched. + pub default_in_hits: u64, + /// The default outbound action. + pub default_out: String, + /// The number of times the default outbound action was matched. + pub default_out_hits: u64, + /// The inbound flow table. + pub ft_in: Vec<(Flow, ActionDescEntryDump)>, + /// The outbound flow table. + pub ft_out: Vec<(Flow, ActionDescEntryDump)>, +} + +impl CmdOk for DumpLayerResp {} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ListLayersReq { + pub port_name: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct LayerDesc { + /// Name of the layer. + pub name: String, + /// Number of rules inbound. + pub rules_in: usize, + /// Number of rules outbound. + pub rules_out: usize, + /// Default action inbound. + pub default_in: String, + /// Default action outbound. + pub default_out: String, + /// Number of active flows. + pub flows: u32, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ListLayersResp { + pub layers: Vec, +} + +impl CmdOk for ListLayersResp {} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ClearUftReq { + pub port_name: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ClearLftReq { + pub port_name: String, + pub layer_name: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpUftReq { + pub port_name: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpUftResp { + pub in_limit: u32, + pub in_num_flows: u32, + pub in_flows: Vec<(Flow, UftEntryDump)>, + pub out_limit: u32, + pub out_num_flows: u32, + pub out_flows: Vec<(Flow, UftEntryDump)>, +} + +impl CmdOk for DumpUftResp {} + +#[derive(Debug, Deserialize, Serialize)] +pub struct UftEntryDump { + pub hits: u64, + pub summary: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpTcpFlowsReq { + pub port_name: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DumpTcpFlowsResp { + pub flows: Vec<(Flow, TcpFlowEntryDump)>, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct TcpFlowEntryDump { + pub hits: u64, + pub inbound_ufid: Option, + pub tcp_state: TcpFlowStateDump, + pub segs_in: u64, + pub segs_out: u64, + pub bytes_in: u64, + pub bytes_out: u64, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct TcpFlowStateDump { + pub tcp_state: TcpState, + pub guest_seq: Option, + pub guest_ack: Option, + pub remote_seq: Option, + pub remote_ack: Option, +} + +impl CmdOk for DumpTcpFlowsResp {} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ActionDescEntryDump { + pub hits: u64, + pub summary: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RuleTableEntryDump { + pub id: RuleId, + pub hits: u64, + pub rule: RuleDump, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RuleDump { + pub priority: u16, + pub predicates: Vec, + pub data_predicates: Vec, + pub action: String, +} diff --git a/crates/opte-api/src/lib.rs b/crates/opte-api/src/lib.rs index d930a5c9..11cdcc69 100644 --- a/crates/opte-api/src/lib.rs +++ b/crates/opte-api/src/lib.rs @@ -28,6 +28,7 @@ pub mod encap; pub mod ip; pub mod mac; pub mod ndp; +pub mod tcp; pub mod ulp; pub use cmd::*; @@ -37,6 +38,7 @@ pub use encap::*; pub use ip::*; pub use mac::*; pub use ndp::*; +pub use tcp::*; pub use ulp::*; /// The overall version of the API. @@ -49,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 = 35; +pub const API_VERSION: u64 = 36; /// Major version of the OPTE package. pub const MAJOR_VERSION: u64 = 0; diff --git a/lib/opte-ioctl/src/lib.rs b/lib/opte-ioctl/src/lib.rs index 3dd04f43..129f6925 100644 --- a/lib/opte-ioctl/src/lib.rs +++ b/lib/opte-ioctl/src/lib.rs @@ -5,15 +5,27 @@ // Copyright 2025 Oxide Computer Company use opte::api::API_VERSION; +use opte::api::ClearLftReq; +use opte::api::ClearUftReq; use opte::api::ClearXdeUnderlayReq; use opte::api::CmdOk; use opte::api::Direction; +use opte::api::DumpLayerReq; +use opte::api::DumpLayerResp; +use opte::api::DumpTcpFlowsReq; +use opte::api::DumpTcpFlowsResp; +use opte::api::DumpUftReq; +use opte::api::DumpUftResp; +use opte::api::ListLayersReq; +use opte::api::ListLayersResp; use opte::api::NoResp; use opte::api::OpteCmd; use opte::api::OpteCmdIoctl; pub use opte::api::OpteError; use opte::api::SetXdeUnderlayReq; use opte::api::XDE_IOC_OPTE_CMD; +use opte::engine::packet::InnerFlowId; +use oxide_vpc::api::AddFwRuleReq; use oxide_vpc::api::AddRouterEntryReq; use oxide_vpc::api::AllowCidrReq; use oxide_vpc::api::ClearVirt2BoundaryReq; @@ -23,10 +35,13 @@ use oxide_vpc::api::DelRouterEntryReq; use oxide_vpc::api::DelRouterEntryResp; use oxide_vpc::api::DeleteXdeReq; use oxide_vpc::api::DhcpCfg; +use oxide_vpc::api::DumpVirt2BoundaryReq; +use oxide_vpc::api::DumpVirt2BoundaryResp; use oxide_vpc::api::DumpVirt2PhysReq; use oxide_vpc::api::DumpVirt2PhysResp; use oxide_vpc::api::IpCidr; use oxide_vpc::api::ListPortsResp; +use oxide_vpc::api::RemFwRuleReq; use oxide_vpc::api::RemoveCidrReq; use oxide_vpc::api::RemoveCidrResp; use oxide_vpc::api::SetExternalIpsReq; @@ -141,19 +156,50 @@ impl OpteHdl { run_cmd_ioctl(self.device.as_raw_fd(), OpteCmd::ListPorts, None::<&()>) } - /// Create a new handle to the OPTE control node. - pub fn open(what: &str) -> Result { + /// List all layers in a given port. + pub fn list_layers(&self, port: &str) -> Result { + let cmd = OpteCmd::ListLayers; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&ListLayersReq { port_name: port.to_string() }), + ) + } + + /// Return the contents of an OPTE layer. + pub fn dump_layer( + &self, + port_name: &str, + name: &str, + ) -> Result, Error> { + let cmd = OpteCmd::DumpLayer; + let req = DumpLayerReq { + port_name: port_name.to_string(), + name: name.to_string(), + }; + run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) + } + + /// Create a new handle to an OPTE control node on an arbitrary file. + pub fn open_on(what: &str) -> Result { Ok(OpteHdl { device: OpenOptions::new().read(true).write(true).open(what)?, }) } - pub fn dump_v2p( - &self, - req: &DumpVirt2PhysReq, - ) -> Result { + /// Create a new handle to the OPTE control node. + pub fn open() -> Result { + Self::open_on(Self::XDE_CTL) + } + + /// Dump the Virtual-to-Physical mappings. + pub fn dump_v2p(&self) -> Result { let cmd = OpteCmd::DumpVirt2Phys; - run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&DumpVirt2PhysReq { unused: 0 }), + ) } pub fn set_v2p(&self, req: &SetVirt2PhysReq) -> Result { @@ -179,6 +225,16 @@ impl OpteHdl { run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) } + /// Dump the Virtual-to-Boundary mappings. + pub fn dump_v2b(&self) -> Result { + let cmd = OpteCmd::DumpVirt2Boundary; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&DumpVirt2BoundaryReq { unused: 99 }), + ) + } + /// Set xde underlay devices. pub fn set_xde_underlay( &self, @@ -213,7 +269,28 @@ impl OpteHdl { run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) } - pub fn set_fw_rules(&self, req: &SetFwRulesReq) -> Result { + /// Add a firewall rule + pub fn add_firewall_rule( + &self, + req: &AddFwRuleReq, + ) -> Result { + let cmd = OpteCmd::AddFwRule; + run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) + } + + /// Remove a firewall rule. + pub fn remove_firewall_rule( + &self, + req: &RemFwRuleReq, + ) -> Result { + let cmd = OpteCmd::RemFwRule; + run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(req)) + } + + pub fn set_firewall_rules( + &self, + req: &SetFwRulesReq, + ) -> Result { let cmd = OpteCmd::SetFwRules; run_cmd_ioctl(self.device.as_raw_fd(), cmd, Some(&req)) } @@ -253,6 +330,59 @@ impl OpteHdl { Some(&RemoveCidrReq { cidr, port_name: port_name.into(), dir }), ) } + + /// Return the TCP flows. + pub fn dump_tcp_flows( + &self, + port_name: &str, + ) -> Result, Error> { + let cmd = OpteCmd::DumpTcpFlows; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&DumpTcpFlowsReq { port_name: port_name.to_string() }), + ) + } + + /// Clear all entries from the Unified Flow Table (UFT). + pub fn clear_uft(&self, port_name: &str) -> Result { + let cmd = OpteCmd::ClearUft; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&ClearUftReq { port_name: port_name.to_string() }), + ) + } + + /// Clear all entries from the given Layer's Flow Table (LFT). + pub fn clear_lft( + &self, + port_name: &str, + layer_name: &str, + ) -> Result { + let cmd = OpteCmd::ClearLft; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&ClearLftReq { + port_name: port_name.to_string(), + layer_name: layer_name.to_string(), + }), + ) + } + + /// Return the Unified Flow Table (UFT). + pub fn dump_uft( + &self, + port_name: &str, + ) -> Result, Error> { + let cmd = OpteCmd::DumpUft; + run_cmd_ioctl( + self.device.as_raw_fd(), + cmd, + Some(&DumpUftReq { port_name: port_name.to_string() }), + ) + } } pub fn run_cmd_ioctl( diff --git a/lib/opte/src/engine/ioctl.rs b/lib/opte/src/engine/ioctl.rs deleted file mode 100644 index 0a731fbc..00000000 --- a/lib/opte/src/engine/ioctl.rs +++ /dev/null @@ -1,185 +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 2023 Oxide Computer Company - -//! The ioctl interface. -//! -//! XXX This stuff needs to be moved to oxide-api. -use super::layer::RuleId; -use super::packet::InnerFlowId; -use super::port::Port; -use super::predicate::DataPredicate; -use super::tcp::TcpState; -use alloc::string::String; -use alloc::vec::Vec; -use core::fmt::Debug; -use opte_api::CmdOk; -use opte_api::OpteError; -use serde::Deserialize; -use serde::Serialize; - -/// Dump various information about a layer, for use in debugging or -/// administrative purposes. -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpLayerReq { - /// The name of the port whose layer you want to dump. - pub port_name: String, - /// The name of the layer to dump. - pub name: String, -} - -/// The response to a [`DumpLayerReq`]. -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpLayerResp { - /// The name of the layer. - pub name: String, - /// The inbound rules. - pub rules_in: Vec, - /// The outbound rules. - pub rules_out: Vec, - /// The default inbound action. - pub default_in: String, - /// The number of times the default inbound action was matched. - pub default_in_hits: u64, - /// The default outbound action. - pub default_out: String, - /// The number of times the default outbound action was matched. - pub default_out_hits: u64, - /// The inbound flow table. - pub ft_in: Vec<(InnerFlowId, ActionDescEntryDump)>, - /// The outbound flow table. - pub ft_out: Vec<(InnerFlowId, ActionDescEntryDump)>, -} - -impl CmdOk for DumpLayerResp {} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ListLayersReq { - pub port_name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct LayerDesc { - /// Name of the layer. - pub name: String, - /// Number of rules inbound. - pub rules_in: usize, - /// Number of rules outbound. - pub rules_out: usize, - /// Default action inbound. - pub default_in: String, - /// Default action outbound. - pub default_out: String, - /// Number of active flows. - pub flows: u32, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ListLayersResp { - pub layers: Vec, -} - -impl CmdOk for ListLayersResp {} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ClearUftReq { - pub port_name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ClearLftReq { - pub port_name: String, - pub layer_name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpUftReq { - pub port_name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpUftResp { - pub in_limit: u32, - pub in_num_flows: u32, - pub in_flows: Vec<(InnerFlowId, UftEntryDump)>, - pub out_limit: u32, - pub out_num_flows: u32, - pub out_flows: Vec<(InnerFlowId, UftEntryDump)>, -} - -impl CmdOk for DumpUftResp {} - -#[derive(Debug, Deserialize, Serialize)] -pub struct UftEntryDump { - pub hits: u64, - pub summary: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpTcpFlowsReq { - pub port_name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct DumpTcpFlowsResp { - pub flows: Vec<(InnerFlowId, TcpFlowEntryDump)>, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct TcpFlowEntryDump { - pub hits: u64, - pub inbound_ufid: Option, - pub tcp_state: TcpFlowStateDump, - pub segs_in: u64, - pub segs_out: u64, - pub bytes_in: u64, - pub bytes_out: u64, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct TcpFlowStateDump { - pub tcp_state: TcpState, - pub guest_seq: Option, - pub guest_ack: Option, - pub remote_seq: Option, - pub remote_ack: Option, -} - -impl CmdOk for DumpTcpFlowsResp {} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ActionDescEntryDump { - pub hits: u64, - pub summary: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct RuleTableEntryDump { - pub id: RuleId, - pub hits: u64, - pub rule: super::ioctl::RuleDump, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct RuleDump { - pub priority: u16, - pub predicates: Vec, - pub data_predicates: Vec, - pub action: String, -} - -pub fn dump_layer( - port: &Port, - req: &DumpLayerReq, -) -> Result { - port.dump_layer(&req.name) -} - -pub fn dump_tcp_flows( - port: &Port, - _req: &DumpTcpFlowsReq, -) -> Result { - port.dump_tcp_flows() -} diff --git a/lib/opte/src/engine/layer.rs b/lib/opte/src/engine/layer.rs index ef853134..b627953c 100644 --- a/lib/opte/src/engine/layer.rs +++ b/lib/opte/src/engine/layer.rs @@ -11,8 +11,6 @@ use super::flow_table::FLOW_DEF_EXPIRE_SECS; use super::flow_table::FlowEntry; use super::flow_table::FlowTable; use super::flow_table::FlowTableDump; -use super::ioctl; -use super::ioctl::ActionDescEntryDump; use super::packet::BodyTransformError; use super::packet::FLOW_ID_DEFAULT; use super::packet::InnerFlowId; @@ -52,7 +50,11 @@ use core::num::NonZeroU32; use core::result; use illumos_sys_hdrs::c_char; use illumos_sys_hdrs::uintptr_t; +use opte_api::ActionDescEntryDump; use opte_api::Direction; +use opte_api::DumpLayerResp; +use opte_api::RuleDump; +use opte_api::RuleTableEntryDump; #[derive(Debug)] pub enum LayerError { @@ -548,11 +550,11 @@ impl Layer { /// Dump the contents of this layer. This is used for presenting /// the layer state in a human-friendly manner. - pub(crate) fn dump(&self) -> ioctl::DumpLayerResp { + pub(crate) fn dump(&self) -> DumpLayerResp { let rules_in = self.rules_in.dump(); let rules_out = self.rules_out.dump(); let ftd = self.ft.dump(); - ioctl::DumpLayerResp { + DumpLayerResp { name: self.name.to_string(), ft_in: ftd.ft_in, ft_out: ftd.ft_out, @@ -1533,13 +1535,9 @@ struct RuleTableEntry { rule: Rule, } -impl From<&RuleTableEntry> for ioctl::RuleTableEntryDump { +impl From<&RuleTableEntry> for RuleTableEntryDump { fn from(rte: &RuleTableEntry) -> Self { - Self { - id: rte.id, - hits: rte.hits, - rule: super::ioctl::RuleDump::from(&rte.rule), - } + Self { id: rte.id, hits: rte.hits, rule: RuleDump::from(&rte.rule) } } } @@ -1579,10 +1577,10 @@ impl RuleTable { self.next_id += 1; } - fn dump(&self) -> Vec { + fn dump(&self) -> Vec { let mut dump = Vec::new(); for rte in &self.rules { - dump.push(ioctl::RuleTableEntryDump::from(rte)); + dump.push(RuleTableEntryDump::from(rte)); } dump } diff --git a/lib/opte/src/engine/mod.rs b/lib/opte/src/engine/mod.rs index b1437db6..222ac0d2 100644 --- a/lib/opte/src/engine/mod.rs +++ b/lib/opte/src/engine/mod.rs @@ -18,7 +18,6 @@ pub mod geneve; #[macro_use] pub mod headers; pub mod icmp; -pub mod ioctl; pub mod ip; pub mod layer; pub mod nat; diff --git a/lib/opte/src/engine/port/mod.rs b/lib/opte/src/engine/port/mod.rs index 78ed89e3..9d3646cc 100644 --- a/lib/opte/src/engine/port/mod.rs +++ b/lib/opte/src/engine/port/mod.rs @@ -19,10 +19,6 @@ use super::headers::EncapPush; use super::headers::HeaderAction; use super::headers::IpPush; use super::headers::UlpHeaderAction; -use super::ioctl; -use super::ioctl::TcpFlowEntryDump; -use super::ioctl::TcpFlowStateDump; -use super::ioctl::UftEntryDump; use super::ip::L3Repr; use super::ip::v4::Ipv4; use super::ip::v6::Ipv6; @@ -51,7 +47,6 @@ use super::rule::Rule; use super::rule::TransformFlags; use super::tcp::KEEPALIVE_EXPIRE_TTL; use super::tcp::TIME_WAIT_EXPIRE_TTL; -use super::tcp::TcpState; use super::tcp_state::TcpFlowState; use super::tcp_state::TcpFlowStateError; use crate::ExecCtx; @@ -97,8 +92,17 @@ use ingot::types::Read; use ingot::udp::Udp; use meta::ActionMeta; use opte_api::Direction; +use opte_api::DumpLayerResp; +use opte_api::DumpTcpFlowsResp; +use opte_api::DumpUftResp; +use opte_api::LayerDesc; +use opte_api::ListLayersResp; use opte_api::MacAddr; use opte_api::OpteError; +use opte_api::TcpFlowEntryDump; +use opte_api::TcpFlowStateDump; +use opte_api::TcpState; +use opte_api::UftEntryDump; use zerocopy::ByteSlice; use zerocopy::ByteSliceMut; @@ -373,12 +377,12 @@ impl PortBuilder { } /// List each [`Layer`] under this port. - pub fn list_layers(&self) -> ioctl::ListLayersResp { + pub fn list_layers(&self) -> ListLayersResp { let mut tmp = vec![]; let lock = self.layers.lock(); for layer in lock.iter() { - tmp.push(ioctl::LayerDesc { + tmp.push(LayerDesc { name: layer.name().to_string(), rules_in: layer.num_rules(Direction::In), rules_out: layer.num_rules(Direction::Out), @@ -388,7 +392,7 @@ impl PortBuilder { }); } - ioctl::ListLayersResp { layers: tmp } + ListLayersResp { layers: tmp } } /// Return the name of the port. @@ -963,7 +967,7 @@ impl Port { /// # States /// /// This command is valid for any [`PortState`]. - pub fn dump_layer(&self, name: &str) -> Result { + pub fn dump_layer(&self, name: &str) -> Result> { let data = self.data.read(); for l in &data.layers { @@ -984,14 +988,14 @@ impl Port { /// * [`PortState::Running`] /// * [`PortState::Paused`] /// * [`PortState::Restored`] - pub fn dump_tcp_flows(&self) -> Result { + pub fn dump_tcp_flows(&self) -> Result> { let data = self.data.read(); check_state!( data.state, [PortState::Running, PortState::Paused, PortState::Restored] )?; - Ok(ioctl::DumpTcpFlowsResp { flows: data.tcp_flows.dump() }) + Ok(DumpTcpFlowsResp { flows: data.tcp_flows.dump() }) } /// Clear all entries from the Unified Flow Table (UFT). @@ -1037,7 +1041,7 @@ impl Port { /// * [`PortState::Running`] /// * [`PortState::Paused`] /// * [`PortState::Restored`] - pub fn dump_uft(&self) -> Result { + pub fn dump_uft(&self) -> Result> { let data = self.data.read(); check_state!( @@ -1053,7 +1057,7 @@ impl Port { let out_num_flows = data.uft_out.num_flows(); let out_flows = data.uft_out.dump(); - Ok(ioctl::DumpUftResp { + Ok(DumpUftResp { in_limit, in_num_flows, in_flows, @@ -1178,12 +1182,12 @@ impl Port { /// # States /// /// This command is valid for any [`PortState`]. - pub fn list_layers(&self) -> ioctl::ListLayersResp { + pub fn list_layers(&self) -> ListLayersResp { let data = self.data.read(); let mut tmp = vec![]; for layer in &data.layers { - tmp.push(ioctl::LayerDesc { + tmp.push(LayerDesc { name: layer.name().to_string(), rules_in: layer.num_rules(Direction::In), rules_out: layer.num_rules(Direction::Out), @@ -1193,7 +1197,7 @@ impl Port { }); } - ioctl::ListLayersResp { layers: tmp } + ListLayersResp { layers: tmp } } /// Return the MAC address of this port. @@ -2988,9 +2992,9 @@ impl Display for TcpFlowEntryState { } impl Dump for TcpFlowEntryStateInner { - type DumpVal = TcpFlowEntryDump; + type DumpVal = TcpFlowEntryDump; - fn dump(&self, hits: u64) -> TcpFlowEntryDump { + fn dump(&self, hits: u64) -> Self::DumpVal { TcpFlowEntryDump { hits, inbound_ufid: self.inbound_ufid, @@ -3004,9 +3008,9 @@ impl Dump for TcpFlowEntryStateInner { } impl Dump for TcpFlowEntryState { - type DumpVal = TcpFlowEntryDump; + type DumpVal = TcpFlowEntryDump; - fn dump(&self, hits: u64) -> TcpFlowEntryDump { + fn dump(&self, hits: u64) -> Self::DumpVal { let inner = self.inner.lock(); inner.dump(hits) } diff --git a/lib/opte/src/engine/print.rs b/lib/opte/src/engine/print.rs index 8802f652..fd82586f 100644 --- a/lib/opte/src/engine/print.rs +++ b/lib/opte/src/engine/print.rs @@ -2,22 +2,22 @@ // 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 2024 Oxide Computer Company +// Copyright 2025 Oxide Computer Company //! Print comannd responses in human-friendly manner. //! //! This is mostly just a place to hang printing routines so that they //! can be used by both opteadm and integration tests. -use super::ioctl::ActionDescEntryDump; -use super::ioctl::DumpLayerResp; -use super::ioctl::DumpTcpFlowsResp; -use super::ioctl::DumpUftResp; -use super::ioctl::ListLayersResp; -use super::ioctl::RuleDump; -use super::ioctl::TcpFlowEntryDump; -use super::ioctl::UftEntryDump; use super::packet::InnerFlowId; +use opte_api::ActionDescEntryDump; +use opte_api::DumpLayerResp; +use opte_api::DumpTcpFlowsResp; +use opte_api::DumpUftResp; +use opte_api::ListLayersResp; +use opte_api::RuleDump; +use opte_api::TcpFlowEntryDump; +use opte_api::UftEntryDump; use std::collections::VecDeque; use std::io::Write; use std::string::String; @@ -25,14 +25,14 @@ use std::string::ToString; use tabwriter::TabWriter; /// Print a [`DumpLayerResp`]. -pub fn print_layer(resp: &DumpLayerResp) -> std::io::Result<()> { +pub fn print_layer(resp: &DumpLayerResp) -> std::io::Result<()> { print_layer_into(&mut std::io::stdout(), resp) } /// Print a [`DumpLayerResp`]. pub fn print_layer_into( writer: &mut impl Write, - resp: &DumpLayerResp, + resp: &DumpLayerResp, ) -> std::io::Result<()> { let mut t = TabWriter::new(writer); @@ -105,14 +105,14 @@ pub fn print_list_layers_into( } /// Print a [`DumpUftResp`]. -pub fn print_uft(uft: &DumpUftResp) -> std::io::Result<()> { +pub fn print_uft(uft: &DumpUftResp) -> std::io::Result<()> { print_uft_into(&mut std::io::stdout(), uft) } /// Print a [`DumpUftResp`] into a given writer. pub fn print_uft_into( writer: &mut impl Write, - uft: &DumpUftResp, + uft: &DumpUftResp, ) -> std::io::Result<()> { let mut t = TabWriter::new(writer); @@ -238,14 +238,16 @@ pub fn print_uft_flow( } /// Print a [`DumpTcpFlowsResp`]. -pub fn print_tcp_flows(flows: &DumpTcpFlowsResp) -> std::io::Result<()> { +pub fn print_tcp_flows( + flows: &DumpTcpFlowsResp, +) -> std::io::Result<()> { print_tcp_flows_into(&mut std::io::stdout(), flows) } /// Print a [`DumpTcpFlowsResp`] into a given writer. pub fn print_tcp_flows_into( writer: &mut impl Write, - flows: &DumpTcpFlowsResp, + flows: &DumpTcpFlowsResp, ) -> std::io::Result<()> { let mut t = TabWriter::new(writer); @@ -260,7 +262,7 @@ pub fn print_tcp_flows_into( fn print_tcp_flow( t: &mut impl Write, id: &InnerFlowId, - entry: &TcpFlowEntryDump, + entry: &TcpFlowEntryDump, ) -> std::io::Result<()> { writeln!( t, diff --git a/lib/opte/src/engine/rule.rs b/lib/opte/src/engine/rule.rs index d3695a99..0d0325d8 100644 --- a/lib/opte/src/engine/rule.rs +++ b/lib/opte/src/engine/rule.rs @@ -63,6 +63,7 @@ use ingot::types::InlineHeader; use ingot::types::Read; use ingot::udp::UdpMut; use opte_api::Direction; +use opte_api::RuleDump; use serde::Deserialize; use serde::Serialize; use zerocopy::ByteSliceMut; @@ -1113,18 +1114,16 @@ impl Rule { } } -impl From<&Rule> for super::ioctl::RuleDump { +impl From<&Rule> for RuleDump { fn from(rule: &Rule) -> Self { let predicates = rule.state.preds.as_ref().map_or(vec![], |rp| { rp.hdr_preds.iter().map(ToString::to_string).collect() }); - let data_predicates = rule - .state - .preds - .as_ref() - .map_or(vec![], |rp| rp.data_preds.clone()); + let data_predicates = rule.state.preds.as_ref().map_or(vec![], |rp| { + rp.data_preds.iter().map(|v| v.to_string()).collect() + }); - super::ioctl::RuleDump { + RuleDump { priority: rule.priority, predicates, data_predicates, diff --git a/lib/opte/src/engine/tcp_state.rs b/lib/opte/src/engine/tcp_state.rs index 01e6cf35..c8d25b99 100644 --- a/lib/opte/src/engine/tcp_state.rs +++ b/lib/opte/src/engine/tcp_state.rs @@ -7,7 +7,6 @@ //! Basic TCP state machine. use super::packet::InnerFlowId; -use super::tcp::TcpState; use core::ffi::CStr; use core::fmt; use core::fmt::Display; @@ -16,6 +15,8 @@ use illumos_sys_hdrs::uintptr_t; use ingot::tcp::TcpFlags as IngotTcpFlags; use ingot::tcp::TcpRef; use opte_api::Direction; +use opte_api::TcpFlowStateDump; +use opte_api::TcpState; use zerocopy::ByteSlice; /// An error processing a TCP flow. @@ -95,7 +96,7 @@ pub struct TcpFlowState { remote_ack: Option, } -impl From for super::ioctl::TcpFlowStateDump { +impl From for TcpFlowStateDump { fn from(tfs: TcpFlowState) -> Self { Self { tcp_state: tfs.tcp_state, diff --git a/xde-tests/src/lib.rs b/xde-tests/src/lib.rs index f0ba1915..a2f29131 100644 --- a/xde-tests/src/lib.rs +++ b/xde-tests/src/lib.rs @@ -108,14 +108,14 @@ impl OptePort { vni: Vni::new(1701u32).unwrap(), phys_ip: phys_ip.parse().unwrap(), }; - let adm = OpteAdm::open(OpteAdm::XDE_CTL)?; + let adm = OpteAdm::open()?; adm.create_xde(name, cfg.clone(), DhcpCfg::default(), false)?; Ok(OptePort { name: name.into(), cfg }) } /// Add an overlay routing entry to this port. pub fn add_router_entry(&self, dest: &str) -> Result<()> { - let adm = OpteAdm::open(OpteAdm::XDE_CTL)?; + let adm = OpteAdm::open()?; adm.add_router_entry(&AddRouterEntryReq { port_name: self.name.clone(), dest: IpCidr::Ip4(format!("{}/32", dest).parse().unwrap()), @@ -127,7 +127,7 @@ impl OptePort { /// Allow all traffic through the overlay firewall. pub fn fw_allow_all(&self) -> Result<()> { - let adm = OpteAdm::open(OpteAdm::XDE_CTL)?; + let adm = OpteAdm::open()?; let mut filters = Filters::new(); filters.set_hosts(Address::Any); filters.set_ports(Ports::Any); @@ -165,7 +165,7 @@ impl OptePort { impl Drop for OptePort { /// When this port is dropped, remove it from the underlying xde device. fn drop(&mut self) { - let adm = match OpteAdm::open(OpteAdm::XDE_CTL) { + let adm = match OpteAdm::open() { Ok(adm) => adm, Err(e) => { eprintln!("failed to open xde device on drop: {}", e); @@ -187,14 +187,14 @@ pub struct Xde {} impl Xde { /// Set the underlay data links that all OPTE ports will use. fn set_xde_underlay(dev0: &str, dev1: &str) -> Result<()> { - let adm = OpteAdm::open(OpteAdm::XDE_CTL)?; + let adm = OpteAdm::open()?; adm.set_xde_underlay(dev0, dev1)?; Ok(()) } /// Set the virtual to physical port mappings that all OPTE ports will use. fn set_v2p(vip: &str, ether: &str, ip: &str) -> Result<()> { - let adm = OpteAdm::open(OpteAdm::XDE_CTL)?; + let adm = OpteAdm::open()?; adm.set_v2p(&SetVirt2PhysReq { vip: vip.parse().unwrap(), phys: PhysNet { @@ -212,7 +212,7 @@ impl Drop for Xde { fn drop(&mut self) { // The module can no longer be successfully removed until the underlay // has been cleared. This may not have been done, so this is fallible. - if let Ok(adm) = OpteAdm::open(OpteAdm::XDE_CTL) { + if let Ok(adm) = OpteAdm::open() { let _ = adm.clear_xde_underlay(); } diff --git a/xde/src/xde.rs b/xde/src/xde.rs index 2bdd1e3e..24e83938 100644 --- a/xde/src/xde.rs +++ b/xde/src/xde.rs @@ -2368,7 +2368,7 @@ fn dump_layer_hdlr( return Err(OpteError::PortNotFound(req.port_name)); }; - api::dump_layer(&dev.port, &req) + dev.port.dump_layer(&req.name) } #[unsafe(no_mangle)] @@ -2381,7 +2381,7 @@ fn dump_tcp_flows_hdlr( return Err(OpteError::PortNotFound(req.port_name)); }; - api::dump_tcp_flows(&dev.port, &req) + dev.port.dump_tcp_flows() } #[unsafe(no_mangle)] From 14f967b74053c530050c9874c89af9a7512cab6a Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Thu, 24 Apr 2025 18:15:55 +0100 Subject: [PATCH 2/5] Missed some spots. --- bin/opteadm/Cargo.toml | 2 +- xde-tests/src/lib.rs | 10 ++++++---- xde/src/xde.rs | 31 ++++++++++++++++++++----------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/bin/opteadm/Cargo.toml b/bin/opteadm/Cargo.toml index 199742fe..e74bdcc6 100644 --- a/bin/opteadm/Cargo.toml +++ b/bin/opteadm/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true # XXX For the time being opteadm needs to set the engine feature to # get all the types. Once there types are move to their appropriate # place this feature flag will be replaced/removed. -opte = { workspace = true, features = ["api", "engine", "std"] } +opte = { workspace = true, features = ["api", "std"] } opte-ioctl.workspace = true oxide-vpc = { workspace = true, features = ["api", "engine", "std"] } diff --git a/xde-tests/src/lib.rs b/xde-tests/src/lib.rs index a2f29131..295ea6b8 100644 --- a/xde-tests/src/lib.rs +++ b/xde-tests/src/lib.rs @@ -6,6 +6,7 @@ use anyhow::Result; use opteadm::OpteAdm; +use oxide_vpc::api::AddFwRuleReq; use oxide_vpc::api::AddRouterEntryReq; use oxide_vpc::api::Address; use oxide_vpc::api::DhcpCfg; @@ -131,15 +132,16 @@ impl OptePort { let mut filters = Filters::new(); filters.set_hosts(Address::Any); filters.set_ports(Ports::Any); - adm.add_firewall_rule( - &self.name, - &FirewallRule { + adm.add_firewall_rule(&AddFwRuleReq { + port_name: self.name.clone(), + rule: FirewallRule { direction: Direction::In, action: FirewallAction::Allow, priority: 0, filters, }, - )?; + })?; + Ok(()) } diff --git a/xde/src/xde.rs b/xde/src/xde.rs index 24e83938..ae38a1b0 100644 --- a/xde/src/xde.rs +++ b/xde/src/xde.rs @@ -63,9 +63,19 @@ use ingot::ip::IpProtocol; use ingot::types::HeaderLen; use ingot::udp::Udp; use opte::ExecCtx; +use opte::api::ClearLftReq; +use opte::api::ClearUftReq; use opte::api::ClearXdeUnderlayReq; use opte::api::CmdOk; use opte::api::Direction; +use opte::api::DumpLayerReq; +use opte::api::DumpLayerResp; +use opte::api::DumpTcpFlowsReq; +use opte::api::DumpTcpFlowsResp; +use opte::api::DumpUftReq; +use opte::api::DumpUftResp; +use opte::api::ListLayersReq; +use opte::api::ListLayersResp; use opte::api::NoResp; use opte::api::OpteCmd; use opte::api::OpteCmdIoctl; @@ -89,7 +99,6 @@ use opte::engine::ether::Ethernet; use opte::engine::ether::EthernetRef; use opte::engine::geneve::Vni; use opte::engine::headers::IpAddr; -use opte::engine::ioctl::{self as api}; use opte::engine::ip::v6::Ipv6; use opte::engine::ip::v6::Ipv6Addr; use opte::engine::packet::InnerFlowId; @@ -2311,8 +2320,8 @@ fn dump_v2b_hdlr( #[unsafe(no_mangle)] fn list_layers_hdlr( env: &mut IoctlEnvelope, -) -> Result { - let req: api::ListLayersReq = env.copy_in_req()?; +) -> Result { + let req: ListLayersReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); @@ -2323,7 +2332,7 @@ fn list_layers_hdlr( #[unsafe(no_mangle)] fn clear_uft_hdlr(env: &mut IoctlEnvelope) -> Result { - let req: api::ClearUftReq = env.copy_in_req()?; + let req: ClearUftReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); @@ -2335,7 +2344,7 @@ fn clear_uft_hdlr(env: &mut IoctlEnvelope) -> Result { #[unsafe(no_mangle)] fn clear_lft_hdlr(env: &mut IoctlEnvelope) -> Result { - let req: api::ClearLftReq = env.copy_in_req()?; + let req: ClearLftReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); @@ -2348,8 +2357,8 @@ fn clear_lft_hdlr(env: &mut IoctlEnvelope) -> Result { #[unsafe(no_mangle)] fn dump_uft_hdlr( env: &mut IoctlEnvelope, -) -> Result { - let req: api::DumpUftReq = env.copy_in_req()?; +) -> Result, OpteError> { + let req: DumpUftReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); @@ -2361,8 +2370,8 @@ fn dump_uft_hdlr( #[unsafe(no_mangle)] fn dump_layer_hdlr( env: &mut IoctlEnvelope, -) -> Result { - let req: api::DumpLayerReq = env.copy_in_req()?; +) -> Result, OpteError> { + let req: DumpLayerReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); @@ -2374,8 +2383,8 @@ fn dump_layer_hdlr( #[unsafe(no_mangle)] fn dump_tcp_flows_hdlr( env: &mut IoctlEnvelope, -) -> Result { - let req: api::DumpTcpFlowsReq = env.copy_in_req()?; +) -> Result, OpteError> { + let req: DumpTcpFlowsReq = env.copy_in_req()?; let devs = xde_devs().read(); let Some(dev) = devs.get_by_name(&req.port_name) else { return Err(OpteError::PortNotFound(req.port_name)); From 38492cb8b01f582c81abd6fdc82a22524ed5d91d Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Thu, 24 Apr 2025 18:19:45 +0100 Subject: [PATCH 3/5] .. --- crates/opte-api/src/tcp.rs | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 crates/opte-api/src/tcp.rs diff --git a/crates/opte-api/src/tcp.rs b/crates/opte-api/src/tcp.rs new file mode 100644 index 00000000..08217005 --- /dev/null +++ b/crates/opte-api/src/tcp.rs @@ -0,0 +1,45 @@ +// 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 core::fmt; +use core::fmt::Display; +use serde::Deserialize; +use serde::Serialize; + +/// The standard TCP states. +/// +/// See Figure 13-8 of TCP/IP Illustrated Vol. 1 Ed. 2 +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum TcpState { + Closed, + Listen, + SynSent, + SynRcvd, + Established, + CloseWait, + LastAck, + FinWait1, + FinWait2, + TimeWait, +} + +impl Display for TcpState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + TcpState::Closed => "CLOSED", + TcpState::Listen => "LISTEN", + TcpState::SynSent => "SYN_SENT", + TcpState::SynRcvd => "SYN_RCVD", + TcpState::Established => "ESTABLISHED", + TcpState::CloseWait => "CLOSE_WAIT", + TcpState::LastAck => "LAST_ACK", + TcpState::FinWait1 => "FIN_WAIT_1", + TcpState::FinWait2 => "FIN_WAIT_2", + TcpState::TimeWait => "TIME_WAIT", + }; + write!(f, "{}", s) + } +} From 5a1c16be1cc9d9883634a95c01c1b456d60af74b Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Thu, 24 Apr 2025 18:49:31 +0100 Subject: [PATCH 4/5] Non-engine consumers need `InnerFlowId` --- lib/opte-ioctl/src/lib.rs | 2 +- lib/opte/src/api.rs | 149 +++++++++++++++++++++++++++++++++ lib/opte/src/engine/headers.rs | 3 - lib/opte/src/engine/packet.rs | 142 +------------------------------ lib/opte/src/lib.rs | 6 +- 5 files changed, 155 insertions(+), 147 deletions(-) create mode 100644 lib/opte/src/api.rs diff --git a/lib/opte-ioctl/src/lib.rs b/lib/opte-ioctl/src/lib.rs index 129f6925..b12be9e6 100644 --- a/lib/opte-ioctl/src/lib.rs +++ b/lib/opte-ioctl/src/lib.rs @@ -16,6 +16,7 @@ use opte::api::DumpTcpFlowsReq; use opte::api::DumpTcpFlowsResp; use opte::api::DumpUftReq; use opte::api::DumpUftResp; +pub use opte::api::InnerFlowId; use opte::api::ListLayersReq; use opte::api::ListLayersResp; use opte::api::NoResp; @@ -24,7 +25,6 @@ use opte::api::OpteCmdIoctl; pub use opte::api::OpteError; use opte::api::SetXdeUnderlayReq; use opte::api::XDE_IOC_OPTE_CMD; -use opte::engine::packet::InnerFlowId; use oxide_vpc::api::AddFwRuleReq; use oxide_vpc::api::AddRouterEntryReq; use oxide_vpc::api::AllowCidrReq; diff --git a/lib/opte/src/api.rs b/lib/opte/src/api.rs new file mode 100644 index 00000000..73d97d12 --- /dev/null +++ b/lib/opte/src/api.rs @@ -0,0 +1,149 @@ +// 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 core::fmt::Display; +use core::fmt::{self}; +#[cfg(feature = "engine")] +use core::hash::Hash; +#[cfg(feature = "engine")] +use crc32fast::Hasher; +pub use opte_api::*; +use serde::Deserialize; +use serde::Serialize; + +const AF_INET: i32 = 2; +const AF_INET6: i32 = 26; + +pub static FLOW_ID_DEFAULT: InnerFlowId = InnerFlowId { + proto: 255, + addrs: AddrPair::V4 { src: Ipv4Addr::ANY_ADDR, dst: Ipv4Addr::ANY_ADDR }, + src_port: 0, + dst_port: 0, +}; + +/// The flow identifier. +/// +/// In this case the flow identifier is the 5-tuple of the inner IP +/// packet. +/// +/// NOTE: This should not be defined in `opte`. Rather, the engine +/// should be generic in regards to the flow identifier, and it should +/// be up to the `NetworkImpl` to define it. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +#[repr(C, align(4))] +pub struct InnerFlowId { + // Using a `u8` here for `proto` hides the enum repr from SDTs. + pub proto: u8, + // We could also theoretically get to a 38B packing if we reduce + // AddrPair's repr from `u16` to `u8`. However, on the dtrace/illumos + // side `union addrs` is 4B aligned -- in6_addr_t has a 4B alignment. + // So, this layout has to match that constraint -- placing addrs at + // offset 0x2 with `u16` discriminant sets up 4B alignment for the + // enum variant data (and this struct itself is 4B aligned). + pub addrs: AddrPair, + pub src_port: u16, + pub dst_port: u16, +} + +impl InnerFlowId { + #[cfg(feature = "engine")] + pub fn crc32(&self) -> u32 { + let mut hasher = Hasher::new(); + self.hash(&mut hasher); + hasher.finalize() + } +} + +impl Default for InnerFlowId { + fn default() -> Self { + FLOW_ID_DEFAULT + } +} + +/// Tagged union of a source-dest IP address pair, used to avoid +/// duplicating the discriminator. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +#[repr(C, u16)] +pub enum AddrPair { + V4 { src: Ipv4Addr, dst: Ipv4Addr } = AF_INET as u16, + V6 { src: Ipv6Addr, dst: Ipv6Addr } = AF_INET6 as u16, +} + +impl AddrPair { + pub fn mirror(self) -> Self { + match self { + Self::V4 { src, dst } => Self::V4 { src: dst, dst: src }, + Self::V6 { src, dst } => Self::V6 { src: dst, dst: src }, + } + } +} + +impl InnerFlowId { + /// Swap IP source and destination as well as ULP port source and + /// destination. + pub fn mirror(self) -> Self { + Self { + proto: self.proto, + addrs: self.addrs.mirror(), + src_port: self.dst_port, + dst_port: self.src_port, + } + } + + pub fn src_ip(&self) -> IpAddr { + match self.addrs { + AddrPair::V4 { src, .. } => src.into(), + AddrPair::V6 { src, .. } => src.into(), + } + } + + pub fn dst_ip(&self) -> IpAddr { + match self.addrs { + AddrPair::V4 { dst, .. } => dst.into(), + AddrPair::V6 { dst, .. } => dst.into(), + } + } + + pub fn protocol(&self) -> Protocol { + Protocol::from(self.proto) + } +} + +impl Display for InnerFlowId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}:{}:{}:{}:{}", + self.protocol(), + self.src_ip(), + self.src_port, + self.dst_ip(), + self.dst_port, + ) + } +} diff --git a/lib/opte/src/engine/headers.rs b/lib/opte/src/engine/headers.rs index e19dd986..8e0fc568 100644 --- a/lib/opte/src/engine/headers.rs +++ b/lib/opte/src/engine/headers.rs @@ -43,9 +43,6 @@ use serde::Serialize; use zerocopy::ByteSlice; use zerocopy::ByteSliceMut; -pub const AF_INET: i32 = 2; -pub const AF_INET6: i32 = 26; - pub trait PushAction { fn push(&self) -> HdrM; } diff --git a/lib/opte/src/engine/packet.rs b/lib/opte/src/engine/packet.rs index 1a9396a4..ee8937ed 100644 --- a/lib/opte/src/engine/packet.rs +++ b/lib/opte/src/engine/packet.rs @@ -13,20 +13,15 @@ use super::checksum::Checksum; use super::ether::Ethernet; use super::ether::EthernetPacket; use super::ether::ValidEthernet; -use super::headers::AF_INET; -use super::headers::AF_INET6; use super::headers::EncapMeta; use super::headers::EncapPush; -use super::headers::IpAddr; use super::headers::IpPush; use super::headers::SizeHoldingEncap; use super::headers::ValidEncapMeta; use super::ip::L3; use super::ip::L3Repr; -use super::ip::v4::Ipv4Addr; use super::ip::v4::Ipv4Packet; use super::ip::v4::Ipv4Ref; -use super::ip::v4::Protocol; use super::ip::v6::Ipv6Addr; use super::ip::v6::Ipv6Packet; use super::ip::v6::Ipv6Ref; @@ -38,6 +33,9 @@ use super::rule::CompiledEncap; use super::rule::CompiledTransform; use super::rule::HdrTransform; use super::rule::HdrTransformError; +pub use crate::api::AddrPair; +pub use crate::api::FLOW_ID_DEFAULT; +pub use crate::api::InnerFlowId; use crate::d_error::DError; use crate::ddi::mblk::MsgBlk; use crate::ddi::mblk::MsgBlkIterMut; @@ -51,13 +49,11 @@ use alloc::vec::Vec; use core::cell::Cell; use core::ffi::CStr; use core::fmt; -use core::fmt::Display; use core::hash::Hash; use core::ptr::NonNull; use core::result; use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; -use crc32fast::Hasher; use dyn_clone::DynClone; use illumos_sys_hdrs::mblk_t; use illumos_sys_hdrs::uintptr_t; @@ -86,142 +82,10 @@ use ingot::udp::UdpMut; use ingot::udp::UdpPacket; use ingot::udp::UdpRef; use opte_api::Vni; -use serde::Deserialize; -use serde::Serialize; use zerocopy::ByteSlice; use zerocopy::ByteSliceMut; use zerocopy::IntoBytes; -pub static FLOW_ID_DEFAULT: InnerFlowId = InnerFlowId { - proto: 255, - addrs: AddrPair::V4 { src: Ipv4Addr::ANY_ADDR, dst: Ipv4Addr::ANY_ADDR }, - src_port: 0, - dst_port: 0, -}; - -/// The flow identifier. -/// -/// In this case the flow identifier is the 5-tuple of the inner IP -/// packet. -/// -/// NOTE: This should not be defined in `opte`. Rather, the engine -/// should be generic in regards to the flow identifier, and it should -/// be up to the `NetworkImpl` to define it. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -#[repr(C, align(4))] -pub struct InnerFlowId { - // Using a `u8` here for `proto` hides the enum repr from SDTs. - pub proto: u8, - // We could also theoretically get to a 38B packing if we reduce - // AddrPair's repr from `u16` to `u8`. However, on the dtrace/illumos - // side `union addrs` is 4B aligned -- in6_addr_t has a 4B alignment. - // So, this layout has to match that constraint -- placing addrs at - // offset 0x2 with `u16` discriminant sets up 4B alignment for the - // enum variant data (and this struct itself is 4B aligned). - pub addrs: AddrPair, - pub src_port: u16, - pub dst_port: u16, -} - -impl InnerFlowId { - pub fn crc32(&self) -> u32 { - let mut hasher = Hasher::new(); - self.hash(&mut hasher); - hasher.finalize() - } -} - -impl Default for InnerFlowId { - fn default() -> Self { - FLOW_ID_DEFAULT - } -} - -/// Tagged union of a source-dest IP address pair, used to avoid -/// duplicating the discriminator. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -#[repr(C, u16)] -pub enum AddrPair { - V4 { src: Ipv4Addr, dst: Ipv4Addr } = AF_INET as u16, - V6 { src: Ipv6Addr, dst: Ipv6Addr } = AF_INET6 as u16, -} - -impl AddrPair { - pub fn mirror(self) -> Self { - match self { - Self::V4 { src, dst } => Self::V4 { src: dst, dst: src }, - Self::V6 { src, dst } => Self::V6 { src: dst, dst: src }, - } - } -} - -impl InnerFlowId { - /// Swap IP source and destination as well as ULP port source and - /// destination. - pub fn mirror(self) -> Self { - Self { - proto: self.proto, - addrs: self.addrs.mirror(), - src_port: self.dst_port, - dst_port: self.src_port, - } - } - - pub fn src_ip(&self) -> IpAddr { - match self.addrs { - AddrPair::V4 { src, .. } => src.into(), - AddrPair::V6 { src, .. } => src.into(), - } - } - - pub fn dst_ip(&self) -> IpAddr { - match self.addrs { - AddrPair::V4 { dst, .. } => dst.into(), - AddrPair::V6 { dst, .. } => dst.into(), - } - } - - pub fn protocol(&self) -> Protocol { - Protocol::from(self.proto) - } -} - -impl Display for InnerFlowId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}:{}:{}:{}:{}", - self.protocol(), - self.src_ip(), - self.src_port, - self.dst_ip(), - self.dst_port, - ) - } -} - pub trait PacketState {} /// A packet body transformation. diff --git a/lib/opte/src/lib.rs b/lib/opte/src/lib.rs index 42ec1668..33ee13df 100644 --- a/lib/opte/src/lib.rs +++ b/lib/opte/src/lib.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -// Copyright 2024 Oxide Computer Company +// Copyright 2025 Oxide Computer Company #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::len_without_is_empty)] @@ -34,9 +34,7 @@ use core::fmt::Display; pub use ingot; #[cfg(any(feature = "api", test))] -pub mod api { - pub use opte_api::*; -} +pub mod api; #[cfg(any(feature = "engine", test))] pub mod d_error; #[cfg(any(feature = "engine", test))] From c6a5882f067c2797bf17903d65cc6ef5b1413ec1 Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Thu, 24 Apr 2025 20:18:38 +0100 Subject: [PATCH 5/5] Again. --- lib/opte/src/engine/packet.rs | 1 + lib/oxide-vpc/tests/integration_tests.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/opte/src/engine/packet.rs b/lib/opte/src/engine/packet.rs index ee8937ed..a926e5e5 100644 --- a/lib/opte/src/engine/packet.rs +++ b/lib/opte/src/engine/packet.rs @@ -1726,6 +1726,7 @@ mod test { use ingot::tcp::TcpRef; use ingot::types::HeaderLen; use ingot::udp::Udp; + use opte_api::Ipv4Addr; use opte_api::Ipv6Addr; use opte_api::MacAddr; diff --git a/lib/oxide-vpc/tests/integration_tests.rs b/lib/oxide-vpc/tests/integration_tests.rs index 2bd62399..a2dce13d 100644 --- a/lib/oxide-vpc/tests/integration_tests.rs +++ b/lib/oxide-vpc/tests/integration_tests.rs @@ -17,6 +17,7 @@ use common::icmp::*; use common::*; use opte::api::MacAddr; use opte::api::OpteError; +use opte::api::TcpState; use opte::ddi::mblk::MsgBlk; use opte::ddi::time::Moment; use opte::engine::Direction; @@ -42,7 +43,6 @@ use opte::engine::packet::Packet; use opte::engine::parse::ValidUlp; use opte::engine::port::ProcessError; use opte::engine::tcp::TIME_WAIT_EXPIRE_SECS; -use opte::engine::tcp::TcpState; use opte::ingot::geneve::GeneveRef; use opte::ingot::icmp::IcmpV6Ref; use opte::ingot::tcp::TcpRef;