Интерфейс TUNTAP в C (Linux): не удается перехватить пакеты UDP, отправленные на TUNTAP с помощью sendto()

Я пытаюсь написать программу туннелирования на C, которая будет принимать пакеты UDP с интерфейса TUNTAP и отправлять их на последовательный интерфейс.

Что я делаю, так это выделяю интерфейс из устройства-клона /dev/net/tun, включаю его и даю ему IP-адрес:

int tun_setup(char *dev, int flags) {

  struct sockaddr_in  my_addr;
  struct ifreq ifr;
  int fd, err;
  string clonedev = "/dev/net/tun";

  // Open clone device file descriptor
  if( (fd = open(clonedev.c_str() , O_RDWR)) < 0 ) {
    perror("Opening /dev/net/tun");
    return fd;
  }

  // Initialise interface parameters structure
  memset(&ifr, 0, sizeof(ifr));

  // Set up flags
  ifr.ifr_flags = flags;

  // Set up interface name
  if (*dev) {
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  }

  // Put interface in TUN mode
  if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
    perror("ioctl(TUNSETIFF)");
    close(fd);
    return err;
  }

  strcpy(dev, ifr.ifr_name);

  // Create a socket
  if ( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  perror("socket");
      exit(1);
  }

  // Get interface flags
  if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
    perror("cannot get interface flags");
    exit(1);
  }

  // Turn on interface
  ifr.ifr_flags |= IFF_UP;
  if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
    fprintf(stderr, "ifup: failed ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Set interface address
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  memcpy(&ifr.ifr_addr, &my_addr, sizeof(struct sockaddr));

  if (ioctl(s, SIOCSIFADDR, &ifr) < 0) {
    fprintf(stderr, "Cannot set IP address. ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Return interface file descriptor
  return fd;
}

Затем я создаю поток, который будет опрашивать () дескриптор файла созданного интерфейса и выполнять чтение () + некоторые другие вещи, когда происходит событие.

void* tun_readThreadProc (void* param) {
  struct pollfd fds[1];
  int nread;
  unsigned char buffer[BUFFERSIZE];
  fds[0].fd = tun_fd;
  fds[0].events = POLLIN;

  printf("%s : Entered. tun_fd = %d \n",__FUNCTION__,tun_fd);

  for(;;)
  {
    printf("%s : Entered loop\n",__FUNCTION__);
    if((poll(fds, 1, -1)) == -1)
    {
      perror("poll");
      exit(1);
    }

    printf("%s : Poll sensed something\n",__FUNCTION__);

    if((nread = read(tun_fd, buffer, BUFFERSIZE)) < 0)
    {
      perror("read");
      close(tun_fd);
      exit(1);
    }

    printf("%s : Read something : %d bytes\n",__FUNCTION__,nread);
  }
  return 0;
}

В другой части программы я привязываю сокет UDP к IP-адресу этого интерфейса TUNTAP.

void socketInit( void )
{
  int                 on = 1;
  struct sockaddr_in  my_addr;
  unsigned short DefaultPort = 47808;

  // Create a socket
  if ( (s1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }

  // Bind to it
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  my_addr.sin_port = htons(DefaultPort);

  if ( (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) ) {
    perror("bind");
  }
  // Allow it to broadcast
  if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on)) < 0) {
    perror("setsockopt");
  }
}

В другой функции я использую sendto() для отправки пакетов через этот сокет. Я должен перехватывать эти пакеты с помощью потока poll() + read(), а затем отправлять их через последовательный порт, но poll() никогда не перехватывает события на интерфейсе TUNTAP.

Я могу отправить эхо-запрос через этот интерфейс, используя команду ping -I tun0 [какой-то пункт назначения] (tun0 = имя интерфейса TUNTAP)

Но если я использую ping -I 192.168.2.1 [какой-то пункт назначения] (192.168.2.1 = адрес интерфейса TUNTAP), он проходит через интерфейс по умолчанию (eth0, физический сетевой адаптер).

Я смог проверить это с помощью Wireshark.

Скорее всего, это проблема конфигурации IP-маршрута...

Я был бы очень рад, если бы кто-нибудь мог мне помочь.


