Невозможно получить пакеты UDP на нескольких устройствах TUN - PullRequest
2 голосов
/ 06 апреля 2020

Я пытаюсь создать несколько устройств TUN для туннелирования трафика нескольких устройств c через общий физический интерфейс.

У меня есть такая схема: enter image description here Сокет 1 отправляет трафик c через tun1 с IP-адресом назначения 1.1.1.2, а Socket 2 делает то же самое на другом интерфейсе. У меня есть программа, работающая между обоими устройствами TUN и физическим интерфейсом (eth0), которая инкапсулирует IP-пакеты от устройств TUN в пакеты UDP, а затем отправляет их на сервер. Сервер распаковывает полученные пакеты и отвечает обоим клиентам эхо-пакетами (также инкапсулированными). Когда эти пакеты поступают в eth0, другая программа считывает пакеты и пересылает их на свое устройство TUN. После этого обе программы, работающие за сокетами, блокируются функцией recv (), ожидающей ответа сервера на устройствах tunX. Однако, похоже, что ядро ​​отбрасывает эти пакеты на устройствах TUN, даже когда они правильно сформированы.

Я попытался изменить маску сети обоих интерфейсов на 255.255.255.0, и тогда только один из сокетов получает правильно ответ.

Все это программное обеспечение было написано в C.

Эта функция для создания нового устройства TUN:

#define MTU_SIZE 1500
#define SUBNET_MASK 0xFFFFFFFF

int open_tun_iface(char * name, uint8_t * ip)
{
    int sock;
    struct ifreq ifr_tun;
    struct sockaddr_in * sa;

    int tunfd = tun_init(name, &ifr_tun, IFF_TUN | IFF_NO_PI);

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("Open socket");
        tun_close(tunfd);
        return -1;
    }

    /* Set tun IP */
    if (set_ip(&ifr_tun, sock, ip) < 0) {
        tun_close(tunfd);
        return -1;
    }

    sa = (struct sockaddr_in *)&ifr_tun.ifr_netmask;
    memset(sa,0,sizeof(*sa));
    sa->sin_family = AF_INET;
    sa->sin_addr.s_addr = htonl(SUBNET_MASK);

    /* Set the mask */
    if (ioctl(sock, SIOCSIFNETMASK, &ifr_tun) < 0)
    {
        perror("SIOCSIFNETMASK");
        tun_close(tunfd);
        close(sock);
        return -1;
    }

    /* Read flags */
    if (ioctl(sock, SIOCGIFFLAGS, &ifr_tun) < 0) {
        perror("SIOCGIFFLAGS");
        tun_close(tunfd);
        close(sock);
        return -1;
    }

    /* Set Iface up and flags */
    ifr_tun.ifr_flags |= IFF_UP;
    ifr_tun.ifr_flags |= IFF_RUNNING;

    if (ioctl(sock, SIOCSIFFLAGS, &ifr_tun) < 0)  {
        perror("SIOCGIFFLAGS");
        tun_close(tunfd);
        close(sock);
        return -1;
    }

    /* Set MTU size */
    ifr_tun.ifr_mtu = MTU_SIZE; 
    if (ioctl(sock, SIOCSIFMTU, &ifr_tun) < 0)  {
        perror("SIOCSIFMTU");
        tun_close(tunfd);
        close(sock);
        return -1;
    }

    close(sock);

    return tunfd;
}

Функция, которая читает пакеты из eth0 и перенаправляет их на правильный интерфейс TUN:

void * downlink_distributor(void * args)
{
    uint8_t buffer[BUFFER_DATA_LEN];
    struct sockaddr_in spgw_addr;
    int sockfd;
    int sockaddr_len = sizeof(spgw_addr);
    int len, packet_len, teid;
    eNB * enb = (eNB *) args;

    sockfd = get_spgw_socket(enb);

    while(1)
    {
        /* Read packets from the server */
        len = recvfrom(sockfd, buffer, BUFFER_DATA_LEN, 0, ( struct sockaddr *) &spgw_addr, (socklen_t *)&sockaddr_len);
        if(len < 0)
        {
            perror("recv downlink_distributor");
            return NULL;
        }
        /* Get the TEID that identifies the TUN device */
        teid = analyze_gtp_header(buffer, &packet_len);
        if(teid > -1)
        {
            /* Write the packet in the TUN device associated with the packet TEID */
            tun_write(get_tun_device(tun_descriptors[teid % MAX_TUN_DESCRIPTORS]), buffer + 8, packet_len);
        }
    }

    return NULL;
}

