Не могу получить Ethernet-пакет через TAP device (использую функционал TunTap)

Коллеги, добрый день. Прошу помощи, чтобы разобраться в следующей ситуации. Хочу обрабатывать Ethernet-frame в пользовательском пространстве. Для "получения" фрейма хочу использовать Tap-device (такая задача, Raw-socket в данном контексте не подходит). Собственно вопрос. Создаю Tap-device следующими командами:

sudo ip tuntap add dev tap0 mode tap
sudo ip address add 10.10.10.3/30 dev tap0

запускаю в промежутке самописную программную часть (о ней ниже). Далее делаю

sudo ip link set tap0 up
ip address show tap0

В итоге имею в консоли следующий вывод:

7: tap0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether ba:ba:18:ce:bb:66 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.3/30 scope global tap0
       valid_lft forever preferred_lft forever

В промежутке, при помощи программы связываю созданный tap-device с файлом "/dev/net/tun". Текст программы:

#include <arpa/inet.h>

#include <cstring>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>

#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_tun.h>

#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>

#include <syslog.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <unistd.h>

int tun_alloc(char *dev, short flags);

int main() {

  char tun_name[IFNAMSIZ];
  char buffer[1500];
  printf("Programm is starting\n");

  /* Подключаюсь к tap-device */
  strcpy(tun_name, "tap0");
  int tun_fd = tun_alloc(tun_name, IFF_TAP); /* tun interface */

  if (tun_fd < 0) {
    perror("Allocating interface");
    exit(1);
  }

  printf("Interface number = %d \n", tun_fd);

  printf("Start reading loop \n");
  while (1) {

    printf("Whating for data... \n");
    int nread = read(tun_fd, buffer, sizeof(buffer));
    if (nread < 0) {
      perror("Reading from interface");
      close(tun_fd);
      exit(1);
    }

    /* Здесь обрабатывать полученный фрейм */
    printf("Read %d bytes from device %s\n", nread, tun_name);
  }

  return 0;
}

int tun_alloc(char *dev, short flags) {
  printf("Entering tun_allocate...\n");
  struct ifreq ifr;
  int fd, err;
  char *clonedev = "/dev/net/tun";

  /* Открываем файловый дескриптор вирт.устройства,
    с которого ожидаю получить Ethernet-frame*/
  if ((fd = open(clonedev, O_RDWR)) < 0) {
    perror("open(clonedev, O_RDWR)");
    return fd;
  }

  memset(&ifr, 0, sizeof(ifr));

  ifr.ifr_flags = flags;
  if (*dev) {
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  }

  /* пытаюсь увязать файл и Tap-device */
  if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
    close(fd);
    perror("ioctl(fd, TUNSETIFF, (void *)&ifr)");
    return err;
  }
  strcpy(dev, ifr.ifr_name);
  printf("Everything seems OK! \n");
  return fd;
}

При запуске начинаю получать кучу служебных Ethernet-фреймов, то есть вроде все работает. Но хочется анализировать "свои" пакеты.

Для этого запускаю простой UDP-sender, который сажаю на устройство с адресом 192.168.44.128 : 9999 и начинаю слать данные на 10.10.10.3:9999.

Иииии... на открытом файловом дескрипторе fd я ничего не получаю. При этом ради прикола, пробовал послушать 10.10.10.3:9999 при помощи программы UDP-listener (обычный набор open ... bind ... read ... cout). Там все данные доходят.

Я не понимаю, почему не получаю на fd Ethernet-фреймы, хотя, по мануалу TunTap-a должен их получать, как я понимаю.

Прошу совета почему так происходит. У меня неверный код? Я не правильно понимаю концепцию TunTap? Не верное создание Tap-device? Не верное понимание сетевого стека?

Пробовал порешать похожую задачу с созданием Tun-device, результат аналогичный. Все извращения происходят под VM Ware Workstation 15 +5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux.


Ответы (1 шт):

Автор решения: Fat-Zer

Я не правильно понимаю концепцию TunTap?

Да. Tun/Tap используется для создания VPN соединения. Пакеты отправляемые на L3 адрес, присвоенный tap-интерфейсу, в него по факту не попадают: они обрабатываются ядром и сразу передаются программе, которой они адресованы (если такая есть). Этот адрес нужен, чтобы на него приходили данные с другой стороны VPN соединения.

Т.е. чтобы что-то отправилось через tap-интерфейс, нужно посылать данные в подсеть, которая находится за ним (10.10.10.0/30), например на 10.10.10.1.

Но т.к. tap эмулирует L2-интерфейс, то UDP/IP-пакет тоже «просто так» не уйдёт: чтобы его отправить ядру нужно знать не только L3 (IP) адрес, но и L2 (MAC) адрес интерфейса, на который отправлять пакет. Для того, чтобы его выяснить, используется протокол ARP. Т.о. в текущем варианте это вызовет только отправку нескольких arp-запросов, которые, само собой, останутся без ответа. Чтобы увидеть сам UDP-пакет на интерфейсе можно, например:

  • Установить реальное VPN-соединение с другим узлом.¹
  • Эмулировать ответ на ARP-запрос.

¹ Наколеночный пример из гугла см. здесь.

→ Ссылка