* The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
@ 2018-05-07 10:19 Damir Mansurov
2018-05-07 12:41 ` Paolo Abeni
2018-05-07 16:14 ` Ben Greear
0 siblings, 2 replies; 6+ messages in thread
From: Damir Mansurov @ 2018-05-07 10:19 UTC (permalink / raw)
To: netdev; +Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
[-- Attachment #1: Type: text/plain, Size: 1871 bytes --]
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
dnman@oktetlabs.ru
[-- Attachment #2: b2d_recv.c --]
[-- Type: text/x-csrc, Size: 3108 bytes --]
/*
* 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() */
[-- Attachment #3: b2d_send.c --]
[-- Type: text/x-csrc, Size: 2501 bytes --]
/*
* 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() */
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
2018-05-07 10:19 The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces Damir Mansurov
@ 2018-05-07 12:41 ` Paolo Abeni
2018-05-07 15:23 ` David Ahern
2018-05-08 22:48 ` David Ahern
2018-05-07 16:14 ` Ben Greear
1 sibling, 2 replies; 6+ messages in thread
From: Paolo Abeni @ 2018-05-07 12:41 UTC (permalink / raw)
To: Damir Mansurov, netdev, David Ahern
Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
Hi,
On Mon, 2018-05-07 at 13:19 +0300, Damir Mansurov wrote:
> 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 think that the cause is commit:
commit fb74c27735f0a34e76dbf1972084e984ad2ea145
Author: David Ahern <dsahern@gmail.com>
Date: Mon Aug 7 08:44:16 2017 -0700
net: ipv4: add second dif to udp socket lookups
Something like the following should fix, but I'm unsure it preserves
the intended semathics for 'sdif'. David, can you please have a look?
Thanks!
Paolo
---
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index dd3102a37ef9..0d593d5c33cf 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -401,9 +401,9 @@ static int compute_score(struct sock *sk, struct net *net,
bool dev_match = (sk->sk_bound_dev_if == dif ||
sk->sk_bound_dev_if == sdif);
- if (exact_dif && !dev_match)
+ if (!dev_match)
return -1;
- if (sk->sk_bound_dev_if && dev_match)
+ if (sk->sk_bound_dev_if)
score += 4;
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
2018-05-07 12:41 ` Paolo Abeni
@ 2018-05-07 15:23 ` David Ahern
2018-05-08 22:48 ` David Ahern
1 sibling, 0 replies; 6+ messages in thread
From: David Ahern @ 2018-05-07 15:23 UTC (permalink / raw)
To: Paolo Abeni, Damir Mansurov, netdev
Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
On 5/7/18 6:41 AM, Paolo Abeni wrote:
> Hi,
> On Mon, 2018-05-07 at 13:19 +0300, Damir Mansurov wrote:
>> 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 think that the cause is commit:
>
> commit fb74c27735f0a34e76dbf1972084e984ad2ea145
> Author: David Ahern <dsahern@gmail.com>
> Date: Mon Aug 7 08:44:16 2017 -0700
>
> net: ipv4: add second dif to udp socket lookups
>
> Something like the following should fix, but I'm unsure it preserves
> the intended semathics for 'sdif'. David, can you please have a look?
> Thanks!
>
> Paolo
> ---
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index dd3102a37ef9..0d593d5c33cf 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -401,9 +401,9 @@ static int compute_score(struct sock *sk, struct net *net,
> bool dev_match = (sk->sk_bound_dev_if == dif ||
> sk->sk_bound_dev_if == sdif);
>
> - if (exact_dif && !dev_match)
> + if (!dev_match)
> return -1;
> - if (sk->sk_bound_dev_if && dev_match)
> + if (sk->sk_bound_dev_if)
> score += 4;
> }
>
>
yes, that does look like a mistake -- no match on sk_bound_dev_if should
fail the lookup.
Let me apply the diff and run my vrf tests to make sure they still work.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
2018-05-07 10:19 The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces Damir Mansurov
2018-05-07 12:41 ` Paolo Abeni
@ 2018-05-07 16:14 ` Ben Greear
2018-05-07 21:20 ` David Ahern
1 sibling, 1 reply; 6+ messages in thread
From: Ben Greear @ 2018-05-07 16:14 UTC (permalink / raw)
To: Damir Mansurov, netdev
Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
On 05/07/2018 03:19 AM, Damir Mansurov wrote:
>
> 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.
Hello,
I am not sure if this is your problem or not, but if you are using VRF, then you need
to call SO_BINDTODEVICE before you do the 'normal' bind() call.
Thanks,
Ben
>
>
> 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
> dnman@oktetlabs.ru
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
2018-05-07 16:14 ` Ben Greear
@ 2018-05-07 21:20 ` David Ahern
0 siblings, 0 replies; 6+ messages in thread
From: David Ahern @ 2018-05-07 21:20 UTC (permalink / raw)
To: Ben Greear, Damir Mansurov, netdev
Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
On 5/7/18 10:14 AM, Ben Greear wrote:
> On 05/07/2018 03:19 AM, Damir Mansurov wrote:
>>
>> 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.
>
> Hello,
>
> I am not sure if this is your problem or not, but if you are using VRF,
> then you need
> to call SO_BINDTODEVICE before you do the 'normal' bind() call.
>
This is a different problem -- socket lookup is matching when it should not.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces.
2018-05-07 12:41 ` Paolo Abeni
2018-05-07 15:23 ` David Ahern
@ 2018-05-08 22:48 ` David Ahern
1 sibling, 0 replies; 6+ messages in thread
From: David Ahern @ 2018-05-08 22:48 UTC (permalink / raw)
To: Paolo Abeni, Damir Mansurov, netdev
Cc: Konstantin Ushakov, Alexandra N. Kossovsky, Andrey Dmitrov
On 5/7/18 6:41 AM, Paolo Abeni wrote:
> Hi,
> On Mon, 2018-05-07 at 13:19 +0300, Damir Mansurov wrote:
>> 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 think that the cause is commit:
>
> commit fb74c27735f0a34e76dbf1972084e984ad2ea145
> Author: David Ahern <dsahern@gmail.com>
> Date: Mon Aug 7 08:44:16 2017 -0700
>
> net: ipv4: add second dif to udp socket lookups
>
> Something like the following should fix, but I'm unsure it preserves
> the intended semathics for 'sdif'. David, can you please have a look?
> Thanks!
>
> Paolo
> ---
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index dd3102a37ef9..0d593d5c33cf 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -401,9 +401,9 @@ static int compute_score(struct sock *sk, struct net *net,
> bool dev_match = (sk->sk_bound_dev_if == dif ||
> sk->sk_bound_dev_if == sdif);
>
> - if (exact_dif && !dev_match)
> + if (!dev_match)
> return -1;
> - if (sk->sk_bound_dev_if && dev_match)
> + if (sk->sk_bound_dev_if)
> score += 4;
> }
>
>
The above fixes the reported problem. You should make the same change to
ipv6 as well. Fixes tags:
Fixes: fb74c27735f0a ("net: ipv4: add second dif to udp socket lookups")
Fixes: 1801b570dd2ae ("net: ipv6: add second dif to udp socket lookups")
The change does break a VRF use case, but that case works by accident
given this bug. The use case is a client or server bound to an enslaved
device and trying to communicate locally. In some cases the error is the
ICMP 'Connection refused' getting lost; in other cases the packets don't
make it from one scope to another (eg., VRF based server talking to
device based client). After poking around for a couple of days, I
believe the proper fix for this uses case is beyond the scope of
anything that should be backported to 4.14. So I am fine with the
breakage to what is IMHO a corner case - and there is a reasonable
workaround until I find a proper solution.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2018-05-08 22:48 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-07 10:19 The SO_BINDTODEVICE was set to the desired interface, but packets are received from all interfaces Damir Mansurov
2018-05-07 12:41 ` Paolo Abeni
2018-05-07 15:23 ` David Ahern
2018-05-08 22:48 ` David Ahern
2018-05-07 16:14 ` Ben Greear
2018-05-07 21:20 ` David Ahern
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.