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() */