I am working on a test tool for an existing piece of SW that outputs messages on multicast ip of 240.240.240.1 port 2349. The IP interface on which I am receiving the multicast messages is 192.168.0.4. I can observe these messages being pushed out onto the network via wireshark. Even netstat -ng shows the subscriptions. However, the receiver I wrote in python is not picking them up. I did borrow the code below from another thread, with the attempt to modify it to work in my situation.
import socket
import struct
import sys
multicast_group = '240.240.240.1'
server_address = (multicast_group, 2345)
# Create the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Bind to the server address
sock.bind(server_address)
# Tell the operating system to add the socket to
# the multicast group on all interfaces.
group = socket.inet_aton(multicast_group)
src = bytearray(socket.inet_aton("192.168.0.4"))
mreq = bytearray(group)
mreq.extend(src)
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
mreq)
# Receive/respond loop
while True:
print('\nwaiting to receive message')
data, address = sock.recvfrom(1024)
print('received {} bytes from {}'.format(
len(data), address))
print(data)
print('sending acknowledgement to', address)
sock.sendto(b'ack', address)
Any help would be appreciated in figuring out what I am doing wrong.
So I figured out my own problem. I had checked iptables as being an issue early on, with no luck. But I fixed/modified several things along the way to address the issues being seen. Turns out the code above works just fine, it was firewalld/iptables rules blocking the receipt of multicast.
I want to do a packet sniffer in Python 3.5 which captures UDP, TCP and ICMP. This is a short example of it:
import socket
import struct
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST,0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
n=1
while(n<=400):
print('Number ', n)
data=s.recvfrom(65565)
packet=data[0]
address= data[1]
header=struct.unpack('!BBHHHBBHBBBBBBBB', packet[:20])
if(header[6]==6): #header[6] is the field of the Protocol
print("Protocol = TCP")
elif(header[6]==17):
print("Protocol = UDP")
elif(header[5]==1):
print("Protocol = ICMP")
n=n+1
The problem is that it only captures UDP packets :(
Output:
Number 1 Protocol = UDP Number 2 Protocol = UDP Number 3 Protocol = UDP Number 4 Protocol = UDP Number 5 Protocol = UDP Number 6 Protocol = UDP Number 7
There are 2 options:
The sniffer can only capture UDP packets.
I'm just receiving UDP packets.
I think that the most logical answer is my sniffer doesn't work correctly and it's just capturing UDP. Any idea?
I myself am in the stage of creating a python packet parser/sniffer and in my research I found that, to be able parse all the incoming packets like TCP, ICMP, UDP, ARP ..etc., you must not use the below socket type because socket.IPPROTO_IP gives out only IP packets and is a dummy protocol
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
rather you must use this and works best with Linux systems
s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
You are using "gethostbyname" and this method obtains one between all ip adresses in your computer.
In place of this method you must use "gethostbyname_ex" that obtains a list with several addreses. You must select the address used for the browser, and you will capure TCP, UDP and ICMP packets.
This topic has been covered in a good amount of detail here, unfortunately, I'm still running into some trouble.
I'm trying to subscribe to a stream of motion-capture data from a windows box on my network. The mo-cap server is broadcasting over my network, and I can pick up the data with wireshark on my OS X machine ('the target'). I see that a message originating from the origin ip 204.102.224.2 (windows) broadcasting on 239.255.42.99 via UDP, to port 1511 is going out, as desired.
My python code is as follows:
PORT = 1511
MULTICAST_ADDRESS = '239.255.42.99'
SOCKET_BUFSIZE = 1024
datasock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
datasock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
datasock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasock.bind((MULTICAST_ADDRESS, PORT))
mreq = struct.pack("=4sl", socket.inet_aton(MULTICAST_ADDRESS), socket.INADDR_ANY)
datasock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
datasock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
datasock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFSIZE)
datasock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while 1:
data = datasock.recv(rx.MAX_PACKETSIZE)
packet = rx.unpack(data, version=version)
if type(packet) is rx.SenderData:
version = packet.natnet_version
#print("NatNet version received:", version)
if type(packet) in [rx.SenderData, rx.ModelDefs, rx.FrameOfData]:
packet_dict = packet._asdict()
all_bodies = packet_dict['rigid_bodies']
for body in all_bodies:
contortion = body._asdict()['orientation']
euler = Quat([elem for elem in contortion]).equatorial
I think for my current issue it is safe to ignore some of the code in the loop, as some of it derives from a helper library I'm using (optirx). Note that I didn't start out with all the options, but decided to include all of them to see what should stay and what can go. I've tried various combinations and permutations. I'm on OS X 10.10.3
The problem is here:
datasock.bind((MULTICAST_ADDRESS, PORT))
You shouldn't bind the socket to the multicast address. You should bind it to '0.0.0.0'. The setsockopt call with IP_ADD_MEMBERSHIP takes care of joining the multicast group.
I have two ethernet cards, and I would like to send out UDP broadcast message on the local network(192.168.0.255) but it seams that the UDP message send out only the first ethernet card.
ip ="255.255.255.255"
UDPSocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP )
UDPSocket.setsockopt( socket.SOL_SOCKET, socket.SO_BROADCAST, True )
UDPSocket.settimeout( timeout )
UDPSocket.sendto( msg.xmlmsg, ( ip, UDPport ) )
UDPSocket.close()
How can I specified that witch subnet would I send the message?
You have to bind the socket to a local address.
For a non-broadcast UDP socket you usually bind to the any address (0.0.0.0). You can also choose a particular port or let the system choose one for you (port 0). If you don't bind the socket explicitly it will be bound automatically to 0.0.0.0:0 on the first send.
To do broadcast is always recommended to do the bind explicitly and specify your own local address of the selected interface. If you do not need a particular port you can use port 0.
The accepted answer doesn't work for me, trying to send ICMP ECHO requests on a raw socket. I had to do this:
target = '8.8.8.8'
packet = bytearray(...)
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sock.setsockopt(socket.SOL_SOCKET, 25, 'eth0') # or whatever the name of the interface is
# 25 = SO_BINDTODEVICE
# Some Python installations will have the constant IN.SO_BINDTODEVICE which you can use here, YMMV
sock.sendto(packet, (target, 1))
How do you send and receive UDP multicast in Python? Is there a standard library to do so?
This works for me:
Receive
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IS_ALL_GROUPS = True
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
print sock.recv(10240)
Send
import socket
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
# For Python 3, change next line to 'sock.sendto(b"robot", ...' to avoid the
# "bytes-like object is required" msg (https://stackoverflow.com/a/42612820)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
It is based off the examples from http://wiki.python.org/moin/UdpCommunication which didn't work.
My system is...
Linux 2.6.31-15-generic #50-Ubuntu SMP Tue Nov 10 14:54:29 UTC 2009 i686 GNU/Linux
Python 2.6.4
Multicast sender that broadcasts to a multicast group:
#!/usr/bin/env python
import socket
import struct
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.sendto('Hello World!', (MCAST_GRP, MCAST_PORT))
if __name__ == '__main__':
main()
Multicast receiver that reads from a multicast group and prints hex data to the console:
#!/usr/bin/env python
import socket
import binascii
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except AttributeError:
pass
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
sock.bind((MCAST_GRP, MCAST_PORT))
host = socket.gethostbyname(socket.gethostname())
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host))
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_GRP) + socket.inet_aton(host))
while 1:
try:
data, addr = sock.recvfrom(1024)
except socket.error, e:
print 'Exception'
else:
hexdata = binascii.hexlify(data)
print 'Data = %s' % hexdata
if __name__ == '__main__':
main()
Better use:
sock.bind((MCAST_GRP, MCAST_PORT))
instead of:
sock.bind(('', MCAST_PORT))
because, if you want to listen to multiple multicast groups on the same port, you'll get all messages on all listeners.
In order to Join multicast group Python uses native OS socket interface. Due to portability and stability of Python environment many of socket options are directly forwarded to native socket setsockopt call. Multicast mode of operation such as joining and dropping group membership can be accomplished by setsockopt only.
Basic program for receiving multicast IP packet can look like:
from socket import *
multicast_port = 55555
multicast_group = "224.1.1.1"
interface_ip = "10.11.1.43"
s = socket(AF_INET, SOCK_DGRAM )
s.bind(("", multicast_port ))
mreq = inet_aton(multicast_group) + inet_aton(interface_ip)
s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, str(mreq))
while 1:
print s.recv(1500)
Firstly it creates socket, binds it and triggers triggers multicast group joining by issuing setsockopt. At very end it receives packets forever.
Sending multicast IP frames is straight forward. If you have single NIC in your system sending such packets does not differ from usual UDP frames sending. All you need to take care of is just set correct destination IP address in sendto() method.
I noticed that lot of examples around Internet works by accident in fact. Even on official python documentation. Issue for all of them are using struct.pack incorrectly. Please be advised that typical example uses 4sl as format and it is not aligned with actual OS socket interface structure.
I will try to describe what happens underneath the hood when exercising setsockopt call for python socket object.
Python forwards setsockopt method call to native C socket interface. Linux socket documentation (see man 7 ip) introduces two forms of ip_mreqn structure for IP_ADD_MEMBERSHIP option. Shortest is form is 8 bytes long and longer is 12 bytes long. Above example generates 8 byte setsockopt call where first four bytes define multicast_group and second four bytes define interface_ip.
Just another answer to explain some subtle points in the code of the other answers:
socket.INADDR_ANY - (Edited) In the context of IP_ADD_MEMBERSHIP, this doesn't really bind the socket to all interfaces but just choose the default interface where multicast is up (according to routing table)
Joining a multicast group isn't the same as binding a socket to a local interface address
see What does it mean to bind a multicast (UDP) socket? for more on how multicast works
Multicast receiver:
import socket
import struct
import argparse
def run(groups, port, iface=None, bind_group=None):
# generally speaking you want to bind to one of the groups you joined in
# this script,
# but it is also possible to bind to group which is added by some other
# programs (like another python program instance of this)
# assert bind_group in groups + [None], \
# 'bind group not in groups to join'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# allow reuse of socket (to allow another instance of python running this
# script binding to the same ip/port)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('' if bind_group is None else bind_group, port))
for group in groups:
mreq = struct.pack(
'4sl' if iface is None else '4s4s',
socket.inet_aton(group),
socket.INADDR_ANY if iface is None else socket.inet_aton(iface))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print(sock.recv(10240))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--port', type=int, default=19900)
parser.add_argument('--join-mcast-groups', default=[], nargs='*',
help='multicast groups (ip addrs) to listen to join')
parser.add_argument(
'--iface', default=None,
help='local interface to use for listening to multicast data; '
'if unspecified, any interface would be chosen')
parser.add_argument(
'--bind-group', default=None,
help='multicast groups (ip addrs) to bind to for the udp socket; '
'should be one of the multicast groups joined globally '
'(not necessarily joined in this python program) '
'in the interface specified by --iface. '
'If unspecified, bind to 0.0.0.0 '
'(all addresses (all multicast addresses) of that interface)')
args = parser.parse_args()
run(args.join_mcast_groups, args.port, args.iface, args.bind_group)
sample usage: (run the below in two consoles and choose your own --iface (must be same as the interface that receives the multicast data))
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.1' '224.1.1.2' '224.1.1.3' --bind-group '224.1.1.2'
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.4'
Multicast sender:
import socket
import argparse
def run(group, port):
MULTICAST_TTL = 20
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
sock.sendto(b'from multicast_send.py: ' +
f'group: {group}, port: {port}'.encode(), (group, port))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--mcast-group', default='224.1.1.1')
parser.add_argument('--port', default=19900)
args = parser.parse_args()
run(args.mcast_group, args.port)
sample usage: # assume the receiver binds to the below multicast group address and that some program requests to join that group. And to simplify the case, assume the receiver and the sender are under the same subnet
python3 multicast_send.py --mcast-group '224.1.1.2'
python3 multicast_send.py --mcast-group '224.1.1.4'
Have a look at py-multicast. Network module can check if an interface supports multicast (on Linux at least).
import multicast
from multicast import network
receiver = multicast.MulticastUDPReceiver ("eth0", "238.0.0.1", 1234 )
data = receiver.read()
receiver.close()
config = network.ifconfig()
print config['eth0'].addresses
# ['10.0.0.1']
print config['eth0'].multicast
#True - eth0 supports multicast
print config['eth0'].up
#True - eth0 is up
Perhaps problems with not seeing IGMP, were caused by an interface not supporting multicast?
To make the client code (from tolomea) work on Solaris you need to pass the ttl value for the IP_MULTICAST_TTL socket option as an unsigned char. Otherwise you will get an error.
This worked for me on Solaris 10 and 11:
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
ttl = struct.pack('B', 2)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
GumD and GumC https://github.com/futzu/gumd
I use GumD mostly for video, but you can use any kind of file.
pip3 install gumd
gumd (Daemon)
>>>> from gumd import GumD
>>>> gumd =GumD('235.35.3.5:3535',1)
>>>> gumd.mcast("/home/a/stuff.txt")
# Can also use http(s), UDP and multicast
stream uri: udp://#235.35.3.5:3535
>>>>
Use gumc (Client)
>>>> from gumc import GumC
>>>> gumc = GumC("udp://#235.35.3.5:3535")
>>>> data = gumc.read(8)
>>>> data
b'Helloooo'
tolomea's answer worked for me. I hacked it into socketserver.UDPServer too:
class ThreadedMulticastServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
def __init__(self, *args):
super().__init__(*args)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)