mardi 21 mai 2013, 22:17:06 (UTC+0200)

NIS, NFSv4, autofs5 & dual stack IPv4 + IPv6

Pour employer une formule marketing à 2€cts, la collection des acronymes donnés dans le titre constitue un «savoureux mélange» entre anachronisme et modernité. Ceci-dit, je me doute bien qu'à la lecture du même titre, d'aucuns auront déjà trouvé que la mixture est on ne peut plus indigeste ! Enfin, n'ayant plus du peur du ridicule, voici une petite introduction sur ce faux-nouveau support : Association NIS, NFSv4 et autofs.

Après avoir adapté les supports de la rubrique développement à IPv6, je me suis posé la question de faire le même travail sur les autres supports de travaux pratiques. Écrire une version IPv6 séparée pour chaque séance de travaux pratiques obligerait à maintenir deux versions pour un même document. Au delà de la quantité de travail, il y a fort à parier qu'une version IPv6 isolée ne serait jamais utilisée vu ce que l'on sait de l'engouement général pour adopter rapidement ce protocole. Il restait donc l'option du document unique double pile ou dual stack dans le jargon. À titre d'entraînement, j'ai repris un vieux support abandonné qui associe trois services.

  • Network Information Service (NIS) permet le partage des paramètres de compte utilisateur. C'est un service démodé qui ne fonctionne qu'avec la pile IPv4 ; ce qui le rend intéressant dans le contexte présent.
  • Network File System (NFS) est le système de fichiers réseau de prédilection dans le monde Unix. Sa version 4 fonctionne très bien avec la pile IPv6.
  • Automounter (autofs) permet l'accès transparent au système de fichiers réseau. On l'utilise lors de la connexion d'un utilisateur pour accéder simplement à son répertoire. Normalement, ce service fonctionne avec IPv6. J'ai découvert avec surprise que son utilisation d'IPv6 est assez singulière puisqu'il semble s'appuyer exclusivement sur les enregistrements DNS.

Même si le but recherché était de «croiser» les usages des deux piles de protocole de couche réseau, le résultat a dépassé mes attentes puisqu'en trois services on dispose de trois modes opératoires distincts.

Pour être complet, il faut ajouter les appels de procédures distants (RPC) sur lesquels sont basés NIS et NFS. C'est justement ce mécanisme qui autorise le mixage entre les deux piles IPv4 et IPv6.

# rpcinfo -s ip6-srvr.fake.domain
   program version(s) netid(s)                         service     owner
    100000  2,3,4     local,udp,tcp,udp6,tcp6          portmapper  superuser
    100004  1,2       tcp,udp                          ypserv      superuser
    100009  1         udp                              yppasswdd   superuser
    100003  4,3,2     udp6,tcp6,udp,tcp                nfs         superuser
    100227  3,2       udp6,tcp6,udp,tcp                -           superuser
    100021  4,3,1     tcp6,udp6,tcp,udp                nlockmgr    superuser
 600100069  1         tcp,udp                          fypxfrd     superuser
    100007  1,2       tcp,udp                          ypbind      superuser
    100005  3,2,1     tcp6,udp6,tcp,udp                mountd      superuser

autofs troubleshooting

Comme je l'ai dit plus haut la version du paquet autofs fourni dans la branche testing semble se baser uniquement sur les enregistrements DNS. Voyons comment je suis parvenu à cette conclusion.

  1. On commence par un montage statique de l'arborescence depuis le poste client.
    # mount -t nfs4 [2a01:240:feb2:10::12]:/home /ahome
    root@clnt:/home/etu# ls -lAh /ahome/etu-nis/
    total 16K
    -rw------- 1 etu-nis etu-nis  385 mai   21  2013 .bash_history
    <snip/>
    
    # mount | grep nfs4
    [2a01:240:feb2:10::12]://home on /ahome type nfs4 \
      (rw,relatime,vers=4,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp6, \
       timeo=600,retrans=2,sec=sys,clientaddr=2a01:240:feb2:10::11,minorversion=0, \
       local_lock=none,addr=2a01:240:feb2:10::12)
    Le montage statique utilisant les adresses IPv6 fonctionne. Rien d'original.
  2. On passe à l'utilisation du démon automount avec des fichiers de configuration complétés directement sur le poste client, toujours avec une adresse IPv6 numérique.
    # cat /etc/auto.master
    /ahome          /etc/auto.home
    
    # cat /etc/auto.home 
    * -port=2049,-fstype=nfs4 [2a01:240:feb2:10::12]:/home/&
    
    # service autofs start
    [ ok ] Starting automount....
    # automount -m
    
    autofs dump map information
    ===========================
    
    global options: none configured
    
    Mount point: /ahome
    
    source(s):
    
      instance type(s): file 
      map: /etc/auto.home
    
      * | -port=2049,-fstype=nfs4 [2a01:240:feb2:10::12]:/home/&
    La configuration a bien été prise en compte par le démon ...
    # ls -lAh /ahome/etu-nis
    ls: impossible d'accéder à /ahome/etu-nis: Aucun fichier ou dossier de ce type
    
    # tail -3 /var/log/syslog 
    clnt automount[3652]: attempting to mount entry /ahome/etu-nis
    clnt automount[3652]: mount(nfs): no hosts available
    clnt automount[3652]: failed to mount /ahome/etu-nis
    Catastrophe ! Le montage automatique échoue.
  3. On reprend la même expérience avec un nom d'hôte DNS dont les enregistrements AAAA et PTR sont valides.
    # service autofs start
    [ ok ] Starting automount....
    # automount -m
    
    autofs dump map information
    ===========================
    
    global options: none configured
    
    Mount point: /ahome
    
    source(s):
    
      instance type(s): file 
      map: /etc/auto.home
    
      * | -port=2049,-fstype=nfs4 vm2.fake.domain:/home/&
    
    # dig +short aaaa vm2.fake.domain
    2a01:240:feb2:10::12
    root@clnt:/home/etu# dig +short -x 2a01:240:feb2:10::12
    vm2.fake.domain.
    L'adresse IPv6 utilisée est bien identique à celle du test précédent.
    # ls -lAh /ahome/etu-nis
    total 16K
    -rw------- 1 etu-nis etu-nis  385 mai   21 23:44 .bash_history
    <snip/>
    
    # tail -3 /var/log/syslog 
    clnt automount[3759]: mounted indirect on /ahome with timeout 300, freq 75 seconds
    clnt automount[3759]: attempting to mount entry /ahome/etu-nis
    clnt automount[3759]: mounted /ahome/etu-nis
    Bingo ! Cette fois-ci le montage se fait correctement. Enfin, si les paramètres de configuration sont publiés via NIS, ça fonctionne aussi.

Voilà. On peut considérer que le choix consistant à associer les trois services est pertinent puisqu'il illustre des usages distincts de la pile IPv6. À mon niveau de connaissances, je ne sais pas si le fait d'imposer l'utilisation des enregsitrements DNS par le démon automount est volontaire.

Dans tous les cours sur IPv6, il est courant de dire que comme le format numérique des adresses IPv6 est très difficile à retenir, l'usage des enregistrements DNS est impératif. Pour généraliser l'utilisation de la double pile réseau, il semble qu'il faille revoir la séquence des travaux pratiques. Je vais devoir aborder le service DNS au tout début de façon à en bénéficier pour le cycle sur le stockage et les systèmes de fichiers réseau. En conclusion, le support de travaux pratiques DNS est le prochain sur la liste !

mardi 5 mars 2013, 21:54:58 (UTC+0100)

Initiation au développement IPv4 + IPv6

La rubrique dev vient d'être enrichie d'un nouveau support sur l'Initiation au développement C sur les sockets IPv4 & IPv6.

Ce qui, au départ, ne devait être qu'un complément au précédent document utilisant seulement IPv4 a finalement abouti à une progression en quatre étapes ou programmes. J'ai conservé l'idée initiale d'échange de chaîne de caractères entre un client et un serveur. Cette forme rudimentaire de chat est très bien accueillie par les étudiants.

La première étape est un plagiat éhonté du programme showip du livre Beej's Guide to Network Programming. La technique de parcours des enregistrements addrinfo issus de l'appel à getaddrinfo() est ensuite reprise dans tous les autres programmes du document. À la deuxième étape, le programme client se contente d'ouvrir la première prise réseau ou socket disponible. Jusque là, tout va bien ! C'est avec le programme serveur que les choses se compliquent. Faut-il utiliser une ou deux prises réseau ?

dual stack single socket

Franchement, je n'ai pas le recul nécessaire pour prendre parti sur cette question. Ce qui est sûr, c'est que la solution «académique» est plus séduisante pour un prof. Le code de la couche application est indépendant des protocoles de la couche réseau et la quantité de code est plus réduite. Ça n'empêche pas de souffrir un peu sur l'utilisation de l'option bindv6only et sur l'interprétation des codes d'erreurs associés. J'ai fini par aboutir au tableau de tests suivant. Les deux étapes restantes proposent les programmes serveurs avec une puis deux prises réseau.

client ou talker

Serveur ou listener

socket unique

bindv6only = 0

Serveur ou listener

socket double

bindv6only = 1 -> socket IPv6

Client dual stack

disable_ipv6 = 0

IPv6

IPv6

Client single stack

disable_ipv6 = 1

IPv6

IPv4-mapped IPv6 addresses

IPv4

Une fois la difficulté de gestion des options sur les sockets franchie, la dernière étape avec le codage d'un serveur dual stack à deux prises réseau permet de se familiariser avec l'utilisation de la fonction select() et des macros associées.

Pour conclure, l'utilisation conjointe des deux protocoles IPv4 et IPv6 entraîne un niveau de difficulté plus important dans la manipulation des enregistrements d'adresses IP. Ce pas supplémentaire peut être délicat à franchir pour un public débutant. C'est certainement la raison pour laquelle les enseignants préfèrent s'en remettre à des bibliothèques de plus haut niveau pour aborder les sockets. En Génie Électrique, les développements sont très proches de l'espace noyau et le langage C reste incontournable, ce qui rend le compromis délicat à négocier.

Comme d'habitude, si quelqu'un à le courage de s'attaquer à la lecture du document, je serais très content de lire toutes les remarques ou critiques !

jeudi 7 février 2013, 17:45:15 (UTC+0100)

Architecture système et initiation au développement réseau

Avant les séances de travaux pratiques sur l'initiation au développement sur les sockets avec les étudiants de seconde année de DUT GE2I, j'ai voulu passer à une version dual stack IPv4 et IPv6 des programmes étudiés. L'évolution relativement récente consiste à remplacer les appels à 'gethostbyname' par l'utilisation de 'getaddrinfo' qui renvoie les adresses des deux versions du protocole IP via des pointeurs sur des enregistrements 'addrinfo' chaînés. Tout ceci fonctionne très bien et la façon dont les données sont structurées est très intéressante à étudier ... à mes yeux.

On constate une fois de plus que le niveau requis pour accéder à la compréhension d'une technique ou d'une technologie augmente sans cesse. Pour un débutant complet, il devient de plus en plus difficile de se hisser à un niveau permettant de s'accrocher au train en marche. L'argument qui vient immédiatement consiste à dire que les étudiants actuels sont nés avec l'intégration système et qu'ils utilisent très bien leur smartphone sans se soucier de l'encapsulation des paquets IP dans les réseaux radio numériques. Mon point de vue repose sur le fait que le niveau correspondant à «l'utilisation» des systèmes n'est pas suffisant pour accéder à un emploi et un niveau de revenus décent. Je tente donc à nouveau ma chance en présentant quelques éléments sur l'architecture système. L'idée est de montrer que les systèmes d'exploitation des «dispositifs mobiles» dont les étudiants sont friands ont une longue histoire chaotique mais continue. Suivant cette idée, les «petits programmes pour débuter» viennent bien s'intégrer dans les technologies utilisées au quotidien.

Sous-système réseau du noyau Linux

La présentation est disponible aux formats ODP et PDF. Si vous avez envie d'apporter des corrections, n'hésitez pas !

jeudi 22 novembre 2012, 17:51:40 (UTC+0100)

Aire OSPF et passerelles multiples (2ème)

Suite à l'article Aire OSPF et passerelles multiples et au cycle de travaux pratiques des M1 STRI sur l'interconnexion des réseaux locaux et étendus, voici une variante d'utilisation du marquage de paquets avec des tables de routage multiples. L'idée est toujours d'implanter un mécanisme de tolérance aux pannes entre les passerelles d'une aire OSPF et un routeur de niveau «supérieur».

Interconnexion avec deux routeurs de bordure OSPF

Ici, les trois routeurs protagonistes ont chacun une interface dans le même domaine de diffusion et on utilise un octet d'adresse MAC pour identifier et distinguer les passerelles.

Pour un nouveau flux sortant de l'aire OSPF, il y a ...

  1. Marquage du premier paquet en fonction de l'adresse MAC source dans la chaîne PREROUTING de la table mangle.
  2. Mémorisation du marquage de paquet dans le mécanisme de suivi d'état du système de filtrage (connmark)
  3. Entrée dans la table de routage dédiée au routeur de bordure à l'origine du flux.

Pour un flux retour dans l'aire OSPF, il y a ...

  1. Restauration du marquage de paquet en fonction des enregistrements effectués via le mécanisme de suivi d'état
  2. Entrée dans la table de routage dédiée au routeur de bordure vers lequel le flux doit être dirigé.

La configuration correspondante est alors obtenue avec ...

  • Une table de routage par routeur de bordure :
    # cat /etc/iproute2/rt_tables 
    #
    # reserved values
    #
    255     local
    254     main
    253     default
    0       unspec
    #
    # local
    #
    #1      inr.ruhep
    72      centares
    79      naboo

    # ip route add 10.0.16.0/20 via 172.16.1.1 table centares
    # ip route add 10.0.32.0/20 via 172.16.1.1 table centares
    # ip route add default dev bond0 table centares

    # ip route add 10.0.16.0/20 via 172.16.4.1 table naboo
    # ip route add 10.0.32.0/20 via 172.16.4.1 table naboo
    # ip route add default dev bond0 table naboo
  • Le plan d'adressage est donné dans le support Étude de cas sur l'interconnexion LAN/WAN.

  • Des règles de marquages utilisant la table mangle :
    # iptables -t mangle -A PREROUTING -i bond0.1 -m mac --mac-source 00:1f:c6:01:26:72 -j MARK --set-mark 72
    # iptables -t mangle -A PREROUTING -i bond0.1 -m mac --mac-source 00:1f:c6:01:26:79 -j MARK --set-mark 79
    # iptables -t mangle -A PREROUTING -i bond0.1 -j CONNMARK --save-mark
    # iptables -t mangle -A PREROUTING -i bond0 -j CONNMARK --restore-mark
  • Ici, l'interface bond0 correspond au lien vers l'Internet et l'interface bond0.1 appartient au même réseau local que les deux routeurs de bordure.

  • Des règles d'entrée dans les tables de routage en fonction des marques :
    # ip rule add fwmark 72 table centares
    # ip rule add fwmark 79 table naboo

Voilà ! Cette configuration n'est qu'un exemple supplémentaire d'utilisation des tables de routage multiples sur les systèmes GNU/linux. Cette technique est trop souvent considérée à tort comme «effrayante». Dès lors que l'on accepte de traiter le problème pas à pas, la «représentation intellectuelle» de la gestion des flux se met plus facilement en place ;).

Une fois de plus, le «copié-collé de la mort» si cher aux étudiants ne rend pas service et ne permet pas de visualiser les mécanismes en jeu.

dimanche 23 septembre 2012, 17:24:57 (UTC+0200)

Comment résoudre un problème d'accès à la configuration du service LDAP

En révisant le support de travaux pratiques sur l'introduction aux annuaires LDAP avec OpenLDAP, j'ai perdu un certain temps avant de pouvoir traiter la partie sur l'analyse de la configuration du service LDAP. Relativement à l'édition précédente, les versions du paquet source openldap ont évolué et un petit détail sur la résolution des noms m'avait échappé. Bien sûr, me direz-vous, dans la vraie vie tout le monde installe son service LDAP en ayant préalablement validé que le serveur est connu du service DNS. Eh bien, pas moi ! Dans un contexte de travaux pratiques, on cherche à isoler les services les uns des autres de façon à ne traiter qu'un seul problème bien identifié à la fois. Voyons donc comment on peut se fourvoyer facilement et quelle démarche aurait pu être suivie d'entrée de jeu.

Au début, il était question d'authentification ...

Pour valider les questions et les réponses du TP, on configure de façon très classique deux instances de machines virtuelles avec le système de base et le service SSH en Debian/testing : un serveur et un client. On effectue les mises à jour et on renomme les machines. On obtient avec la topologie suivante :

          |eth0
         _|____
       (_______)           noyau
       |Routeur|    <-- système hôte
       (_______)
          |br0  @IP: 198.51.100.1
       ___|______
      |          |  <-- openvswitch
      `-.------.-'
	|tap0  |tap1
        |    --'.
        |    |' |hostname : vm-ldap-client
        |    |. |@IP      : 198.51.100.3
      --'.   `--'
      |' |hostname : vm-ldap-server
      |. |@IP      : 198.51.100.2
      `--'

Après installation des deux paquets slapd et ldap-utils sur le serveur, on teste l'accès à la configuration.

Traitement des actions différées (« triggers ») pour « man-db »...
Paramétrage de libltdl7:amd64 (2.4.2-1.1) ...
Paramétrage de libodbc1:amd64 (2.2.14p2-5) ...
Paramétrage de libperl5.14 (5.14.2-13) ...
Paramétrage de libslp1 (1.2.1-9) ...
Paramétrage de slapd (2.4.31-1) ...
  Creating new user openldap... done.
  Creating initial configuration... done.
  Creating LDAP directory... done.
[ ok ] Starting OpenLDAP: slapd.
Paramétrage de ldap-utils (2.4.31-1) ...
localepurge: Disk space freed in /usr/share/locale: 0 KiB
localepurge: Disk space freed in /usr/share/man: 0 KiB

Total disk space freed by localepurge: 0 KiB

# ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" dn
ldap_sasl_interactive_bind_s: Local error (-2)

Eh paf ! C'est à partir de là que les choses ont mal tourné. Je me suis focalisé sur l'authentification SASL et non sur la partie interactive_bind_s du message. Pourtant, cette instruction est bien celle préconisée dans le fichier de documentation du paquet slapd : /usr/share/doc/slapd/README.Debian.gz

Je me suis lancé dans la configuration de l'authentification SASL avec le paquet sasl2-bin et le mécanisme PAM pour authentifier les utilisateurs locaux dont le super-utilisateur qui doit avoir accès sans restriction à la configuration du ou des annuaires LDAP configurés sur le serveur. Bien que cette authentification soit fonctionnelle, j'obtenais toujours le même message d'erreur. La situation était bloquée. Pas moyen de modifier le DIT.

À la fin, il n'était plus question que de résolution des noms

Au lieu de m'enfermer dans cette voie sans issue, j'aurais dû commencer par faire appel à strace.

# strace ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" dn
<snipped>
recvfrom(4, "h4\201\203\0\1\0\0\0\1\0\0\16vm-ldap-server\0\0\34\0\1"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.1")}, [16]) = 107
gettimeofday({1348409171, 770189}, NULL) = 0
poll([{fd=4, events=POLLIN}], 1, 4998)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [107])               = 0
recvfrom(4, "6\361\201\203\0\1\0\0\0\1\0\0\16vm-ldap-server\0\0\1\0\1"..., 1941, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.1")}, [16]) = 107
close(4)                                = 0
write(2, "ldap_sasl_interactive_bind_s: Lo"..., 47ldap_sasl_interactive_bind_s: Local error (-2)
) = 47
write(3, "0\5\2\1\1B\0", 7)             = 7
close(3)

Voici à nouveau le message d'erreur consécutivement à une transaction réseau avec des sockets. Mais que diable vient faire cette requête réseau dans une manipulation locale au système ? On passe donc à l'analyse réseau sur le système hôte ; celui avec l'adresse IP 198.51.100.1.

# tshark -i br0 ! port 22
tshark: Lua: Error during loading:
 [string "/usr/share/wireshark/init.lua"]:45: dofile has been disabled
Running as user "root" and group "root". This could be dangerous.
Capturing on br0
  0.000000 198.51.100.2 -> 198.51.100.1 DNS 74 Standard query 0x6213  A vm-ldap-server
  0.000266 198.51.100.1 -> 198.51.100.2 DNS 149 Standard query response 0x6213 No such name
  0.016816 198.51.100.2 -> 198.51.100.1 DNS 74 Standard query 0x2cd4  A vm-ldap-server
  0.016885 198.51.100.2 -> 198.51.100.1 DNS 74 Standard query 0x28a4  AAAA vm-ldap-server
  0.017050 198.51.100.1 -> 198.51.100.2 DNS 149 Standard query response 0x28a4 No such name
  0.017110 198.51.100.1 -> 198.51.100.2 DNS 149 Standard query response 0x2cd4 No such name

Bingo ! la requête locale sur le DIT de l'annuaire à besoin de connaître la correspondance entre nom et adresse IP. On se jette sur l'édition du fichier /etc/hosts dans lequel on donne l'information manquante.

# head -4 /etc/hosts
127.0.0.1       localhost
127.0.1.1       vm-ldap-server

198.51.100.2    vm-ldap-server

Ouf ! Une nouvelle exécution de la commande ldapsearch ne produit plus d'erreur et on peut enfin modifier le DIT de l'annuaire.

# ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" dn
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
# extended LDIF
#
# LDAPv3
# base <cn=config> with scope subtree
# filter: (objectclass=*)
# requesting: dn 
#
<snipped>
# search result
search: 2
result: 0 Success

# numResponses: 12
# numEntries: 11

En guise de conclusion

C'est une nouvelle petite leçon d'humilité. On s'imagine être expérimenté et on se fait attraper comme un débutant sur un détail insignifiant en apparence.

Maintenant, pour rebondir sur le billet intitulé 7 astuces pour rapporter les bogues Debian efficacement et voir vos problèmes résolus, est-ce que le problème que je viens de rencontrer mérite un rapport de bug ? Il m'est déjà arrivé dans le passé d'émettre des rapports qui ont fait flop. Ils étaient soit hors sujet (#616487), soit sans aucun intérêt.