{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TP réseaux - TP 2 : Couche réseau, routage (forwarding)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Le contexte de ce TP est similaire au précédent et on pourra utiliser les fonctionnalités mises en place lors du TP1 :\n",
    "- envoyer une trame ethernet;\n",
    "- utiliser le protocole ARP pour trouver une adresse MAC.\n",
    "\n",
    "L'objectif est maintenant d'implémenter les protocoles de couche réseau. Le code fourni contient les définitions de paquets IP et ICMP et vous pourrez dès le lancement du programme envoyer un paquet IP à une machine située sur le même LAN. Vous aurez donc à vous occuper de la fonction de routage :\n",
    "- remplissage de la table de routage;\n",
    "- utilisation de cette même table."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0. Contexte et code fourni\n",
    "\n",
    "Le code fourni peut largement être considéré comme une boîte noire et vous n'aurez pas le temps de tout lire pendant la séance de TP. Pour résumer :\n",
    "- le fichier `utilisateur.py` est le fichier principal à exécuter, il vous proposera en boucle de taper une commande;\n",
    "- le fichier `linkproto.py` traite de la couche liaison : protocole Ethernet;\n",
    "- les fichiers `ARPpacket.py`, `IPpacket.py` et `ICMPpacket` traitent respectivement des protocoles ARP, IP et ICMP;\n",
    "- le fichier `tools.py` propose quelques outils pour manipuler les adresses (IP et MAC);\n",
    "- le fichier `asyncinput.py` s'occupe uniquement d'une version d'`input` compatible avec asyncio.\n",
    "\n",
    "Les ajouts à effectuer se situeront dans `utilisateur.py` et `IPpacket.py` uniquement.\n",
    "\n",
    "Comme pour le TP1, il vous faut pour commencer récupérer le sujet sur celene et télécharger l'archive contenant les fichiers associés. \n",
    "\n",
    "A partir des quatre adresses réseau suivantes, choisissez les adresses pour vos 8 interfaces :\n",
    "- `172.18.19.48/28` pour `AB`;\n",
    "- `172.18.19.64/28` pour `BC`;\n",
    "- `172.18.19.80/28` pour `CD`;\n",
    "- `172.18.19.96/28` pour `AD`.\n",
    "\n",
    "Le lancement est le même que pour TP1 (mais pour 2 interfaces), rentrez les commandes suivantes :\n",
    "- `kathara lstart`;\n",
    "- `ip link` (à partir de pca, pcb, pcc ou pcd) pour déterminer le nom des interfaces actives (présence de `state UP` sur les lignes correspondant à ces interfaces);\n",
    "- `sudo ./utilisateur.py NomDeLInterface0 NomDeLInterface1` (toujours à partir de pca, pcb, pcc ou pcd);\n",
    "- `kathara wire nom_REZO` pour observer le trafic sur le LAN nommé nom_REZO (remplacer nom_REZO par rezo_AB, rezo_BC, etc). \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Mise en place de la table de routage\n",
    "\n",
    "### 1.1 Premières commandes\n",
    "\n",
    "Pour débuter, utilisez la commande `addip` pour affecter une adresse IP à votre interface réseau. Vous disposez aussi des commandes `showip` et `fluship`, dont les équivalents vus en cours sont :\n",
    "\n",
    "\n",
    "|Commande | | Équivalent `ip` | | Effet|\n",
    "|:---:|-|:-:|-|:---:|\n",
    "`addip` | | `ip addr add @ip/mask dev itf` | | affecter une adresse IP à votre interface réseau\n",
    "`showip` | |`ip addr show`| | visualiser les adresses IP des interfaces utilisées, \n",
    "`fluship` | | `ip addr flush dev NomDeLInterface`| | effacer les adresses liées à une interface\n",
    "\n",
    "Il est possible de placer ces premières commandes de configuration dans un fichier qui sera lu au démarrage, le fichier `initcomms` présent dans l'archive contient un exemple (ajout d'une IP sur l'interface **eth0** puis suppression des adresses IP affectées à cette interface). La commande \n",
    "> ```cat ./initcomms - | sudo ./shared/utilisateur.py NomDeLInterface0 NomDeLInterface1```\n",
    "\n",
    "utilise les lignes du fichier `initcomms` en lieu et place des premières entrées clavier. Vous pourrez modifier ce fichier au cours du TP pour y intégrer les commandes de configuration des interfaces et routes.\n",
    "\n",
    "\n",
    "### 1.2 Envoi d'un premier paquet IP\n",
    "\n",
    "Vous pouvez maintenant envoyer un paquet IP avec la commande `sendip`. Si nécessaire, une requête ARP est effectuée pour déterminer l'adresse MAC du destinataire avant de placer le paquet à envoyer dans une trame. Cela fonctionne (en ce début de TP) sous 2 conditions :\n",
    "- le destinataire est dans le même LAN que vous;\n",
    "- ce LAN est celui sur lequel est branchée la première interface déclarée c'est à dire \"NomDeLInterface0\" (le choix est arbitraire).\n",
    "\n",
    "Puisqu'on peut envoyer un paquet IP, il est aussi possible d'envoyer un ping (paquet ICMP) avec la commande `ping`. Les contraintes sont les mêmes.\n",
    "\n",
    "Lisez le code de la fonction `IPrcv` dans `IPpacket.py` pour comprendre ce qui s'y passe.\n",
    "\n",
    "\n",
    "### 1.3 Informations de routage\n",
    "\n",
    "La première chose à faire est d'identifier les composantes d'une table de routage (cf cours) : quelles sont les informations à placer dans la variable globale `troutage` définie (il faut la décommenter) dans `IPpacket.py` ? Choisissez un format pour `troutage`, par exemple une liste dont vous préciserez le contenu. Vérifiez auprès du chargé de TP que votre choix est raisonnable. Notons qu'une table de routage est ordonnée : on la parcoure des entrées les plus spécifiques aux plus générales. Un dictionnaire ne permet pas d'ordonner les entrées, évitez donc plutôt ce format. Lors de la construction de la table, pour gagner du temps, on n'implémentera pas l'ordre intégral, on se contentera de mettre les lignes sans passerelle en tête de table. Sur les exemples de ce TP cela ne devrait pas poser de problème et vous pourrez compléter le code plus tard.\n",
    "\n",
    "Avant de compléter le fichier `utilisateur.py`, regardez ce que font les fonctions suivantes :\n",
    "- `ipaddr` et `isinnet` dans `tools.py`;\n",
    "- `searchip` dans `IPpacket.py`.\n",
    "\n",
    "Il faut maintenant alimenter la table de routage, cela se fait en deux occasions :\n",
    "- complétez la commande `addip` dans `utilisateur.py`;\n",
    "- complétez la commande `addroute` dans `utilisateur.py`.\n",
    "\n",
    "\n",
    "\n",
    "On pourra compléter les commandes `rmvroute` et `showroute`. À noter, les correspondances avec les commandes standard :\n",
    "|Commande | | Équivalent `ip` | | Effet|\n",
    "|:---:|-|:-:|-|:---:|\n",
    "`addroute` | | `ip route add @ip/mask via ipgw` | | déclarer une route\n",
    "`showroute` | |`ip route show`| | visualiser les routes connues, \n",
    "`rmvroute` | | `ip route del @ip/mask via ipgw`| | supprimer une route"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Routage\n",
    "\n",
    "Il s'agit maintenant d'implémenter les 2 fonctions utilisant la table de routage."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Lire la table\n",
    "\n",
    "Lors de l'envoi, un paquet IP est encapsulé dans une trame ethernet. Pour constituer cette trame, il faut déterminer l'interface de sortie et l'adresse IP de la première interface sur le chemin, qui nous permettra de trouver l'adresse MAC du destinataire de la trame ethernet. Cette première interface a pour adresse IP :\n",
    "- soit l'adresse IP destination si elle est présente sur un LAN attenant à l'émetteur;\n",
    "- soit l'adresse IP de la passerelle à utiliser.\n",
    "\n",
    "Dans les 2 cas, cette information s'obtient en consultant la table de routage. Écrivez la fonction `rout` dans `IPpacket.py` en respectant la description fournie.\n",
    "\n",
    "Réécrivez les commandes `sendip` et `ping` pour ajouter la possibilité d'envoyer un paquet IP dans un LAN quelconque (pourvu qu'une route soit connue) en utilisant l'une ou l'autre des interfaces. Vous pourrez consulter le code de la fonction `iptoeth` dans `linkproto.py`.\n",
    "\n",
    "Implémentez la même modification dans la fonction `IPrcv` de `IPpacket` pour les réponses aux ping.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 Forwarder\n",
    "\n",
    "Pour finir, modifiez la fonction `IPrcv` de `IPpacket` pour forwarder les paquets IP dont l'adresse destination n'est pas une adresse IP de l'hôte qui les reçoit. On utilise de nouveau la fonction `rout` pour trouver le prochain intermédiaire sur le chemin. Pensez à décrémenter le TTL en utilisant la méthode `forw` des paquets IP.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Expérimentation\n",
    "\n",
    "Testez en envoyant un ping sur un chemin de longueur au moins 2. Observez avec wireshark que le paquet IP contenant contenant un paquet ICMP de type ECHO REQUEST est bien envoyé. Si toutes les machines sont fonctionnelles (forward pour les routeurs du chemin et lecture de table de routage pour le destinataire), vous devriez recevoir la réponse."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Pour aller plus loin\n",
    "\n",
    "On peut faire la même expérience en utilisant les commandes IP. Les 2 options interagissent sans problème. Refaites la configuration de votre machine avec les commandes IP."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Fin du TP\n",
    "\n",
    "Récupérez les fichiers que vous avez écrits, reconnectez votre machine au switch d'accès à internet puis rebootez votre machine et vérifiez la connexion internet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Aucun(e)",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
