Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Tag: Noyau

Niveau : Star Star Star Star Empty
Résumé : grub ; noyau ; multiboot ; mandelbrot

Suite à de précédentes bidouilles, on veut faire mieux.

Multiboot

Donc grub peut booter plusieurs formats, l'un d'entre eux est le multiboot, un format de fichier bootable défini par les développeurs de grub espérant qu'il soit adopté par différentes distributions. Il souffre d'un certain nombre de faiblesses et n'est pas encore vraiment un standard. D'autant que grub lui-même ne supporte pas encore complètement la spécification pourtant très courte. Si vous ne voulez pas lire toute la spécification, allez directement au chapitre 4 où on trouve un code d'exemple prêt à compiler pour vous lancer dans le développement.

Vous trouverez un autre exemple chez quelqu'un qui s'est amusé à coder un space invaders bootable directement à partir de la spécification multiboot.

Alors c'est parti, reprenons notre projet précédent. Faites attention, le processeur est dans un état particulier lorsqu'on vous laisse la main (lisez le chapitre 3.2), à vous de vous en accommoder.

Mode graphique

Tout d'abord on voudrait changer le mode graphique, et si possible pour mieux que la première fois. Pour cela, on va utiliser le standard vesa 2.0 qui standardise des modes avec une plus grande définition et qui ajoute de nouvelles fonctions au bios pour manipuler la carte.

Choisissons le mode 0x118 (1024x768x24) avec 16 millions de couleurs. Plus besoin de gérer un palette puisqu'on écrit directement les couleurs RGB à utiliser pour le pixel. On aura tout de même une fonction de palette puisqu'on n'utilise qu'un petit nombre de couleurs placées sur une échelle.

Le problème c'est que vesa n'a pas prévu qu'on puisse appeler les fonctions de la carte en mode protégé sans passer au moins une fois par une interruption en mode réel. Multiboot est bien gentil, il a prévu le cas, malheureusement, grub n'implémente pas les fonctions qu'il devrait pour le faire pour nous. Un patch est disponible pour ceux qui voudrait quand même cette fonctionnalité.

Il nous faudra donc faire nous-même ces interruptions. Pour survivre au mode réel nous devrons être sous le premier Mo de RAM, or grub nous place juste au dessus (en 0x100000). Notre premier bout de code aura donc pour but de nous déplacer en dessous. Pour cela on commence par prendre l'en-tête multiboot (beaucoup plus simple que pour les zImage) et on écrit le code pour se déplacer en 0x1000 (choix arbitraire) :

        /* on se copie ailleurs */
        mov     $KERNEL_SIZE,%ecx /* en octets */
        mov     $KERNEL_ORIG,%esi /* source juste après ce secteur */
        mov     $KERNEL_DEST,%edi /* destination sous les 1Mo */
        rep movsw

        /* et on va dans notre nouveau noyau */
        ljmp    $0x8,$KERNEL_DEST

Bien, maintenant, il faut passer du mode protégé au mode réel et inversement pour pouvoir faire des interruptions. Choisissons comme toujours de ne pas réinventer la roue et repiquons le code de grub qui dispose déjà de ces fonctions en assembleur. Elles s'appellent real_to_prot et prot_to_real. Pour ceux qui voudraient comprendre comment faire, c'est partout sur le net. Comme le mode protégé a besoin de descripteurs de segment, on n'oublie pas de les définir aussi (lire la fin du fichier asm.S) .

Du coup on peut utiliser 2 fonctions vesa bien utiles dans notre code, set_mode pour mettre en place notre mode graphique, et get_modeinfo pour savoir comment l'utiliser. En effet, on voudrait, comme avant pouvoir accéder directement à la mémoire vidéo pour y mettre nos pixels. La fonction get_modeinfo est là pour ça, elle utilise la fonction 0x4F01 du bios. On lui passe une adresse dans laquelle elle va écrire une structure de données nous informant sur le mode choisi. On choisit l'adresse de cette structure au pif puisqu'on n'a pas de gestion de la mémoire et qu'on a toute la ram pour nous. On prend 8000:0000.

La structure renvoyée contient l'adresse du mapping de la mémoire vidéo en 28h, ainsi que ses dimensions. Cela nos permettrait de changer le mode dans notre code facilement (notez les conversions de type pour lire le bon nombre d'octets) :

        /* on récupère les infos sur le mode qui nous intéresse */
        get_modeinfo(0x118);
        /* conversion de l'adresse du format [segment:offset] au format à plat */
        data = (MODEINFO_SEG << 4)+MODEINFO_OFF;
        /* on joue sur les converstions pour la lecture du bon nombre d'octets en ram */
        video = (unsigned char*) *(unsigned int*)(data + 0x28);
        width = (int) *(unsigned short*)(data + 0x12);
        height = (int) *(unsigned short*)(data + 0x14);

Code

On repart du code de l'article précédent. On y apporte quelques modifications techniques liées au changement de mode. Par exemple l'écriture d'un pixel se résume à l'écriture des 3 couleurs au bon endroit en ram :

        int pos = bpp*(x + y*width);
        /* et on ecrit directement en mémoire vidéo */
        video[pos] = r;
        video[pos+1] = g;
        video[pos+2] = b;

Pour ceux qui veulent aller plus loin dans la compréhension du mode protégé, ça aurait pu aussi être l'occasion de mettre en place un segment séparé pour écrire en mémoire vidéo avec la commande lgdt.

On peut imaginer y ajouter des tâches, par exemple une sorte de thread pour gérer le clavier. Mais la ça commence à devenir complexe et il vaudrait mieux lire des OS existants pour bien comprendre tout ça. Par exemple il existe minix qui a été fait dans un but pédagogique.

Compilation

