@@ -19,8 +19,15 @@ pub const VNIC_PREFIX_CONTROL: &str = "oxControl";
19
19
// Viona, and thus plumbed directly to guests.
20
20
pub const VNIC_PREFIX_GUEST : & str = "vopte" ;
21
21
22
+ /// Path to the DLADM command.
22
23
pub const DLADM : & str = "/usr/sbin/dladm" ;
23
24
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
+
24
31
/// Errors returned from [`Dladm::find_physical`].
25
32
#[ derive( thiserror:: Error , Debug ) ]
26
33
pub enum FindPhysicalLinkError {
@@ -49,7 +56,7 @@ pub enum GetMacError {
49
56
#[ error( "Failed to create VNIC {name} on link {link:?}: {err}" ) ]
50
57
pub struct CreateVnicError {
51
58
name : String ,
52
- link : PhysicalLink ,
59
+ link : String ,
53
60
#[ source]
54
61
err : ExecutionError ,
55
62
}
@@ -75,11 +82,77 @@ pub struct DeleteVnicError {
75
82
#[ derive( Debug , Clone , Deserialize , Serialize , PartialEq ) ]
76
83
pub struct PhysicalLink ( pub String ) ;
77
84
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
+
78
110
/// Wraps commands for interacting with data links.
79
111
pub struct Dladm { }
80
112
81
113
#[ cfg_attr( test, mockall:: automock, allow( dead_code) ) ]
82
114
impl 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
+
83
156
/// Returns the name of the first observed physical data link.
84
157
pub fn find_physical ( ) -> Result < PhysicalLink , FindPhysicalLinkError > {
85
158
let mut command = std:: process:: Command :: new ( PFEXEC ) ;
@@ -135,8 +208,8 @@ impl Dladm {
135
208
/// * `vnic_name`: Exact name of the VNIC to be created.
136
209
/// * `mac`: An optional unicast MAC address for the newly created NIC.
137
210
/// * `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 ,
140
213
vnic_name : & str ,
141
214
mac : Option < MacAddr > ,
142
215
vlan : Option < VlanID > ,
@@ -147,7 +220,7 @@ impl Dladm {
147
220
"create-vnic" . to_string( ) ,
148
221
"-t" . to_string( ) ,
149
222
"-l" . to_string( ) ,
150
- physical . 0 . to_string( ) ,
223
+ source . name ( ) . to_string( ) ,
151
224
] ;
152
225
153
226
if let Some ( mac) = mac {
@@ -164,7 +237,7 @@ impl Dladm {
164
237
let cmd = command. args ( & args) ;
165
238
execute ( cmd) . map_err ( |err| CreateVnicError {
166
239
name : vnic_name. to_string ( ) ,
167
- link : physical . clone ( ) ,
240
+ link : source . name ( ) . to_string ( ) ,
168
241
err,
169
242
} ) ?;
170
243
Ok ( ( ) )
0 commit comments