Forgetting ICE candidates

🎻 December 2021 fiddle

Want to learn more about WebRTC?

Look us up here for the WebRTC Fiddle of the Month, created… once a month.

Or just enroll to one of our excellent WebRTC training courses.



Tsahi: Hi, and welcome to the WebRTC Fiddle of the month. And this time we’re going to talk about what happens when you forget your ICE candidates. Philipp, take it away.

Philipp: Yes, let me share. So we have a very simple fiddle again. We have a couple of checkboxes for ignoring ICE candidates in one of the two directions, you can also ignore them in both directions, but that is a bad idea. We have a start button and we have a state display. And here we have two connections, we listen for the on ICE connections state change event and update the state field in the fiddle in the display with the text of the ICE connection state. Nothing fancy so far. Then we have async function, which does the negotiation and sets the on ICE candidate handlers. And we want to ignore them on purpose sometimes, for the sake of the fiddle.

Tsahi: So this means that an ICE candidate will arrive. But what we’re going to do is just ignore it and not add it locally.

Philipp: Yes, add it to the other side. 

Tsahi: Add it to the other side, sorry. So it’s not arriving and ICE candidate was created locally. And we either don’t end up sending it to the other side or the other side ignores taking that candidate and adding it as a candidate. So what we see here, for example, is if peer connection one has an on ICE candidate, a new candidate, we are either going to put it or not put it on peer connection number two. Okay.

Philipp: Yes. And that is easy to forget. And the surprising thing about forgetting that is that even if you forget it, it still works. But we’re going to talk about that in a minute. 

Tsahi: Okay. 

Philipp: So we’re doing a data channel only connection, so we don’t need to get the microphone or the camera, but the principle applies also to audio-video connections. Then we call the fancy new setLocalDescription() without calling create offer before, that’s the implicit offer creation. We cut onset description with the local description of the first connection and same game on the other side. 

Tsahi: Okay. 

Philipp: And then we have a function that gets called when the start button is clicked, the start button is over here. And disable these two checkboxes and then run the connection. 

Tsahi: Okay.

Philipp: So quite easy so far. And normally, you assume everything is going well, you don’t ignore any ICE candidates. And that means the connection will get connected almost instantly. 

Tsahi: Yes. 

Philipp: So let’s ignore it on those ends. What will happen? Nothing, because there are no checks and you forgot all the ICE candidates and nothing happens. And that is pretty easy to see that this is wrong. But what if we do this? We ignore the candidates from PC one to PC 2, click start, and it still gets connected to the same way. Now the question is, why does it get connected the same. And for that, we need to go to web proxy internals. And I’m using Chrome 98. And a nice feature to that it’s the ICE candidate grid. You can also see the selected candidate pair is marked bold, including the local and remote candidates. And this ICE candidate grid shows you everything that is basically local and remote in terms of candidates. And this one does not look right. Let me run again. So let’s ignore — Okay, let me do that, again. Go to webrtc-internals. And we’re looking at the second connection to which we did not add the candidate. 

Tsahi: Okay.

Philipp: So you can see as candidate events here, but now add ICE candidate, and you have set add ICE candidate on this connection, but not on the second one. So what happens if we look at the candidate grid? We see it still has a remote candidate, but it’s not a host candidate. It’s a pair reflexive candidate. And you can see the address of some candidate is hidden because there are some of these nasty WebRTC IP leaks. And rather than giving you that address here, the browser simply sets the field to an empty string. And the way that works is that if the other side is local and can send you a UDP packet to your local candidate, that is okay and enables the connection.

Tsahi: And that is done using mDNS?

Philipp: In this case, it’s done using IP addresses, because I’ve previously allowed the camera access for jsFiddle. But it could as well be happening with mDNS in this place.

Tsahi: Okay. And if we do that remotely by two participants?

Philipp: Then it will not work because, okay, you have my local IP address, but you cannot send it to me, there’s nothing arriving on my local port. So the connection won’t work. It will get blocked either by the NAT traversal device, NAT device, or by the turn permission mechanism.

Tsahi: So I guess the things go wrong, and things don’t connect for you there, and it happens only when you move the let’s say application, the demo, the MVP, the POC, whatever you want to call it, from your local network to something remote, then things that you need to check is first of all, well, you actually have STUN and TURN servers in the configuration, which we don’t have here. But you also need to check that all of the on ICE candidates that you’re receiving on one end are actually sent and received on the other end and cause an add ICE candidate on that peer. You want to actually have these things being able to negotiate between them.

Philipp: Yes.

Tsahi: Okay. So thank you. And we’ll see you all in our next fiddle next month.

Philipp: See you then.