person Alex On The Moon    schedule 21.02.2012    source источник
comment
Код создания сокета здесь: pastebin.com/raw.php?i=JZLKkuxS   -  person Alex On The Moon    schedule 21.02.2012
comment
Что вы подразумеваете под пакетами UDP на этом интерфейсе? Вы имеете в виду пункты назначения, маршрутизируемые через этот интерфейс?   -  person David Schwartz    schedule 21.02.2012
comment
Я добавил ваш код из pastebin сюда, его легче читать (и если ФБР закроет pastebin за слишком большую помощь людям, StackOverfow все равно сможет жить: P!)   -  person Eregrith    schedule 21.02.2012
comment
@Eregrith Спасибо, хотя я пока ничего не вижу   -  person Alex On The Moon    schedule 21.02.2012
comment
@Eregrith Только что одобрил ваши изменения, спасибо за вашу помощь :)   -  person Alex On The Moon    schedule 21.02.2012
comment
@AlexOnTheMoon Мне пришлось снова исправить ваш отступ, пожалуйста, будьте осторожны с тем, что вы публикуете. SO не является контейнером для пасты. Кроме того, вы должны действительно разбивать свой код на функции и избегать использования препроцессора в их середине.   -  person Eregrith    schedule 21.02.2012
comment
@Eregrith В настоящее время я переписываю эту часть кода в отдельную программу, чтобы попытаться изолировать проблему. Я заменю текущий код новым, когда он будет готов. Я удалил материал препроцессора уже в моем новом коде.   -  person Alex On The Moon    schedule 21.02.2012
comment
Чтобы изолировать проблему, вы можете попробовать отправить пакет UDP на кран tun с помощью команды sendip. Это делается следующим образом. sendip -v -p ipv4 -is <<source IP>> -p udp -us <<source PORT>> -ud <<destination PORT>> <<destination IP>> Теперь дайте мне знать, работает ли ваш цикл опроса.   -  person snibu    schedule 21.02.2012
comment
@AlexOnTheMoon: Если вы считаете, что было бы полезно иметь пример кода TAP/TUN в дикой природе, взгляните на «unix-порт» LwIP в пакете contrib. Я почти уверен, что это работает с UDP. На самом деле, вы даже можете попробовать собрать их демо и посмотреть, будет ли оно работать с вашим интерфейсом. Если нет, то, вероятно, проблема связана с настройкой вашего интерфейса, а может быть, и не с вашим кодом.   -  person Brian McFarland    schedule 21.02.2012
comment
@BrianMcFarland Спасибо, что указали мне на LwIP. Я попытаюсь собрать его и посмотреть, как он ведет себя с моим интерфейсом.   -  person Alex On The Moon    schedule 22.02.2012
comment
@user718895 user718895 Я попробую и дам вам знать.   -  person Alex On The Moon    schedule 22.02.2012


Ответы (3)


Как вы установили IP-адрес интерфейса tun tap? Используете команду "ip addr add"? Вы проверили/настроили кран tun, т.е. "ip link set <<tun tap interface name>> up"? Наконец, вы уверены, что ваш код отправки сокета UDP запускается после выполнения двух предыдущих условий, то есть он имеет правильный IP-адрес и интерфейс tun tap работает.

Обновить

Я думаю, что вы понимаете концепцию неправильно, или я не очень хорошо вас понял. Насколько я знаю, вам не нужно делать столько всего. Концепция устройства tun tap заключается в том, что любые пакеты, которые интерфейс tun tap получает, отправляются в программу пользовательского пространства, и все, что программа пользовательского пространства записывает в устройство tun tap, отправляется в сеть. Сказав это и прочитав, что ваше требование состоит в том, чтобы туннелировать пакеты UDP в последовательный интерфейс, вам следует сделать следующее.

1) Установите маршрут по умолчанию на IP-адрес tun tap. Таким образом, все пакеты будут получены программой пользовательского пространства. route add default gw 192.168.2.1 tun0

Итак, теперь в пользовательской программе вы получаете весь пакет с заголовками IP и UDP. Теперь весь этот пакет будет сообщением, которое вы хотите передать через последовательный интерфейс. Поэтому, когда мы отправляем его через последовательный интерфейс, используя UDP или TCP (в зависимости от того, что вы предпочитаете), мы автоматически снова инкапсулируем весь пакет с еще одним заголовком UDP и IP.

2) Вам также необходимо добавить правило хоста с адресом последовательного интерфейса. Таким образом, вышеупомянутые инкапсулированные пакеты для последовательного интерфейса отправляются на последовательный интерфейс, и они не возвращаются обратно на устройство TUN TAP из-за правила по умолчанию. route add -host <<serial ip address>> dev <<serial device>>

Дайте route -n и проверьте правильность маршрутизации. Убедитесь, что у вас нет других нежелательных маршрутов. Если да, удалите их.

