---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.16.0
kernelspec:
  name: python3
  display_name: Python 3
---

# Configuration réseau

```{code-cell} python
:tags: [hide-input]

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.gridspec as gridspec
import numpy as np
import pandas as pd
import seaborn as sns
import ipaddress
import socket

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
```

## Modèle réseau Linux — stack TCP/IP, netfilter, namespaces

### La pile réseau du noyau

Linux implémente la pile TCP/IP directement dans le noyau. Chaque paquet entrant ou sortant traverse une série de couches avant d'atteindre l'application ou le réseau physique.

```
Application (socket)
       │
  ┌────▼────┐
  │ Socket  │  ← couche 5-7 : API POSIX, domaines AF_INET, AF_INET6, AF_UNIX
  ├─────────┤
  │  TCP/UDP│  ← couche 4 : ports, sessions, contrôle de flux
  ├─────────┤
  │   IP    │  ← couche 3 : adressage, routage, fragmentation
  ├─────────┤
  │Netfilter│  ← hooks noyau pour le filtrage et le NAT
  ├─────────┤
  │ Driver  │  ← couche 2 : Ethernet, Wi-Fi, tun/tap…
  └─────────┘
```

Le sous-système **Netfilter** s'insère directement dans la pile IP. Il expose cinq hooks (`NF_INET_PRE_ROUTING`, `NF_INET_LOCAL_IN`, `NF_INET_FORWARD`, `NF_INET_LOCAL_OUT`, `NF_INET_POST_ROUTING`) que des modules noyau peuvent intercepter pour analyser ou modifier les paquets. iptables et nftables s'appuient tous deux sur ces hooks.

### Namespaces réseau

Linux supporte les **network namespaces** (netns), qui isolent complètement une pile réseau : interfaces, tables de routage, règles iptables, sockets. Chaque conteneur Docker possède son propre namespace réseau.

```bash
# Créer un namespace réseau isolé
ip netns add test_ns

# Exécuter une commande dans ce namespace
ip netns exec test_ns ip addr

# Relier deux namespaces via une paire veth
ip link add veth0 type veth peer name veth1
ip link set veth1 netns test_ns

# Lister les namespaces
ip netns list

# Supprimer
ip netns del test_ns
```

Les namespaces sont la brique fondamentale de la conteneurisation réseau. Un processus hérite du namespace de son parent ; seule une appel `unshare(CLONE_NEWNET)` ou `ip netns exec` permet d'en changer.

## Interfaces réseau

### Types d'interfaces

| Interface | Rôle |
|-----------|------|
| `eth0`, `ens3` | Ethernet physique (nommage prévisible systemd) |
| `lo` | Loopback (127.0.0.1, ::1) |
| `wlan0`, `wlp2s0` | Wi-Fi |
| `bond0` | Agrégation de liens (bonding) |
| `br0` | Bridge (pont logiciel) |
| `eth0.10` | VLAN 802.1Q |
| `veth0` | Virtual Ethernet pair (conteneurs) |
| `tun0`, `tap0` | Interfaces virtuelles (VPN) |
| `dummy0` | Interface de test sans matériel |

### Commandes `ip link`

```bash
# Lister toutes les interfaces
ip link show

# Afficher une interface spécifique
ip link show ens3

# Activer / désactiver
ip link set ens3 up
ip link set ens3 down

# Modifier la MTU
ip link set ens3 mtu 9000

# Modifier l'adresse MAC
ip link set ens3 address 02:11:22:33:44:55

# Créer une interface dummy
ip link add dummy0 type dummy
ip link set dummy0 up
```

Sortie typique de `ip link show` :

```
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
```

Les flags entre `<>` sont importants : `UP` indique que l'interface est activée administrativement, `LOWER_UP` que le lien physique est détecté.

### Exploration via `/sys/class/net`

