Проблема в приеме пакетов на низкоуровневом сокете AF_PACKET
Коллеги, добрый день
Заинтересовался я возможностью поработать с низкоуровневыми сокетами в Linux. Набросал простую программу, вида:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#define BUFFER_SIZE 1500
char msgbuf[BUFFER_SIZE];
int main() {
printf("RECEIVER \n");
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
perror("receiver start");
exit(1);
}
while (1) {
int read_value = recvfrom(sock, msgbuf, BUFFER_SIZE, 0, 0, 0);
if (read_value < 0) {
perror("receiver read");
exit(1);
}
printf("--------------------------------------------------------------\n");
printf("Got message. Message size = %d \n", read_value);
}
close(sock);
return 0;
}
Запускаю её из-под рута, кидаю при помощи bash-команды строку:
echo "Hellooooooooo!" > /dev/udp/127.0.0.1/9999
Вижу следующий вывод:
--------------------------------------------------------------
Got message. Message size = 53
--------------------------------------------------------------
Got message. Message size = 53
--------------------------------------------------------------
Got message. Message size = 81
--------------------------------------------------------------
Got message. Message size = 81
Это все хорошо. Дальше дорабатываю код, чтобы слушать конкретный сокет, программа начинает выглядеть следующим образом:
int main() {
printf("RECEIVER \n");
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
perror("receiver start");
exit(1);
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
struct sockaddr_ll addr_ll;
memset(&addr_ll, 0, sizeof(addr_ll));
memset(addr_ll.sll_addr, 0, sizeof(addr_ll.sll_addr));
strcpy(ifr.ifr_name, "eth0");
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl:ifreq:index");
return 1;
} else {
printf("if_index = %d\n", ifr.ifr_ifindex);
printf("index = %d\n", if_nametoindex("eth0"));
}
addr_ll.sll_family = AF_PACKET;
addr_ll.sll_ifindex = ifr.ifr_ifindex;
addr_ll.sll_protocol = htons(ETH_P_ALL);
if (bind(sock, (struct sockaddr *)&addr_ll, sizeof(struct sockaddr_ll)) <
0) {
perror("bind");
exit(1);
} else {
printf("bind is OK \n");
}
while (1) {
int read_value = recvfrom(sock, msgbuf, BUFFER_SIZE, 0, 0, 0);
if (read_value < 0) {
perror("receiver read");
exit(1);
}
printf("--------------------------------------------------------------\n");
printf("Got message. Message size = %d \n", read_value);
}
close(sock);
return 0;
}
Само устройство "eth0" существует, пингуется, ему все норм. Программа запускается, на сокет садится. Так же пытаюсь кидать при помощи echo пакеты, но сокет ничего не ловит. Я не понимаю почему. Ради эксперимента пробовал садить сокет на устройство "lo". Результат не отличается.
Подскажите, пожалуйста, чего не хватает в программе или какие неверные параметры я использую?
UPD: Программа висит в консоли порядка получаса. И в неё периодически валятся пакеты. То есть сокет все же воспринимает какие-то данные. Но не от моего echo "" > /dev/udp и не от ping.
Ответы (1 шт):
Я проверил на реальной системе у меня вроде заработало
Добавил такую функцию чтоб смотреть, что сообщение в принципе появилось
void hexdump(unsigned char *buf, int len, int linelen)
{
int i;
int j;
for (i = 0; i < len; i+=16)
{
for (j = 0; j < linelen && j < len; ++j)
{
printf("%02x ", buf[i + j]);
}
printf("\t\t");
for (j = 0; j < linelen && j < len; ++j)
{
if (isalpha(buf[i + j]) || isdigit(buf[i + j]))
{
printf("%c", buf[i + j]);
}
else
{
printf(".");
}
}
printf("\n");
}
}
Вызываю вот так
hexdump((unsigned char *) msgbuf, read_value, 16);
Можешь пробовать создавать veth интерфейсы
Создать:
ip link add veth0 type veth peer name veth1
Удалить:
ip link del veth0
Это такие интерфейсы которые только друг с другом связаны, с ними удобно экспериментировать, типо в один конец пишешь пишешь на другом получаешь
И в целом просто совет такой, начни лучше с IP уровня, типо не ETH_P_ALL а с IPPROTO_RAW, там проще парсилку писать, потом просто открываешь Wireshark, открываешь свою прогу и прям сравниваешь корректно ли парсятся поля у тебя и в Wireshark, мне так было удобнее