0

I have a hardware device that sends multicast data on my network. I have written a python script that receives the data and prints it. However, I've found that it only works on my Windows XP PC and doesn't work on my Ubuntu Linux 10.04 PC. Under Linux, nothing is received. It just goes round the while loop and there's never any data received. My code is posted below. Can you see any reason why this will not work under Linux? Thanks, Rab.

# Multicast client
# Adapted from: http://chaos.weblogs.us/archives/164
# on 05/03/2013

import socket

ANY = "0.0.0.0" 
MCAST_ADDR = "224.0.33.154"
MCAST_PORT = 31800

# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

# Allow multiple sockets to use the same PORT number
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

# Bind to the port that we know will receive multicast data
sock.bind((ANY,MCAST_PORT))

# Tell the kernel that we are a multicast socket
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)

# Tell the kernel that we want to add ourselves to a multicast group
# The address for the multicast group is the third param
status = sock.setsockopt(socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY));

# setblocking(0) is equiv to settimeout(0.0) which means we poll the socket.
# But this will raise an error if recv() or send() can't immediately find or send data. 
sock.setblocking(0)

while 1:
    try:
        data, addr = sock.recvfrom(1024)
    except socket.error as e:
        pass
    else:
        print "From: ", addr
        print "Data: ", data

Here's some sample output from my Windows PC:

From:  ('0.0.0.0', 31801)
Data:  EDCP

Note the remote hardware device does not have an IP address and is using address 0.0.0.0

EDIT: I've now found that this also doesn't work on my Windows laptop. So, it doesn't look like it's specific to the OS. Furthermore, I've tried running another script to send multicast data to the same multicast address and port. I can send from one PC and all the others are able to receive correctly using my receive script. But, only my one Windows PC is able to receive the data from the hardware device in question. I'm wondering if it is something to do with the ethernet adapters or their configuration. Could it be something to do with the fact that the harware device has IP address 0.0.0.0 and that these ethernet adapters and/or my receiver script needs told to receive messages with this address? Running Wireshark on the Linux PCs sees the data from the hardware device.

robhem
  • 168
  • 2
  • 8
  • Judging by the votes you should probably check out http://stackoverflow.com/questions/603852/multicast-in-python – Eero Aaltonen Mar 08 '13 at 13:15
  • @EeroAaltonen I have already checked that post and found the code to work between my various PCs. But, the receive code still won't receive data sent by the hardware device I wish to analyse (it uses address 0.0.0.0 and I'm thinking this may be the root of the issue). – robhem Mar 09 '13 at 20:48
  • Possible duplicate of [Multicast in Python](https://stackoverflow.com/questions/603852/multicast-in-python) – Trevor Boyd Smith Sep 26 '18 at 15:52

2 Answers2

0

Try binding to the multicast group address instead:

sock.bind((MCAST_ADDR,MCAST_PORT))

Also, you don't need to set multicast TTL on the receiver, that's for senders, and also optional.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • `sock.bind((MCAST_ADDR,MCAST_PORT))` results in error: The requested address is not valid in its context. I think binding to 0.0.0.0 is correct, as this tells the IP layer to use all available adapters for listening. – robhem Mar 08 '13 at 10:03
  • I should add `sock.bind((MCAST_ADDR,MCAST_PORT))` results in error on Windows (not on Linux). But, this didn't solve my issue. – robhem Mar 08 '13 at 16:46
  • Yes, Time To Live is only for the sender. Thanks. – robhem Mar 11 '13 at 10:20
0

I fought this same issue for two days. Wireshark saw the packets but my code did not. None of the supposedly "definitive" answers from various sources worked for me. The key came from https://serverfault.com/questions/163244/linux-kernel-not-passing-through-multicast-udp-packets.

Running "ip maddr" showed that code similar to yours was not adding the multicast address to any interface. I forced it to be added with smcroute (see link above). Still no joy. The packets have a source IP of 172.22... My interface is 172.17... I added a 172.22 address to that NIC. Bingo! Now my code received the packets.

Now how to make the program work without smcroute? I commented out the setsockopt() calls. Still worked. Unlinked the multicast address with smcroute -- failed. Uncommented the setsockopt() calls and replaced "ANY" with my 172.22 address. Success!

Summary:

  1. Make sure you have an IP on the same network segment as the incoming packets.
  2. Use that address in the IP_ADD_MEMBERSHIP call instead of INADDR_ANY.

It's possible you won't need to do 2) if you only have one NIC. I have three and must do it.

In case it's germane, I'm using Ubuntu 12.04. I did not need to change any of the default /etc/sysctl.conf settings as others have described. I tried them. They did not help, so I reset them back to installation defaults.

Community
  • 1
  • 1
  • Thanks for your input. I'll look into these things. – robhem Mar 09 '13 at 23:47
  • If I try to run `sudo smcroute -d` I get `MC-Router IPv4 already in use` and `Kernel does not support multicast routing` errors. I've confirmed the kernel config has multicast enabled as stated in your link. Any other way I can add an IP to eth0? For what it's worth, I tried the following: I've made `rp_filter=0` in `/proc/sys/net/ipv4/conf/all/rp_filter` and in `/proc/sys/net/ipv4/conf/default/rp_filter` and in `/etc/sysctl.d/10-network-security.conf`. Also uncommented `net.ipv4.ip_forward=1` in `/etc/sysctl.conf`. Still no joy. – robhem Mar 11 '13 at 15:28
  • I think "MC-Router IPv4 already in use" means that the daemon is already running. Use "sudo smcroute -j eth0 " to add the IP to eth0. I had to kill the smcroute daemon and restart it. Otherwise it would not accept the IP. It was automatically loaded when I booted today. Not sure what arguments were used. Things that woked last week need some prodding today. – AstroLad Mar 11 '13 at 15:55
  • Thanks. For some reason I get "unknown interface eth0" when I do this. It accepts a join to the "lo" interface though. – robhem Mar 11 '13 at 16:24
  • However, I can see my code is doing the bind correctly, as the multicast address shows up for eth0 when I run `ip maddr`. – robhem Mar 11 '13 at 16:38
  • The only thing I can see that is different between my send script and the hardware device is that my send script is sending from a PC with IP 192.168.x.x and the hardware device has IP 0.0.0.0; my receive script on any other PC with IP 192.168.x.x receives the data from my send script over the network. But, I only have my one Windows PC that can receive the data from the hardware device. – robhem Mar 11 '13 at 16:45
  • Sounds like 0.0.0.0 is the problem. Once I got the mcast IP registered, I still had to have an interface on the same network segment as the source. – AstroLad Mar 11 '13 at 17:30
  • Yes, it looks like IP 0.0.0.0 is the issue. There must be a way of dealing with this though, as Wireshark can work with it. – robhem Mar 12 '13 at 11:12