@@ -11,8 +11,9 @@ use crate::network::netlink::Route;
11
11
use crate :: wrap;
12
12
use log:: debug;
13
13
use mozim:: { DhcpV4ClientAsync , DhcpV4Config , DhcpV4Lease as MozimV4Lease } ;
14
+ use std:: sync:: Arc ;
15
+ use tokio:: sync:: Mutex ;
14
16
use tokio_stream:: StreamExt ;
15
-
16
17
use tonic:: { Code , Status } ;
17
18
18
19
/// The kind of DhcpServiceError that can be caused when finding a dhcp lease
@@ -39,6 +40,7 @@ impl DhcpServiceError {
39
40
}
40
41
41
42
/// DHCP service is responsible for creating, handling, and managing the dhcp lease process.
43
+ #[ derive( Debug ) ]
42
44
pub struct DhcpV4Service {
43
45
client : DhcpV4ClientAsync ,
44
46
network_config : NetworkConfig ,
@@ -83,6 +85,9 @@ impl DhcpV4Service {
83
85
previous_lease : None ,
84
86
} )
85
87
}
88
+ pub fn previous_lease ( & self ) -> Option < MozimV4Lease > {
89
+ self . previous_lease . clone ( )
90
+ }
86
91
87
92
/// Performs a DHCP DORA on a ipv4 network configuration.
88
93
/// # Arguments
@@ -129,6 +134,19 @@ impl DhcpV4Service {
129
134
"Could not find a lease within the timeout limit" . to_string ( ) ,
130
135
) )
131
136
}
137
+
138
+ /// Sends a DHCPRELEASE message for the given lease.
139
+ /// This is a "best effort" operation and should not block teardown.
140
+ pub fn release_lease ( & mut self , lease : & MozimV4Lease ) -> Result < ( ) , DhcpServiceError > {
141
+ debug ! (
142
+ "Attempting to release lease for MAC: {}" ,
143
+ & self . network_config. container_mac_addr
144
+ ) ;
145
+ // Directly call the release function on the underlying mozim client.
146
+ self . client
147
+ . release ( lease)
148
+ . map_err ( |e| DhcpServiceError :: new ( Bug , e. to_string ( ) ) )
149
+ }
132
150
}
133
151
134
152
impl std:: fmt:: Display for DhcpServiceError {
@@ -149,46 +167,61 @@ impl From<DhcpServiceError> for Status {
149
167
}
150
168
}
151
169
152
- pub async fn process_client_stream ( mut client : DhcpV4Service ) {
153
- while let Some ( lease) = client. client . next ( ) . await {
154
- match lease {
155
- Ok ( lease) => {
156
- log:: info!(
157
- "got new lease for mac {}: {:?}" ,
158
- & client. network_config. container_mac_addr,
159
- & lease
160
- ) ;
161
- // get previous lease and check if ip addr changed, if not we do not have to do anything
162
- if let Some ( old_lease) = & client. previous_lease {
163
- if old_lease. yiaddr != lease. yiaddr
164
- || old_lease. subnet_mask != lease. subnet_mask
165
- || old_lease. gateways != lease. gateways
166
- {
167
- // ips do not match, remove old ones and assign new ones.
168
- log:: info!(
169
- "ip or gateway for mac {} changed, update address" ,
170
- & client. network_config. container_mac_addr
171
- ) ;
172
- match update_lease_ip (
173
- & client. network_config . ns_path ,
174
- & client. network_config . container_iface ,
175
- old_lease,
176
- & lease,
177
- ) {
178
- Ok ( _) => { }
179
- Err ( err) => {
180
- log:: error!( "{err}" ) ;
181
- continue ;
170
+ pub async fn process_client_stream ( service_arc : Arc < Mutex < DhcpV4Service > > ) {
171
+ loop {
172
+ let next_lease_result = {
173
+ let mut service = service_arc. lock ( ) . await ;
174
+ service. client . next ( ) . await
175
+ } ;
176
+ if let Some ( lease_result) = next_lease_result {
177
+ // Now that we have the result, we can re-lock to update the state.
178
+ // This is safe because this part doesn't involve `.await`.
179
+ match lease_result {
180
+ Ok ( lease) => {
181
+ let mut client = service_arc. lock ( ) . await ;
182
+ log:: info!(
183
+ "got new lease for mac {}: {:?}" ,
184
+ & client. network_config. container_mac_addr,
185
+ & lease
186
+ ) ;
187
+ // get previous lease and check if ip addr changed, if not we do not have to do anything
188
+ if let Some ( old_lease) = & client. previous_lease {
189
+ if old_lease. yiaddr != lease. yiaddr
190
+ || old_lease. subnet_mask != lease. subnet_mask
191
+ || old_lease. gateways != lease. gateways
192
+ {
193
+ // ips do not match, remove old ones and assign new ones.
194
+ log:: info!(
195
+ "ip or gateway for mac {} changed, update address" ,
196
+ & client. network_config. container_mac_addr
197
+ ) ;
198
+ match update_lease_ip (
199
+ & client. network_config . ns_path ,
200
+ & client. network_config . container_iface ,
201
+ old_lease,
202
+ & lease,
203
+ ) {
204
+ Ok ( _) => { }
205
+ Err ( err) => {
206
+ log:: error!( "{err}" ) ;
207
+ continue ;
208
+ }
182
209
}
183
210
}
184
211
}
212
+ client. previous_lease = Some ( lease) ;
213
+ }
214
+ Err ( err) => {
215
+ let client = service_arc. lock ( ) . await ;
216
+ log:: error!(
217
+ "Failed to renew lease for {}: {err}" ,
218
+ & client. network_config. container_mac_addr
219
+ ) ;
185
220
}
186
- client. previous_lease = Some ( lease)
187
221
}
188
- Err ( err) => log:: error!(
189
- "Failed to renew lease for {}: {err}" ,
190
- & client. network_config. container_mac_addr
191
- ) ,
222
+ } else {
223
+ // The stream has ended (e.g., the client disconnected), so we exit the loop.
224
+ break ;
192
225
}
193
226
}
194
227
}
0 commit comments