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