Dans notre précédent article, nous avons commencé à discuter des mécanismes régissant le protocole ICE, notamment lorsque le processus s’initie : récupération des couples adresse/port par lesquelles une extrémité sera en mesure d’émettre une requête, puis les échanger avec la partie distante jusqu’à ce que l’un comme l’autre disposent de l’ensemble des informations. Cette étape est essentielle, mais, comme dans la réalité, lorsque des candidats sont définis, il faut encore, par la suite, les élire de manière à définir lequel sera choisi au final. Ce second article sur ICE traite de ce sujet en particulier : la sélection d’un candidat au travers de tests de connectivité.

Poursuivant le procédé initié précédemment, et maintenant que chaque extrémité  est en possession de l’ensemble des candidats, tant locaux que distants, notre contrôleur (ICE-CONTROLLING) est en mesure de confectionner des paires de candidats. Ces paires mettent en relation toutes les paires locales avec toutes les paires distantes. Ces paires, tout comme les candidats qui les composent, sont ordonnées en fonction de leur priorité :

pp = 232 × MIN(X,Y) + 21 × MAX(X,Y) + (X > Y ? 1:0)

Local             Distant     Priorité

Host-X      <->   Host-Y      126 – 126

Host-X      <->   Srflx-Y     126 – 100

Host-X      <->   Relay-Y     126 –   0

Srflx-X     <->   Host-Y      100 – 126

Srflx-X     <->   Srflx-Y     100 – 100

Srflx-X     <->   Relay-Y     100 –   0

Relay-X     <->   Host-Y        0 – 126

Relay-X     <->   Srflx-Y       0 – 100

Relay-X     <->   Relay-Y       0 –   0

Puisque la priorité des paires se repose sur la priorité de leurs candidats, et que celle-ci met l’accent, par défaut, sur le type de candidat (local, réflexif, relayé), il est possible ici de représenter la priorité de la paire de la même manière. Les paires redondantes, tout comme les candidats précédemment doivent être supprimées. Toutefois, avant d’envisager cette action, les candidats server reflexive locaux doivent être modifiés : de manière à émettre à partir du candidat réflexif, notre entité émet en définitive sa requête à partir du candidat local (host) lui ayant fourni, à l’origine, le candidat réflexif. Lorsque les candidats locaux srflx ont tous été remplacés par host, il est possible de supprimer les paires redondantes, en ne gardant que celles disposant de la priorité la plus élevée.

Local             Distant     Priorité

Host-X      <->   Host-Y      126 – 126

Host-X      <->   Srflx-Y     126 – 100

Host-X      <->   Relay-Y     126 –   0

Host-X      <->   Host-Y      100 – 126

Host-X      <->   Srflx-Y     100 – 100

Host-X      <->   Relay-Y     100 –   0

Relay-X     <->   Host-Y        0 – 126

Relay-X     <->   Srflx-Y       0 – 100

Relay-X     <->   Relay-Y       0 –   0

Selon toute logique, le type relayé devraient aussi être soumis à cette même règle. Toutefois, ce type est sujet à un traitement différent, indiquant au client qu’il doit au préalable envoyer sa requête au serveur relai.

Cette liste de paires permet au contrôleur d’initier des tests de connectivité qui vont lui permettre de définir laquelle utiliser pour un flux donné. Le protocole se repose sur l’utilisation de checklist définissant quelle paire doit être testée. Ces checklists sont formées à partir de la fondation de chaque paire (soit la combinaison des fondations de ses candidats) ou, le cas échéant, à partir de l’identifiant du composant ou de la priorité. Les tests sont déclenchés périodiquement et tous les tests se soldent soit par un succès, soit par un échec, tel que le décrit schématiquement la figure ci-dessous :

Les tests de connectivités sont effectués à l’aide de requêtes STUN entre la source et le destinataire. De ce fait, chaque extrémité d’un environnement ICE est à la fois un client et un serveur STUN, capable tant d’émettre que de recevoir des requêtes binding (voir notre article au sujet du protocole STUN publié il y a quelques temps). Selon la situation, il est essentiel de considérer certains points importants :

  •  Le test des paires contenant des relayed candidates requière la création au préalable des permissions nécessaires, sans lesquelles les requêtes et réponses STUN ne seront pas transmises correctement.
  • Lorsqu’un test est envoyé, la requête STUN est complétée d’un attribut PRIORITY représentant la priorité d’un éventuel candidat peer reflexive découvert au cours du test. Un candidat peer reflexive (d’où l’intérêt de parler de server reflexive candidate pour les candidats appris via STUN) ne peut survenir que dans le cas de NAT dont l’association dépend de la destination, par exemple symétrique. En effet, puisque la destination change, l’association change aussi, créant de ce fait un nouveau candidat.

Lorsqu’un test est couronné de succès, l’autre partie effectue un test miroir (en utilisant la paire équivalente, mais inversée) afin de vérifier que la connectivité existe aussi dans ce sens. Un succès ici aussi implique que ce candidat est susceptible d’être utilisé pour la transmission du flux considéré, et un nouveau test est émis du contrôleur spécifiant cette fois-ci le paramètre USE-CANDIDATE. Un test ne peut être couronné de succès que si et seulement si les conditions suivantes sont réunies :

  • La réponse STUN contient l’attribut XOR-MAPPED-ADDRESS (réponse positive).
  • La réponse émane de la même adresse que la destination de la requête originelle.
  • La réponse est à destination de la source de la requête originelle.

