Discussion:
Socket receives packet to multicast group to which it was not joined since kernel 3.13.10-1
Yurij M. Plotnikov
2014-05-12 16:38:03 UTC
Permalink
On kernel 3.13.10-1 I see that socket joined to one multicast group
receives packets to another multicast address. That can be reproduced by
the following example:

1. socket(DGRAM) -> 3
2. bind(3, 0.0.0.0:12345) -> 0
3. setsockopt(3, IP_MULTICAST_IF, {224.168.2.9, 7}) -> 0
// "7" is correct interface index

4. Send packet from peer host to 224.168.2.9:12345
5. poll({3, POLLIN}) -> 1
6. recv(3) -> <data_length>

5. Send packet from peer host to 225.168.2.9:12345
// Note that the address is not the same!
6. poll({3, POLLIN}) -> 1
7. recv(3) -> <data_length>

I checked kernel 3.12.6-2, there is no such problem. I have placed
simple C-program in attachment to reproduce the behaviour. It should be
called:
./mult_recv <mcast_address> <interface index> i.e. in example above:
./mult_recv 224.168.2.9 7
Eric Dumazet
2014-05-12 17:18:02 UTC
Permalink
Post by Yurij M. Plotnikov
On kernel 3.13.10-1 I see that socket joined to one multicast group
receives packets to another multicast address. That can be reproduced by
1. socket(DGRAM) -> 3
2. bind(3, 0.0.0.0:12345) -> 0
3. setsockopt(3, IP_MULTICAST_IF, {224.168.2.9, 7}) -> 0
// "7" is correct interface index
4. Send packet from peer host to 224.168.2.9:12345
5. poll({3, POLLIN}) -> 1
6. recv(3) -> <data_length>
5. Send packet from peer host to 225.168.2.9:12345
// Note that the address is not the same!
6. poll({3, POLLIN}) -> 1
7. recv(3) -> <data_length>
I checked kernel 3.12.6-2, there is no such problem. I have placed
simple C-program in attachment to reproduce the behaviour. It should be
./mult_recv 224.168.2.9 7
I suspect problem came with commit
421b3885bf6d56391297844f43fb7154a6396e12
("udp: ipv4: Add udp early demux")

Is that better if you try :

echo 0 >/proc/sys/net/ipv4/ip_early_demux
Yurij M. Plotnikov
2014-05-13 06:25:37 UTC
Permalink
Post by Eric Dumazet
Post by Yurij M. Plotnikov
On kernel 3.13.10-1 I see that socket joined to one multicast group
receives packets to another multicast address. That can be reproduced by
1. socket(DGRAM) -> 3
2. bind(3, 0.0.0.0:12345) -> 0
3. setsockopt(3, IP_MULTICAST_IF, {224.168.2.9, 7}) -> 0
// "7" is correct interface index
4. Send packet from peer host to 224.168.2.9:12345
5. poll({3, POLLIN}) -> 1
6. recv(3) -> <data_length>
5. Send packet from peer host to 225.168.2.9:12345
// Note that the address is not the same!
6. poll({3, POLLIN}) -> 1
7. recv(3) -> <data_length>
I checked kernel 3.12.6-2, there is no such problem. I have placed
simple C-program in attachment to reproduce the behaviour. It should be
./mult_recv 224.168.2.9 7
I suspect problem came with commit
421b3885bf6d56391297844f43fb7154a6396e12
("udp: ipv4: Add udp early demux")
echo 0 >/proc/sys/net/ipv4/ip_early_demux
Yes, this fixes the problem.
Shawn Bohrer
2014-05-13 21:36:41 UTC
Permalink
Post by Yurij M. Plotnikov
Post by Eric Dumazet
Post by Yurij M. Plotnikov
On kernel 3.13.10-1 I see that socket joined to one multicast group
receives packets to another multicast address. That can be reproduced by
1. socket(DGRAM) -> 3
2. bind(3, 0.0.0.0:12345) -> 0
3. setsockopt(3, IP_MULTICAST_IF, {224.168.2.9, 7}) -> 0
// "7" is correct interface index
4. Send packet from peer host to 224.168.2.9:12345
5. poll({3, POLLIN}) -> 1
6. recv(3) -> <data_length>
5. Send packet from peer host to 225.168.2.9:12345
// Note that the address is not the same!
6. poll({3, POLLIN}) -> 1
7. recv(3) -> <data_length>
I checked kernel 3.12.6-2, there is no such problem. I have placed
simple C-program in attachment to reproduce the behaviour. It should be
./mult_recv 224.168.2.9 7
I suspect problem came with commit
421b3885bf6d56391297844f43fb7154a6396e12
("udp: ipv4: Add udp early demux")
echo 0 >/proc/sys/net/ipv4/ip_early_demux
Yes, this fixes the problem.
Eric can you (or someone) comment on if I actually broke something
here? The example Yurij provided binds to INADDR_ANY and thus will
receive any packets destined to port 12345. So for example even with
ip_early_demux disabled simply running a second instance of the
mult_recv example on the same host results in the same behavior, e.g.

