1use pest::Parser;
6use pest::iterators::Pair;
7
8use fidl_fuchsia_net as fnet;
9use fidl_fuchsia_net_filter_ext as filter_ext;
10use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
11use fidl_fuchsia_net_matchers_ext as fnet_matchers_ext;
12
13use crate::grammar::{Error, FilterRuleParser, InvalidReason, Rule};
14use crate::util;
15
16fn parse_action(pair: Pair<'_, Rule>) -> filter_ext::Action {
17 assert_eq!(pair.as_rule(), Rule::action);
18 match pair.into_inner().next().unwrap().as_rule() {
19 Rule::pass => filter_ext::Action::Accept,
20 Rule::drop => filter_ext::Action::Drop,
21 Rule::dropreset => todo!("not yet supported in the filter2 API"),
25 _ => unreachable!("action must be one of (pass|drop|dropreset)"),
26 }
27}
28
29#[derive(Copy, Clone, Debug, PartialEq)]
32pub enum Direction {
33 LocalIngress,
34 LocalEgress,
35}
36
37fn parse_direction(pair: Pair<'_, Rule>) -> Direction {
38 assert_eq!(pair.as_rule(), Rule::direction);
39 match pair.into_inner().next().unwrap().as_rule() {
40 Rule::incoming => Direction::LocalIngress,
41 Rule::outgoing => Direction::LocalEgress,
42 _ => unreachable!("direction must be one of (in|out)"),
43 }
44}
45
46enum TransportProtocol {
49 Tcp,
50 Udp,
51 Icmp,
52}
53
54fn parse_proto(pair: Pair<'_, Rule>) -> Option<TransportProtocol> {
55 assert_eq!(pair.as_rule(), Rule::proto);
56 pair.into_inner().next().map(|pair| match pair.as_rule() {
57 Rule::tcp => TransportProtocol::Tcp,
58 Rule::udp => TransportProtocol::Udp,
59 Rule::icmp => TransportProtocol::Icmp,
60 _ => unreachable!("protocol must be one of (tcp|udp|icmp)"),
61 })
62}
63
64fn parse_devclass(pair: Pair<'_, Rule>) -> Option<fnet_interfaces_ext::PortClass> {
65 assert_eq!(pair.as_rule(), Rule::devclass);
66 pair.into_inner().next().map(|pair| match pair.as_rule() {
67 Rule::virt => fnet_interfaces_ext::PortClass::Virtual,
68 Rule::ethernet => fnet_interfaces_ext::PortClass::Ethernet,
69 Rule::wlan => fnet_interfaces_ext::PortClass::WlanClient,
70 Rule::ppp => fnet_interfaces_ext::PortClass::Ppp,
71 Rule::bridge => fnet_interfaces_ext::PortClass::Bridge,
72 Rule::ap => fnet_interfaces_ext::PortClass::WlanAp,
73 Rule::lowpan => fnet_interfaces_ext::PortClass::Lowpan,
74 _ => unreachable!("devclass must be one of (virt|ethernet|wlan|ppp|bridge|ap|lowpan)"),
75 })
76}
77
78fn parse_src(
79 pair: Pair<'_, Rule>,
80) -> Result<(Option<fnet_matchers_ext::Address>, Option<fnet_matchers_ext::Port>), Error> {
81 assert_eq!(pair.as_rule(), Rule::src);
82 parse_src_or_dst(pair)
83}
84
85fn parse_dst(
86 pair: Pair<'_, Rule>,
87) -> Result<(Option<fnet_matchers_ext::Address>, Option<fnet_matchers_ext::Port>), Error> {
88 assert_eq!(pair.as_rule(), Rule::dst);
89 parse_src_or_dst(pair)
90}
91
92fn parse_src_or_dst(
93 pair: Pair<'_, Rule>,
94) -> Result<(Option<fnet_matchers_ext::Address>, Option<fnet_matchers_ext::Port>), Error> {
95 let mut inner = pair.into_inner();
96 match inner.next() {
97 Some(pair) => match pair.as_rule() {
98 Rule::invertible_subnet => {
99 let (subnet, invert_match) = util::parse_invertible_subnet(pair)?;
100 let port = match inner.next() {
101 Some(pair) => Some(parse_port_range(pair)?),
102 None => None,
103 };
104 Ok((
105 Some(fnet_matchers_ext::Address {
106 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
107 fnet_matchers_ext::Subnet::try_from(subnet)
108 .map_err(|err| Error::Fidl(err.into()))?,
109 ),
110 invert: invert_match,
111 }),
112 port,
113 ))
114 }
115 Rule::port_range => Ok((None, Some(parse_port_range(pair)?))),
116 _ => unreachable!("src or dst must be either an invertible subnet or port range"),
117 },
118 None => Ok((None, None)),
119 }
120}
121
122fn parse_port_range(pair: Pair<'_, Rule>) -> Result<fnet_matchers_ext::Port, Error> {
123 assert_eq!(pair.as_rule(), Rule::port_range);
124 let mut inner = pair.into_inner();
125 let pair = inner.next().unwrap();
126 match pair.as_rule() {
127 Rule::port => {
128 let port_num = util::parse_port_num(inner.next().unwrap())?;
129 fnet_matchers_ext::Port::new(port_num, port_num, false).map_err(|err| match err {
130 fnet_matchers_ext::PortError::InvalidPortRange => {
131 Error::Invalid(InvalidReason::InvalidPortRange)
132 }
133 })
134 }
135 Rule::range => {
136 let port_start = util::parse_port_num(inner.next().unwrap())?;
137 let port_end = util::parse_port_num(inner.next().unwrap())?;
138 fnet_matchers_ext::Port::new(port_start, port_end, false).map_err(|err| match err {
139 fnet_matchers_ext::PortError::InvalidPortRange => {
140 Error::Invalid(InvalidReason::InvalidPortRange)
141 }
142 })
143 }
144 _ => unreachable!("port range must be either a single port, or a port range"),
145 }
146}
147
148fn parse_rule(
149 pair: Pair<'_, Rule>,
150 routines: &FilterRoutines,
151 index: usize,
152) -> Result<filter_ext::Rule, Error> {
153 assert_eq!(pair.as_rule(), Rule::rule);
154 let mut pairs = pair.into_inner();
155
156 let action = parse_action(pairs.next().unwrap());
157 let direction = parse_direction(pairs.next().unwrap());
158 let proto = parse_proto(pairs.next().unwrap());
159 let port_class = parse_devclass(pairs.next().unwrap());
160 let mut in_interface = None;
161 let mut out_interface = None;
162 let routine_id = match direction {
163 Direction::LocalIngress => {
164 let Some(ref routine_id) = routines.local_ingress else {
166 return Err(Error::RoutineNotProvided(direction));
167 };
168 in_interface = port_class.map(|class| fnet_matchers_ext::Interface::PortClass(class));
169 routine_id
170 }
171 Direction::LocalEgress => {
172 let Some(ref routine_id) = routines.local_egress else {
174 return Err(Error::RoutineNotProvided(direction));
175 };
176 out_interface = port_class.map(|class| fnet_matchers_ext::Interface::PortClass(class));
177 routine_id
178 }
179 };
180 let (src_addr, src_port) = parse_src(pairs.next().unwrap())?;
181 let (dst_addr, dst_port) = parse_dst(pairs.next().unwrap())?;
182 let transport_protocol = proto.map(|proto| match proto {
183 TransportProtocol::Tcp => fnet_matchers_ext::TransportProtocol::Tcp { src_port, dst_port },
184 TransportProtocol::Udp => fnet_matchers_ext::TransportProtocol::Udp { src_port, dst_port },
185 TransportProtocol::Icmp => fnet_matchers_ext::TransportProtocol::Icmp,
186 });
187
188 Ok(filter_ext::Rule {
189 id: filter_ext::RuleId { routine: routine_id.clone(), index: index as u32 },
190 matchers: filter_ext::Matchers {
191 in_interface,
192 out_interface,
193 src_addr,
194 dst_addr,
195 transport_protocol,
196 ..Default::default()
197 },
198 action,
199 })
200}
201
202#[derive(Debug, Default)]
206pub struct FilterRoutines {
207 pub local_ingress: Option<filter_ext::RoutineId>,
208 pub local_egress: Option<filter_ext::RoutineId>,
209}
210
211#[derive(Debug, Default)]
215pub struct NatRoutines {}
216
217fn validate_rule(rule: &filter_ext::Rule) -> Result<(), Error> {
218 if let (Some(src_subnet), Some(dst_subnet)) = (&rule.matchers.src_addr, &rule.matchers.dst_addr)
219 {
220 if let (
221 fnet_matchers_ext::AddressMatcherType::Subnet(src_subnet),
222 fnet_matchers_ext::AddressMatcherType::Subnet(dst_subnet),
223 ) = (&src_subnet.matcher, &dst_subnet.matcher)
224 {
225 if !util::ip_version_eq(
226 &fnet::Subnet::from(*src_subnet).addr,
227 &fnet::Subnet::from(*dst_subnet).addr,
228 ) {
229 return Err(Error::Invalid(InvalidReason::MixedIPVersions));
230 }
231 }
232 }
233
234 Ok(())
235}
236
237pub fn parse_str_to_rules(
238 line: &str,
239 routines: &FilterRoutines,
240) -> Result<Vec<filter_ext::Rule>, Error> {
241 let mut pairs =
242 FilterRuleParser::parse(Rule::rules, &line).map_err(|err| Error::Pest(Box::new(err)))?;
243 let mut rules = Vec::new();
244 for (index, filter_rule) in pairs.next().unwrap().into_inner().into_iter().enumerate() {
245 match filter_rule.as_rule() {
246 Rule::rule => {
247 let rule = parse_rule(filter_rule, &routines, index)?;
248 validate_rule(&rule)?;
249 rules.push(rule);
250 }
251 Rule::EOI => (),
252 _ => unreachable!("rule must only have a rule case"),
253 }
254 }
255 Ok(rules)
256}
257
258pub fn parse_str_to_nat_rules(
259 _line: &str,
260 _routines: &NatRoutines,
261) -> Result<Vec<filter_ext::Rule>, Error> {
262 todo!("not yet supported in the filter2 API")
265}
266
267pub fn parse_str_to_rdr_rules(
268 _line: &str,
269 _routines: &NatRoutines,
270) -> Result<Vec<filter_ext::Rule>, Error> {
271 todo!("not yet supported in the filter2 API")
274}
275
276#[cfg(test)]
277mod test {
278 use super::*;
279
280 use net_declare::fidl_subnet;
281
282 fn test_filter_routines() -> FilterRoutines {
283 FilterRoutines {
284 local_ingress: Some(local_ingress_routine()),
285 local_egress: Some(local_egress_routine()),
286 }
287 }
288
289 fn local_ingress_routine() -> filter_ext::RoutineId {
290 test_routine_id("local_ingress")
291 }
292
293 fn local_egress_routine() -> filter_ext::RoutineId {
294 test_routine_id("local_egress")
295 }
296
297 fn test_routine_id(name: &str) -> filter_ext::RoutineId {
298 filter_ext::RoutineId {
299 namespace: filter_ext::NamespaceId(String::from("namespace")),
300 name: String::from(name),
301 }
302 }
303
304 #[test]
305 fn test_rule_with_proto_any() {
306 assert_eq!(
307 parse_str_to_rules("pass in;", &test_filter_routines()),
308 Ok(vec![filter_ext::Rule {
309 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
310 matchers: filter_ext::Matchers::default(),
311 action: filter_ext::Action::Accept,
312 }])
313 );
314 }
315
316 #[test]
317 fn test_rule_local_ingress_without_corresponding_routine() {
318 assert_eq!(
319 parse_str_to_rules("pass in;", &FilterRoutines::default()),
320 Err(Error::RoutineNotProvided(Direction::LocalIngress))
321 );
322 }
323
324 #[test]
325 fn test_rule_local_egress_without_corresponding_routine() {
326 assert_eq!(
327 parse_str_to_rules("pass out;", &FilterRoutines::default()),
328 Err(Error::RoutineNotProvided(Direction::LocalEgress))
329 );
330 }
331
332 #[test]
333 fn test_rule_with_proto_tcp() {
334 assert_eq!(
335 parse_str_to_rules("pass in proto tcp;", &test_filter_routines()),
336 Ok(vec![filter_ext::Rule {
337 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
338 matchers: filter_ext::Matchers {
339 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
340 src_port: None,
341 dst_port: None,
342 }),
343 ..Default::default()
344 },
345 action: filter_ext::Action::Accept,
346 }])
347 )
348 }
349
350 #[test]
351 fn test_multiple_rules() {
352 assert_eq!(
353 parse_str_to_rules("pass in proto tcp; drop out proto udp;", &test_filter_routines()),
354 Ok(vec![
355 filter_ext::Rule {
356 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
357 matchers: filter_ext::Matchers {
358 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
359 src_port: None,
360 dst_port: None,
361 }),
362 ..Default::default()
363 },
364 action: filter_ext::Action::Accept,
365 },
366 filter_ext::Rule {
367 id: filter_ext::RuleId { routine: local_egress_routine(), index: 1 },
368 matchers: filter_ext::Matchers {
369 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Udp {
370 src_port: None,
371 dst_port: None,
372 }),
373 ..Default::default()
374 },
375 action: filter_ext::Action::Drop,
376 }
377 ])
378 )
379 }
380
381 #[test]
382 fn test_rule_with_from_v4_address() {
383 assert_eq!(
384 parse_str_to_rules("pass in proto tcp from 1.2.3.0/24;", &test_filter_routines()),
385 Ok(vec![
386 filter_ext::Rule {
387 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
388 matchers: filter_ext::Matchers {
389 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
390 src_port: None,
391 dst_port: None,
392 }),
393 src_addr: Some(fnet_matchers_ext::Address {
394 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
395 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1.2.3.0/24"))
396 .unwrap()
397 ),
398 invert: false,
399 }),
400 ..Default::default()
401 },
402 action: filter_ext::Action::Accept,
403 }
404 .into()
405 ])
406 )
407 }
408
409 #[test]
410 fn test_rule_with_from_port() {
411 assert_eq!(
412 parse_str_to_rules("pass in proto tcp from port 10000;", &test_filter_routines()),
413 Ok(vec![
414 filter_ext::Rule {
415 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
416 matchers: filter_ext::Matchers {
417 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
418 src_port: Some(
419 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
420 ),
421 dst_port: None,
422 }),
423 ..Default::default()
424 },
425 action: filter_ext::Action::Accept,
426 }
427 .into()
428 ])
429 )
430 }
431
432 #[test]
433 fn test_rule_with_from_range() {
434 assert_eq!(
435 parse_str_to_rules(
436 "pass in proto tcp from range 10000:10010;",
437 &test_filter_routines()
438 ),
439 Ok(vec![
440 filter_ext::Rule {
441 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
442 matchers: filter_ext::Matchers {
443 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
444 src_port: Some(
445 fnet_matchers_ext::Port::new(10000, 10010, false).unwrap()
446 ),
447 dst_port: None,
448 }),
449 ..Default::default()
450 },
451 action: filter_ext::Action::Accept,
452 }
453 .into()
454 ])
455 )
456 }
457
458 #[test]
459 fn test_rule_with_from_invalid_range() {
460 assert_eq!(
461 parse_str_to_rules(
462 "pass in proto tcp from range 10005:10000;",
463 &test_filter_routines()
464 ),
465 Err(Error::Invalid(InvalidReason::InvalidPortRange))
466 );
467 }
468
469 #[test]
470 fn test_rule_with_from_v4_address_port() {
471 assert_eq!(
472 parse_str_to_rules(
473 "pass in proto tcp from 1.2.3.0/24 port 10000;",
474 &test_filter_routines()
475 ),
476 Ok(vec![
477 filter_ext::Rule {
478 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
479 matchers: filter_ext::Matchers {
480 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
481 src_port: Some(
482 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
483 ),
484 dst_port: None,
485 }),
486 src_addr: Some(fnet_matchers_ext::Address {
487 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
488 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1.2.3.0/24"))
489 .unwrap()
490 ),
491 invert: false,
492 }),
493 ..Default::default()
494 },
495 action: filter_ext::Action::Accept,
496 }
497 .into()
498 ])
499 )
500 }
501
502 #[test]
503 fn test_rule_with_from_not_v4_address_port() {
504 assert_eq!(
505 parse_str_to_rules(
506 "pass in proto tcp from !1.2.3.0/24 port 10000;",
507 &test_filter_routines()
508 ),
509 Ok(vec![
510 filter_ext::Rule {
511 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
512 matchers: filter_ext::Matchers {
513 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
514 src_port: Some(
515 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
516 ),
517 dst_port: None,
518 }),
519 src_addr: Some(fnet_matchers_ext::Address {
520 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
521 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1.2.3.0/24"))
522 .unwrap()
523 ),
524 invert: true,
525 }),
526 ..Default::default()
527 },
528 action: filter_ext::Action::Accept,
529 }
530 .into()
531 ])
532 )
533 }
534
535 #[test]
536 fn test_rule_with_from_v6_address_port() {
537 assert_eq!(
538 parse_str_to_rules(
539 "pass in proto tcp from 1234:5678::/32 port 10000;",
540 &test_filter_routines()
541 ),
542 Ok(vec![
543 filter_ext::Rule {
544 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
545 matchers: filter_ext::Matchers {
546 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
547 src_port: Some(
548 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
549 ),
550 dst_port: None,
551 }),
552 src_addr: Some(fnet_matchers_ext::Address {
553 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
554 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1234:5678::/32"))
555 .unwrap()
556 ),
557 invert: false,
558 }),
559 ..Default::default()
560 },
561 action: filter_ext::Action::Accept,
562 }
563 .into()
564 ])
565 )
566 }
567
568 #[test]
569 fn test_rule_with_to_v6_address_port() {
570 assert_eq!(
571 parse_str_to_rules(
572 "pass in proto tcp to 1234:5678::/32 port 10000;",
573 &test_filter_routines()
574 ),
575 Ok(vec![
576 filter_ext::Rule {
577 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
578 matchers: filter_ext::Matchers {
579 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
580 src_port: None,
581 dst_port: Some(
582 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
583 ),
584 }),
585 dst_addr: Some(fnet_matchers_ext::Address {
586 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
587 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1234:5678::/32"))
588 .unwrap()
589 ),
590 invert: false,
591 }),
592 ..Default::default()
593 },
594 action: filter_ext::Action::Accept,
595 }
596 .into()
597 ])
598 )
599 }
600
601 #[test]
602 fn test_rule_with_from_v6_address_port_to_v4_address_port() {
603 assert_eq!(
604 parse_str_to_rules(
605 "pass in proto tcp from 1234:5678::/32 port 10000 to 1.2.3.0/24 port 1000;",
606 &test_filter_routines()
607 ),
608 Err(Error::Invalid(InvalidReason::MixedIPVersions))
609 );
610 }
611
612 #[test]
613 fn test_rule_with_from_v6_address_port_to_v6_address_port() {
614 assert_eq!(
615 parse_str_to_rules(
616 "pass in proto tcp from 1234:5678::/32 port 10000 to 2345:6789::/32 port 1000;",
617 &test_filter_routines()
618 ),
619 Ok(vec![
620 filter_ext::Rule {
621 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
622 matchers: filter_ext::Matchers {
623 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
624 src_port: Some(
625 fnet_matchers_ext::Port::new(10000, 10000, false).unwrap()
626 ),
627 dst_port: Some(
628 fnet_matchers_ext::Port::new(1000, 1000, false).unwrap()
629 ),
630 }),
631 src_addr: Some(fnet_matchers_ext::Address {
632 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
633 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("1234:5678::/32"))
634 .unwrap()
635 ),
636 invert: false,
637 }),
638 dst_addr: Some(fnet_matchers_ext::Address {
639 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
640 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("2345:6789::/32"))
641 .unwrap()
642 ),
643 invert: false,
644 }),
645 ..Default::default()
646 },
647 action: filter_ext::Action::Accept,
648 }
649 .into()
650 ])
651 )
652 }
653
654 #[test]
655 fn test_rule_with_port_class() {
656 assert_eq!(
657 parse_str_to_rules("pass in proto tcp devclass ap;", &test_filter_routines()),
658 Ok(vec![filter_ext::Rule {
659 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
660 matchers: filter_ext::Matchers {
661 in_interface: Some(fnet_matchers_ext::Interface::PortClass(
662 fnet_interfaces_ext::PortClass::WlanAp,
663 )),
664 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
665 src_port: None,
666 dst_port: None,
667 }),
668 ..Default::default()
669 },
670 action: filter_ext::Action::Accept,
671 }])
672 )
673 }
674
675 #[test]
676 fn test_rule_with_port_class_and_dst_range() {
677 assert_eq!(
678 parse_str_to_rules(
679 "pass in proto tcp devclass ap to range 1:2;",
680 &test_filter_routines()
681 ),
682 Ok(vec![
683 filter_ext::Rule {
684 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
685 matchers: filter_ext::Matchers {
686 in_interface: Some(fnet_matchers_ext::Interface::PortClass(
687 fnet_interfaces_ext::PortClass::WlanAp
688 )),
689 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
690 src_port: None,
691 dst_port: Some(fnet_matchers_ext::Port::new(1, 2, false).unwrap()),
692 }),
693 ..Default::default()
694 },
695 action: filter_ext::Action::Accept,
696 }
697 .into()
698 ])
699 )
700 }
701
702 #[test]
706 fn test_rule_with_unused_fields() {
707 assert_eq!(
708 parse_str_to_rules("pass in proto tcp log no state;", &test_filter_routines()),
709 Ok(vec![filter_ext::Rule {
710 id: filter_ext::RuleId { routine: local_ingress_routine(), index: 0 },
711 matchers: filter_ext::Matchers {
712 transport_protocol: Some(fnet_matchers_ext::TransportProtocol::Tcp {
713 src_port: None,
714 dst_port: None,
715 }),
716 ..Default::default()
717 },
718 action: filter_ext::Action::Accept,
719 }])
720 )
721 }
722}