I have a linux network interface that IPv6 multicast traffic is arriving on. There are both ICMPv6 packets and UDP packets arriving for the same multicast group.
I’m trying to receive the UDP traffic. The code below is in Python but I don’t believe this is important; the Python library here is a pretty thin wrapper around the BSD sockets API. This is what I’m doing:
import socket import struct import select import ipaddress # .packet is a byte array mcast_group = ipaddress.IPv6Address("ff22:5eea:675c:d1fc:17c::1").packed address = ipaddress.IPv6Address("...ipv6 global scope address of interface...") sock = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) group = struct.pack("16s i", mcast_group, 3 #Interface ID ) sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group) sock.bind((address.compressed, 0, 0, 0)) recv_socks, _, _ = select.select([sock], [], [], 10) if recv_socks: print("Received packet")
With the code above, I appear to be receiving both the UDP and ICMPv6 packets. If I change the second parameter to socket.socket()
from socket.SOCK_RAW
to socket.SOCK_DGRAM
then I receive no packets at all.
I’ve confirmed that both packet types are arriving on that interface, addressed to that multicast group, using wireshark.
There is something here that I’ve fundamentally not understood about socket()
. Shouldn’t specifying IPPROTO_UDP
mean that I only get UDP packets? Shouldn’t specifying SOCK_DGRAM
still allow UDP packets through?
The Linux kernel is 4.9 on aarch64, in case that’s important.
Advertisement
Answer
Eventually figured this out. Here’s the correct way of doing this:
import socket import struct import select import ipaddress # .packet is a byte array mcast_group = ipaddress.IPv6Address("ff22:5eea:675c:d1fc:17c::1") address = ipaddress.IPv6Address("...ipv6 global scope address of interface...") port_no = 5555 interface_idx = 3 sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) group = struct.pack("16s i", mcast_group.packed, 3 #Interface ID ) sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group) sock.bind((str(mcast_group), port_no, 0, interface_idx)) recv_socks, _, _ = select.select([sock], [], [], 10) if recv_socks: print("Received packet")
Important differences from the code in the question:
- UDP (
SOCK_DGRAM
) sockets need to be bound to a port number. AFAICT this is why I was getting ICMP traffic as well as UDP withSOCK_RAW
and no traffic at all withSOCK_DGRAM
. - Instead of binding the socket to a unicast address on the interface of interest, bind it to the multicast group but use the interface index as the
scope_id
(the fourth in the address tuple passed tosocket.bind()
).