It's called NAT punchthrough. Today costs of dedicated servers are so low (and advantages of having it are so much better) almost nobody uses it.
When you're behind NAT your router will forward packets from outside to you only if you initiated communication first, so router has mapped that outside IP and random port to your computer.
Solution: basically when user hosts (P2P hosting) a game, all clients (all games) start sending packets to host and host sends packets to them, so hosts opens up to packets from outside after few tries. It's actually more complicated then that, here's what RakNet's documation on NAT punchtrough says:
NAT Punchthrough Algorithm
Peer P1 wants to connect to peer P2, both of whom are connected to a third Non-NAT system, F
Peer P1 calls OpenNAT() with the RakNetGUID (unique identifier) of P2 to F.
F returns failure if P2 is not connected, or already attempting punchthrough to P1.
F remembers busy state of P1 and P2. If either P1 or P2 is busy, the request is pushed to a queue. Otherwise F requests most recently used external port from P1 and P2. P1 and P2 are flagged as busy.
If either P1 or P2 do not respond punchthrough fails with ID_NAT_TARGET_UNRESPONSIVE and the busy flag is unset. Otherwise, F sends timestamped connection message to P1 and P2 simultaneously.
P1 and P2 act identically at this point. First, they send multiple UDP datagrams to each other's internal LAN addresses. They then try each other's external IP/port as seen by F. Ports are attempted sequentially, up to MAX_PREDICTIVE_PORT_RANGE.
If at any point a datagram arrives from the remote peer, we enter state PUNCHING_FIXED_PORT. Datagrams are only sent to that IP/port combination the remainder of the algorithm. If our reply arrives on the remote system, the NAT is considered bidirectional and ID_NAT_PUNCHTHROUGH_SUCCEEDED is returned to the user.
When NAT is open, or if we exhaust all ports, P1 and P2 send to F that they are ready for a new punchthrough attempt.