Greetings,

After successful call of the setsockopt(SO_BINDTODEVICE) function to set data reception from only one interface, the data is still received from all interfaces. Function setsockopt() returns 0 but then recv() receives data from all available network interfaces.

The problem is reproducible on linux kernels 4.14 - 4.16, but it does not on linux kernels 4.4, 4.13.

I have written C-code to reproduce this issue (see attached files b2d_send.c and b2d_recv.c). See below explanation of tested configuration.


        PC-1                              PC-2
 -------------------               -------------------
 | b2d_send        |               | b2d_recv        |
 |                 |               |                 |
 |           ------|               |------           |
 |          | eth0 |---------------| eth0 |          |
 |           ------|               |------           |
 |                 |               |                 |
 |           ------|               |------           |
 |          | eth1 |---------------| eth1 |          |
 |           ------|               |------           |
 |                 |               |                 |
 -------------------               -------------------

Steps:
1. Copy b2d_recv.c to PC-2, compile it ("gcc -o b2d_recv b2d_recv.c") and run "./b2d_recv eth0 23777" to get derived data only from eth0 interface. Port number in this example is 23777 only for sample.

2. Copy b2d_send.c to PC-1, compile it ("gcc -o b2d_send b2d_send.c") and run "./b2d_send ip1 ip2 23777" where ip1 and ip2 are ip addresses of interfaces eth0 and eth1 of PC-2.

3. Result:
- b2d_recv prints out data from eth0 and eth1 on linux kernels from 4.14 up to 4.16.
- b2d_recv prints out data from only eth0 on linux kernels below 4.14.


******************
Thanks,
Damir Mansurov
dn...@oktetlabs.ru
/*
 * Receive udp packets from desired interface
 *
 * This tool is used to check that option SO_BINDTODEVICE works correctly
 * setsockop(SO_BINDTODEVICE)
 * Use together with b2d_send.c
 *
 * 1. Start b2d_recv on receiver PC
 * 2. Start b2d_send on sender PC
 * 3. Check that packets are received only from the selected interface
 *
 * usage:   ./b2d_recv interface port
 * example: ./b2d_recv eth0 23777
 */

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

#define RX_BUFF_SIZE 1024

/* create recv socket, if error occured exit(EXIT_FAILURE) */
int create_recv_sock(const char * str_interface, const char * str_port,
                     struct sockaddr_in * sock_addr);

int
main(int argc, char *argv[])
{
    struct sockaddr_in sa_recv;

    if (argc != 3)
    {
        printf("usage: %s interface port\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sfd_recv  = create_recv_sock(argv[1], argv[2], &sa_recv);

    char rx_buff[RX_BUFF_SIZE] = {0};
    struct sockaddr_in src_addr;
    socklen_t addr_len;
    memset(&src_addr, 0, sizeof(struct sockaddr_in));

    while (1)
    {
        ssize_t res = recvfrom(sfd_recv, rx_buff, RX_BUFF_SIZE - 1, 0,
                       (struct sockaddr *)&src_addr, &addr_len);
        if (res < 0)
        {
            perror("recvfrom");
            exit(EXIT_FAILURE);
        }

        char buf[256];
        const char * x = inet_ntop(src_addr.sin_family, &src_addr.sin_addr,
                                   buf, addr_len);
        if (x == NULL)
        {
            perror("inet_ntop");
            exit(EXIT_FAILURE);
        }
        printf("recv %ld bytes from %s:%u: \"%s\"\n",
                res, buf, ntohs(src_addr.sin_port), rx_buff);
    }

    exit(EXIT_SUCCESS);
}


int
create_recv_sock(const char * str_interface, const char * str_port,
                       struct sockaddr_in * sock_addr)
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    memset(sock_addr, 0, sizeof(struct sockaddr_in));
    sock_addr->sin_family = AF_INET;
    sock_addr->sin_addr.s_addr = htonl(INADDR_ANY);
    sock_addr->sin_port = htons((uint16_t)atoi(str_port));

    int res = bind(sockfd, (struct sockaddr*)sock_addr,
                      sizeof(struct sockaddr_in));

    if (res < 0)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (strlen(str_interface) == 0)
    {
        puts("Data will be received from all interfaces");
    }
    else
    {
        res = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                         str_interface, strlen(str_interface) + 1);
        if (res < 0)
        {
            perror("setsockopt");
            exit(EXIT_FAILURE);
        }
        else
        {
            printf("success bind to device \"%s\"\n", str_interface);
        }
    }
    printf("recv port %s\n", str_port);
    return sockfd;

}/* create_recv_socket() */


/*
 * Send udp packets from two various interfaces,
 *
 * This tool used to check correctly work option
 * setsockopt(SO_BINDTODEVICE)
 * Use together with b2d_recv.c
 * Detailed description in file b2d_recv.c
 *
 * usage    ./b2d_send ip1 ip2 port
 * example: ./b2d_send 192.168.44.2 192.168.45.2 23777
 */

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

#define RX_BUFF_SIZE 1024

/* create sender socket, if error occured exit(EXIT_FAILURE) */
int create_sender_sock(const char * str_ip, const char * str_port,
                       struct sockaddr_in * sock_addr);

int
main(int argc, char *argv[])
{
    struct sockaddr_in sa_sender1, sa_sender2;

    if (argc != 4)
    {
        printf("usage: %s ip1 ip2 port\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sfd_sender1 = create_sender_sock(argv[1], argv[3], &sa_sender1);
    int sfd_sender2 = create_sender_sock(argv[2], argv[3], &sa_sender2);

    char tx_buff1[] = "Data from first socket";
    char tx_buff2[] = "Data from second socket";

    ssize_t res = send(sfd_sender1, tx_buff1, strlen(tx_buff1) + 1, 0);
    if (res < 0)
    {
        perror("sender1");
        exit(EXIT_FAILURE);
    }
    printf("success send %ld bytes to %s:%s \"%s\"\n",
            res, argv[1], argv[3], tx_buff1);

    res = send(sfd_sender2, tx_buff2, strlen(tx_buff2) + 1, 0);
    if (res < 0)
    {
        perror("sender2");
        exit(EXIT_FAILURE);
    }
    printf("success send %ld bytes to %s:%s \"%s\"\n",
            res, argv[2], argv[3], tx_buff2);

    exit(EXIT_SUCCESS);

}/* main() */


int
create_sender_sock(const char * str_ip, const char * str_port,
                   struct sockaddr_in * sock_addr)
{
    int res;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    memset(sock_addr, 0, sizeof(struct sockaddr_in));
    sock_addr->sin_family = AF_INET;
    if (inet_pton(AF_INET, str_ip, &(sock_addr->sin_addr)) != 1)
    {
        perror("Bad ip_add");
        exit(EXIT_FAILURE);
    }
    sock_addr->sin_port = htons((uint16_t)atoi(str_port));

    res = connect(sockfd, (struct sockaddr*)sock_addr,
                  sizeof(struct sockaddr_in));
    if (res < 0)
    {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    return sockfd;

}/* create_sender_socket() */


Reply via email to