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};
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_data_mut(device, &address_id, |address_state| {
222 let IpAddressData { flags: _, config } = address_state;
223 let Some(config) = config else {
224 return Err(NotFoundError.into());
227 };
228
229 #[derive(GenericOverIp)]
230 #[generic_over_ip(I, Ip)]
231 struct Wrap<'a, I: IpDeviceIpExt, Inst: Instant>(&'a mut I::AddressConfig<Inst>);
232 let CommonAddressProperties { valid_until, preferred_lifetime } = I::map_ip_in(
233 Wrap(config),
234 |Wrap(Ipv4AddrConfig { config: _, properties })| Ok(properties),
235 |Wrap(config)| match config {
236 Ipv6AddrConfig::Slaac(_) => Err(SetIpAddressPropertiesError::NotManual),
237 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
238 config: _,
239 properties,
240 temporary: _,
241 }) => Ok(properties),
242 },
243 )?;
244
245 let CommonAddressProperties {
246 valid_until: next_valid_until,
247 preferred_lifetime: next_preferred_lifetime,
248 } = next_properties;
249 let mut changed = core::mem::replace(valid_until, next_valid_until) != next_valid_until;
250 changed |= core::mem::replace(preferred_lifetime, next_preferred_lifetime)
251 != next_preferred_lifetime;
252
253 if changed {
254 bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
255 device: device.clone(),
256 addr: address,
257 valid_until: next_valid_until,
258 preferred_lifetime: next_preferred_lifetime,
259 });
260 }
261 Ok(())
262 })
263 }
264
265 pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnet<I::Addr>)>(
267 &mut self,
268 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
269 f: F,
270 ) {
271 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
272 addrs
273 .filter_map(|addr| {
274 let assigned = core_ctx.with_ip_address_data(device, &addr, |addr_data| {
275 let IpAddressData { flags: IpAddressFlags { assigned }, config: _ } =
276 addr_data;
277 *assigned
278 });
279 assigned.then(|| addr.addr_sub().to_witness())
280 })
281 .for_each(f);
282 })
283 }
284
285 pub fn get_assigned_ip_addr_subnets(
288 &mut self,
289 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
290 ) -> Vec<AddrSubnet<I::Addr>> {
291 let mut vec = Vec::new();
292 self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
293 vec
294 }
295
296 pub fn inspect<N: Inspector>(
298 &mut self,
299 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
300 inspector: &mut N,
301 ) where
302 C::CoreContext: GmpStateContext<I, C::BindingsContext>,
303 {
304 inspector.record_child("Addresses", |inspector| {
305 self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
306 for addr in addrs {
307 inspector.record_display_child(addr.addr_sub(), |inspector| {
308 core_ctx.with_ip_address_data(device, &addr, |addr_state| {
309 inspector.delegate_inspectable(addr_state)
310 })
311 });
312 }
313 })
314 });
315 inspector.record_child("Configuration", |inspector| {
316 self.core_ctx().with_ip_device_configuration(device, |config, _core_ctx| {
317 let IpDeviceConfiguration {
318 gmp_enabled,
319 unicast_forwarding_enabled,
320 multicast_forwarding_enabled,
321 dad_transmits,
322 } = config.as_ref();
323 inspector.record_bool("GmpEnabled", *gmp_enabled);
324 inspector.record_bool("ForwardingEnabled", *unicast_forwarding_enabled);
325 inspector.record_bool("MulticastForwardingEnabled", *multicast_forwarding_enabled);
326 inspector.record_uint("DadTransmits", dad_transmits.map(|t| t.get()).unwrap_or(0));
327 })
328 });
329 inspector.record_child("GMP", |inspector| {
330 self.core_ctx().with_gmp_state(device, |groups, gmp_state| {
331 inspector.record_inspectable_value("Mode", gmp_state.mode());
332 inspector.record_inspectable_value("Groups", groups);
333 })
334 })
335 }
336}
337pub struct DeviceIpAnyApi<C>(C);
339
340impl<C> DeviceIpAnyApi<C> {
341 pub fn new(ctx: C) -> Self {
343 Self(ctx)
344 }
345}
346
347impl<C> DeviceIpAnyApi<C>
348where
349 C: ContextPair,
350 C::CoreContext: IpDeviceConfigurationContext<Ipv4, C::BindingsContext>
351 + IpDeviceConfigurationHandler<Ipv4, C::BindingsContext>
352 + IpRoutingDeviceContext<Ipv4>
353 + IpDeviceConfigurationContext<Ipv6, C::BindingsContext>
354 + IpDeviceConfigurationHandler<Ipv6, C::BindingsContext>
355 + IpRoutingDeviceContext<Ipv6>,
356 C::BindingsContext: IpDeviceBindingsContext<Ipv4, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
357 + IpDeviceBindingsContext<Ipv6, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
358{
359 fn ip<I: Ip>(&mut self) -> DeviceIpApi<I, &mut C> {
360 let Self(pair) = self;
361 DeviceIpApi::new(pair)
362 }
363
364 pub fn add_ip_addr_subnet(
366 &mut self,
367 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
368 addr_sub_and_config: impl Into<
369 AddrSubnetAndManualConfigEither<<C::BindingsContext as InstantBindingsTypes>::Instant>,
370 >,
371 ) -> Result<(), AddIpAddrSubnetError> {
372 match addr_sub_and_config.into() {
373 AddrSubnetAndManualConfigEither::V4(addr_sub, config) => {
374 self.ip::<Ipv4>().add_ip_addr_subnet_with_config(device, addr_sub, config)
375 }
376 AddrSubnetAndManualConfigEither::V6(addr_sub, config) => {
377 self.ip::<Ipv6>().add_ip_addr_subnet_with_config(device, addr_sub, config)
378 }
379 }
380 }
381
382 pub fn del_ip_addr(
384 &mut self,
385 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
386 addr: impl Into<SpecifiedAddr<IpAddr>>,
387 ) -> Result<
388 RemoveResourceResult<
389 AddrSubnetEither,
390 Either<
395 <C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv4Addr>>,
396 <C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv6Addr>>,
397 >,
398 >,
399 NotFoundError,
400 > {
401 let addr = addr.into();
402 match addr.into() {
403 IpAddr::V4(addr) => self
404 .ip::<Ipv4>()
405 .del_ip_addr(device, addr)
406 .map(|r| r.map_removed(Into::into).map_deferred(Either::Left)),
407 IpAddr::V6(addr) => self
408 .ip::<Ipv6>()
409 .del_ip_addr(device, addr)
410 .map(|r| r.map_removed(Into::into).map_deferred(Either::Right)),
411 }
412 }
413
414 pub fn get_routing_metric(
416 &mut self,
417 device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
418 ) -> RawMetric {
419 let metric = self.ip::<Ipv4>().get_routing_metric(device_id);
422 debug_assert_eq!(metric, self.ip::<Ipv6>().get_routing_metric(device_id));
423 metric
424 }
425
426 pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnetEither)>(
429 &mut self,
430 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
431 mut f: F,
432 ) {
433 self.ip::<Ipv4>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
434 self.ip::<Ipv6>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
435 }
436
437 pub fn get_assigned_ip_addr_subnets(
440 &mut self,
441 device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
442 ) -> Vec<AddrSubnetEither> {
443 let mut vec = Vec::new();
444 self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
445 vec
446 }
447}
448
449#[derive(Debug)]
452pub enum AddrSubnetAndManualConfigEither<Instant> {
453 V4(AddrSubnet<Ipv4Addr>, Ipv4AddrConfig<Instant>),
455 V6(AddrSubnet<Ipv6Addr>, Ipv6AddrManualConfig<Instant>),
457}
458
459impl<Inst: Instant> AddrSubnetAndManualConfigEither<Inst> {
460 pub(crate) fn new<I: Ip + IpDeviceIpExt>(
462 addr_subnet: AddrSubnet<I::Addr>,
463 config: I::ManualAddressConfig<Inst>,
464 ) -> Self {
465 #[derive(GenericOverIp)]
466 #[generic_over_ip(I, Ip)]
467 struct AddrSubnetAndConfig<I: IpDeviceIpExt, Inst: Instant> {
468 addr_subnet: AddrSubnet<I::Addr>,
469 config: I::ManualAddressConfig<Inst>,
470 }
471
472 let result = I::map_ip_in(
473 AddrSubnetAndConfig { addr_subnet, config },
474 |AddrSubnetAndConfig { addr_subnet, config }| {
475 AddrSubnetAndManualConfigEither::V4(addr_subnet, config)
476 },
477 |AddrSubnetAndConfig { addr_subnet, config }| {
478 AddrSubnetAndManualConfigEither::V6(addr_subnet, config)
479 },
480 );
481 result
482 }
483
484 pub fn addr_subnet_either(&self) -> AddrSubnetEither {
486 match self {
487 Self::V4(addr_subnet, _) => AddrSubnetEither::V4(*addr_subnet),
488 Self::V6(addr_subnet, _) => AddrSubnetEither::V6(*addr_subnet),
489 }
490 }
491}
492
493impl<Inst: Instant> From<AddrSubnetEither> for AddrSubnetAndManualConfigEither<Inst> {
494 fn from(value: AddrSubnetEither) -> Self {
495 match value {
496 AddrSubnetEither::V4(addr_subnet) => {
497 AddrSubnetAndManualConfigEither::new::<Ipv4>(addr_subnet, Default::default())
498 }
499 AddrSubnetEither::V6(addr_subnet) => {
500 AddrSubnetAndManualConfigEither::new::<Ipv6>(addr_subnet, Default::default())
501 }
502 }
503 }
504}
505
506impl<Inst: Instant, I: IpAddress> From<AddrSubnet<I>> for AddrSubnetAndManualConfigEither<Inst> {
507 fn from(value: AddrSubnet<I>) -> Self {
508 AddrSubnetEither::from(value).into()
509 }
510}
511
512#[derive(Debug, Eq, PartialEq)]
515pub enum AddIpAddrSubnetError {
516 Exists,
518 InvalidAddr,
521}
522
523#[derive(Error, Debug, PartialEq)]
525pub enum SetIpAddressPropertiesError {
526 #[error(transparent)]
528 NotFound(#[from] NotFoundError),
529
530 #[error("tried to set properties on a non-manually-configured address")]
532 NotManual,
533}