Bug #8159

Build failure introduced by Rinda changes

Added by Luis Lavena about 1 year ago. Updated 9 months ago.

[ruby-core:53692]
Status:Closed
Priority:Normal
Assignee:Usaku NAKAMURA
Category:test
Target version:2.1.0
ruby -v:ruby 2.1.0dev (2013-03-24 trunk 39905) [x64-mingw32] Backport:

Description

=begin

Seems latest Rinda-related changes affected build under Windows:

http://ci.rubyinstaller.org/job/ruby-trunk-x64-test-all/936/console

2) Error:
testtakebug8215(Rinda::TupleSpaceProxyTest):
NotImplementedError: fork() function is unimplemented on this machine
C:/Users/Worker/Jenkins/workspace/ruby-trunk-x64-build/test/rinda/test
rinda.rb:486:in fork'
C:/Users/Worker/Jenkins/workspace/ruby-trunk-x64-build/test/rinda/test_rinda.rb:486:in
testtakebug_8215'

3) Error:
testmakesocketipv4multicast(Rinda::TestRingServer):
Errno::EADDRNOTAVAIL: The requested address is not valid in its context. - bind(2)
C:/Users/Worker/Jenkins/workspace/ruby-trunk-x64-build/lib/rinda/ring.rb:117:in bind'
C:/Users/Worker/Jenkins/workspace/ruby-trunk-x64-build/lib/rinda/ring.rb:117:in
makesocket'
C:/Users/Worker/Jenkins/workspace/ruby-trunk-x64-build/test/rinda/test
rinda.rb:542:in `testmakesocketipv4multicast'

r39895 seems to have introduced a test that is not skipping on non-fork() platforms.

=end

0001-Fix-multicast-of-rinda.patch Magnifier (2.37 KB) Hiroshi Shirosaki, 04/06/2013 06:15 PM

0002-Fix-multicast-of-rinda.patch Magnifier (6.13 KB) Hiroshi Shirosaki, 04/09/2013 06:26 PM


Related issues

Related to ruby-trunk - Feature #8073: Add multicast support to Rinda::Ring* Closed 03/11/2013
Blocks ruby-trunk - misc #8288: Ruby 2.1.0 release engeneering Closed 04/18/2013 12/25/2013

Associated revisions

Revision 39922
Added by Eric Hodel about 1 year ago

  • test/rinda/test_rinda.rb: Skip IPv6 tests if no IPv6 addresses exist. Skip fork-dependent test if fork is not available. [ruby-trunk - Bug #8159]

Revision 39930
Added by Yui NARUSE about 1 year ago

Use more general approach to get scope_id see #8159

Revision 40472
Added by shirosaki 12 months ago

ring.rb: specify multicast interface

  • lib/rinda/ring.rb (Rinda::RingServer#initialize): accept array
    arguments of address to specify multicast interface.

  • lib/rinda/ring.rb (Rinda::RingServer#make_socket): add optional
    arguments for multicast interface.

  • test/rinda/testrinda.rb
    (TestRingFinger#test
    ringserveripv4multicast,
    TestRingFinger#test
    ringserveripv6_multicast): add tests for
    above change.

  • test/rinda/testrinda.rb
    (TestRingServer#test
    makesocketipv4multicast,
    TestRingServer#test
    makesocketipv6_multicast): change bound
    interface address because multicast address is not allowed on Linux
    or Windows.
    [Bug #8159]

History

#1 Updated by Luis Lavena about 1 year ago

  • Status changed from Open to Assigned

#2 Updated by Eric Hodel about 1 year ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r39922.
Luis, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • test/rinda/test_rinda.rb: Skip IPv6 tests if no IPv6 addresses exist. Skip fork-dependent test if fork is not available. [ruby-trunk - Bug #8159]

#3 Updated by Eric Hodel about 1 year ago

  • Status changed from Closed to Assigned
  • % Done changed from 100 to 50

I have skipped the test using fork and the IPv6 tests when IPv6 addresses are missing.

I'm unsure what to do about the IPv4 multicast test, does something special need to be done for multicast on windows?

#4 Updated by Yui NARUSE about 1 year ago

I create an experimental patch as following for failures on Linux:
http://u64.rubyci.org/~chkbuild/ruby-trunk/log/20130324T210202Z.log.html.gz

diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 1ddaea9..44703c1 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -1585,7 +1585,9 @@ socketsipaddresslist(VALUE self)
for (p = ifp; p; p = p->ifanext) {
if (p->ifa
addr != NULL && ISIPFAMILY(p->ifaaddr->safamily)) {
struct sockaddr *addr = p->ifaaddr;
- rb
arypush(list, sockaddrobj(addr, sockaddrlen(addr)));
+ ai = sockaddr
obj(addr, sockaddrlen(addr));
+ rb
ivarset(ai, rbintern("@ifaname"), rbstrnewcstr(p->ifaname));
+ rb
ary_push(list, ai);
}
}

@@ -1856,6 +1858,12 @@ socketsipaddresslist(VALUE self)
#define socketsipaddresslist rbfnotimplement
#endif

+VALUE
+socksifnametoindex(VALUE klass, VALUE name)
+{
+ return UINT2NUM(if
nametoindex(StringValueCStr(name)));
+}
+
void
Initsocket()
{
@@ -1999,6 +2007,7 @@ Init
socket()
rbdefinesingletonmethod(rbcSocket, "gethostbyaddr", socksgethostbyaddr, -1);
rbdefinesingletonmethod(rbcSocket, "getservbyname", socksgetservbyname, -1);
rbdefinesingletonmethod(rbcSocket, "getservbyport", socksgetservbyport, -1);
+ rbdefinesingletonmethod(rbcSocket, "ifnametoindex", socksifnametoindex, 1);
rbdefinesingletonmethod(rbcSocket, "getaddrinfo", socksgetaddrinfo, -1);
rbdefinesingletonmethod(rbcSocket, "getnameinfo", socksgetnameinfo, -1);
rbdefinesingletonmethod(rbcSocket, "sockaddrin", sockspacksockaddr_in, 2);
diff --git a/lib/rinda/ring.rb b/lib/rinda/ring.rb
index 9b3e273..b87800c 100644
--- a/lib/rinda/ring.rb
+++ b/lib/rinda/ring.rb
@@ -59,6 +59,8 @@ module Rinda
end
end

  • attraccessor :multicastinterface
    +
    ##
    # Advertises +ts+ on the given +addresses+ at +port+.
    #
    @@ -84,6 +86,7 @@ module Rinda

    @wservices = writeservices
    @rservice = replyservice

  •  @multicast_interface = 0
    

    end

    ##
    @@ -111,6 +114,9 @@ module Rinda
    mreq = IPAddr.new(addrinfo.ip_address).hton + [0].pack('I')

       socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
    
  •      sa = addrinfo.to_sockaddr
    
  •      sa[-4, 4] = [@multicast_interface].pack('I')
    
  •      addrinfo = Addrinfo.new(sa)
     end
    

    end

diff --git a/test/rinda/testrinda.rb b/test/rinda/testrinda.rb
index 65df228..9aaeb66 100644
--- a/test/rinda/testrinda.rb
+++ b/test/rinda/test
rinda.rb
@@ -525,7 +525,24 @@ class TupleSpaceProxyTest < Test::Unit::TestCase
@server = DRb.primaryserver || DRb.startservice
end

+module RingIPv6
+ def prepareipv6(r)
+ Socket.ip
addresslist.any? do |addrinfo|
+ if addrinfo.ipv6? && ipv6
globalunicast?(addrinfo)
+ r.multicast
interface = Socket.ifnametoindex(addrinfo.instancevariableget(:@ifaname))
+ return
+ end
+ end
+ skip 'IPv6 not available'
+ end
+
+ def ipv6globalunicast?(ai)
+ (ai.ipaddress[0].toi & 0b1110) == 0b0010
+ end
+end
+
class TestRingServer < Test::Unit::TestCase
+ include RingIPv6

def setup
@port = Rinda::Ring_PORT
@@ -560,14 +577,8 @@ class TestRingServer < Test::Unit::TestCase
end

def testmakesocketipv6multicast
- skip 'IPv6 not available' unless

- Socket.ipaddresslist.any? { |addrinfo| addrinfo.ipv6? }

  • begin
  • v6mc = @rs.make_socket('ff02::1')
  • rescue Errno::EADDRNOTAVAIL
  • return # IPv6 address for multicast not available
  • end
  • prepare_ipv6(@rs)
  • v6mc = @rs.make_socket('ff02::1')

    if Socket.constdefined?(:SOREUSEPORT) then
    assert v6mc.getsockopt(:SOCKET, :SOREUSEPORT).bool
    @@ -575,7 +586,7 @@ class TestRingServer < Test::Unit::TestCase
    assert v6mc.getsockopt(:SOCKET, :SO
    REUSEADDR).bool
    end

  • assertequal('ff02::1', v6mc.localaddress.ip_address)

  • assertmatch(/\Aff02::1(?:%|\z)/, v6mc.localaddress.ipaddress)
    assert
    equal(@port, v6mc.localaddress.ipport)
    end

@@ -588,22 +599,10 @@ class TestRingServer < Test::Unit::TestCase
end

class TestRingFinger < Test::Unit::TestCase
+ include RingIPv6

def setup
@rf = Rinda::RingFinger.new
- ifindex = nil
- 10.times do |i|
- begin
- addrinfo = Addrinfo.udp('ff02::1', Rinda::RingPORT)
- soc = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
- soc.setsockopt(:IPPROTO
IPV6, :IPV6MULTICASTIF,
- [i].pack('I'))
- ifindex = i
- break
- rescue
- end
- end
- @rf.multicast_interface = ifindex
end

def testmakesocket_unicast
@@ -620,26 +619,23 @@ class TestRingFinger < Test::Unit::TestCase
end

def testmakesocketipv6multicast
- skip 'IPv6 not available' unless

- Socket.ipaddresslist.any? { |addrinfo| addrinfo.ipv6? }

  • prepareipv6(@rf)
    v6mc = @rf.make
    socket('ff02::1')

    assertequal(1, v6mc.getsockopt(:IPPROTOIPV6, :IPV6MULTICASTLOOP).int)
    assertequal(1, v6mc.getsockopt(:IPPROTOIPV6, :IPV6MULTICASTHOPS).int)
    end

  • def testmakesocketmulticasthops

  • def testmakesocketipv4multicast_hops

    @rf.multicast_hops = 2

    v4mc = @rf.make_socket('239.0.0.1')

    assertequal(2, v4mc.getsockopt(:IPPROTOIP, :IPMULTICASTTTL).int)

  • end

- return unless Socket.ipaddresslist.any? { |addrinfo| addrinfo.ipv6? }

  • def testmakesocketipv6multicast_hops
  • prepare_ipv6(@rf)
  • @rf.multicasthops = 2 v6mc = @rf.makesocket('ff02::1') - assertequal(2, v6mc.getsockopt(:IPPROTOIPV6, :IPV6MULTICASTHOPS).int) end

#5 Updated by Akira Tanaka about 1 year ago

2013/3/25 naruse (Yui NARUSE) naruse@airemix.jp:

Issue #8159 has been updated by naruse (Yui NARUSE).

I create an experimental patch as following for failures on Linux:
http://u64.rubyci.org/~chkbuild/ruby-trunk/log/20130324T210202Z.log.html.gz

diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 1ddaea9..44703c1 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -1585,7 +1585,9 @@ socketsipaddresslist(VALUE self)
for (p = ifp; p; p = p->ifanext) {
if (p->ifa
addr != NULL && ISIPFAMILY(p->ifaaddr->safamily)) {
struct sockaddr *addr = p->ifaaddr;
- rb
arypush(list, sockaddrobj(addr, sockaddrlen(addr)));
+ ai = sockaddr
obj(addr, sockaddrlen(addr));
+ rb
ivarset(ai, rbintern("@ifaname"), rbstrnewcstr(p->ifaname));
+ rb
ary_push(list, ai);
}
}

It seems we need Socket.getifaddrs.
--
Tanaka Akira

#6 Updated by Yui NARUSE about 1 year ago

akr (Akira Tanaka) wrote:

2013/3/25 naruse (Yui NARUSE) naruse@airemix.jp:

Issue #8159 has been updated by naruse (Yui NARUSE).

I create an experimental patch as following for failures on Linux:
http://u64.rubyci.org/~chkbuild/ruby-trunk/log/20130324T210202Z.log.html.gz

diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 1ddaea9..44703c1 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -1585,7 +1585,9 @@ socketsipaddresslist(VALUE self)
for (p = ifp; p; p = p->ifanext) {
if (p->ifa
addr != NULL && ISIPFAMILY(p->ifaaddr->safamily)) {
struct sockaddr *addr = p->ifaaddr;
- rb
arypush(list, sockaddrobj(addr, sockaddrlen(addr)));
+ ai = sockaddr
obj(addr, sockaddrlen(addr));
+ rb
ivarset(ai, rbintern("@ifaname"), rbstrnewcstr(p->ifaname));
+ rb
ary_push(list, ai);
}
}

It seems we need Socket.getifaddrs.

Sure, but what is the type of its return value?
Extended Addrinfo or a new object like Ifaddr?

#7 Updated by Akira Tanaka about 1 year ago

2013/3/25 naruse (Yui NARUSE) naruse@airemix.jp:

It seems we need Socket.getifaddrs.

Sure, but what is the type of its return value?
Extended Addrinfo or a new object like Ifaddr?

I think it should have new class.

Addrinfo has bunch of methods for a struct sockaddr.
But struct ifaddrs has multiple pointers to struct sockaddr.
It is difficult to deal with them fairly.
--
Tanaka Akira

#8 Updated by Eric Hodel about 1 year ago

I want to update #8075 to use getifaddrs so it will contain at least the interface name, IP address and netmask for the interface. With this information we can fix this bug and have RFC-compliant one-shot mDNS support in Resolv (as you only want to accept packets from the local network, see #8089).

I propose the class Socket::Interface for this data along with Socket.interface_list.

I have not had the time to update my patch in #8075, though, as retreiving the netmask seems difficult on windows.

#9 Updated by Yui NARUSE about 1 year ago

After some inspection, I came to doubt RingServer#make_socket is buggy.

Actually at least on FreeBSD and darwin testmakesocketipv4multicast and testmakesocketipv6multicast don't fail,
but on Linux and Windows.
It seems because the given address for bind is ff02::1.
I think it must be the client's ip address.
I don't know the detail of multicast and cannot fix it correctly.
So could you confirm them?

#10 Updated by Akira Tanaka about 1 year ago

2013/3/26 drbrain (Eric Hodel) drbrain@segment7.net:

Issue #8159 has been updated by drbrain (Eric Hodel).

I want to update #8075 to use getifaddrs so it will contain at least the interface name, IP address and netmask for the interface. With this information we can fix this bug and have RFC-compliant one-shot mDNS support in Resolv (as you only want to accept packets from the local network, see #8089).

I propose the class Socket::Interface for this data along with Socket.interface_list.

I'm not sure it is good idea.

The result of getifaddrs() has no one-to-one mapping to the result of
if_nameindex().

Also, ifnameindex() is standardized by POSIX (and RFC 3493) but getifaddrs()
is not standardized.
So it is good to separate the features to be if
nameindex() is usable even on
a platform which doesn't support getifaddrs().
--
Tanaka Akira

#11 Updated by Akira Tanaka about 1 year ago

2013/3/26 Tanaka Akira akr@fsij.org:

Sure, but what is the type of its return value?
Extended Addrinfo or a new object like Ifaddr?

I think it should have new class.

Addrinfo has bunch of methods for a struct sockaddr.
But struct ifaddrs has multiple pointers to struct sockaddr.
It is difficult to deal with them fairly.

One more point for new class:

Addrinfo is possible to marshal now but
struct ifaddrs is impossible to marshal
because we don't know about internal of ifa_data.
--
Tanaka Akira

#12 Updated by Akira Tanaka about 1 year ago

2013/3/25 Tanaka Akira akr@fsij.org

It seems we need Socket.getifaddrs.

I implemented it in
https://github.com/akr/ruby/tree/getifaddrs
--
Tanaka Akira

#13 Updated by Yui NARUSE about 1 year ago

On Linux, IPv6 UDP multicast with link-local IP address (FF02::1 is link-local) requires scope_id specified.
http://d.hatena.ne.jp/torutk/20080520/p1
http://stackoverflow.com/questions/3851061/listening-for-ipv6-multicasts-on-linux
http://stackoverflow.com/questions/3068231/ipv6-link-local-multicasting

So at first you must decide the meaning of Ringserver.new "ff02::1" on Linux.
If it means only one interface, the interface must be specified.
If it means all interfaces, make_socket must get interface by itself, and make related sockets for all interfaces.

#14 Updated by Eric Hodel about 1 year ago

It is odd, considering IPV6MULTICASTIF, but I will make these updates.

Thank you for researching this for me while I have been busy.

#15 Updated by Hiroshi Shirosaki about 1 year ago

I created a patch to fix test errors.
It seems a socket should be binded to another non multicast address and join a multicast group.

I got it from the following examples.
http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6xmulticast.htm

#16 Updated by Yui NARUSE about 1 year ago

h.shirosaki (Hiroshi Shirosaki) wrote:

I created a patch to fix test errors.
It seems a socket should be binded to another non multicast address and join a multicast group.

I got it from the following examples.
http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6xmulticast.htm

Yeah, I think it is correct.
But your patch doesn't specify what multicast group the socket join.
Current make_socket's argument is only one and it specifies both binding address and joining multicast group,
so I think the API must be modified.

#17 Updated by Hiroshi Shirosaki about 1 year ago

I've updated a patch which includes API change. Users can specify network interface by optional arguments. But I'm not sure about API spec.
If #8075 is applied, we will be able to specify an interface for IPv6 by name.

#18 Updated by Yui NARUSE about 1 year ago

Could you temporally commit the patch because it causes test failure?

#19 Updated by Anonymous 12 months ago

  • Status changed from Assigned to Closed
  • % Done changed from 50 to 100

This issue was solved with changeset r40472.
Luis, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


ring.rb: specify multicast interface

  • lib/rinda/ring.rb (Rinda::RingServer#initialize): accept array
    arguments of address to specify multicast interface.

  • lib/rinda/ring.rb (Rinda::RingServer#make_socket): add optional
    arguments for multicast interface.

  • test/rinda/testrinda.rb
    (TestRingFinger#test
    ringserveripv4multicast,
    TestRingFinger#test
    ringserveripv6_multicast): add tests for
    above change.

  • test/rinda/testrinda.rb
    (TestRingServer#test
    makesocketipv4multicast,
    TestRingServer#test
    makesocketipv6_multicast): change bound
    interface address because multicast address is not allowed on Linux
    or Windows.
    [Bug #8159]

#20 Updated by Hiroshi Shirosaki 12 months ago

Committed at r40472 to fix test failure. If there are problems, please fix it.

#21 Updated by Yui NARUSE 12 months ago

  • Status changed from Closed to Assigned
  • Target version changed from next minor to 2.1.0

#22 Updated by Luis Lavena 11 months ago

  • Assignee changed from Eric Hodel to Usaku NAKAMURA
  • % Done changed from 100 to 80

Rinda failure now seems to be related to ifindex() being missing:

http://ci.rubyinstaller.org/job/ruby-trunk-x86-test-all/lastFailedBuild/console

#23 Updated by Luis Lavena 9 months ago

  • Status changed from Assigned to Closed

issue shown here no longer present in CI.

Thank you.

Also available in: Atom PDF