2016-06-22 1 views
2

J'essaie d'utiliser C pour envoyer des paquets Ethernet bruts via sendmsg. Ce code ouvre avec succès un socket de paquet brut, tente de remplir une structure iovec avec un seul tableau d'octets (char message []), puis remplit une structure msghdr avec l'adresse de destination, la longueur de l'adresse et un pointeur vers le struct iovec le message. sendmsg() renvoie EINVAL pour chaque appel mais je n'ai aucune idée des arguments qui sont invalides. (Je l'ai supprimé certains perror() appelle à rendre ce code plus simple à lire, la sortie est « Invalid argument ».)Utilisation de sendmsg/sendmmsg avec des trames Ethernet brutes

Je n'ai pas été en mesure de trouver des exemples de la façon dont sendmsg() travaillerait avec raw sockets, mais un code similaire à l'aide de sendto() fonctionne comme prévu. Dans ce code, je forme explicitement la trame Ethernet, y compris les en-têtes et les informations de protocole, mais à ma connaissance cela n'est pas nécessaire avec un appel sendmsg()? J'ai également essayé de faire pointer le point de message.iov_base vers un tampon contenant cette trame Ethernet explicitement formée comprenant l'en-tête de 14 octets, mais sendmsg() rechigne aussi à cela.

Est-ce que sendmsg() et sendmmsg() peuvent fonctionner avec des trames Ethernet brutes? Y a-t-il quelque chose qui me manque dans l'iovec qui le rend invalide?

30 int main(void) { 
32   unsigned char dest[ETH_ALEN] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}; // destination MAC address 
33 
34   // Socket variables 
35   int    s; 
36   unsigned short protocol = htons(ETH_P_802_EX1); 
38 
39   // Message variables 
40   char   message[] = {"Test message. Test message. Test message!\n"}; 
41   size_t   msg_len = strlen(message) + 1;   // Message length includes null terminator 
42   int    e;          // Error code 
43   struct msghdr msg; 
44   struct iovec msgvec; 
45 
46   // Setup source-side socket 
47   s = socket(AF_PACKET, SOCK_RAW, protocol); 
48   if (s < 0) { printf("%d: %s\n", errno, strerror(errno)); return EXIT_FAILURE; } 
49 
50   msgvec.iov_base = message; 
51   msgvec.iov_len = msg_len; 
52   memset(&msg, 0, sizeof(msg)); 
53   msg.msg_name = dest; 
54   msg.msg_namelen = ETH_ALEN; 
55   msg.msg_control = NULL; 
56   msg.msg_controllen = 0; 
57   msg.msg_flags = 0; 
65   msg.msg_iov = &msgvec; 
66   msg.msg_iovlen = 1; 
67 
68   for (int i=0; i<10; i++) { 
69     e = sendmsg(s, &msg, 0); 
73   } 
79   close(s); 
80   return EXIT_SUCCESS; 
81 } 

Répondre

3

J'ai obtenu ce code pour travailler après quelques ajustements. Au lieu d'envoyer l'adresse en tant que chaîne d'octets comme implicite par la page de manuel pour sendmsg(), j'ai utilisé une structure sockaddr_ll. J'ai également pointé le iovec à un tampon contenant la trame Ethernet complète y compris les en-têtes. Pourquoi la page man spécifie-t-elle explicitement une structure sockaddr * dans le prototype sendto mais un void * dans la définition msghdr reste inconnu pour moi.

J'ajouté ce code après l'appel à la prise():

39   struct sockaddr_ll address; 
40   struct ifreq buffer;         // To get information with ioctl() 
41   char   ifname[] = {"eth0"}; 
42   int    ifindex;        // Interface index, from ioctl() call 

57   memset(&buffer, 0, sizeof(buffer));      // Getting interface index value 
58   strncpy(buffer.ifr_name, ifname, IFNAMSIZ); 
59   ioctl(s, SIOCGIFINDEX, &buffer); 
60   ifindex = buffer.ifr_ifindex; 
61 
62   // Setup sockaddr_ll address 
63   memset((void*) &address, 0, sizeof(address)); 
64   address.sll_family = PF_PACKET; 
65   address.sll_ifindex = ifindex; 
66   address.sll_halen = ETH_ALEN; 
67   memcpy((void*) (address.sll_addr), (void*) dest, ETH_ALEN); 

et remplacé ces lignes de code pour l'attribution struct msghdr:

81   msg.msg_name = &address; 
82   msg.msg_namelen = sizeof(address);