-
Notifications
You must be signed in to change notification settings - Fork 291
add autonat v2 spec #538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add autonat v2 spec #538
Changes from 4 commits
d663611
1db8613
0ff8ac6
f2a431c
62123df
0771bab
3e57202
f6def9a
b769d79
e4efaae
f28511c
d4da279
5b8d37d
05a0de2
8b52643
dd2750c
4e6ecaa
2af3309
6b1604b
f979fac
209b215
094089b
b4a856b
1c76613
03718ef
0195203
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # NAT Discovery <!-- omit in toc --> | ||
| > How we detect if we're behind a NAT. | ||
|
|
||
|
|
||
| Specifications: | ||
| - [autonat v1](autonat-v1.md) | ||
| - [autonat v2](autonat-v2.md) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| # AutonatV2: spec | ||
sukunrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| | Lifecycle Stage | Maturity | Status | Latest Revision | | ||
| |-----------------|--------------------------|--------|-----------------| | ||
| | 1A | Working Draft | Active | r2, 2023-04-15 | | ||
|
|
||
| Authors: [@sukunrt] | ||
|
|
||
| Interest Group: [@marten-seemann], [@marcopolo], [@mxinden] | ||
|
|
||
| [@sukunrt]: https://github.com/sukunrt | ||
| [@marten-seemann]: https://github.com/marten-seemann | ||
| [@mxinden]: https://github.com/mxinden | ||
| [@marcopolo]: https://github.com/marcopolo | ||
|
|
||
|
|
||
| ## Overview | ||
|
|
||
| A priori, a node cannot know if it is behind a NAT / firewall or if it is | ||
| publicly reachable. Knowing its NAT status is essential for the node to be | ||
| well-behaved in the network: A node that's behind a NAT / firewall doesn't need | ||
| to advertise its (undialable) addresses to the rest of the network, preventing | ||
| superfluous dials from other peers. Furthermore, it might actively seek to | ||
| improve its connectivity by finding a relay server, which would allow other | ||
| peers to establish a relayed connection. | ||
|
|
||
| In `autonat v2` client sends a priority ordered list of addresses. On receiving | ||
| this list the server dials the first address on the list that it is capable of | ||
| dialing. `autonat v2` allows nodes to determine reachability for individual | ||
| addresses. Using `autonat v2` nodes can build an address pipeline where they can | ||
| test individual addresses discovered by different sources like identify, upnp | ||
| mappings, circuit addresses etc for reachability. Having a priority ordered list | ||
| of addresses provides the ability to verify low priority addresses. | ||
| Implementations can generate low priority address guesses and add them to | ||
| requests for high priority addresses as a nice to have. This is especially | ||
| helpful when introducing a new transport. Initially, such a transport will not | ||
| be widely supported in the network. Requests for verifying such addresses can be | ||
| reused to get information about other addresses | ||
|
|
||
| Compared to `autonat v1` there are two major differences | ||
| 1. `autonat v1` allowed testing reachability for the node. `autonat v2` allows | ||
| testing reachability for an individual address | ||
| 2. `autonat v2` provides a mechanism for nodes to verify whether the peer | ||
| actually successfully dialled an address. | ||
|
|
||
|
|
||
| ## AutoNAT V2 Protocol | ||
sukunrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| A node wishing to determine reachability of its adddresses sends a `DialRequest` | ||
| message to a peer on a stream with protocol ID | ||
| `/libp2p/autonat/2.0.0/dial-request`. | ||
|
|
||
| This `DialRequest` message has a list of `AddressDialRequest`s. Each item in | ||
| this list contains an address and a fixed64 nonce. The list is ordered in | ||
| descending order of priority for verfication. | ||
|
|
||
| Upon receiving this message the peer attempts to dial the first address from the | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| list that it is capable of dialing. It dials this address, opens a stream with | ||
| Protocol ID `/libp2p/autonat/2.0.0/dial-attempt` and sends a `DialAttempt` | ||
| message with the nonce received in the corresponding `AddressDialRequest`. The | ||
| peer MUST NOT dial any address other than the first address in the list that it | ||
| is capable of dialing. | ||
|
|
||
| Upon completion of the dial attempt, the peer sends a `DialResponse` message to | ||
| the initiator node on the `/libp2p/autonat/2.0.0/dial-request` stream with the | ||
| index(0 based) of the address that it attempted to dial and the appropriate | ||
| `ResponseStatus`. see [Requirements For | ||
| ResponseStatus](#requirements-for-responsestatus) | ||
|
|
||
| The initiator MUST check that the nonce received in the `DialAttempt` is the | ||
| same as the nonce the initiator sent in the `AddressDialRequest` for the address | ||
| index received in `DialResponse`. If the nonce is different, the initiator MUST | ||
| discard this response. | ||
|
|
||
|
|
||
| ### Requirements for ResponseStatus | ||
|
|
||
| On receiving a `DialRequest` the peer selects the first address on the list it | ||
| is capable of dialing. This address is referred to as _addr_. The | ||
| `ResponseStatus` sent by the peer in the `DialResponse` message MUST be set | ||
| according to the following requirements | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| `OK`: the peer was able to dial _addr_ successfully. | ||
|
|
||
| `E_DIAL_ERROR`: the peer attempted to dial _addr_ and was unable to connect. | ||
|
|
||
| `E_DIAL_REFUSED`: the peer didn't attempt a dial because of rate limiting, | ||
| resource limit reached or blacklisting. | ||
|
|
||
| `E_TRANSPORT_NOT_SUPPORTED`: the peer didn't have the ability to dial any of the | ||
| requested addresses. | ||
|
|
||
| `E_BAD_REQUEST`: the peer didn't attempt a dial because it was unable to decode | ||
| the message. This includes inability to decode the requested address to dial. | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| `E_INTERNAL_ERROR`: error not classified within the above error codes occured on | ||
| peer that prevented it from completing the request. | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| ### Consideration for DDOS Prevention | ||
|
|
||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| In order to prevent attacks like the one described in [RFC 3489, Section | ||
| 12.1.1](https://www.rfc-editor.org/rfc/rfc3489#section-12.1.1) (see excerpt | ||
| below), implementations MUST NOT dial any multiaddress unless it is based on the | ||
| IP address the requesting node is observed as. This restriction as well implies | ||
| that implementations MUST NOT accept dial requests via relayed connections as | ||
| one can not validate the IP address of the requesting node. | ||
|
|
||
| > RFC 3489 12.1.1 Attack I: DDOS Against a Target | ||
| > | ||
| > In this case, the attacker provides a large number of clients with the same | ||
| > faked MAPPED-ADDRESS that points to the intended target. This will trick all | ||
| > the STUN clients into thinking that their addresses are equal to that of the | ||
| > target. The clients then hand out that address in order to receive traffic on | ||
| > it (for example, in SIP or H.323 messages). However, all of that traffic | ||
| > becomes focused at the intended target. The attack can provide substantial | ||
| > amplification, especially when used with clients that are using STUN to enable | ||
| > multimedia applications. | ||
|
|
||
|
|
||
| ## Implementation Suggestions | ||
|
|
||
| For any given address, implementations SHOULD do the following | ||
| - periodically recheck reachability status | ||
| - query multiple peers to determine reachability | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The suggested heuristic for implementations is to consider an address reachable | ||
| if more than 3 peers report a successful dial and to consider an address | ||
| unreachable if more than 3 peers report unsuccessful dials. | ||
|
|
||
| Implementations are free to use different heuristics than this one | ||
|
|
||
|
|
||
| ## RPC Messages | ||
sukunrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| All RPC messages sent over a stream are prefixed with the message length in | ||
| bytes, encoded as an unsigned variable length integer as defined by the | ||
| [multiformats unsigned-varint spec][uvarint-spec]. | ||
|
|
||
| All RPC messages are of type `Message`. A `DialRequest` message is sent as a | ||
| `Message` with the `dialRequest` field set and the `type` field set to | ||
| `DIAL_REQUEST`. Other message types `DialResponse` and `DialAttempt` are handled | ||
| similarly. | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| ```proto | ||
| syntax = "proto3"; | ||
|
|
||
| message Message { | ||
| enum MessageType { | ||
| DIAL_REQUEST = 0; | ||
| DIAL_RESPONSE = 1; | ||
| DIAL_ATTEMPT = 2; | ||
| } | ||
|
|
||
| MessageType type = 1; | ||
| DialRequest dialRequest = 2; | ||
| DialResponse dialResponse = 3; | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| DialAttempt dialAttempt = 4; | ||
| } | ||
|
|
||
| message AddressDialRequest { | ||
| bytes addr = 1; | ||
| fixed64 nonce = 2; | ||
| } | ||
|
|
||
| message DialRequest { | ||
| repeated AddressDialRequest addressDialRequests = 1; | ||
| } | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| message DialAttempt { | ||
| fixed64 nonce = 1; | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| message DialResponse { | ||
| enum ResponseStatus { | ||
| OK = 0; | ||
| E_DIAL_ERROR = 100; | ||
| E_DIAL_REFUSED = 101; | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| E_TRANSPORT_NOT_SUPPORTED = 102; | ||
| E_BAD_REQUEST = 200; | ||
| E_INTERNAL_ERROR = 300; | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
sukunrt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ResponseStatus status = 1; | ||
| string statusText = 2; | ||
|
||
| int32 addrIdx = 3; | ||
| } | ||
| ``` | ||
|
|
||
| [uvarint-spec]: https://github.com/multiformats/unsigned-varint | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.