[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]