summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--lib/rinda/ring.rb58
-rw-r--r--test/rinda/test_rinda.rb42
3 files changed, 111 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 2275785525..60a6558996 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+Fri Apr 26 00:07:52 2013 Hiroshi Shirosaki <h.shirosaki@gmail.com>
+
+ * 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/test_rinda.rb
+ (TestRingFinger#test_ring_server_ipv4_multicast,
+ TestRingFinger#test_ring_server_ipv6_multicast): add tests for
+ above change.
+
+ * test/rinda/test_rinda.rb
+ (TestRingServer#test_make_socket_ipv4_multicast,
+ TestRingServer#test_make_socket_ipv6_multicast): change bound
+ interface address because multicast address is not allowed on Linux
+ or Windows.
+ [ruby-core:53692] [Bug #8159]
+
Thu Apr 25 23:45:02 2013 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* lib/rinda/ring.rb (Rinda::RingServer#initialize): add a socket
diff --git a/lib/rinda/ring.rb b/lib/rinda/ring.rb
index f6566c906a..29ea5f0ff6 100644
--- a/lib/rinda/ring.rb
+++ b/lib/rinda/ring.rb
@@ -67,6 +67,29 @@ module Rinda
# +addresses+ can contain multiple addresses. If a multicast address is
# given in +addresses+ then the RingServer will listen for multicast
# queries.
+ #
+ # If you use IPv4 multicast you may need to set an address of the inbound
+ # interface which joins a multicast group.
+ #
+ # ts = Rinda::TupleSpace.new
+ # rs = Rinda::RingServer.new(ts, [['239.0.0.1', '9.5.1.1']])
+ #
+ # You can set addresses as an Array Object. The first element of the
+ # Array is a multicast address and the second is an inbound interface
+ # address. If the second is omitted then '0.0.0.0' is used.
+ #
+ # If you use IPv6 multicast you may need to set both the local interface
+ # address and the inbound interface index:
+ #
+ # rs = Rinda::RingServer.new(ts, [['ff02::1', '::1', 1]])
+ #
+ # The first element is a multicast address and the second is an inbound
+ # interface address. The third is an inbound interface index.
+ #
+ # At this time there is no easy way to get an interface index by name.
+ #
+ # If the second is omitted then '::1' is used.
+ # If the third is omitted then 0 (default interface) is used.
def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
@port = port
@@ -80,7 +103,11 @@ module Rinda
@ts = ts
@sockets = []
addresses.each do |address|
- make_socket(address)
+ if Array === address
+ make_socket(*address)
+ else
+ make_socket(address)
+ end
end
@w_services = write_services
@@ -89,8 +116,20 @@ module Rinda
##
# Creates a socket at +address+
+ #
+ # If +address+ is multicast address then +interface_address+ and
+ # +multicast_interface+ can be set as optional.
+ #
+ # A created socket is bound to +interface_address+. If you use IPv4
+ # multicast then the interface of +interface_address+ is used as the
+ # inbound interface. If +interface_address+ is omitted or nil then
+ # '0.0.0.0' or '::1' is used.
+ #
+ # If you use IPv6 multicast then +multicast_interface+ is used as the
+ # inbound interface. +multicast_interface+ is a network interface index.
+ # If +multicast_interface+ is omitted then 0 (default interface) is used.
- def make_socket(address)
+ def make_socket(address, interface_address=nil, multicast_interface=0)
addrinfo = Addrinfo.udp(address, @port)
socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
@@ -105,19 +144,26 @@ module Rinda
end
if addrinfo.ipv4_multicast? then
+ interface_address = '0.0.0.0' if interface_address.nil?
+ socket.bind(Addrinfo.udp(interface_address, @port))
+
mreq = IPAddr.new(addrinfo.ip_address).hton +
- IPAddr.new('0.0.0.0').hton
+ IPAddr.new(interface_address).hton
socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
else
- mreq = IPAddr.new(addrinfo.ip_address).hton + [0].pack('I')
+ interface_address = '::1' if interface_address.nil?
+ socket.bind(Addrinfo.udp(interface_address, @port))
+
+ mreq = IPAddr.new(addrinfo.ip_address).hton +
+ [multicast_interface].pack('I')
socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
end
+ else
+ socket.bind(addrinfo)
end
- socket.bind(addrinfo)
-
socket
end
diff --git a/test/rinda/test_rinda.rb b/test/rinda/test_rinda.rb
index 577eb1a7e5..263dbfb50d 100644
--- a/test/rinda/test_rinda.rb
+++ b/test/rinda/test_rinda.rb
@@ -575,8 +575,8 @@ class TestRingServer < Test::Unit::TestCase
assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
end
- assert_equal('239.0.0.1', v4mc.local_address.ip_address)
- assert_equal(@port, v4mc.local_address.ip_port)
+ assert_equal('0.0.0.0', v4mc.local_address.ip_address)
+ assert_equal(@port, v4mc.local_address.ip_port)
end
def test_make_socket_ipv6_multicast
@@ -595,7 +595,43 @@ class TestRingServer < Test::Unit::TestCase
assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
end
- assert_equal('ff02::1', v6mc.local_address.ip_address)
+ assert_equal('::1', v6mc.local_address.ip_address)
+ assert_equal(@port, v6mc.local_address.ip_port)
+ end
+
+ def test_ring_server_ipv4_multicast
+ @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
+ v4mc = @rs.instance_variable_get('@sockets').first
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
+ else
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
+ end
+
+ assert_equal('0.0.0.0', v4mc.local_address.ip_address)
+ assert_equal(@port, v4mc.local_address.ip_port)
+ end
+
+ def test_ring_server_ipv6_multicast
+ skip 'IPv6 not available' unless
+ Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
+
+ begin
+ @rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
+ rescue Errno::EADDRNOTAVAIL
+ return # IPv6 address for multicast not available
+ end
+
+ v6mc = @rs.instance_variable_get('@sockets').first
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
+ else
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
+ end
+
+ assert_equal('::1', v6mc.local_address.ip_address)
assert_equal(@port, v6mc.local_address.ip_port)
end