Subject: [Taps] UDP rendezvous

From: Michael Welzl <[email-masked]>
Date: Sat, 25 Apr 2020 14:44:07 +0200

Dear all,

I know way too little about rendezvous mechanisms, so I may get this completely wrong - please bear with me!
It may be that I describe an issue that’s a non-issue. But, if I do understand this right, maybe that’s something which needs fixing?

Ok, here it goes…

Our current Rendezvous text in the API draft says the following:
"The Rendezvous() Action causes the Preconnection to listen on the Local Endpoint for an incoming Connection from the Remote Endpoint, while simultaneously trying to establish a Connection from the Local Endpoint to the Remote Endpoint. This corresponds to a TCP simultaneous open, for example.”

Now, if an application chooses unreliable data transmission, it may get SCTP with partial reliability, or it may (in the future) get QUIC with datagrams, or whatever…  and then, the protocol may be able to do what’s needed (something similar to a simultaneous open).

However, in case of UDP:
- Listening is quite okay,
- “simultaneously trying to establish a Connection” really means nothing - no packet will be sent.

I believe that applications doing this over UDP will implement their own “SYN”-type signal for this - but then it’s up to the application, which, over TAPS, would need to:
- create a passive connection,
- send a Message from that same connection, i.e. using the same local address and port (I believe?) - which is a matter of configuring the local endpoint, and doable if the underlying transport system can pull it off. I think there’s nothing in our API preventing an application from doing this.

If all of the above is correct, the problem that I see is that an application may ask for unreliable transmission, and then have one case in which calling “Rendezvous” is the right thing to do, and another case in which it is not - so it would need to be able to distinguish between these two cases: is it a protocol with Rendezvous-capability, or do I have to do it myself.  Maybe, in case of UDP, the call to Rendezvous should just fail, and our text should say that Rendezvous failing would inform the application that it would have to implement it by itself, by issuing Listen and then sending a Message to the peer.

Thoughts? Am I completely off track, or is this something that needs a text fix?

Cheers,
Michael

---

From: Magnus Westerlund <[email-masked]>
Date: Mon, 27 Apr 2020 08:45:32 +0000

Hi Michael,

(This is intended as an individual contribution)

So I a bit thin on the TAPS context for your discussion her. However, the part
of the UDP rendezvous I have more familarity. 