Наконец, эта функция работает на обоих сокетах и ​​генерирует трафик c:

void * _generate_udp_traffic(void * args)
{
    uint8_t buffer[BUFFER_DATA_LEN];
    struct sockaddr_in dest_addr, spgw_addr;
    fd_set fds;
    struct timeval timeout;
    int retval;
    timeout.tv_sec = 0;
    timeout.tv_usec = 1;
    int tunfd;
    char msg[] = "Uplink traffic";
    int sockfd;
    int len;
    int sockaddr_len = sizeof(dest_addr);
    udp_generator gen;

    /* Copy args */
    memcpy(&gen, args, sizeof(udp_generator));
    free(args);

    /* Get TUN device */
    tunfd = get_tun_device(gen.ue);

    /* Get UE data socket (TUN socket) */
    sockfd = get_data_plane_socket(gen.ue);

    /* Configure destiny address */
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET; 
    dest_addr.sin_port = htons(gen.port_dest);
    memcpy((void*) &dest_addr.sin_addr.s_addr, gen.dest_ip, sizeof(struct in_addr));

    /* Configure SPGW address */
    memset(&spgw_addr, 0, sizeof(spgw_addr));
    spgw_addr.sin_family = AF_INET; 
    spgw_addr.sin_port = htons(S1_U_PORT);
    memcpy((void*) &spgw_addr.sin_addr.s_addr, get_spgw_ip(gen.ue), sizeof(struct in_addr));

    while(1)
    {
        sendto(sockfd, msg, strlen(msg), 0, (const struct sockaddr *) &dest_addr,  sizeof(dest_addr));
        while(1)
        {
            FD_ZERO(&fds);
            FD_SET(tunfd, &fds);
            retval = select(tunfd+1, &fds, 0, 0, &timeout);
            if (retval == -1){
                perror("select()");
            }
            else if (retval){
                /* Reuse of the buffer with enough space for the GTP header */
                len = tun_read(tunfd, buffer+8, BUFFER_DATA_LEN); /* We call read to clean the buffer from external traffic */
                /* Detect IPv4 packets*/
                if((buffer[8] & 0xF0) == 0x40)
                {
                    generate_gtp_header(buffer, gen.ue, len);
                    break;
                }
            }
        }
        /* Protect the access to the eNB socket with a lock*/
        pthread_mutex_lock(&lock);
        /* Send tho the SPGW */
        sendto(get_spgw_socket(gen.enb), buffer, len+8, 0, (const struct sockaddr *) &spgw_addr,  sizeof(spgw_addr));
        /* Unlock the mutex */
        pthread_mutex_unlock(&lock);


        /* Receive server answer listening on the TUN device */
        len = recvfrom(sockfd, buffer, BUFFER_DATA_LEN, 0, ( struct sockaddr *) &dest_addr, (socklen_t *)&sockaddr_len);
        printf("UE %d has received: %s\n", get_ue_id(gen.ue), buffer);

        /* Sleep 5 seconds */
        sleep(5);
    }

    return NULL;
}

Если маска сети составляет 32, обе программы, генерирующие клиентский трафик c, блокируются функцией recvfrom () внутри функции _generate_udp_traffi c. Если я установлю обе маски сети на 24, только один из них получит ответ от сервера, а другой застрянет на recvfrom.

Как настроить оба устройства TUN для правильной работы обоих подключенных сокетов?

ОБНОВЛЕНИЕ : Я думаю, что моя проблема - это проблема таблицы маршрутизации ядра. Если я использую только один сокет, я получаю только трафик c с сетевой маской <= 30. С сетевой маской 32, я вижу, что ответ сервера вводится правильно на интерфейсе TUN, но каким-то образом сокет, заблокированный в recv () ничего не получает. </p>

...