Python 原生 IPv6 套接字错误
我在用Python处理原始IPv6套接字时遇到了一些问题。我是通过以下方式连接的:
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)
这里的self._interface是我的本地地址,具体是"fe80::fa1e:dfff:fed6:221d"。当我尝试这样做时,出现了以下错误:
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
如果我把self._interface设置为我的IPv6本地地址("::1"),我实际上可以绑定这个地址,但却无法发送任何东西:
self._socket.sendall(data)
File "<string>", line 1, in sendall
socket.error: [Errno 39] Destination address required
为什么原始套接字需要一个目标地址呢?有没有人用过Python的原始IPv6套接字,能帮我理解为什么会出现这种情况吗?
3 个回答
这段代码提供了一个可以直接访问第二层网络协议的原始套接字。不过不幸的是,OSX系统不支持socket.PF_PACKET这个功能...
soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) #create the raw-socket
soc.bind(("lo0", 0))
soc.send(packet)
我不太明白你是怎么把 bind
和 sendall
结合在一起的。在我看来,bind
是用在服务器套接字上的,而 sendall
需要一个连接。你是不是想说 connect
而不是 bind
呢?
无论如何,IPv6 中与 INADDR_ANY 相对应的东西是 IN6ADDR_ANY_INIT。根据手册,Python 并没有为它定义一个常量,但它和 '::'
(全零)是一样的。
这个方法对我有效(以管理员身份):
>>> import socket
>>> s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW)
>>> s.bind(('::', 0))
编辑:
哎呀,我刚开始没注意到你其实已经把套接字绑定到一个地址了。不过你的第二个问题很明显:你必须先连接到某个地址,才能发送数据。或者使用 sendto
并指定一个地址。这和 IPv4 是一样的。
虽然这个问题已经有点老了,但我想加一个有效的答案,帮助以后遇到这个问题的人。
关键问题有:
原始套接字没有绑定和连接到其他套接字。此外,使用sendto是正确的方式。
而且,对于ipv6数据包来说,目的地址需要四个部分的结构,而ipv4只需要两个部分。
最后,在Linux mint 15上,处理ipv6数据包的规则更严格。如果你尝试发送一个空的icmpv4回显请求,python会允许你这样做,并发送一个没有意义的数据包。而对于ipv6来说,如果你尝试发送一个空的数据包,它会直接报错,提示'无效参数'。因此,发送ipv6数据包时也需要一个有效的请求。下面的例子展示了如何为ipv6发送一个有效的ping回显请求到回环地址。
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')