Basing on multicast receiver from:
How do you UDP multicast in Python?
I adjusted it to my needs considering the fact I wanted to stream 10,5 MB test file video using VLC and analyse packets sent by it.
Here is configuration of VLC:
https://imgur.com/lAJIbTx
The problem is that I see on wireshark 769 captured packets where python prints out that he received a little bit above 600. What may be cause? I believe my method of calculating packets may be wrong in python. Do you have any solutions?
Here is python vs wireshark packets shown:
https://imgur.com/n3NmKhg
import socket
import struct
MCAST_GRP = '239.200.200.1'
MCAST_PORT = 5252
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)
counter = 0
print("Entering while loop")
while True:
datagram = sock.recv(1328)
counter +=1
print(counter)
I would like to calculate bitrate by knowing amount of captured packets sent by multicast streamer and dividing this number by time of observation. But the thing is I want to be sure if amount of captured packets by python is correct, but so big divergence between wireshark and python looks like it is not correct.
The interesting thing is that if I'm understanding it correctly, even 769 packets are not enough. 769 * 1328 = 1.021.232 bytes which is about 1MB where video is 10.5MB. Am I thinking correctly?
Related
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.
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 with SOCK_RAW and no traffic at all with SOCK_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 to socket.bind()).
I am quite newbie in networking programming in python.
I use the script I found on the internet that that connects to multicast adress and recieve MPEG-TS packets. I see on wireshark that after sock.setsockopt command, MPEG-TS packets are arriving to my computer.
Screen from wireshark
https://imgur.com/XJDwd61
But the problem occurs when I want to print the sock.recv() result. I believe it's because of blocking state if I'm understanding the documentation properly. After uncommenting setblocking(0) I got 10035 error.
Do you have any clue what I need to add in order to print recieved data in terminal?
Thank you in advance.
I tried changing the buffer_size in sock.recv() to be less, equal and just how it is right now - above 1358 bytes which is amount of single datagram bytes.
import socket
import struct
import time
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
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)
#sock.setblocking(0)
print(f"Entering while loop")
while True:
time.sleep(1)
print(f"I'm in while loop")
print(sock.recv(4096))
Based on the example code from How do you UDP multicast in Python? when I try to run it on my system, it works fine. So, I changed it with your changes, this is the receiver.py code,
import socket
import struct
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
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)
print("Entering while loop")
while True:
print("I'm in while loop")
print(sock.recv(4096))
Notice, I slightly modified print statements and removed f as it was generating an error. And this is the sender.py code,
import socket
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
# 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)
sock.sendto(bytes("robot", 'UTF-8'), (MCAST_GRP, MCAST_PORT))
Notice that I am converting robot string into bytes with UTF-8 encoding, as this is necessary if using python3, it is not necessary in python2. This is my execution result,
As you can see when I invoke the sender, then the receiver does display robot string and loops through the while loop. It works fine.
In the case, if you are already receiving the multicast packets which you showed on your Wireshark, you need to ensure that those packets are being sent to the same port number on which you are listening, i-e 12345 in your case. If they are being sent to different port number [which is probably the case], then please change your receiver to listen on that port number to start receiving those packets.
Try this simple code, you can set less BUFF_SIZE if you want.
#BUFF_SIZE = 4096
BUFF_SIZE = 1024
data = b''
while True:
#chunk = s.recv(BUFF_SIZE)
#FOR UDP (#Saeed credits)
chuck = sock.recvfrom(BUFF_SIZE)
data += chunk
if len(chunk) < BUFF_SIZE:
break
print(data)
This question already has an answer here:
Duplicate packets in Python multicast receiver
(1 answer)
Closed 7 years ago.
On an Ubuntu 14.04 server I have two processes, each listening for multicast messages on the same port, but from different groups. I would not have expected this, but each sees traffic from both the group they want, and the other group.
As far as I can tell, this is known behavior (though I would call it a problem). I found this SO question, which provides some techniques for determining the multicast group that data is being received from, but it does not answer the question of why this is even happening in the first place. I would have thought that the underlying system network code would have filtered out messages on multicast groups I am not attempting to receive.
While I am mostly working in C++, I can provide some simple Python code to demonstrate the problem. In one terminal window I listen on multicast group 239.1.1.1, port 12345. In another terminal window on the same server I listen to multicast group 239.2.2.2, also port 12345. On a second machine, I transmit one multicast message on 239.1.1.1:12345, and a different message on 239.2.2.2:12345.
On svr3, the transmitter:
rcook#svr3:~$ mcast_snd 239.1.1.1 12345 "from 1.1.1"
multicasting from 1.1.1 to 239.1.1.1 port 12345
rcook#svr3:~$ mcast_snd 239.2.2.2 12345 "from 2.2.2"
multicasting from 2.2.2 to 239.2.2.2 port 12345
On svr2, window 1, the first receiver:
rcook#svr2:~$ mcast_rcv 239.1.1.1 12345
listening for multicast data on 239.1.1.1 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2
On svr2, terminal 2, the other receiver:
rcook#svr2:~$ mcast_rcv 239.2.2.2 12345
listening for multicast data on 239.2.2.2 port 12345
received 10 bytes: from 1.1.1
received 10 bytes: from 2.2.2
As you can see, both receivers receive both messages. Can someone explain why this is? And if there is a better way to configure the receivers to not receive messages from other groups, please share that too.
For reference, here is the code for mcast_rcv:
#!/usr/bin/python
import socket
import struct
import sys
if len(sys.argv) != 3:
print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>'
sys.exit(0)
mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', mcast_port))
mreq = struct.pack('4sl', socket.inet_aton(mcast_group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print 'listening for multicast data on', mcast_group, 'port', mcast_port
while True:
msg = sock.recv(10240)
print 'received', len(msg), 'bytes:', msg
And here is the code for mcast_snd:
#!/usr/bin/python
import socket
import sys
if len(sys.argv) != 4:
print 'usage:', sys.argv[0], '<multicast group>', '<multicast port>', '<mess
age>'
sys.exit(0)
mcast_group = sys.argv[1]
mcast_port = int(sys.argv[2])
message = sys.argv[3]
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
print 'multicasting', message, 'to', mcast_group, 'port', mcast_port
sock.sendto(message, (mcast_group, mcast_port))
The main difference with the Receving multiple multicast feeds on the same port - C, Linux is you are using python.
In mcast_rcv you can enable the group filter disabling the IP_MULTICAST_ALL option, letting the INADDR_ANY like this :
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', mcast_port))
mreq = struct.pack('4sl', socket.inet_aton(mcast_group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# disable mc_all
if hasattr(socket,'IP_MULTICAST_ALL') != True:
socket.IP_MULTICAST_ALL = 49
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_ALL, 0)
As the python I used doesnot define socket.IP_MULTICAST_ALL, it could be needed to define it.
Problem solved. I need to specify the multicast group to bind to in the receiver, not just the port. This SO question clued me in. By leaving the address as '' in the Python code, or INADDR_ANY in my C++, I am basically telling the OS that I want all messages on that port, regardless of group. The system is giving me exactly what I am asking for, like it always does.
If I replace the line sock.bind(('', mcast_port)) with sock.bind((mcast_group, mcast_port)) in mcast_rcv, the behavior is as I expect and want. Merely joining the multicast group (sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)) is not enough to filter out non-group messages on that port.
There is a script that opens a socket and read from it the multicast (from Multicast in Python)
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 1234
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 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:
print sock.recv(10240)
Everything is fine as long as I do not run parallel to the same script to another multicast group, but the ports are the same, for example
rtp://224.1.1.1:1234
rtp://224.1.1.2:1234
After starting the second script starts mess - the first script sees packets for the second and the second to first.
I tried to do as a mcast.py - a similar result.
Why is this happening and how to cure?
UPD Fix
-sock.bind(('', MCAST_PORT))
+sock.bind((MCAST_GRP, MCAST_PORT))
An application listening to all incoming connections on a port will get all messages to that port, no matter which application initiated multicast group membership. To mitigate this, have every application listen to the multicast address it's expecting data from, by specifying it as the first argument in the address tupel given to bind.
This following is a straightforward IPv4 UDP broadcast, followed by listening on all interfaces.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
sock.bind(("", 1337))
sock.sendto("hello world", ("255.255.255.255", 1337))
while True:
data, addr = sock.recvfrom(0x100)
print "received from {0}: {1!r}".format(addr, data)
I want to adjust this to send and receive both IPv4 and IPv6.
I've poked around and read as much as I can and believe the following is roughly the route I need to take:
Create an IPv6 socket.
Add the socket to a link or site local multicast group.
Send the UDP packets to the multicast address for the group in use.
Further info I have is that I may need to bind to several interfaces, and tell the socket using setsockopt() that it should also receive multicast packets. Lastly getaddrinfo() can be used across the board to gracefully "drop back" to IPv4 where IPv6 isn't available.
I have much of this implemented, but stumble primarily on the multicasting parts. A complete code example in Python, or vivid description of the constants and addresses needed are preferred.
Here's a link to python mcast demo, does both IPv4 and IPv6.
I'm currently asking a question over here that involves getting the multicast address of a received message, but the source code answers your question!
To listen:
# Initialise socket for IPv6 datagrams
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# Allows address to be reused
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Binds to all interfaces on the given port
sock.bind(('', 8080))
# Allow messages from this socket to loop back for development
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True)
# Construct message for joining multicast group
mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, "ff02::abcd:1"), (chr(0) * 16).encode('utf-8'))
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
data, addr = sock.recvfrom(1024)
and to send:
# Create ipv6 datagram socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
# Allow own messages to be sent back (for local testing)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True)
sock.sendto("hello world".encode('utf-8'), ("ff02::abcd:1", 8080))
This is for python3.6, with python 2.7 I don't think the encodes are necessary. Also in the struct.pack line, I've seen variations of "16s15s" such as "4s", but I don't know what it is and what I have worked for me!