Использование sendmsg/sendmmsg с необработанными сетевыми фреймами
Я пытаюсь использовать C для отправки сырых пакетов Ethernet через sendmsg. Этот код успешно открывает необработанный сокет пакета, пытается заполнить struct iovec одним массивом байтов (char message[]), затем заполняет struct msghdr адресом назначения, длиной адреса и указателем на структуру iovec, содержащую сообщение. sendmsg() возвращает EINVAL для каждого вызова, но я не знаю, какие аргументы неверны. (Я удалил некоторые вызовы perror(), чтобы сделать этот код проще для чтения; вывод "Неверный аргумент".)
Я не смог найти никаких примеров того, как sendmsg () будет работать с необработанными сокетами, но похожий код, использующий sendto(), работает так, как ожидалось. В этом коде я явно формирую кадр Ethernet, включая заголовки и информацию о протоколе, но, насколько я понимаю, в вызове sendmsg () нет необходимости? Я также попытался сделать так, чтобы message.iov_base указывал на буфер, содержащий этот явно сформированный кадр Ethernet, включая 14-байтовый заголовок, но sendmsg () также препятствует этому.
Могут ли sendmsg () и sendmmsg() работать с необработанными кадрами Ethernet? Есть ли что-то, что я пропускаю в iovec, что делает его недействительным?
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 }
1 ответ
Я получил этот код для работы после внесения некоторых корректировок.
Вместо того, чтобы отправлять адрес в виде строки байтов, как подразумевается в справочной странице для sendmsg(), я использовал struct sockaddr_ll. Я также указал iovec на буфер, содержащий полный кадр Ethernet, включая заголовки. Почему страница man явно указывает const struct sockaddr* в прототипе sendto, а void* в определении msghdr - мне неизвестно.
Я добавил этот код после вызова socket():
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 );
и заменил эти строки кода для назначения структуры msghdr:
81 msg.msg_name = &address;
82 msg.msg_namelen = sizeof(address);