1use core::num::{NonZeroU16, NonZeroU8};
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::igmp::IgmpConfigMode;
22use crate::internal::gmp::mld::MldConfigMode;
23use crate::internal::gmp::GmpHandler;
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
124impl<CC, BC> IpDeviceConfigurationHandler<Ipv4, BC> for CC
125where
126 CC: IpDeviceConfigurationContext<Ipv4, BC>,
127 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
128{
129 fn apply_configuration(
130 &mut self,
131 bindings_ctx: &mut BC,
132 config: PendingIpDeviceConfigurationUpdate<'_, Ipv4, Self::DeviceId>,
133 ) -> Ipv4DeviceConfigurationUpdate {
134 let PendingIpDeviceConfigurationUpdate(
135 Ipv4DeviceConfigurationUpdate { ip_config, igmp_mode },
136 device_id,
137 ) = config;
138 let device_id: &CC::DeviceId = device_id;
139
140 let handle_config_and_flags =
142 |config: &mut Ipv4DeviceConfiguration, flags: &mut IpDeviceFlags| {
143 let IpDeviceConfigurationUpdate {
144 ip_enabled,
145 gmp_enabled,
146 unicast_forwarding_enabled,
147 multicast_forwarding_enabled,
148 dad_transmits,
149 } = ip_config;
150 (
151 get_prev_next_and_update(&mut flags.ip_enabled, ip_enabled),
152 get_prev_next_and_update(&mut config.ip_config.gmp_enabled, gmp_enabled),
153 get_prev_next_and_update(
154 &mut config.ip_config.unicast_forwarding_enabled,
155 unicast_forwarding_enabled,
156 ),
157 get_prev_next_and_update(
158 &mut config.ip_config.multicast_forwarding_enabled,
159 multicast_forwarding_enabled,
160 ),
161 get_prev_next_and_update(&mut config.ip_config.dad_transmits, dad_transmits),
162 )
163 };
164
165 self.with_ip_device_configuration_mut(device_id, |mut inner| {
166 let (
167 ip_enabled_updates,
168 gmp_enabled_updates,
169 unicast_forwarding_enabled_updates,
170 multicast_forwarding_enabled_updates,
171 dad_transmits,
172 ) = inner.with_configuration_and_flags_mut(device_id, handle_config_and_flags);
173
174 let (config, mut core_ctx) = inner.ip_device_configuration_and_ctx();
175 let core_ctx = &mut core_ctx;
176
177 let ip_enabled = handle_change_and_get_prev(ip_enabled_updates, |next| {
178 if next {
179 device::enable_ipv4_device_with_config(
180 core_ctx,
181 bindings_ctx,
182 device_id,
183 config,
184 )
185 } else {
186 device::disable_ipv4_device_with_config(
187 core_ctx,
188 bindings_ctx,
189 device_id,
190 config,
191 )
192 }
193 bindings_ctx.on_event(IpDeviceEvent::EnabledChanged {
194 device: device_id.clone(),
195 ip_enabled: next,
196 })
197 });
198
199 let igmp_mode = igmp_mode
202 .map(|igmp_mode| core_ctx.gmp_set_mode(bindings_ctx, device_id, igmp_mode));
203
204 let gmp_enabled = handle_change_and_get_prev(gmp_enabled_updates, |next| {
205 if next {
206 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id)
207 } else {
208 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id)
209 }
210 });
211 let unicast_forwarding_enabled =
212 dont_handle_change_and_get_prev(unicast_forwarding_enabled_updates);
213 let multicast_forwarding_enabled =
214 dont_handle_change_and_get_prev(multicast_forwarding_enabled_updates);
215 let dad_transmits = dont_handle_change_and_get_prev(dad_transmits);
216 let ip_config = IpDeviceConfigurationUpdate {
217 ip_enabled,
218 gmp_enabled,
219 unicast_forwarding_enabled,
220 multicast_forwarding_enabled,
221 dad_transmits,
222 };
223 Ipv4DeviceConfigurationUpdate { ip_config, igmp_mode }
224 })
225 }
226}
227
228#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
230pub struct Ipv6DeviceConfigurationUpdate {
231 pub max_router_solicitations: Option<Option<NonZeroU8>>,
233 pub slaac_config: SlaacConfigurationUpdate,
235 pub ip_config: IpDeviceConfigurationUpdate,
237 pub mld_mode: Option<MldConfigMode>,
239}
240
241impl From<IpDeviceConfigurationUpdate> for Ipv6DeviceConfigurationUpdate {
242 fn from(ip_config: IpDeviceConfigurationUpdate) -> Self {
243 Self { ip_config, ..Default::default() }
244 }
245}
246
247impl AsRef<IpDeviceConfigurationUpdate> for Ipv6DeviceConfigurationUpdate {
248 fn as_ref(&self) -> &IpDeviceConfigurationUpdate {
249 &self.ip_config
250 }
251}
252
253impl<CC, BC> IpDeviceConfigurationHandler<Ipv6, BC> for CC
254where
255 CC: Ipv6DeviceConfigurationContext<BC>,
256 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
257{
258 fn apply_configuration(
259 &mut self,
260 bindings_ctx: &mut BC,
261 config: PendingIpDeviceConfigurationUpdate<'_, Ipv6, Self::DeviceId>,
262 ) -> Ipv6DeviceConfigurationUpdate {
263 let PendingIpDeviceConfigurationUpdate(
264 Ipv6DeviceConfigurationUpdate {
265 max_router_solicitations,
266 slaac_config,
267 ip_config,
268 mld_mode,
269 },
270 device_id,
271 ) = config;
272 self.with_ipv6_device_configuration_mut(device_id, |mut inner| {
273 let (
274 max_router_solicitations_updates,
275 slaac_config_updates,
276 ip_enabled_updates,
277 gmp_enabled_updates,
278 unicast_forwarding_enabled_updates,
279 multicast_forwarding_enabled_updates,
280 dad_transmits,
281 ) = inner.with_configuration_and_flags_mut(device_id, |config, flags| {
282 let IpDeviceConfigurationUpdate {
283 ip_enabled,
284 gmp_enabled,
285 unicast_forwarding_enabled,
286 multicast_forwarding_enabled,
287 dad_transmits,
288 } = ip_config;
289 (
290 get_prev_next_and_update(
291 &mut config.max_router_solicitations,
292 max_router_solicitations,
293 ),
294 config.slaac_config.update(slaac_config),
295 get_prev_next_and_update(&mut flags.ip_enabled, ip_enabled),
296 get_prev_next_and_update(&mut config.ip_config.gmp_enabled, gmp_enabled),
297 get_prev_next_and_update(
298 &mut config.ip_config.unicast_forwarding_enabled,
299 unicast_forwarding_enabled,
300 ),
301 get_prev_next_and_update(
302 &mut config.ip_config.multicast_forwarding_enabled,
303 multicast_forwarding_enabled,
304 ),
305 get_prev_next_and_update(&mut config.ip_config.dad_transmits, dad_transmits),
306 )
307 });
308
309 let (config, mut core_ctx) = inner.ipv6_device_configuration_and_ctx();
310 let core_ctx = &mut core_ctx;
311
312 let max_router_solicitations =
313 dont_handle_change_and_get_prev(max_router_solicitations_updates);
314
315 let mld_mode =
318 mld_mode.map(|mld_mode| core_ctx.gmp_set_mode(bindings_ctx, device_id, mld_mode));
319
320 let ip_config = IpDeviceConfigurationUpdate {
321 ip_enabled: handle_change_and_get_prev(ip_enabled_updates, |next| {
322 if next {
323 device::enable_ipv6_device_with_config(
324 core_ctx,
325 bindings_ctx,
326 device_id,
327 config,
328 )
329 } else {
330 device::disable_ipv6_device_with_config(
331 core_ctx,
332 bindings_ctx,
333 device_id,
334 config,
335 )
336 }
337
338 bindings_ctx.on_event(IpDeviceEvent::EnabledChanged {
339 device: device_id.clone(),
340 ip_enabled: next,
341 })
342 }),
343 gmp_enabled: handle_change_and_get_prev(gmp_enabled_updates, |next| {
344 if next {
345 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id)
346 } else {
347 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id)
348 }
349 }),
350 unicast_forwarding_enabled: handle_change_and_get_prev(
351 unicast_forwarding_enabled_updates,
352 |next| {
353 if next {
354 device::join_ip_multicast_with_config(
355 core_ctx,
356 bindings_ctx,
357 device_id,
358 Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS,
359 config,
360 );
361 } else {
362 device::leave_ip_multicast_with_config(
363 core_ctx,
364 bindings_ctx,
365 device_id,
366 Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS,
367 config,
368 );
369 }
370 },
371 ),
372 multicast_forwarding_enabled: dont_handle_change_and_get_prev(
373 multicast_forwarding_enabled_updates,
374 ),
375 dad_transmits: dont_handle_change_and_get_prev(dad_transmits),
376 };
377 Ipv6DeviceConfigurationUpdate {
378 max_router_solicitations,
379 slaac_config: slaac_config_updates,
380 ip_config,
381 mld_mode,
382 }
383 })
384 }
385}
386
387struct Delta<T> {
388 prev: T,
389 next: T,
390}
391
392fn get_prev_next_and_update<T: Copy>(field: &mut T, next: Option<T>) -> Option<Delta<T>> {
393 next.map(|next| Delta { prev: core::mem::replace(field, next), next })
394}
395
396fn handle_change_and_get_prev<T: PartialEq>(
397 delta: Option<Delta<T>>,
398 f: impl FnOnce(T),
399) -> Option<T> {
400 delta.map(|Delta { prev, next }| {
401 if prev != next {
402 f(next)
403 }
404 prev
405 })
406}
407
408fn dont_handle_change_and_get_prev<T: PartialEq>(delta: Option<Delta<T>>) -> Option<T> {
409 handle_change_and_get_prev(delta, |_: T| {})
410}
411
412#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
414pub struct IpDeviceConfigurationAndFlags<I: IpDeviceIpExt> {
415 pub config: I::Configuration,
417 pub flags: IpDeviceFlags,
419 pub gmp_mode: I::GmpProtoConfigMode,
421}
422
423impl<I: IpDeviceIpExt> AsRef<IpDeviceConfiguration> for IpDeviceConfigurationAndFlags<I> {
424 fn as_ref(&self) -> &IpDeviceConfiguration {
425 self.config.as_ref()
426 }
427}
428
429impl<I: IpDeviceIpExt> AsMut<IpDeviceConfiguration> for IpDeviceConfigurationAndFlags<I> {
430 fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
431 self.config.as_mut()
432 }
433}
434
435impl<I: IpDeviceIpExt> AsRef<IpDeviceFlags> for IpDeviceConfigurationAndFlags<I> {
436 fn as_ref(&self) -> &IpDeviceFlags {
437 &self.flags
438 }
439}