-
Notifications
You must be signed in to change notification settings - Fork 49
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
How to correctly handle Transmit::source
and Candidate::base
?
#453
Comments
I just tried this and it is an absolute rabbit hole haha. See (non-compiling) commit: thomaseizinger@c205c02 |
I'm trying to get my head around it. It was some time since I had the spec clear in my head :D |
My expectation when I wrote this was that such knowledge would be outside of str0m. Similar problems could occur on multihomed servers where it's important to know which NIC to use, or indeed keeping a TURN server connection active (where I believe you need some additional timers, creds etc). I.e. str0m has a Candidate with some source address. To the user it says "I want to send from this source address, I don't care how", the user of str0m then needs themselves to keep track of "What additional things do I need to know to send from this source address". |
One way forward here could potentially be to make a similar mechanism that we have for I'm imagining something like |
I think the difference here is that it is not opaque user data at all. The str0m has all the knowledge about this because I am giving it the local socket address that data was received on when it parses the reply out of a packet. It would be nice if it could properly retain this information and give it back to me "correctly" in |
This would also avoid the edge case of: str0m told me to send from an address that we don't have a local candidate for. In order to keep the mapping, I have to iterate all local candidates on each I totally get the "should be handled outside of str0m" point for TURN etc. In this particular case, it just appears that we can do better :) |
I still don't think I follow. The difference between a Both mean some source address, that needs to be routed via som other server. And in both cases, it's up to the user of str0m to know how to route the source address via a STUN or TURN server. |
Here's my mental model:
|
Conceptually yes. Perhaps I need to improve my design but in practise the two differ because To generically handle and transform
The last bit breaks for What I could do is keep a mapping every time I discover a |
So technically base should be different to addr for server reflexive (and maybe peer reflexive?). And then Transmit needs to use the base for |
No that's not it either… 😂 |
let local = pair.local_candidate(&self.local_candidates);
let proto = local.proto();
let local_addr = local.base();
let remote = pair.remote_candidate(&self.remote_candidates);
let remote_addr = remote.addr(); Maybe you want let via = remote.base(); |
Is this quoting the RFC? Or where is that text coming from? :) Because that sounds like what I want! |
That's the RFC. https://datatracker.ietf.org/doc/html/rfc8445#page-15 |
Okay, well that means we need to fix str0m to adhere to that, right? The ctor for |
Yes I think the PR I want would be
|
Why not just keep setting Edit: Or differently asked, if we change |
We shouldn't change the semantics of See my mental model above. To be consistent, source is always the IP the peer see as the source. The local NIC (or STUN/TURN server IP), or whatever you want the base to be, is extra information. We don't really need Maybe we should provide a lookup of candidate on address instead. That way we can keep Transmit simple. |
Playing devil's advocate: What value does the consistency bring if it overall makes the API harder to use? What would I use the information of The mental model I have is:
This corresponds directly to how the RFC defines
It keeps |
…ckets (#3411) It turns out that we need to do some post-processing of the `Transmit.source` attribute from `str0m`. In its current state, `str0m` may also set that to a server-reflexive address which is **not** a local socket. There is a longer discussion around this here: algesten/str0m#453. This depends on an unmerged PR in `str0m`: algesten/str0m#455.
Is this a better mental model? |
I think so? (Assuming I am getting all the important bits you want to convey). |
In the connectivity library I built on top of
str0m
'sIceAgent
, I copied its model ofTransmit
and return those messages to the upper layer. I also handle TURN allocations and channels for the user.Here comes the catch: When
str0m
wants to make a STUN binding request from one of my relay candidates, it setsTransmit::source
to the allocation address. This is a result ofCandidate::base
being set to the same value as the actual addr in the constructor:str0m/src/ice/candidate.rs
Line 216 in 30ba27e
This is actually useful! It allows me to cross-reference
Transmit::source
with the list of TURN servers and if I detect that I have a TURN server for this IP, I know that I need to wrap theTransmit::contents
in a channel data message toTransmit::destination
.The issue gets trickier for server-reflexive addresses. Here,
Transmit::source
is set to the server-reflexive address. Without keeping a mapping myself, I therefore don't know which local interface this packet needs to be sent from. My first thought was that we should simply change theCandidate
constructors to accept an additionalbase
parameter that designates the local socket the data was received on that generated this candidate. However, this will break the case for relay candidates because I now have no way of knowing, whether the data needs to be sent through a relay.So far, I've come up with the following solutions and I don't like any of them:
Candidate
constructors withbase
and set it correctly BUT also changeTransmit
to always useaddr
. Then, whenever processing aTransmit
from str0m, one needs to iterate the local candidates, find the corresponding one and check thebase
for server-reflexive address candidates to know which socket to use. For host candidates,addr
works just fine and for relay candidates, one can look up the mapping similar as to today.Candidate
constructors withbase
and splitTransmit::source
into two fields:final_source
/perceived_source
: Thesource
that the remote should see. This is only useful for relayed candidates.local_source
: The local socket to use when sending the data.Candidate
constructors withbase
and keep returning the actual allocation socket on the relay asbase
for relay candidates.The last one is the easiest but also the most confusing, especially because it is hard to understand and the type system doesn't guide you. Perhaps a variation of the last one would be to model this explicitly by introducing a
Source
enum that is:The text was updated successfully, but these errors were encountered: