Python: Multicast

Posted on Jul 16, 2022

Send/Receive multicast messages:

The following code block shows how to instantiate a multicast socket on windows/linux.

Features:

  • socket can both send/receive multicast messages
  • avoid binding to all interfaces (‘0.0.0.0’), this can be a security issue

Many online examples for python multicast sockets no longer work for newer version of Linux.
This code snippet was tested to work on Ubuntu 22 as of 7/24/2022.

YOU LIE: This code does not work on my system!

There are many linux settings that can prevent application level sockets from capturing multicast messages.
These are the debugging steps I used to diagnose missing multicast messages.

Tools:

  • Wireshark (windows/linux)
  • tcpdump (linux)

Because Wireshark and tcpdump use “promiscuous mode” it circumvents the usual settings blocking multicast traffic into an application.
example tcpdump call: “tcpdump -i eth0 -s0 -vv host 239.255.255.250”

System Settings:

  • Temporarily Turn off firewall (Windows / Linux)
    • sudo systemctl stop firewalld (Linux)
    • sudo ufw disable (other Linux distros)
    • windows defender firewall (Windows)
  • Edit /stc/sysctl.conf (Linux)
    • rf_filter = 0

There are many stack overflow topics on missing multicast traffic in python but still visable with tcpdump.
I will eventually compile them all into this page but because I am currently lazy, you will have to google yourself.
An example stack overflow page that goes over some first level problems:
https://serverfault.com/questions/163244/linux-kernel-not-passing-through-multicast-udp-packets

working on python 3.8.0-3.8.12 (problems on windows for 3.8.12+ OS_ERROR)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# basic inputs
MCAST_GRP = '239.2.3.5'
INTERFACE = '192.168.1.171'
MCAST_PORT = 50001

# create udp socket 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

# set socket as reusable
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  

# set timeout
sock.settimeout(1)

# bind to socket (platform dependent)
system_name = platform.system()
if system_name == "Linux":
  sock.bind((MCAST_GRP, MCAST_PORT))
elif system_name == "Windows":
  sock.bind((INTERFACE, MCAST_PORT))
else:
  raise SystemError("unsupported system")

# send multicast messages through interface
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(INTERFACE))

# join multicast group
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MCAST_GRP) + socket.inet_aton(INTERFACE))

# ==== multicast send code ====
import threading
import time 

def send():
  while True:
    message = b'hello'
    print(f"Sending Message: {message}")
    # relevant sending code, after socket has been created
    sock.sendto(message, (MCAST_GRP, MCAST_PORT))
    time.sleep(1)

threading.Thread(target=send, daemon=True).start()
# =============================

# ==== multicast recv code ====
try:
  while True:
    try:
      data, addr = sock.recvfrom(1024)
      print(f"Receiving Message: {data}")
    except socket.timeout:
      continue
except KeyboardInterrupt:
  print("exiting")
finally:
  sock.close()
# =============================