Почему в ядре Linux V5 UDP-соединение и отключение не работает? - PullRequest
2 голосов
/ 06 марта 2020

У меня есть приложение, которое очень долго работало в разных дистрибутивах под ядром v2, v3 и v4, и у меня никогда не было проблем. Сейчас я пытаюсь заставить его работать на Ubuntu с ядром V5, и у меня есть проблема. Это основано на сокетах UDP и есть механизм обнаружения, который использует широковещательные пакеты. Как это работает, клиент отправляет широковещательный пакет UPD, и все серверы отвечают на эту широковещательную рассылку. После этого клиент получает информацию с каждого сервера, отправляя направленный пакет UPD на все серверы, которые ответили на трансляцию. На стороне сервера, когда получен пакет UDP, я вызываю connect с адресом клиента, поэтому мне не нужно больше следовать адресу клиента и порту в коде. Как только ответ отправлен, я «разъединяю» сокет, вызывая connect с AF_UNSPE C sin_family.

Это работало нормально, но не с ядром V5. После того, как я отключился, кажется, что сервер не получает направленных сообщений. Он получает широковещательный UPD, но не тот, который специально адресован к нему.

На всякий случай причиной проблемы могут быть другие факторы (firewall, et c ...), я сделал небольшой проверить код и увидеть то же самое; этот код включен в мой пост. Если я не использую подключение / отключение в коде сервера, он работает нормально. Но не с подключением / отключением. И если я использую ядро ​​4 и ниже, он прекрасно работает с подключением / отключением. Для ядра V5 я тестировал на 5.2.9 и 5.3.

Я не понимаю, потому что это правильный и документированный способ работы с сокетами UDP. Я много гуглил и не нашел ничего такого, что указывало бы на то, что оно не работает с Linux ядром V5.x.

Кто-нибудь имеет представление об этой проблеме?

Спасибо.

Фрэнк

Код сервера

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>

#define PORT     20000 
#define MAXLINE 512 

// Driver code 
int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    char ifName[50] = "enp2s0";
    struct sockaddr_in servaddr, cliaddr; 

    char resp[50];
    fd_set  rset;

    struct sockaddr hwaddr;
    struct sockaddr_in  curr_ipaddr, ipaddr;
    struct sockaddr_in  udp_addr;
    struct timeval      toval, *ptoval;

    int     maxfdp1, errcode;
    int     on = 1;

    resp[0] = 0xaa;
    resp[1] = 0x55;
    resp[2] = 0x80;
    resp[3] = 0x7f;
    resp[4] = 0xd0;
    resp[5] = 0x00;
    resp[6] = 0x09;
    resp[7] = 0xaa;
    resp[8] = 0x00;
    resp[9] = 0x02;
    resp[10] = 0x2c;
    resp[11] = 0x08;
    resp[12] = 0x00;
    resp[13] = 0x40;
    resp[14] = 0x10;
    resp[15] = 0x92;

    // Creating socket file descriptor 
    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        perror("socket creation failed"); 
        exit(EXIT_FAILURE); 
    } 

    // Allow broadcast
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr)); 
    memset(&cliaddr, 0, sizeof(cliaddr)); 

    // Filling server information 
    servaddr.sin_family    = AF_INET; // IPv4 
    servaddr.sin_addr.s_addr = INADDR_ANY; 
    servaddr.sin_port = htons(PORT); 

    // Bind the socket with the server address 
    if ( bind(sockfd, (const struct sockaddr *)&servaddr,  
            sizeof(servaddr)) < 0 ) 
    { 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 

    int len, n; 

    len = sizeof(cliaddr);  //len is value/resuslt 

    maxfdp1 = sockfd+1;
    FD_ZERO(&rset);

    for ( ; ; )
        {
        FD_SET(sockfd, &rset);

        toval.tv_sec = 0;
        toval.tv_usec = 500000;
        ptoval = &toval;

        if ((errcode = select(maxfdp1, &rset, NULL, NULL, ptoval)) < 0)
            {
            if (errno == EINTR)
                {
                continue;
                }
            else
                {
                printf("Select error %d", errno);
                }
            }

        if (FD_ISSET(sockfd, &rset))
            {
            n = recvfrom(sockfd, (char *)buffer, MAXLINE,  
                MSG_WAITALL, ( struct sockaddr *) &cliaddr, 
                &len); 
            buffer[n] = '\0'; 

            printf("received =\n");
            for (int i=0; i<n; i++) printf("%02x ", (unsigned char)buffer[i]);
            printf("\n");

            // Connect the socket; this way it can be used as a TCP socket
            connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));

            if ((unsigned char)buffer[4] == 0xc0)
                {
                // This is to answerr to the broadcast so I can receive the
                // the next directed packet
                printf("sending ans\n");
                sendto(sockfd, (const char *)resp, 16,  
                    MSG_CONFIRM, (const struct sockaddr *) &cliaddr, 
                        len); 
                }

            // Unconnect the UDP socket
            struct sockaddr_in dumaddr; 
            // Filling server information 
            dumaddr.sin_family    = AF_UNSPEC; // IPv4 
            dumaddr.sin_addr.s_addr = INADDR_ANY; 
            dumaddr.sin_port = htons(PORT); 
            int cerr = connect(sockfd, (struct sockaddr *)&dumaddr, sizeof(dumaddr));
            printf("cerr = %d\n", cerr);
            }      
        }
    return 0; 
} 