```bash
# Vitesse de l'interface (Mb/s)
cat /sys/class/net/ens3/speed

# État opérationnel
cat /sys/class/net/ens3/operstate    # up, down, unknown

# Statistiques (octets reçus/émis, erreurs)
cat /sys/class/net/ens3/statistics/rx_bytes
cat /sys/class/net/ens3/statistics/tx_packets
cat /sys/class/net/ens3/statistics/rx_errors

# Driver utilisé
readlink /sys/class/net/ens3/device/driver
```

## Configuration IP

### Adresses avec `ip addr`

```bash
# Lister les adresses
ip addr show
ip addr show ens3

# Ajouter une adresse IPv4 avec masque CIDR
ip addr add 192.168.1.10/24 dev ens3

# Ajouter une adresse IPv6
ip addr add 2001:db8::1/64 dev ens3

# Supprimer une adresse
ip addr del 192.168.1.10/24 dev ens3

# Vider toutes les adresses d'une interface
ip addr flush dev ens3
```

### Routes avec `ip route`

```bash
# Table de routage principale
ip route show
ip route show table main

# Ajouter une route statique
ip route add 10.10.0.0/16 via 192.168.1.1 dev ens3

# Ajouter la route par défaut
ip route add default via 192.168.1.254

# Supprimer une route
ip route del 10.10.0.0/16

# Tester quel chemin sera utilisé pour une destination
ip route get 8.8.8.8
```

Sortie de `ip route get 8.8.8.8` :

```
8.8.8.8 via 192.168.1.254 dev ens3 src 192.168.1.10 uid 1000
    cache
```

### Policy routing avec `ip rule`

Le **policy routing** (routage par politique) permet d'utiliser plusieurs tables de routage selon des critères : adresse source, marque de paquet, ToS.

```bash
# Lister les règles
ip rule list

# Créer une table de routage personnalisée
echo "200 custom_table" >> /etc/iproute2/rt_tables

# Ajouter une règle : paquets venant de 10.0.0.0/8 → table custom_table
ip rule add from 10.0.0.0/8 table custom_table

# Ajouter une route dans cette table
ip route add default via 10.0.0.1 table custom_table

# Supprimer la règle
ip rule del from 10.0.0.0/8
```

Les tables de routage prédéfinies sont `local` (255), `main` (254) et `default` (253). Le noyau les consulte dans l'ordre croissant de priorité défini par `ip rule`.

## NetworkManager

NetworkManager est le gestionnaire de connexions réseau standard sur les distributions modernes (RHEL, Fedora, Ubuntu Desktop, Debian…). Il gère les profils de connexion et supporte Ethernet, Wi-Fi, VPN, bonds, bridges, VLANs.

### `nmcli` — interface en ligne de commande

```bash
# État général
nmcli general status

# Lister les connexions
nmcli connection show

# Lister les périphériques
nmcli device status

# Activer / désactiver une connexion
nmcli connection up "Wired connection 1"
nmcli connection down "Wired connection 1"

# Créer une connexion Ethernet statique
nmcli connection add type ethernet \
    con-name "serveur-eth0" \
    ifname ens3 \
    ipv4.addresses 192.168.1.10/24 \
    ipv4.gateway 192.168.1.254 \
    ipv4.dns "1.1.1.1,8.8.8.8" \
    ipv4.method manual

# Modifier une connexion existante
nmcli connection modify "serveur-eth0" ipv4.dns "9.9.9.9"

# Supprimer une connexion
nmcli connection delete "serveur-eth0"

# Recharger une connexion après modification
nmcli connection reload
```

Les profils de connexion sont stockés dans `/etc/NetworkManager/system-connections/` au format INI (`.nmconnection`).

```bash
# Contenu type d'un profil
cat /etc/NetworkManager/system-connections/serveur-eth0.nmconnection
```

```ini
[connection]
id=serveur-eth0
type=ethernet
interface-name=ens3

[ethernet]

[ipv4]
address1=192.168.1.10/24,192.168.1.254
dns=1.1.1.1;8.8.8.8;
method=manual

[ipv6]
method=auto
```

