@@ -447,6 +447,220 @@ mod tests {
447447 assert_eq ! ( packets[ 0 ] . get_done( ) , Some ( DoneReason :: Filtered ) ) ;
448448 }
449449
450+ #[ test]
451+ fn test_flow_filter_multiple_matches_no_dst_vpcd ( ) {
452+ // Setup table with overlapping destination prefixes from different VPCs
453+ let mut table = FlowFilterTable :: new ( ) ;
454+ let src_vpcd = vpcd ( 100 ) ;
455+
456+ // Manually set up a scenario where dst_vpcd lookup returns MultipleMatches
457+ // This happens when the same destination can be reached from multiple VPCs
458+ table
459+ . insert (
460+ src_vpcd,
461+ VpcdLookupResult :: MultipleMatches ,
462+ Prefix :: from ( "10.0.0.0/24" ) ,
463+ None ,
464+ Prefix :: from ( "20.0.0.0/24" ) ,
465+ None ,
466+ )
467+ . unwrap ( ) ;
468+
469+ let mut writer = FlowFilterTableWriter :: new ( ) ;
470+ writer. update_flow_filter_table ( table) ;
471+
472+ let mut flow_filter = FlowFilter :: new ( "test-filter" , writer. get_reader ( ) ) ;
473+
474+ // Create test packet
475+ let packet = create_test_packet (
476+ Some ( vpcd ( 100 ) ) ,
477+ "10.0.0.5" . parse ( ) . unwrap ( ) ,
478+ "20.0.0.10" . parse ( ) . unwrap ( ) ,
479+ ) ;
480+
481+ let packets = flow_filter
482+ . process ( [ packet] . into_iter ( ) )
483+ . collect :: < Vec < _ > > ( ) ;
484+
485+ assert_eq ! ( packets. len( ) , 1 ) ;
486+ // Without table flow lookup we can't find the right dst_vpcd, so we should drop the packet
487+ assert ! ( packets[ 0 ] . is_done( ) ) ;
488+ assert ! ( packets[ 0 ] . meta( ) . dst_vpcd. is_none( ) ) ;
489+ }
490+
491+ #[ test]
492+ fn test_flow_filter_table_overlap_cases ( ) {
493+ let vni1 = Vni :: new_checked ( 100 ) . unwrap ( ) ;
494+ let vni2 = Vni :: new_checked ( 200 ) . unwrap ( ) ;
495+ let vni3 = Vni :: new_checked ( 300 ) . unwrap ( ) ;
496+
497+ let mut vpc_table = VpcTable :: new ( ) ;
498+ vpc_table
499+ . add ( Vpc :: new ( "vpc1" , "VPC01" , vni1. as_u32 ( ) ) . unwrap ( ) )
500+ . unwrap ( ) ;
501+ vpc_table
502+ . add ( Vpc :: new ( "vpc2" , "VPC02" , vni2. as_u32 ( ) ) . unwrap ( ) )
503+ . unwrap ( ) ;
504+ vpc_table
505+ . add ( Vpc :: new ( "vpc3" , "VPC03" , vni3. as_u32 ( ) ) . unwrap ( ) )
506+ . unwrap ( ) ;
507+
508+ // - vpc1-to-vpc2:
509+ // VPC01:
510+ // prefixes:
511+ // - 1.0.0.0/24
512+ // VPC02:
513+ // prefixes:
514+ // - 5.0.0.0/24
515+ //
516+ // - vpc2-to-vpc3:
517+ // VPC02:
518+ // prefixes:
519+ // - 5.0.0.0/24
520+ // - 6.0.0.0/24
521+ // VPC03:
522+ // prefixes:
523+ // - 1.0.0.64/26 // 1.0.0.64 to 1.0.0.127
524+ let mut peering_table = VpcPeeringTable :: new ( ) ;
525+ peering_table
526+ . add ( VpcPeering :: new (
527+ "vpc1-to-vpc2" ,
528+ VpcManifest {
529+ name : "vpc1" . to_string ( ) ,
530+ exposes : vec ! [ VpcExpose :: empty( ) . ip( "1.0.0.0/24" . into( ) ) ] ,
531+ } ,
532+ VpcManifest {
533+ name : "vpc2" . to_string ( ) ,
534+ exposes : vec ! [ VpcExpose :: empty( ) . ip( "5.0.0.0/24" . into( ) ) ] ,
535+ } ,
536+ None ,
537+ ) )
538+ . unwrap ( ) ;
539+
540+ peering_table
541+ . add ( VpcPeering :: new (
542+ "vpc2-to-vpc3" ,
543+ VpcManifest {
544+ name : "vpc2" . to_string ( ) ,
545+ exposes : vec ! [
546+ VpcExpose :: empty( ) . ip( "5.0.0.0/24" . into( ) ) ,
547+ VpcExpose :: empty( ) . ip( "6.0.0.0/24" . into( ) ) ,
548+ ] ,
549+ } ,
550+ VpcManifest {
551+ name : "vpc3" . to_string ( ) ,
552+ exposes : vec ! [ VpcExpose :: empty( ) . ip( "1.0.0.64/26" . into( ) ) ] ,
553+ } ,
554+ None ,
555+ ) )
556+ . unwrap ( ) ;
557+
558+ let mut overlay = Overlay :: new ( vpc_table, peering_table) ;
559+ // Build overlay.vpc_table's peerings from peering_table, with no validation.
560+ // We don't validate because overlapping prefixes actually make the config invalid; but it
561+ // doesn't matter for the test.
562+ overlay. collect_peerings ( ) ;
563+
564+ let table = FlowFilterTable :: build_from_overlay ( & overlay) . unwrap ( ) ;
565+
566+ let mut writer = FlowFilterTableWriter :: new ( ) ;
567+ writer. update_flow_filter_table ( table) ;
568+
569+ let mut flow_filter = FlowFilter :: new ( "test-filter" , writer. get_reader ( ) ) ;
570+
571+ // Test with packets
572+
573+ // VPC-1 -> VPC-2: No ambiguity
574+ let packet = create_test_packet (
575+ Some ( vpcd ( 100 ) ) ,
576+ "1.0.0.5" . parse ( ) . unwrap ( ) ,
577+ "5.0.0.10" . parse ( ) . unwrap ( ) ,
578+ ) ;
579+
580+ let packets = flow_filter
581+ . process ( [ packet] . into_iter ( ) )
582+ . collect :: < Vec < _ > > ( ) ;
583+
584+ assert_eq ! ( packets. len( ) , 1 ) ;
585+ assert ! ( !packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
586+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, Some ( vpcd( vni2. into( ) ) ) ) ;
587+
588+ // VPC-3 -> VPC-2: No ambiguity
589+ let packet = create_test_packet (
590+ Some ( vpcd ( 300 ) ) ,
591+ "1.0.0.70" . parse ( ) . unwrap ( ) ,
592+ "5.0.0.10" . parse ( ) . unwrap ( ) ,
593+ ) ;
594+
595+ let packets = flow_filter
596+ . process ( [ packet] . into_iter ( ) )
597+ . collect :: < Vec < _ > > ( ) ;
598+
599+ assert_eq ! ( packets. len( ) , 1 ) ;
600+ assert ! ( !packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
601+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, Some ( vpcd( vni2. into( ) ) ) ) ;
602+
603+ // VPC-2 -> VPC-1 using lower non-overlapping destination prefix section
604+ let packet = create_test_packet (
605+ Some ( vpcd ( 200 ) ) ,
606+ "5.0.0.10" . parse ( ) . unwrap ( ) ,
607+ "1.0.0.5" . parse ( ) . unwrap ( ) ,
608+ ) ;
609+
610+ let packets = flow_filter
611+ . process ( [ packet] . into_iter ( ) )
612+ . collect :: < Vec < _ > > ( ) ;
613+
614+ assert_eq ! ( packets. len( ) , 1 ) ;
615+ assert ! ( !packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
616+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, Some ( vpcd( vni1. into( ) ) ) ) ;
617+
618+ // VPC-2 -> VPC-1 using upper non-overlapping destination prefix section
619+ let packet = create_test_packet (
620+ Some ( vpcd ( 200 ) ) ,
621+ "5.0.0.10" . parse ( ) . unwrap ( ) ,
622+ "1.0.0.205" . parse ( ) . unwrap ( ) ,
623+ ) ;
624+
625+ let packets = flow_filter
626+ . process ( [ packet] . into_iter ( ) )
627+ . collect :: < Vec < _ > > ( ) ;
628+
629+ assert_eq ! ( packets. len( ) , 1 ) ;
630+ assert ! ( !packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
631+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, Some ( vpcd( vni1. into( ) ) ) ) ;
632+
633+ // VPC-2 -> VPC-3 using non-overlapping source prefix
634+ let packet = create_test_packet (
635+ Some ( vpcd ( 200 ) ) ,
636+ "6.0.0.11" . parse ( ) . unwrap ( ) ,
637+ "1.0.0.70" . parse ( ) . unwrap ( ) ,
638+ ) ;
639+
640+ let packets = flow_filter
641+ . process ( [ packet] . into_iter ( ) )
642+ . collect :: < Vec < _ > > ( ) ;
643+
644+ assert_eq ! ( packets. len( ) , 1 ) ;
645+ assert ! ( !packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
646+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, Some ( vpcd( vni3. into( ) ) ) ) ;
647+
648+ // VPC-2 -> VPC-??? using overlapping prefix sections: multiple matches
649+ let packet = create_test_packet (
650+ Some ( vpcd ( 200 ) ) ,
651+ "5.0.0.10" . parse ( ) . unwrap ( ) ,
652+ "1.0.0.70" . parse ( ) . unwrap ( ) ,
653+ ) ;
654+
655+ let packets = flow_filter
656+ . process ( [ packet] . into_iter ( ) )
657+ . collect :: < Vec < _ > > ( ) ;
658+
659+ assert_eq ! ( packets. len( ) , 1 ) ;
660+ assert ! ( packets[ 0 ] . is_done( ) , "{:?}" , packets[ 0 ] . get_done( ) ) ;
661+ assert_eq ! ( packets[ 0 ] . meta( ) . dst_vpcd, None )
662+ }
663+
450664 #[ test]
451665 fn test_flow_filter_ipv6 ( ) {
452666 // Setup table
0 commit comments