Discussion:
bind() to IPv6 link-local multicast group gives EADDRNOTAVAIL
(too old to reply)
Richard Hansen
2014-07-23 06:53:44 UTC
Permalink
Hi all,

I'm trying to troubleshoot an IPv6 multicast problem I'm experiencing on
NetBSD 6.1_STABLE. When I run the following Python (2.6 or 2.7) script:

import socket
ip = 'ff02::101%re1' # replace re1 with the name of your interface
port = 8123
a = socket.getaddrinfo(ip, port, 0, socket.SOCK_DGRAM)[0]
s = socket.socket(*a[0:3])
addr = a[4]
#addr = ('::',) + a[4][1:]
s.bind(addr)

I get the following error:

Traceback (most recent call last):
File "./minimal.py", line 8, in <module>
s.bind(addr)
File "<string>", line 1, in bind
socket.error: [Errno 49] Can't assign requested address

Note that errno 49 is EADDRNOTAVAIL. I'm guessing (but haven't
verified) that the EADDRNOTAVAIL comes from in6_pcbbind_addr() at
src/sys/netinet6/in6_pcb.c line 246, due to ifa_ifwithaddr() returning
NULL (of course that would return NULL -- no interface has the multicast
IPv6 address assigned to it).

The same script works on Linux as expected. A similar program written
in C behaves like the Python script.

If I uncomment the commented line (line 7), there is no exception.
However, if I then join the multicast group (IPV6_JOIN_GROUP sockopt)
and call recvfrom(), I get all packets received by the system that were
sent to UDP port 8123, not just packets sent to the ff02::101%re1
multicast group.

It seems to me like in6_pcbbind_addr() should have another 'else if'
condition before line 241:

} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
...

Or am I doing something wrong?

Thanks,
Richard

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Christos Zoulas
2014-07-23 11:11:54 UTC
Permalink
Post by Richard Hansen
Hi all,
It seems to me like in6_pcbbind_addr() should have another 'else if'
} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
...
Or am I doing something wrong?
Does this work for you? (similar to in_pcbbind_addr())...

christos

Index: in6_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.125
diff -u -p -u -r1.125 in6_pcb.c
--- in6_pcb.c 30 May 2014 01:39:03 -0000 1.125
+++ in6_pcb.c 23 Jul 2014 11:10:23 -0000
@@ -218,7 +218,9 @@ in6_pcbbind_addr(struct in6pcb *in6p, st
if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
return (error);

- if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /* always succeed */
+ } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
return (EINVAL);
if (sin6->sin6_addr.s6_addr32[3]) {


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Richard Hansen
2014-07-23 23:31:18 UTC
Permalink
Post by Christos Zoulas
Post by Richard Hansen
Hi all,
It seems to me like in6_pcbbind_addr() should have another 'else if'
} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
...
Or am I doing something wrong?
Does this work for you? (similar to in_pcbbind_addr())...
Thanks for the patch. I'll try it out in a bit, but before I do I have
some questions. Some of these will be answered by my testing, but I
wanted to ask them anyway in case you already know the answer and would
like to update the patch:

* With this patch, in6_pcbind_addr() will set in6p->in6p_laddr to the
multicast group address. Will this cause an outgoing packet sent
via the socket to have the source address set to the multicast
address (which would violate RFC4291)?

* Shouldn't in6_pcbind_addr() ensure that sin6->sin6_scope_id is
non-0 if the multicast address is link-local? (or perhaps
non-global?)

* How does sin6->sin6_scope_id affect the binding? Suppose I have two
interfaces, wm0 and wm1. I create two sockets and bind one to
ff02::101%wm0 port 123 and the other to ff02:101%wm1 port 123.
Each should only receive packets that arrive on the specified
interface, but with this patch I'm guessing one of the following
will happen:
- the second bind() will fail (the call to in6_pcblookup_port()
in in6_pcbbind_port() doesn't consider sin6_scope_id, so I
think NetBSD will return EADDRINUSE)
- both sockets will receive packets destined to either interface
- one socket will receive packets destined to both interfaces
while the other socket receives nothing

* Should in6_pcbind_addr() reject the bind to the multicast address
if the socket type is SOCK_STREAM?