## Netplan (Ubuntu)

Netplan est la couche d'abstraction de configuration réseau d'Ubuntu (depuis 17.10). Les fichiers YAML dans `/etc/netplan/` sont traduits en configuration pour le backend choisi (NetworkManager ou systemd-networkd).

### Structure d'un fichier Netplan

```bash
# /etc/netplan/00-installer-config.yaml
```

```yaml
network:
  version: 2
  renderer: networkd          # ou NetworkManager

  ethernets:
    ens3:
      dhcp4: false
      addresses:
        - 192.168.1.10/24
        - 2001:db8::10/64
      routes:
        - to: default
          via: 192.168.1.254
      nameservers:
        addresses: [1.1.1.1, 8.8.8.8]
        search: [example.com, local.example.com]
      mtu: 1500

  bonds:
    bond0:
      interfaces: [ens3, ens4]
      parameters:
        mode: active-backup
        primary: ens3
        mii-monitor-interval: 100

  vlans:
    vlan10:
      id: 10
      link: ens3
      addresses: [10.10.10.1/24]
```

### Commandes Netplan

```bash
# Tester la configuration sans l'appliquer
netplan try              # revient automatiquement si non confirmé

# Appliquer la configuration
netplan apply

# Générer les fichiers backend sans appliquer
netplan generate

# Déboguer
netplan --debug apply
```

```{admonition} Backends Netplan
:class: note
`networkd` (systemd-networkd) est adapté aux serveurs sans interface graphique. `NetworkManager` convient aux postes de travail où des connexions Wi-Fi et VPN sont gérées dynamiquement. Le choix du backend n'affecte pas la syntaxe YAML Netplan.
```

## DNS

### `/etc/resolv.conf` et `/etc/hosts`

`/etc/resolv.conf` définit les serveurs DNS utilisés par la libc (et donc la plupart des programmes) :

```
nameserver 1.1.1.1
nameserver 8.8.8.8
search example.com local.example.com
options ndots:5 timeout:2 attempts:3
```

`/etc/hosts` permet de résoudre des noms localement sans passer par DNS :

```
127.0.0.1   localhost
::1         localhost ip6-localhost
192.168.1.5  srv-db.example.com  srv-db
```

L'ordre de résolution est défini par `/etc/nsswitch.conf` :

```
hosts: files dns myhostname
```

### systemd-resolved

`systemd-resolved` est le résolveur DNS intégré à systemd. Il offre un cache local, la validation DNSSEC, le DNS-over-TLS et la résolution par interface.

```bash
# État du résolveur
resolvectl status

# Résoudre un nom
resolvectl query github.com

# Statistiques du cache
resolvectl statistics

# Vider le cache
resolvectl flush-caches

# DNS actif par interface
resolvectl dns ens3

# Configurer un serveur DNS pour une interface
resolvectl dns ens3 9.9.9.9
```

Quand systemd-resolved est actif, `/etc/resolv.conf` est souvent un lien symbolique vers `/run/systemd/resolve/stub-resolv.conf` (qui pointe vers 127.0.0.53, le stub resolver).

### Outils de diagnostic DNS

```bash
# dig — outil de référence
dig github.com                         # enregistrement A
dig github.com AAAA                    # enregistrement AAAA
dig github.com MX                      # enregistrements MX
dig @8.8.8.8 github.com               # requête vers un serveur spécifique
dig +trace github.com                  # résolution itérative complète
dig -x 140.82.121.4                    # résolution inverse (PTR)
dig +short github.com                  # sortie compacte

# nslookup — outil interactif
nslookup github.com
nslookup -type=MX github.com 8.8.8.8

# host — commande simple
host github.com
host -t NS github.com
```

## Diagnostic réseau

### `ss` — état des sockets

`ss` (socket statistics) remplace `netstat` et est beaucoup plus rapide car il interroge directement le noyau via Netlink.

```bash
# Toutes les connexions TCP établies
ss -t state established

# Ports en écoute (TCP et UDP)
ss -tlunp

# Connexions avec processus propriétaire
ss -tlnp

# Filtrer par port
ss -t sport = :443

# Statistiques globales
ss -s
```

Sortie de `ss -tlnp` :

```
State  Recv-Q Send-Q  Local Address:Port   Peer Address:Port  Process
LISTEN 0      128           0.0.0.0:22          0.0.0.0:*     users:(("sshd",pid=1234,fd=3))
LISTEN 0      511           0.0.0.0:80          0.0.0.0:*     users:(("nginx",pid=5678,fd=6))
LISTEN 0      511           0.0.0.0:443         0.0.0.0:*     users:(("nginx",pid=5678,fd=7))
```

### Connectivité et routage

```bash
# Ping basique
ping -c 4 8.8.8.8
ping6 -c 4 2001:4860:4860::8888

# Traceroute UDP (par défaut)
traceroute 8.8.8.8

# Traceroute ICMP (plus compatible)
traceroute -I 8.8.8.8

# mtr — combinaison ping + traceroute en temps réel
mtr 8.8.8.8
mtr --report --report-cycles 10 8.8.8.8

# Tester la connectivité TCP sans telnet
nc -zv 192.168.1.5 22
nc -zv -w 3 github.com 443
```

### `tcpdump` — capture de paquets

```bash
# Capturer sur l'interface ens3
tcpdump -i ens3

# Filtrer par host
tcpdump -i ens3 host 192.168.1.5

# Filtrer par port
tcpdump -i ens3 port 80 or port 443

# Capturer et sauvegarder (pour Wireshark)
tcpdump -i ens3 -w capture.pcap

# Lire un fichier de capture
tcpdump -r capture.pcap

# Afficher le contenu ASCII des paquets HTTP
tcpdump -i ens3 -A port 80

# N'afficher que les en-têtes (pas de résolution DNS pour plus de vitesse)
tcpdump -i ens3 -n -q port 22
```

```{admonition} Performances de tcpdump
:class: tip
Sur un lien chargé, utilisez `-c N` pour limiter le nombre de paquets capturés et `-s snaplen` pour capturer uniquement les N premiers octets de chaque paquet (par défaut 262144 octets). Une capture avec `-w` est nettement plus performante que la sortie texte en temps réel.
```

## Bonding et teaming

Le **bonding** agrège plusieurs interfaces physiques en une interface logique unique, offrant redondance et/ou augmentation de bande passante.

### Modes de bonding

| Mode | Nom | Description |
|------|-----|-------------|
| 0 | balance-rr | Round-robin, répartition des paquets |
| 1 | active-backup | Une seule interface active, bascule en cas de panne |
| 2 | balance-xor | Répartition par hachage src/dst MAC |
| 3 | broadcast | Envoi sur toutes les interfaces |
| 4 | 802.3ad (LACP) | Agrégation dynamique IEEE, switch doit supporter LACP |
| 5 | balance-tlb | Répartition adaptative en émission |
| 6 | balance-alb | Répartition adaptative en émission + réception |

Le mode 1 (active-backup) est le plus simple et ne nécessite aucune configuration du switch. Le mode 4 (LACP) offre les meilleures performances mais exige un switch manageable.

### Configuration d'un bond avec `ip`

```bash
# Créer l'interface bond
ip link add bond0 type bond

# Définir le mode
echo active-backup > /sys/class/net/bond0/bonding/mode
echo 100 > /sys/class/net/bond0/bonding/miimon

# Attacher les esclaves
ip link set ens3 down
ip link set ens3 master bond0
ip link set ens4 down
ip link set ens4 master bond0

# Activer le bond
ip link set bond0 up
ip addr add 192.168.1.10/24 dev bond0

# Vérifier l'état
cat /proc/net/bonding/bond0
```