Lorsqu’un test est couronné de succès, toutes les paires ayant la même fondation, si elles existent, sont placées dans la checklist en priorité : il est en effet préférable que tous les flux suivent si possible le même chemin de manière à ce qu’il n’y ait pas de décalage important entre un flux audio et vidéo par exemple. Tous les flux composant la future communication doivent, selon toute logique, trouver une paire valide leur permettant de participer à la communication. Lorsque tous les flux disposent au minimum d’une paire valide, la communication peut légitimement s’établir. Toutefois, son établissement immédiat ou différé dépend du type de finalisation implémenté :

  • Classique

La méthode classique effectue tous les tests avant de choisir une paire valide en particulier. De ce fait, le contrôleur n’enverra l’attribut USE-CANDIDATE seulement à la toute fin de la négociation, lorsque son choix se sera porté sur une paire en particulier. De ce fait, le test initial et le test miroir sont effectués normalement, mais le dernier test, comprenant l’attribut USE-CANDIDATE, ne sera envoyé que bien plus tard, alors que toutes les paires valides auront été récupérées. L’utilisation de cette méthode ajoute un certain niveau de latence dans l’établissement de la communication, mais s’assure que les flux seront stables.

  • Agressive

La méthode agressive, au contraire, transmet immédiatement l’attribut USE-CANDIDATE dès qu’un test est émis. De ce fait, le moindre succès, et si tous les flux disposent de leur paire, la communication s’établit. Toutefois, ICE poursuit ses tests de connectivités, dans l’hypothèse où une meilleure connectivité est trouvée. De ce fait, si la latence est drastiquement réduite, il n’est pas impossible que des flux « sautent », le temps pour ICE de trouver le candidat idéal et de passer de l’un à l’autre.

Cette notion de latence a une importance particulière au sein de ICE. La grande quantité de message et de requête laisse en effet à penser que l’établissement de la connexion entre deux entités prend un temps considérable. Certes, le temps d’attente est rallongé, mais les réseaux actuels sont normalement tous en mesure de fournir une qualité de service suffisante afin de réduire cette sensation au maximum. De manière à ce que l’expérience utilisateur ne s’en ressente pas trop, toutefois, ICE prévoit de modifier légèrement le comportement « classique » des applications (SIP, par exemple).

En règle générale, lorsqu’un INVITE est reçu, le client émet immédiatement un 180 Ringing afin de signifier qu’il sonne. ICE se sert de ce message pour transmettre les candidats de la destination à la source. Malheureusement dans ce cas, les tests de connectivités viennent à peine de s’initier. Dès lors, si la destination décroche rapidement, il est parfaitement envisageable qu’aucun flux ne soit en mesure de transiter… Dès lors, ICE suggère que les candidats de la destination soient transmis au travers d’un message provisionnel (100 Trying par exemple), autorisant les tests de connectivités à s’effectuer avant que le téléphone ne sonne. De ce fait, la connexion avec la destination est établie avant même que le téléphone ne sonne à la destination, réduisant du même fait l’impression de latence, tout du moins au niveau du destinataire.

Il est intéressant de noter que toutes les paires validées ne seront pas systématiquement utilisées. Dès lors il convient, si possible, de libérer les ressources. Si les candidats locaux sont facilement libérés (cela se fait au niveau du système), les associations des NAT seront automatiquement supprimées après une certaine période (dépendante du NAT). Dans le cas des candidats relayés, ils peuvent être facilement supprimés à l’aide la méthode refresh du protocole TURN.

Toutes les considérations abordées jusqu’à maintenant ne concernent que des cas relativement simples (deux terminaux souhaitant communiquer entre eux). Que se passe-t-il par contre lorsqu’une tierce partie est concernée aussi, comme pas exemple du contrôle d’appel (3rd party call control) ? Ce cas de figure ne pose aucun problème en soi : ICE devra simplement négocier les candidats indépendamment pour chacune des parties, afin de négocier dans les deux cas les flux multimédias : nouveau processus complet, et nouveaux identifiants pour la session (notamment ufrag et upwd pour l’authentification des tests de connectivité). Au regard de la latence apportée par ICE, il paraît logique, et l’IETF le recommande, d’initier le processus le plus rapidement possible : dès que la nouvelle destination est connue, la récupération des candidats doit être initié.

 

ICE est un protocole complexe, imbriquant de nombreux protocoles (STUN/TURN notamment) afin de lui permettre de fonctionner proprement. Cette complexité, mais aussi et surtout l’impact que ce protocole peut avoir sur les solutions « industrielles » (ALG/SBC notamment), rebute bien souvent le déploiement de ICE. Des implémentations existent toutefois, majoritairement sous la forme de softphones (par exemple Blink ou les solutions CounterPath). Il n’en demeure pas moins que l’infrastructure sous-jacente (serveurs STUN/TURN, serveurs DNS, …) n’est actuellement pas prête à accepter une telle solution. La sera-t-elle un jour ? Seul l’avenir nous le dira, mais les fonctionnalités offertes par ICE sont réellement intéressantes puisqu’elles permettent une traversée des NAT réellement efficace quel que soit le cas de figure considéré, ce qui tend à considérer ICE comme le Saint Graal des solutions de traversée de NAT (ou aussi la Grand Unified Theory of NAT Traversal).

Nous venons de terminer le traitement des solutions normalisées de traversée de NAT. Plusieurs fois, toutefois, nous avons mentionné des solutions « industrielles », sous la forme d’ALG/SBC ou d’approches empiriques. Les prochains articles dans notre série NAT Traversal traiteront successivement de ces deux points qui, bien que non standardisées, apportent eux-aussi leur part d’intérêt !