Интерфейс TUNTAP в C (Linux): невозможно перехватить UDP-пакеты, отправленные на TUNTAP с помощью sendto () - PullRequest
4 голосов
/ 21 февраля 2012

Я пытаюсь написать туннельную программу на 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;
}

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

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, физический NIC).

Мне удалось проверитьчто с Wireshark.

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

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

Ответы [ 3 ]

1 голос
/ 21 сентября 2012

Похоже, что вы только назначили IP-адрес (192.168.2.1) устройству tun, но не указали маску подсети или не настроили маршрут.Без определенного маршрута, определенного для сети 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 comamnd для настройки маршрутов:

ip route add 192.168.2.0/24 dev tun0
1 голос
/ 21 февраля 2012

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

Обновление

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

1) Установите маршрут по умолчанию для IP-адреса. Таким образом, все пакеты будут получены программой пользователя. 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.

0 голосов
/ 26 февраля 2015

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

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

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

...