Avec Netplan (mode LACP) :

```yaml
bonds:
  bond0:
    interfaces: [ens3, ens4]
    addresses: [192.168.1.10/24]
    parameters:
      mode: 802.3ad
      lacp-rate: fast
      mii-monitor-interval: 100
      transmit-hash-policy: layer3+4
```

## VLANs — 802.1Q

Un **VLAN** (Virtual LAN) segmente logiquement un réseau physique. En 802.1Q, une étiquette de 4 octets est insérée dans l'en-tête Ethernet ; les 12 bits de VLAN ID permettent 4094 VLANs (1-4094).

### Configuration avec `ip link`

```bash
# Créer une interface VLAN sur ens3 pour le VLAN 10
ip link add link ens3 name ens3.10 type vlan id 10

# Activer et configurer l'IP
ip link set ens3.10 up
ip addr add 10.10.10.1/24 dev ens3.10

# Créer le VLAN 20
ip link add link ens3 name ens3.20 type vlan id 20
ip link set ens3.20 up
ip addr add 10.20.20.1/24 dev ens3.20

# Vérifier
ip -d link show ens3.10
cat /proc/net/vlan/config
```

Avec Netplan :

```yaml
vlans:
  vlan10:
    id: 10
    link: ens3
    addresses: [10.10.10.1/24]
    nameservers:
      addresses: [10.10.10.53]
  vlan20:
    id: 20
    link: ens3
    addresses: [10.20.20.1/24]
```

```{admonition} Trunk vs Access
:class: note
Du côté du switch, le port connecté au serveur doit être configuré en mode **trunk** pour transporter plusieurs VLANs avec leurs étiquettes 802.1Q. Un port **access** n'appartient qu'à un seul VLAN et ne transmet pas d'étiquettes.
```

---

## Démonstrations Python

### Module `ipaddress` — calcul de sous-réseaux

```{code-cell} python
import ipaddress

# Réseau de base
net = ipaddress.IPv4Network("192.168.0.0/22")

print(f"Réseau       : {net}")
print(f"Masque       : {net.netmask}")
print(f"Masque joker : {net.hostmask}")
print(f"Adresse réseau: {net.network_address}")
print(f"Broadcast    : {net.broadcast_address}")
print(f"Nb d'hôtes   : {net.num_addresses - 2}")
print(f"Préfixe      : /{net.prefixlen}")
print()

# Décomposer en sous-réseaux /24
print("Sous-réseaux /24 :")
for subnet in net.subnets(new_prefix=24):
    hosts = list(subnet.hosts())
    print(f"  {subnet}  →  premier hôte: {hosts[0]}  dernier: {hosts[-1]}")

print()

# Supernet
supernet = net.supernet()
print(f"Supernet (/21) : {supernet}")

# Vérifier l'appartenance
ip = ipaddress.IPv4Address("192.168.1.42")
print(f"\n{ip} dans {net} : {ip in net}")
print(f"{ip} est privée : {ip.is_private}")
print(f"{ip} est globale: {ip.is_global}")

# IPv6
net6 = ipaddress.IPv6Network("2001:db8::/32")
print(f"\nRéseau IPv6    : {net6}")
print(f"Nb d'adresses  : {net6.num_addresses:.2e}")
subnet6 = list(net6.subnets(new_prefix=48))[0]
print(f"Premier /48    : {subnet6}")
```

### Plan d'adressage — visualisation