So lets discuss this from the context of ICE (RFC 8445) and especially with the
Trickle (https://datatracker.ietf.org/doc/draft-ietf-ice-trickle/) and PAC (
https://datatracker.ietf.org/doc/draft-ietf-ice-pac/) extensions. 

You maybe also have to consider if the interface needs to cater for rendezvous
servers and such protocols, like TURN (RFC8656) or HIP. These protocols
basically moves your UDP listener port to a relay server, i.e. you suddenly have
a UDP port with a address and port beyond the local host. I note that these
ports can have some filtering behaviour on them. For example TURN needs you to
send outgoing traffic towards the address + ports from where you like to receive
traffic. 

So an easy answer would be say that this is all application land, and then I
think you are throwing away a lot of the functionality of TAPS interface. As you
basically need one or more UDP sockets with a listener and tight sending
capability. 

However, if one actually like to have an interface that tries to do the best
here. Then one maybe like to have an API with baked in ICE functionality. Thus
requesting an UDP port with rendezvous capabilities would require configuring
STUN and TURN servers. The STUN and TURN server configuration would be to learn
the hosts server reflexive candidates and relay candidates. Then generating ICE
credentials, receive peer(s) credential, and a list of candidates (potentially
empty initially). And with Trickle your list of remote peer candidates will be
dynamically updated over time. It would also require asynchronous feedback
towards the application as the layer learn more candidates for reaching the
local application. I would also note that the timeout horizon for these
operations are a minimal of approximately 40 seconds and depending on candidate
additions etc it can grow to several minutes. 

There is also the mode when one would basically prepare a UDP port for
rendezvous, which would mean gathering candidates, establish relay server
candidates etc but simply hold waiting for remote candidates and during that
time maintain these candidates which requires periodic keep-alive messages. 

So this may be way beyond what you asked for. However, normal listening UDP has
limited utility in the face of stateful firewalls. 

Cheers

Magnus Westerlund

On Sat, 2020-04-25 at 14:44 +0200, Michael Welzl wrote:
> It may be that I describe an issue that’s a non-issue. But, if I do understand
> this right, maybe that’s something which needs fixing?
> 
> Ok, here it goes…
> 
> "The Rendezvous() Action causes the Preconnection to listen on the Local
> Endpoint for an incoming Connection from the Remote Endpoint, while
> simultaneously trying to establish a Connection from the Local Endpoint to the
> Remote Endpoint. This corresponds to a TCP simultaneous open, for example.”
> 
> Now, if an application chooses unreliable data transmission, it may get SCTP
> with partial reliability, or it may (in the future) get QUIC with datagrams,
> or whatever…  and then, the protocol may be able to do what’s needed
> (something similar to a simultaneous open).
> 
> However, in case of UDP:
> I believe that applications doing this over UDP will implement their own
> “SYN”-type signal for this - but then it’s up to the application, which, over
> TAPS, would need to:
> - send a Message from that same connection, i.e. using the same local address
> and port (I believe?) - which is a matter of configuring the local endpoint,
> and doable if the underlying transport system can pull it off. I think there’s
> nothing in our API preventing an application from doing this.
> 
> If all of the above is correct, the problem that I see is that an application
> may ask for unreliable transmission, and then have one case in which calling
> “Rendezvous” is the right thing to do, and another case in which it is not -
> so it would need to be able to distinguish between these two cases: is it a
> protocol with Rendezvous-capability, or do I have to do it myself.  Maybe, in
> case of UDP, the call to Rendezvous should just fail, and our text should say
> that Rendezvous failing would inform the application that it would have to
> implement it by itself, by issuing Listen and then sending a Message to the
> peer.
> 
> _______________________________________________
> Taps mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/taps
-- 
Cheers

Magnus Westerlund 

----------------------------------------------------------------------
Networks, Ericsson Research
----------------------------------------------------------------------
Ericsson AB                 | Phone  +46 10 7148287
Torshamnsgatan 23           | Mobile +46 73 0949079
SE-164 80 Stockholm, Sweden | mailto: [email-masked]
----------------------------------------------------------------------

---

From: Colin Perkins <[email-masked]>
Date: Mon, 27 Apr 2020 11:23:02 +0100

Hi,

This is one of the areas where the drafts likely need expanding, but I think the model is:

1) application configures STUN (and maybe also TURN) local endpoints

2) application calls Resolve() on these, to find server reflexive candidates

3) application shares these candidates with its peer, via out-of-band means, and receives remote endpoint candidates from the peer

4) application adds remote endpoints for the peer’s candidates

5) application calls Rendezvous(), which performs the ICE-style probing to find a working path, based on the candidates, then returns s Connection

This relies on the Endpoint abstraction supporting STUN, which is maybe implicit in the drafts. 

Also, as usual with TAPS, the application doesn’t say that it wants UDP. It says it wants, e.g., RTP preferably over an unreliable connection, and this higher-level protocool gives the de-framer the ability to distinguish the STUN packets from the data packets.

Colin

> On 25 Apr 2020, at 13:44, Michael Welzl <[email-masked]> wrote:
> 

-- 
Colin Perkins
https://csperkins.org/

---

From: Michael Welzl <[email-masked]>
Date: Mon, 27 Apr 2020 13:38:07 +0200

Hi,

I’m answering this email, but Magnus, consider yourself answered too with a big THANK YOU for your thorough explanation!

I agree that we want some (much!) of this functionality “under the hood” if possible. And, of course, I agree that we don’t want the application to be aware of e.g. UDP specifically if we can avoid it. I think we’re all on the same page on that; but I think that, indeed, the text in the API draft needs some fixing.

Colin, thank you very much for outlining the 5 steps below: they give a very good context for this conversation. So, I answer in line below:

> On Apr 27, 2020, at 12:23 PM, Colin Perkins <[email-masked]> wrote:
> 
> Hi,
> 

These two steps are reflected in the API as it stands, and so this is already reasonably clear, I think. IMO, nothing to do here.

> 3) application shares these candidates with its peer, via out-of-band means, and receives remote endpoint candidates from the peer

Based on Magnus’ email, I suppose that this *could* be done in standard ways with trickle, and hence theoretically put “under the hood” too.
But, I think that keeping this in the application space for the sake of TAPS is quite reasonable. This step is perhaps implicit in the text, but I think it’s pretty obvious that it would need to happen. People doing rendezvous will have to be aware of this step anyway, so I see no real problem here.

> 4) application adds remote endpoints for the peer’s candidates

