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, IpDeviceConfiguration, Ipv4AddrConfig, Ipv4AddressState,
29 Ipv6AddrConfig, Ipv6AddrManualConfig, Ipv6AddressState,
30};
31use crate::internal::device::{
32 self, AddressRemovedReason, DelIpAddr, IpDeviceAddressContext as _, IpDeviceBindingsContext,
33 IpDeviceConfigurationContext, IpDeviceEvent, IpDeviceIpExt, IpDeviceStateContext as _,
34};
35use crate::internal::gmp::{GmpHandler as _, GmpStateContext};
36use crate::internal::routing::IpRoutingDeviceContext;
37use crate::internal::types::RawMetric;
38
39pub struct DeviceIpApi<I: Ip, C>(C, IpVersionMarker<I>);
41
42impl<I: Ip, C> DeviceIpApi<I, C> {
43 pub fn new(ctx: C) -> Self {
45 Self(ctx, IpVersionMarker::new())
46 }
47}
48
49impl<I, C> DeviceIpApi<I, C>
50where
51 I: IpDeviceIpExt,
52 C: ContextPair,
53 C::CoreContext: IpDeviceConfigurationContext<I, C::BindingsContext>
54 + IpDeviceConfigurationHandler<I, C::BindingsContext>
55 + IpRoutingDeviceContext<I>,
56 C::BindingsContext:
57 IpDeviceBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
58{
59 fn core_ctx(&mut self) -> &mut C::CoreContext {
60 let Self(pair, IpVersionMarker { .. }) = self;
61 pair.core_ctx()
62 }
63
64 fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
65 let Self(pair, IpVersionMarker { .. }) = self;
66 pair.contexts()
67 }
68
69 pub fn add_ip_addr_subnet(
72 &mut self,
73 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
74 addr_subnet: AddrSubnet<I::Addr>,
75 ) -> Result<(), AddIpAddrSubnetError> {
76 self.add_ip_addr_subnet_with_config(device, addr_subnet, Default::default())
77 }
78
79 pub fn add_ip_addr_subnet_with_config(
85 &mut self,
86 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
87 addr_subnet: AddrSubnet<I::Addr>,
88 addr_config: I::ManualAddressConfig<<C::BindingsContext as InstantBindingsTypes>::Instant>,
89 ) -> Result<(), AddIpAddrSubnetError> {
90 let addr_subnet = addr_subnet
91 .replace_witness::<I::AssignedWitness>()
92 .ok_or(AddIpAddrSubnetError::InvalidAddr)?;
93 if !device.is_loopback() && I::LOOPBACK_SUBNET.contains(&addr_subnet.addr().get()) {
94 return Err(AddIpAddrSubnetError::InvalidAddr);
95 }
96 let (core_ctx, bindings_ctx) = self.contexts();
97 core_ctx.with_ip_device_configuration(device, |config, mut core_ctx| {
98 device::add_ip_addr_subnet_with_config(
99 &mut core_ctx,
100 bindings_ctx,
101 device,
102 addr_subnet,
103 addr_config.into(),
104 config,
105 )
106 .map(|_address_id| ())
107 .map_err(|ExistsError| AddIpAddrSubnetError::Exists)
108 })
109 }
110
111 pub fn del_ip_addr(
113 &mut self,
114 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
115 addr: SpecifiedAddr<I::Addr>,
116 ) -> Result<
117 RemoveResourceResultWithContext<AddrSubnet<I::Addr>, C::BindingsContext>,
118 NotFoundError,
119 > {
120 let (core_ctx, bindings_ctx) = self.contexts();
121 device::del_ip_addr(
122 core_ctx,
123 bindings_ctx,
124 device,
125 DelIpAddr::SpecifiedAddr(addr),
126 AddressRemovedReason::Manual,
127 )
128 }
129
130 pub fn new_configuration_update<'a>(
146 &mut self,
147 device_id: &'a <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
148 config: I::ConfigurationUpdate,
149 ) -> Result<
150 PendingIpDeviceConfigurationUpdate<
151 'a,
152 I,
153 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
154 >,
155 UpdateIpConfigurationError,
156 > {
157 PendingIpDeviceConfigurationUpdate::new(config, device_id)
158 }
159
160 pub fn apply_configuration(
165 &mut self,
166 config: PendingIpDeviceConfigurationUpdate<
167 '_,
168 I,
169 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
170 >,
171 ) -> I::ConfigurationUpdate {
172 let (core_ctx, bindings_ctx) = self.contexts();
173 IpDeviceConfigurationHandler::apply_configuration(core_ctx, bindings_ctx, config)
174 }
175
176 pub fn update_configuration(
179 &mut self,
180 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
181 config: I::ConfigurationUpdate,
182 ) -> Result<I::ConfigurationUpdate, UpdateIpConfigurationError> {
183 let pending = self.new_configuration_update(device_id, config)?;
184 Ok(self.apply_configuration(pending))
185 }
186
187 pub fn get_configuration(
189 &mut self,
190 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
191 ) -> IpDeviceConfigurationAndFlags<I> {
192 self.core_ctx().with_ip_device_configuration(device_id, |config, mut core_ctx| {
193 IpDeviceConfigurationAndFlags {
194 config: config.clone(),
195 flags: core_ctx.with_ip_device_flags(device_id, |flags| flags.clone()),
196 gmp_mode: core_ctx.gmp_get_mode(device_id),
197 }
198 })
199 }
200
201 pub fn get_routing_metric(
203 &mut self,
204 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
205 ) -> RawMetric {
206 self.core_ctx().get_routing_metric(device_id)
207 }
208
209 pub fn set_addr_properties(
211 &mut self,
212 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
213 address: SpecifiedAddr<I::Addr>,
214 next_properties: CommonAddressProperties<
215 <C::BindingsContext as InstantBindingsTypes>::Instant,
216 >,
217 ) -> Result<(), SetIpAddressPropertiesError> {
218 trace!("set_ip_addr_properties: setting {:?} for addr={:?}", next_properties, address);
219 let (core_ctx, bindings_ctx) = self.contexts();
220 let address_id = core_ctx.get_address_id(device, address)?;
221 core_ctx.with_ip_address_state_mut(device, &address_id, |address_state| {
222 #[derive(GenericOverIp)]
223 #[generic_over_ip(I, Ip)]
224 struct Wrap<'a, I: IpDeviceIpExt, II: Instant>(&'a mut I::AddressState<II>);
225 let CommonAddressProperties { valid_until, preferred_lifetime } = I::map_ip_in(
226 Wrap(address_state),
227 |Wrap(Ipv4AddressState { config })| {
228 match config {
229 Some(Ipv4AddrConfig { common }) => Ok(common),
230 None => Err(NotFoundError.into()),
233 }
234 },
235 |Wrap(Ipv6AddressState { flags: _, config })| {
236 match config {
237 None => Err(NotFoundError.into()),
240 Some(Ipv6AddrConfig::Slaac(_)) => {
241 Err(SetIpAddressPropertiesError::NotManual)
242 }
243 Some(Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
244 common,
245 temporary: _,
246 })) => Ok(common),
247 }
248 },
249 )?;
250
251 let CommonAddressProperties {
252 valid_until: next_valid_until,
253 preferred_lifetime: next_preferred_lifetime,
254 } = next_properties;
255 let mut changed = core::mem::replace(valid_until, next_valid_until) != next_valid_until;
256 changed |= core::mem::replace(preferred_lifetime, next_preferred_lifetime)
257 != next_preferred_lifetime;
258
259 if changed {
260 bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
261 device: device.clone(),
262 addr: address,
263 valid_until: next_valid_until,
264 preferred_lifetime: next_preferred_lifetime,
265 });
266 }
267 Ok(())
268 })
269 }
270
271 pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnet<I::Addr>)>(
273 &mut self,
274 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
275 f: F,
276 ) {
277 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
278 addrs
279 .filter_map(|addr| {
280 let assigned = core_ctx.with_ip_address_state(device, &addr, |addr_state| {
281 I::is_addr_assigned(addr_state)
282 });
283 assigned.then(|| addr.addr_sub().to_witness())
284 })
285 .for_each(f);
286 })
287 }
288
289 pub fn get_assigned_ip_addr_subnets(
292 &mut self,
293 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
294 ) -> Vec<AddrSubnet<I::Addr>> {
295 let mut vec = Vec::new();
296 self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
297 vec
298 }
299
300 pub fn inspect<N: Inspector>(
302 &mut self,
303 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
304 inspector: &mut N,
305 ) where
306 C::CoreContext: GmpStateContext<I, C::BindingsContext>,
307 {
308 inspector.record_child("Addresses", |inspector| {
309 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
310 for addr in addrs {
311 inspector.record_display_child(addr.addr_sub(), |inspector| {
312 core_ctx.with_ip_address_state(device, &addr, |addr_state| {
313 inspector.delegate_inspectable(addr_state)
314 })
315 });
316 }
317 })
318 });
319 inspector.record_child("Configuration", |inspector| {
320 self.core_ctx().with_ip_device_configuration(device, |config, _core_ctx| {
321 let IpDeviceConfiguration {
322 gmp_enabled,
323 unicast_forwarding_enabled,
324 multicast_forwarding_enabled,
325 } = config.as_ref();
326 inspector.record_bool("GmpEnabled", *gmp_enabled);
327 inspector.record_bool("ForwardingEnabled", *unicast_forwarding_enabled);
328 inspector.record_bool("MulticastForwardingEnabled", *multicast_forwarding_enabled);
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}