1use alloc::fmt::Debug;
8use core::marker::PhantomData;
9
10use log::debug;
11use net_types::ip::{Ipv4, Ipv6};
12use netstack3_base::{
13 AnyDevice, ContextPair, CoreTimerContext, Device, DeviceIdAnyCompatContext, DeviceIdContext,
14 Inspector, RecvFrameContext, ReferenceNotifiers, ReferenceNotifiersExt as _,
15 RemoveResourceResultWithContext, ResourceCounterContext, TimerContext,
16};
17use netstack3_ip::device::{
18 IpAddressIdSpecContext, IpDeviceBindingsContext, IpDeviceConfigurationContext, IpDeviceTimerId,
19 Ipv6DeviceConfigurationContext,
20};
21use netstack3_ip::gmp::{IgmpCounters, MldCounters};
22use netstack3_ip::{self as ip, IpCounters, RawMetric};
23use packet::BufferMut;
24
25use crate::internal::base::{
26 DeviceCollectionContext, DeviceCounters, DeviceLayerStateTypes, DeviceLayerTypes,
27 DeviceReceiveFrameSpec, OriginTrackerContext,
28};
29use crate::internal::blackhole::BlackholeDevice;
30use crate::internal::config::{
31 ArpConfiguration, ArpConfigurationUpdate, DeviceConfiguration, DeviceConfigurationContext,
32 DeviceConfigurationUpdate, DeviceConfigurationUpdateError, NdpConfiguration,
33 NdpConfigurationUpdate,
34};
35use crate::internal::ethernet::EthernetLinkDevice;
36use crate::internal::id::{
37 for_any_device_id, BaseDeviceId, BasePrimaryDeviceId, BaseWeakDeviceId, DeviceId,
38 DeviceProvider,
39};
40use crate::internal::loopback::LoopbackDevice;
41use crate::internal::pure_ip::PureIpDevice;
42use crate::internal::state::{BaseDeviceState, DeviceStateSpec, IpLinkDeviceStateInner};
43
44pub struct PendingDeviceConfigurationUpdate<'a, D>(DeviceConfigurationUpdate, &'a D);
53
54pub struct DeviceApi<D, C>(C, PhantomData<D>);
56
57impl<D, C> DeviceApi<D, C> {
58 pub fn new(ctx: C) -> Self {
60 Self(ctx, PhantomData)
61 }
62}
63
64impl<D, C> DeviceApi<D, C>
65where
66 D: Device + DeviceStateSpec + DeviceReceiveFrameSpec,
67 C: ContextPair,
68 C::CoreContext: DeviceApiCoreContext<D, C::BindingsContext>,
69 C::BindingsContext: DeviceApiBindingsContext,
70{
71 pub(crate) fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
72 let Self(pair, PhantomData) = self;
73 pair.contexts()
74 }
75
76 pub(crate) fn core_ctx(&mut self) -> &mut C::CoreContext {
77 let Self(pair, PhantomData) = self;
78 pair.core_ctx()
79 }
80
81 pub fn add_device(
87 &mut self,
88 bindings_id: <C::BindingsContext as DeviceLayerStateTypes>::DeviceIdentifier,
89 properties: D::CreationProperties,
90 metric: RawMetric,
91 external_state: D::External<C::BindingsContext>,
92 ) -> <C::CoreContext as DeviceIdContext<D>>::DeviceId
93 where
94 C::CoreContext: DeviceApiIpLayerCoreContext<D, C::BindingsContext>,
95 {
96 debug!("adding {} device with {:?} metric:{metric}", D::DEBUG_TYPE, properties);
97 let (core_ctx, bindings_ctx) = self.contexts();
98 let origin = core_ctx.origin_tracker();
99 let primary = BasePrimaryDeviceId::new(
100 |weak_ref| {
101 let link = D::new_device_state::<C::CoreContext, _>(
102 bindings_ctx,
103 weak_ref.clone(),
104 properties,
105 );
106 IpLinkDeviceStateInner::new::<_, _, C::CoreContext>(
107 bindings_ctx,
108 weak_ref.into(),
109 link,
110 metric,
111 origin,
112 )
113 },
114 external_state,
115 bindings_id,
116 );
117 let id = primary.clone_strong();
118 core_ctx.insert(primary);
119 id
120 }
121
122 #[cfg(any(test, feature = "testutils"))]
128 pub fn add_device_with_default_state(
129 &mut self,
130 properties: D::CreationProperties,
131 metric: RawMetric,
132 ) -> <C::CoreContext as DeviceIdContext<D>>::DeviceId
133 where
134 <C::BindingsContext as DeviceLayerStateTypes>::DeviceIdentifier: Default,
135 D::External<C::BindingsContext>: Default,
136 C::CoreContext: DeviceApiIpLayerCoreContext<D, C::BindingsContext>,
137 {
138 self.add_device(Default::default(), properties, metric, Default::default())
139 }
140
141 pub fn remove_device(
154 &mut self,
155 device: BaseDeviceId<D, C::BindingsContext>,
156 ) -> RemoveResourceResultWithContext<D::External<C::BindingsContext>, C::BindingsContext>
157 where
158 BaseDeviceId<D, C::BindingsContext>: Into<DeviceId<C::BindingsContext>>,
160 C::CoreContext: IpDeviceConfigurationContext<Ipv4, C::BindingsContext>
161 + Ipv6DeviceConfigurationContext<C::BindingsContext>
162 + DeviceIdContext<AnyDevice, DeviceId = DeviceId<C::BindingsContext>>,
163 C::BindingsContext: IpDeviceBindingsContext<Ipv4, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
164 + IpDeviceBindingsContext<Ipv6, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
165 {
166 let (core_ctx, bindings_ctx) = self.contexts();
170 {
171 let device = device.clone().into();
172 ip::device::clear_ipv4_device_state(core_ctx, bindings_ctx, &device);
173 ip::device::clear_ipv6_device_state(core_ctx, bindings_ctx, &device);
174 };
175
176 debug!("removing {device:?}");
177 let primary = core_ctx.remove(&device).expect("tried to remove device not in stack");
178 assert_eq!(device, primary);
179 core::mem::drop(device);
180 C::BindingsContext::unwrap_or_notify_with_new_reference_notifier(
181 primary.into_inner(),
182 |state: BaseDeviceState<_, _>| state.external_state,
183 )
184 }
185
186 pub fn receive_frame<B: BufferMut + Debug>(
188 &mut self,
189 meta: D::FrameMetadata<BaseDeviceId<D, C::BindingsContext>>,
190 frame: B,
191 ) {
192 let (core_ctx, bindings_ctx) = self.contexts();
193 core_ctx.receive_frame(bindings_ctx, meta, frame)
194 }
195
196 pub fn apply_configuration(
202 &mut self,
203 pending: PendingDeviceConfigurationUpdate<'_, BaseDeviceId<D, C::BindingsContext>>,
204 ) -> DeviceConfigurationUpdate {
205 let PendingDeviceConfigurationUpdate(DeviceConfigurationUpdate { arp, ndp }, device_id) =
206 pending;
207 let core_ctx = self.core_ctx();
208 let arp = core_ctx.with_nud_config_mut::<Ipv4, _, _>(device_id, move |device_config| {
209 let device_config = match device_config {
210 Some(c) => c,
211 None => {
212 assert!(arp.is_none());
216 return None;
217 }
218 };
219 arp.map(|ArpConfigurationUpdate { nud }| {
220 let nud = nud.map(|config| config.apply_and_take_previous(device_config));
221 ArpConfigurationUpdate { nud }
222 })
223 });
224 let ndp = core_ctx.with_nud_config_mut::<Ipv6, _, _>(device_id, move |device_config| {
225 let device_config = match device_config {
226 Some(c) => c,
227 None => {
228 assert!(ndp.is_none());
232 return None;
233 }
234 };
235 ndp.map(|NdpConfigurationUpdate { nud }| {
236 let nud = nud.map(|config| config.apply_and_take_previous(device_config));
237 NdpConfigurationUpdate { nud }
238 })
239 });
240 DeviceConfigurationUpdate { arp, ndp }
241 }
242
243 pub fn new_configuration_update<'a>(
248 &mut self,
249 device: &'a BaseDeviceId<D, C::BindingsContext>,
250 config: DeviceConfigurationUpdate,
251 ) -> Result<
252 PendingDeviceConfigurationUpdate<'a, BaseDeviceId<D, C::BindingsContext>>,
253 DeviceConfigurationUpdateError,
254 > {
255 let core_ctx = self.core_ctx();
256 let DeviceConfigurationUpdate { arp, ndp } = &config;
257 if arp.is_some() && core_ctx.with_nud_config::<Ipv4, _, _>(device, |c| c.is_none()) {
258 return Err(DeviceConfigurationUpdateError::ArpNotSupported);
259 }
260 if ndp.is_some() && core_ctx.with_nud_config::<Ipv6, _, _>(device, |c| c.is_none()) {
261 return Err(DeviceConfigurationUpdateError::NdpNotSupported);
262 }
263 Ok(PendingDeviceConfigurationUpdate(config, device))
264 }
265
266 pub fn get_configuration(
268 &mut self,
269 device: &BaseDeviceId<D, C::BindingsContext>,
270 ) -> DeviceConfiguration {
271 let core_ctx = self.core_ctx();
272 let arp = core_ctx
273 .with_nud_config::<Ipv4, _, _>(device, |config| config.cloned())
274 .map(|nud| ArpConfiguration { nud });
275 let ndp = core_ctx
276 .with_nud_config::<Ipv6, _, _>(device, |config| config.cloned())
277 .map(|nud| NdpConfiguration { nud });
278 DeviceConfiguration { arp, ndp }
279 }
280
281 pub fn inspect<N: Inspector>(
283 &mut self,
284 device: &BaseDeviceId<D, C::BindingsContext>,
285 inspector: &mut N,
286 ) {
287 inspector.record_child("Counters", |inspector| {
288 inspector.delegate_inspectable(
289 ResourceCounterContext::<_, DeviceCounters>::per_resource_counters(
290 self.core_ctx(),
291 device,
292 ),
293 );
294 inspector.delegate_inspectable(
295 ResourceCounterContext::<_, D::Counters>::per_resource_counters(
296 self.core_ctx(),
297 device,
298 ),
299 );
300 inspector.record_child("IPv4", |inspector| {
301 inspector.delegate_inspectable(
302 ResourceCounterContext::<_, IpCounters<Ipv4>>::per_resource_counters(
303 self.core_ctx(),
304 device,
305 ),
306 )
307 });
308 inspector.record_child("IPv6", |inspector| {
309 inspector.delegate_inspectable(
310 ResourceCounterContext::<_, IpCounters<Ipv6>>::per_resource_counters(
311 self.core_ctx(),
312 device,
313 ),
314 )
315 });
316 inspector.record_child("IGMP", |inspector| {
317 inspector.delegate_inspectable(
318 ResourceCounterContext::<_, IgmpCounters>::per_resource_counters(
319 self.core_ctx(),
320 device,
321 ),
322 );
323 });
324 inspector.record_child("MLD", |inspector| {
325 inspector.delegate_inspectable(
326 ResourceCounterContext::<_, MldCounters>::per_resource_counters(
327 self.core_ctx(),
328 device,
329 ),
330 );
331 });
332 });
333 }
334}
335
336pub struct DeviceAnyApi<C>(C);
338
339impl<C> DeviceAnyApi<C> {
340 pub fn new(ctx: C) -> Self {
342 Self(ctx)
343 }
344}
345
346impl<C> DeviceAnyApi<C>
347where
348 C: ContextPair,
349 C::CoreContext: DeviceApiCoreContext<EthernetLinkDevice, C::BindingsContext>
350 + DeviceApiCoreContext<LoopbackDevice, C::BindingsContext>
351 + DeviceApiCoreContext<PureIpDevice, C::BindingsContext>
352 + DeviceApiCoreContext<BlackholeDevice, C::BindingsContext>,
353 C::BindingsContext: DeviceApiBindingsContext,
354{
355 fn device<D>(&mut self) -> DeviceApi<D, &mut C> {
356 let Self(pair) = self;
357 DeviceApi::new(pair)
358 }
359
360 pub fn apply_configuration(
362 &mut self,
363 pending: PendingDeviceConfigurationUpdate<'_, DeviceId<C::BindingsContext>>,
364 ) -> DeviceConfigurationUpdate {
365 let PendingDeviceConfigurationUpdate(config, device) = pending;
366 for_any_device_id!(DeviceId, device,
367 device => {
368 self.device().apply_configuration(PendingDeviceConfigurationUpdate(config, device))
369 }
370 )
371 }
372
373 pub fn new_configuration_update<'a>(
376 &mut self,
377 device: &'a DeviceId<C::BindingsContext>,
378 config: DeviceConfigurationUpdate,
379 ) -> Result<
380 PendingDeviceConfigurationUpdate<'a, DeviceId<C::BindingsContext>>,
381 DeviceConfigurationUpdateError,
382 > {
383 for_any_device_id!(DeviceId, device,
384 inner => {
385 self.device()
386 .new_configuration_update(inner, config)
387 .map(|PendingDeviceConfigurationUpdate(config, _)| {
388 PendingDeviceConfigurationUpdate(config, device)
389 })
390 }
391 )
392 }
393
394 pub fn get_configuration(
396 &mut self,
397 device: &DeviceId<C::BindingsContext>,
398 ) -> DeviceConfiguration {
399 for_any_device_id!(DeviceId, device,
400 device => self.device().get_configuration(device))
401 }
402
403 pub fn inspect<N: Inspector>(
405 &mut self,
406 device: &DeviceId<C::BindingsContext>,
407 inspector: &mut N,
408 ) {
409 for_any_device_id!(DeviceId, DeviceProvider, D, device,
410 device => self.device::<D>().inspect(device, inspector))
411 }
412}
413
414pub trait DeviceApiCoreContext<
417 D: Device + DeviceStateSpec + DeviceReceiveFrameSpec,
418 BC: DeviceApiBindingsContext,
419>:
420 DeviceIdContext<D, DeviceId = BaseDeviceId<D, BC>, WeakDeviceId = BaseWeakDeviceId<D, BC>>
421 + OriginTrackerContext
422 + DeviceCollectionContext<D, BC>
423 + DeviceConfigurationContext<D>
424 + RecvFrameContext<D::FrameMetadata<BaseDeviceId<D, BC>>, BC>
425 + ResourceCounterContext<Self::DeviceId, DeviceCounters>
426 + ResourceCounterContext<Self::DeviceId, D::Counters>
427 + ResourceCounterContext<Self::DeviceId, IpCounters<Ipv4>>
428 + ResourceCounterContext<Self::DeviceId, IpCounters<Ipv6>>
429 + ResourceCounterContext<Self::DeviceId, IgmpCounters>
430 + ResourceCounterContext<Self::DeviceId, MldCounters>
431 + CoreTimerContext<D::TimerId<Self::WeakDeviceId>, BC>
432{
433}
434
435impl<CC, D, BC> DeviceApiCoreContext<D, BC> for CC
436where
437 D: Device + DeviceStateSpec + DeviceReceiveFrameSpec,
438 BC: DeviceApiBindingsContext,
439 CC: DeviceIdContext<D, DeviceId = BaseDeviceId<D, BC>, WeakDeviceId = BaseWeakDeviceId<D, BC>>
440 + OriginTrackerContext
441 + DeviceCollectionContext<D, BC>
442 + DeviceConfigurationContext<D>
443 + RecvFrameContext<D::FrameMetadata<BaseDeviceId<D, BC>>, BC>
444 + ResourceCounterContext<Self::DeviceId, DeviceCounters>
445 + ResourceCounterContext<Self::DeviceId, D::Counters>
446 + ResourceCounterContext<Self::DeviceId, IpCounters<Ipv4>>
447 + ResourceCounterContext<Self::DeviceId, IpCounters<Ipv6>>
448 + ResourceCounterContext<Self::DeviceId, IgmpCounters>
449 + ResourceCounterContext<Self::DeviceId, MldCounters>
450 + CoreTimerContext<D::TimerId<Self::WeakDeviceId>, BC>,
451{
452}
453
454pub trait DeviceApiBindingsContext: DeviceLayerTypes + ReferenceNotifiers + TimerContext {}
457
458impl<O> DeviceApiBindingsContext for O where O: DeviceLayerTypes + ReferenceNotifiers + TimerContext {}
459
460pub trait DeviceApiIpLayerCoreContext<D: Device, BC: DeviceLayerTypes>:
463 DeviceIdAnyCompatContext<D>
464 + IpAddressIdSpecContext
465 + CoreTimerContext<
466 IpDeviceTimerId<
467 Ipv6,
468 <Self as DeviceIdContext<AnyDevice>>::WeakDeviceId,
469 Self::AddressIdSpec,
470 >,
471 BC,
472 > + CoreTimerContext<
473 IpDeviceTimerId<
474 Ipv4,
475 <Self as DeviceIdContext<AnyDevice>>::WeakDeviceId,
476 Self::AddressIdSpec,
477 >,
478 BC,
479 >
480{
481}
482
483impl<O, D, BC> DeviceApiIpLayerCoreContext<D, BC> for O
484where
485 D: Device,
486 BC: DeviceLayerTypes,
487 O: DeviceIdAnyCompatContext<D>
488 + IpAddressIdSpecContext
489 + CoreTimerContext<
490 IpDeviceTimerId<
491 Ipv6,
492 <Self as DeviceIdContext<AnyDevice>>::WeakDeviceId,
493 Self::AddressIdSpec,
494 >,
495 BC,
496 > + CoreTimerContext<
497 IpDeviceTimerId<
498 Ipv4,
499 <Self as DeviceIdContext<AnyDevice>>::WeakDeviceId,
500 Self::AddressIdSpec,
501 >,
502 BC,
503 >,
504{
505}