18. Transport (Transport)
La couche transport est responsable de la transmission réelle des requêtes et des réponses sur les transports réseau. Cela inclut la détermination de la connexion à utiliser pour une requête ou une réponse dans le cas des transports orientés connexion.
La couche transport est responsable de la gestion des connexions persistantes pour les protocoles de transport comme TCP et SCTP, ou TLS par-dessus, y compris celles ouvertes à la couche transport. Cela inclut les connexions ouvertes par les transports client ou serveur, de sorte que les connexions sont partagées entre les fonctions de transport client et serveur. Ces connexions sont indexées par le tuple formé de l'adresse, du port et du protocole de transport à l'extrémité opposée de la connexion. Lorsqu'une connexion est ouverte par la couche transport, cet index est défini sur l'IP de destination, le port et le transport. Lorsque la connexion est acceptée par la couche transport, cet index est défini sur l'adresse IP source, le numéro de port et le transport. Notez que, comme le port source est souvent éphémère, mais qu'il ne peut pas être su s'il est éphémère ou sélectionné via les procédures de [4], les connexions acceptées par la couche transport ne seront fréquemment pas réutilisées. Il en résulte que deux proxies dans une relation de « peering » utilisant un transport orienté connexion auront fréquemment deux connexions en cours d'utilisation, une pour les transactions initiées dans chaque direction.
Il est RECOMMANDÉ de garder les connexions ouvertes pendant une durée définie par l'implémentation après le dernier message envoyé ou reçu sur cette connexion. Cette durée DEVRAIT au moins égaler la plus longue durée dont l'élément aurait besoin pour amener une transaction de son instanciation à l'état terminé. Cela afin de rendre probable l'achèvement des transactions sur la même connexion sur laquelle elles sont initiées (par exemple, requête, réponse, et dans le cas d'un INVITE, ACK pour les réponses non 2xx). Cela signifie généralement au moins 64*T1 (voir la section 17.1.1.1 pour une définition de T1). Cependant, cela pourrait être plus grand dans un élément dont le TU utilise une grande valeur pour le timer C (point 11 de la section 16.6), par exemple.
Tous les éléments SIP DOIVENT implémenter UDP et TCP. Les éléments SIP PEUVENT implémenter d'autres protocoles.
Rendre TCP obligatoire pour le UA est un changement substantiel par rapport à la RFC 2543. Il est né de la nécessité de gérer des messages plus grands, qui DOIVENT utiliser TCP, comme discuté ci-dessous. Ainsi, même si un élément n'envoie jamais de grands messages, il peut en recevoir un et doit être capable de les gérer.
18.1 Clients (Clients)
18.1.1 Envoi des requêtes (Sending Requests)
Le côté client de la couche transport est responsable de l'envoi de la requête et de la réception des réponses. L'utilisateur de la couche transport passe au transport client la requête, une adresse IP, un port, un transport, et éventuellement un TTL pour les destinations multicast.
Si une requête est à moins de 200 octets du MTU du chemin, ou si elle est plus grande que 1300 octets et que le MTU du chemin est inconnu, la requête DOIT être envoyée en utilisant un protocole de transport à contrôle de congestion RFC 2914 [43], tel que TCP. Si cela provoque un changement de protocole de transport par rapport à celui indiqué dans le Via le plus haut, la valeur dans le Via le plus haut DOIT être modifiée. Cela évite la fragmentation des messages sur UDP et fournit un contrôle de congestion pour les messages plus grands. Cependant, les implémentations DOIVENT être capables de gérer des messages jusqu'à la taille maximale de paquet datagramme. Pour UDP, cette taille est de 65 535 octets, en incluant les en-têtes IP et UDP.
La « marge » de 200 octets entre la taille du message et le MTU prend en compte le fait que la réponse en SIP peut être plus grande que la requête. Cela se produit en raison de l'ajout de valeurs de champ Record-Route aux réponses aux INVITE, par exemple. Avec la marge supplémentaire, la réponse peut être environ 170 octets plus grande que la requête, et toujours ne pas être fragmentée en IPv4 (environ 30 octets sont consommés par IP/UDP, en supposant aucun IPSec). 1300 est choisi quand le MTU du chemin n'est pas connu, sur la base de l'hypothèse d'un MTU Ethernet de 1500 octets.
Si un élément envoie une requête via TCP à cause de ces contraintes de taille de message, et que cette requête aurait autrement été envoyée via UDP, si la tentative d'établissement de la connexion génère soit un ICMP Protocol Not Supported, soit aboutit à une réinitialisation TCP, l'élément DEVRAIT réessayer la requête en utilisant UDP. Cela est uniquement pour fournir une compatibilité descendante avec les implémentations conformes à la RFC 2543 qui ne prennent pas en charge TCP. Il est anticipé que ce comportement sera déprécié dans une future révision de cette spécification.
Un client qui envoie une requête à une adresse multicast DOIT ajouter le paramètre « maddr » à sa valeur de champ Via contenant l'adresse multicast de destination, et pour IPv4, DEVRAIT ajouter le paramètre « ttl » avec une valeur de 1. L'utilisation du multicast IPv6 n'est pas définie dans cette spécification, et sera l'objet d'une future normalisation lorsque le besoin se présentera.
Ces règles entraînent une limitation délibérée du multicast en SIP. Sa fonction principale est de fournir un service de type « découverte à saut unique », livrant une requête à un groupe de serveurs homogènes, où il n'est requis de traiter la réponse que de l'un quelconque d'entre eux. Cette fonctionnalité est particulièrement utile pour les enregistrements. En fait, sur la base des règles de traitement des transactions de la section 17.1.3, la transaction client acceptera la première réponse, et considérera les autres comme des retransmissions car elles contiennent toutes le même identifiant de branche Via.
Avant qu'une requête soit envoyée, le transport client DOIT insérer une valeur du champ « sent-by » dans le champ d'en-tête Via. Ce champ contient une adresse IP ou un nom d'hôte, et un port. L'usage d'un FQDN est RECOMMANDÉ. Ce champ est utilisé pour envoyer des réponses sous certaines conditions, décrites ci-dessous. Si le port est absent, la valeur par défaut dépend du transport. Elle est de 5060 pour UDP, TCP et SCTP, et de 5061 pour TLS.
Pour les transports fiables, la réponse est normalement envoyée sur la connexion sur laquelle la requête a été reçue. Par conséquent, le transport client DOIT être prêt à recevoir la réponse sur la même connexion utilisée pour envoyer la requête. Dans des conditions d'erreur, le serveur peut tenter d'ouvrir une nouvelle connexion pour envoyer la réponse. Pour gérer ce cas, la couche transport DOIT également être prête à recevoir une connexion entrante sur l'adresse IP source depuis laquelle la requête a été envoyée et le numéro de port dans le champ « sent-by ». Elle DOIT également être prête à recevoir des connexions entrantes sur toute adresse et tout port qui seraient sélectionnés par un serveur sur la base des procédures décrites à la section 5 de [4].
Pour les transports unicast non fiables, le transport client DOIT être prêt à recevoir des réponses sur l'adresse IP source depuis laquelle la requête est envoyée (les réponses étant renvoyées à l'adresse source) et le numéro de port dans le champ « sent-by ». De plus, comme avec les transports fiables, dans certains cas la réponse sera envoyée ailleurs. Le client DOIT être prêt à recevoir des réponses sur toute adresse et tout port qui seraient sélectionnés par un serveur sur la base des procédures décrites à la section 5 de [4].
Pour le multicast, le transport client DOIT être prêt à recevoir des réponses sur le même groupe multicast et port vers lesquels la requête est envoyée (c'est-à-dire qu'il doit être membre du groupe multicast vers lequel il a envoyé la requête).
Si une requête est destinée à une adresse IP, un port et un transport pour lesquels une connexion existante est ouverte, il est RECOMMANDÉ d'utiliser cette connexion pour envoyer la requête, mais une autre connexion PEUT être ouverte et utilisée.
Si une requête est envoyée en utilisant le multicast, elle est envoyée au groupe d'adresses, au port et au TTL fournis par l'utilisateur de transport. Si une requête est envoyée en utilisant des transports unicast non fiables, elle est envoyée à l'adresse IP et au port fournis par l'utilisateur de transport.
18.1.2 Réception des réponses (Receiving Responses)
Lorsqu'une réponse est reçue, le transport client examine la valeur du champ d'en-tête Via le plus haut. Si la valeur du paramètre « sent-by » dans cette valeur de champ d'en-tête ne correspond pas à une valeur que le transport client est configuré pour insérer dans les requêtes, la réponse DOIT être ignorée silencieusement.
S'il existe des transactions client, le transport client utilise les procédures de correspondance de la section 17.1.3 pour tenter de faire correspondre la réponse à une transaction existante. S'il y a correspondance, la réponse DOIT être passée à cette transaction. Sinon, la réponse DOIT être passée au cœur (qu'il s'agisse d'un proxy sans état, d'un proxy avec état ou d'un UA) pour traitement ultérieur. La gestion de ces réponses « égarées » dépend du cœur (un proxy les transférera, alors qu'un UA les ignorera, par exemple).
18.2 Serveurs (Servers)
18.2.1 Réception des requêtes (Receiving Requests)
Un serveur DOIT être prêt à recevoir des requêtes sur toute combinaison d'adresse IP, de port et de transport qui peut résulter d'une recherche DNS sur un URI SIP ou SIPS [4] remis pour les besoins de la communication avec ce serveur. Dans ce contexte, « remettre » inclut le placement d'un URI dans un champ d'en-tête Contact dans une requête REGISTER ou une réponse de redirection, ou dans un champ d'en-tête Record-Route dans une requête ou une réponse. Un URI peut également être « remis » en le plaçant sur une page web ou une carte de visite. Il est également RECOMMANDÉ qu'un serveur écoute les requêtes sur les ports SIP par défaut (5060 pour TCP et UDP, 5061 pour TLS sur TCP) sur toutes les interfaces publiques. L'exception typique serait les réseaux privés, ou lorsque plusieurs instances de serveur s'exécutent sur le même hôte. Pour tout port et toute interface sur lequel un serveur écoute en UDP, il DOIT écouter sur ce même port et cette même interface en TCP. Cela est dû au fait qu'un message peut devoir être envoyé en utilisant TCP plutôt que UDP s'il est trop volumineux. Par conséquent, la réciproque n'est pas vraie. Un serveur n'a pas besoin d'écouter en UDP sur une adresse et un port particuliers simplement parce qu'il écoute sur cette même adresse et ce même port en TCP. Il peut bien sûr y avoir d'autres raisons pour lesquelles un serveur a besoin d'écouter en UDP sur une adresse et un port particuliers.
Lorsque le transport serveur reçoit une requête sur n'importe quel transport, il DOIT examiner la valeur du paramètre « sent-by » dans la valeur de champ d'en-tête Via la plus haute. Si la partie hôte du paramètre « sent-by » contient un nom de domaine, ou si elle contient une adresse IP différente de l'adresse source du paquet, le serveur DOIT ajouter un paramètre « received » à cette valeur de champ d'en-tête Via. Ce paramètre DOIT contenir l'adresse source à partir de laquelle le paquet a été reçu. Cela afin d'aider la couche transport serveur à envoyer la réponse, puisqu'elle doit être envoyée à l'adresse IP source à partir de laquelle la requête est arrivée.
Considérons une requête reçue par le transport serveur qui ressemble, en partie, à :
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP bobspc.biloxi.com:5060
La requête est reçue avec une adresse IP source de 192.0.2.4. Avant de transmettre la requête vers le haut, le transport ajoute un paramètre « received », de sorte que la requête ressemblerait, en partie, à :
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP bobspc.biloxi.com:5060;received=192.0.2.4
Ensuite, le transport serveur tente de faire correspondre la requête à une transaction serveur. Il le fait en utilisant les règles de correspondance décrites à la section 17.2.3. Si une transaction serveur correspondante est trouvée, la requête est passée à cette transaction pour traitement. Si aucune correspondance n'est trouvée, la requête est passée au cœur, qui peut décider de construire une nouvelle transaction serveur pour cette requête. Notez que lorsqu'un cœur UAS envoie une réponse 2xx à un INVITE, la transaction serveur est détruite. Cela signifie que lorsque le ACK arrive, il n'y aura aucune transaction serveur correspondante, et sur la base de cette règle, le ACK est passé au cœur UAS, où il est traité.
18.2.2 Envoi des réponses (Sending Responses)
Le transport serveur utilise la valeur du champ d'en-tête Via le plus haut afin de déterminer où envoyer une réponse. Il DOIT suivre le processus suivant :
o Si le « sent-protocol » est un protocole de transport fiable tel que TCP ou SCTP, ou TLS par-dessus, la réponse DOIT être envoyée en utilisant la connexion existante vers la source de la requête originale ayant créé la transaction, si cette connexion est toujours ouverte. Cela nécessite que le transport serveur maintienne une association entre les transactions serveur et les connexions de transport. Si cette connexion n'est plus ouverte, le serveur DOIT ouvrir une connexion vers l'adresse IP dans le paramètre « received », si présent, en utilisant le port dans la valeur « sent-by », ou le port par défaut pour ce transport si aucun port n'est spécifié. Si cette tentative de connexion échoue, le serveur DOIT utiliser les procédures de [4] pour les serveurs afin de déterminer l'adresse IP et le port pour ouvrir la connexion et envoyer la réponse.
o Sinon, si la valeur de champ d'en-tête Via contient un paramètre « maddr », la réponse DOIT être transférée à l'adresse listée là, en utilisant le port indiqué dans « sent-by », ou le port 5060 si aucun n'est présent. Si l'adresse est une adresse multicast, la réponse DOIT être envoyée en utilisant le TTL indiqué dans le paramètre « ttl », ou avec un TTL de 1 si ce paramètre n'est pas présent.
o Sinon (pour les transports unicast non fiables), si le Via le plus haut a un paramètre « received », la réponse DOIT être envoyée à l'adresse dans le paramètre « received », en utilisant le port indiqué dans la valeur « sent-by », ou en utilisant le port 5060 si aucun n'est spécifié explicitement. Si cela échoue, par exemple en provoquant une réponse ICMP « port unreachable », les procédures de la section 5 de [4] DOIVENT être utilisées pour déterminer où envoyer la réponse.
o Sinon, si elle n'est pas marquée par le récepteur, la réponse DOIT être envoyée à l'adresse indiquée par la valeur « sent-by », en utilisant les procédures de la section 5 de [4].
18.3 Trame (Framing)
Dans le cas des transports orientés message (tels qu'UDP), si le message possède un champ d'en-tête Content-Length, le corps du message est supposé contenir ce nombre d'octets. S'il y a des octets supplémentaires dans le paquet de transport au-delà de la fin du corps, ils DOIVENT être ignorés. Si le paquet de transport se termine avant la fin du corps du message, cela est considéré comme une erreur. Si le message est une réponse, il DOIT être ignoré. Si le message est une requête, l'élément DOIT générer une réponse 400 (Bad Request). Si le message ne possède pas de champ d'en-tête Content-Length, le corps du message est supposé se terminer à la fin du paquet de transport.
Dans le cas des transports orientés flux tels que TCP, le champ d'en-tête Content-Length indique la taille du corps. Le champ d'en-tête Content-Length DOIT être utilisé avec les transports orientés flux.
18.4 Gestion des erreurs (Error Handling)
La gestion des erreurs est indépendante du fait que le message était une requête ou une réponse.
Si l'utilisateur de transport demande l'envoi d'un message sur un transport non fiable, et que le résultat est une erreur ICMP, le comportement dépend du type d'erreur ICMP. Les erreurs host, network, port ou protocol unreachable, ou les erreurs parameter problem DOIVENT amener la couche transport à informer l'utilisateur de transport d'un échec d'envoi. Les erreurs ICMP source quench et TTL exceeded DOIVENT être ignorées.
Si l'utilisateur de transport demande l'envoi d'une requête sur un transport fiable, et que le résultat est un échec de connexion, la couche transport DOIT informer l'utilisateur de transport d'un échec d'envoi.