I work on open source middleware for multi-agent systems, and we recently had to create a UDP-based transport option for p2p usage over 4G/3G networks. We've tested this on T Mobile's data plans, as well as various academic networks behind NATs, and I feel pretty confident in the implementation for the moment. Since there do not appear to be any solid solutions to this question here, I thought I would share the type of solution we currently have implemented in the MADARA middleware (http://madara.sourceforge.net/) via the REGISTRY_CLIENT transport option.
For us, we opted for what you might call a registry server or name service (if you're familiar with CORBA) for P2P endpoints. The registry server needs to run at some public ip:port that UDP can reach in a one way message. For our tests, we leased an Amazon EC2 node and made sure the firewall settings allowed UDP traffic through on a UDP port that we were going to bind to. On the server side (the public ip:port pairing for the registry server), we bind to the port and wait for clients to register. Any P2P client that wants to talk to others sends a registry message to the Registry Server
P2P client ----> [register message] ---> Registry Server
The register message can have any arbitrary contents. Ours actually have no contents other than a message header from our normal data protocol. On the server side, we check the remote host of the register message sender (the P2P client to the above left) via normal socket recv calls. This remote host should be the endpoint information through the firewall that is protecting the P2P client.
To understand what is going on here, we have to look at how messages are being routed for the P2P client to our public registry server. The following ASCII diagram shows the remote information a socket might see (i.e. an example firewall mapping) along the path from a P2P client to our EC2 server. This shows just one Firewall, but it should work with multiple NATs in between the P2P client and Registry Server, as long as the NATs are keeping the public ip:ports open when traffic is going through the assigned ip:port at that particular NAT.
P2P client ----> [message from 192.168.1.10:35000] ---> Firewall ---> [message from 111.111.50.34:5627] --> Registry Server
Now, if we tried to send a message to 192.168.1.10:35000 from our registry server, it would fail (or at least, it would almost certainly go to the wrong place). Similarly, you can see that the 4G Firewall is also changing the port from 35000 to 5627. To send a message back to the P2P client, you will need to do two things: 1) send a return message through 111.111.50.34:5627 rather than whatever ip:port information we initially bound to on the P2P client side, and 2) make sure either the P2P client or the registry server resend data back to each other frequently--once per second seemed fine for our purpose to permanently bind to the public ip:port of 111.111.50.34:5627, in our example.
As part of our protocol, we don't just send a useless packet back to the P2P client that registered. We send all of the relevant public ip:port pairings of P2P clients that are in that client's group/clique/domain/whatever. Basically, we send the P2P client discovery information for everything the registry server is aware of that seems relevant to the P2P client.
For instance, let's say that two P2P clients have messaged the registry server through 111.111.50.34:5627 and 111.111.37.24:15234 ip:port bindings through the 4G provider's firewall. A new P2P client connects from 111.111.70.105:7000, and we respond back with the other 2 end points in a simple listing:
[Registry Response for P2P Client #3]
111.111.50.34:5627
111.111.37.24:15234
Now, on the P2P Client #3 end, you take this list and use it as potential endpoints for your P2P application. These are your P2P peers. You can create UDP packets to them via the same socket you registered with and as long as they are not behind conservative firewalls (such as mobile tethered hotspots on your phone, which from my experience are traditionally designed to block all inbound UDP traffic on ephemeral port bindings with no configuration settings available to enable port forwarding) and traffic should be able to flow in both directions.
As noted earlier, to keep the P2P UDP connections valid you basically just need to resend data periodically to/from this endpoint to keep the public ip:port binding alive and active on each firewall protecting the P2P clients. This can be done by periodically resending registration information to the public registry server and/or receiving UDP packets from other P2P clients to that public ip:port that the 4G firewall has allocated to your P2P client.