[patch] NAT hole punching, RFC 4787, and PF/LibAlias/IPFILTER
Damjan Jovanovic
damjan.jov at gmail.com
Sun Jun 11 07:51:17 UTC 2017
Hi
Gaming, VoIP, WebRTC, peer to peer, and many other UDP applications,
require the ability to send to and receive from multiple peers through a
single UDP socket, and/or communicate the address of this socket to others
so they know where to transmit to.
Historically NAT usually got in the way, but many applications are
NAT-friendly these days, and can establish connections using NAT hole
punching, which is the best available technique of NAT traversal, able to
work through any number of layers of NAT (eg. both SOHO NAT and carrier
grade NAT), requires no special configuration, allows applications to
discover the external IP:port that the NAT gave them, and even allows
clients to connect to each other when both are NATed.
What NAT hole punching needs to work most of all, is what RFC 4787 calls an
endpoint-independent mapping NAT, also called a "full cone" NAT: all UDP
packets from the internal IP:port pair X:x, go through the same external
Y:y no matter the Z:z, and nothing but X:x uses Y:y:
Internal External
X:x -----> NAT Y:y ----> Z:z
This allows X:x to discover the Y:y that the NAT gives it by querying an
external server (eg. a STUN server), and rely on packets sent to any Z:z to
have Y:y as the source. It can communicate Y:y to a Z:z somehow, and have
that Z:z send to it (possibly after first sending to Z:z). This is a
mandatory requirement in RFC 4787.
Sadly, of FreeBSD's firewalls/NATs, only IPFILTER supports this desirable
property (as does "iptables" on Linux). PF's NAT and the LibAlias-based
NATs (IPFW, and presumably natd and pppd) are "symmetric" NATs, the worst
kind, randomly choosing a different Y:y for each Z:z from the same X:x,
which makes it impossible for X:x to predict its Y:y, which makes it
impossible for Z:z to learn about Y:y and reach it, especially if Z:z is
itself also behind a symmetric NAT. Connections will either fail, or
applications will resort to relaying data through other servers (eg. TURN),
often compromising bandwidth, latency, security, and/or privacy, and
increasing cost.
So I've written patches to change NAT port selection in PF and LibAlias to
endpoint-independent mapping. Please see here:
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=219803
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=219918
Currently neither are "full cone" NATs: Z:z can only send to X:x through
Y:y if X:x previously sent to Z:z, so they are "address- and
port-restricted cone" NATs, or rather an endpoint-independent mapping NAT
with address and port-dependent filtering. But applications can discover
this using a protocol such as STUN, and can still connect to known peers by
sending them a packet first.
There's more work to do. RFC 4787 lists several requirements, of which we
still fail on at least timeouts and hairpinning. Hairpinning is
particularly difficult: on IPFW the hairpinned packet doesn't even reach
LibAlias, and on PF there's a whole new path through the code. It may also
be desirable to implement a full-cone NAT, at least as an optional extra
due to the lower security, but that requires a different traversal of the
firewall rules, as unsolicited packets from Z:z to Y:y will match a NAT
rule backwards. Would anyone like to help?
Thank you
Damjan Jovanovic
References:
[1] http://alumnus.caltech.edu/~dank/peer-nat.html
[2] http://www.brynosaurus.com/pub/net/p2pnat
[3] https://tools.ietf.org/html/rfc4787
[4] https://tools.ietf.org/html/rfc5128
More information about the freebsd-net
mailing list