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