Cette traduction a été générée par apprentissage automatique et peut ne pas être exacte à 100%. Voir la version anglaise

Protocole de la ferme à l'ail

Proposal 150
Ouvert
Author zzz
Created 2019-05-02
Last Updated 2019-05-20

Aperçu

Il s’agit de la spécification du protocole réseau Garlic Farm, basée sur JRaft, son code « exts » pour l’implémentation sur TCP, et son exemple d’application « dmprinter » JRAFT .

Nous n’avons pas pu trouver d’implémentation avec un protocole réseau documenté. Cependant, l’implémentation JRaft est suffisamment simple pour que nous puissions examiner le code puis documenter son protocole. Cette proposition est le résultat de cet effort.

Cela servira de backend pour la coordination des routeurs publiant des entrées dans un Meta LeaseSet. Voir la proposition 123.

Objectifs

  • Petite taille de code
  • Basé sur une implémentation existante
  • Pas d’objets Java sérialisés ni de fonctionnalités ou encodage spécifiques à Java
  • Toute amorce (bootstrapping) est hors sujet. On suppose qu’au moins un autre serveur est codé en dur ou configuré en dehors de ce protocole.
  • Prendre en charge les cas d’utilisation hors bande et dans I2P.

Conception

Le protocole Raft n’est pas un protocole concret ; il définit uniquement une machine à états. Par conséquent, nous documentons le protocole concret de JRaft et nous basons notre protocole dessus. Aucun changement n’est apporté au protocole JRaft, hormis l’ajout d’une poignée de main d’authentification.

Raft élit un Leader dont le rôle est de publier un journal (log). Le journal contient des données de configuration Raft et des données applicatives. Les données applicatives contiennent l’état du routeur de chaque serveur et la Destination du cluster Meta LS2. Les serveurs utilisent un algorithme commun pour déterminer l’éditeur et le contenu du Meta LS2. L’éditeur du Meta LS2 n’est PAS nécessairement le Leader Raft.

Spécification

Le protocole réseau fonctionne sur des sockets SSL ou des sockets I2P non chiffrés. Les sockets I2P sont relayés via le proxy HTTP. Il n’y a aucun support pour les sockets claires non SSL.

Poignée de main et authentification

Non défini par JRaft.

Objectifs :

  • Méthode d’authentification utilisateur/mot de passe
  • Identifiant de version
  • Identifiant de cluster
  • Extensible
  • Facilité de relais lorsqu’utilisé pour les sockets I2P
  • Ne pas exposer inutilement le serveur comme serveur Garlic Farm
  • Protocole simple afin qu’une implémentation complète de serveur web ne soit pas requise
  • Compatible avec les standards courants, afin que les implémentations puissent utiliser des bibliothèques standard si désiré

Nous utiliserons une poignée de main similaire à WebSocket et l’authentification HTTP Digest RFC 2617 . L’authentification Basic de la RFC 2617 n’est PAS prise en charge. Lors du relais via le proxy HTTP, communiquer avec le proxy comme spécifié dans RFC 2616 .

Identifiants

Le fait que les noms d’utilisateur et mots de passe soient par cluster ou par serveur dépend de l’implémentation.

Requête HTTP 1

L’initiateur enverra ce qui suit.

Toutes les lignes se terminent par CRLF comme exigé par HTTP.


GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
  Host: (ip):(port)
  Cache-Control: no-cache
  Connection: close
  (tout autre en-tête ignoré)
  (ligne vide)

  CLUSTER est le nom du cluster (par défaut "farm")
  VERSION est la version Garlic Farm (actuellement "1")

Réponse HTTP 1

Si le chemin n’est pas correct, le destinataire enverra une réponse standard « HTTP/1.1 404 Not Found », comme dans RFC 2616 .

Si le chemin est correct, le destinataire enverra une réponse standard « HTTP/1.1 401 Unauthorized », incluant l’en-tête WWW-Authenticate d’authentification Digest HTTP, comme dans RFC 2617 .

Les deux parties fermeront ensuite la socket.

Requête HTTP 2

L’initiateur enverra ce qui suit, comme dans RFC 2617 .

Toutes les lignes se terminent par CRLF comme exigé par HTTP.


GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
  Host: (ip):(port)
  Cache-Control: no-cache
  Connection: keep-alive, Upgrade
  Upgrade: websocket
  (en-têtes Sec-Websocket-* si relayé)
  Authorization: (en-tête d'autorisation Digest HTTP comme dans RFC 2617)
  (tout autre en-tête ignoré)
  (ligne vide)

  CLUSTER est le nom du cluster (par défaut "farm")
  VERSION est la version Garlic Farm (actuellement "1")

Réponse HTTP 2

Si l’authentification n’est pas correcte, le destinataire enverra une autre réponse standard « HTTP/1.1 401 Unauthorized », comme dans RFC 2617 .

Si l’authentification est correcte, le destinataire enverra la réponse suivante, comme dans le protocole WebSocket.

Toutes les lignes se terminent par CRLF comme exigé par HTTP.


HTTP/1.1 101 Switching Protocols
  Connection: Upgrade
  Upgrade: websocket
  (en-têtes Sec-Websocket-*)
  (tout autre en-tête ignoré)
  (ligne vide)

Après réception de ceci, la socket reste ouverte. Le protocole Raft tel que défini ci-dessous commence, sur la même socket.

Mise en cache

Les identifiants doivent être mis en cache pendant au moins une heure, afin que les connexions ultérieures puissent passer directement à « Requête HTTP 2 » ci-dessus.

Types de messages

Il existe deux types de messages : requêtes et réponses. Les requêtes peuvent contenir des entrées de journal (Log Entries) et sont de taille variable ; les réponses ne contiennent pas d’entrées de journal et sont de taille fixe.

Les types de messages 1 à 4 sont les messages RPC standards définis par Raft. Il s’agit du protocole Raft de base.

Les types de messages 5 à 15 sont les messages RPC étendus définis par JRaft, pour prendre en charge les clients, les modifications dynamiques de serveurs et la synchronisation efficace des journaux.

Les types de messages 16 à 17 sont les messages RPC de compactage du journal définis dans la section 7 de Raft.

MessageNuméroEnvoyé parEnvoyé àNotes
RequestVoteRequest1CandidatSuiveurRPC standard Raft ; ne doit pas contenir d’entrées de journal
RequestVoteResponse2SuiveurCandidatRPC standard Raft
AppendEntriesRequest3LeaderSuiveurRPC standard Raft
AppendEntriesResponse4SuiveurLeader / ClientRPC standard Raft
ClientRequest5ClientLeader / SuiveurRéponse est AppendEntriesResponse ; ne doit contenir que des entrées de journal applicatives
AddServerRequest6ClientLeaderNe doit contenir qu’une seule entrée de journal ClusterServer
AddServerResponse7LeaderClientLe leader enverra également une JoinClusterRequest
RemoveServerRequest8SuiveurLeaderNe doit contenir qu’une seule entrée de journal ClusterServer
RemoveServerResponse9LeaderSuiveur
SyncLogRequest10LeaderSuiveurNe doit contenir qu’une seule entrée de journal LogPack
SyncLogResponse11SuiveurLeader
JoinClusterRequest12LeaderNouveau serveurInvitation à rejoindre ; ne doit contenir qu’une seule entrée de journal Configuration
JoinClusterResponse13Nouveau serveurLeader
LeaveClusterRequest14LeaderSuiveurCommande pour quitter
LeaveClusterResponse15SuiveurLeader
InstallSnapshotRequest16LeaderSuiveurSection 7 Raft ; ne doit contenir qu’une seule entrée de journal SnapshotSyncRequest
InstallSnapshotResponse17SuiveurLeaderSection 7 Raft

Établissement

Après la poignée de main HTTP, la séquence d’établissement est la suivante :


Nouveau serveur Alice              Suiveur aléatoire Bob

  ClientRequest   ------->
          <---------   AppendEntriesResponse

  Si Bob indique qu'il est le leader, continuer comme ci-dessous.
  Sinon, Alice doit se déconnecter de Bob et se connecter au leader.


  Nouveau serveur Alice              Leader Charlie

  ClientRequest   ------->
          <---------   AppendEntriesResponse
  AddServerRequest   ------->
          <---------   AddServerResponse
          <---------   JoinClusterRequest
  JoinClusterResponse  ------->
          <---------   SyncLogRequest
                       OU InstallSnapshotRequest
  SyncLogResponse  ------->
  OU InstallSnapshotResponse

Séquence de déconnexion :


Suiveur Alice              Leader Charlie

  RemoveServerRequest   ------->
          <---------   RemoveServerResponse
          <---------   LeaveClusterRequest
  LeaveClusterResponse  ------->

Séquence d’élection :


Candidat Alice               Suiveur Bob

  RequestVoteRequest   ------->
          <---------   RequestVoteResponse

  si Alice gagne l'élection :

  Leader Alice                Suiveur Bob

  AppendEntriesRequest   ------->
  (heartbeat)
          <---------   AppendEntriesResponse

Définitions

  • Source : Identifie l’origine du message
  • Destination : Identifie le destinataire du message
  • Termes : Voir Raft. Initialisés à 0, augmentent de façon monotone
  • Index : Voir Raft. Initialisés à 0, augmentent de façon monotone

Requêtes

Les requêtes contiennent un en-tête et zéro ou plusieurs entrées de journal. Les requêtes contiennent un en-tête de taille fixe et des entrées de journal facultatives de taille variable.

En-tête de requête

L’en-tête de requête fait 45 octets, comme suit. Toutes les valeurs sont non signées, big-endian.


Type de message :      1 octet
  Source :            ID, entier sur 4 octets
  Destination :       ID, entier sur 4 octets
  Term :              Terme actuel (voir notes), entier sur 8 octets
  Last Log Term :     8 octets
  Last Log Index :    8 octets
  Commit Index :      8 octets
  Taille des entrées de journal :  Taille totale en octets, entier sur 4 octets
  Entrées de journal :       voir ci-dessous, longueur totale spécifiée

Notes

Dans RequestVoteRequest, Term est le terme du candidat. Sinon, c’est le terme actuel du leader.

Dans AppendEntriesRequest, lorsque la taille des entrées de journal est nulle, ce message est un message de heartbeat (keepalive).

Entrées de journal

Le journal contient zéro ou plusieurs entrées. Chaque entrée de journal est comme suit. Toutes les valeurs sont non signées, big-endian.


Term :           8 octets
  Type de valeur :     1 octet
  Taille de l'entrée :     En octets, entier sur 4 octets
  Entrée :          longueur spécifiée

Contenu du journal

Toutes les valeurs sont non signées, big-endian.

Type de valeur du journalNuméro
Application1
Configuration2
ClusterServer3
LogPack4
SnapshotSyncRequest5

Application

Le contenu applicatif est encodé en UTF-8 JSON . Voir la section Couche applicative ci-dessous.

Configuration

Utilisé par le leader pour sérialiser une nouvelle configuration de cluster et la répliquer aux pairs. Contient zéro ou plusieurs configurations ClusterServer.


Log Index :  8 octets
  Last Log Index :  8 octets
  Données ClusterServer pour chaque serveur :
    ID :                4 octets
    Longueur des données d'endpoint : En octets, 4 octets
    Données d'endpoint :     Chaîne ASCII de la forme "tcp://localhost:9001", longueur spécifiée

ClusterServer

Les informations de configuration d’un serveur dans un cluster. Inclus uniquement dans un message AddServerRequest ou RemoveServerRequest.

Utilisé dans un message AddServerRequest :


ID :                4 octets
  Longueur des données d'endpoint : En octets, 4 octets
  Données d'endpoint :     Chaîne ASCII de la forme "tcp://localhost:9001", longueur spécifiée

Utilisé dans un message RemoveServerRequest :


ID :                4 octets

LogPack

Inclus uniquement dans un message SyncLogRequest.

Le suivant est compressé avec gzip avant transmission :


Longueur des données d'index : En octets, 4 octets
  Longueur des données de journal :   En octets, 4 octets
  Données d'index :     8 octets pour chaque index, longueur spécifiée
  Données de journal :       longueur spécifiée

SnapshotSyncRequest

Inclus uniquement dans un message InstallSnapshotRequest.


Last Log Index :  8 octets
  Last Log Term :   8 octets
  Longueur des données de configuration : En octets, 4 octets
  Données de configuration :     longueur spécifiée
  Offset :          L'offset des données dans la base, en octets, 8 octets
  Longueur des données :        En octets, 4 octets
  Données :            longueur spécifiée
  Is Done :         1 si terminé, 0 sinon (1 octet)

Réponses

Toutes les réponses font 26 octets, comme suit. Toutes les valeurs sont non signées, big-endian.


Type de message :   1 octet
  Source :         ID, 4 octets
  Destination :    Habituellement l'ID du destinataire réel (voir notes), 4 octets
  Term :           Terme actuel, 8 octets
  Next Index :     Initialisé à dernier index de journal du leader + 1, 8 octets
  Is Accepted :    1 si accepté, 0 sinon (voir notes), 1 octet

Notes

L’ID de destination est habituellement l’ID du destinataire réel pour ce message. Cependant, pour AppendEntriesResponse, AddServerResponse et RemoveServerResponse, il s’agit de l’ID du leader actuel.

Dans RequestVoteResponse, Is Accepted vaut 1 pour un vote en faveur du candidat (demandeur), et 0 pour aucun vote.

Couche applicative

Chaque serveur poste périodiquement des données applicatives dans le journal via une ClientRequest. Les données applicatives contiennent l’état du routeur de chaque serveur et la Destination du cluster Meta LS2. Les serveurs utilisent un algorithme commun pour déterminer l’éditeur et le contenu du Meta LS2. Le serveur ayant le statut « le meilleur » récent dans le journal est l’éditeur du Meta LS2. L’éditeur du Meta LS2 n’est PAS nécessairement le Leader Raft.

Contenu des données applicatives

Les données applicatives sont encodées en UTF-8 JSON , pour plus de simplicité et d’extensibilité. La spécification complète est à déterminer. L’objectif est de fournir suffisamment de données pour écrire un algorithme déterminant le meilleur routeur pour publier le Meta LS2, et pour que l’éditeur dispose d’informations suffisantes pour pondérer les Destinations dans le Meta LS2. Les données contiendront des statistiques sur le routeur et les Destinations.

Les données peuvent éventuellement contenir des données de détection à distance sur la santé des autres serveurs et la capacité de récupérer le Meta LS. Ces données ne seraient pas prises en charge dans la première version.

Les données peuvent éventuellement contenir des informations de configuration postées par un client administrateur. Ces données ne seraient pas prises en charge dans la première version.

Si « nom : valeur » est indiqué, cela spécifie la clé et la valeur de la carte JSON. Sinon, la spécification est à déterminer.

Données de cluster (niveau supérieur) :

  • cluster : Nom du cluster
  • date : Date de ces données (long, ms depuis l’époque)
  • id : ID Raft (entier)

Données de configuration (config) :

  • Tous les paramètres de configuration

Statut de publication MetaLS (meta) :

  • destination : destination metals, base64
  • lastPublishedLS : si présent, encodage base64 du metals publié précédemment
  • lastPublishedTime : en ms, ou 0 si jamais
  • publishConfig : statut de configuration de l’éditeur : activé/désactivé/auto
  • publishing : statut booléen de l’éditeur metals vrai/faux

Données du routeur (router) :

  • lastPublishedRI : si présent, encodage base64 des dernières infos du routeur publiées
  • uptime : Temps de fonctionnement en ms
  • Délai des tâches (Job lag)
  • Tunnels exploratoires
  • Tunnels participant
  • Bande passante configurée
  • Bande passante actuelle

Destinations (destinations) : Liste

Données de destination :

  • destination : la destination, base64
  • uptime : Temps de fonctionnement en ms
  • Tunnels configurés
  • Tunnels actuels
  • Bande passante configurée
  • Bande passante actuelle
  • Connexions configurées
  • Connexions actuelles
  • Données de liste noire

Données de détection de routeur distant :

  • Dernière version RI vue
  • Temps de récupération LS
  • Données de test de connexion
  • Données de profil des floodfills les plus proches pour les périodes hier, aujourd’hui et demain

Données de détection de destination distante :

  • Dernière version LS vue
  • Temps de récupération LS
  • Données de test de connexion
  • Données de profil des floodfills les plus proches pour les périodes hier, aujourd’hui et demain

Données de détection Meta LS :

  • Dernière version vue
  • Temps de récupération
  • Données de profil des floodfills les plus proches pour les périodes hier, aujourd’hui et demain

Interface d’administration

À déterminer, éventuellement une proposition séparée. Non requis pour la première version.

Exigences d’une interface d’administration :

  • Support de plusieurs destinations maîtres, c’est-à-dire plusieurs clusters virtuels (farms)
  • Fournir une vue complète de l’état partagé du cluster - toutes les statistiques publiées par les membres, qui est le leader actuel, etc.
  • Capacité à forcer la suppression d’un participant ou du leader du cluster
  • Capacité à forcer la publication du metaLS (si le nœud actuel est l’éditeur)
  • Capacité à exclure des hachages du metaLS (si le nœud actuel est l’éditeur)
  • Fonctionnalité d’import/export de configuration pour les déploiements en masse

Interface du routeur

À déterminer, éventuellement une proposition séparée. i2pcontrol n’est pas requis pour la première version et les modifications détaillées seront incluses dans une proposition séparée.

Exigences pour l’API Garlic Farm vers routeur (java in-JVM ou i2pcontrol)

  • getLocalRouterStatus()
  • getLocalLeafHash(Hash masterHash)
  • getLocalLeafStatus(Hash leaf)
  • getRemoteMeasuredStatus(Hash masterOrLeaf) // probablement pas dans MVP
  • publishMetaLS(Hash masterHash, List contents) // ou MetaLeaseSet signé ? Qui signe ?
  • stopPublishingMetaLS(Hash masterHash)
  • authentification à déterminer ?

Justification

Atomix est trop volumineux et ne permet pas la personnalisation nécessaire pour acheminer le protocole via I2P. De plus, son format réseau est non documenté et dépend de la sérialisation Java.

Notes

Problèmes

  • Il n’existe aucun moyen pour un client de découvrir et se connecter à un leader inconnu. Ce serait un changement mineur pour qu’un Suiveur envoie la Configuration comme entrée de journal dans AppendEntriesResponse.

Migration

Aucun problème de compatibilité ascendante.

Références