Ticket #5086: fix-ipv6-literals-udp.5.patch

File fix-ipv6-literals-udp.5.patch, 25.7 KB (added by marto1_, 3 years ago)

Here multicast works for v6 as far as loopback testing goes.

  • twisted/test/test_udp.py

     
    168168
    169169        return d.addCallback(cbStarted, p)
    170170
     171    def test_bindToIPv6Interface(self):
     172        """
     173        Bind to ipv6 interface.
     174        """
     175        server = Server()
     176        p = reactor.listenUDP(0, server, interface="::1")
     177        self.assertEqual(p.getHost().host, "::1")
    171178
     179        return p.stopListening()
     180
     181    def cbServerStarted(self, ignored, client, interface, d):
     182        self.port2 = reactor.listenUDP(0, client, interface=interface)
     183        return d
     184
     185    def cbClientStarted(self, ignored, client, server, interface):
     186        client.transport.connect(interface,
     187                                 server.transport.getHost().port)
     188        cAddr = client.transport.getHost()
     189        sAddr = server.transport.getHost()
     190
     191        serverSend = client.packetReceived = defer.Deferred()
     192        server.transport.write(b"hello", (cAddr.host, cAddr.port))
     193
     194        clientWrites = [
     195            (b"a",),
     196            (b"b", None),
     197            (b"c", (sAddr.host, sAddr.port))]
     198
     199        def cbClientSend(ignored):
     200            if clientWrites:
     201                nextClientWrite = server.packetReceived = defer.Deferred()
     202                nextClientWrite.addCallback(cbClientSend)
     203                client.transport.write(*clientWrites.pop(0))
     204                return nextClientWrite
     205
     206        # No one will ever call .errback on either of these Deferreds,
     207        # but there is a non-trivial amount of test code which might
     208        # cause them to fail somehow.  So fireOnOneErrback=True.
     209        return defer.DeferredList([
     210                cbClientSend(None),
     211                serverSend], fireOnOneErrback=True)
     212
     213
     214    def cbSendsFinished(self, ignored, client, server, addressAddition=()):
     215        cAddr = client.transport.getHost()
     216        sAddr = server.transport.getHost()
     217        clientAddr = (cAddr.host, cAddr.port) + addressAddition
     218        serverAddr = (sAddr.host, sAddr.port) + addressAddition
     219        self.assertEqual(
     220            client.packets,
     221            [(b"hello", serverAddr)])
     222        self.assertEqual(
     223            server.packets,
     224            [(b"a", clientAddr),
     225             (b"b", clientAddr),
     226             (b"c", clientAddr)])
     227
     228
     229    def cbFinished(self, ignored, port1, port2):
     230        return defer.DeferredList([
     231            defer.maybeDeferred(port1.stopListening),
     232            defer.maybeDeferred(port2.stopListening)],
     233            fireOnOneErrback=True)
     234
     235
     236
    172237    def test_bindError(self):
    173238        """
    174239        A L{CannotListenError} exception is raised when attempting to bind a
     
    192257        d.addCallback(cbFinished)
    193258        return d
    194259
    195 
    196     def test_sendPackets(self):
     260    def performTransferServerClient(self, interface, addressAddition=()):
    197261        """
    198         Datagrams can be sent with the transport's C{write} method and
    199         received via the C{datagramReceived} callback method.
     262        Helper method that creates UDP server and client,
     263        connects the client to the server, sends some packets
     264        and asserts addresses.
    200265        """
    201266        server = Server()
    202267        serverStarted = server.startedDeferred = defer.Deferred()
    203         port1 = reactor.listenUDP(0, server, interface="127.0.0.1")
     268        port1 = reactor.listenUDP(0, server, interface=interface)
    204269
    205270        client = GoodClient()
    206271        clientStarted = client.startedDeferred = defer.Deferred()
    207272
    208         def cbServerStarted(ignored):
    209             self.port2 = reactor.listenUDP(0, client, interface="127.0.0.1")
    210             return clientStarted
     273        d = serverStarted.addCallback(self.cbServerStarted, client, interface,
     274                                      clientStarted)
     275        d.addCallback(self.cbClientStarted, client, server, interface)
     276        d.addCallback(self.cbSendsFinished, client, server, addressAddition)
     277        d.addCallback(self.cbFinished, port1, self.port2)
    211278
    212         d = serverStarted.addCallback(cbServerStarted)
     279        return d
    213280
    214         def cbClientStarted(ignored):
    215             client.transport.connect("127.0.0.1",
    216                                      server.transport.getHost().port)
    217             cAddr = client.transport.getHost()
    218             sAddr = server.transport.getHost()
     281    def test_writeToIPv6Interface(self):
     282        """
     283        Send packets to ipv6 address.
     284        """
     285        return self.performTransferServerClient("::1", (0, 0))
    219286
    220             serverSend = client.packetReceived = defer.Deferred()
    221             server.transport.write(b"hello", (cAddr.host, cAddr.port))
    222287
    223             clientWrites = [
    224                 (b"a",),
    225                 (b"b", None),
    226                 (b"c", (sAddr.host, sAddr.port))]
     288    def test_sendPackets(self):
     289        """
     290        Datagrams can be sent with the transport's C{write} method and
     291        received via the C{datagramReceived} callback method.
     292        """
     293        return self.performTransferServerClient("127.0.0.1")
    227294
    228             def cbClientSend(ignored):
    229                 if clientWrites:
    230                     nextClientWrite = server.packetReceived = defer.Deferred()
    231                     nextClientWrite.addCallback(cbClientSend)
    232                     client.transport.write(*clientWrites.pop(0))
    233                     return nextClientWrite
    234295
    235             # No one will ever call .errback on either of these Deferreds,
    236             # but there is a non-trivial amount of test code which might
    237             # cause them to fail somehow.  So fireOnOneErrback=True.
    238             return defer.DeferredList([
    239                 cbClientSend(None),
    240                 serverSend],
    241                 fireOnOneErrback=True)
    242 
    243         d.addCallback(cbClientStarted)
    244 
    245         def cbSendsFinished(ignored):
    246             cAddr = client.transport.getHost()
    247             sAddr = server.transport.getHost()
    248             self.assertEqual(
    249                 client.packets,
    250                 [(b"hello", (sAddr.host, sAddr.port))])
    251             clientAddr = (cAddr.host, cAddr.port)
    252             self.assertEqual(
    253                 server.packets,
    254                 [(b"a", clientAddr),
    255                  (b"b", clientAddr),
    256                  (b"c", clientAddr)])
    257 
    258         d.addCallback(cbSendsFinished)
    259 
    260         def cbFinished(ignored):
    261             return defer.DeferredList([
    262                 defer.maybeDeferred(port1.stopListening),
    263                 defer.maybeDeferred(self.port2.stopListening)],
    264                 fireOnOneErrback=True)
    265 
    266         d.addCallback(cbFinished)
    267         return d
    268 
    269 
    270296    def test_connectionRefused(self):
    271297        """
    272298        A L{ConnectionRefusedError} exception is raised when a connection
     
    500526
    501527class MulticastTestCase(unittest.TestCase):
    502528
     529    localhost = "127.0.0.1"
     530    multicast = "225.0.0.250"
     531    all = "0.0.0.0"
     532
    503533    def setUp(self):
    504534        self.server = Server()
    505535        self.client = Client()
     
    530560        """
    531561        self.assertEqual(self.server.transport.getLoopbackMode(), 1)
    532562        addr = self.server.transport.getHost()
    533         joined = self.server.transport.joinGroup("225.0.0.250")
     563        joined = self.server.transport.joinGroup(self.multicast,
     564                                                 self.all)
    534565
    535566        def cbJoined(ignored):
    536567            d = self.server.packetReceived = Deferred()
    537             self.server.transport.write(b"hello", ("225.0.0.250", addr.port))
     568            self.server.transport.write(b"hello", (self.multicast, addr.port))
    538569            return d
    539570        joined.addCallback(cbJoined)
    540571
     
    542573            self.assertEqual(len(self.server.packets), 1)
    543574            self.server.transport.setLoopbackMode(0)
    544575            self.assertEqual(self.server.transport.getLoopbackMode(), 0)
    545             self.server.transport.write(b"hello", ("225.0.0.250", addr.port))
     576            self.server.transport.write(b"hello", (self.multicast, addr.port))
    546577
    547578            # This is fairly lame.
    548579            d = Deferred()
     
    562593        Test C{getOutgoingInterface} and C{setOutgoingInterface}.
    563594        """
    564595        self.assertEqual(
    565             self.client.transport.getOutgoingInterface(), "0.0.0.0")
     596            self.client.transport.getOutgoingInterface(), self.all)
    566597        self.assertEqual(
    567             self.server.transport.getOutgoingInterface(), "0.0.0.0")
     598            self.server.transport.getOutgoingInterface(), self.all)
    568599
    569         d1 = self.client.transport.setOutgoingInterface("127.0.0.1")
    570         d2 = self.server.transport.setOutgoingInterface("127.0.0.1")
     600        d1 = self.client.transport.setOutgoingInterface(self.localhost)
     601        d2 = self.server.transport.setOutgoingInterface(self.localhost)
    571602        result = gatherResults([d1, d2])
    572603
    573604        def cbInterfaces(ignored):
    574605            self.assertEqual(
    575                 self.client.transport.getOutgoingInterface(), "127.0.0.1")
     606                self.client.transport.getOutgoingInterface(), self.localhost)
    576607            self.assertEqual(
    577                 self.server.transport.getOutgoingInterface(), "127.0.0.1")
     608                self.server.transport.getOutgoingInterface(), self.localhost)
    578609        result.addCallback(cbInterfaces)
    579610        return result
    580611
     
    583614        """
    584615        Test that multicast a group can be joined and left.
    585616        """
    586         d = self.client.transport.joinGroup("225.0.0.250")
     617        d = self.client.transport.joinGroup(self.multicast, self.all)
    587618
    588619        def clientJoined(ignored):
    589             return self.client.transport.leaveGroup("225.0.0.250")
     620            return self.client.transport.leaveGroup(self.multicast, self.all)
    590621        d.addCallback(clientJoined)
    591622
    592623        def clientLeft(ignored):
    593             return self.server.transport.joinGroup("225.0.0.250")
     624            return self.server.transport.joinGroup(self.multicast, self.all)
    594625        d.addCallback(clientLeft)
    595626
    596627        def serverJoined(ignored):
    597             return self.server.transport.leaveGroup("225.0.0.250")
     628            return self.server.transport.leaveGroup(self.multicast, self.all)
    598629        d.addCallback(serverJoined)
    599630
    600631        return d
     
    607638        """
    608639        # 127.0.0.1 is not a multicast address, so joining it should fail.
    609640        return self.assertFailure(
    610             self.client.transport.joinGroup("127.0.0.1"),
     641            self.client.transport.joinGroup(self.localhost, self.all),
    611642            error.MulticastJoinError)
    612643    if runtime.platform.isWindows() and not runtime.platform.isVista():
    613644        test_joinFailure.todo = "Windows' multicast is wonky"
     
    619650        received from it.
    620651        """
    621652        c = Server()
    622         p = reactor.listenMulticast(0, c)
     653        p = reactor.listenMulticast(0, c, interface=self.all)
    623654        addr = self.server.transport.getHost()
    624655
    625         joined = self.server.transport.joinGroup("225.0.0.250")
     656        joined = self.server.transport.joinGroup(self.multicast, self.all)
    626657
    627658        def cbJoined(ignored):
    628659            d = self.server.packetReceived = Deferred()
    629             c.transport.write(b"hello world", ("225.0.0.250", addr.port))
     660            c.transport.write(b"hello world", (self.multicast, addr.port))
    630661            return d
    631662        joined.addCallback(cbJoined)
    632663
     
    650681        """
    651682        firstClient = Server()
    652683        firstPort = reactor.listenMulticast(
    653             0, firstClient, listenMultiple=True)
     684            0, firstClient, listenMultiple=True, interface=self.all)
    654685
    655686        portno = firstPort.getHost().port
    656687
    657688        secondClient = Server()
    658689        secondPort = reactor.listenMulticast(
    659             portno, secondClient, listenMultiple=True)
     690            portno, secondClient, listenMultiple=True, interface=self.all)
    660691
    661         theGroup = "225.0.0.250"
    662         joined = gatherResults([self.server.transport.joinGroup(theGroup),
    663                                 firstPort.joinGroup(theGroup),
    664                                 secondPort.joinGroup(theGroup)])
     692        theGroup = self.multicast
     693        joined = gatherResults([self.server.transport.joinGroup(theGroup,
     694                                                                self.all),
     695                                firstPort.joinGroup(theGroup, self.all),
     696                                secondPort.joinGroup(theGroup, self.all)])
    665697
    666698
    667699        def serverJoined(ignored):
     
    689721                                 "processes can listen, but not multiple sockets "
    690722                                 "in same process?")
    691723
     724class IPv6MulticastTestCase(MulticastTestCase):
    692725
     726    localhost = "::1"
     727    multicast = "ffb1::"
     728    all = "::"
     729
     730    def setUp(self):
     731        self.server = Server()
     732        self.client = Client()
     733        self.port1 = reactor.listenMulticast(0, self.server, interface="::")
     734        self.port2 = reactor.listenMulticast(0, self.client, interface="::")
     735        self.client.transport.connect(
     736            "::1", self.server.transport.getHost().port)
     737
     738    def test_interface(self):
     739        """
     740        Test C{getOutgoingInterface} and C{setOutgoingInterface}.
     741        """
     742        self.assertEqual(
     743            self.client.transport.getOutgoingInterface(), self.all)
     744        self.assertEqual(
     745            self.server.transport.getOutgoingInterface(), self.all)
     746
     747        d1 = self.client.transport.setOutgoingInterface(self.localhost)
     748        d2 = self.server.transport.setOutgoingInterface(self.localhost)
     749        result = gatherResults([d1, d2])
     750
     751        def cbInterfaces(ignored):
     752            self.assertEqual(
     753                self.client.transport.getOutgoingInterface(), self.all)
     754            self.assertEqual(
     755                self.server.transport.getOutgoingInterface(), self.all)
     756        result.addCallback(cbInterfaces)
     757        return result
     758
     759
     760   
    693761if not interfaces.IReactorUDP(reactor, None):
    694762    UDPTestCase.skip = "This reactor does not support UDP"
    695763    ReactorShutdownInteraction.skip = "This reactor does not support UDP"
  • twisted/internet/test/test_address.py

     
    199199        """
    200200        return IPv6Address("TCP", "::2", 0)
    201201
     202    def buildWholeAddress(self):
     203        """
     204        Like L{buldAddress}, but assigns all fields specific to
     205        ipv6 (including flow info and scope id)
     206        """
     207        return IPv6Address("TCP", "FE80::", 1, 1, None)
    202208
     209    def test_specificFields(self):
     210        """
     211        Simply check if ipv6 specific fields exist.
     212        """
     213        addr = self.buildWholeAddress()
     214        self.assertTrue(hasattr(addr, "flowInfo"))
     215        self.assertTrue(hasattr(addr, "scopeId"))
    203216
     217
     218
    204219class UNIXAddressTestCase(unittest.SynchronousTestCase, AddressTestCaseMixin):
    205220    addressArgSpec = (("name", "%r"),)
    206221
  • twisted/internet/test/test_udp.py

     
    2020from twisted.internet.defer import Deferred, maybeDeferred
    2121from twisted.internet.interfaces import (
    2222    ILoggingContext, IListeningPort, IReactorUDP)
    23 from twisted.internet.address import IPv4Address
     23from twisted.internet.address import IPv4Address, IPv6Address
    2424from twisted.internet.protocol import DatagramProtocol
    2525
    2626from twisted.internet.test.connectionmixins import (LogObserverMixin,
     
    4848        """
    4949        return "(UDP Port %s Closed)" % (port.getHost().port,)
    5050
     51    def getIPv6ListeningPort(self, reactor, protocol):
     52        """
     53        Get a UDP port binded to ipv6 interface.
     54        """
     55        return reactor.listenUDP(0, protocol, "::1")
    5156
    5257
     58
    5359class DatagramTransportTestsMixin(LogObserverMixin):
    5460    """
    5561    Mixin defining tests which apply to any port/datagram based transport.
     
    162168        self.assertEqual(
    163169            port.getHost(), IPv4Address('UDP', host, portNumber))
    164170
     171    def test_getHostIPv6(self):
     172        """
     173        L{IListeningPort.getHost} returns an L{IPv6Address} giving a
     174        IPv6 address, the port number that the protocol is listening on
     175        and the port number.
     176        """
     177        reactor = self.buildReactor()
     178        port = self.getIPv6ListeningPort(reactor, DatagramProtocol())
     179        addr = port.getHost()
     180        self.assertEqual(addr.host, "::1")
     181        self.assertEqual(addr.flowInfo, 0)
     182        self.assertEqual(addr.scopeId, 0)
    165183
     184
     185
    166186    def test_logPrefix(self):
    167187        """
    168188        Datagram transports implement L{ILoggingContext.logPrefix} to return a
  • twisted/internet/abstract.py

     
    99from __future__ import division, absolute_import
    1010
    1111from socket import AF_INET6, inet_pton, error
     12from struct import unpack
    1213
     14
    1315from zope.interface import implementer
    1416
    1517# Twisted Imports
     
    526528        return False
    527529    return True
    528530
     531#Look:
     532#http://stackoverflow.com/questions/10558441/inet-aton-similar-function-for-ipv6
     533def covertIPv6ToInteger(address):
     534    """Convert ipv6 address to integer"""
     535    address = inet_pton(AF_INET6, address)
     536    a, b = unpack(">QQ", address)
     537    return (a << 64) | b
    529538
    530 __all__ = ["FileDescriptor", "isIPAddress", "isIPv6Address"]
     539__all__ = ["FileDescriptor", "isIPAddress", "isIPv6Address",
     540           "covertIPv6ToInteger"]
  • twisted/internet/base.py

     
    564564        if not name:
    565565            # XXX - This is *less than* '::', and will screw up IPv6 servers
    566566            return defer.succeed('0.0.0.0')
    567         if abstract.isIPAddress(name):
     567        if abstract.isIPAddress(name) or abstract.isIPv6Address(name):
    568568            return defer.succeed(name)
    569569        return self.resolver.getHostByName(name, timeout)
    570570
  • twisted/internet/address.py

     
    7777        IPv6 address; for example, "::1".
    7878    @type host: C{str}
    7979    """
     80    def __init__(self, type, host, port, flowInfo=0, scopeId=0, _bwHack=None):
     81        _IPAddress.__init__(self, type, host, port)
     82        self.flowInfo = flowInfo
     83        self.scopeId = scopeId
     84        if _bwHack is not None:
     85            warnings.warn("twisted.internet.address.IPv4Address._bwHack "
     86                          "is deprecated since Twisted 11.0",
     87                          DeprecationWarning, stacklevel=2)
    8088
    8189
    8290
  • twisted/internet/udp.py

     
    8080        self.interface = interface
    8181        self.setLogStr()
    8282        self._connectedAddr = None
     83        self.setAddressFamily()
    8384
    8485    def __repr__(self):
    8586        if self._realPortNumber is not None:
     
    207208        """
    208209        if self._connectedAddr:
    209210            raise RuntimeError("already connected, reconnecting is not currently supported")
    210         if not abstract.isIPAddress(host):
     211        if not abstract.isIPAddress(host) and not abstract.isIPv6Address(host):
    211212            raise ValueError("please pass only IP addresses, not domain names")
    212213        self._connectedAddr = (host, port)
    213214        self.socket.connect((host, port))
     
    252253        logPrefix = self._getLogPrefix(self.protocol)
    253254        self.logstr = "%s (UDP)" % logPrefix
    254255
     256    def setAddressFamily(self):
     257        """
     258        Resolve address family for the socket.
     259        """
     260        if abstract.isIPv6Address(self.interface):
     261            if not socket.has_ipv6:
     262                raise RuntimeError("IPV6 is not supported, "+\
     263                                       "compile with ipv6 support")
     264            self.addressFamily = socket.AF_INET6
     265        elif abstract.isIPAddress(self.interface):
     266            self.addressFamily = socket.AF_INET
     267       
    255268
     269
    256270    def logPrefix(self):
    257271        """
    258272        Return the prefix to log with.
     
    262276
    263277    def getHost(self):
    264278        """
    265         Returns an IPv4Address.
     279        Returns an IPv4Address or IPv6Address.
    266280
    267281        This indicates the address from which I am connecting.
    268282        """
    269         return address.IPv4Address('UDP', *self.socket.getsockname())
     283        if self.addressFamily == socket.AF_INET:
     284            return address.IPv4Address('UDP', *self.socket.getsockname())
     285        elif self.addressFamily == socket.AF_INET6:
     286            return address.IPv6Address('UDP', *self.socket.getsockname())
    270287
    271288
    272289
     
    276293    """
    277294
    278295    def getOutgoingInterface(self):
    279         i = self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF)
    280         return socket.inet_ntoa(struct.pack("@i", i))
     296        i = self.socket.getsockopt(self.IPLayer, self.multicastOUT)
     297        #FIXME This might be a bug.
     298        if self.IPLayer == socket.IPPROTO_IPV6:
     299            addr = struct.pack("@i", i)
     300            addr = addr.ljust(16, '\x00')
     301        else:
     302            addr = struct.pack("@i", i)
     303        return socket.inet_ntop(self.addressFamily, addr)
    281304
    282305    def setOutgoingInterface(self, addr):
    283306        """Returns Deferred of success."""
    284307        return self.reactor.resolve(addr).addCallback(self._setInterface)
    285308
    286309    def _setInterface(self, addr):
    287         i = socket.inet_aton(addr)
    288         self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, i)
     310        i = socket.inet_pton(self.addressFamily, addr)
     311        self.socket.setsockopt(self.IPLayer, self.multicastOUT, i)
    289312        return 1
    290313
    291314    def getLoopbackMode(self):
    292         return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP)
     315        return self.socket.getsockopt(self.IPLayer, self.multicastLoop)
    293316
    294317    def setLoopbackMode(self, mode):
    295         mode = struct.pack("b", operator.truth(mode))
    296         self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, mode)
     318        if self.IPLayer != socket.IPPROTO_IPV6:
     319            mode = struct.pack("b", operator.truth(mode))
     320        self.socket.setsockopt(self.IPLayer, self.multicastLoop, mode)
    297321
    298322    def getTTL(self):
    299         return self.socket.getsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL)
     323        return self.socket.getsockopt(self.IPLayer, self.multicastHops)
    300324
    301325    def setTTL(self, ttl):
    302         ttl = struct.pack("B", ttl)
    303         self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
     326        if self.IPLayer != socket.IPPROTO_IPV6:
     327            ttl = struct.pack("B", ttl)
     328        self.socket.setsockopt(self.IPLayer, self.multicastHops, ttl)
    304329
     330    def _checkIPVersions(self, addr1, addr2):
     331        """Check if address versions match"""
     332        addrIsIPv6 = abstract.isIPv6Address(addr1)
     333        interfaceIsIPv6 = abstract.isIPv6Address(addr2)
     334        if addrIsIPv6 and interfaceIsIPv6:
     335            pass
     336        elif not addrIsIPv6 and not addrIsIPv6:
     337            pass
     338        else:
     339            raise RuntimeError("IP version mismatch")
     340
    305341    def joinGroup(self, addr, interface=""):
    306342        """Join a multicast group. Returns Deferred of success."""
     343        self._checkIPVersions(addr, interface)
    307344        return self.reactor.resolve(addr).addCallback(self._joinAddr1, interface, 1)
    308345
    309346    def _joinAddr1(self, addr, interface, join):
    310347        return self.reactor.resolve(interface).addCallback(self._joinAddr2, addr, join)
    311348
    312349    def _joinAddr2(self, interface, addr, join):
    313         addr = socket.inet_aton(addr)
    314         interface = socket.inet_aton(interface)
     350
     351        addr = socket.inet_pton(self.addressFamily, addr)
     352        interface = socket.inet_pton(self.addressFamily, interface)
    315353        if join:
    316             cmd = socket.IP_ADD_MEMBERSHIP
     354            cmd = self.multicastJoin
    317355        else:
    318             cmd = socket.IP_DROP_MEMBERSHIP
     356            cmd = self.multicastLeave
    319357        try:
    320             self.socket.setsockopt(socket.IPPROTO_IP, cmd, addr + interface)
     358            self.socket.setsockopt(self.IPLayer, cmd, addr + interface)
    321359        except socket.error as e:
    322             return failure.Failure(error.MulticastJoinError(addr, interface, *e.args))
     360            return failure.Failure(error.MulticastJoinError(
     361                    addr, interface, *e.args))
    323362
    324363    def leaveGroup(self, addr, interface=""):
    325364        """Leave multicast group, return Deferred of success."""
     365        self._checkIPVersions(addr, interface)
    326366        return self.reactor.resolve(addr).addCallback(self._joinAddr1, interface, 0)
    327367
    328368
     
    331371    """
    332372    UDP Port that supports multicasting.
    333373    """
     374    IPLayer = socket.IPPROTO_IP
     375    multicastJoin = socket.IP_ADD_MEMBERSHIP
     376    multicastLeave = socket.IP_DROP_MEMBERSHIP
     377    multicastOUT = socket.IP_MULTICAST_IF
     378    multicastLoop = socket.IP_MULTICAST_LOOP
     379    multicastHops = socket.IP_MULTICAST_TTL
    334380
    335381    def __init__(self, port, proto, interface='', maxPacketSize=8192, reactor=None, listenMultiple=False):
    336382        """
    337383        @see: L{twisted.internet.interfaces.IReactorMulticast.listenMulticast}
    338384        """
    339385        Port.__init__(self, port, proto, interface, maxPacketSize, reactor)
     386        if abstract.isIPv6Address(interface):
     387            self.IPLayer = socket.IPPROTO_IPV6
     388            self.multicastJoin = socket.IPV6_JOIN_GROUP
     389            self.multicastLeave = socket.IPV6_LEAVE_GROUP
     390            self.multicastOUT = socket.IPV6_MULTICAST_IF
     391            self.multicastLoop = socket.IPV6_MULTICAST_LOOP
     392            self.multicastHops = socket.IPV6_MULTICAST_HOPS
     393
    340394        self.listenMultiple = listenMultiple
    341395
    342396    def createInternetSocket(self):