Python 原生 IPv6 套接字错误

1 投票
3 回答
3829 浏览
提问于 2025-04-16 05:45

我在用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 个回答

0

这段代码提供了一个可以直接访问第二层网络协议的原始套接字。不过不幸的是,OSX系统不支持socket.PF_PACKET这个功能...

soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) #create the raw-socket
soc.bind(("lo0", 0))

soc.send(packet)
0

我不太明白你是怎么把 bindsendall 结合在一起的。在我看来,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 是一样的。

2

虽然这个问题已经有点老了,但我想加一个有效的答案,帮助以后遇到这个问题的人。

关键问题有:

原始套接字没有绑定和连接到其他套接字。此外,使用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')

撰写回答