Skip to content
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

RFC: dialMe protocol #64

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

RFC: dialMe protocol #64

wants to merge 3 commits into from

Conversation

dryajov
Copy link
Member

@dryajov dryajov commented May 29, 2018

Initial spec for a dialme protocol.

// cc @Stebalien @vyzo @diasdavid @whyrusleeping @mkg20001 @Kubuxu @mgoelzer

P.S. please cc anyone else that might be interested.

@ghost ghost assigned dryajov May 29, 2018
@ghost ghost added the in progress label May 29, 2018
dialme/README.md Outdated

- Should be transports agnostic
- Should be connection agnostic
- Can work with or without connection upgrades
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldnt this all be simplified by saying that the dialme protocol is a standard libp2p protocol? all libp2p protocols are transport and connection agnostic and assume nothing about the network stack implementation

dialme/README.md Outdated
message DialMe {
message Peer {
required bytes id = 1; // peer id
repeated bytes addrs = 2; // array of multiadrs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my main concern with this protocol (and probably the reason it hasnt moved forward until now) is figuring out how to deal with spam and fake dials. If i can tell any peer to try dialing some random set of addresses, i could hypothetically use this to DoS a given ip address. Just connecting to a bunch of libp2p peers and saying "dialme at [all these addresses that are really the sameish]"

Copy link
Member Author

@dryajov dryajov May 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think in this case, having an array of addresses is a huge attack vector, we can limit that to only one address per request, as well as throttle dial back requests coming from the same peer. I though about nonces and challenge response schemes, but they don't seem to work in this case.

@whyrusleeping
Copy link
Contributor

The other hard part here is the logic surrounding this protocol. When we get a successful dial back, we have to detect it, and switch over to using that connection as the main connection, and then ideally close the original relayed connection.

Also, knowing when to call dialme will be tricky.

That said, as a protocol, this should pretty much just work.

@dryajov
Copy link
Member Author

dryajov commented May 30, 2018

Thanks for the feedback @whyrusleeping.

When we get a successful dial back, we have to detect it, and switch over to using that connection as the main connection, and then ideally close the original relayed connection.

I was thinking that we can have the dial back mechanism as part of the dial sequence. Where for now, we only use it when we've dialed over circuit. The advantage of doing it as part of the dial flow, is that we don't have to make any changes to support connection upgrades and such. Later, we can come up with a mechanism that allows upgrading the connections seamlessly - providing this functionality seems to go hand in hand with detecting network changes (mobile networks/clients, random access points, etc...).

So here is how I think we can make this work right now. We need circuit and NAT (or public IP) for this to make sense, and know which transports the other peer can dial us on.

  • A wants to dial B
    • A has a public ip or an active NAT port mapping
    • B is behind a NAT
  • Dial flow begins
    • Circuit succeeded and we have a connection to B
    • Ask B to dial A on some transports that both A and B know about
      • A asks B to dial back
        • B dials on A's public address
          • success
            • perform secio (again), muxing, etc and close the old circuited connection
          • failed
            • continue using the circuited connection
    • A awaits the dial back
      • response arrived on time
        • perform secio (again), muxing, etc and close the old circuited connection
      • response timed out
        • continue using the circuited connection
  • Dial flow ends

I think it should work without making any changes to swarm/switch or libp2p itself. I know that in js this fits the dial flow nicely, and shouldn't be a problem, but I haven't checked Go yet.

@dryajov dryajov mentioned this pull request May 31, 2018
dialme/README.md Outdated
@@ -30,7 +30,7 @@ This is a rather simple protocol that communicates a multiaddress to perform the
message DialMe {
message Peer {
required bytes id = 1; // peer id
repeated bytes addrs = 2; // array of multiadrs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit should have changed repeated to optional (unless I'm missing something).

dialme/README.md Outdated
@@ -30,7 +30,7 @@ This is a rather simple protocol that communicates a multiaddress to perform the
message DialMe {
message Peer {
required bytes id = 1; // peer id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't include this, the remote side should already know our peer ID and we definitely don't want to end up dialing a different peer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I agree with you, I can't think of a use for this honestly. Unless anyone objects I'll remove it.

@Stebalien
Copy link
Member

Just to ask the question: Do we actually need this? We could also:

  1. Avoid announcing undialable addresses over identify (we should already do this).
  2. Have some service that detects heavy usage of a connection using a "slow" transport and tries to establish a better connection (if it can find a dialable address for that peer). (we could also do this based on the service using the connection instead of relying on some "heavy-usage" metric).

IMO, this is a strictly better approach in theory. This way, the dialing node makes the decision on whether or not to dial.

However, it could be tricky in practice. We'd need to get much better at tracking which addresses are dialable (which we should be doing anyways...).

@dryajov
Copy link
Member Author

dryajov commented May 31, 2018

Avoid announcing undialable addresses over identify (we should already do this).

Agree on the above, but there is more to this, IMO.

Have some service that detects heavy usage of a connection using a "slow" transport and tries to establish a better connection (if it can find a dialable address for that peer). (we could also do this based on the service using the connection instead of relying on some "heavy-usage" metric).

IMO, this reduces the amount of heuristics we need to keep track of in order to create a direct connection between two peers. Also, I don't think that this conflicts with any of the above really, and we can and probably should use it in combination with what you describe.

@Stebalien
Copy link
Member

Yeah, I guess for push protocols (e.g., pubsub) one might want to connect to a node and say "no really, we need a better connection).


After thinking about it a bit, we probably should allow specifying multiple dial-back addresses as the remote end may not know how to dial some of them (unsupported transport). For DoS protection, we don't actually have to dial every address specified (we can choose to dial the first N supported addresses where N can even be 1).

@dryajov
Copy link
Member Author

dryajov commented May 31, 2018

@whyrusleeping brought up a great point, we don't really have a way of reliably saying if those addrs are from the peer requesting the dial back, not even if they are valid IPFS peers at all.

After thinking about it a bit, we probably should allow specifying multiple dial-back addresses as the remote end may not know how to dial some of them (unsupported transport).

For unsupported transports, we might either incorporate something to list the transports the other end speaks, or do a full blown identify exchange?

For DoS protection, we don't actually have to dial every address specified (we can choose to dial the first N supported addresses where N can even be 1).

This is definitely the simplest way of going about it, and we can always tweak things to make it less DDoS prone. Not opposed to it at all.

So to sumarize:

  • We have to be careful not to open up a nasty DDoS attack vectors, we can either:
    • allow multiple addrs to be sent, but limit them to something reasonable so that we don't blast a bunch of random peers in the network
    • send only one addr, but incorporate some way of discovering which transports the other end supports and send the addr only for the transports that both peers speak

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Triage
Development

Successfully merging this pull request may close these issues.

3 participants