My first hiccup: I don’t see that this is supported in our current API - how would one add remote endpoints? I think, the way we have it, there’s only one for each Connection.
Have I overlooked something?

> 5) application calls Rendezvous(), which performs the ICE-style probing to find a working path, based on the candidates, then returns s Connection

THIS would be really good, but it’s not at all how I read the current text. It says:

My interpretation of this sentence is that Rendezvous doesn’t do much more than listen + send something, using the same local ports. For TCP, I would have implemented this functionality by just doing an active connect, using a pre-configured local port (TCP will retry sending SYNs, and if both sides issue this call with the correct ports, I guess it all works out eventually). However, from below, it seems to me that such an implementation would be doing way too little!

> This relies on the Endpoint abstraction supporting STUN, which is maybe implicit in the drafts. 

Yes - there is text there about STUN configuration, but nothing about it in the Rendezvous text (unless I missed it, apologies if I have!). And it’s not just STUN, it’s the ICE-style probing, with various candidates supplied, that’s missing.

> Also, as usual with TAPS, the application doesn’t say that it wants UDP. It says it wants, e.g., RTP preferably over an unreliable connection, and this higher-level protocool gives the de-framer th…

Okay, I understand this… but what happens if UDP *is* the protocol that is available (not e.g. RTP), and the application calls Rendezvous?
(I guess failing, with a useful error code, is the best choice, then?)

Again, just to be clear, I agree with everything you say here, and I thank you (and Magnus) again for explaining it to me!   - I just want to get the text right. Actually, I was just trying to understand how to implement this call’s functionality, and was left puzzled.

Cheers,
Michael

---

From: Colin Perkins <[email-masked]>
Date: Mon, 27 Apr 2020 12:55:40 +0100

> On 27 Apr 2020, at 12:38, Michael Welzl <[email-masked]> wrote:
> 
> Hi,
> 

This has to be done by the applications, since it will involve the signalling protocol (SIP, WebRTC, etc).

> Have I overlooked something?

They go on the Preconnection. The allow multiple remotes there.

> My interpretation of this sentence is that Rendezvous doesn’t do much more than listen + send something, using the same local ports. For TCP, I would have implemented this functionality by just doi…

It sends the STUN packets to probe connectivity. I read this as “establish a Connection”, but we maybe need to clarify the wording (or just give an additional example).

> (I guess failing, with a useful error code, is the best choice, then?)

Applications don’t use raw UDP or TCP – they always run some protocol, however trivial inside it, so there’s a framer.

Cheers,
Colin

-- 
Colin Perkins
https://csperkins.org/

---

From: Magnus Westerlund <[email-masked]>
Date: Mon, 27 Apr 2020 12:59:16 +0000

Hi,

Michael if you want an example of an API that contains the ICE related
rendezvous aspects it is the WebRTC. https://www.w3.org/TR/webrtc/

You will find the ICEserver with configuration information. Note also how
candidates are handled. I would also note the possibility to enable or disable
trickling of candidates. Which is possibly something an application could want
to indicate. I know the ICE parts is only a part of the complete WebRTC API so
there is a lot to wade through here. 

I would also note that there might be an other aspect of embedding ICE under the
hood is the potential to use this as a method to update the path used between
two peers during an ongoing communication session. There are two takes on this.
Either the more standards compliant way of triggering an ICE restart, which
involved application interaction through a new candidate exchange. 