./mult_recv 225.168.2.9 7 >/dev/null 2>&1 &
./mult_recv 224.168.2.9 7

Both programs will receive packets destined to 224.168.2.9 and
225.168.2.9. Specifically this works because of the IP_ADD_MEMBERSHIP
for 225.168.2.9, which leads to my next question. When we _don't_ do
the IP_ADD_MEMBERSHIP for 225.168.2.9 why are they getting delivered
to the interface in the first place? Shouldn't igmp be preventing
this?

If I did "break" something here it appears to be because with
ip_early_demux we only call ip_check_mc_rcu() once on the initial
packet, and subsequent packets destined for that socket simply need to
pass the __udp_is_mcast_sock() test. With ip_early_demux disaled we
call ip_check_mc_rcu() for every packet.

--
Shawn
Shawn Bohrer
2014-05-14 20:40:10 UTC
Permalink
Post by Shawn Bohrer
Eric can you (or someone) comment on if I actually broke something
here? The example Yurij provided binds to INADDR_ANY and thus will
receive any packets destined to port 12345. So for example even with
ip_early_demux disabled simply running a second instance of the
mult_recv example on the same host results in the same behavior, e.g.
./mult_recv 225.168.2.9 7 >/dev/null 2>&1 &
./mult_recv 224.168.2.9 7
Both programs will receive packets destined to 224.168.2.9 and
225.168.2.9. Specifically this works because of the IP_ADD_MEMBERSHIP
for 225.168.2.9, which leads to my next question. When we _don't_ do
the IP_ADD_MEMBERSHIP for 225.168.2.9 why are they getting delivered
to the interface in the first place? Shouldn't igmp be preventing
this?
Partially answering my own question here about why the 225.168.2.9
packets arrive at the host in the first place without doing an
IP_ADD_MEMBERSHIP. Essentially the first 9 bits of the IP address do
not map to the MAC-layer multicast address. Thus only incrementing
the first octect of the IP means they both have the same MAC-layer
multicast address and are both delivered to the interface. Here is a
fine microsoft article explaining the mapping of IP multicast address
to MAC-layer Multicast.

http://technet.microsoft.com/en-us/library/cc957928.aspx

The Microsoft article mentions that the host may receive these packets
for groups to which it does not belong, but they will be dropped once
the destination IP is determined. So it seems that I likely did
indeed break things.
Post by Shawn Bohrer
If I did "break" something here it appears to be because with
ip_early_demux we only call ip_check_mc_rcu() once on the initial
packet, and subsequent packets destined for that socket simply need to
pass the __udp_is_mcast_sock() test. With ip_early_demux disaled we
call ip_check_mc_rcu() for every packet.
So is the solution to effectively call ip_check_mc_rcu() inside
udp_v4_early_demux()?

--
Shawn

Loading...