I am trying to create socket that can receive multicast UDP packages on my Ubuntu using Python 2.7.3.
I have based my code on https://stackoverflow.com/a/1794373/1444854
Unfortunately I keep getting the same error:
self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument
This is the code I have used
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind(("", 12345))
self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
Where for 'mreq' I have tried various stuff, of which I show a few here. For the format in the struct examples I have used both with and without the network byte order indicator ('!'). The native one seems to double the size (from 8 to 16). I have also tried both signed and unsigned longs ('l' and 'L').
group = "127.0.0.1" # Or any other ipv4 address...
mreq = socket.inet_aton(group) + socket.inet_aton("0.0.0.0")
mreq = struct.pack("4sL", socket.inet_aton(self.group), socket.inet_aton("0.0.0.0"))
mreq = struct.pack("4sL", socket.inet_aton(self.group), socket.htonl(socket.INADDR_ANY))
mreq = struct.pack("4sL", socket.inet_aton(self.group), socket.INADDR_ANY)
At this point I do not know what the problem is, and could use some help with this.
I thought that the wrong 'mreq´ would provide an issue, but after so many tries I feel there must be something else I am missing.
Any help is appreciated. If more information is needed, I will gladly provide it.
EDIT:
The problem, which I totally overlooked, was the fact that I needed a proper multicast interface. Using 'ifconfig wlan0' or any other interface, you can check that MultiCast is in fact enabled. Furthermore I think any address between 224.3.* and 224.250.* has not been assigned for other uses. The 'mreq' that works for me is
mreq = struct.pack("!4sl", socket.inet_aton(self.group), socket.INADDR_ANY)
This thread is old but it might help to know that IPV4 Multicast addresses use reserved class D address range: 224.0.0.0 through 239.255.255.255
If IP address is out of this range, setsockopt will throw 'Invalid argument' error.
cheers
Related
Basing on MC receiver from stack:
How do you UDP multicast in Python?
I would like to completely understand what is going on. Here is what I understand and what not:
As far as I understand: socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) => creating socket with IP proto ver 4, that will receive MC datagrams using UDP.
socket_name.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) this is responsible for setting parameter that socket could use the same address
(and in this case, port, because SO_REUSEADDR = SO_REUSEPORT for Multicasts).
(Paragraph multicast ~ How do SO_REUSEADDR and SO_REUSEPORT differ?)
if IS_ALL_GROUPS: socket_name.bind(('', MCAST_PORT)) means that if IS_ALL_GROUPS is true, bind socket to any addres, and else: socket_name.bind((MCAST_GRP, MCAST_PORT)) means bind socket to specific given IP address.
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) means convert IP address to binary form and socket_name.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) means that we add membership of group and after this line packets starting to arrive.
However I don't understand some things.
Why when I used IS_ALL_GROUPS = true where MCAST_GRP = '239.0.1.104' program could start and when the parameter was false, it didn't bound to specific multicast address? My logic is that when the parameter is true it binds to any MCast addres he gets from IGMP join messages and when parameter is false, it binds to specific given address. Am I correct?
I have multithread program that analyses bitrate to which i provide more than one address in form of list. When I set IS_ALL_GROUPS to false, program works correctly printing out e.g. 10.5, 4.5, 5.0 where each result is bitrate of one stream from unique address, however all of addresses share the same port 12345. When I set IS_ALL_GROUPS to true, program sums up results giving 20.0, 20.0, 20.0, do you know what may be the cause?
import socket
import struct
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True
socket_name = 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
socket_name.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
socket_name.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:
print socket_name.recv(10240)
I think there are a few things going on:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
should probably be:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
the only "datagram" based protocol for IPv4 is UDP, the protocol field is just for "raw sockets" which are used by programs like tcpdump. this shouldn't matter though.
next:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
just allows multiple sockets to be bound to this same (addr, port) combination at once, i.e. you can run multiple copies of your program at once, or in quick succession. which I think is what you're saying
next:
sock.bind((host, port))
says you want to receive packets destined for the a given address and port. the special address '' (i.e. INADDR_ANY) means that the kernel shouldn't do any filtering on address. you probably don't want to bind to any specific address at this stage, see What does it mean to bind a multicast (UDP) socket?
next:
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
I'd suggest using "=4sl" for the format string as this gives me 8 bytes, rather than 16, and is consistent with a C struct ip_mreq on my (Linux) system. this difference in padding might just be a 32/64bit issue, and doesn't seem to hurt
to summarise: 1. the setsockopt(IP_ADD_MEMBERSHIP) call has asked your kernel's network stack to arrange (via IGMP) for multicast packets sent to MCAST_GRP to be delivered to a network interface attached to your host. 2. the bind() call has arranged for packets received via the network interface to make it to the socket in your process. it might be important that the bind() specifies INADDR_ANY so that all multicast packets that arrive are delivered to your process rather than being filtered
a couple of command lines that might be useful are:
tcpdump -i eth0 -vv 'ip proto 2'
which dumps IGMP packets, and:
tcpdump -i eth0 -vv 'net 224.0.0.0/4'
which dumps multicast packets
I'm not sure what your "multithread analysis program" is doing, as you've not given any relevant info. your posted code is also wrong, presumably socket_name and sock are the same? I'd also suggest using Python 3 as Python 2 is dying
Ok guys, I can't understand this code:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
What does:
socket.AF_INET
socket.SOCK_STREAM
do?
I really read everything abount them, but I can't understand what dows they do, could you please explain me, them in simple words?Thanks for read, have a great day!
TL;DR
socket.AF_INET = The type of socket address
socket.SOCK_STREAM = The type of socket
Explanation
Whenever you provide socket.AF_INET to your server you instruct it to listen to an IPv4 address, and to your client to connect to an IPv4 address. This will work. Same for IPv6. However mixing them up doesn't.
That would be the same me waiting for you to talk to me on StackOverflow while you send me messages by email. We are not looking at the same spot, so we won't be able to communicate.
Same for socket.SOCK_STREAM (and the other values). The difference lies in the fact that this tells python's socket how we are going to communicate. socket.SOCK_STREAM will be TCP, while socket.SOCK_DGRAM will be UDP.
Let's come back to our "real world" example and let's imagine we agreed on communicating by email. I could expect either one email from you (explaining me everything you have to tell) or several (with a part of what you have to say in each email). That's the same as TCP vs UDP.
References
Well, I guess you have read both:
python's socket module
python's socket How to
Potentially:
SO: What is Address Family?
Wikipedia: IPv4
Also, I guess:
Difference Between Socket Types
(and the references therein)
Wikipedia: Network socket - Types
Super long explanation but mostly testing
So. If after all that you don't understand. Let's try:
# server.py
import socket
s = socket.socket()
s.bind(('localhost', 5050))
s.listen(5)
while True:
(clientsocket, address) = s.accept()
print 'client connected'
And:
# client.py
import socket
s = socket.socket(socket.AF_INET)
s.connect(('127.0.0.1', 5050))
print "Yeah! I'm connected :)"
So far, everything as in the how to.
We launch our server:
$ python server.py
And then we launch our client:
$ python client.py
Yeah! I'm connected :)
Everything works fine. That's good.
Now, lets change our client:
# client.py
import socket
s = socket.socket(socket.AF_INET6)
s.connect(('127.0.0.1', 5050))
print "Yeah! I'm connected :)"
And relaunch our new client:
$ python client.py
Traceback (most recent call last):
File "client.py", line 4, in <module>
s.connect(('127.0.0.1', 5050))
File "/.../lib/python2.7/socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 65] No route to host
Aie! Everything breaks!
But what happens? 127.0.0.1:5050 is an IPv4 address, hence the socket module tells us it's not happy about what we are doing! Indeed, we said our connection will be using an IPv6 address but we are providing it with an IPv4 address.
Okay... But if I use the same address but in IPv6, will it work? Let's try by changing our client (you could check out this SO answer for the equivalent of 127.0.0.1 for IPv6):
# client.py
import socket
s = socket.socket(socket.AF_INET6)
s.connect(('::1', 5050))
print "Yeah! I'm connected :)"
and our server:
# server.py
import socket
s = socket.socket(socket.AF_INET6)
s.bind(('::1', 5050))
s.listen(5)
while True:
(clientsocket, address) = s.accept()
print 'client connected'
We relaunch our server and our client:
$ python client.py
Yeah! I'm connected :)
Success!
The same procedure could be used to understand/test the socket.SOCK_STREAM parameter (but I think my answer is already long enough).
Hope this helped :).
socket.STREAM is the kind of socket you want. In this case, you are looking to stream bytes to and from the host you want to connect to, rather than just issue a one-time call. This means that the socket will listen for bytes until it receives an empty byte b'', at which point it will terminate the connection (because the stream is complete).
I would imagine you aren't too worried about the type of socket, so low-level understanding here isn't paramount, nor could I give you a particularly satisfactory answer to that, either.
socket.AF_INET is the AddressFamily, ipv4 or ipv6. This tells sockets what kind of address to expect. You will most likely use ipv4, so (host, port) tuple will work just fine.
AF_INET is well described in there. It is basically the method you are using for sending data over to the other socket. SOCK_STREAM basically describes that you are sending using TCP and essentially describes rules for the endpoint to which you are sending and recieving data (IP adress and port number).
But since you seem confused over these terms I'd suggest just just think of them as specifications on how you are going to transmit data between your two socket endpoints.
I am trying to receive IPv6 multicast packets (sent to the ff02::1 address) on Windows using this python 2.7 code-
import socket
import win_inet_pton
import struct
socket.IPPROTO_IPV6=41 #because using python 2.7 on wondows
PORT = 1234
UDP_BROADCAST_IPv6 = "ff02::1"
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("",PORT)) # not working with "::" either
# Join multicast group
addrinfo = socket.getaddrinfo(UDP_BROADCAST_IPv6, None)[0]
group = socket.inet_pton(addrinfo[0], addrinfo[4][0])
mreq = group + struct.pack('#I', 0)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
while True:
msg=sock.recv(1024)
print msg
I send packets from another computer that is connected to my computer via Ethernet; in addition, my computer also has a WiFi interface. Although I'm able to see the relevant packets when sniffing the Ethernet connection with Wireshark, the packets are not received by this code.
However, when I disable the WiFi network card, the packets are received.
This makes me think that while the WiFi interface is enabled the code listens only to packets from that interface.
I read that binding to "" should enable receiving packets from all network interfaces, but for some reason it doesn't work for me.
Does anyone have any idea to something that I have forgotten to do? or a different way to solve this?
Thanks!
Solved it :)
So apparently IPv6 doesn't listen to multicast from all interfaces. This syntax
mreq = group + struct.pack('#I', 0)
was wrong. According to this, mreq is composed of the group id and the interface id, where 0 is the default interface (in my case- WiFi). In order to listen to multicast from other interfaces, the network interface index should be specified.
The network interface index is the number thet appears after the % in the ipv6 address when running ipconfig, and can also be found running "route print" in cmd.
I used this code to find it on python:
import netifaces as ni
import _winreg as wr # use "winreg" in python3
def get_ethernet_ipv6_ifindex():
x=ni.interfaces()
con_names=get_connection_name_from_guid(x)
ethernet_index= con_names.index('Ethernet')
addresses= ni.ifaddresses(x[ethernet_index])
brod_addr=addresses[socket.AF_INET6][-1]["broadcast"]
return int(brod_addr[brod_addr.find("%")+1:])
"""
Taken from the very helpful https://stackoverflow.com/questions/29913516/how-to-get-meaningful-network-interface-names-instead-of-guids-with-netifaces-un
"""
def get_connection_name_from_guid(iface_guids):
iface_names = ['(unknown)' for i in range(len(iface_guids))]
reg = wr.ConnectRegistry(None, wr.HKEY_LOCAL_MACHINE)
reg_key = wr.OpenKey(reg, r'SYSTEM\CurrentControlSet\Control\Network\{4d36e972-e325-11ce-bfc1-08002be10318}')
for i in range(len(iface_guids)):
try:
reg_subkey = wr.OpenKey(reg_key, iface_guids[i] + r'\Connection')
iface_names[i] = wr.QueryValueEx(reg_subkey, 'Name')[0]
except WindowsError:
pass
return iface_names
And then-
mreq = group + struct.pack('#I', get_ethernet_ipv6_ifindex())
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 am having some problems using raw IPv6 sockets in python. I connect via:
if self._socket != None:
# Close out old socket first
self._socket.close()
self._socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW)
self._socket.bind((self._interface,0))
self._socket.sendall(data)
where self._interface is my local address; specifically "fe80::fa1e:dfff:fed6:221d". When trying this, I get the following error:
File "raw.py", line 164, in connect
self._socket.bind((self._interface,0))
File "<string>", line 1, in bind
socket.error: [Errno 49] Can't assign requested address
If I use my ipv6 localhost address for self._interface ("::1") I can actually bind the address, but can not send anything:
self._socket.sendall(data)
File "<string>", line 1, in sendall
socket.error: [Errno 39] Destination address required
Why would a raw socket need a destination address? Has anyone worked with raw IPv6 sockets in python, and can help me understand why this is happening?
Although this is an old question, i thought of adding an answer that works and helps any one who stumbles upon it latter.
The key problems are:
Raw sockets are not bound and connected to other sockets. Also sendto is the correct api to use.
Moreover, 4 tuple structure for destination address is required for ipv6 packets as opposed to two tuple ones for ipv4.
Lastly, the stack (at least on Linux mint 15) is more strict on ipv6 packets. If you try sending an empty icmpv4 echo request, python allows it and sends a meaning less packet on wire. Where as in case of ipv6, it simply gives error of 'invalid argument' when you try sending an empty packet. Hence a valid request is also required in case of ipv6. Following example does that all for ipv6 and sends a valid ping echo request to loop back address.
import socket
def main(dest_name):
addrs = socket.getaddrinfo(dest_name, 0, socket.AF_INET6, 0, socket.SOL_IP)
print addrs
dest = addrs[0]
# A minimal ICMP6-echo message (thanks to abarnert)
data = '\x80\0\0\0\0\0\0\0'
icmp = socket.getprotobyname('ipv6-icmp')
#print icmp
send_socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW, icmp)
print "sent to " + str(dest[4])
send_socket.sendto(data, dest[4])
send_socket.close()
if __name__ == '__main__':
main('::1')
I don't understand your combination of bind and sendall. In my understanding, bind is for server sockets and sendall requires a connection. Did you mean connect instead of bind?
Anyway, the IPv6 equivalent of INADDR_ANY is, according to the man page, IN6ADDR_ANY_INIT. Python does not define a constant for it, but this is the same as '::' (all zero).
This worked for me (as root):
>>> import socket
>>> s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW)
>>> s.bind(('::', 0))
EDIT:
Oops, I first did not saw that you actually managed to bind the socket to an address. However your second problem is obvious: You must first connect to some address before you can send data. Or use sendto with an address. This is not different from IPv4.
This code provides a raw socket with L2 access. Unfortunately OSX does not support the socket.PF_PACKET...
soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) #create the raw-socket
soc.bind(("lo0", 0))
soc.send(packet)