hello z
hello z

Reputation: 1

UDP packets arrive on wrong socket with SO_REUSEPORT

In Linux kernel 4.19, I have two UDP sockets which are bound to the same local address using SO_REUSEPORT. I have also connected one sockets (using connect(2)) to remote addresses, and the other was not connected. Let the remote socket always send messages to the local.

The phenomenon I want is that the data is always accepted by the socket that is already connected. But the reality is that depending on the order in which the two sockets are created, when the connected socket was created first, and the unconnected socket was created later, data is always sent to the later created socket(without connect).When the creation order is reversed, data is always sent to the first created socket(with connect). Is this a bug?

In Linux kernel 5.15, Data was always sent to the socket with connect, regardless of the order in which sockets are created. That's what I hope.

Running the following code program will always print :listenfd recv

example :

#include<iostream>
#include<string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread>
using namespace std;

int listenfd = 0, connfd = 0, clientfd = 0, efd;
int server_port = 6666, client_port = 5555;
char server_ip[] = "127.0.0.1", client_ip[] = "127.0.0.1";

void setFd(int fd, int property){
    int sockopt = 1;
    setsockopt(fd, SOL_SOCKET, property, (void* )&sockopt, sizeof(sockopt));
}
void setNonBlock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
void updateEvents( int fd, int events, int op) {
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.events = events;
    ev.data.fd = fd;
    int r = epoll_ctl(efd, op, fd, &ev);
}

void addEvent(int fd) {
    updateEvents(fd, EPOLLIN, EPOLL_CTL_ADD);
}

void delEvent(int fd) {
    updateEvents(fd, EPOLLIN, EPOLL_CTL_DEL);
}

void startfd(int &fd, struct sockaddr_in& bind_addr, struct sockaddr_in& conn_addr,  bool conn) {
    if(fd) {
        delEvent(fd);
        close(fd);
        fd = 0;
    }

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    setFd(fd,SO_REUSEADDR);
    setFd(fd,SO_REUSEPORT);
    setNonBlock(fd);
    
    int r = ::bind(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr));

    if(conn) {
        connect(fd , (struct sockaddr * )&conn_addr, sizeof(struct sockaddr));
    } 
    addEvent(fd);
}

void e_loop() {
    for(;;) {
        const int kMaxEvents = 20;
        struct epoll_event activeEvs[100];
        int n = epoll_wait(efd, activeEvs, kMaxEvents, 10000);

        for (int i = 0; i < n; i++) {
            int fd = activeEvs[i].data.fd;
            int events = activeEvs[i].events;
            if (events & (EPOLLIN | EPOLLERR)) {
                char s[10];
                int recv_n = recv(fd, s, 10, 0);
                if(fd == listenfd) {
                    cout << "listenfd recv\n";
                } else if(fd == connfd) {
                    cout << "connfd recv\n";
                }
            }
        }
    }
}

int main() {
    efd = epoll_create(1);
    thread t(e_loop);

    struct sockaddr_in caddr, saddr;
    memset(&caddr, 0, sizeof caddr);
    memset(&saddr, 0, sizeof saddr);
    caddr.sin_family = AF_INET;
    saddr.sin_family = AF_INET;
    caddr.sin_port = htons(client_port);
    saddr.sin_port = htons(server_port);
    caddr.sin_addr.s_addr = inet_addr(client_ip);
    saddr.sin_addr.s_addr = inet_addr(server_ip);

    startfd(connfd, saddr, caddr, true);
    startfd(listenfd, saddr, caddr,  false);
    startfd(clientfd, caddr, saddr,  true);

    while(1) {
        getchar();
        send(clientfd, "", 0, 0);
    }
}

Upvotes: 0

Views: 33

Answers (0)

Related Questions