1use alloc::vec::Vec;
8
9use either::Either;
10use log::trace;
11use net_types::ip::{
12 AddrSubnet, AddrSubnetEither, GenericOverIp, Ip, IpAddr, IpAddress, IpVersionMarker, Ipv4,
13 Ipv4Addr, Ipv6, Ipv6Addr,
14};
15use net_types::{SpecifiedAddr, Witness as _};
16use netstack3_base::{
17 AnyDevice, ContextPair, DeviceIdContext, DeviceIdentifier as _, EventContext as _, ExistsError,
18 Inspector, Instant, InstantBindingsTypes, IpAddressId as _, NotFoundError, ReferenceNotifiers,
19 RemoveResourceResult, RemoveResourceResultWithContext,
20};
21use thiserror::Error;
22
23use crate::internal::device::config::{
24 IpDeviceConfigurationAndFlags, IpDeviceConfigurationHandler,
25 PendingIpDeviceConfigurationUpdate, UpdateIpConfigurationError,
26};
27use crate::internal::device::state::{
28 CommonAddressProperties, IpAddressData, IpAddressFlags, IpDeviceConfiguration, Ipv4AddrConfig,
29 Ipv6AddrConfig, Ipv6AddrManualConfig,
30};
31use crate::internal::device::{
32 self, AddressRemovedReason, DelIpAddr, IpDeviceAddressContext as _, IpDeviceBindingsContext,
33 IpDeviceConfigurationContext, IpDeviceEvent, IpDeviceIpExt, IpDeviceStateContext as _,
34 WithIpDeviceConfigurationMutInner,
35};
36use crate::internal::gmp::{GmpHandler as _, GmpStateContext};
37use crate::internal::routing::IpRoutingDeviceContext;
38use crate::internal::types::RawMetric;
39
40pub struct DeviceIpApi<I: Ip, C>(C, IpVersionMarker<I>);
42
43impl<I: Ip, C> DeviceIpApi<I, C> {
44 pub fn new(ctx: C) -> Self {
46 Self(ctx, IpVersionMarker::new())
47 }
48}
49
50impl<I, C> DeviceIpApi<I, C>
51where
52 I: IpDeviceIpExt,
53 C: ContextPair,
54 C::CoreContext: IpDeviceConfigurationContext<I, C::BindingsContext>
55 + IpDeviceConfigurationHandler<I, C::BindingsContext>
56 + IpRoutingDeviceContext<I>,
57 C::BindingsContext:
58 IpDeviceBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
59{
60 fn core_ctx(&mut self) -> &mut C::CoreContext {
61 let Self(pair, IpVersionMarker { .. }) = self;
62 pair.core_ctx()
63 }
64
65 fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
66 let Self(pair, IpVersionMarker { .. }) = self;
67 pair.contexts()
68 }
69
70 pub fn add_ip_addr_subnet(
73 &mut self,
74 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
75 addr_subnet: AddrSubnet<I::Addr>,
76 ) -> Result<(), AddIpAddrSubnetError> {
77 self.add_ip_addr_subnet_with_config(device, addr_subnet, Default::default())
78 }
79
80 pub fn add_ip_addr_subnet_with_config(
86 &mut self,
87 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
88 addr_subnet: AddrSubnet<I::Addr>,
89 addr_config: I::ManualAddressConfig<<C::BindingsContext as InstantBindingsTypes>::Instant>,
90 ) -> Result<(), AddIpAddrSubnetError> {
91 let addr_subnet = addr_subnet
92 .replace_witness::<I::AssignedWitness>()
93 .ok_or(AddIpAddrSubnetError::InvalidAddr)?;
94 if !device.is_loopback() && I::LOOPBACK_SUBNET.contains(&addr_subnet.addr().get()) {
95 return Err(AddIpAddrSubnetError::InvalidAddr);
96 }
97 let (core_ctx, bindings_ctx) = self.contexts();
98 core_ctx.with_ip_device_configuration_mut(device, |mut core_ctx| {
99 let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
100 device::add_ip_addr_subnet_with_config(
101 &mut core_ctx,
102 bindings_ctx,
103 device,
104 addr_subnet,
105 addr_config.into(),
106 config,
107 )
108 .map(|_address_id| ())
109 .map_err(|ExistsError| AddIpAddrSubnetError::Exists)
110 })
111 }
112
113 pub fn del_ip_addr(
115 &mut self,
116 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
117 addr: SpecifiedAddr<I::Addr>,
118 ) -> Result<
119 RemoveResourceResultWithContext<AddrSubnet<I::Addr>, C::BindingsContext>,
120 NotFoundError,
121 > {
122 let (core_ctx, bindings_ctx) = self.contexts();
123 device::del_ip_addr(
124 core_ctx,
125 bindings_ctx,
126 device,
127 DelIpAddr::SpecifiedAddr(addr),
128 AddressRemovedReason::Manual,
129 )
130 }
131
132 pub fn new_configuration_update<'a>(
148 &mut self,
149 device_id: &'a <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
150 config: I::ConfigurationUpdate,
151 ) -> Result<
152 PendingIpDeviceConfigurationUpdate<
153 'a,
154 I,
155 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
156 >,
157 UpdateIpConfigurationError,
158 > {
159 PendingIpDeviceConfigurationUpdate::new(config, device_id)
160 }
161
162 pub fn apply_configuration(
167 &mut self,
168 config: PendingIpDeviceConfigurationUpdate<
169 '_,
170 I,
171 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
172 >,
173 ) -> I::ConfigurationUpdate {
174 let (core_ctx, bindings_ctx) = self.contexts();
175 IpDeviceConfigurationHandler::apply_configuration(core_ctx, bindings_ctx, config)
176 }
177
178 pub fn update_configuration(
181 &mut self,
182 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
183 config: I::ConfigurationUpdate,
184 ) -> Result<I::ConfigurationUpdate, UpdateIpConfigurationError> {
185 let pending = self.new_configuration_update(device_id, config)?;
186 Ok(self.apply_configuration(pending))
187 }
188
189 pub fn get_configuration(
191 &mut self,
192 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
193 ) -> IpDeviceConfigurationAndFlags<I> {
194 self.core_ctx().with_ip_device_configuration(device_id, |config, mut core_ctx| {
195 IpDeviceConfigurationAndFlags {
196 config: config.clone(),
197 flags: core_ctx.with_ip_device_flags(device_id, |flags| flags.clone()),
198 gmp_mode: core_ctx.gmp_get_mode(device_id),
199 }
200 })
201 }
202
203 pub fn get_routing_metric(
205 &mut self,
206 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
207 ) -> RawMetric {
208 self.core_ctx().get_routing_metric(device_id)
209 }
210
211 pub fn set_addr_properties(
213 &mut self,
214 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
215 address: SpecifiedAddr<I::Addr>,
216 next_properties: CommonAddressProperties<
217 <C::BindingsContext as InstantBindingsTypes>::Instant,
218 >,
219 ) -> Result<(), SetIpAddressPropertiesError> {
220 trace!("set_ip_addr_properties: setting {:?} for addr={:?}", next_properties, address);
221 let (core_ctx, bindings_ctx) = self.contexts();
222 let address_id = core_ctx.get_address_id(device, address)?;
223 core_ctx.with_ip_address_data_mut(device, &address_id, |address_state| {
224 let IpAddressData { flags: _, config } = address_state;
225 let Some(config) = config else {
226 return Err(NotFoundError.into());
229 };
230
231 #[derive(GenericOverIp)]
232 #[generic_over_ip(I, Ip)]
233 struct Wrap<'a, I: IpDeviceIpExt, Inst: Instant>(&'a mut I::AddressConfig<Inst>);
234 let CommonAddressProperties { valid_until, preferred_lifetime } = I::map_ip_in(
235 Wrap(config),
236 |Wrap(Ipv4AddrConfig { config: _, properties })| Ok(properties),
237 |Wrap(config)| match config {
238 Ipv6AddrConfig::Slaac(_) => Err(SetIpAddressPropertiesError::NotManual),
239 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
240 config: _,
241 properties,
242 temporary: _,
243 }) => Ok(properties),
244 },
245 )?;
246
247 let CommonAddressProperties {
248 valid_until: next_valid_until,
249 preferred_lifetime: next_preferred_lifetime,
250 } = next_properties;
251 let mut changed = core::mem::replace(valid_until, next_valid_until) != next_valid_until;
252 changed |= core::mem::replace(preferred_lifetime, next_preferred_lifetime)
253 != next_preferred_lifetime;
254
255 if changed {
256 bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
257 device: device.clone(),
258 addr: address,
259 valid_until: next_valid_until,
260 preferred_lifetime: next_preferred_lifetime,
261 });
262 }
263 Ok(())
264 })
265 }
266
267 pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnet<I::Addr>)>(
269 &mut self,
270 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
271 f: F,
272 ) {
273 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
274 addrs
275 .filter_map(|addr| {
276 let assigned = core_ctx.with_ip_address_data(device, &addr, |addr_data| {
277 let IpAddressData { flags: IpAddressFlags { assigned }, config: _ } =
278 addr_data;
279 *assigned
280 });
281 assigned.then(|| addr.addr_sub().to_witness())
282 })
283 .for_each(f);
284 })
285 }
286
287 pub fn get_assigned_ip_addr_subnets(
290 &mut self,
291 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
292 ) -> Vec<AddrSubnet<I::Addr>> {
293 let mut vec = Vec::new();
294 self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
295 vec
296 }
297
298 pub fn inspect<N: Inspector>(
300 &mut self,
301 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
302 inspector: &mut N,
303 ) where
304 C::CoreContext: GmpStateContext<I, C::BindingsContext>,
305 {
306 inspector.record_child("Addresses", |inspector| {
307 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
308 for addr in addrs {
309 inspector.record_display_child(addr.addr_sub(), |inspector| {
310 core_ctx.with_ip_address_data(device, &addr, |addr_state| {
311 inspector.delegate_inspectable(addr_state)
312 })
313 });
314 }
315 })
316 });
317 inspector.record_child("Configuration", |inspector| {
318 self.core_ctx().with_ip_device_configuration(device, |config, _core_ctx| {
319 let IpDeviceConfiguration {
320 gmp_enabled,
321 unicast_forwarding_enabled,
322 multicast_forwarding_enabled,
323 dad_transmits,
324 } = config.as_ref();
325 inspector.record_bool("GmpEnabled", *gmp_enabled);
326 inspector.record_bool("ForwardingEnabled", *unicast_forwarding_enabled);
327 inspector.record_bool("MulticastForwardingEnabled", *multicast_forwarding_enabled);
328 inspector.record_uint("DadTransmits", dad_transmits.map(|t| t.get()).unwrap_or(0));
329 })
330 });
331 inspector.record_child("GMP", |inspector| {
332 self.core_ctx().with_gmp_state(device, |groups, gmp_state| {
333 inspector.record_inspectable_value("Mode", gmp_state.mode());
334 inspector.record_inspectable_value("Groups", groups);
335 })
336 })
337 }
338}
339pub struct DeviceIpAnyApi<C>(C);
341
342impl<C> DeviceIpAnyApi<C> {
343 pub fn new(ctx: C) -> Self {
345 Self(ctx)
346 }
347}
348
349impl<C> DeviceIpAnyApi<C>
350where
351 C: ContextPair,
352 C::CoreContext: IpDeviceConfigurationContext<Ipv4, C::BindingsContext>
353 + IpDeviceConfigurationHandler<Ipv4, C::BindingsContext>
354 + IpRoutingDeviceContext<Ipv4>
355 + IpDeviceConfigurationContext<Ipv6, C::BindingsContext>
356 + IpDeviceConfigurationHandler<Ipv6, C::BindingsContext>
357 + IpRoutingDeviceContext<Ipv6>,
358 C::BindingsContext: IpDeviceBindingsContext<Ipv4, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
359 + IpDeviceBindingsContext<Ipv6, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
360{
361 fn ip<I: Ip>(&mut self) -> DeviceIpApi<I, &mut C> {
362 let Self(pair) = self;
363 DeviceIpApi::new(pair)
364 }
365
366 pub fn add_ip_addr_subnet(
368 &mut self,
369 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
370 addr_sub_and_config: impl Into<
371 AddrSubnetAndManualConfigEither<<C::BindingsContext as InstantBindingsTypes>::Instant>,
372 >,
373 ) -> Result<(), AddIpAddrSubnetError> {
374 match addr_sub_and_config.into() {
375 AddrSubnetAndManualConfigEither::V4(addr_sub, config) => {
376 self.ip::<Ipv4>().add_ip_addr_subnet_with_config(device, addr_sub, config)
377 }
378 AddrSubnetAndManualConfigEither::V6(addr_sub, config) => {
379 self.ip::<Ipv6>().add_ip_addr_subnet_with_config(device, addr_sub, config)
380 }
381 }
382 }
383
384 pub fn del_ip_addr(
386 &mut self,
387 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
388 addr: impl Into<SpecifiedAddr<IpAddr>>,
389 ) -> Result<
390 RemoveResourceResult<
391 AddrSubnetEither,
392 Either<
397 <C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv4Addr>>,
398 <C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv6Addr>>,
399 >,
400 >,
401 NotFoundError,
402 > {
403 let addr = addr.into();
404 match addr.into() {
405 IpAddr::V4(addr) => self
406 .ip::<Ipv4>()
407 .del_ip_addr(device, addr)
408 .map(|r| r.map_removed(Into::into).map_deferred(Either::Left)),
409 IpAddr::V6(addr) => self
410 .ip::<Ipv6>()
411 .del_ip_addr(device, addr)
412 .map(|r| r.map_removed(Into::into).map_deferred(Either::Right)),
413 }
414 }
415
416 pub fn get_routing_metric(
418 &mut self,
419 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
420 ) -> RawMetric {
421 let metric = self.ip::<Ipv4>().get_routing_metric(device_id);
424 debug_assert_eq!(metric, self.ip::<Ipv6>().get_routing_metric(device_id));
425 metric
426 }
427
428 pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnetEither)>(
431 &mut self,
432 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
433 mut f: F,
434 ) {
435 self.ip::<Ipv4>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
436 self.ip::<Ipv6>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
437 }
438
439 pub fn get_assigned_ip_addr_subnets(
442 &mut self,
443 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
444 ) -> Vec<AddrSubnetEither> {
445 let mut vec = Vec::new();
446 self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
447 vec
448 }
449}
450
451#[derive(Debug)]
454pub enum AddrSubnetAndManualConfigEither<Instant> {
455 V4(AddrSubnet<Ipv4Addr>, Ipv4AddrConfig<Instant>),
457 V6(AddrSubnet<Ipv6Addr>, Ipv6AddrManualConfig<Instant>),
459}
460
461impl<Inst: Instant> AddrSubnetAndManualConfigEither<Inst> {
462 pub(crate) fn new<I: Ip + IpDeviceIpExt>(
464 addr_subnet: AddrSubnet<I::Addr>,
465 config: I::ManualAddressConfig<Inst>,
466 ) -> Self {
467 #[derive(GenericOverIp)]
468 #[generic_over_ip(I, Ip)]
469 struct AddrSubnetAndConfig<I: IpDeviceIpExt, Inst: Instant> {
470 addr_subnet: AddrSubnet<I::Addr>,
471 config: I::ManualAddressConfig<Inst>,
472 }
473
474 let result = I::map_ip_in(
475 AddrSubnetAndConfig { addr_subnet, config },
476 |AddrSubnetAndConfig { addr_subnet, config }| {
477 AddrSubnetAndManualConfigEither::V4(addr_subnet, config)
478 },
479 |AddrSubnetAndConfig { addr_subnet, config }| {
480 AddrSubnetAndManualConfigEither::V6(addr_subnet, config)
481 },
482 );
483 result
484 }
485
486 pub fn addr_subnet_either(&self) -> AddrSubnetEither {
488 match self {
489 Self::V4(addr_subnet, _) => AddrSubnetEither::V4(*addr_subnet),
490 Self::V6(addr_subnet, _) => AddrSubnetEither::V6(*addr_subnet),
491 }
492 }
493}
494
495impl<Inst: Instant> From<AddrSubnetEither> for AddrSubnetAndManualConfigEither<Inst> {
496 fn from(value: AddrSubnetEither) -> Self {
497 match value {
498 AddrSubnetEither::V4(addr_subnet) => {
499 AddrSubnetAndManualConfigEither::new::<Ipv4>(addr_subnet, Default::default())
500 }
501 AddrSubnetEither::V6(addr_subnet) => {
502 AddrSubnetAndManualConfigEither::new::<Ipv6>(addr_subnet, Default::default())
503 }
504 }
505 }
506}
507
508impl<Inst: Instant, I: IpAddress> From<AddrSubnet<I>> for AddrSubnetAndManualConfigEither<Inst> {
509 fn from(value: AddrSubnet<I>) -> Self {
510 AddrSubnetEither::from(value).into()
511 }
512}
513
514#[derive(Debug, Eq, PartialEq)]
517pub enum AddIpAddrSubnetError {
518 Exists,
520 InvalidAddr,
523}
524
525#[derive(Error, Debug, PartialEq)]
527pub enum SetIpAddressPropertiesError {
528 #[error(transparent)]
530 NotFound(#[from] NotFoundError),
531
532 #[error("tried to set properties on a non-manually-configured address")]
534 NotManual,
535}