Une autre chose change, c'est la compilation. Puisque le format multiboot est plus simple, nous n'avons plus besoin du script setup.ld. Par contre, puisque notre binaire contient deux morceaux (souvenez vous de la copie sous les 1Mo en début d'article), on devra modifier notre compilation.

Ce qui se résume en 2 partie, une pour le code de copie fait pour tourner en 0x100000 :

LDFLAGS1="-nostdlib -Wl,-N -Wl,-Ttext -Wl,100000 -m32"
gcc $LDFLAGS1 -o boot.exec boot.o
objcopy -O binary boot.exec boot

Et une partie contenant tout le reste du code, qu'on va placer en 0x1000 :

LDFLAGS2="-nostdlib -Wl,-N -Wl,-Ttext -Wl,1000 -m32"
gcc $LDFLAGS2 -o kernel.exec start.o asm.o kernel.o debug.o
objcopy -O binary kernel.exec kernel

Et enfin une dernière copie qui va concaténer les 2 binaires :

# on force la taille pour éviter les problèmes avec le bootloader
dd if=/dev/zero of=mandel2 bs=256 count=32 2>/dev/null 
# la premiere partie
dd if=boot bs=256 count=1 of=mandel2 conv=notrunc 2>/dev/null
# la 2e partie a une position fixée dans le code de la première (boot.S + header.h)
dd if=kernel bs=256 seek=1 of=mandel2 conv=notrunc 2>/dev/null

Run

Encore une fois, il suffit de préciser le fichier à grub pour qu'il le boote :

kernel         (hd0,0)/boot/mandel

Vous trouverez les sources en pièce jointe de cet article, et en complément une image disque contenant les 2 projets avec un grub installé qui fonctionne directement sur qemu.

$ qemu image.dd

Bravo à vous !

mandel2.png

PJ :

Niveau : Star Star Star Star Empty
Résumé : grub ; noyau ; mandelbrot

Vous souvenez-vous du bon vieux temps ? Je vous parle du temps de l'autoexec.bat et des driver de quelques ko, le temps où on écrivait directement en mémoire vidéo, le temps de l'assembleur, du mode réel et des interruptions.

Hé bien cette époque bien que révolue nous a laissé des traces. Il est toujours possible de coder sans noyau, de faire son propre noyau, de se passer de système d'exploitation ou de booter directement sur une application en quelques secondes.

Boot loader

Choisissons un bootloader, grub par exemple, nous allons lui demander de lancer notre programme. Avec grub il existe plusieurs méthodes pour charger un os, entre autres :

  • Chainloader charge 512 octets, met le processeur en mode en mode réel, tel qu'il serait après le passage du bios. Pratique pour partir de zéro, mais 512 octet c'est un peu petit et nous n'avons pas vraiment envie de gérer le chargement de fichier sur le disque en plus.
  • Linux charge un noyau au format zimage ou bzimage et lui passe le contrôle en mode réel 16 bits. Il est possible de charger des fichiers de plusieurs méga avec le format bzimage.
  • Multiboot charge un noyau au format multiboot et lui passe le contrôle en mode protégé 32 bits après un certain nombre d'initialisations. On utilisera cette méthode une prochaine fois.

zImage

On commence par une solution simple à mettre en place, un exécutable au format zImage. Le format est décrit dans la documentation des sources du noyau linux : /usr/src/linux-2.X.XX/Documentation/i386/boot.txt. C'est dans ce format que nous allons développer un binaire bootable directement.

Le bootloader (grub) lit l'en-tête du fichier zImage puis pose la première section (attention, taille limitée à quelques dizaines de ko) à l'adresse 0x90000, Ensuite, il lit le reste du fichier et le place en 0x1000 (pour une zImage, limitée à 512ko) ou en 0x100000 (pour une bzImage). Ensuite, dans le cas de linux, le noyau s'amuse à redéplacer tout ça selon un processus plus ou moins compliqué décrit ici et ici. Attention, ne prenez pas ces documents pour argent comptant, ils sont un peu dépassés. En pratique nous n'irons pas si loin puisque nous resterons en mode réel et que 512ko nous seront largement suffisant (640Ko ça devrait suffire pour tout le monde !).

Nous allons donc faire simple et récupérer le code de boot de linux pour lancer notre petit projet. Le fichier qui contient le code de lancement ainsi que les en-têtes qui vont bien s'appelle header.S.

Attention, il est écrit en assembleur GNU (gas), celui-ci diffère beaucoup de l'assembleur intel. Le point le plus important est que les arguments sont inversés (source puis destination). header.S Apportons quelques modifications au fichier pour qu'il corresponde à nos besoins :

  • suppression des include
  • changement de la version de format pour se simplifier la vie
-               .word   0x0209          # header version number (>= 0x0105)
+               .word   0x0207          # header version number (>= 0x0105)
  • pas de payload (il faudra tout faire dans 4 secteurs (2Ko)
-payload_offset:                .long input_data
-payload_length:                .long input_data_end-input_data
+payload_offset:                .long 0
+payload_length:                .long 0
  • suppression des appels à puts pour éviter les dépendances inutiles
-       calll   puts
  • ajout des #define correspondant à nos besoins (surtout pour que ça compile)
+#define DEF_SYSSEG       0x1000                                  
+#define DEF_SYSSIZE  0x200
+#define ASK_VGA          0xfffd          /* ask for it at bootup */
+#define STACK_SIZE      512     /* Minimum number of bytes for stack */
+#define CONFIG_PHYSICAL_ALIGN 0x200000
+#define COMMAND_LINE_SIZE 2048

Ce fichier se termine par un appel à la fonction cmain qu'on codera en C, bien plus sympa à écrire que l'assembleur.

Fractint

Comme nous voulons avoir des résultats rapidement, nous allons choisir un projet simple, dessiner l'ensemble de Mandelbrot en vga. Résumons l'algorithme en 3 étapes : initialiser le mode graphique, afficher la fractale et attendre.

On utilise gcc (du projet gnu pour ceux qui suivent ;-), mais son compilateur C ne génère que de l'assembleur 32 bits. Donc la ruse de sioux est de préfixer le code par la directive suivante pour que l'assembleur le transforme code compatible 16 bits (nécessaire puisque nous sommes en mode réel) :

asm(".code16gcc");

Pour le mode graphique, choisissons le mode vga 0x13 (320x200x8), il est facile à utiliser car chaque pixel est mappé sur une adresse en RAM et donc adressable directement en mode réel à l'adresse 0xA0000 (A000:0000 pour les habitués de l'assembleur Intel 16 bits). Les vieux savent bien que la carte graphique se gère avec l'interruption 10h, fonction 0 pour choisir le mode vidéo.

C'est l'occasion de regarder comment on incruste de l'assembleur dans du code C avec gcc :

/* Je vous fais la version longue */
/* Le code assembleur paramétré (d'où les %%) */        asm ("pushl %%ebp; pushw %%ds; int $0x10; popw %%ds; popl %%ebp"
/* L'affectation des valeurs de retour  */                      :
/* L'affectation des paramètres */                              : "a" (mode)
/* La liste des registres à sauvegarder */                      : "ebx", "ecx", "edx", "esi", "edi");

On trouve en ligne les détails pour faire du code assembleur en gcc. Remarquez que si vous n'avez aucun '_:_' (pas de paramètre) les %% doivent être remplacés par des %.

Maintenant, la fonction pour modifier la palette de couleurs (8 bits, 256 couleurs, une palette) avec des valeurs RGB sur 6 bits :

/* la technique d'origine pour modifier la palette vga */
/* on modifie directement des registres de la carte graphique */
        asm( "outb %%al,%%dx" : : "a" (idx), "d" (0x03c8) );
        asm( "outb %%al,%%dx" : : "a" (r), "d" (0x03c9) );
        asm( "outb %%al,%%dx" : : "a" (g), "d" (0x03c9) );
        asm( "outb %%al,%%dx" : : "a" (b), "d" (0x03c9) );

Et la fonction pour modifier un pixel :

/* calcul de la position du pixel en mémoire vidéo */
        int pos = 320*y+x;
/* Technique à la mano car gas ne supporte pas l'adressage 16bits avec des segments
 * - on passe par ds qu'on doit sauvegarder
 * - la mémoire vidéo du mode vga 13h commence en A000:0000
 */
        asm ("push %%ds; movw %%ax,%%ds; movb %%cl,(%%bx); pop %%ds"
                : : "a" (0xA000), "b"(pos), "c" (color));

Le reste est un algorithme basique de calcul de Mandelbrot. Pour rappeler quelques souvenirs :

  • c = (x,y)
  • p0 = (0,0)
  • p(n+1) = p(n)^2 + c
  • plus p diverge vite, plus on est loin de l'ensemble (-> choix de la couleur)

Compilation

Il faut maintenant compiler tout ça pour en faire un binaire au format zImage.

On compile avec gcc en faisant bien attention à ne pas inclure la libc (et à spécifier une architecture 32 bits pour les amd64) :

CFLAGS="-fno-builtin -nostdinc -O2 -I. -Wall -m32"
gcc $CFLAGS -c header.S
gcc $CFLAGS -c kernel.c

Ensuite, il faut faire l'édition de lien (on fait toujours attention à la libc), nous avons besoin d'un script (setup.ld) pour maîtriser complètement le binaire sortant. Il est repiqué de linux, avec une instruction supplémentaire à la fin pour aligner la taille du binaire à celle précisée dans l'en-tête :

LDFLAGS="-nostdlib -Wl,-N"
gcc $LDFLAGS -o mandel.exec -T setup.ld header.o kernel.o

Et on extrait le binaire final de notre binaire au format elf :

# extraction du binaire
objcopy -O binary mandel mandel.exec

Grub, qemu

Il nous faut maintenant tester notre œuvre. Plutôt que de devoir rebooter notre vraie machine, nous allons utiliser une machine virtuelle complète (qemu, vmware, virtualbox ...), dans laquelle on installe grub.

Et dans votre menu (ou dans le shell grub) :

kernel (hd0,0)/boot/mandel

C'est jôliii ! mandel1.png

Pour ceux qui veulent tester, il y a en pièces jointes, les sources ainsi qu'une image disque toute prête pour qemu contenant les sources, le binaire compilé, ainsi qu'un grub préinstallé avec un menu (et le contenu du prochain article avec, mais chut). Pour ceux qui n'ont pas tout suivi, essayez de lire les sources, ca peut être instructif.

$ qemu mandel.dd

Dans le prochain numéro, nous ferons la même chose en plus grand, plus beau et en mode protégé.

PJ :

Niveau : Star Empty Empty Empty Empty
Résumé : Internet Protocol

Pour ceux qui n'ont pas tout compris au dernier article réseau, voici quelques compléments sur le protocole IP. Celui qui vous permet d'envoyer n'importe quelle information à l'autre bout de la planète.

Toute machine sur internet dispose d'une adresse IP (v4 pour l'instant). Écrite sous la forme w.x.y.z où chaque lettre est un nombre compris entre 0 et 255 (donc un total de 32 bits). A chaque fois qu'une machine veut communiquer avec une autre elle remplit un paquet ip qu'elle envoie sur le réseau. Ce paquet soit contenir au moins les informations suivantes :

IP source
IP destination
protocole
Diverses choses

L'ip source, c'est tout simplement l'ip de la machine qui envoie le paquet et l'ip de destination peut être récupérée par le dns si le nom complet de la machine est connu.

Ce paquet doit être envoyé au bon routeur qui se débrouillera avec, et c'est là qu'intervient la configuration de la machine.

Supposons que nous soyons le noyau et essayons de traiter le paquet

#on regarde toutes les routes et on sélectionne celle qui nous intéresse
$ ip route show 
# on regarde l'ip source et la carte à partir de laquelle on va envoyer nos paquets
$ ip addr show
#ces deux opérations peuvent être résumées ainsi
$ ip route get w.x.y.z

La configuration de votre réseau se fait (au niveau du noyau) avec les commandes

# activation de la carte
$ ip link set dev toto up
# mise en place d'un adresse ip (et de son réseau)
$ ip addr add 10.0.1/24 dev toto
# mise en place d'un routage
$ ip route add default via 10.0.0.2

Heureusement, tout cela est fait automatiquement par votre distribution (à la lecture de /etc/nerwork/interfaces pour debian).

Vous aurez remarqué la notion de réseau, qui est apparu sous la forme 10.0.0.1/24. Cela veut dire que l'adresse 10.0.0.1 appartient au réseau constitué des ip de 10.0.0.0 à 10.0.0.255. Nous l'avons déduit grâce au maque /24 (qui s'écrit aussi 255.255.255.0), c'est-à-dire que les 24 premiers bits sont fixe.

On a beau avoir un cerveau brillant et pouvoir faire ceci de tête, le plus simple pour ne pas se tromper est d'utiliser un commande qui fait les calculs pour nous :

$ ipcalc 10.0.0.1/24

Vous remarquerez que dans un réseau il y a toujours 2 adresses qui ne peuvent pas être utilisées : la première, qui sert à désigner le réseau lui-même et la dernière qui sert de broadcast (envoi des paquets à tout le monde). Le plus petit réseau possible est donc un /30 avec 2 ip. Il est possible de faire plus petit, mais ce n'est plus vraiment un réseau au sens IP. On peut faire communiquer 2 IP dans un /31 moyennant quelques variations dans la configuration.

Niveau : Star Star Empty Empty Empty
Résumé : ip_forward ; iptables -j MASQUERADE ; iptables -j DROP ; brctl ; ip

Routeur

Un routeur, c'est une machine qui renvoie les paquets qui ne lui sont pas destinés vers d'autres machines qui les accepteront. Faisons-en un sous linux :

$ echo 1 > /proc/sys/net/ipv4/all/ip_forward

Facile hein ! Maintenant linux (le noyau) renverra tous les paquets qu'il reçoit (au niveau ethernet) mais qui ne lui sont pas destinés (au niveau IP). Reste plus qu'à maintenir une table de routage (avec la commande "route", "ip route" ou avec un démon dédié comme quagga qui le gère dynamiquement).

NAT

Parfois la machine de départ utilise des IP qui n'ont pas le droit d'aller sur internet (c'est pratique pour communiquer en interne, mais c'est tout). Le routeur (la passerelle) doit donc traduire ces IP privées en IP publiques (en général il utilise la sienne). C'est le NAT. Les réseaux ip privées sont : 10.0.0.0/8 172.16.0.0/12 et 192.168.0.0/16

Pour activer le nat sur un linux, il faut déjà qu'il fasse routeur (voir ci dessus), puis on transforme les paquets avec netfilter (netfilter est le fltre du noyau, iptables est la commande pour le piloter) :

$ iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

Firewall

Un firewall va examiner la couche IP et la couche TCP pour filtrer ce qui se passe. Il peut tout faire, couper, changer le paquet ... Tout ceci se fait avec iptables sous linux. C'est un peu complexe, donc voici simplement un exemple qui coupe un utilisateur :

$ iptables -A FORWARD -s 10.0.0.12 -j DROP

Pont

Un pont (bridge) relie 2 (ou plus) réseaux ethernet comme le ferait un switch. Pour faire un bridge sous linux on utilise la commande brctl.

# on crée un nouveau bridge nommé bridge0
$ brctl addbr bridge0
# on ajoute les cartes réseau au bridge (le bridge prend l'adresse mac de la première carte ajoutée)
$ brctl addif bridge0 eth0
$ brctl addif bridge0 eth1

Et voila, à partir de ce moment, les paquets sont bien retransmis sur le réseau, si un paquet arrive sur une interface, il est retransmis sur l'autre s'il y a lieu. Mais si on veut que l'ordinateur qui fait bridge soit aussi sur le réseau en question, il faut y ajouter quelques modifications :

# on désactive la pile ip sur les cartes du bridge
$ ip addr del 10.0.0.1/24 dev eth0
$ ip addr del 10.0.1.1/24 dev eth1
# et on l'active sur le bridge lui-même (représenté pare une carte réseau virtuelle)
$ ip addr add 10.0.0.1/24 dev bridge0

Réseaux multiples

La notion de réseau IP est décorrélé de la notion de réseau ethernet. Il est donc parfaitement possible d'avoir plusieurs réseaux IP sur le même réseau ethernet. Et cela se fait très simplement :

$ ip addr add 10.0.0.1/24 dev eth0
$ ip addr add 192.168.0.1/24 dev eth0

Tunnel

Un tunnel permet d'encapsuler un flux entre 2 machines dans un autre flux. Par exemple le tunnel ssh encapsule un flux tcp dans un flux ssh.

Faisons tunnel ip, avec le module gre du noyau :

# on crée un tunnel nommé tunnel0 
$ ip tunnel add tunnel0 mode gre remote 1.2.3.4
# ce tunnel est en fait une interface virtuelle qu'on configure
$ ip link set tunnel0 up
$ ip addr add 10.0.1.1 dev tunnel0
# un tunnel n'a que 2 bouts, on choisit de gérer le sous-réseau à la main
$ ip route add 10.0.1.2 dev tunnel0

N'oubliez pas qu'il faut faire la même chose à l'autre bout du tunnel (avec des ip inversée bien sûr) et vous avez maintenant une carte réseau sur chaque machine reliées par un câble virtuel.

Niveau : Star Star Empty Empty Empty
Résumé : linux

Imaginez que vous soyez un simple utilisateur (un compte dans une université par exemple), alors les joies de l'administration système vous sont interdites. Heureusement une bande de jeunes hippies a décidé de faire quelque chose contre nature, essayer de transformer le noyau en simple application qui tournerait dans l'espace utilisateur. Et ça a marché, le noyau est maintenant un processus comme les autres.

On peut donc faire tourner linux sur un système existant, théoriquement un bsd ou un solaris peut même faire l'affaire, on a même trouvé des fous pour porter linux sous windows (cherchez colinux, vous trouverez).

Le noyau

Si vous êtes sous debian prenez le paquet user-mode-linux, et si vous êtes fans de la compilation de noyau :

$ make menuconfig ARCH=um
$ make ARCH=um

Et voila, il suffit de lancer linux :

$ linux

Le système

Bon, tout n'est pas si simple, il faudra bien que vous ayez un système de fichier pour ce linux. Si vous n'êtes pas root, vous devrez le préparer sur une autre machine. Il vous faut faire une image disque, soit à partir d'une installation existante, soit une nouvelle installation (par exemple avec debootstrap) :

# récupérer un disque existant
$ dd if=/dev/sda1 of=/data/image.dd

# créer un nouveau disque
$ dd if=/dev/zero of=/data/image.dd bs=1M count=512
$ mke2fs -j /data/image.dd
$ mount -o loop /data/image.dd /mnt
$ debootstrap etch /mnt
$ umount /mnt

Maintenant que nous avons notre image, c'est parti :

$ linux ubd0=/data/image.dd

Les options

Vos pouvez saupoudrer de quelques options, la liste est disponible avec la commande :

$ linux --help

Auxquelles ils faut ajouter les commandes habituelles qu'il est possible de passer au noyau à travers le bootloader. Les commandes intéressantes sont :

  • mem=64M : pour allouer une certaine quantité de mémoire au noyau, c'est toujours une bonne idée de le préciser
  • hostfs=/home/user : pour autoriser l'accès au système de fichier de la machine hôte dans l'uml, il se monte avec la commande suivante :
mount -t hostfs none /host

Le réseau pour un utilisateur

Ensuite vous voudrez sûrement le réseau, et là, beaucoup solutions pour beaucoup de problèmes différents. Commençons pas le cas où vous êtes simple utilisateur (pas root). Vous avez le choix entre utiliser la connexion de la machine hôte ou faire un tunnel vers une machine (où vous êtes root) qui vous laissera accès à une ip et un accès complet.

Prenons le premier cas, vous n'aurez le droit qu'à ce qui est autorisé à un simple utilisateur c'est-à-dire des connexions TCP et UDP (avec une sorte de nat), pas de ping (/bin/ping est suid). Si slirp n'est pas installé, vous devrez le compiler :

# réseau hôte avec slirp
$ linux ubd0=/data/image.dd eth0=slirp,,~/bin/slirp
# le réseau reste à configurer dans l'uml

Dans le cas d'un tunnel. Si vde n'est pas installé, vous devrez le compiler :

# Un switch virtuel
$ vde_switch -s /tmp/switch1
# on y connecte notre nouveau noyau
$ linux ubd0=/data/image.dd eth0=vde,/tmp/switch1
# le réseau reste à configurer dans l'uml

# Sur la machine distante où vous êtes root, l'autre bout du switch
$ vde_switch -tap
# et là nat (vous pouvez aussi faire un bridge si vous voulez)
$ iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE

# Et on connecte les 2 switchs virtuels
# à faire dans le sens qui vous intéresse
$ dpipe vde_plug = ssh remote.machine.org vde_plug

Si vous voulez seulement faire des réseaux d'uml local, vous pouvez aussi les brancher entre eux sans accès à l'extérieur :

# Un switch virtuel connecte 3 linux
$ vde_switch -s /tmp/switch1
$ linux ubd0=/data/image1.dd eth0=vde,/tmp/switch1
$ linux ubd0=/data/image2.dd eth0=vde,/tmp/switch1
$ linux ubd0=/data/image3.dd eth0=vde,/tmp/switch1

# ou si vous ne voulez pas installer vde, uml_switch est fourni directement avec uml
$ uml_switch -s /tmp/switch1
$ linux ubd0=/data/image1.dd eth0=daemon,,,/tmp/switch1
$ linux ubd0=/data/image2.dd eth0=daemon,,,/tmp/switch1
$ linux ubd0=/data/image3.dd eth0=daemon,,,/tmp/switch1

Le réseau pour root

Si vous êtes root vous aurez plus de facilité. Vous avez le choix (en plus des 2 précédents) entre vous attribuer une ip interne et faire du nat ou vous brancher directement sur le réseau de l'hôte (bridge).

Dans tous les cas vous allez faire une interface virtuelle qui apparaîtra sur la machine hôte :

# 10.0.0.1 est l'ip de l'interface virtuelle côté hôte 
$ linux ubd0=/data/image.dd eth0=tuntap,,,10.0.0.1
# À vous de configurer l'interface dans l'uml (par exemple avec l'ip 10.0.0.2)

Je vous laisse faire le reste comme un grand.

Plus

  • Un howto plus détaillé
  • man uml_mconsole vous aidera à manipuler l'uml pendant qu'il tourne
  • Le wiki peut vous aider

Niveau : Star Star Star Empty Empty
Résumé : make menuconfig ; make ; make-kpkg

Un bon sysadmin doit savoir recompiler son noyau, et ce pour plusieurs raisons. Tout d'abord, c'est l'occasion de passer en revue les nouveautés (support d'un nouveau driver, changement d'architecture ...), c'est aussi comme ça qu'on peut ajouter les patchs dont on a besoin. Ensuite cela peut permettre de se passer de l'initrd (et donc de gagner une étape au boot) et enfin cela permet de faire des noyaux purement monolithiques si l'on veut renforcer la sécurité d'équipements critiques.

Avant de compiler un noyau, il faut le configurer. Cette étape est longue, très longue, surtout pour une première fois. Mais c'est aussi la plus intéressante. Je vais donc vous la présenter en long, en large, et surtout en travers. Si cela vous parait lourd, ne partez pas, sautez quelques étapes et revenez-y plus tard.

La base

Commençons par récupérer les sources du noyau. Personnellement je prends les sources officielles de Linus, simples à trouver :

# usuellement on se met dans /usr/src, mais ça n'a rien d'obligatoire
$ lftp ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.XXX.tar.bz2
# qu'on va décompresser
$ tar xfj linux-2.6.XXX.tar.bz2
$ cd linux-2.6.XXX

# les debianeux peuvent aussi avoir les sources patchés debian
$ apt-get install linux-source-2.6.XX

Nous allons récupérer la configuration actuelle du noyau pour partir sur des bases saines et ne pas avoir peur de tout casser si on saute une étape. Le fichier de configuration s'appelle .config

# la meilleur méthode si disponible : lire la configuration en live
$ zcat/proc/config.gz > .config
# sinon récupérer un ancien fichier
$ cp ../linux-2.6.XX-1/.config .
# ou celui fourni par la distrib
$ cp /boot/config-2.6.XX-1 .config

Lançons la configuration :

# en mode texte (il vous faut libncurses5-dev)
$ make menuconfig
# en mode graphique (qvec qt depuis quelque temps)
$ make xconfig 
# ou avec gtk
$ make gconfig

Le mode graphique a l'avantage de vous autoriser à voir les options cachées par des dépendances. Notez que les purs qui veulent uniquement configurer les nouveautés lors de la mise à jour d'un noyau lancent la commande suivante :

$ make oldconfig

Après ceci, on va voir les options, mises en fin d'article pour plus de lisibilité. Lorsque vous ne savez pas quoi mettre, une valeur par défaut vous est généralement suggérée. Le menu est simple et vous permet d'avoir une aide, d'entrer dans un sous menu ou de sélectionner une entrée. Un certain nombre d'entrées (entre <>) peuvent être mises en module (M) ou en dur (*) au choix.

Évitez les valeurs marquées EXPERIMENTAL ou OBSOLETE. NEW indique une entrée que vous n'avez encore jamais modifiée. Une aide est disponible pour la plupart des options. "Généralement" quelque chose en trop prend de la place et du temps de compilation mais ne gêne pas la fonctionnement du noyau. Les distributions activent toujours un très grand nombre d'options pour supporter un maximum d'architectures, en module pour éviter les problèmes de boot.

Et maintenant, on compile :

# le noyau et ses modules
$ make
# si vous êtes sous debian vous allez préférer faire un paquet
$ make-kpkg binary-arch

Et on installe :

# le noyau va dans /boot et les modules dans /lib/modules/2.6.XXX
$ make install
# debian 
$ dpkg -i ../linux-2.6.XXX.deb

Les options

Détaillons les options disponibles (on peut en compter 2000 donc prenez votre temps), enfin les plus importantes.

Ici un 2.6.26 sur amd64, les options varient au cours des versions, mais en général assez peu. Si vous ne voyez pas certaines options, il est possible qu'elles ne soient pas disponibles pour votre architecture, mais il est aussi possible qu'elle dépende d'une autre option que vous n'avez pas activée :

  • General setup : ce qu'on ne sait pas classer ailleurs
    • paging of anonymous memory : support de la swap, indispensable
    • System V IPC : communication interprocessus, indispensable
    • BSD Process Accounting : pour surveiller l'activité de vos processus
    • Kernel .config support : pratique pour récupérer la configuration en live comme on vient de le faire
    • Initial RAM filesystem and RAM disk : pour booter sur un initrd (utile en prévision de ...)
    • Disable heap randomization : sécurisation contre certaines exploitations de failles (si non coché), peut empêcher de vieux programmes (genre libc5) de fonctionner.
  • Enable loadable module support : pour charger des modules en live (modprobe ...)
    • Module unloading : indispensable sauf question de sécurité
    • Automatic kernel module loading : bien pretique pour les desktop
  • Enable the block layer : écriture sur disque, toussa
    • IO Schedulers : la technique utilisée pour optimiser les lectures / écritures sur disque. Utilisez CFQ sauf si vous avez des problèmes spéciaux
  • Processor type and features : le processeur est un périphérique comme un autre
    • Tickless System : c'est grâce à ça que vous faites des économies d'énergies et que la virtualisation ne rame pas
    • High Resolution Timer : c'est bien, vous pourrez théoriquement faire des sleep à la nanoseconde
    • Symmetric multi-processing : multi coeurs, multiprocesseurs, hyperthreading, c'est ici, mais ça ne coûte rien de le mettre dans tous les cas
    • Paravirtualized guest : pour ceux qui veulent acccélérer la virtualisation sous kvm
    • Memtest boot parameter : c'est tout nouveau, le noyau peut faire des tests mémoire
    • Processor family : c'est ici que vous choisissez l'optimisation à la compilation
    • Preemption Model : change la méthode de réaction aux évènements, plus ils réagit vite moins ils peut en traiter (d'où la distinction serveur pour la quantité, et bureau pour la réactivité)
    • Machine Check Exception : à activer, permet par exemple d'éviter que le processeur grille en cas de surchauffe (si c'est supporté)
    • Allow for memory hot-add : c'est explicite non ? Attention, le matériel doit le supporter (ou c'est une machine virtuelle)
    • MTRR : pour gérer les méthodes d'accès aux différentes portions de mémoire, indispensable pour beaucoup de driver vidéo
    • x86 PAT support: pareil en mieux, oui
    • Timer frequency : nombre d'interruptions par seconde de l'espace utilisateur par le noyau (même genre influence que la préemptivité)
    • kexec system call : c'est récent, ça permet de lancer un nouveau noyau à la place d'un noyau existant (utilisé pour les freeze et pour la récupération d'infos en cas de crash)
  • Power management options : a surveiller pour les portables
    • Suspend to RAM and standby : explicite non ?
    • Hibernation (aka 'suspend to disk') : idem
    • ACPI : gère tout ce qui est lié aux trucs électriques dans votre machine, les infos de la batterie, le bouton power, le ventilateur ...
    • CPU Frequency scaling : à activer pour permettre de faire varier la fréquence (et donc la consommation) de votre processeur, les "governor" sont les techniques de gestion, choisissez au moins "userspace" pour permettre de le gérer avec un démon genre cpufreqd, et choisissez le driver correspondant à votre processeur
    • CPU idle PM support : a activer pour encore plus d'économies
  • Bus options (PCI etc.) : toutes les cartes qui se branchent sur la carte mère
    • Notez le support pci, pci express, isa, et le pci hotplug (matériel compatible, ou prière+driver fake pci hotplug)
  • Executable file formats : méthode utilisée pour charger des binaires comme votre shell
    • Kernel support for ELF binaries : indispensable
    • Kernel support for MISC binaries : peu de gens l'utilisent, c'est pour lancer nativement des choses qui ne sont pas des binaires natifs (du java, des programmes DOS ...)
    • Kernel support for a.out binaries : support des vieilles bouses
    • IA32 Emulation : spécial 64 bits pour lancer des exécutables 32 bits
  • Networking : support du réseau, attention c'est long
  • Device Drivers : trèèès long, en général je finis par ça
  • Firmware Drivers : en avez-vous vraiment besoin ?
  • File systems : linux est très fort
    • ext2, ext3, ext4, reiserfs, jfs, xfs : activez ce dont vous avez besoin
    • OCFS2: fs distribué
    • Inotify file change notification : indispensable de nos jours
    • Dnotify : comme inotofy en moins bien, devrais bientôt disparaître
    • Inotify support for userspace : à activer aussi
    • Quota : pour limiter l'espace disque consommé par chaque utilisateur
    • Kernel automounter version 4 : automount, il est probable que vous l'utilisiez
    • Filesystem in Userspace : indispensable de nos jours
    • CD-ROM/DVD Filesystems : oui
    • DOS/FAT/NT Filesystems : c'est vous qui voyez, notez que pour l'écriture sur du NTFS vous n'aurez le droit que de modifier des fichiers existants sans en changer la taille
    • Pseudo filesystems
      • /proc file system : bien sûr
      • Virtual memory file system : fréquemment utilisé pour des fichiers temporaires
    • Miscellaneous filesystems : des systèmes obsolètes ou rarement utilisés
    • Network File Systems : NFS, voisinage réseau windows (SMB pour les vieux, CIFS pour les récents)
    • Partition Types : gestion des partitionnements exotiques (bsd, atari, solaris, volumes dynamiques windows ...)
    • Native language : support d'encodages dans les systèmes de fichiers, indispensable pour ne pas tout casser pendant les copie sur des systèmes non Linux. Choisissez une langue par défaut, celle que vous utilisez pour stocker vos fichiers sur votre /, et que vous utilisez probablement en console.
  • Kernel hacking : plein de bidouilles
    • Magic SysRq key : la seule qui vous intéresse vraiment, elle permet d'utiliser les combinaisons alt-sysrq-x
  • Security options :
    • Enable different security models / Default Linux Capabilities : très utiles pour un certain nombre de logiciels (comme vsftpd), permet de limiter les droit d'un processus, même root
  • Cryptographic API : outils de chiffrement et de checksum
    • ... nombreux algorithmes à activer à volonté, certains le sont déjà car ils sont utiles à d'autres modules (comme IPSec)
  • Virtualization : c'est récent
    • Kernel-based Virtual Machine : virtualisation à base de processeur spécialisé (intel VT, ou amd V)
    • Virtio balloon driver : permet d'augmenter et réduire en live la mémoire allouée aux machines virtuelles
  • Library routines : du bazar, laissez les dépendances se faire

Je séparé 2 sections un peu longues. Le réseau :

  • Networking options : tout ce qui est lié à ip, au dessus, au dessous et à coté, l'usage de tout ce qui se trouve ici est décrit dans le LARTC
    • Packet Socket : permet l'accès direct aux paquets (raw), utilisé par les sniffeurs, ids ...
    • Unix domain sockets : les socket unix, utilisé par tout le monde
    • TCP/IP networking : la seule l'unique
      • IP: multicasting : support du multicast en tant que client ou serveur
      • IP: policy routing : pour faire des choses comme du source routing
      • IP: equal cost multipath : lorsque vous avez plusieurs sorties disponibles sur une passerelle (plusieurs fai par exemple)
      • IP: verbose route monitoring : divers warning sur les paquets reçus
      • IP: kernel level autoconfiguration : client dhcp dans le noyau (ceci devrait en pratique être en userland, mais c'est quasiment indispensable pour le root nfs)
      • IP: tunneling : pour faire des tunnel ip/ip (utilisez plutôt GRE si vous avez le choix)
      • IP: GRE tunnels over IP : tunnels ip over ip mais compatibles cisco
      • IP: multicast routing : si vous êtes un routeur dans une architecture multicast (attention le multicast nécessite aussi des outils userland)
      • IP: TCP syncookie : très pratique pour éviter les synflood, on conseille tout de même de le désactiver car il ne supporte pas un certain nombre d'options TCP
      • AH, ESP, IPComp IPSec : support IPSec sur IPv4
      • Large Receive Offload : regrouper les paquets reçus pour les traiter par bloc (réduit la consommation cpu si vous avez beaucoup de paquets)
      • INET: socket monitoring interface : pour avoir des statistiques sur les communications
      • TCP: advanced congestion control : contrôle de congestion pour ceux qui ont des sites surchargés
      • IP virtual server : pour faire du loadbalancing (par exemple avec keepalived)
      • The IPv6 protocol : devinez (en sous menu on trouve les différentes options ipv6 correspondant aux différentes extensions d'en-têtes
    • Network packet filtering framework (Netfilter) : le firewall
      • Core Netfilter : les options de filtrage et matching non spécifiques ip
      • IP: Netfilter : les options de filtrage et matching spécifiques ipv4
      • IPv6: Netfilter : les options de filtrage et matching spécifiques ipv6
      • Bridge: Netfilter : pour faire du filtrage au niveau ethernet (ou même du nat ethernet, c'est possible :-)
    • 802.1d Ethernet Bridging : faire fonctionner la machine comme un switch (un pont en fait)
    • 802.1Q VLAN Support : support des vlan taggués (c'est la machine qui choisit sur quels vlans elle est)
    • DECnet Support : pour les ancêtres
    • The IPX protocol : aussi
    • Appletalk protocol : aussi
    • QoS and/or fair queueing : pour faire de la régulation de réseau (traffic shaping ou qos)
      • Queueing/Scheduling : les différentes techniques d'ordonnancement des paquets en fonction de leur classes (plus ou moins équitables)
      • Classification : les différentes méthode pour savoir quel paquet mettre dans quelle classe
      • Actions : pour faire un peu plus que de la qos avec les classes précédentes
  • Amateur Radio support : pour ceux qui bidouillent
  • IrDA (infrared) : pour la communication infrarouge avec votre téléphone portable
    • IrCOMM protocol : transformer votre port infrarouge en port série, c'est le moyen de communication le plus couramment utilisé pour les téléphones.
    • IrNET protocol : pour faire du réseau PPP (donc ip) sur votre infrarouge
  • Bluetooth subsystem : les dents bleues
    • L2CAP protocol : base du protocole utile pour avoir le reste
    • SCO links : pour les oreillettes
    • RFCOMM protocol : pour les communication série (transfert de vcard, de fichier unique ...)
    • BNEP protocol : pour les réseaux locaux (PAN) de périphériques bluetooth
    • HIDP protocol : pour les périphériques bluetooth d'entrée comme le clavier ou la souris
  • Wireless : la base des driver wifi
    • Generic IEEE 802.11 Networking Stack (mac80211) : nouvelle pile réseau utilisé (théoriquement) dans tous les nouveaux drivers wifi

Et les drivers, c'est très long, tout n'y est pas :

  • Memory Technology Device (MTD) : les cartes flash
    • ... on y trouve en fouillis les différents drivers
    • UBI - Unsorted block images : la base d'une gestion unifiée des périphériques flash
  • Parallel port support : on en trouve encore :-)
  • Plug and Play support : pareil
  • Block devices : quelques périphériques de bloc qui ne rentrent pas ailleurs
    • Normal floppy disk support : disquettes
    • Loopback device support : les /dev/loopX
    • Cryptoloop : c'est dépassé, utilisez device mapper
    • Network block device support : accéder à des disques à distance (à la iscsi), on peut faire des trucs assez sympa avec ça
    • RAM block device support : les ramdisk, des faux disques entièrement en ram, ceux qui étaient utilisés avant pour les initrd
    • Packet writing on CD/DVD media : pour faire croire à votre système qu'on peut écrire sur un CD comme sur un disque dur
    • ATA over Ethernet support : pour faire comme de l'iscsi mais avec de l'ata (ide ou sata)
  • ATA/ATAPI/MFM/RLL : tous les trucs que vous branchez en ide
    • Enhanced ... : tout sauf les vieilles bouses
      • Include IDE/ATA-2 DISK : indispensable
      • Include IDE/ATAPI CDROM : lecteurs et graveurs cd
      • SCSI emulation : inutile
      • ... les autres options sont des fix pour permettre d'activer proprement le dma (entre autre) sur les différents chipset disponibles
    • Old hard disk : les vieilles bouses
  • SCSI device
    • ... On trouve d'abord les différentes options et périphériques scsi
    • SCSI generic : les trucs scsi qui ne sont pas des disques
    • SCSI disk : il parait que c'est aussi indispensable pour le sata
    • SCSI low-level drivers : les drivers pour les cartes scsi
  • Serial ATA (prod) and Parallel ATA : pour les disques sata (et remplacera aussi bientôt les driver pour ide)
    • ATA SFF support : indispensable, et dessous les support de tous vos chipsets
  • Multiple devices driver support (RAID and LVM) : md (multiple device) et dm (device mapper)
    • RAID : les différentes formes de raid possible
    • Device mapper : ce que je vous ai présenté il y a peu
  • Fusion MPT device support : vous en avez ? vous êtes riche, achetez la doc
  • IEEE 1394 (FireWire) : firewire
    • OHCI-1394 controllers : la plupart des drivers utilisent ceci
  • I2O device : protocole "intelligent..." utilisé par certaines cartes scsi, raid,
  • Macintosh device drivers : la souris a 2 boutons !!
  • Network device : vos cartes réseau
    • Dummy net driver : pour faire des tests
    • Bonding driver : pour coupler 2 cartes réseau en une seule
    • EQL (serial line load balancing) : pour répartir du traffic entre 2 ports série
    • Universal TUN/TAP device driver : des cartes virtuelles (pour les tunnels ou les machines virtuelles)
    • PHY Device support and infrastructure : certains drivers se basent sur celui-ci
    • Ethernet 10, 100, 1000, 10000 : tout est supporté, vous devriez trouver votre bonheur
    • Token Ring driver : papyyyyy
    • Wireless LAN : le wifi
    • Wan interfaces : pour les cartes réseaux longue distance
    • PLIP (parallel port) : bidouilles pour communiquer sur le port parallèle
    • PPP (point-to-point protocol) : pour faire des tunnels ou communiquer sur port série ou modem
    • SLIP (serial line) : même genre
  • ISDN : Il y a encore des abonnés numeris dans la salle ?
  • Input device :
    • Mouse interface : en général oui
    • Joystick interface : pour que les joysticks soient visibles
    • Event interface : l'interface d'avenir pour xorg
    • Miscellaneous devices / PC Speaker support : hé oui il est bien caché
    • ... souris, claviers, joysticks, touchscreen
  • Character devices : ce qu'on accède octet par octet
    • /dev/kmem virtual device : accès direct à la ram
    • Serial drivers : c'est ici
    • Legacy (BSD) PTY : si certains logiciels ne supportent pas encore les pts (ça se fait rare)
    • Parallel printer : tout le monde a ça dans sa poubelle
    • Hardware Random Number Generator Core : dommage, ils ne se trouvent que rarement
    • /dev/nvram : pour ceux qui ont de la nvram (routeurs wifi par exemple)
    • RAW driver (/dev/raw/rawN) : pour lire un périphérique de bloc comme un périphérique de caractères (à qui ca peut bien servir ?)
  • I2C : utilisé parles sensors (température) et par certaines cartes pci. C'est assez difficile de savoir quoi choisir, en général je mets tout sauf le debug
  • Dallas's 1-wire : les i-button ! (vous êtes prof de techno ?)
  • Hardware Monitoring : les capteurs, sélectionnez tout ce sera plus simple
  • Watchdog Timer : si vous avez un watchdog sur votre machine (périphérique physique qui détecte les blocages de la machine pour la rebooter physiquement)
  • Multimedia devices : vos récepteurs TV, satellite, radio
    • Video For Linux : indispensable pour faire de la vidéo (tv, satellite ou caméras)
  • Graphics : les cartes graphiques
    • Direct Rendering Manager : l'accélération avec xorg (indispensable pour la 3D)
    • Support for frame buffer devices : drivers pour l'accès direct à la mémoire vidéo (indispensable pour avoir une console avec un logo ou avec une grosse définition)
    • Console display driver : c'est ici qu'on active le framebuffer en console
    • Bootup logo : les tux affichés pendant le boot
  • Sound : devinez
    • Advanced Linux Sound Architecture : les drivers récents
  • HID Devices : indispensable pour booter avec un clavier usb
  • USB : les drivers usb
    • .. pour ne pas se louper sur les chipset sélectionnez EHCI, UHCI et OHCI
  • MMC/SD card : la communication avec les cartes flash
  • Real Time Clock : accès direct à l'horloge physique
    • ... /dev/rtc est indispensable pour un certain nombre d'application vidéo ou audio

Niveau : Star Star Star Star Empty
Résumé : dmsetup

Maintenant que vous savez tout sur lvm, regardons sous le capot, pour voir comment ça tourne.

lvm

Lvm n'est en réalité qu'une surcouche à un système bien intégré dans linux et qui se nomme device-mapper ou dm en abrégé. Dm ne fait qu'une chose et il le fait bien : mapper (cartographier) un ou plusieurs périphériques de bloc sur un autre périphérique. Donc ce que fait lvm quand vous déclarez un nouveau lv, c'est uniquement déclarer à device mapper qu'un nouveau périphérique de bloc (le lv) correspond à telle et telle portion d'un périphérique physique (les pv).

Bon lvm fait un peu plus que ça puisqu'il sait faire tout ça tout seul au démarrage sans rien vous demander, il stocke ses informations au bon endroit sur les disques et tout marche de façon transparente. Mais supposons que nous voulions le faire nous-même à la main. C'est parfaitement possible avec la commande dmsetup :

# on crée un device nommé monlv, qui fait 10000 blocs et qui est mappé sur /dev/hda à partir du 1234e bloc
$ echo 0 10000 linear /dev/hda 1234 | dmsetup create monlv

Si vous voulez savoir comment sont fait vos lv, il suffit simplement de le demander à dmsetup :

$ dmsetup table /dev/mapper/vg0-lv0
>0 1028160 linear /dev/hda 0
>1028160 3903762 linear /dev/hdb 0

dm

Maintenant, voyons plus loin que lvm, dm vous permet de faire beaucoup de choses (mais à la main). En fait la liste des choses que vous pouvez faire est disponible avec la commande suivante :

$ dmsetup targets
>crypt            v1.5.0
>snapshot-origin  v1.6.0
>snapshot         v1.6.0
>mirror           v1.0.20
>multipath        v1.0.5
>striped          v1.1.0
>linear           v1.0.2
>error            v1.0.1

Cela liste les différentes formes de mapping disponibles dans dm. Vous y reconnaissez des fonctionnalités de lvm, mais aussi d'autres comme error (qui retourne des erreur, pratique surtout pour le développement) ou comme multipath (pour des périphériques auxquels ont peux accéder par des méthodes différentes (redondance)).

Une cible intéressante est crypt qui chiffre à la volée les périphériques de façon transparente. C'est l'utilitaire cryptsetup qui va vous aider à le configurer (hé oui, c'est rigolo à la main, mais c'est pas très pratique). Cet utilitaire vous permettra aussi de gérer différents formats de métadonnées (luks, cryptoloop ...).

Plus rigolo, avec device-mapper vous pouvez tout mélanger et tout superposer. On peut donc imaginer du chiffrement sur seulement la moitié du disque, superposer plusieurs chiffrements, un disque où on laisse des trous, etc.

Et enfin, imaginez que vous puissiez développer votre propre mapper, lequel feriez-vous ? Ne pensez pas que c'est très difficile, il suffit d'écrire 5 fonctions dans le noyau (dont une seule qui fera le mapping). Il est donc possible que si vous avez une bonne idée elle puisse être en développée rapidement. Je vous propose quelques suggestions :

  • Un overlay en ram (lecture sur disque, écriture en ram), pratique pour transformer un fs en lecture seule en lecture/écriture temporaire (déja faisable avec des snapshot et des ramdisk, a la main)
  • Support des qcow et autres format de disque des machines virtuelles
  • Un périphérique avec des checkpoint permettent d'annuler ou de commiter les modifications depuis le dernier checkpoint (plus ou moins jouable avec les snapshot et lvmerge)
  • Un périphérique avec une légère redondance pour récupérer les erreurs du medium (par exemple avec par2)
  • Un système de statistiques sur le périphérique

Et notez une dernière mauvaise idée : faire de la compression. Faire de la compression au niveau périphérique est extrêmement compliqué car cela implique d'avoir des blocs de taille variable et des périphériques de taille variable, ceci n'est actuellement géré ni au niveau des périphériques ni au niveau des systèmes de fichier. La compression est donc bien mieux gérée au niveau du système de fichier.