ruby-changes:28420
From: shirosaki <ko1@a...>
Date: Fri, 26 Apr 2013 00:43:31 +0900 (JST)
Subject: [ruby-changes:28420] shirosaki:r40472 (trunk): ring.rb: specify multicast interface
shirosaki 2013-04-26 00:43:22 +0900 (Fri, 26 Apr 2013) New Revision: 40472 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=40472 Log: 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/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] Modified files: trunk/ChangeLog trunk/lib/rinda/ring.rb trunk/test/rinda/test_rinda.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 40471) +++ ChangeLog (revision 40472) @@ -1,3 +1,23 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Apr 26 00:07:52 2013 Hiroshi Shirosaki <h.shirosaki@g...> + + * 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@g...> * lib/rinda/ring.rb (Rinda::RingServer#initialize): add a socket Index: lib/rinda/ring.rb =================================================================== --- lib/rinda/ring.rb (revision 40471) +++ lib/rinda/ring.rb (revision 40472) @@ -67,6 +67,29 @@ module Rinda https://github.com/ruby/ruby/blob/trunk/lib/rinda/ring.rb#L67 # +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 https://github.com/ruby/ruby/blob/trunk/lib/rinda/ring.rb#L103 @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 https://github.com/ruby/ruby/blob/trunk/lib/rinda/ring.rb#L116 ## # 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 https://github.com/ruby/ruby/blob/trunk/lib/rinda/ring.rb#L144 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 Index: test/rinda/test_rinda.rb =================================================================== --- test/rinda/test_rinda.rb (revision 40471) +++ test/rinda/test_rinda.rb (revision 40472) @@ -575,8 +575,8 @@ class TestRingServer < Test::Unit::TestC https://github.com/ruby/ruby/blob/trunk/test/rinda/test_rinda.rb#L575 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::TestC https://github.com/ruby/ruby/blob/trunk/test/rinda/test_rinda.rb#L595 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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/