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());
}