2012-02-21 2 views
4

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

Что я делаю, это выделить интерфейс из клона устройства/Dev/нетто/тун, включите его и дайте ему 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.

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

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

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

Это наиболее вероятно, проблема конфигурации маршрута ф ...

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

+0

Оправа код создания здесь: http://pastebin.com/raw.php?i=JZLKkuxS –

+0

Что вы имеете в виду UDP-пакетов «на этом интерфейсе»? Вы имеете в виду направления, направляемые через этот интерфейс? –

+0

@DavidSchwartz Да точно –

ответ

1

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

Update

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

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

Я использовал ioctl для установки адреса интерфейса при открытии udp-сокета: ioctl (portSocket, SIOCSIFADDR, & ifr). Я также включил его с помощью ioctl: ifr.IRFFLAGS | = IFF_UP; (ioctl (portSocket, SIOCSIFFLAGS, &ifr); Так что я уверен, что эти шаги выполняются перед отправкой любых пакетов. В любом случае, я сейчас переписываю код и обновляю свой вопрос с помощью этого нового кода, когда он будет готов. Надеюсь, это поможет кто-нибудь (включая меня) с выяснением, что не так. –

+0

Я не могу использовать IP-адрес последовательной линии (SLIP). Я должен захватить пакеты «вручную» из туннеля и затем инкапсулировать их определенным образом, связанным с принимающим устройством. Я говорю об этом здесь, поскольку он напрямую не связан с моей проблемой. –

+0

Но выполнение 'route add default gw 192.168.2.1 tun0' маршрутизирует весь трафик через tun0, его не то, что я хочу ... –

1

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

Вы можете указать маску подсети (/ 24) при установке IP-адреса для устройства tun или задать маршрут для сети 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 

 Смежные вопросы

  • Нет связанных вопросов^_^