```{code-cell} python
:tags: [hide-input]

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

# Plan d'adressage d'une infrastructure typique
plan = [
    {"VLAN": 1,  "Nom": "Management",   "Réseau": "10.0.1.0/24",  "Hôtes": 10,  "Couleur": "#4e79a7"},
    {"VLAN": 10, "Nom": "Production",   "Réseau": "10.0.10.0/24", "Hôtes": 80,  "Couleur": "#f28e2b"},
    {"VLAN": 20, "Nom": "Staging",      "Réseau": "10.0.20.0/24", "Hôtes": 30,  "Couleur": "#59a14f"},
    {"VLAN": 30, "Nom": "DMZ",          "Réseau": "10.0.30.0/24", "Hôtes": 15,  "Couleur": "#e15759"},
    {"VLAN": 40, "Nom": "Sauvegarde",   "Réseau": "10.0.40.0/24", "Hôtes": 8,   "Couleur": "#76b7b2"},
    {"VLAN": 50, "Nom": "Supervision",  "Réseau": "10.0.50.0/24", "Hôtes": 5,   "Couleur": "#edc948"},
]

df = pd.DataFrame(plan)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Tableau
ax = axes[0]
ax.axis("off")
table_data = [[row["VLAN"], row["Nom"], row["Réseau"],
               str(row["Hôtes"]), f"{254 - row['Hôtes']} libres"]
              for _, row in df.iterrows()]
col_labels = ["VLAN", "Segment", "Réseau", "Utilisés", "Disponibles"]
tbl = ax.table(cellText=table_data, colLabels=col_labels,
               loc="center", cellLoc="center")
tbl.auto_set_font_size(False)
tbl.set_fontsize(10)
tbl.scale(1.2, 1.8)
for i, row in enumerate(plan):
    for j in range(5):
        tbl[(i + 1, j)].set_facecolor(row["Couleur"] + "55")
for j in range(5):
    tbl[(0, j)].set_facecolor("#333333")
    tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Plan d'adressage VLAN", fontweight="bold", pad=12)

# Utilisation des hôtes
ax2 = axes[1]
noms = df["Nom"]
utilises = df["Hôtes"]
libres = 254 - df["Hôtes"]
x = np.arange(len(noms))
width = 0.55
bars1 = ax2.bar(x, utilises, width, label="Utilisés", color=[r["Couleur"] for r in plan])
bars2 = ax2.bar(x, libres, width, bottom=utilises, label="Disponibles",
                color=[r["Couleur"] + "33" for r in plan], edgecolor=[r["Couleur"] for r in plan],
                linewidth=0.8)
ax2.set_xticks(x)
ax2.set_xticklabels(noms, rotation=20, ha="right")
ax2.set_ylabel("Adresses hôtes (/24)")
ax2.set_title("Utilisation par segment", fontweight="bold")
ax2.set_ylim(0, 265)
ax2.legend(loc="upper right")
for bar, val in zip(bars1, utilises):
    ax2.text(bar.get_x() + bar.get_width() / 2, val + 4,
             str(val), ha="center", va="bottom", fontsize=9)

plt.suptitle("Infrastructure réseau multi-VLAN", fontsize=13, fontweight="bold", y=1.02)
plt.show()
```

### Table de routage — simulation

