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