Introduction à la programmation wifi en C sous NetBSD
Signature : | Mis en ligne le : 26/11/2007
Catégorie(s) :
  • GNU/Linux Magazine HS
  • | Domaine :
    Commentez creative commons
    Tu aimes Dave, tu aimes aussi Sheila, tu es fan de Dalida, tu as toujours rêvé de savoir danser sur un damier illuminé et tu répètes "Parole" depuis des années devant le miroir de ton armoire. Si c'est bien ce que tu cherches, cet article n'est pas pour toi, mais je suis enchanté de faire ta connaissance. Cependant, si tu as un peu de temps (tu répèteras plus tard devant ton miroir), je vais te faire rencontrer des lutins, des lutins magiques, des miettes de réseau qui volent dans l'air que tu respires chaque jour. Je vais te parler de code et de wireless sous NetBSD. Il s'agit d'une introduction, je répète une introduction, à la programmation 802.11 en utilisant l'API unifiée (/usr/include/net80211/*) ieee80211 de NetBSD. C'est probablement ce dont j'aurais eu besoin lors de mes premières idées de programmes wifi ou de portages d'applications sans fil Linux vers NetBSD (comme toi quand tu as répété "L'été indien" devant ton miroir, imagine si tu avais pu avoir Joe Dassin à tes côtés). Nous allons tenter d'être pragmatique dans notre approche (oui, pour apprendre à danser, il faut danser, mais ça tu le sais déjà) : montrer concrètement comment effectuer les opérations de base et indiquer où se renseigner sur d'autres opérations que tu aimerais réaliser. Cet article n'expliquera pas le fonctionnement du protocole 802.11 et ses variantes ; le lecteur possède déjà les concepts de programmation de base en C, une compréhension générale des réseaux wireless, ainsi qu'une bonne connaissance de la variété française (Pouuurr le plAaAAAaiiiSIiiiiiIrrrr). De plus, le code ne se veut pas exhaustif, ni "secure", ni audité, ni même utilisable dans le cadre d'une application destinée à tourner en production. C'est une simple illustration de la manipulation de cette API.

    1. Wireless : le standard

    Je ne vais pas rentrer dans les détails, Wikipédia en parlera mieux que moi : En gros, c'est génial, plus de fils, plus de trucs qui traînent (pas comme chez moi), etc.

    2. Modes d'opération courants

    Voici la liste des modes dans lesquels une carte wifi peut se trouver :
    • ibss (independant bss or infrastructure bss), connu aussi sous d'autres termes : managed, client, adhoc, etc. ;
    • hostap : la carte se comporte comme un point d'accès (access point) au réseau wireless ;
    • monitor : la carte récupère passivement tous les paquets wireless qu'elle observe ou qui lui passent sous le nez.
    C'est vite résumé et simplifié, on est d'accord.

    3. NetBSD et le 802.11

    Le système d'exploitation NetBSD possède une couche d'abstraction permettant le contrôle de différentes cartes wireless, indépendamment du fabricant. La majorité de cette abstraction est présente dans le noyau et chaque driver wireless propose une partie ou le support complet des différents appels de cette API. Le schéma 1 très simplifié montre son intégration au sein du système. La majorité des drivers récents en font un usage quasi complet, mais certains drivers ne l'utilisent pas complètement. Ils fournissent leurs propres méthodes pour certaines opérations comme configurer la clef WEP (par exemple dans le driver an(4)). Cette API a été écrite par Atsushi Onoe, puis reprise et maintenue par Sam Leffler. FreeBSD et NetBSD travaillent de concert (avec un lead très FreeBSDien) pour la maintenir et la faire évoluer. La personne responsable pour NetBSD est David Young. Résumons donc ! En gros, ça facilite le code, ça rend les choses plus lisibles et plus simples à maintenir et évidemment "small is beautiful", tout ça, tout ça, tout ça... De plus, c'est plus simple à gérer que 36 appels pour chaque driver qui devrait alors implémenter ses propres ioctls, etc. Pour en faire usage ? Simple, il suffit d'utiliser les includes appropriés qui se situent dans le répertoire usr/include/net80211/. Ils définissent aussi bien les différentes options pour demander et configurer les paramètres du driver, que les structures et des macros permettant de créer

    /img-articles/lmhs/29/cc-art-wifi/fig-1.jpg

    un parseur de paquets 802.11 assez facilement. Étant utilisés par le noyau lui-même pour gérer les paquets arrivant sur la carte, ils sont parfaitement adaptés si on désire gérer le parsing de données à partir de paquets bruts arrivant directement du noyau vers l'espace utilisateur (en monitor mode avec pcap par exemple). Dans les exemples qui suivent, nous aurons uniquement besoin des includes suivants :
    • <net80211/ieee80211.h> contient les structures des différentes trames wireless, des valeurs et quelques macros ;
    • <net80211/ieee80211_ioctl.h> contient les ioctls courants pour l'API ;
    • <net80211/ieee80211_radiotap.h> contient la structure du header RadioTap et les différents champs, mais nous en reparlons dans l'exemple n°5.
    Afin d'accéder aux informations des interfaces ou de les configurer, il nous faudra également utiliser les includes relatifs à la gestion des interfaces réseau. Dans les exemples qui suivent, nous utiliserons uniquement les includes suivants :
    • <net/if.h>
    • <net/if_media.h>
    Si d'autres informations sont nécessaires, il est très facile d'aller jeter un coup d’œil au code source de l'outil ifconfig et de regarder comment celui-ci s'y prend pour les obtenir ou les modifier. Passons à la pratique. Nous allons passer en revue quelques opérations simples et introduire la manière d'effectuer ces opérations sous NetBSD. Cet article a été écrit avec la version 4.0_BETA2, une carte Intel 3915ABG et une carte Atheros, ainsi qu'une carte Aironet.

    4. Exemple n°1 : Activer/Désactiver une interface wireless

    Cette opération n'a rien de spécifique au wifi. Elle est commune à toute interface. Il suffit de remplir les membres ifr_name et ifr_flags de la struct ifreq (<net/if.h>). Le flag d'activation d'une interface est IFF_UP. Et voici le code d'exemple wifi_switch.c : A l'exécution : Résultat ifconfig : On rallume : Résultat ifconfig : Il n'est pas nécessaire de préciser le média, sachant que, par défaut, le comportement est l'auto-sélection du média et du mode d'opération (OFDM24, OFDM36, 11b, 11g, etc.). Le mode d'opération par défaut est en infrastructure (IBSS). L'ioctl(2) pour obtenir l'état de l'interface est SIOCGIFFLAGS. Il suffit alors de vérifier si le flag IFF_UP est actif dans le membre ifr_flags de la structure ifreq. Requêtes ioctls :
    • SIOCGIFFLAGS : récupérer les flags pour l'interface ;
    • SIOCSIFFLAGS : configurer les flags pour l'interface
    Maintenant, on est capable d'allumer et d'éteindre une interface : tu dois déjà te sentir un peu plus proche de tes chanteurs préférés.

    5. Exemple n°2 : L'association

    C'est très simple, le driver se charge de toute la négociation et des échanges. Côté userland, il suffit de proposer à la carte un SSID (Service Set ID) via l'ioctl(2) SIOCS80211NWID de la couche net80211 et une clef WEP via l'ioctl(2) SIOCS80211NWKEY. Nous n'aborderons pas WPA dans cet article, qui se veut une introduction. Pour configurer le SSID, il vous suffit de :
    1. Remplir la structure ieee80211_nwid définie dans le fichier <net80211/ieee80211_ioctl.h> ;
    2. L'envoyer au driver via l'ioctl SIOCS80211NWID.
    Il faut faire de même pour la(les) clef(s) WEP, ce qui nécessitera de remplir une structure ieee80211_nwkey (également définie dans <net80211/ieee80211_ioctl.h>), puis d'envoyer ces informations au driver via l'ioctl SIOCS80211NWKEY. Bon nombre de ces structures attendent qu'un champ contienne le nom de l'interface pour déterminer à quel driver envoyer les informations et les ordres. C'était le cas avec le champ ifr_name de la structure ifreq vue précédemment. Voici un petit exemple wifi_assoc.c, afin d'illustrer la configuration d'un SSID et d'une clef WEP sur une interface nommée wpi0 : A l'exécution, nous obtenons les résultats suivants : Sans clef WEP : Vérifions avec ifconfig : Avec clef WEP : Vérifions avec ifconfig : Dans notre cas, le driver wpi(4) va maintenant se charger de faire la sale besogne, c'est-à-dire l'association avec le réseau sans fil. La partie userland se résume donc à ça. Simple, non ? :) Vous pouvez bien entendu récupérer ces informations via les requêtes G (Get) ; les requêtes S (Set) étant présentes pour configurer ("setter") un des paramètres (cf. ioctl(2)). Requêtes ioctl :
    • SIOCG80211NWID : récupérer la valeur du ssid courant
    • SIOCS80211NWID : configurer la valeur du ssid courant
    • SIOCG80211NWKEY : récupérer la valeur de(s) la clef(s) WEP
    • SIOCS80211NWKEY : configurer la valeur de(s) clef(s) WEP
    Ces requêtes ioctl(2) et d'autres sont décrites dans le fichier <net80211/ieee80211_ioctl.h>. On y trouve également les structures associées aux requêtes.

    6. Exemple n°3 : Scanner activement

    Vous pouvez demander à votre driver, par conséquent à votre carte, de "balayer" l'environnement afin de trouver d'autres points d'accès dans son périmètre. La requête se fait au travers de deux ioctl(2) 802.11 génériques :
    • SIOCS80211 : configurer des paramètres 802.11 ;
    • SIOCG80211 : récupérer la valeur des paramètres 802.11.
    La structure associée à ces requêtes est ieee80211req définie dans le fichier <net80211/ieee80211_ioctl.h> (eh oui, encore lui !) : Elle définit un champ i_type qui permet de choisir une sous-requête (spécifique aux opérations wireless) à appeler. Voici un exemple de sous-requêtes définies dans le fichier <net80211/ieee80211_ioctl.h> : Dans le cas du scan, les sous-requêtes nécessaires pour effectuer cette opération sont les suivantes :
    • IEEE80211_IOC_SCAN_REQ
    • IEEE80211_IOC_SCAN_RESULTS
    L'application n'a absolument rien à faire, si ce n'est envoyer la demande au driver via ces appels. Le driver se chargera encore et toujours de la sale besogne : il enverra alors des probe requests, afin d'obtenir une réponse des points d'accès environnants. Lors d'une telle requête, le driver va simplement empiler les résultats en utilisant la structure suivante : Les résultats sont écrits les uns derrière les autres dans le buffer que vous aurez fourni (dans le champ i_data) lors de la demande des résultats. Le buffer, une fois rempli, devrait ressembler à ça :

    /img-articles/lmhs/29/cc-art-wifi/fig-2.jpg

    et ainsi de suite. Lors de la demande des résultats, la taille effective des données écrasera la taille initiale du buffer dans le champ i_len. Rien de mieux qu'un petit exemple avec wifi_scan.c : A l'exécution, nous obtenons les résultats suivants : Le point d'accès près de moi au moment du scan est celui auquel j'étais attaché précédemment : Le BSSID du scan et celui du point d'accès sont identiques. Notez que l'API net80211 est en mouvement ! Des changements sont encore à venir concernant cette interface qui permet aux applications utilisateurs de demander un scan. Il se peut donc que les appels actuels soient déjà obsolètes ou en chantier. De plus, ils ne semblent pas complètement fonctionnels selon les cartes utilisées. Auparavant, des outils comme wimon ou wiconfig utilisaient l'ioctl SIOCGWAVELAN, ainsi que les sous-requêtes associées WI_RID_SCAN_APS et WI_RID_READ_APS.

    7. Exemple n°4 : Activer et utiliser le monitoring aka RF_MON

    Une grande majorité des cartes réseau sans fil proposent la capacité d'écouter passivement tout le trafic. Ce mode d'opération est souvent appelé "monitor mode" ou RFMON. Il est équivalent au mode "promiscuous" des réseaux filaires. Ce mode est celui utilisé pour la découverte de réseaux ou wardriving par des logiciels comme kismet, airsnort ou wireshark. Regardons comment nous pouvons utiliser ce mode d'opération et l'activer sur notre carte. L'outil ifconfig nous propose de voir et/ou configurer le mode d'opération de la carte. La ligne qui nous intéresse est la ligne media, la carte fonctionne en 802.11 en auto-sélection (fréquence, mode, etc.), et aucune option ne semble être présente concernant ce mode de fonctionnement. Pour altérer ces informations concernant l'interface wireless, il suffit de s'y prendre comme à l'exemple n°1. On va passer par notre bonne vieille structure ifreq et on ne va pas altérer les flags, mais le média maintenant. C'est logiquement que je vais mettre les doigts sur le champ ifr_media (hmm, je touche le dernier 33 tours de Claude François, hmmm Cloclo, hmmmMmmMMmmmmm...) et voici les deux flags qui m'intéressent :
    • IFM_IEEE80211
    • IFM_IEEE80211_MONITOR
    Allez hop, c'est parti, c'est bien plus parlant avec un exemple. Nous allons éteindre l'interface, activer le mode monitoring, rallumer l'interface et récupérer des données dessus. Pour simplifier, j'ai décidé d'utiliser la libpcap. Elle est simple d'utilisation, nécessite peu de lignes et elle m'est assez familière. Le petit exemple avec wifi_mon.c : A l'exécution : Youpi, nous voilà donc avec quelque chose qui reçoit les paquets directement depuis le kernel sans modification, à la manière dont le font Kismet et Cie. J'ai inclus un micro-exemple de routine de décodage, histoire de donner des idées :)

    8. Exemple n°5 : RadioTap

    RadioTap, initialement développé sur NetBSD, fournit une couche de description du signal commune à tous les drivers wireless. Voilà la structure du paquet avec RadioTap activé :

    /img-articles/lmhs/29/cc-art-wifi/fig-3.jpg

    Ce header transporte une variété d'informations liées à chaque trame reçue comme :
    • la qualité du signal ;
    • la qualité de la réception ;
    • la présence ou non du FCS (Frame Check Sequence, une somme de vérification de l'intégrité du paquet).
    Le header RadioTap se trouve dans le fichier <net80211/ieee80211_radiotap.h> et se présente de la manière suivante : Les différents champs possibles sont décrits quelques lignes en dessous dans le même fichier, ainsi que dans la page de manuel "ieee80211_radiotap(9)". Le champ it_present vous indique quelles sont les informations que le driver vous remonte dans le paquet reçu. Il suffit donc de parser ce champ pour savoir exactement quelles informations nous pouvons obtenir du driver en temps réel et pour chaque trame. En exemple, nous allons prendre le programme précédent et rajouter le support RadioTap, en vous laissant, pour le fun, la joie d'écrire un parser approprié pour récupérer les informations dont vous avez besoin dans ce header :) Le header RadioTap à parser se trouve dans le code d'exemple wifi_radiotap.c. Il est nommé r dans la fonction ieee80211_decode : A l'exécution, peu de changements :) Et voilà !

    Conclusion

    Certaines choses n'ont pas été abordées dans cet article, comme l'envoi de paquets 802.11 depuis les applications utilisateurs ou simplement la gestion de WPA, la gestion de l'énergie, les statistiques, la mesure du signal (même si RadioTap donne déjà ces dernières informations). Cet article se voulait une introduction pragmatique à l'utilisation de cette couche d'abstraction et du wireless en C sous NetBSD pour des applications utilisateurs. J'espère que l'article pourra en aider quelques-uns à démarrer ou à comprendre comment utiliser cette API, l'abstraction étant étroitement partagée entre NetBSD et FreeBSD. Les exemples demanderont un effort minime pour fonctionner également sous FreeBSD. J'espère également que vos connaissances en matière de variété française ont été améliorées et que les nombreux artistes qui ont fait notre culture musicale (merci Gilbert M. !) continueront à vous bercer après la lecture de cet article. Voilà, c'est terminé, amusez-vous bien, vous pouvez maintenant repartir vous entraîner à chanter "L'été indien" devant votre miroir. On irrrrrraaaaa, où tu voudraaaaaAaas quand tuuuuuu vouUUdrrAaaaaaAAAaaaaaas et on s'aimera encoOOOOOOoooooreee, lorsque l'amMMMMooouuuUUUurr sera moOOooort... (Vive les tomates !). Références
    Vous souhaitez commenter cet article ?
    Brèves Flux RSS
    Édito : GNU/Linux Magazine 149
    Édito : GNU/Linux Magazine HS N°60
    Édito : Misc 61
    Édito : Linux Pratique 71
    Édito : Linux Essentiel N°25
    Communication RSS Com. RSS Presse
    Lancement de la plateforme de vente en ligne de PDF des Éditions Diamond ! Un...
    Misc N°61 – Communiqué de presse
    GNU/Linux Magazine N°149 – Communiqué de presse
    GNU/Linux Magazine HS N°60 – Communiqué de presse
    Linux Pratique N°71 – Communiqué de presse
    prochainement moteur de recherches des articles
     
    :
    :
    Jours heures minutes secondes
    En kiosque Flux RSS

    Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Essentiel est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...