Код клиента

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>

#define PORT     20000 
#define MAXLINE 512 

// Driver code 
int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    struct sockaddr_in servaddr, cliaddr; 

    char cmdc0[20], cmdc1[20];

    int     on = 1;

    cmdc0[0] = 0xaa;
    cmdc0[1] = 0x55;
    cmdc0[2] = 0x80;
    cmdc0[3] = 0x7f;
    cmdc0[4] = 0xc0;
    cmdc0[5] = 0x00;
    cmdc0[6] = 0x00;
    cmdc0[7] = 0x00;
    cmdc0[8] = 0x00;
    cmdc0[9] = 0x00;

    cmdc1[0] = 0xaa;
    cmdc1[1] = 0x55;
    cmdc1[2] = 0x80;
    cmdc1[3] = 0x7f;
    cmdc1[4] = 0xc1;
    cmdc1[5] = 0x00;
    cmdc1[6] = 0x00;
    cmdc1[7] = 0x00;
    cmdc1[8] = 0x00;
    cmdc1[9] = 0x00;


    // Creating socket file descriptor 
    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        perror("socket creation failed"); 
        exit(EXIT_FAILURE); 
    } 

    // Allow broadcast
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr)); 
    memset(&cliaddr, 0, sizeof(cliaddr)); 

    // Filling server information 
    servaddr.sin_family    = AF_INET; // IPv4 
    servaddr.sin_addr.s_addr = INADDR_BROADCAST; 
    servaddr.sin_port = htons(PORT); 

    int len, n; 

    len = sizeof(servaddr);  //len is value/resuslt 

    sendto(sockfd, (const char *)cmdc0, 7,  
        MSG_CONFIRM, (const struct sockaddr *) &servaddr, 
            len); 

    while (1 == 1)
        {
        int clen=sizeof(cliaddr);

        n = recvfrom(sockfd, buffer, MAXLINE-1, 0, (struct sockaddr *)&cliaddr, &clen);
        buffer[n] = '\0';

        char *baddr=(char *)&(cliaddr.sin_addr);
        printf("received from = %d-%d-%d-%d\n", (unsigned char)baddr[0], (unsigned char)baddr[1],
                                                (unsigned char)baddr[2], (unsigned char)baddr[3]);
        for (int i=0; i<n; i++) printf("%02x ", (unsigned char)buffer[i]);
        printf("\n");

        if ((unsigned char)buffer[4] == 0xd0)
            {
            // Send another command but directed this time
            printf("Sending C1...\n");
            sendto(sockfd, cmdc1, 7, 0, (const struct sockaddr *)&cliaddr, clen);
            }
        }

    return 0; 
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...