Примечание. Также вы можете извлечь из пакета только полезные данные и, используя сокеты RAW, создать собственный заголовок UDP с назначением в качестве IP-адреса последовательного интерфейса и записать его с помощью устройства TUN TAP. Этот пакет будет рассматриваться как экземпляр маршрутизации ядра как принадлежащий последовательному интерфейсу и будет перенаправлен туда из-за правила хоста. Если вы собираетесь использовать этот случай, вам нужно создать собственный заголовок IP и UDP, а также вычислить CRC.

person snibu    schedule 21.02.2012
comment
Я использовал ioctl для установки адреса интерфейса при открытии сокета udp: ioctl(portSocket, SIOCSIFADDR, &ifr) . Я также включаю его с помощью ioctl : ifr.IRFFLAGS |= IFF_UP; (ioctl(portSocket, SIOCSIFFLAGS, &ifr); Поэтому я почти уверен, что эти шаги выполняются до отправки каких-либо пакетов. В любом случае, сейчас я переписываю код и обновлю свой вопрос этим новым кодом, когда он будет готов. Я надеюсь, что это поможет любому (включая меня) выяснить, что не так. - person Alex On The Moon; 22.02.2012
comment
Я не могу использовать последовательный IP-адрес (SLIP). Я должен захватывать пакеты вручную из tun tap, а затем инкапсулировать их определенным образом, связанным с принимающим устройством. Я не говорил об этом здесь, так как это не имеет прямого отношения к моей проблеме. - person Alex On The Moon; 23.02.2012
comment
Но выполнение route add default gw 192.168.2.1 tun0 направляет весь трафик через tun0, а это не то, что мне нужно... - person Alex On The Moon; 23.02.2012
comment
1) Вставьте вывод route -n 2) Пробовали ли вы отправить UDP с помощью sendip на TUN TAP? В этом случае опрос работает нормально? - person snibu; 23.02.2012
comment
Когда я использую route add default gw 192.168.2.1 tun0 опрос/чтение делает свое дело, но с другой стороны весь трафик идет через tun0 что бесполезно. Итак, в качестве временного решения я просто использую route add -net 192.168.2.0 netmask 255.255.255.0 dev tun0... Что позволяет мне провести некоторое тестирование в контексте моего проекта. Хотя я хотел бы иметь постоянное решение, которое позволяет мне убедиться, что любой пакет с исходным IP-адресом 192.168.2.1 (адрес tuntap) действительно проходит через tuntap - person Alex On The Moon; 23.02.2012
comment
Вот route -n после выполнения route add default gw 192.168.2.1 tun0 Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0 tun0 0.0.0.0 192.168.0.1 0.0.0.0 UG 0 0 0 eth0 - person Alex On The Moon; 23.02.2012
comment
Должно быть, немного тяжело читать такое. Вот pastebin: pastebin.com/raw.php?i=4UcYS1dN - person Alex On The Moon; 23.02.2012
comment
Из вашей таблицы маршрутизации я вижу, что нет записи с IP-адресом tun0, кроме маршрута по умолчанию. Это означает, что настройка адреса интерфейса с помощью ioctl(portSocket, SIOCSIFADDR, &ifr) работает неправильно. Удалите сетевой маршрут и попробуйте выполнить приведенные ниже команды после запуска программы ip link set tun0 up ip addr add 192.168.2.1/24 dev tun0 - person snibu; 23.02.2012

Похоже, вы только присвоили tun-устройству IP-адрес (192.168.2.1), но не указали маску подсети и не настроили маршрут. Без определенного маршрута, определенного для сети 192.168.2.0/24, уровень IP будет считать, что этот пакет предназначен для отправки по маршруту по умолчанию.

Вы можете либо указать маску подсети (/24) при установке IP-адреса для настраиваемого устройства, либо установить маршрут для сети 192.168.2.0/24 вручную.

Один из способов сделать это — вызвать «ip» с помощью функции system(). Чтобы указать адрес с маской подсети (с уже включенным заголовком):

// assuming the tun device name is "tun0"
system("ip addr add 192.168.2.1/24 dev tun0"); 

Также, если у вас уже есть IP-адрес, назначенный устройству, вы можете использовать команду ip route для настройки маршрутов:

ip route add 192.168.2.0/24 dev tun0
person Song Gao    schedule 20.09.2012

Вы очистили события на pollfd. fds[0].revents=0?

Нам нужно очистить полученные события.

Источник: http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/

person Suman    schedule 26.02.2015