Thanks,
Richard
Post by Christos Zoulas
christos
Index: in6_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.125
diff -u -p -u -r1.125 in6_pcb.c
--- in6_pcb.c 30 May 2014 01:39:03 -0000 1.125
+++ in6_pcb.c 23 Jul 2014 11:10:23 -0000
@@ -218,7 +218,9 @@ in6_pcbbind_addr(struct in6pcb *in6p, st
if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
return (error);
- if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /* always succeed */
+ } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
return (EINVAL);
if (sin6->sin6_addr.s6_addr32[3]) {
--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Christos Zoulas
2014-07-24 10:45:58 UTC
Permalink
On Jul 23, 7:31pm, ***@bbn.com (Richard Hansen) wrote:
-- Subject: Re: bind() to IPv6 link-local multicast group gives EADDRNOTAVAIL

| Thanks for the patch. I'll try it out in a bit, but before I do I have
| some questions. Some of these will be answered by my testing, but I
| wanted to ask them anyway in case you already know the answer and would
| like to update the patch:
|
| * With this patch, in6_pcbind_addr() will set in6p->in6p_laddr to the
| multicast group address. Will this cause an outgoing packet sent
| via the socket to have the source address set to the multicast
| address (which would violate RFC4291)?
|
| * Shouldn't in6_pcbind_addr() ensure that sin6->sin6_scope_id is
| non-0 if the multicast address is link-local? (or perhaps
| non-global?)
|
| * How does sin6->sin6_scope_id affect the binding? Suppose I have two
| interfaces, wm0 and wm1. I create two sockets and bind one to
| ff02::101%wm0 port 123 and the other to ff02:101%wm1 port 123.
| Each should only receive packets that arrive on the specified
| interface, but with this patch I'm guessing one of the following
| will happen:
| - the second bind() will fail (the call to in6_pcblookup_port()
| in in6_pcbbind_port() doesn't consider sin6_scope_id, so I
| think NetBSD will return EADDRINUSE)
| - both sockets will receive packets destined to either interface
| - one socket will receive packets destined to both interfaces
| while the other socket receives nothing
|
| * Should in6_pcbind_addr() reject the bind to the multicast address
| if the socket type is SOCK_STREAM?

Yes, these are all good points; I will think about it more and send an
updated patch.

christos

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Motoyuki OHMORI
2014-07-24 14:06:05 UTC
Permalink
Hi,

I guess that in6_pcbind_addr() should always reject binding to a multicast
address regardless of a socket type, and bind() should be used to determine
a source address.

Instead, we need to join an IP multicast address group on an interface that
would cause the interface to raise received multicast packets to a kernel.
I am, then, afraid that binding to a multicast address seems not to be useful,
and that the current source is correct.

On Linux, can your code actually receive an IP multicast packet only with
binding to the address (i.e., without joining the group)?

Best regards,
--
Post by Richard Hansen
Post by Christos Zoulas
Post by Richard Hansen
Hi all,
It seems to me like in6_pcbbind_addr() should have another 'else if'
} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
...
Or am I doing something wrong?
Does this work for you? (similar to in_pcbbind_addr())...
Thanks for the patch. I'll try it out in a bit, but before I do I have
some questions. Some of these will be answered by my testing, but I
wanted to ask them anyway in case you already know the answer and would
* With this patch, in6_pcbind_addr() will set in6p->in6p_laddr to the
multicast group address. Will this cause an outgoing packet sent
via the socket to have the source address set to the multicast
address (which would violate RFC4291)?
* Shouldn't in6_pcbind_addr() ensure that sin6->sin6_scope_id is
non-0 if the multicast address is link-local? (or perhaps
non-global?)
* How does sin6->sin6_scope_id affect the binding? Suppose I have two
interfaces, wm0 and wm1. I create two sockets and bind one to
ff02::101%wm0 port 123 and the other to ff02:101%wm1 port 123.
Each should only receive packets that arrive on the specified
interface, but with this patch I'm guessing one of the following
- the second bind() will fail (the call to in6_pcblookup_port()
in in6_pcbbind_port() doesn't consider sin6_scope_id, so I
think NetBSD will return EADDRINUSE)
- both sockets will receive packets destined to either interface
- one socket will receive packets destined to both interfaces
while the other socket receives nothing
* Should in6_pcbind_addr() reject the bind to the multicast address
if the socket type is SOCK_STREAM?
Thanks,
Richard
Post by Christos Zoulas
christos
Index: in6_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.125
diff -u -p -u -r1.125 in6_pcb.c
--- in6_pcb.c 30 May 2014 01:39:03 -0000 1.125
+++ in6_pcb.c 23 Jul 2014 11:10:23 -0000
@@ -218,7 +218,9 @@ in6_pcbbind_addr(struct in6pcb *in6p, st
if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
return (error);
- if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /* always succeed */
+ } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
return (EINVAL);
if (sin6->sin6_addr.s6_addr32[3]) {
--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Richard Hansen
2014-07-24 19:44:39 UTC
Permalink
Post by Motoyuki OHMORI
Hi,
I guess that in6_pcbind_addr() should always reject binding to a multicast
address regardless of a socket type, and bind() should be used to determine
a source address.
Refusing to bind() to a multicast address is unusual, and goes against
RFC 3493 and Stevens' UNIX Network Programming vol. 1.

Note that an application might want to perform the following tasks:

1. join a group
2. specify the source IP address for an outgoing UDP packet (or let
the kernel choose)
3. specify the destination IP address that must be in an incoming
UDP packet for it to be received by the socket (or accept any
destination IP address)

Task #1 is accomplished via the straightforward IPV6_JOIN_GROUP sockopt.

Accomplishing tasks #2 and #3 is messy, particularly for multicast. For
unicast, bind() is used to both set the source IP address and limit the
destination IP address, though it can't do one without also doing the
other. For multicast, other operating systems use bind() to limit the
destination IP address to a particular multicast group, but it can't
also set the source IP address. (RFC 4291 says that the source address
can't be a multicast address.) Other mechanisms must be used to force a
particular source address, e.g., create and use a second socket for
transmission or use the advanced APIs described in RFCs 3542 and 5014.
Post by Motoyuki OHMORI
Instead, we need to join an IP multicast address group on an interface that
would cause the interface to raise received multicast packets to a kernel.
I am, then, afraid that binding to a multicast address seems not to be useful,
and that the current source is correct.
Binding to a multicast address prevents an application from being
confused by packets sent to other destinations.

For example, suppose application #1 joins ff02::1234%wm0 and binds to IP
address ff02::1234%wm0 and UDP port 49152. Application #2 joins
ff0e::5678 and binds to UDP port 49152 but does NOT bind to ff0e::5678.
In this scenario, application #2 will receive packets destined to
application #1's group even though application #2 never asked to join
ff02::1234%wm0. This could break application #2, especially if the two
applications use different protocols.
Post by Motoyuki OHMORI
On Linux, can your code actually receive an IP multicast packet only with
binding to the address (i.e., without joining the group)?
Maybe, but only if some other process on the system has already joined
the group.

(Note that I omitted the group join sockopt from my example code to make
it as minimal as possible. I can send a more complete Python script if
there is interest.)

-Richard
Post by Motoyuki OHMORI
Best regards,
--
Post by Richard Hansen
Post by Christos Zoulas
Post by Richard Hansen
Hi all,
It seems to me like in6_pcbbind_addr() should have another 'else if'
} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
...
Or am I doing something wrong?
Does this work for you? (similar to in_pcbbind_addr())...
Thanks for the patch. I'll try it out in a bit, but before I do I have
some questions. Some of these will be answered by my testing, but I
wanted to ask them anyway in case you already know the answer and would
* With this patch, in6_pcbind_addr() will set in6p->in6p_laddr to the
multicast group address. Will this cause an outgoing packet sent
via the socket to have the source address set to the multicast
address (which would violate RFC4291)?
* Shouldn't in6_pcbind_addr() ensure that sin6->sin6_scope_id is
non-0 if the multicast address is link-local? (or perhaps
non-global?)
* How does sin6->sin6_scope_id affect the binding? Suppose I have two
interfaces, wm0 and wm1. I create two sockets and bind one to
ff02::101%wm0 port 123 and the other to ff02:101%wm1 port 123.
Each should only receive packets that arrive on the specified
interface, but with this patch I'm guessing one of the following
- the second bind() will fail (the call to in6_pcblookup_port()
in in6_pcbbind_port() doesn't consider sin6_scope_id, so I
think NetBSD will return EADDRINUSE)
- both sockets will receive packets destined to either interface
- one socket will receive packets destined to both interfaces
while the other socket receives nothing
* Should in6_pcbind_addr() reject the bind to the multicast address
if the socket type is SOCK_STREAM?
Thanks,
Richard
Post by Christos Zoulas
christos
Index: in6_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.125
diff -u -p -u -r1.125 in6_pcb.c
--- in6_pcb.c 30 May 2014 01:39:03 -0000 1.125
+++ in6_pcb.c 23 Jul 2014 11:10:23 -0000
@@ -218,7 +218,9 @@ in6_pcbbind_addr(struct in6pcb *in6p, st
if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
return (error);
- if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /* always succeed */
+ } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
return (EINVAL);
if (sin6->sin6_addr.s6_addr32[3]) {
--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Loading...