Or the less so which is to not really terminate ICE. And instead keep your
checklist alive and continously verify what candidate pairs that actually work
and use re-occuring nomination to move the application datagram flow between
working candidate pairs (
https://datatracker.ietf.org/doc/draft-thatcher-ice-renomination/). I would
recommend avoiding the later, but wanted to mention it for completeness. 

As I haven't been active in TAPS, I would appreciate any pointers to where the
WG is in regards to the need for parameter exchange that need to be done by the
application using the TAPS interface, rather than letting the lower layers
handle that aspect. 

Cheers

Magnus

On Mon, 2020-04-27 at 13:38 +0200, Michael Welzl wrote:
> Hi,
> 
> I’m answering this email, but Magnus, consider yourself answered too with a
> big THANK YOU for your thorough explanation!
> 
> I agree that we want some (much!) of this functionality “under the hood” if
> possible. And, of course, I agree that we don’t want the application to be
> aware of e.g. UDP specifically if we can avoid it. I think we’re all on the
> same page on that; but I think that, indeed, the text in the API draft needs
> some fixing.
> 
> Colin, thank you very much for outlining the 5 steps below: they give a very
> good context for this conversation. So, I answer in line below:
> 
> These two steps are reflected in the API as it stands, and so this is already
> reasonably clear, I think. IMO, nothing to do here.
> 
> 
> > 3) application shares these candidates with its peer, via out-of-band means,
> > and receives remote endpoint candidates from the peer
> 
> Based on Magnus’ email, I suppose that this *could* be done in standard ways
> with trickle, and hence theoretically put “under the hood” too.
> But, I think that keeping this in the application space for the sake of TAPS
> is quite reasonable. This step is perhaps implicit in the text, but I think
> it’s pretty obvious that it would need to happen. People doing rendezvous will
> have to be aware of this step anyway, so I see no real problem here.
> 
> 
> My first hiccup: I don’t see that this is supported in our current API - how
> would one add remote endpoints? I think, the way we have it, there’s only one
> for each Connection.
> > 5) application calls Rendezvous(), which performs the ICE-style probing to
> > find a working path, based on the candidates, then returns s Connection
> 
> My interpretation of this sentence is that Rendezvous doesn’t do much more
> than listen + send something, using the same local ports. For TCP, I would
> have implemented this functionality by just doing an active connect, using a
> pre-configured local port (TCP will retry sending SYNs, and if both sides
> issue this call with the correct ports, I guess it all works out eventually).
> However, from below, it seems to me that such an implementation would be doing
> way too little!
> 
> 
> Yes - there is text there about STUN configuration, but nothing about it in
> the Rendezvous text (unless I missed it, apologies if I have!). And it’s not
> just STUN, it’s the ICE-style probing, with various candidates supplied,
> that’s missing.
> 
> 
> > Also, as usual with TAPS, the application doesn’t say that it wants UDP. It
> > says it wants, e.g., RTP preferably over an unreliable connection, and this
> > higher-level protocool gives the de-framer the ability to distinguish the
> > STUN packets from the data packets.
> 
> Okay, I understand this… but what happens if UDP *is* the protocol that is
> available (not e.g. RTP), and the application calls Rendezvous?
> Again, just to be clear, I agree with everything you say here, and I thank you
> (and Magnus) again for explaining it to me!   - I just want to get the text
> right. Actually, I was just trying to understand how to implement this call’s
> functionality, and was left puzzled.
> 
> Cheers,
> Michael
> 
-- 
Cheers

Magnus Westerlund

---

From: Michael Welzl <[email-masked]>
Date: Mon, 27 Apr 2020 15:43:13 +0200

> On Apr 27, 2020, at 1:55 PM, Colin Perkins <[email-masked]> wrote:
> 
> 
> 

Ok; got it.

> They go on the Preconnection. The allow multiple remotes there.

Aha!
(yes that’s allowed, I saw that text now)

> It sends the STUN packets to probe connectivity. I read this as “establish a Connection”, but we maybe need to clarify the wording (or just give an additional example).

I think so. To me, as someone who only has a faint idea of how these things work, “establish a Connection” would have been way less than that. Or, something else: AFAIK STUN is only use to the STUN server, but one wouldn’t use STUN packets to then try to connect to the peer, right? Rather SIP, or something.  VERY sorry if this is all a silly misunderstanding on my behalf, which is quite possible!

> Applications don’t use raw UDP or TCP – they always run some protocol, however trivial inside it, so there’s a framer.

… okay, so you’re saying the sender-side framer could supply that first type of message that is sent to the peer when rendezvous() is called (and the first one to expect from the peer)?  That would sound reasonable to me - but, I thought framers are associated with a Message (or, rather, its messageContext) - but we’re not sending a Message yet. Shouldn't the rendezvous() Action have optional Message and messageContext parameters, then?

> https://csperkins.org/ <https://csperkins.org/>

---

From: Michael Welzl <[email-masked]>
Date: Mon, 27 Apr 2020 15:51:04 +0200

Hi,

Thanks again!  I knew that WebRTC plays all these tricks but must admit to never having dug into the ICE details.
Regarding “where the WG is in regards to…” - I think Colin’s previous email with the 5 steps is a good summary of what TAPS is intending to do wrt. rendezvous mechanisms.

Cheers,
Michael

> On Apr 27, 2020, at 2:59 PM, Magnus Westerlund <[email-masked]> wrote:
> 
> Hi,
>