1use alloc::vec::Vec;
8use assert_matches::assert_matches;
9use net_types::ip::{Ip, IpAddr, IpVersionMarker, Ipv4, Ipv6};
10use net_types::{SpecifiedAddr, Witness as _};
11
12use netstack3_base::sync::PrimaryRc;
13use netstack3_base::{
14 AnyDevice, ContextPair, DeferredResourceRemovalContext, DeviceIdContext, InspectableValue,
15 Inspector, InspectorDeviceExt, Marks, ReferenceNotifiersExt as _,
16 RemoveResourceResultWithContext, StrongDeviceIdentifier, SubnetMatcher, WrapBroadcastMarker,
17};
18
19use crate::IpRoutingBindingsTypes;
20use crate::internal::base::{
21 self, BaseRoutingTableState, IpLayerBindingsContext, IpLayerContext, IpLayerIpExt,
22 IpRouteTableContext, IpRouteTablesContext, IpStateContext as _, ResolveRouteError,
23 RoutingTableCookie, RoutingTableId,
24};
25use crate::internal::device::{
26 IpDeviceBindingsContext, IpDeviceConfigurationContext, IpDeviceIpExt,
27};
28use crate::internal::routing::rules::{
29 Rule, RuleAction, RuleMatcher, RulesTable, TrafficOriginMatcher,
30};
31use crate::internal::types::{
32 Destination, Entry, EntryAndGeneration, Metric, NextHop, OrderedEntry, ResolvedRoute,
33 RoutableIpAddr,
34};
35use diagnostics_traits::InspectorRouteTableExt;
36
37#[derive(Debug, Clone, Default)]
39pub struct RouteResolveOptions {
40 pub marks: Marks,
42}
43
44pub type RoutesApiTableId<I, C> = RoutingTableId<
46 I,
47 <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
48 <C as ContextPair>::BindingsContext,
49>;
50
51pub struct RoutesApi<I: Ip, C>(C, IpVersionMarker<I>);
53
54impl<I: Ip, C> RoutesApi<I, C> {
55 pub fn new(ctx: C) -> Self {
57 Self(ctx, IpVersionMarker::new())
58 }
59}
60
61impl<I, C> RoutesApi<I, C>
62where
63 I: IpLayerIpExt + IpDeviceIpExt,
64 C: ContextPair,
65 C::CoreContext: RoutesApiCoreContext<I, C::BindingsContext>,
66 C::BindingsContext:
67 RoutesApiBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
68 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId: Ord,
69{
70 fn core_ctx(&mut self) -> &mut C::CoreContext {
71 let Self(pair, IpVersionMarker { .. }) = self;
72 pair.core_ctx()
73 }
74
75 pub fn new_table(
77 &mut self,
78 bindings_id: <C::BindingsContext as IpRoutingBindingsTypes>::RoutingTableId,
79 ) -> RoutesApiTableId<I, C> {
80 self.core_ctx().with_ip_routing_tables_mut(|tables| {
81 let new_table = PrimaryRc::new(BaseRoutingTableState::with_bindings_id(
82 RoutingTableCookie::BindingsId(bindings_id),
83 ));
84 let table_id = RoutingTableId::new(PrimaryRc::clone_strong(&new_table));
85 assert_matches!(tables.insert(table_id.clone(), new_table), None);
86 table_id
87 })
88 }
89
90 pub fn remove_table(
96 &mut self,
97 id: RoutesApiTableId<I, C>,
98 ) -> RemoveResourceResultWithContext<(), C::BindingsContext> {
99 assert!(id != self.main_table_id(), "main table should never be removed");
100 self.core_ctx().with_ip_routing_tables_mut(|tables| {
101 let table = assert_matches!(
102 tables.remove(&id),
103 Some(removed) => removed
104 );
105 C::BindingsContext::unwrap_or_notify_with_new_reference_notifier(table, |_| ())
106 })
107 }
108
109 pub fn main_table_id(&mut self) -> RoutesApiTableId<I, C> {
111 self.core_ctx().main_table_id()
112 }
113
114 pub fn collect_routes_into<
116 X: From<Entry<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>>,
117 T: Extend<X>,
118 >(
119 &mut self,
120 table_id: &RoutesApiTableId<I, C>,
121 target: &mut T,
122 ) {
123 self.core_ctx().with_ip_routing_table(table_id, |_core_ctx, table| {
124 target.extend(table.iter_table().cloned().map(Into::into))
125 })
126 }
127
128 pub fn collect_main_table_routes_into<
130 X: From<Entry<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>>,
131 T: Extend<X>,
132 >(
133 &mut self,
134 target: &mut T,
135 ) {
136 self.core_ctx().with_main_ip_routing_table(|_core_ctx, table| {
137 target.extend(table.iter_table().cloned().map(Into::into))
138 })
139 }
140
141 pub fn fold_routes<B, F>(&mut self, init: B, mut cb: F) -> B
145 where
146 F: FnMut(
147 B,
148 &RoutesApiTableId<I, C>,
149 &Entry<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
150 ) -> B,
151 {
152 self.core_ctx().with_ip_routing_tables(|ctx, tables| {
153 tables.keys().fold(init, |state, table_id| {
154 ctx.with_ip_routing_table(table_id, |_ctx, table| {
155 table.iter_table().fold(state, |state, entry| cb(state, table_id, entry))
156 })
157 })
158 })
159 }
160
161 pub fn resolve_route(
166 &mut self,
167 destination: Option<RoutableIpAddr<I::Addr>>,
168 RouteResolveOptions { marks }: &RouteResolveOptions,
169 ) -> Result<
170 ResolvedRoute<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
171 ResolveRouteError,
172 > {
173 base::resolve_output_route_to_destination(self.core_ctx(), None, None, destination, marks)
174 }
175
176 pub fn select_device_for_gateway(
179 &mut self,
180 gateway: SpecifiedAddr<I::Addr>,
181 ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId> {
182 self.core_ctx().with_main_ip_routing_table_mut(|core_ctx, table| {
183 table.lookup(core_ctx, None, *gateway).and_then(
184 |Destination { next_hop: found_next_hop, device: found_device }| {
185 match found_next_hop {
186 NextHop::RemoteAsNeighbor => Some(found_device),
187 NextHop::Broadcast(marker) => {
188 I::map_ip::<_, ()>(
189 WrapBroadcastMarker(marker),
190 |WrapBroadcastMarker(())| (),
191 |WrapBroadcastMarker(never)| match never {},
192 );
193 Some(found_device)
194 }
195 NextHop::Gateway(_intermediary_gateway) => None,
196 }
197 },
198 )
199 })
200 }
201
202 pub fn inspect<N>(&mut self, inspector: &mut N)
204 where
205 N: Inspector,
206 for<'a> N::ChildInspector<'a>: InspectorRouteTableExt<RoutesApiTableId<I, C>>
207 + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
208 {
209 inspector.record_child("Rules", |inspector| {
210 self.inspect_rules(inspector);
211 });
212 inspector.record_child("RoutingTables", |inspector| {
213 self.inspect_routes(inspector);
214 })
215 }
216
217 pub fn inspect_rules<N>(&mut self, inspector: &mut N)
219 where
220 N: Inspector + InspectorRouteTableExt<RoutesApiTableId<I, C>>,
221 {
222 self.core_ctx().with_rules_table(
223 |_core_ctx,
224 rule_table: &RulesTable<
225 _,
226 _,
227 C::BindingsContext,
228 >| {
229 for Rule {
230 matcher:
231 RuleMatcher { source_address_matcher, traffic_origin_matcher, mark_matchers },
232 action,
233 } in rule_table.iter()
234 {
235 inspector.record_unnamed_child(|inspector| {
236 inspector.record_child("Matchers", |inspector| {
237 if let Some(SubnetMatcher(subnet)) = source_address_matcher {
238 inspector.record_display("SourceAddressFrom", subnet);
239 }
240 if let Some(matcher) = traffic_origin_matcher {
241 match matcher {
242 TrafficOriginMatcher::NonLocal => {
243 inspector.record_str("TrafficOrigin", "NonLocal")
244 }
245 TrafficOriginMatcher::Local { bound_device_matcher } => inspector
246 .record_child("LocalOrigin", |inspector| {
247 if let Some(bound_device_matcher) = bound_device_matcher {
248 bound_device_matcher.record("BoundDevice", inspector);
249 }
250 }),
251 }
252 }
253 for (domain, matcher) in
254 mark_matchers.iter().filter_map(|(d, m)| m.map(|m| (d, m)))
255 {
256 matcher.record(domain.name(), inspector);
257 }
258 });
259 match action {
260 RuleAction::Unreachable => inspector.record_str("Action", "Unreachable"),
261 RuleAction::Lookup(table_id) => {
262 inspector.record_child("Action", |inspector| {
263 N::record_route_table(inspector, "Lookup", table_id)
264 });
265 }
266 }
267 })
268 }
269 })
270 }
271
272 pub fn inspect_routes<N>(&mut self, inspector: &mut N)
274 where
275 N: Inspector
276 + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
277 + InspectorRouteTableExt<RoutesApiTableId<I, C>>,
278 {
279 self.core_ctx().with_ip_routing_tables(|core_ctx, tables| {
280 for table_id in tables.keys() {
281 core_ctx.with_ip_routing_table(table_id, |_core_ctx, table| {
282 inspector.record_display_child(N::display_route_table(table_id), |inspector| {
283 for Entry { subnet, device, gateway, metric, route_preference } in
284 table.iter_table()
285 {
286 inspector.record_unnamed_child(|inspector| {
287 inspector.record_display("Destination", subnet);
288 N::record_device(inspector, "InterfaceId", device);
289 match gateway {
290 Some(gateway) => {
291 inspector.record_ip_addr("Gateway", gateway.get());
292 }
293 None => {
294 inspector.record_str("Gateway", "[NONE]");
295 }
296 }
297 let (metric, tracks_interface) = match metric {
298 Metric::MetricTracksInterface(metric) => (metric, true),
299 Metric::ExplicitMetric(metric) => (metric, false),
300 };
301 inspector.record_uint("Metric", *metric);
302 inspector.record_bool("MetricTracksInterface", tracks_interface);
303 inspector.record_debug("RoutePreference", route_preference);
304 });
305 }
306 })
307 })
308 }
309 });
310 }
311
312 pub fn set_routes(
314 &mut self,
315 table_id: &RoutesApiTableId<I, C>,
316 mut entries: Vec<
317 EntryAndGeneration<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
318 >,
319 ) {
320 entries.sort_unstable_by(|a, b| {
322 OrderedEntry::<'_, _, _>::from(a).cmp(&OrderedEntry::<'_, _, _>::from(b))
323 });
324 self.core_ctx().with_ip_routing_table_mut(table_id, |_core_ctx, table| {
325 table.table = entries;
326 });
327 }
328
329 pub fn set_rules(
331 &mut self,
332 rules: Vec<
333 Rule<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId, C::BindingsContext>,
334 >,
335 ) {
336 self.core_ctx().with_rules_table_mut(|_core_ctx, rule_table| {
337 rule_table.replace(rules);
338 })
339 }
340
341 #[cfg(feature = "testutils")]
343 pub fn list_table_ids(&mut self) -> Vec<RoutesApiTableId<I, C>> {
344 self.core_ctx().with_ip_routing_tables(|_ctx, tables| tables.keys().cloned().collect())
345 }
346}
347
348pub struct RoutesAnyApi<C>(C);
350
351impl<C> RoutesAnyApi<C> {
352 pub fn new(ctx: C) -> Self {
354 Self(ctx)
355 }
356}
357
358impl<C> RoutesAnyApi<C>
359where
360 C: ContextPair,
361 C::CoreContext: RoutesApiCoreContext<Ipv4, C::BindingsContext>
362 + RoutesApiCoreContext<Ipv6, C::BindingsContext>,
363 C::BindingsContext: RoutesApiBindingsContext<Ipv4, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
364 + RoutesApiBindingsContext<Ipv6, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
365 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId: Ord,
366{
367 fn ip<I: Ip>(&mut self) -> RoutesApi<I, &mut C> {
368 let Self(pair) = self;
369 RoutesApi::new(pair)
370 }
371
372 #[cfg(feature = "testutils")]
373 pub fn get_all_routes_in_main_table(
375 &mut self,
376 ) -> Vec<
377 crate::internal::types::EntryEither<
378 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
379 >,
380 > {
381 let mut vec = Vec::new();
382 self.ip::<Ipv4>().collect_main_table_routes_into(&mut vec);
383 self.ip::<Ipv6>().collect_main_table_routes_into(&mut vec);
384 vec
385 }
386
387 pub fn select_device_for_gateway(
389 &mut self,
390 gateway: SpecifiedAddr<IpAddr>,
391 ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId> {
392 match gateway.into() {
393 IpAddr::V4(gateway) => self.ip::<Ipv4>().select_device_for_gateway(gateway),
394 IpAddr::V6(gateway) => self.ip::<Ipv6>().select_device_for_gateway(gateway),
395 }
396 }
397}
398
399pub trait RoutesApiBindingsContext<I, D>:
402 IpDeviceBindingsContext<I, D> + IpLayerBindingsContext<I, D> + DeferredResourceRemovalContext
403where
404 D: StrongDeviceIdentifier,
405 I: IpLayerIpExt + IpDeviceIpExt,
406{
407}
408
409impl<I, D, BC> RoutesApiBindingsContext<I, D> for BC
410where
411 D: StrongDeviceIdentifier,
412 I: IpLayerIpExt + IpDeviceIpExt,
413 BC: IpDeviceBindingsContext<I, D>
414 + IpLayerBindingsContext<I, D>
415 + DeferredResourceRemovalContext,
416{
417}
418
419pub trait RoutesApiCoreContext<I, BC>:
422 IpLayerContext<I, BC> + IpDeviceConfigurationContext<I, BC>
423where
424 I: IpLayerIpExt + IpDeviceIpExt,
425 BC: IpDeviceBindingsContext<I, <Self as DeviceIdContext<AnyDevice>>::DeviceId>
426 + IpLayerBindingsContext<I, <Self as DeviceIdContext<AnyDevice>>::DeviceId>,
427{
428}
429
430impl<I, BC, CC> RoutesApiCoreContext<I, BC> for CC
431where
432 CC: IpLayerContext<I, BC> + IpDeviceConfigurationContext<I, BC>,
433 I: IpLayerIpExt + IpDeviceIpExt,
434 BC: IpDeviceBindingsContext<I, <Self as DeviceIdContext<AnyDevice>>::DeviceId>
435 + IpLayerBindingsContext<I, <Self as DeviceIdContext<AnyDevice>>::DeviceId>,
436{
437}