```{code-cell} python
:tags: [hide-input]

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

routes = pd.DataFrame([
    {"Destination": "0.0.0.0/0",       "Via": "192.168.1.254", "Interface": "ens3",   "Métrique": 100, "Proto": "static",  "Type": "default"},
    {"Destination": "10.0.10.0/24",    "Via": "0.0.0.0",       "Interface": "ens3",   "Métrique": 0,   "Proto": "kernel",  "Type": "local"},
    {"Destination": "10.0.20.0/24",    "Via": "10.0.1.2",      "Interface": "ens3",   "Métrique": 200, "Proto": "static",  "Type": "network"},
    {"Destination": "10.0.30.0/24",    "Via": "10.0.1.3",      "Interface": "ens3",   "Métrique": 200, "Proto": "static",  "Type": "network"},
    {"Destination": "172.16.0.0/12",   "Via": "10.0.1.1",      "Interface": "bond0",  "Métrique": 150, "Proto": "ospf",    "Type": "network"},
    {"Destination": "192.168.1.0/24",  "Via": "0.0.0.0",       "Interface": "ens3",   "Métrique": 0,   "Proto": "kernel",  "Type": "local"},
    {"Destination": "10.8.0.0/24",     "Via": "0.0.0.0",       "Interface": "tun0",   "Métrique": 0,   "Proto": "kernel",  "Type": "vpn"},
])

color_map = {
    "default": "#e15759",
    "local":   "#4e79a7",
    "network": "#59a14f",
    "vpn":     "#f28e2b",
}

fig, axes = plt.subplots(1, 2, figsize=(15, 5))

# Tableau des routes
ax = axes[0]
ax.axis("off")
tbl = ax.table(
    cellText=routes[["Destination", "Via", "Interface", "Métrique", "Proto"]].values,
    colLabels=["Destination", "Passerelle", "Interface", "Métrique", "Proto"],
    loc="center", cellLoc="center"
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(9)
tbl.scale(1.1, 1.7)
for i, (_, row) in enumerate(routes.iterrows()):
    c = color_map[row["Type"]] + "44"
    for j in range(5):
        tbl[(i + 1, j)].set_facecolor(c)
for j in range(5):
    tbl[(0, j)].set_facecolor("#444444")
    tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Table de routage principale", fontweight="bold", pad=12)

# Distribution par protocole
ax2 = axes[1]
proto_counts = routes["Proto"].value_counts()
colors = ["#4e79a7", "#59a14f", "#f28e2b", "#e15759"]
wedges, texts, autotexts = ax2.pie(
    proto_counts.values,
    labels=proto_counts.index,
    autopct="%1.0f%%",
    colors=colors[:len(proto_counts)],
    startangle=90,
    pctdistance=0.75
)
for at in autotexts:
    at.set_fontsize(10)
    at.set_fontweight("bold")
ax2.set_title("Routes par protocole", fontweight="bold")

legend_patches = [mpatches.Patch(color=color_map[t], label=t.capitalize()) for t in color_map]
axes[0].legend(handles=legend_patches, loc="lower center",
               bbox_to_anchor=(0.5, -0.08), ncol=4, fontsize=8)
plt.suptitle("Analyse de la table de routage", fontsize=13, fontweight="bold")
plt.show()
```

### Diagramme d'architecture réseau

