2006-09-13 14:13:59

by Jeff Layton

[permalink] [raw]
Subject: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

The situation is this:

Two programs have opened IPv4 UDP sockets, set SO_REUSEADDR on them, and
are bound to INADDR_ANY on the same port. One program joins a multicast
group address, the other program joins a different one. When a multicast
packet is sent to this port on one of the group addresses to which these
sockets are bound, both sockets get delivered a copy of the packet. Only
the socket that is bound to the group address to which the packet was
sent should get it.

The issue seems to be that there is no actual check to see if the socket
is joined to the multicast group. This patch adds such a check, and
corrected the problem on my test rig.

I looked briefly at the ipv6 equivalent code, and it appears to already
handle this correctly with a check near the top of inet6_mc_check(). I
have not actually verified this, however.

This patch should apply cleanly to Linus' git tree as of last night.

Signed-off-by: Jeff Layton <[email protected]>

--- linux-2.6/net/ipv4/udp.c.mcast-filter
+++ linux-2.6/net/ipv4/udp.c
@@ -286,6 +286,8 @@ static inline struct sock *udp_v4_mcast_
struct hlist_node *node;
struct sock *s = sk;
unsigned short hnum = ntohs(loc_port);
+ struct ip_mc_socklist *mc_list;
+ int matched;

sk_for_each_from(s, node) {
struct inet_sock *inet = inet_sk(s);
@@ -299,6 +301,23 @@ static inline struct sock *udp_v4_mcast_
continue;
if (!ip_mc_sf_allow(s, loc_addr, rmt_addr, dif))
continue;
+
+ /* only deliver multicast packets to sockets that are
+ * explicitly joined to the multicast group */
+ if (MULTICAST(loc_addr)) {
+ matched = 0;
+ for (mc_list=inet->mc_list ; mc_list;
+ mc_list=mc_list->next) {
+ if (mc_list->multi.imr_multiaddr.s_addr ==
+ loc_addr) {
+ matched = 1;
+ break;
+ }
+ }
+ if (!matched)
+ continue;
+ }
+
goto found;
}
s = NULL;



2006-09-13 14:32:32

by David Stevens

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

[email protected] wrote on 09/13/2006 07:13:55 AM:

> Only
> the socket that is bound to the group address to which the packet was
> sent should get it.

This is not true on any OS I'm aware of, including the
original sockets multicast implementation on early BSD.

Multicast group membership is per-interface, not per-socket.
Joining a group on any socket on the machine allows packets for that
group to be delivered on the interface where it was joined.

Delivery of packets to a socket is determined by the binding, and
INADDR_ANY means "any".

IPv6 behaves the same way.
+-DLS


2006-09-13 16:12:33

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

On Wed, 2006-09-13 at 07:32 -0700, David Stevens wrote:
> [email protected] wrote on 09/13/2006 07:13:55 AM:
>
> This is not true on any OS I'm aware of, including the
> original sockets multicast implementation on early BSD.
>

The current Linux behavior does seem to be consistent with the way
recent BSD and OSX seem to work. Solaris 8 seems to behave consistent
with the patch I posted.

Most of the RFC's I've looked at don't seem to have much to say about
how multicasting works at the socket level. Is there an RFC or
specification that spells this out, or is this one of those situations
where things are somewhat open to interpretation?

Thanks,
Jeff


2006-09-13 16:46:42

by David Stevens

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

Jeff Layton <[email protected]> wrote on 09/13/2006 09:12:23 AM:

> Most of the RFC's I've looked at don't seem to have much to say about
> how multicasting works at the socket level. Is there an RFC or
> specification that spells this out, or is this one of those situations
> where things are somewhat open to interpretation?

RFC's are standards for protocols and are concerned with
interoperability among multiple implementations. They, in general,
do not define API's (and those that do are "informational").
There are after-the-fact standards for sockets, including
POSIX, but older features are effectively defined by industry
practice; in this case, Deering's BSD implementation from nearly
20 years ago.
Sections 7.1 & 7.2 of RFC 988 [Deering, 1986] hint at this
interpretation (separation of membership from socket delivery),
esp. when it says "Incoming multicast IP datagrams are
delivered to upper-layer protocol modules using the
same 'Receive IP' operation as normal, unicast
datagrams."
But Deering's own implementation, which
does not distinguish which socket joined the group,
is the source of the standard practice.

+-DLS

2006-09-13 20:21:19

by Alexey Kuznetsov

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

Hello!

> IPv6 behaves the same way.

Actually, Linux IPv6 filters received multicasts, inet6_mc_check() does
this.

IPv4 does not. I remember that attempts to do this were made in the past
and failed, because some applications, related to multicast routing,
did expect to receive all the multicasts even though they did not join
any multicast addresses. So, it was left intact.

Alexey

2006-09-13 20:42:10

by David Stevens

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

Alexey Kuznetsov <[email protected]> wrote on 09/13/2006 01:20:29 PM:

> Hello!
>
> > IPv6 behaves the same way.
>
> Actually, Linux IPv6 filters received multicasts, inet6_mc_check() does
> this.

No, it returns 1 (allow) if there are no filters to explicitly
filter it. I wrote that code. :-)
+-DLS

2006-09-14 01:08:58

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

From: Jeff Layton <[email protected]>
Date: Wed, 13 Sep 2006 12:12:23 -0400

> Most of the RFC's I've looked at don't seem to have much to say about
> how multicasting works at the socket level. Is there an RFC or
> specification that spells this out, or is this one of those situations
> where things are somewhat open to interpretation?

In these cases, whatever BSD does is effectively the definition of
what should happen since it is the "BSD socket API".

2006-09-14 10:31:13

by Alexey Kuznetsov

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

Hello!

> No, it returns 1 (allow) if there are no filters to explicitly
> filter it. I wrote that code. :-)

I see. It did not behave this way old times.

>From your mails I understood that current behaviour matches another
implementations (BSD whatever), is it true?

Alexey

2006-09-14 15:39:49

by David Stevens

[permalink] [raw]
Subject: Re: [PATCH] make ipv4 multicast packets only get delivered to sockets that are joined to group

Alexey Kuznetsov <[email protected]> wrote on 09/14/2006 03:30:37 AM:

> Hello!
>
> > No, it returns 1 (allow) if there are no filters to explicitly
> > filter it. I wrote that code. :-)
>
> I see. It did not behave this way old times.
>
> From your mails I understood that current behaviour matches another
> implementations (BSD whatever), is it true?

Hi, Alexey,

If you mean IPv6 code in current BSD derivatives, I don't know.

The IPv6 behaviour was different from IPv4 on Linux and was changed for
compatibility with IPv4 (discussion on netdev agreed that binding
should determine socket delivery, not group membership, or simply
that there was no reason to be different from long-standing IPv4
practice).

The IPv4 code is that way for compatibility with everything else since
about ~4.3BSD (with the possible exception of Solaris 8, apparently).

FWIW, I think Deering's original interpretation is correct. Adding
a multicast address to an interface by joining a group is little
different from adding a unicast address via SIOCSIFADDR, which
certainly does affect packets delivered to the machine and to any
INADDR_ANY-bound socket. Binding to the multicast address and not
INADDR_ANY will give you only packets for that group, if that's
what you want, just as in the unicast address-specific bind.

+-DLS