Good day, colleagues.
Some time ago I became interested in a subject. The search gave a lot of material, while studying which a number of questions arose and I wanted to check some theoretical points in practice. Who cares - please.
')
For those who are not in the subject briefly about what Symmetric NAT is.
Consider a simple scheme in which
host1, host2 - user hosts for NATs
NAT1, NAT2 - border devices providing NAT
When host1 initiates an outgoing connection (TCP or UDP) with a real_IP, the NAT1 device replaces the source IP in its outgoing packet with its own (not necessarily its own, but accept it for simplicity and clarity), and also generally replaces PORT_SRC with a random PORT_SRC1.
host1 ____ PORT_SRC, PORT_DST ---> device NAT1 PORT_SRC1, PORT_DST ----> real_IPSo, NAT will be symmetrical if the host_real_IP response packet is passed by the NAT1 device if and only if the source’s actual IP in such a reply packet is real_IP, the source port is PORT_DST, and the destination port is PORT_SRC1. That is, real_IP will respond from its address, from the same port that it was connected to, and to the same port from which there was an outgoing connection from the NAT1 device. It is not difficult to notice that the term “symmetry” was not in vain here.
What do we have when the task is to “break through” symmetric NAT and allow the hosts to communicate directly with it?
In general, we have the following simple schema.
host1 ----> NAT1 SRC1_ random, DST1_the wish ---->
<- DST2_health, SRC2_
random NAT2 <- host2SRC1_s chance - the source port after NAT substitution by the NAT1 device;
DST1_by_ wish — the destination port for an outgoing connection established from host1 through NAT1 can be chosen by us;
DST2_to_ wish — the destination port for a packet sent from host2 through NAT2 to NAT1 can be chosen by us;
SRC2_incident - source port after NAT substitution by NAT2 device.
If you look closely at this schema, the difficulties for the "breakdown" are obvious. In order for traffic exchange to occur, the following must be done: SRC1_slip = DST2_glove and DST1_glove = SRC2_slip.
The host1 and host2 devices cannot control the SRC1_cause and SRC2_cord ports. In general, they will be really random, plus depending on the destination IP and the destination port. The mass of materials found by the search on this topic ignored this simple fact, considering that a simple STUN server is enough to find out these unknowns.
Like, yes. If you take NAT, organized with, say, iptables, then constructions like
-t nat -A POSTROUTING -j MASQUERADE
or
-t nat -A POSTROUTING -j SNAT --to-source
will only replace the source IP, the outgoing port will remain the same as on the client. Thus, the task is greatly simplified and the mass of articles describing the role of an intermediary for this NAT traversal technology begin to hit the mark.
But this is a private and simple case.
For the same iptables, these structures can be replaced by
-t nat -A POSTROUTING -j MASQUERADE ... --random
or
-t nat -A POSTROUTING -j SNAT --to-source ... --random
and the situation will be less fun. The outgoing port will start to be selected when the NAT translation is random.
Total, after a careful look at the schematic above, we have the following problems.
Neither host1 nor host2 is aware of SRC1_cause and SRC2_cause, they can only influence them indirectly, for example, changing the source port for their outgoing packets or sufficient timeout between sending data so that NAT records on NAT-devices have time to become obsolete and reset. .
Host2 should, among other things, find out DST1_the_willingness (in general, the port can be agreed upon in advance, but we will consider the worst option).
Without going into long explanations of the need for an intermediary, I will set forth at once an approximate algorithm for the actions of all participants in the exchange.
1. A mediator is strictly necessary with which host1 and host2 establish full-fledged control connections with free two-way traffic. Initiators of such control sessions are hosts for NAT. The mediator distributes among the end hosts information about their white IPs.
2. Suppose that host1 is the initiator of the target connection / client, and for host2 the connection will be incoming, that is, it will act as a server.
3. Host1 starts sending packets to the real NAT2 address, choosing DST1_ according to wish arbitrarily.
4. Host1 on the control connection informs the broker about the selected DST1_ according to the wish and continues to send packets with the same parameters (source port, destination port) to maintain constant broadcast on NAT1 so that SRC1_ does not change. The delay between shipments can be determined experimentally, due to the triviality of the task I do not schedule it and do not include it in this text.
5. The broker scans NAT1, replacing the source IP address with the NAT2 address, providing the source port DST1_wheel and sorting out the destination port in the range from 1024 (or the entire range of 65535?) And above. All this is necessary, because we decided earlier that NAT is not for nothing called symmetric.
When, as a result of busting (if we are not banned by NAT1 by this time), we finally send a packet with the destination port SRC1_accidental, then such a packet, if any particularly tricky protection mechanisms that go beyond the NAT functionality, do not interfere with the host1 . What he detects and further on the control channel will notify the intermediary, and he will remember the guessed value SRC1_ random.
6. The intermediary reports on the control channel to the host2 SRC1_sluggish.
7. Host2 starts sending packets to the external address NAT1, the destination port in these packets should be DST2_ wish = SRC1_ random. It will be forced to do this until the SRC2_s chance, chosen by the NAT2 device, matches DST1_growth.
When this long-awaited coincidence happens, two options are possible.
The first. This package will reach the host1, he will notify the intermediary, the intermediary will notify the host2 and the “happy” session will continue.
Second. Host2, not wanting to annoy NAT1 once again with “swotting”, changed IP-ttl in its outgoing packets to such a value that they would pass through NAT2, but did not reach NAT1. In this case, translations to NAT2 are created, packets do not reach NAT1, but when the cherished broadcast is created, the packet from host1 (and we did not forget that it supports its broadcast with the previously described, namely, “dolbytsya” on NAT2 with constant parameters) will reach host2, and that one in turn will notify the intermediary and so on.
As you can see, in the general case, the breakdown of symmetric NAT is not such an easy and quick task. On those or other implementations (recalling iptables), it can be significantly simplified and turned into a procedure with guaranteed success, but, I repeat, not in the general case.
What are the "bottlenecks" of the described algorithm?
1. The need for spoofing in clause 5. The mediator should have a way to access the network that would allow it and not get problems from the provider.
2. Scanning a large range of ports in step 5. Let me repeat again, in the case of a “correct” NAT, you cannot host1 simply connect to an intermediary and assume that SRC1_specific in the case of a connection to NAT2 will be the same, all other things being equal. Changing the destination address will result in a change of SRC1_ random. Scan, of course, can be done fairly quickly and aggressively, you can be careful, but longer. In any case, this is a weak point.
3. The steps in clause 7 may be indefinitely long. Just this moment caused my curiosity and desire to try.
4. And of course, someone who is tired of reading so many letters can say: “I need it, drive all the target traffic through an intermediary and that's it!”. It is certainly difficult to argue here, I apologize to those for the time taken by this text.
Now to practice. Perhaps I will disappoint those who are interested in the topic, but I did not write a mediator :) In his role tcpdump played in the right place at the right time, as well as the author’s eyes and hands :) But I did get some interesting results.
So. We have a screw workstation with 3G = host1. On the modem connection, the gray address was 10.140.80.130. This is the internal address of the host1 behind NAT.
We have an AT AR-750s router with a white address xx.xx.xx.xx and host2 directly behind this router.
Choose arbitrary DST1_ according to wish, in my case it was equal to 21393.
We start at intervals of 10 seconds from host1 to send UDP packets to xx.xx.xx.xx: 21393.
Passing the malicious scan stage, we convey “secretly” information about port 21393 to host2, as well as “spied on” information that SRC1_ is random for the NAT of the cellular operator, we have 45499 (well, IP, from which we are NAT-i-world-wide too = yy.yy.yy.yy).
Starting with host2, we start “dolby” on yy.yy.yy.yy: 45499 and wait for us to get lucky and our outgoing packet will receive the source port for NAT that is 21393. . The packet generation rate from host2 was ~ 5 packets per second. At the same time, the NAT2 router was slightly loaded with outsiders (users would hear it) with traffic.
The first "breakdown" happened about 8 hours after the start of the experiment. Then a few more of them happened, since all this household was left to work at night. Subsequent began to occur more quickly several times, here you can fantasize about the impact of "outsider" traffic.
Here's what the "breakdown" looked like. The output is selective, which includes only the "happy" packets with the necessary coincidence.
The output of the sniffer in the “tricky” traffic pick-up point, where it (traffic) is visible already after the NAT on the external interface of the AR-750 router
02: 19: 05.060809 IP xx.xx.xx.xx.21393> yy.yy.yy.yy.45499: UDP, length 0
05: 07: 00.178149 IP xx.xx.xx.xx.21393> yy.yy.yy.yy.45499: UDP, length 0
06: 28: 35.355623 IP xx.xx.xx.xx.21393> yy.yy.yy.yy.45499: UDP, length 0
07: 16: 29.764069 IP xx.xx.xx.xx.21393> yy.yy.yy.yy.45499: UDP, length 0
11: 28: 06.899109 IP xx.xx.xx.xx.21393> yy.yy.yy.yy.45499: UDP, length 0
The output of a sniffer on host1, in which a breakdown is “detected” (time is not synchronized, hence some discrepancy between time stamps and printout from the traffic dump of host2).
02: 18: 20.480468 IP xx.xx.xx.xx.21393> 10.140.80.130.2429: UDP, length 0
05: 06: 15.496093 IP xx.xx.xx.xx.21393> 10.140.80.130.2429: UDP, length 0
06: 27: 50.464843 IP xx.xx.xx.xx.21393> 10.140.80.130.2429: UDP, length 0
07: 15: 44.839843 IP xx.xx.xx.xx.21393> 10.140.80.130.2429: UDP, length 0
11: 27: 21.589843 IP xx.xx.xx.xx.21393> 10.140.80.130.2429: UDP, length 0
There was another result of the dump directly on host2, in which it was clear that the packets from point 4 to host2, which support constant broadcast on NAT1, began to reach, but I sowed it.
Here is a not entirely honest experiment. But he showed that even with a relatively low frequency of enumeration in paragraph 7, the desired result can be achieved in a relatively reasonable time. It can become even more sensible with a more aggressive search. Of course, the "industrial" application does not pull, but ... But it is possible. The described algorithm and experiment gives food for thought on the topic of acceleration-optimization-multi-thread approach and so on.
UPD: I forgot to mention that from host2 the packets were sent with the change of the source port, so that the source port would also change over NAT2, it’s too expensive to wait for the broadcast to NAT2 to die.