This patch implements RFC 4787 requirements 1 and 3, changing PF's allocation of NAT mappings for UDP from the current "symmetric" NAT to a "endpoint-independent mapping" NAT, a.k.a "full cone" NAT. All UDP packets from the internal IP:port X:x go through the same external Y:y no matter the Z:z, and nothing but X:x uses Y:y.
X:x -----> NAT Y:y ----> Z:z
The implementation is relatively straightforward. pf_state for UDP connections now reference a pf_udp_mapping, which is reference counted, and kept alive as long as at least 1 pf_state is referencing it. Every new NAT mapping that gets created tries to find a pf_udp_mapping by its source X:x endpoint and reuses its external Y:y, failing which, it creates a new one through an unused Y:y. Only allocation of NAT mappings is changed. Each X:x <-> Z:z still has its own distinct connection state (struct pf_state) and behaves the same as before.
Currently, only if a Z:z was previously transmitted to by X:x, can it transmit back to X:x through Y:y, i.e it behaves as a "port-restricted cone" NAT (or endpoint-independent mapping NAT with address- and port-dependent filtering, as per RFC 4787).
This provides application with a NAT hole punching capability. Unlike in a symmetric NAT, in any cone-type NAT, an internal UDP application can negotiate to receive packets from a known peer, by using STUN to create a external IP:port for its UDP socket and discover what they are, communicating them to its peer and learning what external IP:port its peer is using, and even if it's behind the most restrictive "ported-restricted cone" NAT, it can just send 1 packet to its peer's IP:port to create a connection and allow that peer to send packets back.
This works even if both peers are NATed, as long as at least 1 (the server) is not a symmetric NAT.
Submitted By: Damjan Jovanovic <firstname.lastname@example.org>