0002-Fix-multicast-of-rinda.patch

Hiroshi Shirosaki, 04/09/2013 06:26 PM

Download (6.13 KB)

View differences:

lib/rinda/ring.rb
67 67
    # +addresses+ can contain multiple addresses.  If a multicast address is
68 68
    # given in +addresses+ then the RingServer will listen for multicast
69 69
    # queries.
70
    #
71
    # If you use IPv4 multicast you may need to set an address of the inbound
72
    # interface which joins a multicast group.
73
    #
74
    #   ts = Rinda::TupleSpace.new
75
    #   rs = Rinda::RingServer.new(ts, [['239.0.0.1', '9.5.1.1']])
76
    #
77
    # You can set addresses as an Array Object.  The first element of the
78
    # Array is a multicast address and the second is an inbound interface
79
    # address.  If the second is omitted then '0.0.0.0' is used.
80
    #
81
    # If you use IPv6 multicast you may need to set both the local interface
82
    # address and the inbound interface index:
83
    #
84
    #   rs = Rinda::RingServer.new(ts, [['ff02::1', '::1', 1]])
85
    #
86
    # The first element is a multicast address and the second is an inbound
87
    # interface address.  The third is an inbound interface index.
88
    #
89
    # At this time there is no easy way to get an interface index by name.
90
    #
91
    # If the second is omitted then '::1' is used.
92
    # If the third is omitted then 0 (default interface) is used.
70 93

  
71 94
    def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
72 95
      @port = port
......
78 101
      @renewer = Renewer.new
79 102

  
80 103
      @ts = ts
81
      @sockets = addresses.map do |address|
82
        make_socket(address)
104
      @sockets = []
105
      addresses.each do |address|
106
        if Array === address
107
          make_socket(*address)
108
        else
109
          make_socket(address)
110
        end
83 111
      end
84 112

  
85 113
      @w_services = write_services
......
88 116

  
89 117
    ##
90 118
    # Creates a socket at +address+
119
    #
120
    # If +address+ is multicast address then +interface_address+ and
121
    # +multicast_interface+ can be set as optional.
122
    #
123
    # A created socket is bound to +interface_address+.  If you use IPv4
124
    # multicast then the interface of +interface_address+ is used as the
125
    # inbound interface.
126
    #
127
    # If you use IPv6 multicast then +multicast_interface+ is used as the
128
    # inbound interface.  +multicast_interface+ is a network interface index.
91 129

  
92
    def make_socket(address)
130
    def make_socket(address, interface_address=nil, multicast_interface=0)
93 131
      addrinfo = Addrinfo.udp(address, @port)
94 132

  
95 133
      socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
......
103 141
        end
104 142

  
105 143
        if addrinfo.ipv4_multicast? then
144
          interface_address = '0.0.0.0' if interface_address.nil?
145
          socket.bind(Addrinfo.udp(interface_address, @port))
146

  
106 147
          mreq = IPAddr.new(addrinfo.ip_address).hton +
107
            IPAddr.new('0.0.0.0').hton
148
            IPAddr.new(interface_address).hton
108 149

  
109 150
          socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
110 151
        else
111
          mreq = IPAddr.new(addrinfo.ip_address).hton + [0].pack('I')
152
          interface_address = '::1' if interface_address.nil?
153
          socket.bind(Addrinfo.udp(interface_address, @port))
154

  
155
          mreq = IPAddr.new(addrinfo.ip_address).hton +
156
            [multicast_interface].pack('I')
112 157

  
113 158
          socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
114 159
        end
160
      else
161
        socket.bind(addrinfo)
115 162
      end
116 163

  
117
      socket.bind(addrinfo)
118

  
164
      @sockets << socket
119 165
      socket
120 166
    end
121 167

  
test/rinda/test_rinda.rb
570 570
      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
571 571
    end
572 572

  
573
    assert_equal('239.0.0.1', v4mc.local_address.ip_address)
574
    assert_equal(@port,       v4mc.local_address.ip_port)
573
    assert_equal('0.0.0.0', v4mc.local_address.ip_address)
574
    assert_equal(@port,     v4mc.local_address.ip_port)
575 575
  end
576 576

  
577 577
  def test_make_socket_ipv6_multicast
......
590 590
      assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
591 591
    end
592 592

  
593
    assert_equal('ff02::1',  v6mc.local_address.ip_address)
593
    assert_equal('::1', v6mc.local_address.ip_address)
594
    assert_equal(@port, v6mc.local_address.ip_port)
595
  end
596

  
597
  def test_ring_server_ipv4_multicast
598
    @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
599
    v4mc = @rs.instance_variable_get('@sockets').first
600

  
601
    if Socket.const_defined?(:SO_REUSEPORT) then
602
      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
603
    else
604
      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
605
    end
606

  
607
    assert_equal('0.0.0.0', v4mc.local_address.ip_address)
608
    assert_equal(@port,     v4mc.local_address.ip_port)
609
  end
610

  
611
  def test_ring_server_ipv6_multicast
612
    skip 'IPv6 not available' unless
613
      Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
614

  
615
    begin
616
      @rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
617
    rescue Errno::EADDRNOTAVAIL
618
      return # IPv6 address for multicast not available
619
    end
620

  
621
    v6mc = @rs.instance_variable_get('@sockets').first
622

  
623
    if Socket.const_defined?(:SO_REUSEPORT) then
624
      assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
625
    else
626
      assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
627
    end
628

  
629
    assert_equal('::1', v6mc.local_address.ip_address)
594 630
    assert_equal(@port, v6mc.local_address.ip_port)
595 631
  end
596 632

  
597
-