1use core::num::{NonZeroU8, NonZeroU16};
8
9use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6};
10use netstack3_base::{AnyDevice, DeviceIdContext, DeviceIdentifier};
11
12use crate::internal::device::route_discovery::RouteDiscoveryConfigurationUpdate;
13use crate::internal::device::slaac::SlaacConfigurationUpdate;
14use crate::internal::device::state::{
15 IpDeviceConfiguration, IpDeviceFlags, Ipv4DeviceConfiguration,
16};
17use crate::internal::device::{
18 self, IpDeviceBindingsContext, IpDeviceConfigurationContext, IpDeviceEvent, IpDeviceIpExt,
19 Ipv6DeviceConfigurationContext, WithIpDeviceConfigurationMutInner as _,
20 WithIpv6DeviceConfigurationMutInner as _,
21};
22use crate::internal::gmp::GmpHandler;
23use crate::internal::gmp::igmp::IgmpConfigMode;
24use crate::internal::gmp::mld::MldConfigMode;
25
26pub trait IpDeviceConfigurationHandler<I: IpDeviceIpExt, BC>: DeviceIdContext<AnyDevice> {
33 fn apply_configuration(
35 &mut self,
36 bindings_ctx: &mut BC,
37 config: PendingIpDeviceConfigurationUpdate<'_, I, Self::DeviceId>,
38 ) -> I::ConfigurationUpdate;
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, GenericOverIp)]
43#[generic_over_ip()]
44pub struct IpDeviceConfigurationUpdate {
45 pub ip_enabled: Option<bool>,
47 pub unicast_forwarding_enabled: Option<bool>,
49 pub multicast_forwarding_enabled: Option<bool>,
51 pub gmp_enabled: Option<bool>,
53 pub dad_transmits: Option<Option<NonZeroU16>>,
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
59pub struct Ipv4DeviceConfigurationUpdate {
60 pub ip_config: IpDeviceConfigurationUpdate,
62 pub igmp_mode: Option<IgmpConfigMode>,
64}
65
66impl From<IpDeviceConfigurationUpdate> for Ipv4DeviceConfigurationUpdate {
67 fn from(ip_config: IpDeviceConfigurationUpdate) -> Self {
68 Self { ip_config, ..Default::default() }
69 }
70}
71
72impl AsRef<IpDeviceConfigurationUpdate> for Ipv4DeviceConfigurationUpdate {
73 fn as_ref(&self) -> &IpDeviceConfigurationUpdate {
74 &self.ip_config
75 }
76}
77
78#[derive(Debug, Eq, PartialEq, Copy, Clone)]
80pub enum UpdateIpConfigurationError {
81 UnicastForwardingNotSupported,
83 MulticastForwardingNotSupported,
85}
86
87#[derive(GenericOverIp)]
91#[generic_over_ip(I, Ip)]
92pub struct PendingIpDeviceConfigurationUpdate<'a, I: IpDeviceIpExt, D>(
93 I::ConfigurationUpdate,
94 &'a D,
95);
96
97impl<'a, I: IpDeviceIpExt, D: DeviceIdentifier> PendingIpDeviceConfigurationUpdate<'a, I, D> {
98 pub(crate) fn new(
101 config: I::ConfigurationUpdate,
102 device_id: &'a D,
103 ) -> Result<Self, UpdateIpConfigurationError> {
104 let IpDeviceConfigurationUpdate {
105 ip_enabled: _,
106 gmp_enabled: _,
107 unicast_forwarding_enabled,
108 multicast_forwarding_enabled,
109 dad_transmits: _,
110 } = config.as_ref();
111
112 if device_id.is_loopback() {
113 if unicast_forwarding_enabled.unwrap_or(false) {
114 return Err(UpdateIpConfigurationError::UnicastForwardingNotSupported);
115 }
116 if multicast_forwarding_enabled.unwrap_or(false) {
117 return Err(UpdateIpConfigurationError::MulticastForwardingNotSupported);
118 }
119 }
120
121 Ok(Self(config, device_id))
122 }
123
124 pub fn config_update(&self) -> &I::ConfigurationUpdate {
126 &self.0
127 }
128
129 pub fn device_id(&self) -> &'a D {
131 self.1
132 }
133}
134
135impl<CC, BC> IpDeviceConfigurationHandler<Ipv4, BC> for CC
136where
137 CC: IpDeviceConfigurationContext<Ipv4, BC>,
138 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
139{
140 fn apply_configuration(
141 &mut self,
142 bindings_ctx: &mut BC,
143 config: PendingIpDeviceConfigurationUpdate<'_, Ipv4, Self::DeviceId>,
144 ) -> Ipv4DeviceConfigurationUpdate {
145 let PendingIpDeviceConfigurationUpdate(
146 Ipv4DeviceConfigurationUpdate { ip_config, igmp_mode },
147 device_id,
148 ) = config;
149 let device_id: &CC::DeviceId = device_id;
150
151 let handle_config_and_flags =
153 |config: &mut Ipv4DeviceConfiguration, flags: &mut IpDeviceFlags| {
154 let IpDeviceConfigurationUpdate {
155 ip_enabled,
156 gmp_enabled,
157 unicast_forwarding_enabled,
158 multicast_forwarding_enabled,
159 dad_transmits,
160 } = ip_config;
161 (
162 get_prev_next_and_update(&mut flags.ip_enabled, ip_enabled),
163 get_prev_next_and_update(&mut config.ip_config.gmp_enabled, gmp_enabled),
164 get_prev_next_and_update(
165 &mut config.ip_config.unicast_forwarding_enabled,
166 unicast_forwarding_enabled,
167 ),
168 get_prev_next_and_update(
169 &mut config.ip_config.multicast_forwarding_enabled,
170 multicast_forwarding_enabled,
171 ),
172 get_prev_next_and_update(&mut config.ip_config.dad_transmits, dad_transmits),
173 )
174 };
175
176 self.with_ip_device_configuration_mut(device_id, |mut inner| {
177 let (
178 ip_enabled_updates,
179 gmp_enabled_updates,
180 unicast_forwarding_enabled_updates,
181 multicast_forwarding_enabled_updates,
182 dad_transmits,
183 ) = inner.with_configuration_and_flags_mut(device_id, handle_config_and_flags);
184
185 let (config, mut core_ctx) = inner.ip_device_configuration_and_ctx();
186 let core_ctx = &mut core_ctx;
187
188 let ip_enabled = handle_change_and_get_prev(ip_enabled_updates, |next| {
189 if next {
190 device::enable_ipv4_device_with_config(
191 core_ctx,
192 bindings_ctx,
193 device_id,
194 config,
195 )
196 } else {
197 device::disable_ipv4_device_with_config(
198 core_ctx,
199 bindings_ctx,
200 device_id,
201 config,
202 )
203 }
204 bindings_ctx.on_event(IpDeviceEvent::EnabledChanged {
205 device: device_id.clone(),
206 ip_enabled: next,
207 })
208 });
209
210 let igmp_mode = igmp_mode
213 .map(|igmp_mode| core_ctx.gmp_set_mode(bindings_ctx, device_id, igmp_mode));
214
215 let gmp_enabled = handle_change_and_get_prev(gmp_enabled_updates, |next| {
216 if next {
217 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id)
218 } else {
219 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id)
220 }
221 });
222 let unicast_forwarding_enabled =
223 dont_handle_change_and_get_prev(unicast_forwarding_enabled_updates);
224 let multicast_forwarding_enabled =
225 dont_handle_change_and_get_prev(multicast_forwarding_enabled_updates);
226 let dad_transmits = dont_handle_change_and_get_prev(dad_transmits);
227 let ip_config = IpDeviceConfigurationUpdate {
228 ip_enabled,
229 gmp_enabled,
230 unicast_forwarding_enabled,
231 multicast_forwarding_enabled,
232 dad_transmits,
233 };
234 Ipv4DeviceConfigurationUpdate { ip_config, igmp_mode }
235 })
236 }
237}
238
239#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
241pub struct Ipv6DeviceConfigurationUpdate {
242 pub max_router_solicitations: Option<Option<NonZeroU8>>,
244 pub slaac_config: SlaacConfigurationUpdate,
246 pub route_discovery_config: RouteDiscoveryConfigurationUpdate,
248 pub ip_config: IpDeviceConfigurationUpdate,
250 pub mld_mode: Option<MldConfigMode>,
252}
253
254impl From<IpDeviceConfigurationUpdate> for Ipv6DeviceConfigurationUpdate {
255 fn from(ip_config: IpDeviceConfigurationUpdate) -> Self {
256 Self { ip_config, ..Default::default() }
257 }
258}
259
260impl AsRef<IpDeviceConfigurationUpdate> for Ipv6DeviceConfigurationUpdate {
261 fn as_ref(&self) -> &IpDeviceConfigurationUpdate {
262 &self.ip_config
263 }
264}
265
266impl<CC, BC> IpDeviceConfigurationHandler<Ipv6, BC> for CC
267where
268 CC: Ipv6DeviceConfigurationContext<BC>,
269 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
270{
271 fn apply_configuration(
272 &mut self,
273 bindings_ctx: &mut BC,
274 config: PendingIpDeviceConfigurationUpdate<'_, Ipv6, Self::DeviceId>,
275 ) -> Ipv6DeviceConfigurationUpdate {
276 let PendingIpDeviceConfigurationUpdate(
277 Ipv6DeviceConfigurationUpdate {
278 max_router_solicitations,
279 slaac_config,
280 route_discovery_config,
281 ip_config,
282 mld_mode,
283 },
284 device_id,
285 ) = config;
286 self.with_ipv6_device_configuration_mut(device_id, |mut inner| {
287 let (
288 max_router_solicitations_updates,
289 slaac_config_updates,
290 route_discovery_config_updates,
291 ip_enabled_updates,
292 gmp_enabled_updates,
293 unicast_forwarding_enabled_updates,
294 multicast_forwarding_enabled_updates,
295 dad_transmits,
296 ) = inner.with_configuration_and_flags_mut(device_id, |config, flags| {
297 let IpDeviceConfigurationUpdate {
298 ip_enabled,
299 gmp_enabled,
300 unicast_forwarding_enabled,
301 multicast_forwarding_enabled,
302 dad_transmits,
303 } = ip_config;
304 (
305 get_prev_next_and_update(
306 &mut config.max_router_solicitations,
307 max_router_solicitations,
308 ),
309 config.slaac_config.update(slaac_config),
310 config.route_discovery_config.update(route_discovery_config),
311 get_prev_next_and_update(&mut flags.ip_enabled, ip_enabled),
312 get_prev_next_and_update(&mut config.ip_config.gmp_enabled, gmp_enabled),
313 get_prev_next_and_update(
314 &mut config.ip_config.unicast_forwarding_enabled,
315 unicast_forwarding_enabled,
316 ),
317 get_prev_next_and_update(
318 &mut config.ip_config.multicast_forwarding_enabled,
319 multicast_forwarding_enabled,
320 ),
321 get_prev_next_and_update(&mut config.ip_config.dad_transmits, dad_transmits),
322 )
323 });
324
325 let (config, mut core_ctx) = inner.ipv6_device_configuration_and_ctx();
326 let core_ctx = &mut core_ctx;
327
328 let max_router_solicitations =
329 dont_handle_change_and_get_prev(max_router_solicitations_updates);
330
331 let mld_mode =
334 mld_mode.map(|mld_mode| core_ctx.gmp_set_mode(bindings_ctx, device_id, mld_mode));
335
336 let ip_config = IpDeviceConfigurationUpdate {
337 ip_enabled: handle_change_and_get_prev(ip_enabled_updates, |next| {
338 if next {
339 device::enable_ipv6_device_with_config(
340 core_ctx,
341 bindings_ctx,
342 device_id,
343 config,
344 )
345 } else {
346 device::disable_ipv6_device_with_config(
347 core_ctx,
348 bindings_ctx,
349 device_id,
350 config,
351 )
352 }
353
354 bindings_ctx.on_event(IpDeviceEvent::EnabledChanged {
355 device: device_id.clone(),
356 ip_enabled: next,
357 })
358 }),
359 gmp_enabled: handle_change_and_get_prev(gmp_enabled_updates, |next| {
360 if next {
361 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id)
362 } else {
363 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id)
364 }
365 }),
366 unicast_forwarding_enabled: handle_change_and_get_prev(
367 unicast_forwarding_enabled_updates,
368 |next| {
369 if next {
370 device::join_ip_multicast_with_config(
371 core_ctx,
372 bindings_ctx,
373 device_id,
374 Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS,
375 config,
376 );
377 } else {
378 device::leave_ip_multicast_with_config(
379 core_ctx,
380 bindings_ctx,
381 device_id,
382 Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS,
383 config,
384 );
385 }
386 },
387 ),
388 multicast_forwarding_enabled: dont_handle_change_and_get_prev(
389 multicast_forwarding_enabled_updates,
390 ),
391 dad_transmits: dont_handle_change_and_get_prev(dad_transmits),
392 };
393 Ipv6DeviceConfigurationUpdate {
394 max_router_solicitations,
395 slaac_config: slaac_config_updates,
396 route_discovery_config: route_discovery_config_updates,
397 ip_config,
398 mld_mode,
399 }
400 })
401 }
402}
403
404struct Delta<T> {
405 prev: T,
406 next: T,
407}
408
409fn get_prev_next_and_update<T: Copy>(field: &mut T, next: Option<T>) -> Option<Delta<T>> {
410 next.map(|next| Delta { prev: core::mem::replace(field, next), next })
411}
412
413fn handle_change_and_get_prev<T: PartialEq>(
414 delta: Option<Delta<T>>,
415 f: impl FnOnce(T),
416) -> Option<T> {
417 delta.map(|Delta { prev, next }| {
418 if prev != next {
419 f(next)
420 }
421 prev
422 })
423}
424
425fn dont_handle_change_and_get_prev<T: PartialEq>(delta: Option<Delta<T>>) -> Option<T> {
426 handle_change_and_get_prev(delta, |_: T| {})
427}
428
429#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
431pub struct IpDeviceConfigurationAndFlags<I: IpDeviceIpExt> {
432 pub config: I::Configuration,
434 pub flags: IpDeviceFlags,
436 pub gmp_mode: I::GmpProtoConfigMode,
438}
439
440impl<I: IpDeviceIpExt> AsRef<IpDeviceConfiguration> for IpDeviceConfigurationAndFlags<I> {
441 fn as_ref(&self) -> &IpDeviceConfiguration {
442 self.config.as_ref()
443 }
444}
445
446impl<I: IpDeviceIpExt> AsMut<IpDeviceConfiguration> for IpDeviceConfigurationAndFlags<I> {
447 fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
448 self.config.as_mut()
449 }
450}
451
452impl<I: IpDeviceIpExt> AsRef<IpDeviceFlags> for IpDeviceConfigurationAndFlags<I> {
453 fn as_ref(&self) -> &IpDeviceFlags {
454 &self.flags
455 }
456}