lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
EHC250: flags=0<> mtu 0
EHC253: flags=0<> mtu 0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=b<RXCSUM,TXCSUM,VLAN_HWTAGGING>
ether c8:bc:c8:96:ec:2a
inet6 fe80::1c83:5086:dc41:c9ec%en0 prefixlen 64 secured scopeid 0x6
inet6 2001::1 prefixlen 72
inet XXX.X.XX.40 netmask 0xfffffc00 broadcast 172.22.15.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (100baseTX <full-duplex,flow-control>)
status: active
With the default setting of 0 for ipv6mr_interface in the IPV6_JOIN_GROUP request struct, joining the group(s) fails with error 49 (Can't assign requested address) and no IPv6 multicast packets are received.
After changing it to 6 (the interface index), the multicast join works without a hitch and the multicast packets are received.
Test code:
import asyncio
import socket
import struct
addrs = {socket.AF_INET: ['239.0.0.183'],
socket.AF_INET6: ['FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2']
}
class MulticastListener(asyncio.DatagramProtocol):
def __init__(self, name):
self.transport = None
self.name = name
def datagram_received(self, data, addr):
if data.startswith(b'LSL:shortinfo'):
_, query, returnaddr = data.splitlines()
returnaddr, queryid = returnaddr.split(b' ')
print(f'{self.name} received {query} from {addr} -> {returnaddr}:')
def error_received(self, exc):
print('Error received:', exc)
def connection_lost(self, exc):
asyncio.get_event_loop().stop()
@staticmethod
def server_socket(family):
mcastaddrs = addrs[family]
# (_, _, _, _, sockaddr) = socket.getaddrinfo(mcastaddrs[0], MCastHelper.port, family, socket.SOCK_DGRAM)[0]
s = socket.socket(family, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if hasattr(socket, 'SO_REUSEADDR'):
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if family == socket.AF_INET:
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
elif family == socket.AF_INET6:
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 2)
s.bind(('', 16571))
for group in mcastaddrs:
try:
binaddr = socket.inet_pton(family, group)
if family == socket.AF_INET:
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, binaddr + struct.pack('=I', socket.INADDR_ANY))
elif family == socket.AF_INET6:
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, binaddr + struct.pack('@I', 0))
print(f'Bound IP family {family} socket to {group}')
except OSError as e:
print(f'Error joining socket to group {group}: {e.errno}, {e.strerror}')
return s
async def main():
loop = asyncio.get_running_loop()
tasks = []
for name, family in [('IPv4', socket.AF_INET), ('IPv6', socket.AF_INET6)]:
tasks.append(await loop.create_datagram_endpoint(lambda: MulticastListener(name=name),
sock=MulticastListener.server_socket(family)))
try:
await asyncio.sleep(60)
finally:
for transport, protocol in tasks:
transport.close()
if __name__ == '__main__':
asyncio.run(main())
Output of
ifconfig
:With the default setting of 0 for
ipv6mr_interface
in theIPV6_JOIN_GROUP
request struct, joining the group(s) fails with error 49 (Can't assign requested address
) and no IPv6 multicast packets are received.After changing it to 6 (the interface index), the multicast join works without a hitch and the multicast packets are received.
Test code: