Les bases du réseau
Networking 101
Switching
Qu'est ce qu'un réseau ?
Supposons que nous ayons deux ordinateurs, VM, Cloud Center, etc A et B.
Comment A communique-t-il avec B ?
graph LR
A[PC A] -->|?| B[PC B];
B[PC B] -->|?| A[PC A];
On les connecte tous les deux à un switch commun, et ce switch crée un réseau contenant A et B.
graph LR
A[PC A] --> C[switch];
B[PC B] --> C[switch];
C[switch] --> B[PC B];
C[switch] --> A[PC A];
Pour pouvoir les connecter, on a besoin d'une interface, qu'elle soit physique ou virtuelle.
Pour connaître cette interface, on peut taper la commande ip link
.
Exemple
Sur mon pc, cela donne le résulat suivant.
Dans la nomenclature Linux, en
est le préfixe pour une interface ethernet, et wl
pour une interface wifi.
Remarquons que Docker à sa propre interface docker0
pour faire le lien entre les conteneurs et le reste du réseau.
Attention
Les nomenclatures peuvent quelque peu varier suivant les distributions, il n'est pas rare de voir eth0
pour une interface ethernet.
Supposons que le réseau créé possède l'adresse \(192.168.1.0\) et que A et B on deux interface ethernet nommé enp3s0
et enp3s1
.
graph LR
A[PC A <br/> enp3s0] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> enp3s1] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> enp3s1];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> enp3s0];
Pour ajouter les deux ordinateurs A et B, on leur assigne alors une adresse ip sur ce réseau, via la commande suivante.
ip addr add 192.168.1.10/24 dev enp3s0
pour A,ip addr add 192.168.1.11/24 dev enp3s1
pour B.
graph LR
A[PC A <br/> 192.168.1.10] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> 192.168.1.11] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> 192.168.1.11];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> 192.168.1.10];
Chacun des deux pc possède alors une adresse sur ce réseau et peut communiquer avec l'autre. Il est pas exemple possible depuis A de faire un ping
vers B en tapant ping 192.168.1.11
.
Les communications ici entre A et B se font via le switch. Le switch ne peut recevoir et envoyer des informations que depuis des interfaces faisant parties du réseau défini.
Mettons maintenant deux réseaux définis par deux switchs avec deux adresses ip différentes. Comment A peut-il communiquer avec C ?
graph TD
subgraph réseau 1
A[PC A <br/> 192.168.1.10] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> 192.168.1.11] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> 192.168.1.11];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> 192.168.1.10];
end
subgraph réseau 2
D[PC C <br/> 192.168.2.10] --> E[switch <br/> 192.168.2.0];
F[PC D <br/> 192.168.2.11] --> E[switch <br/> 192.168.2.0];
E[switch <br/> 192.168.2.0] --> D[PC D <br/> 192.168.2.11];
E[switch <br/> 192.168.2.0] --> F[PC C <br/> 192.168.2.10];
end
A[PC A <br/> 192.168.1.10] -.->|?| D[PC C <br/> 192.168.2.10];
Routing
C'est là qu'intervient le routeur. Un routeur permet de connecter deux réseaux ensembles. On peut le penser comme un serveur avec de multiples ports réseaux.
graph LR
subgraph réseau 1
A[PC A <br/> 192.168.1.10] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> 192.168.1.11] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> 192.168.1.11];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> 192.168.1.10];
end
subgraph réseau 2
D[PC C <br/> 192.168.2.10] --> E[switch <br/> 192.168.2.0];
F[PC D <br/> 192.168.2.11] --> E[switch <br/> 192.168.2.0];
E[switch <br/> 192.168.2.0] --> D[PC D <br/> 192.168.2.11];
E[switch <br/> 192.168.2.0] --> F[PC C <br/> 192.168.2.10];
end
G[Routeur]-.-C[switch <br/> 192.168.1.0];
G[Routeur]-.-E[switch <br/> 192.168.2.0];
Puisque le routeur connecte deux réseaux, il a deux adresses qui lui sont assignées : une pour l'identifier sur chaque réseau.
graph LR
subgraph réseau 1
A[PC A <br/> 192.168.1.10] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> 192.168.1.11] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> 192.168.1.11];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> 192.168.1.10];
end
subgraph réseau 2
D[PC C <br/> 192.168.2.10] --> E[switch <br/> 192.168.2.0];
F[PC D <br/> 192.168.2.11] --> E[switch <br/> 192.168.2.0];
E[switch <br/> 192.168.2.0] --> D[PC D <br/> 192.168.2.11];
E[switch <br/> 192.168.2.0] --> F[PC C <br/> 192.168.2.10];
end
G[Routeur]-.-|192.168.1.1|C[switch <br/> 192.168.1.0];
G[Routeur]-.-|192.168.2.1|E[switch <br/> 192.168.2.0];
Maintenant que le routeur relie les deux réseaux, chacun des 4 pc peut communiquer l'un avec l'autre.
Quand le PC A veut envoyer un paquet au PC C, comment sait-il où est le routeur sur le réseau, pour envoyer le paquet via lui ? Le routeur n'est qu'un système supplémentaire sur le réseau, il peut y en avoir des centaines.
Default Gateway
C'est là que l'on configure les systèmes avec une gateway, ou passerelle. Si un réseau est une chambre, alors la gateway est une porte pour communiquer vers l'extérieur.
Les systèmes ont besoin de savoir où est cette gateway pour pouvoir communiquer entre réseaux.
Pour voir les différentes configuration de route, ou table de routage d'un système, on peut alors taper la commande suivante.
route
Exemple
Sur mon pc, cela donne le résultat suivant.
Dans notre exemple, si l'on tape cette commande, comme aucune passerelle n'a encore été définie, on obtiendra une table de routage vide.
graph LR
subgraph réseau 1
A[PC A <br/> 192.168.1.10] --> C[switch <br/> 192.168.1.0];
B[PC B <br/> 192.168.1.11] --> C[switch <br/> 192.168.1.0];
C[switch <br/> 192.168.1.0] --> B[PC B <br/> 192.168.1.11];
C[switch <br/> 192.168.1.0] --> A[PC A <br/> 192.168.1.10];
end
subgraph réseau 2
D[PC C <br/> 192.168.2.10] --> E[switch <br/> 192.168.2.0];
F[PC D <br/> 192.168.2.11] --> E[switch <br/> 192.168.2.0];
E[switch <br/> 192.168.2.0] --> D[PC D <br/> 192.168.2.11];
E[switch <br/> 192.168.2.0] --> F[PC C <br/> 192.168.2.10];
end
G[Routeur]-.-|192.168.1.1|C[switch <br/> 192.168.1.0];
G[Routeur]-.-|192.168.2.1|E[switch <br/> 192.168.2.0];
Pour configurer une passerelle du PC B vers des systèmes du réseau 2, on tape alors la commande suivante.
ip route add 192.168.2.0/24 via 192.168.1.1
via 192.168.1.1
définit la passerelle qui sera utilisée par le réseau 1 pour communiquer vers le réseau 2.
La table de routage du réseau 1 se mettra alors à jour.
Attention
Si l'on s'arette là, la communication ne va que dans un sens, du réseau 1 vers le réseau 2. Il faut aussi définir la passerelle pour aller dans l'autre sens.
cela se fait via ip route add 192.168.1.0/24 via 192.168.2.1
.
Mais si maintenant le réseau 2 a besoin d'accéder à internet ? Par exemple à l'ip internet 172.217.194.0, qui est celle de Google (ça peut être utile) ?
graph LR
subgraph réseau 1
A[PC A 192.168.1.10] --> C[switch];
B[PC B 192.168.1.11] --> C[switch];
C[switch 192.168.1.0] --> B[PC B 192.168.1.11];
C[switch 192.168.1.0] --> A[PC A 192.168.1.10];
end
subgraph réseau 2
D[PC C 192.168.2.10] --> E[switch];
F[PC D 192.168.2.11] --> E[switch];
E[switch 192.168.2.0] --> D[PC D 192.168.2.11];
E[switch 192.168.2.0] --> F[PC C 192.168.2.10];
end
G[Routeur]-.-|192.168.1.1|C[switch 192.168.1.0];
G[Routeur]-.-|192.168.2.1|E[switch 192.168.2.0];
subgraph Internet
H{{Google 172.217.194.0}}
end
Et bien on rajoute une nouvelle passerelle.
ip route add 172.217.194.0/24 via 192.168.2.1
La table de routage aura alors une nouvelle route.
graph LR
subgraph réseau 1
A[PC A 192.168.1.10] --> C[switch];
B[PC B 192.168.1.11] --> C[switch];
C[switch 192.168.1.0] --> B[PC B 192.168.1.11];
C[switch 192.168.1.0] --> A[PC A 192.168.1.10];
end
subgraph réseau 2
D[PC C 192.168.2.10] --> E[switch];
F[PC D 192.168.2.11] --> E[switch];
E[switch 192.168.2.0] --> D[PC D 192.168.2.11];
E[switch 192.168.2.0] --> F[PC C 192.168.2.10];
end
subgraph Internet
H1{{Google 172.217.194.0}};
end
G[Routeur]-.-|192.168.1.1|C[switch 192.168.1.0];
G[Routeur]-.-|192.168.2.1|E[switch 192.168.2.0];
G[Routeur]-.-H1{{Google 172.217.194.0}};
Mais il y a des milliards de sites, on ne va quand même pas faire ça pour tous ?
graph LR
subgraph réseau 1
A[PC A 192.168.1.10] --> C[switch];
B[PC B 192.168.1.11] --> C[switch];
C[switch 192.168.1.0] --> B[PC B 192.168.1.11];
C[switch 192.168.1.0] --> A[PC A 192.168.1.10];
end
subgraph réseau 2
D[PC C 192.168.2.10] --> E[switch];
F[PC D 192.168.2.11] --> E[switch];
E[switch 192.168.2.0] --> D[PC D 192.168.2.11];
E[switch 192.168.2.0] --> F[PC C 192.168.2.10];
end
G[Routeur]-.-|192.168.1.1|C[switch 192.168.1.0];
G[Routeur]-.-|192.168.2.1|E[switch 192.168.2.0];
G[Routeur]-.-H1{{Google 172.217.194.0}};
subgraph Internet
H1{{Google 172.217.194.0}}
H2{{Site 1}}
H3{{Site 2}}
H4{{...}}
end
Plutôt que d'ajouter une nouvelle route pour chaque adresse ip, on peut simplement dire "pour chaque réseau dont je ne connais pas la route, utilise ce routeur comme passerelle par défaut".
ip route add default via 192.168.2.1
Ainsi, toute requête alors vers un réseau inconnu passe par ce routeur particuliers.
Exemple
Sur mon pc, la passerelle par défaut est livebox.home
, qui est ma box internet, ce qui est cohérent car c'est elle qui fait la jonction entre mon réseau local et internet.
Notons qu'à la place de default
comme destination, on aurait pu avoir 0.0.0.0
, ce qui revient au même puisque 0.0.0.0
signifie "n'importe quelle adresse ip".
Lorsque l'on voit 0.0.0.0
dans la section passerelle, cela signifie simplement que l'on a pas besoin de passerelle pour accéder à ce réseau.
Comment définir un system Linux comme routeur ? Prenons la situation suivante.
graph LR
subgraph A
A1[eth0 <br/> 192.168.1.5];
end
subgraph B
B1[eth0 <br/> 192.168.1.6];
B2[eth1 <br/> 192.168.2.6];
end
subgraph C
C1[eth0 <br/> 192.168.2.5];
end
A1[eth0 <br/> 192.168.1.5]-.-|192.168.1.0|B1[eth0 <br/> 192.168.1.6]
B2[eth1 <br/> 192.168.2.6]-.-|192.168.2.0|C1[eth0 <br/> 192.168.2.5]
On a un système Linux B avec deux interfaces, chacune connectant un autre système Linux. Donc B est connecté aux 2 réseaux, et a donc une ip sur chaque réseau.
Question
Comment A peut-il communiquer avec C ?
Si l'on essaye naïvement de faire un ping 192.168.2.5
depuis le réseau A, on aura le message suivant Connect: Network is unreachable
. Maintenant on sait pourquoi, A n'a aucune idée de comment joindre 192.168.2.0 car il n'y a pas de passerelle. On doit lui dire que la passerelle passe par B.
On fait ça via ip route add 192.168.2.0/24 via 192.168.1.6
, et la même chose dans l'autre sens : ip route add 192.168.1.0/24 via 192.168.2.6
.
Si l'on réesaye ping 192.168.2.5
maintenant, nous n'aurons plus le message d'erreur, mais nous pas non plus de réponses. Par défaut et pour des raisons de sécurité, dans Linux, les paquets ne sont pas transmis d'une interface à l'autre, ie il n'y a aucune communication entre eth0
et eth1
dans le réseau B.
graph LR
subgraph A
A1[eth0 <br/> 192.168.1.5];
end
subgraph B
B1[eth0 <br/> 192.168.1.6];
B2[eth1 <br/> 192.168.2.6];
end
subgraph C
C1[eth0 <br/> 192.168.2.5];
end
A1[eth0 <br/> 192.168.1.5]-.-|192.168.1.0|B1[eth0 <br/> 192.168.1.6]
B2[eth1 <br/> 192.168.2.6]-.-|192.168.2.0|C1[eth0 <br/> 192.168.2.5]
B1[eth0 <br/> 192.168.1.6] -.-|X| B2[eth1 <br/> 192.168.2.6]
Pour autoriser B à transmettre les paquets d'une interface à l'autre, on doit aller voir dans /proc/sys/net/ipv4/ip_forward
.
Exemple
Sur mon pc, cela donne :
La valeur est sur 1, donc le transfert d'une interface à l'autre est autorisé, sinon elle serait à 0.
Attention
Jusqu'à présent, toutes les commandes que l'on a expliquer ne persisterons pas au redémarrage du système ! Il faudrait les refaire à chaque fois.
Pour que les changements soient persistents, il faut modifier la valeur du paramètre net.ipv4.ip_forward
dans /etc/sysctl.conf
en la mettant à 1.
Exemple
DNS sous Linux
On considère deux pc A et B reliés au même réseau, avec des adresse ip qui ont été assignées. Il sont capables de communiquer entre eux, ie ping 192.168.1.11
depuis A fonctionne.
graph LR
subgraph A
A1[eth0 <br/> 192.168.1.10];
end
B1{{192.168.1.0}};
subgraph B
C1[eth0 <br/> 192.168.1.11];
end
A1[eth0 <br/> 192.168.1.10]-.-B1{{192.168.1.0}}
C1[eth0 <br/> 192.168.1.11]-.-B1{{192.168.1.0}}
Le système B possède des bases de données, donc plutôt que de se souvenir de son adresse ip, on souhaite lui associer le nom db
. Ainsi on souhaiterait que la commande ping db
fonctionne, pour l'instant ce n'est pas le cas.
graph LR
subgraph A
A1[eth0 <br/> 192.168.1.10];
end
B1{{192.168.1.0}};
subgraph B
C1[eth0 <br/> db <br/> 192.168.1.11];
end
A1[eth0 <br/> 192.168.1.10]-.-B1{{192.168.1.0}}
C1[db <br/> eth0 <br/> 192.168.1.11]-.-B1{{192.168.1.0}}
En d'autres termes, on souhaiterait dire au système A que lorsque l'on utilise le nom db
, on veut en fait parler de l'adresse ip 192.168.1.11
.
En encore d'autres termes, on devrait créer un dictionnaire sur le système A disant que la clé db
a pour valeur l'adresse ip 192.168.1.11
. C'est exactement ce que fait le fichier /etc/hosts
sous Linux. Il suffit alors de rajouter la ligne 192.168.1.11 db
dans ce fichier pour que le poing depuis A sur B fonctionne.
Exemple
Avec mon laptop sous WSL2, j'ai le fichier suivant.
Un point important à noter, nous avons dit au système A que le système B s'appelait db
, il va prendre ça pour acquis, peut importe le nom que vous mettrez dans /etc/hosts
il sera considéré comme vrai par A.
On peut même assigner deux noms différents pointant sur la même ip cela ne pose aucun soucis, par exemple on peut faire la chose suivante.
et les deux ping
fonctionneront !
Exemple
En faisant cat /etc/hosts
sur mon laptop, on a vu que l'adresse 127.0.1.1
était répertoriée sous 3 clées différentes : localhost
, Laptop3080
et Laptop3080.localdomain
, un ping
sur chacun des " fonctionne sans soucis et appelle la même adresse ip.
En d'autres termes, A ne vérifie pas si le vrai nom de B est db
, pour voir le vrai nom du sysème B, il faut taper la commande suivante dans le sysème B : hostname
.
A s'en fiche, il utilisera le nom qui est dans le fichier /etc/hosts
.
La traduction du nom défini dans /etc/hosts
en une adresse ip valide s'appelle la résolution du nom.
graph LR
subgraph A
A1[eth0 <br/> web <br/> 192.168.1.10];
end
B1{{192.168.1.0}};
subgraph B
C1[eth0 <br/> db <br/> 192.168.1.11];
end
subgraph C
D1[eth0 <br/> nfs <br/> 192.168.1.12];
end
A1[eth0 <br/> web <br/> 192.168.1.10]-.-B1{{192.168.1.0}}
C1[eth0 <br/> db <br/> 192.168.1.11]-.-B1{{192.168.1.0}}
D1[eth0 <br/> nfs <br/> 192.168.1.12]-.-B1{{192.168.1.0}}
Avec des sysèmes relativement simples comme celui au dessus, on peut encore s'en sortir en définissant un même fichier /etc/hosts
pour les 3 systèmes A, B et C.
Pour les sysèmes modernes avec plusieurs milliers d'intefaces connectéss entre elles, ce n'est plus faisables. Si une seule ip change, il est alors nécessaire de changer l'ensemble des fichier /etc/hosts
de toutes les machines, ce qui devient rapidement impossible.
L'idéee est alors de centraliser ce dictionnaire en un unique serveur, lorsque le système A aura alors besoin de faire une résolution de nom, il enverra une requête à ce serveur, qui se chargera de faire la résolution de nom et de lui renvoyer le résultat.
Ce serveur est ce que l'on appelle le serveur DNS.
graph LR
subgraph A
A1[eth0 <br/> web <br/> 192.168.1.10];
end
B1{{192.168.1.0}};
subgraph B
C1[eth0 <br/> db <br/> 192.168.1.11];
end
subgraph C
D1[eth0 <br/> nfs <br/> 192.168.1.12];
end
subgraph D
E1[eth0];
end
subgraph ...
F1[eth0];
end
subgraph DNS
G1[eth0 <br/> 192.168.1.100];
G2[/ /etc/hosts /]
end
A1[eth0 <br/> web <br/> 192.168.1.10]-.-B1{{192.168.1.0}}
C1[eth0 <br/> db <br/> 192.168.1.11]-.-B1{{192.168.1.0}}
D1[eth0 <br/> nfs <br/> 192.168.1.12]-.-B1{{192.168.1.0}}
G1[eth0]-.-B1{{192.168.1.0}}
Comment faire en sorte que chaque système de réseau pointe correctement vers le serveur DNS quand il a besoin de faire une résolution de nom ?
Le serveur DNS est relié au réseau, il a donc une adresse ip, ici 192.168.1.100
. Chaque sysrème Linux possède une fichier de configuration DNS /etc/resolv.conf
. Il suffit alors de le renseigner comme l'on renseignerait le fichier /etc/hosts
.
Exemple
Sur mon laptop, l'adresse du serveur DNS auquel je suis relié est la suivante.
Si un nom ou une adresse ip change, il suffit alors de mettre à jour le registre du serveur DNS. Actuellement, avec les principes de réplications des données, la mise à jour d'un nom de domaine ou d'une adresse ip peut prendre entre 30 minutes et plusieurs heures, le temps que le changement soit pris en compte par l'ensemble des serveurs DNS présents sur internet.
Avoir un serveur DNS ne veut pas dire que le fichier /etc/hosts
devient inutile. Si l'on souhaite provisioner un serveur de tests, en souhaitant que seuls les sytèmes de notre réseau, voire même uniquement notre système puissent y accéder, on peut renseigner unqiement le fichier /etc/hosts
afin d'avoir une résolution locale.
Le fichier /etc/hosts
est toujours prioritaire sur le serveur DNS, pour faire une résolution, un système cherchera d'abord dans /etc/hosts
, et s'il ne trouve pas la solution, il fera ensuite une requête DNS.
Pour changer l'ordre de priorité, cela se fait dans le fichier de configuration /etc/nsswitch.conf
en modifiant l'ordre des valeurs pour le paramètre hosts
.
Exemple
Si votre serveur DNS est sur un réseau privé, par exemple un serveur DNS sur un réseau bancaire, il est possible que votre DNS ne puisse pas résoudre tous les noms, par exemple ping wwww.facebook.com
peut ne pas fonctionner, et donc dans ce cas vous serez dans l'impossibilité d'y accéder.
Il est possible de rajouter d'autres serveurs DNS, par exemple le serveru DNS de Google situé à l'adresse 8.8.8.8
. Pour faire cela il suffit de modifier le fichier /etc/resolv.conf
en ajoutant la ligne Forward All to 8.8.8.8
, les noms non résolus par votre serveru DNS, seront alors transmises au serveur DNS de Google.
Un nom de la forme wwww.facebook.com
avec www.
au début et .com
à la fin est ce que l'on appelle un nom de domaine.
Pourquoi cette synthaxe ?
L'idée est de grouper les choses ensembles. La dernière partie est ce que l'on appelle le nom de domaine de plus haut niveau (top level domain name), il en existe peu, on peut par exemple citer .com
, .fr
, .net
, .edu
, .org
, .io
, etc. Ils sont censés représenter le but du site, .com
pour les sites commerciaux, .fr
pour les sites français, .net
pour network, .edu
pour les écoles/universités, .org
pour les organisations à but non lucratif, etc.
Pour une adresse comme google.com
, on peut alors hiérarchiser de la façon suivante. On a la racine le .
suivi du nom de domaine de plus haut niveau, google
est le nom de domaine assigné à google, et en desous nous avons les sous-domaines, si l'on veut utiliser google maps on ira sur maps.google.com
, pour se connecter à son drive on ira sur drive.google.com
etc.
graph TD
A1{{ Root <br/> . }}
B1{{ Top Level Domain Name <br/> .com }}
C1{{ Google }}
D1{{Subdomain <br/> mail}}
D2{{Subdomain <br/> drive}}
D3{{Subdomain <br/> www}}
D4{{Subdomain <br/> maps}}
D5{{Subdomain <br/> apps}}
A1-.->B1
B1-.->C1
C1-.->D1
C1-.->D2
C1-.->D3
C1-.->D4
C1-.->D5
graph LR
A1[apps.google.com]
B1{{Org <br/> DNS }}
C1{{Root <br/> DNS }}
D1{{.com <br/> DNS }}
D2{{Google <br/> DNS }}
D3[216.58.221.78]
A1-.->B1
B1-.->C1
C1-.->D1
D1-.->D2
D2-.->D3
D3-.->|cache|A1
nslookup
dig
search domain.com
dans /etc/resolv.conf