fn test_nat_resolver_aggregation()

in nfm-controller/src/events/nat_resolver.rs [226:313]


    fn test_nat_resolver_aggregation() {
        // Mirror reflection means NAT was not applied.
        let ct_entry1 = ConntrackEntry {
            original: ConnectionProperties {
                src_ip: IpAddr::from_str("1.2.3.4").unwrap(),
                dst_ip: IpAddr::from_str("5.6.7.8").unwrap(),
                src_port: 1234,
                dst_port: 5678,
                protocol: libc::IPPROTO_TCP as u8,
            },
            reply: ConnectionProperties {
                src_ip: IpAddr::from_str("5.6.7.8").unwrap(),
                dst_ip: IpAddr::from_str("1.2.3.4").unwrap(),
                src_port: 5678,
                dst_port: 1234,
                protocol: libc::IPPROTO_TCP as u8,
            },
        };

        // Change some reply values to represent NAT.
        let mut ct_entry2 = ct_entry1.clone();
        ct_entry2.reply.src_ip = IpAddr::from_str("9.10.11.12").unwrap();
        ct_entry2.reply.src_port = 9876;

        let conntrack_listener = ConntrackListenerSeeded {
            pending_replies: VecDeque::from([vec![ct_entry1], vec![ct_entry2]]),
        };
        let mut nat_resolver = seeded_nat_resolver(conntrack_listener);

        // After the first agg cycle, expect nothing cached.
        nat_resolver.perform_aggregation_cycle();
        assert_eq!(nat_resolver.num_entries(), 0);

        // After the second agg cycle, expect a cache entry for each direction.
        nat_resolver.perform_aggregation_cycle();
        assert_eq!(nat_resolver.num_entries(), 2);

        // Now try NAT resolution.
        let sock_ctx = SockContext {
            local_ipv4: Ipv4Addr::from_str("1.2.3.4").unwrap().to_bits(),
            remote_ipv4: Ipv4Addr::from_str("5.6.7.8").unwrap().to_bits(),
            local_ipv6: [0; 16],
            remote_ipv6: [0; 16],
            local_port: 1234,
            remote_port: 5678,
            address_family: libc::AF_INET.try_into().unwrap(),
            is_client: true,
            ..Default::default()
        };
        let actual_natd_ctx = nat_resolver.get_beyond_nat_entry(&sock_ctx).unwrap();
        let expected_natd_ctx = SockContext {
            local_ipv4: Ipv4Addr::from_str("1.2.3.4").unwrap().to_bits(),
            remote_ipv4: Ipv4Addr::from_str("9.10.11.12").unwrap().to_bits(),
            local_ipv6: [0; 16],
            remote_ipv6: [0; 16],
            local_port: 1234,
            remote_port: 9876,
            address_family: libc::AF_INET.try_into().unwrap(),
            is_client: true,
            ..Default::default()
        };
        assert_eq!(actual_natd_ctx, expected_natd_ctx);

        // Test the resolver updating the sock cache.
        let mut sock_cache = SockCache::new();
        let sock_key: SockKey = 1979;
        let now_us = 1997;
        sock_cache.add_context(sock_key, sock_ctx, now_us);
        let store_result = nat_resolver.store_beyond_nat_entries(&mut sock_cache);
        assert_eq!(
            store_result,
            SockOperationResult {
                completed: 1,
                partial: 0,
                failed: 0,
            }
        );
        assert_eq!(
            sock_cache.get(&sock_key).unwrap().context_external.unwrap(),
            expected_natd_ctx
        );

        // After evicting everything, there's nothing more to resolve.
        for _ in 0..RING_BUF_ENTRIES {
            nat_resolver.perform_eviction();
        }
        assert!(nat_resolver.get_beyond_nat_entry(&sock_ctx).is_none());
    }