I tried to bind a python tcp socket using an IPv6 address.
self.__addr = ('fe80::224:d7ff:fe9d:9800', 5050)
self.__type = socket.AF_INTE6
self.__sock = socket.socket(self.__type, socket.SOCK_STREAM)
for family, _, _, _, sockaddr in socket.getaddrinfo( self.__addr[0], self.__addr[1], 0, 0, socket.SOL_TCP ):
if family == self.__type:
self.__addr = sockaddr
break
self.__sock.bind( self.__addr )
self.__sock.listen(1)
I used the result from socket.getaddrinfo() as mentioned in other solutions but always get this error:
self.__sock.bind( self.__addr )
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument
Here's my ifconfig result for the network interface
wlan0 Link encap:Ethernet Hardware Adresse 00:24:d7:9d:98:00
inet Adresse:192.168.0.103 Bcast:192.168.0.255 Maske:255.255.255.0
inet6-Adresse: fe80::224:d7ff:fe9d:9800/64
Any idea why this error occurs?
Usually network prefixes don't overlap and every IP address is used only on a single interface. However for link-local the (same) prefix (fe80::/10
) is used on every network interface with IPv6 enabled. That means that based on just the link-local address the system cannot determine on which network interface to listen. You will have to specify the interface yourself.
When writing an address in string notation you can do that by appending %
followed by the id of the interface to the IPv6 address. As glglgl has shown, this could be e.g. fe80::224:d7ff:fe9d:9800%wlan0
. The interface name depends on the system that you are on. My OS X machine has interfaces en0
and en1
, my Linux box has eth0
and on a Windows box the interface name would be an integer.
In the socket function the interface is specified using the scope-id. This is part of the address tuple. In your example you use ('fe80::224:d7ff:fe9d:9800', 5050)
. This ok because for IPv6 the last two elements may be omitted. The full address tuple is e.g. ('fe80::224:d7ff:fe9d:9800', 5050, 0, 4)
where 4
is the scope-id.
You can test this with a small script like:
import socket
addrs = [('fe80::cafe:1%en0', 5050),
('fe80::cafe:1%en1', 5050)]
for addr in addrs:
print('Original addr: {}'.format(addr))
for res in socket.getaddrinfo(addr[0], addr[1], socket.AF_INET6,
socket.SOCK_STREAM, socket.SOL_TCP):
af, socktype, proto, canonname, sa = res
print('Full addr: {}'.format(sa))
print('')
On my system running this script shows:
Original addr: ('fe80::cafe:1%en0', 5050)
Full addr: ('fe80::cafe:1%en0', 5050, 0, 4)
Original addr: ('fe80::cafe:1%en1', 5050)
Full addr: ('fe80::cafe:1%en1', 5050, 0, 5)
The part after the %
isn't used anymore by the socket binding, it relies on the scope-id.
This does mean that the following bind
calls produce identical results:
s = socket.socket(af, socktype, proto)
s.bind(('fe80::cafe:1', 5050, 0, 4))
s.bind(('fe80::cafe:1%en0', 5050, 0, 4))
s.bind(('fe80::cafe:1%bla', 5050, 0, 4))
Best to use the raw values that you get back fron getaddrinfo
though. Depending on such undefined behaviour is not a good idea. A simple loop like this should work best:
addr = ('fe80::224:d7ff:fe9d:9800%wlan0', 5050)
s = None
for res in socket.getaddrinfo(addr[0], addr[1], socket.AF_INET6,
socket.SOCK_STREAM, socket.SOL_TCP):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
s.bind(sa)
s.listen(1)
except socket.error:
if s is not None:
s.close()
s = None
if s is None:
print('Socket opening/binding failed')
etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With