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

Finer local candidate address selection #2

Open
sebastianriese opened this issue Mar 14, 2018 · 11 comments
Open

Finer local candidate address selection #2

sebastianriese opened this issue Mar 14, 2018 · 11 comments

Comments

@sebastianriese
Copy link
Contributor

Currently all addresses on all interfaces are automatically selected as candidates by gather_candidates and the loopback device is only discarded based on the addresses bound to it. While this does work, it may not be desirable for all users, who for example have redundant links where one link is unfit for bulk transfers of data negotiated via ICE (e.g. a volume-limited mobile link). Other addresses should perhaps be ignored from the start because they have no routes to the public internet (e.g. a VPN link to some intranet). This information is not available at the level of aioice but may be available to an application (e.g. via configuration or tight integration with the target system).

Some kind of interface with finer granularity for selecting candidates would enable the use of such information. Probably, one could simply manually modify or set Connection.local_candidates to achieve this, but this feels wrong, since I would consider the member variable private by default. If this is the preferred interface, this should be documented (and the issue closed).

I would argue for additional methods for setting the local candidates:

  • by explicitly adding IPs,
  • by specifying interfaces whose addresses to add,
  • ...

I would implement the changes, but wanted to discuss the preferred API first.

@jlaine
Copy link
Collaborator

jlaine commented Mar 14, 2018

It feels as though this could be achieved with some optional keyword arguments to gather_candidates. The IPv4 / Ipv6 flags which are currently in the constructor would for example be better off as arguments to gather_candidates.

I'm not too fond of the idea of explicitly adding IP addresses, but restricting the interfaces (either as a whitelist or a blacklist) sounds interesting.

@jlaine
Copy link
Collaborator

jlaine commented Mar 14, 2018

Maybe we could have a look a what existing ICE libraries (like nice or pjnath) to see how they handle it

@sebastianriese
Copy link
Contributor Author

Yes, explicitly listing IPs also seems not quite right (and can be achieved anyway by setting local candidates manually, if an application really needs to do this). Using a keyword argument for restricting interfaces sounds very convincing to me.

@twisteroidambassador
Copy link

This might also be implemented "backwards", i.e. on each candidate expose which interface it came from, and provide a way to delete candidates from a Connection. The workflow is then:

  • call gather_candidates()
  • iterate through all candidates, inspect their source interface, delete candidates not needed
  • call connect()

@jlaine
Copy link
Collaborator

jlaine commented Jun 26, 2018

@twisteroidambassador I'm not sure that's the right way to go, I think it will complicate adding "full trickle" ICE support, i.e. gathering candidates in an asynchronous fashion.

FYI we currently support "half trickle", i.e. we gather all local candidates in one go, but you can add remote candidates using Connection.add_remote_candidate.

@jlaine
Copy link
Collaborator

jlaine commented Jun 26, 2018

By the way I'm still waiting for someone to provide some "prior art" on how other ICE libraries handle local candidate selection. So far the options I see involve additional keyword arguments:

  • adding an interface keyword argument, like interfaces=['eth0', 'eth1']

  • adding a candidate_filter keyword argument which would take a callable that returns True or False when given a candidate. While this option gives a lot of flexibility, ICE Candidates don't carry the explicit name of the network interface, so if you just want to restrict which network interfaces are used, it will be tricky.

@twisteroidambassador
Copy link

Based on a brief look at their documentations (and I can't say I'm fluent in C), pjnath does not seem to provide a way to selectively include host candidates. libnice can either discover all addresses automatically, or use a list of IP addresses explicitly provided.

Arguably, the goal of ICE is to discover possible network routes and establish a connection without prior knowledge of the network topology and available interfaces / addresses, so the use case of fine-grained host candidate selection is debatable: addresses that have no connectivity to the peer / internet will naturally be ignored.

On the other hand, there are privacy implications of telling the peer all your local IP addresses, especially with WebRTC. An IETF draft discusses this problem, and provides several modes of behavior that may be worthy of implementation: https://tools.ietf.org/html/draft-ietf-rtcweb-ip-handling-09

@jlaine
Copy link
Collaborator

jlaine commented Jun 27, 2018

Thanks @twisteroidambassador that draft is very interesting.

As far as I understand mode 2 could be implemented with a restriction on which interfaces are picked up, though I'm not too sure what "default route" means if you have both ipv4 and ipv6 addresses.

Mode 3 is more problematic as it means discarding the host candidates after gathering the server reflexive candidates, which doesn't play nice with trickle ICE..

@jlaine
Copy link
Collaborator

jlaine commented Jun 27, 2018

@twisteroidambassador
Copy link

The "default route interface" can be identified by examining the routing table, or perhaps more often, by creating a datagram socket, connecting to an Internet address (no actual packet is sent), and examining the resultant bind address (i.e. getsockname). https://github.com/twisteroidambassador/aiostun/blob/2d1c13218d231603ff7754a4ea03f53fdcd64537/aiostun/ip.py#L20

In the context of ICE, the IP address of the STUN / TURN server can be used, assuming they are on the Internet. In case of a dual-stack host, perhaps do the same thing with both an IPv4 and a n IPv6 address.

When Chrome does something like Mode 3, they bind to 0.0.0.0 and collect server reflexive / relay candidates from there. https://www.w3.org/2011/04/webrtc/wiki/images/d/da/WebRTC_IP_Address_Privacy.pdf

Also interesting, libnice only use one datagram socket (bound to 0.0.0.0) per component, shared among all candidates related to the component. https://github.com/libnice/libnice/blob/master/docs/design.txt

@jlaine
Copy link
Collaborator

jlaine commented Jun 27, 2018

Creating the datagram socket to determine the default route is a neat trick, will keep that in mind.

Binding to 0.0.0.0 is something I've tried previously but I seem to recall ending up with strange results depending on the IPv4/IPv6 mixture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants