Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.
Première rédaction de cet article le 6 Janvier 2009
Vous voulez une maison de trois étages à Londres ? Plutôt au Canada, près d'un lac ou en Norvège dans la montagne ? Ou bien un appartement dans le centre de Barcelone ? Vous pouvez avoir tout cela gratuitement en adhérant à un système d'échange de maisons comme Homelink.
Outre l'économie que cela représente par rapport à l'hôtel, ce système a d'autres avantages. On est dans une maison, pas dans une chambre impersonnelle, on a de la place, on cuisine ce qu'on veut quand on veut. Et on vit un peu plus comme les habitants et un peu moins comme un touriste.
Le principe est simple : ils viennent chez vous et vous venez chez eux. Chacun laisse un trousseau de clés, un peu à manger dans le frigo et va découvrir une autre maison.
Il existe plusieurs associations d'échange de maisons. Je suis membre de Homelink, une des plus anciennes. Avant l'Internet, Homelink éditait un gros catalogue papier annuel, regroupant toutes les offres des membres, catalogue distribué à grands frais et plus à jour dès sa parution. Aujourd'hui, c'est plus simplement un site Web, avec un moteur de recherche pour pouvoir passer ses soirées à rêver devant des villas californiennes ou des cabanes en Estonie. Une fois les maisons choisies, on écrit à l'autre membre et on essaie de le convaincre de faire un échange. Bien sûr, il faut souvent écrire des dizaines de messages pour un échange effectif mais on reçoit aussi des propositions spontanées, souvent exotiques.
Il faut donc « se vendre ». Si on habite, comme moi, à Paris, tout est plus simple, beaucoup de gens veulent aller à Paris. Si, comme dans une proposition que j'ai reçu, on est dans une maison perdue au milieu du Hainaut belge, il faut argumenter d'avantage. Notez que les villes sont plus représentées que les campagnes et que le Nord l'est beaucoup plus que le Sud. Si vous pouvez encore trouver une maison à Rome, à partir de la Sicile, cela devient bien plus difficile.
Outre le fossé géographique, il y a aussi un fossé social, on croise surtout des CSP+ (en partie à cause du coût de l'adhésion à Homelink, en partie parce qu'il faut être bien logé pour avoir ses chances de faire un bon échange).
Lorsque j'explique le système à des personnes qui ne le connaissent pas, la réaction immédiate est souvent de crainte : « Mais ils vont tout voler chez moi ! » ou, plus psychologique, « Jamais je ne laisserai des gens que je ne connais pas chez moi ! ». Je ne peux rien dire contre l'argument psychologique mais, sur celui de sécurité, il faut relativiser. La plupart des échanges se passent bien et, lorsqu'il y a des problèmes, c'est moins grave qu'un vol, ce sont plutôt des désaccords sur la quantité de ménage à faire avant de partir. Alors, des risques, oui, il y en a certainement (à l'hôtel aussi) mais, si on n'a pas 100 000 € en liquide chez soi, le risque de vol est nettement plus faible que celui de perte d'une assiette ou deux, suite à une vaisselle trop énergique.
Conçu longtemps avant la mode du Web 2.0 et des réseaux sociaux, le site Web d'Homelink n'a pas de ces mécanismes de vote, de notation ou de réputation qui déclenchent tant de passions et de crises sur eBay ou sur Stack Overflow. C'est reposant. Les seuls critères de sélection sont la maison elle-même, l'accord de l'autre, et le nombre d'échanges qu'il a déjà fait.
Date de publication du RFC : Août 1998
Auteur(s) du RFC : Andy Heffernan (cisco Systems)
Chemin des normes
Première rédaction de cet article le 6 Janvier 2009
TODO: liens WP
Le protocole TCP est vulnérable à plusieurs attaques fondées sur l'envoi de paquets prétendant venir d'un correspondant légitime, alors qu'ils sont injectés dans le réseau par le méchant. La communauté des opérateurs Internet est depuis longtemps particulièrement soucieuse de ces risques pour les sessions BGP entre ses membres et a donc développé ce bricolage qui consiste à insérer une option TCP validant très partiellement les paquets. (Bien que cette vulnérabilité de TCP soit très générale, cette solution n'a été utilisée que pour BGP. Le résultat est un très court RFC, traitant - assez mal - un problème très particulier.)
Une attaque typique contre BGP est une DoS où le méchant envoie un paquet TCP RST (ReSeT, sections 3.1 et 3.4 du RFC 793) qui va couper brutalement la session. Les techniques récentes comme TLS ne protègent pas contre ce genre d'attaques car elle opèrent juste en dessous de la couche application, et ne peuvent pas authentifier les paquets TCP (section 1). Même chose si on avait une solution purement BGP.
La solution de ce RFC est donc (section 2) d'insérer dans chaque segment TCP un résumé cryptographique MD5 portant sur une partie des en-têtes IP et TCP, les données, et un secret partagé entre les deux routeurs. L'attaquant ne connaissant pas ce mot de passe, il ne peut pas fabriquer de faux paquets.
À noter que, bien qu'enregistrée dans le registre IANA des options, ce n'est pas une « vraie » option TCP : les options TCP (section 3.1 du RFC 793) ne sont pas, comme leur nom l'indique, obligatoires. Chacun des deux pairs peut les refuser. Ici, pour éviter toute attaque par repli, la signature TCP MD5 est à prendre ou à laisser.
Passons aux bits sur le câble, avec la section 3 qui décrit le format dans le paquet. Puis aux considérations pratiques avec la section 4 qui détaille les problèmes qui peuvent se produire avec les signatures MD5, comme les soucis qui peuvent provenir car des paquets TCP légitimes peuvent être refusés (section 4.1 mais qui ne mentionne pas le fait que des paquets ICMP, non validés par la signature MD5, peuvent par contre être acceptés alors qu'ils sont faux), le coût en performance à cause des opérations cryptographiques (section 4.2 mais il faut relativiser : lorsque le RFC a été écrit, l'exemple de processeur de routeur était le MIPS R4600, à 100 Mhz) ou l'augmentation de la taille des paquets (section 4.3).
Plus sérieux est le problème de la vulnérabilité de MD5. Elle était déjà bien connue à l'époque (section 4.4) et ça ne s'est évidemment pas arrangé. (En décembre 2008, une attaque réussie contre MD5 a permis de générer de faux certificats X.509). À noter que le RFC ne prévoit pas de moyen d'indiquer un autre algorithme cryptographique que MD5, pour les raisons expliquées dans cette section 4.4. De l'avis de certains, dans le cas de BGP, MD5 reste encore relativement raisonnable.
Presque tous les routeurs dédiés savent utiliser cette option MD5 TCP. Ainsi, sur IOS, c'est le mot-clé password :
router bgp 64542 neighbor 192.0.2.108 password g2KlR43Ag
La situation est plus délicate sur les routeurs non-dédiés, par
exemple fondés sur
Unix. Linux ou
FreeBSD n'ont pas eu pendant longtemps le support du RFC 2385 en série (il existait des patches comme http://hasso.linux.ee/doku.php/english:network:rfc2385), en partie pour une question de principe (cette
option viole le modèle en couches avec ses aller-retours entre
l'application et TCP). (Pour Quagga, voir
certains liens en http://wiki.quagga.net/index.php/Main/AddRes.) Aujourd'hui, le
noyau Linux (par exemple en version 2.6.26) a cette fonction (au
moment de la compilation, activer TCP: MD5 Signature Option
support (RFC2385) (EXPERIMENTAL), ce qui est le cas sur
Debian par défaut).
En pratique, l'exigence de sessions « TCP MD5 » a souvent servi à éliminer ceux qui utilisent Quagga sur Unix et n'avaient pas de moyen facile de configurer leurs routeurs selon cette option.
Une autre objection fréquente faite à ce RFC 2385 est qu'il aurait mieux valu utiliser IPsec, qui a l'avantage de couvrir les autres protocoles (comme ICMP) et d'être plus générique. Mais IPsec, notamment en raison de sa complexité, a connu peu de déploiements.
Le groupe de travail tcpm de
l'IETF est en train de travailler sur un
meilleur protocole, actuellement à l'état
d'Internet-Draft, draft-ietf-tcpm-tcp-auth-opt.
Date de publication du RFC : Mai 1998
Auteur(s) du RFC : Vern Paxson (MS 50B/2239), Guy Almes (Advanced Network & Services, Inc.), Jamshid Mahdavi (Pittsburgh Supercomputing Center), Matt Mathis (Pittsburgh Supercomputing Center)
Pour information
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 4 Janvier 2009
La mesure des performances sur Internet est un vaste sujet, rendu plus difficile par l'utilisation massive de mots mal définis ou, pire, utilisés à contre sens (« débit », « bande passante » ou, pire, « vitesse » étant de bons exemples). C'est pour cela que le travail du groupe IPPM (IP Performance Metrics) a commencé par ce RFC 2330 qui essaie de définir un cadre solide pour parler de mesures de performances.
Prenons l'exemple d'un cas qui semble trivial : un liaison Internet est saturée et on n'arrive plus à y faire passer tous les films qu'on veut regarder. Pour la remplacer, on hésite entre deux liaisons dont on voudrait bien mesurer la capacité, afin de choisir la « meilleure ». Mais que veut dire exactement « cette liaison permet un débit de 20 Mb/s » ? Ce chiffre est mesuré dans quelle couche ? Est-il valable en permanence ou bien est-ce une moyenne ? Dépend-il de la taille (ou d'autres caractéristiques) des paquets ? Sans réponse claire à ces questions, le chiffre de 20 Mb/s ne signifie pas grand'chose. D'autant plus que les vendeurs ont tout intérêt à brouiller les pistes. (Au fait, cette notion, nommée « capacité », est définie dans le RFC 5136.)
La section 3 du RFC résume les buts : définir des métriques, c'est-à-dire des grandeurs mesurables et définies de manière rigoureuse. Pour cela, notre RFC spécifie le cadre commun à toutes les métriques, qui seront développées dans des RFC ultérieurs (comme le RFC 2678 pour la métrique de connectivité ou bien le RFC 2681 pour la métrique de temps d'aller-retour entre deux points).
Quels sont les critères pour une « bonne » métrique ? C'est la question que traite la section 4. Parmi ces critères :
On voit que les questions de vocabulaire vont être cruciales. Pour rompre avec le flou des données, il faut que les grandeurs mesurées soient définies d'une manière rigoureuse et précise. Ainsi, la section 5 donne des définitions pour un certain nombre d'éléments qui seront utilisés dans les métriques. Par exemple, host, machine connectée au réseau, inclus explicitement les routeurs, link est un lien de couche 2, path un ensemble contigu de links, etc.
Une fois ces bases de langage établies, le RFC s'attaque aux concepts (section 6). Il s'attache à bien établir la différence entre la définition d'une métrique (qui doit avant tout être complète et rigoureuse) et la mesure effective (qui n'est pas toujours facile, comme illustré plus loin par la discussion sur la mesure du wire time).
Cette section est aussi l'occasion de poser certaines conventions comme le fait que les temps se mesurent toujours en UTC et que les kilos valent toujours 1000 (suivant la règle des télécommunications, celle du stockage informatique étant plutôt qu'un kilo vaut 1024).
Une fois une métrique définie, il faut la mesurer : c'est l'objet de la section 6.2. On peut le faire directement en mesurant la grandeur qui nous intéresse ou indirectement, par exemple en calculant ou en déduisant cette grandeur d'autres mesures. Les particularités de la mesure doivent être soigneusement étudiées et publiées avec les résultats. Par exemple, certaines mesures modifient la grandeur mesurée (des pings répétés chargent le réseau et modifient donc le temps d'aller-retour que ping est censé mesurer). La science de la mesure est aussi ancienne que la physique et ces problèmes, tout comme ceux des erreurs ou incertitudes (section 6.3) sont bien connus, mais souvent oubliés lorsqu'il s'agit de réseaux informatiques. Le RFC a donc raison de les rappeler et d'insister sur l'importance de les documenter lorsqu'on publie.
Les métriques ne vivent pas seules : il est souvent utile de les composer, ce que traite la section 9. Elle décrit en 9.1 les compositions spatiales où les mesures sur des sous-chemins (subpath) s'additionnent. Par exemple, le délai de propagation d'un paquet le long d'un chemin (path) est proche de la somme des délais de propagation sur les sous-chemins. En 9.2, on trouve les compositions temporelles, où le passé permet de prédire le futur. Par example, si on a mesuré le débit sur un lien depuis vingt-quatre heures, et que sa variation selon l'heure est bien comprise, on va pouvoir déduire le débit futur pendant les prochaines vingt-quatre heures.
La plupart des mesures, en matière de réseau, font intervenir le temps. Le concept est souvent mal compris et une section entière, la 10, est dédiée à Chronos. Certes, l'Internet dispose depuis longtemps d'un protocole standard pour la gestion d'horloges, NTP (RFC 1305). Mais ses buts sont différents : il vise à garder des horloges synchronisées sur une longue période. Pour la mesure, on s'intéresse à des périodes plus courtes pour lesquelles NTP n'est pas forcément la solution.
Quels sont les problèmes avec les horloges ? Une horloge peut avoir un écart (offset) avec le « vrai » temps, UTC (ou bien avec une autre horloge, si on s'intéresse juste au fait qu'elles soient synchronisées). Si cet écart est inférieur à une certaine valeur, on dit que l'horloge est correcte (accurate). Et l'horloge peut avoir un décalage (skew), elle peut avancer plus ou moins vite que la normale (et donc cesser au bout d'un moment d'être correcte). Pire, la dérive peut être variable, auquel cas on mesure la dérivée seconde du décalage sous le nom de dérive (drift). Enfin, l'horloge a une certaine résolution (resolution), la plus petite unité de temps qu'elle peut mesurer.
Pour minimiser l'effet de ces limites sur la mesure, le RFC recommande donc que les horloges des différentes machines impliquées soient synchronisées, idéalement à partir d'une source extérieure commune (comme le GPS). Le RFC illustre l'importance de la synchronisation par un simple exemple. Si on mesure le délai de transmission d'un paquet sur un lien transaméricain, une valeur de 50 ms est raisonnable. Si le décalage est de 0,01 %, après seulement dix minutes, l'écart atteindra 60 ms soit d'avantage que ce qu'on veut mesurer.
NTP n'est pas forcément la bonne solution car son exactitude dépend des liens Internet, exactement ceux dont on veut mesurer les caractéristiques. D'autre part, NTP donne la priorité à la correction, pas à la limitation du décalage. NTP peut délibèrément accélérer ou ralentir une horloge (augmenter ou diminuer le décalage) pour la rapprocher du temps correct. On n'a pas envie que cela survienne pendant une mesure !
Un autre problème de mesure lié au temps est celui du wire time (section 10.2). Les mesures sont souvent effectuées sur les machines elle-mêmes, et pas via un équipement spécial connecté au câble. Les machines ne sont pas forcément optimisées pour la mesure et n'ont pas forcément un noyau temps réel. Elles peuvent donc introduire leurs propres inexactitudes. Pour séparer les délais internes à la machine des délais du réseau, le RFC recommande d'utiliser dans les métriques le « temps du câble » (wire time), pas le temps de la machine. Le premier reflète le moment où le paquet est effectivement présent sur le câble. Plus rigoureux, il est par contre plus difficile à mesurer. Ainsi, pour un programme comme ping, le temps de la machine (host time) est facilement connu grâce à gettimeofday alors qu'il n'y a pas de moyen de mesurer le temps du câble, depuis une application Unix ordinaire. C'est donc une instance particulière du problème discuté plus haut, où le souci de donner une définition correcte de la métrique a priorité sur la facilité de mesure.
Pour réaliser une mesure du temps du câble, le RFC recommande d'utiliser un filtre à paquets comme le BPF qui associe à chaque paquet une estampille temporelle en général proche du temps du câble. Il recommande également de ne pas faire tourner ce filtre sur la machine qu'on veut observer, pour limiter les perturbations.
Les mesures impliquent des statistiques et la section 11 quitte les rivages de la physique pour revenir à ceux de la mathématique. Elle est consacrée à la différence entre les mesures uniques (singleton) et les échantillons (samples), ensembles de mesures uniques. Par exemple, un échantillon de mesures consiste en « une heure de mesures uniques, chacune prise à intervalles de Poisson avec un écart moyen d'une seconde entre les mesures ». L'échantillonnage est un art délicat et la section 11.1 expose différents moyens de le réaliser. Par exemple, le plus simple est la mesure périodique, à intervalles fixes. Mais elle a des défauts :
Le RFC recommande donc d'échantilloner à intervalles aléatoires, suivant une certaine distribution. Quelques exemples de telles distributions suivent comme celle de Poisson en section 11.1.1.
Autre piège de la mathématique, les probabilités (section 12). Le RFC met en garde contre leur abus lors de la définition d'une métrique. Dire que 34 paquets sur 100 ont été perdus (ce que ping affiche sous l'étiquette packet loss) est un fait, dire que « la probabilité de perte d'un paquet est de 0,34 » est une interprétation, qui suppose que le phénomène soit aléatoire (ce qui est faux, la perte des paquets, contrairement à la désintégration d'un atome radioactif est très déterministe).
Outre les questions physiques et mathématiques, la mesure cache des
pièges liés à l'informatique. Pour plusieurs raisons, les différents
paquets ne reçoivent pas le même traitement. Par exemple, certains
FAI ralentissent délibérement les paquets
considérés comme liés à un protocole pair à pair. La mesure dépend donc du type de paquets, ce que la
section 13 formalise avec la notion de « paquets de type
P ». Lorsqu'on dit « la machine
2001:db8:42::bad:cafe est joignable », cela n'est
pas toujours vrai pour tous les protocoles, en raison notamment des
coupe-feux. Il faut donc dire « la machine
2001:db8:42::bad:cafe est joignable pour un
certain type P », où P est défini comme, mettons « paquets
TCP à destination du
port 80 ».
Première rédaction de cet article le 2 Janvier 2009
Le système d'exploitation Ubuntu est souvent présenté comme la solution idéale pour mettre sur le bureau de l'utilisateur ordinaire, non technicien. Fondé sur essentiellement du logiciel libre, Ubuntu bénéficie d'un marketing très dynamique, à base de photos d'utilisateurs multiraciaux et souriants. La promesse : qu'Ubuntu, contrairement aux autres Unix, est utilisable par tous. Quelle est la réalité ?
Ce n'est pas parce qu'on répète quelque chose que c'est
vrai. Pourtant, dans le marketing de l'informatique, c'est une méthode
très fréquente. « Ubuntu, c'est convivial » ou bien « Avec Ubuntu,
Unix est aussi facile qu'avec le système XXXXX » sont des phrases
répétées tellement souvent qu'on finit par y croire. Je gère donc
plusieurs machines Ubuntu, destinées à des utilisateurs exigeants mais
pas du tout techniciens (voir des descriptions plus techniques en
dell-latitude-d430-linux.html, dell-inspiron-7500.html et packard-bell-mx37.html).
Et que constater ? D'abord, l'administration système n'est pas du « tout cuit ». Bien qu'Ubuntu cède partiellement aux pressions de l'industrie du matériel et accepte d'incorporer des pilotes non libres dans son système, le matériel ne fonctionne pas forcément du premier coup. Obtenir des meilleurs résultats nécessite des manipulations qui ne sont certes pas à la portée de l'utilisateur ordinaire comme de compiler une version plus récente du pilote. La lecture de forums comme la liste de diffusion des utilisateurs francophones d'Ubuntu montre bien que ce problème est fréquent. Il est récurrent avec tous les Unix libres et n'est souvent pas de la responsabilité des programmeurs Unix, mais de celle des fabricants de matériel. Néanmoins, nier ou minimiser ce problème, comme le font trop souvent les promoteurs du logiciel libre est contre-productif. L'utilisateur naïf qui a pris ce discours au pied de la lettre risque, de dépit, de s'enfuir bien vite au premier problème.
Mais il y a pire : les problèmes ne s'arrangent pas forcément avec le temps. Parfois, une mise à jour du système résoud soudain un problème matériel qui trainait depuis des mois. Parfois, au contraire, il y a régression (en passant de Ubuntu Heron à Ubuntu Ibex, la fonction de mise en hibernation de mon Dell D430 marche moins bien).
Ces problèmes, je le sais bien, sont largement dûs à l'attitude de l'industrie du matériel, qui refuse de publier des documentations correctes, réservant les informations à ceux qui écrivent des pilots pour Windows. Les autres Unix libres ne font pas forcément mieux qu'Ubuntu sur ce point et ils sont parfois bien plus complexes.
Mais il existe aussi des
problèmes purement logiciels. L'environnement graphique par défaut
d'Ubuntu, Gnome, vise explicitement à copier
MS-Windows. Comme son modèle, la priorité est
aux gadgets qui brillent et pas à la stabilité et à la
fiabilité. Certaines bogues indiquent un mépris de la robustesse qui
est assez étonnant. Ainsi, les nombreuses
bogues de gnome-appearance-properties, le programme lancé
lorsqu'on clique sur Système -> Préférences -> Apparence montrent une
absence totale de contrôle
qualité. gnome-appearance-properties fait des
erreurs de segmentation dès
qu'il rencontre un petit problème, sans qu'aucun message d'erreur ne
signale rien.
Une bogue que j'ai rencontrée personnellement (et qui est
enregistrée sous le numéro
312931) est particulièrement « réussie » : si un fichier avec
une extension .jpg (normalement une image JPEG)
contient en fait un autre format, le programme crashe sans aucun message d'erreur. Le programmeur n'a
pas eu l'idée qu'un fichier pouvait ne pas être au bon format !
Produire un message « Fichier invalide ou inconnu » avec le nom du
fichier n'aurait-il pas été la moindre des choses ? Ce genre de
programmation, dite « défensive » est pourtant enseigné dans les écoles
depuis de nombreuses années.
Pour contourner cette bogue particulière et permettre de lancer le
programme, il a fallu trouver son nom (Système -> Préférences -> Menu
principal puis Choix de la catégorie (par exemple Accessoires) puis
Clic droit sur le nom de l'application (par exemple Calculatrice) puis
Clic sur Propriétés, le champ commande contient... ce nom, merci à
Bernardo pour la méthode, que je ne trouvais nulle part). Ensuite, il
faut faire tourner le programme sous strace pour afficher
les appels systèmes effectués. On voit alors
qu'immédiatement avec le SIGSEGV, il ouvrait un
fichier, fichier qu'il suffit ensuite de supprimer pour que
gnome-appearance-properties fonctionne à nouveau.
Une autre solution aurait été de faire tourner le programme sous un débogueur comme gdb. Dans les deux cas, on ne peut pas dire que cela soit accessible à l'utilisateur typique.
Je suis politiquement attaché au logiciel libre et je suis bien conscient que MS-Windows est lui aussi très loin d'être facile et sans douleur pour l'utilisateur non informaticien. Mais j'aimerais des fois que le marketing du logiciel libre soit un peu moins optimiste et un peu plus réaliste.
Première rédaction de cet article le 23 Décembre 2008
Il y a des tas de raisons de vouloir lire soi-même les paquets capturés sur le réseau. Cela peut être par curiosité, par souci de sécurité (ou pour pirater !) ou bien pour faire des statistiques. Pour un rapide coup d'œil, on utilise en général tcpdump, pour un examen interactif détaillé, l'excellent Wireshark mais, pour des études à long terme, il vaut mieux programmer et développer un programme d'analyse spécifique, ce que je fais ici en langage C.
Souvent les articles sur la question parlent ensemble de la capture
des paquets et de leur analyse. Ici, je ne mentionne que l'analyse,
supposant que la capture a été faite par d'autres moyens, par exemple
tcpdump -w FICHIER ou bien
pcapdump.
L'outil d'analyse le plus fréquent pour les programmeurs C est libpcap. Il existe un excellent tutoriel d'introduction.
Je commence par un programme trivial, qui lit un fichier au format pcap, capturé par un des outils cités dans « Capturer les paquets qui passent sur le réseau », et qui décode suffisamment d'IPv4 pour afficher le protocole de transport utilisé. L'essentiel du programme est :
/* Ouvrir le fichier pcap */
handle = pcap_open_offline(filename, errbuf);
/* Parcourir le fichier */
while (1) {
packet = pcap_next(handle, &header);
/* Décoder l'en-tête Ethernet en convertissant le packet en une
struct */
ethernet = (struct sniff_ethernet *) (packet);
/* Si c'est de l'IPv4 */
if (ntohs(ethernet->ether_type) == IPv4_ETHERTYPE) {
/* Décoder IPv4 : convertir le paquet en une struct. Ne pas
oublier de décaler de SIZE_ETHERNET octets */
ip = (struct sniff_ip *) (packet + SIZE_ETHERNET);
/* Afficher */
printf ("Paquet IPv4 avec le protocole %d\n", ip->ip_p);
Notons bien qu'il faut utiliser ntohs pour convertir le
paquet dans la bonne boutianité.
Le code complet du programme est disponible en readfile-ipv4.c. On peut le compiler sur
Unix avec :
cc -o readfile -lpcap readfile.c
(Si libpcap est bien présente. Sur une Debian, il aura fallu installer le
paquetage libpcap-dev.)
On note qu'il a fallu faire le décodage soi-même, en travaillant au niveaux des bits de l'en-tête du paquet IP. Cela nécessite donc de connaitre le protocole décodé. Pour IPv4, il faut donc lire le RFC 791, section 3.1.
Et pour IPv6 ? Le format est décrit dans le RFC 2460, section 3. Traduisons-le en C :
/* IPv6 header. RFC 2460, section3.
Reading /usr/include/netinet/ip6.h is interesting */
struct sniff_ip {
uint32_t ip_vtcfl; /* version then traffic class
and flow label */
uint16_t ip_len; /* payload length */
uint8_t ip_nxt; /* next header (protocol) */
uint8_t ip_hopl; /* hop limit (ttl) */
struct in6_addr ip_src, ip_dst; /* source and dest address */
};
Il suffit ensuite de « plaquer » cette définition sur le paquet :
if (ntohs(ethernet->ether_type) == IPv6_ETHERTYPE) {
ip = (struct sniff_ip *) (packet + SIZE_ETHERNET);
et hop, on a un paquet IPv6 à analyser. On peut par exemple lire
ip->ip_nxt qui contient le type du prochain
en-tête (en général, la couche transport, mais attention, avec IPv6, d'autres
en-têtes intermédiaires peuvent être présents). Le programme complet
est en readfile-ipv6.c.
Si on veut décoder des protocoles de plus haut niveau, c'est souvent plus complexe. Prenons le DNS comme exemple. Le format est décrit dans le RFC 1035, section 4.1. Il peut se traduire en :
struct sniff_dns {
/* RFC 1035, section 4.1.1 */
uint16_t query_id;
uint16_t codes;
uint16_t qdcount, ancount, nscount, arcount;
};
(Cela ne contient que l'en-tête DNS, il existe également des champs ultérieurs pour stocker la requête, la réponse, etc.)
Les codes sont des bits ou des groupes de bits et, pour y accéder, il faut faire de l'arithmétique de bits. Je définis des macros pour cela :
#define DNS_QR(dns) ((ntohs(dns->codes) & 0x8000) >> 15) #define DNS_OPCODE(dns) ((ntohs(dns->codes) >> 11) & 0x000F) #define DNS_RCODE(dns) (ntohs(dns->codes) & 0x000F)
Ainsi, DNS_QR va extraire le bit 15, qui indique
si le paquet est une requête ou bien une réponse (on masque avec une
valeur où seul ce bit est à 1, 0x8000, puis on décale vers la
droite). Une fois le paquet décodé :
if (source_port == DNS_PORT || dest_port == DNS_PORT) {
dns = (struct sniff_dns *) (packet + SIZE_ETHERNET +
size_ip + SIZE_UDP);
on peut utiliser ces macros pour afficher le contenu du paquet, par exemple :
DNS_QR(dns) == 0 ? "Query" : "Response"
Le code complet figure en readfile-ipv6-dns.c.
Les programmeurs Python peuvent regarder mon article équivalent pour Python.
Date de publication du RFC : Juillet 2007
Auteur(s) du RFC : L-E. Jonsson (Optand), G. Pelletier (Ericsson), K. Sandlund (Ericsson)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rohc
Première rédaction de cet article le 22 Décembre 2008
Quels que soient les progrès des technologies, il n'y a jamais assez de capacité. Même aujourd'hui, les vieux modems restent en service (y compris dans un pays riche, mais étendu, comme les États-Unis) et certaines technologies récentes offrent une capacité limitée (par exemple sur les téléphones mobiles). Il est donc utile de pouvoir comprimer les données et aussi les en-têtes des paquets émis, qui sont souvent très redondants, ouvrant ainsi de bonnes perspectives pour la compression. Plusieurs mécanismes de compression ont été inventés et le projet ROHC (Robust Header Compression) a été créé pour mettre de l'ordre dans ces mécanismes, en développant un cadre commun. Ce RFC spécifie ce cadre.
La section 1 du RFC, l'introduction, donne quelques chiffres sur les gains qu'on peut tirer de la compression. Par exemple, si on utilise RTP (RFC 3550) pour transporter de la voix sur IP, il faudra transporter les 40 octets de l'en-tête (en IPv6), les 12 octets de l'en-tête UDP et les 8 octets de l'en-tête RTP (sans compter les en-têtes de la couche liaison). Ainsi, un paquet de données de 20 octets (une taille courante en voix sur IP) pourrait nécessiter 60 octets d'en-têtes TCP/IP.
Le RFC originel sur ROHC était le RFC 3095 qui incluait à la fois le cadre général et les profils de compression pour certains protocoles (un profil est un protocole de compression donné, adapté à un certain protocole). Une réforme générale de ROHC a eu lieu et il est désormais normalisé dans plusieurs RFC, notre RFC 4995 pour le cadre général, les RFC 4996 et RFC 5225 pour des profils pour, respectivement, TCP et les protocoles sans connexion comme IP ou UDP, le RFC 4997 pour le langage formel de définition des compressions, etc. (Le RFC 3095 n'est pas officiellement abandonné puisque le protocole est le même, il est juste mieux décrit.)
La section 3 du RFC décrit les bases de la compression. Le principe est que le compresseur ne transmette pas certaines informations, car elles n'ont pas changé, ou bien peuvent être déduites automatiquement. Cela implique que le décompresseur puisse se souvenir de l'état de la compression, ce que ROHC nomme le contexte (la section 2.2 décrit toute la terminologie). Le maintien de ce contexte, même en présence de perturbations sur le réseau, est le principal défi de la compression. Sur des liens de mauvaise qualité (par exemple avec beaucoup de pertes), les mécanismes de compression pré-ROHC se comportaient plutôt mal (voir aussi la section 1).
Naturellement, rien n'est gratuit : le gain en capacité du réseau sera obtenu au détriment de la puissance de calcul, puisque compression et décompression nécessitent des calculs. La vitesse des processeurs des téléphones portables augmentant plus vite que leur capacité réseau, le choix de la compression semble raisonnable.
La section 3.2 rappelle l'histoire de la compression. Le premier grand travail dans le monde TCP/IP avait été la compression des en-têtes TCP par Van Jacobson en 1990 (RFC 1144), dont tous les utilisateurs de SLIP dans les années 1980 et 90 se souviennent (« Tu es sûr d'avoir activé la compression VJ ? »).
La section 4 décrit de manière relativement informelle le fonctionnement de ROHC, notamment (section
4.1), les différentes classes de changement dans les en-têtes, qui
permettent leur prédiction par le décompresseur. Ainsi, la classe
STATIC décrit les informations qui ne changent
pas pendant la durée du flux de données (le numéro de version
IP par exemple), la
classe INFERRED étant composée des en-têtes qui
peuvent se déduire ou se calculer à partir d'autres informations
(comme la taille du paquet).
La vraie définition de ROHC est en section 5. C'est là qu'on trouve, par exemple, la description du mécanisme de rétroaction (section 5.2.4) qui permet au décompresseur de tenir le compresseur au courant du succès (ou de l'échec) de son travail. Autre exemple (section 5.1.2), chaque canal de communication ROHC a, parmi ses paramètres, un identificateur du profil utilisé (la liste des identificateurs possibles est dans un registre IANA, voir également la section 8). La section 5.4 normalise ainsi le profil d'identificateur 0x0000, le profil qui ne fait rien, les profils « actifs » étant définis dans d'autres RFC.
Il n'existe apparemment pas d'implémentation libre de ROHC. Mais plein de déploiements sont déjà faits dans les téléphones portables. La section 6 du RFC 3095 contient une excellent section sur les problèmes concrets de mise en œuvre de ROHC (section qui ne semble pas avoir été reprises dans les RFC plus récents).
Première rédaction de cet article le 17 Décembre 2008
Lire les paquets qui passent sur le réseau est une activité indispensable pour l'étudiant qui apprend les réseaux informatiques, pour l'administrateur système qui débogue un problème, pour le chercheur qui rassemble des statistiques ou simplement pour le curieux qui veut s'instruire. Mais avant de pouvoir les lire et les analyser, il faut les capturer. Quels sont les outils disponibles ?
Tout le monde connait et utilise tcpdump, qui permet à la fois de capturer et de décoder les paquets. Pour un coup d'œil rapide, c'est parfait. Mais pour des études plus lourdes, il est souvent recommandé de séparer la capture des données (qui doit pouvoir tourner longtemps, sans trop charger la machine) de leur analyse (souvent très consommatrice en ressources et qui peut parfaitement être faite hors-ligne). Cet article se focalise sur les outils de capture, l'analyse étant faite ailleurs, avec un programme d'exploration comme Wireshark ou bien avec un programme qu'on a développé soi-même, par exemple en Python.
Un bon logiciel de capture doit pouvoir :
Presque tous les logiciels de capture enregistrement au format « pcap », celui de la libpcap.
Voyons les logiciels possibles. L'ancêtre
tcpdump, très connu de tous les administrateurs
réseaux, reste parfaitement utilisable pour
cette tâche. Son option -w permet d'enregistrer
les événements, qui pourront ensuite être lus par tout logiciel
capable de lire du pcap (y compris tcpdump lui-même, avec l'option
-r).
% tcpdump -i eth2 -w mydata.pcap tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 96 bytes [Control-C] 41 packets captured 41 packets received by filter 0 packets dropped by kernel
Et mydata.pcap peut être analysé ensuite à
loisir. tcpdump dispose de nombreuses options qui peuvent être utiles
pour la capture comme -c (s'arrêter après un
nombre donné de paquets) ou -C (donne une taille
maximale au fichier), même si elles sont moins perfectionnées que
celles des logiciels récents.
tcpdump dispose d'un langage de filtrage, nommé
BPF et qui permet d'exprimer des choses simples
comme « uniquement les paquets venant de
192.0.2.3 » ou « uniquement les paquets à
destination du port 80 » ou encore « uniquement les paquets à
destination de 2001:db8:1::deca:fbad,
et venant du port 53 ». Voici cette dernière :
% tcpdump -i eth2 -w mydata.pcap \
dst host 2001:db8:1::deca:fbad and \
src port 53
L'analyseur Wireshark a des mécanismes de décodage des paquets bien
plus riches que ceux de tcpdump. Il vient avec un programme en ligne
de commande, tshark qui peut être utilisé pour la
capture :
% tshark -i eth2 -w mydata.pcap Capturing on eth2 340 [Control-C]
Notez l'affichage en temps réel du nombre de paquets capturés, bien
pratique. Ses options de capture sont plus perfectionnées que celles
de tcpdump, par exemple -a permet de stopper
celle-ci après qu'un temps donné se soit écoulé. En mode « plusieurs
fichiers », les noms des fichiers sont plus parlants qu'avec tcpdump, etc.
tshark dispose d'un langage de filtrage plus perfectionné, documenté dans Display Filter Reference. Par exemple, si on regarde les requêtes DNS, tshark peut filtrer avec des règles comme « uniquement les réponses ». Mais attention, il ne s'agit que de filtre d'affichage, le langage des filtres de capture est bien plus sommaire, c'est le même que celui de tcpdump (on peut trouver plein d'exemples sur le site de Wireshark). La raison de cette limitation est probablement que la capture doit aller vite, sans nécessiter de copier des paquets depuis le noyau et que le langage BPF, lui, peut tourner entièrement dans le noyau.
Pour des options de capture encore plus riches, on peut regarder
pcapdump. Il
fait partie d'un groupe de programmes utilisant pcap, développés
spécialement pour Debian, mais qui peuvent être
compilés sur d'autres systèmes. Par exemple, sur
Gentoo, il faut installer la bibliothèque Judy
(emerge judy) puis simplement utiliser
make pour compiler. Ensuite, on copie les exécutables
(dans src/) à la main.
Les forces de pcapdump, un logiciel spécialisé dans la capture,
sont notamment ses capacités de tourner en démon (l'option n'est pas évidente à
trouver, c'est -P, par exemple pcapdump
-P /var/run/pcapdump.opid -C $(pwd)/dns.pcapdump), de
changer de fichier sur plusieurs critères, de nommer les fichiers de
capture selon un modèle qu'on choisit, d'échantilloner en
n'enregistrant qu'un paquer sur N, etc. Il peut aussi lire ces options
dans un fichier de configuration, dont voici un exemple qui lit les
paquets DNS (port 53) et change de fichier tous les jours :
device=eth0 bpf="udp and port 53" interval=86400 snaplen=512 promisc=0 filefmt=/var/tmp/pcapdump/eth0.%Y%m%d.%H%M.%S.pcap mode=0600 owner=smith group=root
Si on veut des filtres de capture très sophistiqués (tous les
logiciels précédents se limitent au langage BPF), il faut les
programmer soi-même ou bien utiliser un logiciel spécialisé comme
dnscap. Ce
programme dispose de filtres de capture spécifiques au DNS, par exemple on peut
sélectionner uniquement les réponses et seulement celles qui sont
négatives (NXDOMAIN, ce domaine n'existe
pas).
dnscap n'a pas de documentation en ligne, tout est dans la page de
manuel incluse dans la distribution. Sa compilation n'est pas toujours
évidente. Sur Gentoo ou
Debian, il faut ajouter dans le
Makefile :
PORTLIBS= /usr/lib/libresolv.a BINDLIB=-lbind9
Voici un exemple d'utilisation de dnscap, pour ne capturer que les
paquets de réponses (-sr), et seulement si la
réponse est négative (nom de domaine inexistant,
-ex) et juste si la question concernait le
domaine sources.org :
% dnscap -x 'sources\.org' -i eth0 -sr -g -ex
Comme pcapdump, il permet de changer automatiquement de fichier de
capture en cours de route (par exemple avec -t
86400 pour tourner tous les jours), ce qui le rend
utilisable pour des études sur le long terme.
Contrairement aux filtres BPF, le filtrage fait par dnscap est effectué dans l'espace utilisateur, pas dans le noyau. Il a donc fallu copier le paquet dans ledit espace et effectuer des opérations parfois complexes avant de décider de le garder ou pas. Les capacités de capture peuvent donc s'en ressentir.
Et si on développe un programme soi-même ? C'est assez facile, dans plusieurs langages de programmation. On peut ainsi choisir les critères de capture à volonté mais il faut prendre garde aux performances : beaucoup de paquets peuvent arriver en très peu de temps.
En langage C, libpcap, maintenue par les responsables de tcpdump, est une solution très répandue, très bien documentée, et qui marche bien. Écrire un programme de capture est assez simple.
Voici un exemple du squelette d'un tel programme (montrant la
capture mais pas l'écriture sur disque), sniff-only.c. Il se compile, par exemple, avec :
% gcc -Wall -o sniff -lpcap sniff-only.c
Pour la programmation en C, une alternative à pcap est ncap, que je n'ai pas testé. Il semble que son format de stockage soit incompatible avec pcap.
Première rédaction de cet article le 17 Décembre 2008
Il y a des tas de raisons de vouloir lire soi-même les paquets capturés sur le réseau. Cela peut être par curiosité, par souci de sécurité ou bien pour faire des statistiques. Pour un rapide coup d'œil, on utilise en général tcpdump, pour un examen interactif détaillé, l'excellent Wireshark mais, pour des études à long terme, il vaut mieux programmer et développer un programme d'analyse spécifique.
Souvent les articles sur la question parlent ensemble de la
capture des paquets et de leur
analyse. Ici, je ne mentionne que l'analyse,
supposant que la capture a été faite par d'autres moyens, par exemple
tcpdump -w FICHIER ou bien pcapdump.
L'outil d'analyse le plus fréquent pour les programmeurs C est libpcap (dont je parle dans « .Lire des paquets capturés sur le réseau en C »). Et en Python ? Il existe plusieurs interfaces à la libpcap, notamment pylibpcap et pcapy. pcapy semble de plus haut niveau et c'est celle que j'utilise.
pcapy peut faire la capture et la lecture des fichiers au format pcap. Ses capacités d'analyse sont limitées, on utilise en général la bibliothèque impacket, des mêmes auteurs, ou bien carrément scapy.
Voici un programme simple de lecture d'un fichier pcap, tel que produit par tcpdump :
import pcapy
reader = pcapy.open_offline("mydata.pcap")
while True:
try:
(header, payload) = reader.next()
print "Got a packet of length %d" % header.getlen()
except pcapy.PcapError:
break
Il ne semble pas y avoir de moyen de détecter la fin du fichier à part
en récupérant l'erreur très générique
PcapError.
payload est juste une chaîne non décodée d'octets, représentés en caractères. Si je veux l'analyser,
je peux le faire « à la main » avec les fonctions de manipulation
binaire de Python. Cela nécessite évidemment de lire les différentes
normes pour connaître le format des paquets :
import pcapy
reader = pcapy.open_offline("mydata.pcap")
while True:
try:
(header, payload) = reader.next()
# Ethernet type is two-bytes long and starts at 12 (before, you
# have the MAC addresses)
if payload[12:14] == '\x86\xDD': # 0x86DD is the Ethernet type for IPv6
ip_payload = payload[14:]
ip_version = (ord(ip_payload[0]) & 0xf0) >> 4
if (ip_version != 6):
raise Exception("Packet has ethernet type of IPv6 but internal IP version is %d" % ip_version)
next_header = ord(ip_payload[6]) # RFC 2460, section 3 to know the offset
print "Next header (probably the transport protocol) is %d" % next_header
except pcapy.PcapError:
break
Ici, on teste le type indiqué dans l'en-tête
Ethernet pour ne garder que
0x86DD (IPv6). On saute
les quatorze premiers octets (la taille de l'en-tête Ethernet) pour
récupérer le paquet IPv6 que l'on décode en suivant le RFC 2460. Le numéro de version fait moins d'un octet, d'où les
manipulations de bits avec
& et
>>.
Mais il peut être plus simple d'utiliser la bibliothèque impacket, conçue pour faciliter le décodage :
import pcapy
import impacket.ImpactDecoder as Decoders
reader = pcapy.open_offline("mydata.pcap")
decoder = Decoders.EthDecoder()
while True:
try:
(header, payload) = reader.next()
result = decoder.decode(payload)
# http://www.iana.org/assignments/ethernet-numbers
if result.get_ether_type() == 0x86DD: # IPv6
print "Got an IPv6 packet of length %d" % header.getlen()
except pcapy.PcapError:
break
Ici, plus besoin de connaître la structure de l'en-tête Ethernet, on
peut utiliser get_ether_type() au lieu de
payload[12:14].
Cela va permettre de s'attaquer à des protocoles de plus haut niveau, souvent plus complexes à décoder. Commençons par UDP :
import pcapy
import impacket.ImpactDecoder as Decoders
import impacket.ImpactPacket as Packets
reader = pcapy.open_offline("mydata.pcap")
eth_decoder = Decoders.EthDecoder()
ip_decoder = Decoders.IPDecoder()
udp_decoder = Decoders.UDPDecoder()
while True:
try:
(header, payload) = reader.next()
ethernet = eth_decoder.decode(payload)
if ethernet.get_ether_type() == Packets.IP.ethertype:
ip = ip_decoder.decode(payload[ethernet.get_header_size():])
if ip.get_ip_p() == Packets.UDP.protocol:
udp = udp_decoder.decode(
payload[ethernet.get_header_size()+ip.get_header_size():])
print "IPv4 UDP packet %s:%d->%s:%d" % (ip.get_ip_src(),
udp.get_uh_sport(),
ip.get_ip_dst(),
udp.get_uh_dport())
except pcapy.PcapError:
break
Ce programme affiche les paquets UDP sur IPv4 en indiquant les
adresses et ports de source et de destination. On note qu'il n'y a pas
eu besoin de lire le RFC 768, tout est pris en charge par
Impacket (y compris le problème, que j'ai soigneusement évité dans
l'exemple du décodage manuel, de l'ordre
des octets dans les champs multi-octets). Il faut juste
penser, à chaque fois qu'on grimpe d'une couche, à se décaler dans le paquet (du résultat de
get_header_size() sur la couche précédente).
Pour le décodage de l'en-tête IP, Impacket ne dispose malheureusement que d'un décodeur IPv4. Pour IPv6, il faut l'écrire soi-même, peut-être plutôt en utilisant Scapy. Ce sera pour un autre article.
Première rédaction de cet article le 11 Décembre 2008
Traditionnellement, les SGBD ne traitaient que des données simples et courtes, comme le nom d'un employé ou son salaire. Les recherches portaient donc sur la totalité du champ. Désormais, il est de plus en plus fréquent de voir un SGBD utilisé pour stocker des textes relativement longs (des articles d'un blog, par exemple) et la recherche dans ces textes devient très longue et peu pratique. Elle nécessite l'indexation et des outils de recherche spécialisés. Qu'en est-il sur PostgreSQL ?
Pendant longtemps, le moteur de recherche plein texte de PostgreSQL était tsearch2. Distribué dans le répertoire
contrib/ de PostgreSQL, il devait être installé
séparément (ou via un paquetage du système d'exploitation
considéré, par exemple
databases/postgresql82-tsearch2 sur NetBSD). Désormais, depuis la version
8.3, il est intégré complètement à PostgreSQL, et pourvu d'une
excellente
documentation, très détaillée.
Le principe est de couper le texte en élements lexicaux, puis en
lexèmes, qui sont des versions
normalisées des éléments lexicaux. Cela se fait
avec la fonction to_tsvector :
essais=> SELECT to_tsvector('les gros chats mangent les rats maigres');
to_tsvector
----------------------------------------------------------
'le':1,5 'rat':6 'chat':3 'gros':2 'maigr':7 'mangent':4
(1 row)
Ici, elle a analysé la phrase, trouvé six mots (dont un répété) et réduit (normalisé) ceux qu'elles pouvaient. Ce processus dépend de la langue et donne parfois des résultats surprenants (« mangent » n'a pas été normalisé en « manger »).
On cherche ensuite dans ces éléments lexicaux avec la fonction de
correspondance, @@. Elle prend comme
paramètres un vecteur (tsvector) comme celui ci-dessus et une requête, créée
avec to_tsquery :
essais=> SELECT to_tsquery('chat|rat');
to_tsquery
----------------
'chat' | 'rat'
(1 row)
Le langage des requêtes (vous avez sans doute déjà deviné que
| veut dire OU) est évidemment documenté.
En combinant requête, vecteur de mots et opérateur de correspondance, on peut faire une recherche complète :
essais=> SELECT to_tsvector('les gros chats mangent les rats maigres')
@@ to_tsquery('chat|rat');
?column?
----------
t
(1 row)
essais=> SELECT to_tsvector('on ne parle pas de ces animaux ici')
@@ to_tsquery('chat|rat');
?column?
----------
f
(1 row)
On a trouvé un résultat dans le premier cas et zéro dans le second.
Bien sûr, taper de telles requêtes à la main est plutôt pénible, on a donc intérêt à créer ses propres fonctions.
L'analyse d'une phrase en mots et la normalisation de ces derniers
dépend de la langue. Il y a un paramètre à
to_tsvector et to_tsquery
qui indique une configuration ('french' par défaut, sur mon
site). Voyons quelles configurations a une installation typique de
PostgreSQL 8.3 (ici, le paquetage Debian) :
essais=> \dF
List of text search configurations
Schema | Name | Description
------------+------------+---------------------------------------
...
pg_catalog | finnish | configuration for finnish language
pg_catalog | french | configuration for french language
pg_catalog | german | configuration for german language
...
essais=> show default_text_search_config;
default_text_search_config
----------------------------
pg_catalog.french
(1 row)
Prenons maintenant un exemple réel, un moteur de recherche dans la liste des RFC. On commence par créer la base :
-- Un RFC a un numéro, un titre et le corps, du texte brut
CREATE TABLE Rfcs (id SERIAL,
inserted TIMESTAMP default now(),
num INTEGER,
title TEXT,
body TEXT);
-- Créer son propre type facilite l'utilisation de la fonction search()
CREATE TYPE Results AS (num INTEGER, title TEXT,
body TEXT, rank REAL);
-- Les RFC sont en anglais donc on utilise systématiquement la
-- configuration 'english'
CREATE FUNCTION search (TEXT) RETURNS SETOF Results AS
'SELECT num, title,
body, ts_rank_cd(to_tsvector(''english'', title || body),
to_tsquery(''english'',$1))
FROM Rfcs
WHERE to_tsvector(''english'', title || body) @@ to_tsquery(''english'',$1)
ORDER BY ts_rank_cd(to_tsvector(''english'', title || body),
to_tsquery(''english'',$1)) DESC;'
LANGUAGE SQL;
La fonction search() appelle une fonction
ts_rank_cd qui
calcule la pertinence d'un texte par rapport à une requête. Il faut
aussi remarquer la concaténation des champs (title ||
body) qui indique qu'on cherche dans les deux colonnes. Testons
search() :
essais=> SELECT num,title,rank FROM search('dnssec') ORDER BY rank DESC;
num | title | rank
------+----------------------------------------------------+------
4035 | Protocol Modifications for the DNS Security ... | 10.1
3130 | Notes from the State-Of-The-Technology: DNSSEC | 7.4
4641 | DNSSEC Operational Practices | 7.1
...
La recherche peut être plus compliquée, par exemple si on cherche les RFC qui parlent de DNSSEC et LDAP (notons que les pertinences sont bien plus faibles) :
essais=> SELECT num,title,rank FROM search('dnssec & ldap') ORDER BY rank DESC;
num | title | rank
------+-----------------------------------------------+-------------
5000 | Internet Official Protocol Standards | 0.111842
4238 | Voice Message Routing Service | 0.0170405
4513 | LDAP: Authentication Methods and Security ... | 0.00547112
...
Chercher dans la totalité de la base à chaque fois peut être assez long. Les moteurs de recherche emploient typiquement l'indexation pour accélerer la recherche. PostgreSQL dispose de plusieurs types d'index. Pour favoriser le temps de recherche (au détriment du temps d'indexation), j'utilise les index GIN :
CREATE INDEX rfcs_idx ON Rfcs USING gin(to_tsvector('english', title || body));
Avec ces index, les recherches sont bien plus rapides. En effet, sans
index, il faut balayer toute la table comme l'indique
EXPLAIN :
essais=> EXPLAIN SELECT id,num FROM Rfcs WHERE to_tsvector('english', body) @@ to_tsquery('dnssec');
QUERY PLAN
-----------------------------------------------------------------------------------
Seq Scan on rfcs (cost=0.00..186.11 rows=5 width=8)
Filter: (to_tsvector('english'::regconfig, body) @@ to_tsquery('dnssec'::text))
(2 rows)
(Seq Scan est sequential
scan.)
Avec l'index, la recherche se fait d'abord dans l'index :
essais=> EXPLAIN SELECT id,num FROM Rfcs WHERE to_tsvector('english', body) @@ to_tsquery('dnssec');
QUERY PLAN
---------------------------------------------------------------------------------------------
Bitmap Heap Scan on rfcs (cost=40.70..57.34 rows=5 width=8)
Recheck Cond: (to_tsvector('english'::regconfig, body) @@ to_tsquery('dnssec'::text))
-> Bitmap Index Scan on rfcs_idx (cost=0.00..40.70 rows=5 width=0)
Index Cond: (to_tsvector('english'::regconfig, body) @@ to_tsquery('dnssec'::text))
(4 rows)
Le programme qui prend l'ensemble des RFC et les met dans la base
est index-rfcs.py.
Date de publication du RFC : Décembre 2008
Auteur(s) du RFC : G. Huston
Pour information
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 11 Décembre 2008
De même qu'il existe des adresses IP et des noms de domaine réservés à la documentation, les numéros d'AS de 64496 à 64511 et de 65536 à 65551 sont désormais réservées pour les auteurs des documentations sur le routage.
Écrire un cours sur le routage (comme mon cours BGP) nécessite de choisir les numéros d'AS qui apparaitront. Prendre des numéros existants est risqués car il y aura toujours un distrait pour copier-coller les configurations de routeurs qu'il a lu dans le cours, créant ainsi des problèmes avec l'AS qui a ce numéro.
Le même problème existe pour les adresses IP (d'où la réservation de
192.0.2.0/24 dans le RFC 3330 ou de 2001:DB8::/32 dans le RFC 3849) ou pour les noms de domaine (pour lesquels
.example a été réservé par le RFC 2606).
Comme le rappelle la section 1, même les numéros privés (pour les AS, de 64512 à 65534) ne conviennent pas puisqu'ils sont utilisés en production dans certains réseaux. C'est donc logiquement que notre RFC réserve deux plages de seize numéros d'AS pour la documentation : de 64496 à 64511 pour les AS sur deux octets et de 65536 à 65551 pour les AS sur quatre octets (ces derniers ayant été introduits par le RFC 4893).
Date de publication du RFC : Décembre 2008
Auteur(s) du RFC : S. Niccolini (NEC), S. Tartarelli (NEC), J. Quittek (NEC), T. Dietz (NEC), M. Swany (UDel)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 11 Décembre 2008
Le programme traceroute est un des piliers de l'Internet. Cet outil de déboguage a servi à des dizaines de milliers d'administrateurs réseaux, lors de difficiles sessions de recherche de panne ou de divers problèmes. Né de la nécessité, il n'a jamais eu de modèle de données formel, ni de norme de présentation des résultats. C'est désormais fait avec ce nouveau document.
Cette normalisation permet notamment de stocker les résultats d'un traceroute et de les comparer de manière automatique à un autre (il existe déjà de tels programmes de comparaison, par exemple sous forme d'un moniteur pour le logiciel de surveillance réseau mon, mais tous ne comparent que la sortie texte de traceroute et peuvent donc être perturbés par des changements purement de présentation). La section 1 détaille ce cahier des charges.
La section 3 rappelle le principe de fonctionnement de traceroute :
envoyer des paquets UDP (certaines versions
peuvent utiliser TCP ou
ICMP ; l'annexe A contient une très
intéressante description des différentes versions de traceroute) à une machine distante, en mettant
délibérement des valeurs trop basses au TTL (ou
Hop Count) des
paquets IP. Les
routeurs qui, après la décrémentation de ce champ, trouveront zéro,
signaleront leur existence en envoyant un paquet ICMP
TIME_EXCEEDED (RFC 792).
La section 4 liste ensuite les résultats qu'on obtient avec traceroute. Selon la version de ce dernier, on peut trouver :
Enfin, la section 5 définit le modèle de données utilisé pour créer
le fichier XML de résultats. 5.1 contient les
types de données. Certains sont tirés des schémas du W3C comme Boolean
ou DateTime, d'autres sont définis ici (par exemple InetAddress pour
les adresses IP). La section 5.2 donne la liste de tous les élements
XML utilisés comme ProbeRoundTripTime (section
5.2.3.8) qui contient le RTT en
millisecondes. On peut donc écrire :
<ProbeRoundTripTime> <roundTripTime>13</roundTripTime> </ProbeRoundTripTime>
Cet élement ProbeRoundTripTime peut,
alternativement, contenir un élément roundTripTimeNotAvailable.
La section 6 explique le choix de XML, notamment parce que les fichiers XML sont lisibles par un humain et parce qu'il existe un grand nombre d'outils existants. Cette section explique aussi pourquoi des modèles de données proches comme la MIB du RFC 4560 (également discutée en annexe C) ou l'encodage d'IPFIX dans le RFC 5101 n'ont pas été utilisés.
Enfin, une longue section 7 contient le schéma lui-même, défini
avec le langage W3C
Schema. L'élément ProbeRoundTripTime
cité plus haut est ainsi défini comme le choix entre un entier ou la
valeur roundTripTimeNotAvailable :
<xs:element name="ProbeRoundTripTime"
type="_roundTripTime">
</xs:element>
<xs:complexType name="_roundTripTime">
<xs:choice>
<xs:element name="roundTripTime">
<xs:simpleType>
<xs:restriction base="xs:unsignedInt"/>
</xs:simpleType>
</xs:element>
<xs:element name="roundTripTimeNotAvailable">
<xs:complexType/>
</xs:element>
</xs:choice>
</xs:complexType>
Je ne connais pas encore d'implémentation « officielle » de traceroute qui
produise ces fichiers XML. Si vous en écrivez une, attention aux
caractères spéciaux (speciaux pour XML comme & ou <) dans les
noms de machines : rien ne vous garantit qu'une requête DNS inverse ne va pas vous
renvoyer des noms avec de tels caractères. Comme toujours quand un
programme produit du XML, il ne doit pas se contenter de mettre des
<balise> un peu partout, il doit aussi
veiller à ces caractères spéciaux.
Il existe à ma connaissance une mise en œuvre expérimentale de traceroute
qui produit du XML à ce format (dérivée du traceroute de NANOG). Elle est assez sommaire mais elle
marche. traceroute-nanog-xml.c.
Il nécessite libxml2. Pour le compiler, sur GNU/Linux :
cc -DXML -lresolv $(xml2-config --cflags) $(xml2-config --libs) \
traceroute-nanog-xml.c -o traceroute-nanog-xml
Sur FreeBSD, même chose sans le
-lresolv. Ensuite, on ne lance avec l'option
-X pour produire du XML.
Une fois produits, les fichiers XML peuvent être vérifiés, par exemple avec xmllint :
% xmllint --noout --schema traceroute.xsd traceroute.xml traceroute.xml validates
Voici des exemples de résultats stockés dans ce format, un exemple pris dans le RFC (annexe D) et un autre exemple, produit par mon programme.
Date de publication du RFC : Décembre 2008
Auteur(s) du RFC : G. Huston (APNIC, G. Michaelson (APNIC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 10 Décembre 2008
Après de très longs débats, ce RFC normalise enfin un format pour
représenter les numéros de systèmes autonomes
de 32 bits. Ils seront affichés dans la notation ASPLAIN, c'est-à-dire
sans séparateur interne, par exemple 101234.
Depuis que le RFC 4893, en mai 2007, avait
normalisé les numéros de systèmes autonomes sur
quatre octets (donc potentiellement supérieurs à 65536), le débat
faisait rage, dans la communauté des opérateurs Internet, sur la
meilleure représentation
textuelle à leur donner. Deux solutions s'affrontaient pour
écrire le numéro 101234 : celle qui l'a emportée, nommée ASPLAIN, où
le numéro était écrit sans séparateur interne, et une première
solution, ASDOT, où on écrivait le numéro en deux parties séparées par
un point, ici 1.35698 (1 * 65536 + 35698). La
méthode ASPLAIN convenait mieux aux programmes, ASDOT était préféré
par les humains.
Il y avait aussi une méthode ASDOT+ où même les numéros inférieurs à
65536 étaient écrits en deux parties, 20768 se
notant 0.20768. ASDOT avait semblé avoir le vent
en poupe d'abord, mais avait
échoué devant l'IESG.
La crainte des adversaires d'ASDOT était que beaucoup de programmes écrits pour gérer les routeurs (car un gros opérateurs ayant des centaines de Juniper ou de Cisco n'édite évidemment pas les centaines de configurations depuis une interface Web, il a un programme pour cela), des gros scripts Perl ou équivalent, n'aient pas été prévus pour des points au milieu d'un numéro d'AS... Pour prendre un exemple cité par Philip Smith :
Well, here is a regexp from a router I have with IOS-XR, just to show how the . notation impacts regexp work:
as-path-set 4byte-asn ios-regex '_2\.0_.*_[0-9]+\.[0-9]+_', ios-regex '_5\.1_', end-set
This basically says:
I'm sure some ISPs around here must have similar 16-bit ASN regexps in use at the moment. To handle 32-bit ASNs, they'll need to convert all these to escape the "." as I did above...
Voir aussi http://www.swissix.ch/asn32/doku.php pour d'autres débats sur la question.
Notre RFC 5396 tranche donc en faveur d'ASPLAIN. C'est ce format qui sera utilisé dans la configuration des routeurs, dans les discussions sur les listes de diffusion d'opérateurs comme NANOG, ou dans les IRR par exemple au RIPE-NCC (qui avait failli développer sa propre politique, vue la difficulté de l'IETF à décider). Comme le note la section 3 du RFC, cette notation a l'avantage de refléter l'absence de structure interne du routage BGP (la notation ASDOT pouvait faire croire à une hiérarchie).
Première rédaction de cet article le 6 Décembre 2008
Le MUA Thunderbird permet d'utiliser la cryptographie pour protéger le relevé ou l'envoi du courrier. Mais trouver la bonne option n'est pas facile !
Une fois des serveurs de messagerie configurés pour utiliser le protocole de cryptographie TLS (pour Postfix, voir « Configurer Postfix avec TLS »), il reste à configurer les clients.
Thunderbird est un des plus utilisés. Logiciel libre, à interface graphique, il dispose de nombreuses fonctions, notamment de filtrage des spams par algorithme bayésien. Il dispose également du protocole TLS. Mais il n'est pas évident de choisir l'option à activer, notamment en raison d'un nommage bizarre des protocoles.
Sans Secure connections - Thunderbird, je ne serai jamais arrivé à le configurer en mode « 100 % crypto » (pour IMAP et SMTP). Cela relativise l'argument comme quoi les interfaces graphiques sont forcément simples. Le problème est qu'il est trivial de trouver quoi faire pour activer "TLS" (on clique sur un bouton) mais moins évident de savoir s'il faut choisir "TLS", "TLS, if available" ou "SSL". SSL et TLS sont pourtant la même chose (SSL est l'ancien nom du protocole qui est devenu TLS et est aujourd'hui normalisé dans le RFC 5246).
Les options "TLS" et "TLS, if available" font presque la même chose, et qui est en fait du STARTTLS (commencer en clair puis basculer en chiffré s'il est disponible). La seule différence est que "TLS" tout court impose que STARTTLS soit possible et coupe la communication si cette sécurité n'est pas posisble.
C'est l'option "SSL", très mal nommée, qui permet de faire du TLS directement (sans STARTTLS).
Donc, en SMTP : il faut configurer "TLS" (qui veut dire en fait STARTTLS) et le port 587, le port de soumission du courrier (RFC 4409).
En IMAP, avec la configuration par défaut du serveur IMAP Courier (deux ports, un pour le clair et un pour le chiffré), il faut "SSL" et ne pas cocher "secure authentication" (qui désigne un défi/réponse, mais c'est déjà assez sûr avec TLS) et il faut un certificat correct, le certificat auto-signé généré par Courier par défaut ne marche pas (mais l