1pub mod validation;
6
7use alloc::format;
8use alloc::string::ToString as _;
9use alloc::sync::Arc;
10use alloc::vec::Vec;
11use core::fmt::Debug;
12use core::hash::{Hash, Hasher};
13use core::num::NonZeroU16;
14use core::ops::RangeInclusive;
15use netstack3_base::socket::SocketCookie;
16
17use derivative::Derivative;
18use net_types::ip::{GenericOverIp, Ip};
19use netstack3_base::{
20 CoreTimerContext, Inspectable, InspectableValue, Inspector as _, MarkDomain, Marks,
21 MatcherBindingsTypes, TimerContext,
22};
23use packet_formats::ip::IpExt;
24
25use crate::actions::MarkAction;
26use crate::conntrack::{self, ConnectionDirection};
27use crate::context::FilterBindingsTypes;
28use crate::logic::FilterTimerId;
29use crate::logic::nat::NatConfig;
30use crate::matchers::PacketMatcher;
31use crate::state::validation::ValidRoutines;
32
33#[derive(Derivative)]
35#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
36pub enum Action<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
37 Accept,
44 Drop,
50 Jump(UninstalledRoutine<I, BT, RuleInfo>),
52 Return,
59 TransparentProxy(TransparentProxy<I>),
76 Redirect {
94 dst_port: Option<RangeInclusive<NonZeroU16>>,
100 },
101 Masquerade {
115 src_port: Option<RangeInclusive<NonZeroU16>>,
125 },
126 Mark {
138 domain: MarkDomain,
140 action: MarkAction,
142 },
143
144 None,
146}
147
148#[derive(Debug, Clone)]
159#[allow(missing_docs)]
160pub enum TransparentProxy<I: IpExt> {
161 LocalAddr(I::Addr),
162 LocalPort(NonZeroU16),
163 LocalAddrAndPort(I::Addr, NonZeroU16),
164}
165
166impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Inspectable for Action<I, BT, RuleInfo> {
167 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
168 let value = match self {
169 Self::Accept
170 | Self::Drop
171 | Self::Return
172 | Self::TransparentProxy(_)
173 | Self::Redirect { .. }
174 | Self::Masquerade { .. }
175 | Self::Mark { .. }
176 | Self::None => {
177 format!("{self:?}")
178 }
179 Self::Jump(UninstalledRoutine { routine: _, id }) => {
180 format!("Jump(UninstalledRoutine({id:?}))")
181 }
182 };
183 inspector.record_string("action", value);
184 }
185}
186
187#[derive(Derivative)]
190#[derivative(Clone(bound = ""), Debug(bound = ""))]
191pub struct UninstalledRoutine<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
192 pub(crate) routine: Arc<Routine<I, BT, RuleInfo>>,
193 id: usize,
194}
195
196impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> UninstalledRoutine<I, BT, RuleInfo> {
197 pub fn new(rules: Vec<Rule<I, BT, RuleInfo>>, id: usize) -> Self {
199 Self { routine: Arc::new(Routine { rules }), id }
200 }
201
202 pub fn get(&self) -> &Routine<I, BT, RuleInfo> {
204 &*self.routine
205 }
206}
207
208impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> PartialEq
209 for UninstalledRoutine<I, BT, RuleInfo>
210{
211 fn eq(&self, other: &Self) -> bool {
212 let Self { routine: lhs, id: _ } = self;
213 let Self { routine: rhs, id: _ } = other;
214 Arc::ptr_eq(lhs, rhs)
215 }
216}
217
218impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Eq for UninstalledRoutine<I, BT, RuleInfo> {}
219
220impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Hash for UninstalledRoutine<I, BT, RuleInfo> {
221 fn hash<H: Hasher>(&self, state: &mut H) {
222 let Self { routine, id: _ } = self;
223 Arc::as_ptr(routine).hash(state)
224 }
225}
226
227impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for UninstalledRoutine<I, BT, ()> {
228 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
229 let Self { routine, id } = self;
230 inspector.record_child(&id.to_string(), |inspector| {
231 inspector.delegate_inspectable(&**routine);
232 });
233 }
234}
235
236#[derive(Derivative, GenericOverIp)]
239#[generic_over_ip(I, Ip)]
240#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
241pub struct Rule<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
242 pub matcher: PacketMatcher<I, BT>,
244 pub action: Action<I, BT, RuleInfo>,
246 #[derivative(Debug = "ignore")]
252 pub validation_info: RuleInfo,
253}
254
255impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Rule<I, BT, ()> {
256 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
257 let Self { matcher, action, validation_info: () } = self;
258 inspector.record_child("matchers", |inspector| {
259 let PacketMatcher {
260 in_interface,
261 out_interface,
262 src_address,
263 dst_address,
264 transport_protocol,
265 external_matcher,
266 } = matcher;
267
268 fn record_matcher<Inspector: netstack3_base::Inspector, M: InspectableValue>(
269 inspector: &mut Inspector,
270 name: &str,
271 matcher: &Option<M>,
272 ) {
273 if let Some(matcher) = matcher {
274 inspector.record_inspectable_value(name, matcher);
275 }
276 }
277
278 record_matcher(inspector, "in_interface", in_interface);
279 record_matcher(inspector, "out_interface", out_interface);
280 record_matcher(inspector, "src_address", src_address);
281 record_matcher(inspector, "dst_address", dst_address);
282 record_matcher(inspector, "transport_protocol", transport_protocol);
283 record_matcher(inspector, "external_matcher", external_matcher);
284 });
285 inspector.delegate_inspectable(action);
286 }
287}
288
289#[derive(Derivative, GenericOverIp)]
291#[generic_over_ip(I, Ip)]
292#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
293pub struct Routine<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
294 pub rules: Vec<Rule<I, BT, RuleInfo>>,
296}
297
298impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Routine<I, BT, ()> {
299 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
300 let Self { rules } = self;
301 inspector.record_usize("rules", rules.len());
302 for rule in rules {
303 inspector.record_unnamed_child(|inspector| inspector.delegate_inspectable(rule));
304 }
305 }
306}
307
308#[derive(Derivative, GenericOverIp)]
311#[generic_over_ip(I, Ip)]
312#[derivative(Default(bound = ""), Debug(bound = ""))]
313pub struct Hook<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
314 pub routines: Vec<Routine<I, BT, RuleInfo>>,
316}
317
318impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Hook<I, BT, ()> {
319 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
320 let Self { routines } = self;
321 inspector.record_usize("routines", routines.len());
322 for routine in routines {
323 inspector.record_unnamed_child(|inspector| {
324 inspector.delegate_inspectable(routine);
325 });
326 }
327 }
328}
329
330#[derive(Derivative)]
332#[derivative(Default(bound = ""), Debug(bound = ""))]
333pub struct IpRoutines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
334 pub ingress: Hook<I, BT, RuleInfo>,
336 pub local_ingress: Hook<I, BT, RuleInfo>,
338 pub forwarding: Hook<I, BT, RuleInfo>,
340 pub local_egress: Hook<I, BT, RuleInfo>,
343 pub egress: Hook<I, BT, RuleInfo>,
345}
346
347impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for IpRoutines<I, BT, ()> {
348 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
349 let Self { ingress, local_ingress, forwarding, local_egress, egress } = self;
350
351 inspector.record_child("ingress", |inspector| inspector.delegate_inspectable(ingress));
352 inspector.record_child("local_ingress", |inspector| {
353 inspector.delegate_inspectable(local_ingress)
354 });
355 inspector
356 .record_child("forwarding", |inspector| inspector.delegate_inspectable(forwarding));
357 inspector
358 .record_child("local_egress", |inspector| inspector.delegate_inspectable(local_egress));
359 inspector.record_child("egress", |inspector| inspector.delegate_inspectable(egress));
360 }
361}
362
363#[derive(Derivative)]
368#[derivative(Default(bound = ""), Debug(bound = ""))]
369pub struct NatRoutines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
370 pub ingress: Hook<I, BT, RuleInfo>,
372 pub local_ingress: Hook<I, BT, RuleInfo>,
374 pub local_egress: Hook<I, BT, RuleInfo>,
377 pub egress: Hook<I, BT, RuleInfo>,
379}
380
381impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> NatRoutines<I, BT, RuleInfo> {
382 pub(crate) fn contains_rules(&self) -> bool {
383 let Self { ingress, local_ingress, local_egress, egress } = self;
384
385 let hook_contains_rules =
386 |hook: &Hook<_, _, _>| hook.routines.iter().any(|routine| !routine.rules.is_empty());
387 hook_contains_rules(&ingress)
388 || hook_contains_rules(&local_ingress)
389 || hook_contains_rules(&local_egress)
390 || hook_contains_rules(&egress)
391 }
392}
393
394impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for NatRoutines<I, BT, ()> {
395 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
396 let Self { ingress, local_ingress, local_egress, egress } = self;
397
398 inspector.record_child("ingress", |inspector| inspector.delegate_inspectable(ingress));
399 inspector.record_child("local_ingress", |inspector| {
400 inspector.delegate_inspectable(local_ingress)
401 });
402 inspector
403 .record_child("local_egress", |inspector| inspector.delegate_inspectable(local_egress));
404 inspector.record_child("egress", |inspector| inspector.delegate_inspectable(egress));
405 }
406}
407
408#[derive(Derivative, GenericOverIp)]
410#[generic_over_ip(I, Ip)]
411#[derivative(Default(bound = ""), Debug(bound = ""))]
412pub struct Routines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
413 pub ip: IpRoutines<I, BT, RuleInfo>,
415 pub nat: NatRoutines<I, BT, RuleInfo>,
417}
418
419#[derive(Default)]
423pub struct OneWayBoolean(bool);
424
425impl OneWayBoolean {
426 pub const TRUE: Self = Self(true);
428
429 pub fn get(&self) -> bool {
431 let Self(inner) = self;
432 *inner
433 }
434
435 pub fn set(&mut self) {
441 let Self(inner) = self;
442 *inner = true;
443 }
444}
445
446pub struct State<I: IpExt, A, BT: FilterBindingsTypes> {
448 pub installed_routines: ValidRoutines<I, BT>,
450 pub(crate) uninstalled_routines: Vec<UninstalledRoutine<I, BT, ()>>,
456 pub conntrack: conntrack::Table<I, NatConfig<I, A>, BT>,
458 pub nat_installed: OneWayBoolean,
473}
474
475impl<I, A, BC> State<I, A, BC>
476where
477 I: IpExt,
478 BC: FilterBindingsTypes + TimerContext,
479{
480 pub fn new<CC: CoreTimerContext<FilterTimerId<I>, BC>>(bindings_ctx: &mut BC) -> Self {
482 Self {
483 installed_routines: Default::default(),
484 uninstalled_routines: Default::default(),
485 conntrack: conntrack::Table::new::<CC>(bindings_ctx),
486 nat_installed: OneWayBoolean::default(),
487 }
488 }
489}
490
491impl<I: IpExt, A: InspectableValue, BT: FilterBindingsTypes> Inspectable for State<I, A, BT> {
492 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
493 let Self { installed_routines, uninstalled_routines, conntrack, nat_installed: _ } = self;
494 let Routines { ip, nat } = installed_routines.get();
495
496 inspector.record_child("IP", |inspector| inspector.delegate_inspectable(ip));
497 inspector.record_child("NAT", |inspector| inspector.delegate_inspectable(nat));
498 inspector.record_child("uninstalled", |inspector| {
499 inspector.record_usize("routines", uninstalled_routines.len());
500 for routine in uninstalled_routines {
501 inspector.delegate_inspectable(routine);
502 }
503 });
504
505 inspector.record_child("conntrack", |inspector| {
506 inspector.delegate_inspectable(conntrack);
507 });
508 }
509}
510
511pub trait FilterIpMetadata<I: IpExt, A, BT: FilterBindingsTypes>: FilterPacketMetadata {
514 fn take_connection_and_direction(
516 &mut self,
517 ) -> Option<(conntrack::Connection<I, NatConfig<I, A>, BT>, ConnectionDirection)>;
518
519 fn replace_connection_and_direction(
522 &mut self,
523 conn: conntrack::Connection<I, NatConfig<I, A>, BT>,
524 direction: ConnectionDirection,
525 ) -> Option<conntrack::Connection<I, NatConfig<I, A>, BT>>;
526}
527
528pub trait FilterPacketMetadata {
535 fn apply_mark_action(&mut self, domain: MarkDomain, action: MarkAction);
537 fn cookie(&self) -> Option<SocketCookie>;
539 fn marks(&self) -> &Marks;
541}
542
543#[derive(Default)]
546pub struct FakePacketMetadata {
547 marks: Marks,
548}
549
550impl<I: IpExt, A, BT: FilterBindingsTypes> FilterIpMetadata<I, A, BT> for FakePacketMetadata {
551 fn take_connection_and_direction(
552 &mut self,
553 ) -> Option<(conntrack::Connection<I, NatConfig<I, A>, BT>, ConnectionDirection)> {
554 None
555 }
556
557 fn replace_connection_and_direction(
558 &mut self,
559 _conn: conntrack::Connection<I, NatConfig<I, A>, BT>,
560 _direction: ConnectionDirection,
561 ) -> Option<conntrack::Connection<I, NatConfig<I, A>, BT>> {
562 None
563 }
564}
565
566impl FilterPacketMetadata for FakePacketMetadata {
567 fn apply_mark_action(&mut self, _domain: MarkDomain, _action: MarkAction) {}
568
569 fn cookie(&self) -> Option<SocketCookie> {
570 None
571 }
572
573 fn marks(&self) -> &Marks {
574 &self.marks
575 }
576}