```{code-cell} python
:tags: [hide-input]

sns.set_theme(style="white", palette="muted", font_scale=1.0)

fig, ax = plt.subplots(figsize=(13, 9))
ax.set_xlim(0, 13)
ax.set_ylim(0, 9)
ax.axis("off")

def boite(ax, x, y, w, h, label, sublabel="", color="#4e79a7", fontsize=9):
    rect = plt.Rectangle((x - w/2, y - h/2), w, h,
                          facecolor=color + "33", edgecolor=color, linewidth=2, zorder=3)
    ax.add_patch(rect)
    ax.text(x, y + (0.15 if sublabel else 0), label, ha="center", va="center",
            fontsize=fontsize, fontweight="bold", zorder=4)
    if sublabel:
        ax.text(x, y - 0.25, sublabel, ha="center", va="center",
                fontsize=7.5, color="#555555", zorder=4)

def fleche(ax, x1, y1, x2, y2, label="", color="#888888"):
    ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle="<->", color=color, lw=1.5), zorder=2)
    if label:
        mx, my = (x1 + x2) / 2, (y1 + y2) / 2
        ax.text(mx + 0.15, my, label, fontsize=7.5, color=color, va="center")

# Internet
boite(ax, 6.5, 8.3, 2.2, 0.8, "Internet", "", "#888888", fontsize=10)

# Firewall
boite(ax, 6.5, 6.8, 2.4, 0.9, "Pare-feu", "iptables / nftables", "#e15759")
fleche(ax, 6.5, 7.75, 6.5, 7.25, color="#e15759")

# Switch/routeur cœur
boite(ax, 6.5, 5.3, 2.4, 0.9, "Routeur cœur", "bond0 LACP", "#f28e2b")
fleche(ax, 6.5, 6.35, 6.5, 5.75, color="#f28e2b")

# DMZ
boite(ax, 2.5, 3.7, 2.4, 0.9, "DMZ (VLAN 30)", "10.0.30.0/24", "#e15759")
fleche(ax, 5.3, 5.1, 3.7, 4.0, color="#aaaaaa")

# Production
boite(ax, 6.5, 3.7, 2.4, 0.9, "Production (VLAN 10)", "10.0.10.0/24", "#59a14f")
fleche(ax, 6.5, 4.85, 6.5, 4.15, color="#aaaaaa")

# Management
boite(ax, 10.5, 3.7, 2.4, 0.9, "Management (VLAN 1)", "10.0.1.0/24", "#4e79a7")
fleche(ax, 7.7, 5.1, 9.3, 4.0, color="#aaaaaa")

# Serveurs dans les zones
boite(ax, 2.5, 2.2, 1.8, 0.7, "srv-web", "nginx / TLS", "#76b7b2", fontsize=8)
boite(ax, 5.5, 2.2, 1.8, 0.7, "srv-app", "Python / Java", "#59a14f", fontsize=8)
boite(ax, 7.5, 2.2, 1.8, 0.7, "srv-db", "PostgreSQL", "#4e79a7", fontsize=8)
boite(ax, 10.5, 2.2, 1.8, 0.7, "srv-mgmt", "Ansible / Zabbix", "#edc948", fontsize=8)

fleche(ax, 2.5, 3.25, 2.5, 2.55, color="#aaaaaa")
fleche(ax, 6.5, 3.25, 5.5, 2.55, color="#aaaaaa")
fleche(ax, 6.5, 3.25, 7.5, 2.55, color="#aaaaaa")
fleche(ax, 10.5, 3.25, 10.5, 2.55, color="#aaaaaa")

# Légende VLANs
for i, (label, color) in enumerate([
    ("DMZ", "#e15759"), ("Production", "#59a14f"), ("Management", "#4e79a7")
]):
    ax.add_patch(plt.Rectangle((0.3 + i * 3.5, 0.3), 0.35, 0.35,
                                facecolor=color + "55", edgecolor=color, linewidth=1.5))
    ax.text(0.8 + i * 3.5, 0.48, label, fontsize=8.5, va="center")

ax.set_title("Architecture réseau Linux multi-VLAN — vue logique",
             fontsize=13, fontweight="bold", pad=14)
plt.show()
```

---

## Résumé

La configuration réseau sous Linux repose sur deux niveaux : les outils noyau exposés via la suite `iproute2` (`ip link`, `ip addr`, `ip route`, `ip rule`) et les gestionnaires de haut niveau (NetworkManager, Netplan) qui persistent la configuration au redémarrage.

Points essentiels à retenir :

- **`iproute2`** est l'ensemble d'outils canonique : `ip` remplace `ifconfig`, `route`, `arp`. Les modifications sont immédiates mais non persistantes.
- **NetworkManager** est adapté aux environnements dynamiques ; `nmcli` permet une gestion complète en ligne de commande.
- **Netplan** (Ubuntu) abstrait le backend et simplifie la configuration déclarative en YAML.
- **systemd-resolved** centralise la résolution DNS avec cache, DNSSEC et routage par interface.
- Le module Python `ipaddress` permet de manipuler des réseaux IP avec précision, sans dépendance externe.
- Le **bonding** et les **VLANs** sont des fonctionnalités noyau accessibles directement via `ip link`, indépendamment du gestionnaire de haut niveau.
- Pour le diagnostic, `ss` (sockets), `tcpdump` (capture), `mtr` (routage) et `resolvectl` (DNS) couvrent l'essentiel des situations.

La maîtrise de la pile réseau Linux est un prérequis pour les chapitres suivants sur le filtrage (pare-feu), les services réseau (Nginx, DNS, NFS) et la sécurisation des accès (SSH, VPN).
