@@ -19,8 +19,15 @@ pub const VNIC_PREFIX_CONTROL: &str = "oxControl";
1919// Viona, and thus plumbed directly to guests.
2020pub const VNIC_PREFIX_GUEST : & str = "vopte" ;
2121
22+ /// Path to the DLADM command.
2223pub const DLADM : & str = "/usr/sbin/dladm" ;
2324
25+ /// The name of the etherstub to be created for the underlay.
26+ pub const ETHERSTUB_NAME : & str = "stub0" ;
27+
28+ /// The name of the etherstub VNIC to be created in the global zone.
29+ pub const ETHERSTUB_VNIC_NAME : & str = "underlay0" ;
30+
2431/// Errors returned from [`Dladm::find_physical`].
2532#[ derive( thiserror:: Error , Debug ) ]
2633pub enum FindPhysicalLinkError {
@@ -49,7 +56,7 @@ pub enum GetMacError {
4956#[ error( "Failed to create VNIC {name} on link {link:?}: {err}" ) ]
5057pub struct CreateVnicError {
5158 name : String ,
52- link : PhysicalLink ,
59+ link : String ,
5360 #[ source]
5461 err : ExecutionError ,
5562}
@@ -75,11 +82,77 @@ pub struct DeleteVnicError {
7582#[ derive( Debug , Clone , Deserialize , Serialize , PartialEq ) ]
7683pub struct PhysicalLink ( pub String ) ;
7784
85+ /// The name of an etherstub
86+ #[ derive( Debug , Clone , Deserialize , Serialize , PartialEq ) ]
87+ pub struct Etherstub ( pub String ) ;
88+
89+ /// The name of an etherstub's underlay VNIC
90+ #[ derive( Debug , Clone , Deserialize , Serialize , PartialEq ) ]
91+ pub struct EtherstubVnic ( pub String ) ;
92+
93+ /// Identifies that an object may be used to create a VNIC.
94+ pub trait VnicSource {
95+ fn name ( & self ) -> & str ;
96+ }
97+
98+ impl VnicSource for Etherstub {
99+ fn name ( & self ) -> & str {
100+ & self . 0
101+ }
102+ }
103+
104+ impl VnicSource for PhysicalLink {
105+ fn name ( & self ) -> & str {
106+ & self . 0
107+ }
108+ }
109+
78110/// Wraps commands for interacting with data links.
79111pub struct Dladm { }
80112
81113#[ cfg_attr( test, mockall:: automock, allow( dead_code) ) ]
82114impl Dladm {
115+ /// Creates an etherstub, or returns one which already exists.
116+ pub fn create_etherstub ( ) -> Result < Etherstub , ExecutionError > {
117+ if let Ok ( stub) = Self :: get_etherstub ( ) {
118+ return Ok ( stub) ;
119+ }
120+ let mut command = std:: process:: Command :: new ( PFEXEC ) ;
121+ let cmd =
122+ command. args ( & [ DLADM , "create-etherstub" , "-t" , ETHERSTUB_NAME ] ) ;
123+ execute ( cmd) ?;
124+ Ok ( Etherstub ( ETHERSTUB_NAME . to_string ( ) ) )
125+ }
126+
127+ /// Finds an etherstub.
128+ fn get_etherstub ( ) -> Result < Etherstub , ExecutionError > {
129+ let mut command = std:: process:: Command :: new ( PFEXEC ) ;
130+ let cmd = command. args ( & [ DLADM , "show-etherstub" , ETHERSTUB_NAME ] ) ;
131+ execute ( cmd) ?;
132+ Ok ( Etherstub ( ETHERSTUB_NAME . to_string ( ) ) )
133+ }
134+
135+ /// Creates a VNIC on top of the etherstub.
136+ ///
137+ /// This VNIC is not tracked like [`crate::illumos::vnic::Vnic`], because
138+ /// it is expected to exist for the lifetime of the sled.
139+ pub fn create_etherstub_vnic (
140+ source : & Etherstub ,
141+ ) -> Result < EtherstubVnic , CreateVnicError > {
142+ if let Ok ( vnic) = Self :: get_etherstub_vnic ( ) {
143+ return Ok ( vnic) ;
144+ }
145+ Self :: create_vnic ( source, ETHERSTUB_VNIC_NAME , None , None ) ?;
146+ Ok ( EtherstubVnic ( ETHERSTUB_VNIC_NAME . to_string ( ) ) )
147+ }
148+
149+ fn get_etherstub_vnic ( ) -> Result < EtherstubVnic , ExecutionError > {
150+ let mut command = std:: process:: Command :: new ( PFEXEC ) ;
151+ let cmd = command. args ( & [ DLADM , "show-vnic" , ETHERSTUB_VNIC_NAME ] ) ;
152+ execute ( cmd) ?;
153+ Ok ( EtherstubVnic ( ETHERSTUB_VNIC_NAME . to_string ( ) ) )
154+ }
155+
83156 /// Returns the name of the first observed physical data link.
84157 pub fn find_physical ( ) -> Result < PhysicalLink , FindPhysicalLinkError > {
85158 let mut command = std:: process:: Command :: new ( PFEXEC ) ;
@@ -135,8 +208,8 @@ impl Dladm {
135208 /// * `vnic_name`: Exact name of the VNIC to be created.
136209 /// * `mac`: An optional unicast MAC address for the newly created NIC.
137210 /// * `vlan`: An optional VLAN ID for VLAN tagging.
138- pub fn create_vnic (
139- physical : & PhysicalLink ,
211+ pub fn create_vnic < T : VnicSource + ' static > (
212+ source : & T ,
140213 vnic_name : & str ,
141214 mac : Option < MacAddr > ,
142215 vlan : Option < VlanID > ,
@@ -147,7 +220,7 @@ impl Dladm {
147220 "create-vnic" . to_string( ) ,
148221 "-t" . to_string( ) ,
149222 "-l" . to_string( ) ,
150- physical . 0 . to_string( ) ,
223+ source . name ( ) . to_string( ) ,
151224 ] ;
152225
153226 if let Some ( mac) = mac {
@@ -164,7 +237,7 @@ impl Dladm {
164237 let cmd = command. args ( & args) ;
165238 execute ( cmd) . map_err ( |err| CreateVnicError {
166239 name : vnic_name. to_string ( ) ,
167- link : physical . clone ( ) ,
240+ link : source . name ( ) . to_string ( ) ,
168241 err,
169242 } ) ?;
170243 Ok ( ( ) )
0 commit comments