Bonsoir,
C'est un problème à la fois simple et complexe que je vais exposer. Simple puisque la question principale est précise, complexe parce qu'elle en amène de nombreuses autres. Je cherche à envoyer directement des trames ethernet, sans passer par le protocole IP.
Il est légitime de se demander pourquoi je cherche à faire ceci. En réalité, je cherche à implémenter un Wake On Lan, et comme l'indique Wikipedia (pour cela, je n'ai pas non plus cherché à lire la RFC), pour réveiller une machine, il faut envoyer une trame magique FF:FF:FF:FF:FF:FF, suivie de 5 fois (edit, j'y étais presque, en fait c'est 16 fois :p) l'adresse MAC de la carte réseau cible.
Pour l'implémenter, après de nombreuses recherches, lectures de man, déchiffrages de code source, tests, j'ai réussi à compiler ce code :
Donc ce code compile correctement avec gcc. Pour le test, je l'effectue en root sur Mac OS X Leopard, le pc cible démarré (pour les tests) sur une kubuntu 8.10 et correctement connecté (selon le ifconfig). Le résultat est que le système considère la trame envoyée (la fonction sendto me renvoie une valeur positive), mais un sudo tcpdump de chaque côté ne voit pas la trame passer.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #include <sys/socket.h> #include <sys/types.h> #include <net/if_dl.h> #include <netinet/in.h> #include <net/if_types.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> int main() { int sock,res; if((sock=socket(PF_INET, SOCK_RAW, IPPROTO_RAW))<0) // C'est une des rares combinaisons d'arguments à fonctionner. { printf("Erreur 1\n"); return -1; } char msg[] = "test 1"; struct sockaddr_dl sll = { sizeof(struct sockaddr_dl), AF_LINK, // Faut bien lui dire quelquepart qu'on manipule des adresses mac. 4, // J'ai récupéré ce numéro avec if_nametoindex, mais j'ai essayé avec 0 IFT_ETHER, // Je suppose que c'est cela, ma carte réseau est une carte ethernet 0, 6, 12, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0} /* J'ai essayé en spécifiant {'e', 'n', '0', …} pour le tableau, et les arguments alen à 3, nlen à 6, et slen 12 respectivement pour correspondre à la longueur des données. De même, en fournissant l'adresse mac réelle, cela ne fonctionne pas. */ }; // J'ai aussi essayé d'utiliser ascii2addr, même verdict. if((res=sendto(sock, msg, 6*sizeof(char), 0, (struct sockaddr *) &sll, sizeof(struct sockaddr_dl)))<0) { printf("Erreur 2 : %d\n", res); return -2; } printf("%d\n",res); // Affiche systématiquement 6, donc le message a été envoyé. return 0; }
J'aimerais savoir si je m'y prends mal. Comment envoie-t-on une trame ethernet sur des systèmes Unix ?
J'ai eu beaucoup de mal à trouver de la documentation, le man notamment est piteusement fourni, avez-vous des sources complémentaires ?
Lors de mes tests, j'ai été très étonné du fait que certaines valeurs ne pouvaient pas être valides. Je m'explique. En utilisant la fonction socket, j'ai fait mes tests avec entre autres PF_LINK et PF_UNSPEC, en voyant qu'aucune combinaison des deux autres arguments ne fonctionnaient. J'ai ensuite lancé un while complet sur les 3 arguments, pour voir que la grosse majorité des PF_* ne pouvaient juste pas être utilisées dans socket, la fonction renvoyant une erreur EPROTONOSUPPORT.
Est-ce que c'est spécifique à mon noyau ? Est-ce que vous avez des détails sur l'implémentation des sockets dans les noyaux des systèmes les plus courants ?
Bon, je vais m'arrêter là, si vous arrivez jusque là, j'espère que vous trouverez des éléments de réponse, ou bien une grosse bourde de ma part.
Je reste à votre disposition pour plus de détails.
Merci d'avance.
Partager