1

I have an application that meeds to:

  • talks in UDP
  • support optionally disabling multicast.
  • treat multicast message the same way as unicast message but with few restrictions

I thought I should use same UDP socket to receive both unicast and multicast datagrams.

# udp_multicast_recv.py
# Python version used: 3.9.1
import socket
import struct

host = "224.1.1.1"
# host = "0.0.0.0"
listen_all = True
port = 5683

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if listen_all:
    s.bind(("", port))
else:
    s.bind((host, port))
mreq = struct.pack("4sl", socket.inet_aton(host), socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

data = None
print("Listening")
while True:
    # data, addr = s.recvfrom(1024)
    data, ancdata, msg_flags, addr = s.recvmsg(1024)
    print(addr, ":", data, ancdata, msg_flags)

# udp_multicast_send.py

import socket

dest = "224.1.1.1"
dest = "192.168.86.188"
port = 5683

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.sendto("Hello world".encode(), (dest, port))

I soon noticed I need a way to distinguish multicast datagram from unicast datagram. But I was unable to find a solution nor related question on how to achieve this on the internet.

Hence the question: When receiving UDP datagram from same socket, is there a way to tell whether datagram is unicast or multicast?

8749236
  • 444
  • 7
  • 12
  • 1
    [`socket.recvmsg()`](https://docs.python.org/3/library/socket.html#socket.socket.recvmsg) can give you the IP that the packet was sent to, and which interface received it, but only if the `IP_PKTINFO` option is enabled on the socket. You need to parse that msg data. The `socket.recvmsg()` documentation shows you how to iterate the data fields of the returned `ancdata`. Also see [cmsg](https://linux.die.net/man/3/cmsg), and [Get destination address of a received UDP packet](https://stackoverflow.com/questions/5281409/), for more details – Remy Lebeau Jan 13 '21 at 23:20
  • 1
    @RemyLebeau Thank you, I stumbled across IP_PKTINFO flag during my search, it appears this feature is not yet implemented as of Python 3.9: https://bugs.python.org/issue31203 – 8749236 Jan 13 '21 at 23:56
  • Python 3 does have IPV6_RECVPKTINFO flag available. However, with some testing I was not able to get control message (got empty control messages instead) as expected. No idea why this happening. Maybe I should open a separate question focus on this flag instead. – 8749236 Jan 13 '21 at 23:58
  • 1
    What is stopping you from simply defining `IP_PKTINFO` in your own code? It is just a number (8): `IP_PKTINFO = 8`. You can call [`socket.setsockopt()`](https://docs.python.org/3/library/socket.html#socket.socket.setsockopt) directly to enable it: `s.setsockopt(socket.IPPROTO_IP, IP_PKTINFO, 1)` – Remy Lebeau Jan 14 '21 at 00:21
  • @RemyLebeau I'm uncomfortable with using magic numbers, and I know some of these flags may have different number on different platform. I will try it though. – 8749236 Jan 14 '21 at 17:36
  • "*I know some of these flags may have different number on different platform*" - True. For instance, `IP_PKTINFO` is 8 on Linux but is 18 on Windows. – Remy Lebeau Jan 14 '21 at 18:27
  • @RemyLebeau I am currently working on MacOS and IP_PKTINFO value is 26. However I still failed to get datagram destination address as expected after adding `s.setsockopt(socket.IPPROTO_IP, 26, 1)` on both Linux and MacOS. I had to move forward with two separate socket approach. But I'm very curious why IPV6_RECVPKTINFO and IP_PKTINFO(26) opt never behaved the way I expected. After a quick google, both Linux and MacOS should support these opt. I will start a separate question when I got time to play with this. – 8749236 Jan 14 '21 at 20:59
  • On Linux, `IPPROTO_IP` option 26 is `IP_RECVERR_RFC4884`. And note that `IPV6_RECVPKTINFO` is separate from (replaces?) `IPV6_PKTINFO`, and they may be different values from each other, too. Isn't using `PKTINFO` fun? :-) – Remy Lebeau Jan 14 '21 at 22:01
  • @RemyLebeau Sadly python didn't define IP_PKTINFO =(. So I had to write code to detect different platform and set the value accordingly. More over, I still haven't been able to get IP_PKTINFO to work =( – 8749236 Jan 15 '21 at 18:10

0 Answers0