Python 确切地说,什么被传递为;地址;在ipv6的sendto中

Python 确切地说,什么被传递为;地址;在ipv6的sendto中,python,sockets,ipv6,icmp,sendto,Python,Sockets,Ipv6,Icmp,Sendto,我试图以root用户身份(Linux上的python 2.7)发送一个icmpv6 ping数据包 我知道,在ipv4的情况下,sendto使用了两元组结构(它可以工作),并且知道ipv6使用了四元组结构。我还是不能让它工作 它会导致“无效参数”或“socket.gairro:[Errno-2]名称或服务未知” 下面是一个简单的示例,显示了我的尝试。如果我能让它在ipv6的情况下与本地主机一起工作,我甚至可以:1 import socket def main(dest_name): #

我试图以root用户身份(Linux上的python 2.7)发送一个icmpv6 ping数据包

我知道,在ipv4的情况下,sendto使用了两元组结构(它可以工作),并且知道ipv6使用了四元组结构。我还是不能让它工作

它会导致“无效参数”或“socket.gairro:[Errno-2]名称或服务未知”

下面是一个简单的示例,显示了我的尝试。如果我能让它在ipv6的情况下与本地主机一起工作,我甚至可以:1

import socket

def main(dest_name):
    #dest_addr = socket.gethostbyname(dest_name)
    addrs = socket.getaddrinfo(dest_name, 0, socket.AF_INET6, 0, socket.SOL_IP)

    print addrs
    dest = addrs[2]

    port = 33434 # just some random number because of icmp
    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('', (str(dest[4]), port))
    send_socket.close()

if __name__ == '__main__':
    main('ipv6.google.com')
实际上,我尝试了addr列表中的每个元组,但结果是一样的

更新:

还尝试使用sendto的参数替换,但无论我使用本地主机还是google ipv6地址,都会导致无效参数

send_socket.sendto('', dest[4])
更新2:

作为参考,工作ipv4代码如下(如注释中所述)

更新3:

当我以dest[4]作为唯一参数(没有字符串,只有元组和端口)运行v6版本时,下面是我的机器(Mint 15)上的输出,其中包括打印接口

sudo python test_v6.py 
[(10, 1, 6, '', ('::1', 0, 0, 0)), (10, 2, 17, '', ('::1', 0, 0, 0)), (10, 3, 0, '', ('::1', 0, 0, 0))]
sent to ('::1', 0, 0, 0)
Traceback (most recent call last):
  File "test_v6.py", line 18, in <module>
    main('::1')
  File "test_v6.py", line 14, in main
    send_socket.sendto('', dest[4])
socket.error: [Errno 22] Invalid argument
sudopython测试_v6.py
[(10, 1, 6, '', ('::1', 0, 0, 0)), (10, 2, 17, '', ('::1', 0, 0, 0)), (10, 3, 0, '', ('::1', 0, 0, 0))]
已发送到('::1',0,0,0)
回溯(最近一次呼叫最后一次):
文件“test_v6.py”,第18行,在
main('::1')
文件“test_v6.py”,第14行,在main中
send_socket.sendto(“”,dest[4])
socket.error:[Errno 22]参数无效

我不知道为什么它仍然会产生无效的参数

您要寻找的所有答案几乎都在列表中

首先,端口号是
getaddrinfo
返回的信息的一部分。可以这样称呼:

def main(dest_name):
    # A minimal ICMP6-echo message (thanks to abarnert)
    data = '\x80\0\0\0\0\0\0\0'

    # Parameters for getaddrinfo
    req_port = 0
    req_family = socket.AF_INET6
    req_socktype = socket.SOCK_RAW
    req_proto = socket.getprotobyname('ipv6-icmp')

    # Resolve the name and get the addrinfo
    addrs = socket.getaddrinfo(dest_name, req_port, req_family, req_socktype, req_proto)

    # This gives me: [(30, 3, 58, '', ('2a00:1450:4013:c01::63', 0, 0, 0))]
    # Which is what you use in your calls to `socket` and `sendto`, like:
    success = False
    for addr in addrs:
        try:
            (family, socktype, proto, canonname, sockaddr) = addr
            send_socket = socket.socket(family, socktype, proto)
            sent = send_socket.sendto(data, sockaddr)
            send_socket.close()
        except socket.error:
            # Try the next address
            continue

        # Stop if it worked
        if sent == len(data):
            success = True
            break

    return success
现在运行
main('ipv6.google.com')
时,您可以使用
tcpdump
查看ping和回复:

01:14:46.763160 IP6 2a00:8640::5ce4 > 2a00:1450:4013:c01::63: ICMP6, echo request, seq 0, length 8
01:14:46.785060 IP6 2a00:1450:4013:c01::63 > 2a00:8640::5ce4: ICMP6, echo reply, seq 0, length 8

您最初的问题是,奇怪的事情,例如第一个成员是4元组地址的Python字符串表示的2元组,甚至不接近于指定地址的有效方法

您只需使用
dest[4]
本身,即作为
getaddrinfo
-的sockaddr部分返回的元组作为地址,就可以解决这个问题。(正如Sander Steffann的回答所解释的那样,您并没有完全清楚地做到这一点。但在您的情况下,至少对于
':1'
'localhost'
以及您指定的其他值,您将获得正确的值来使用。)您也可能应该使用
addrs[0]
而不是
addrs[2]

无论如何,在您的更新3中,您似乎完全做到了这一点,并且您得到的是
socket.error:[Errno 22]无效参数
。但是,
sendto
有两个参数,另一个参数无效:
'
不是有效的ICMP6数据包,因为它没有ICMP6头

您可以很容易地测试这一点,方法是首先将
连接到
dest[4]
,这将成功,然后执行一个普通的
发送
,这将失败并出现相同的错误

出于某种原因,在Fedora10(古代linux)上,调用似乎无论如何都会成功。我不知道电线上到底出了什么(如果有的话)。但是在Ubuntu13.10(当前的linux)上,它在
EINVAL
上失败了,这正是它应该做的。在OSX10.7.5和10.9.0上,它在ENOBUFS上失败,这很奇怪。在这三种情况下,如果我将
sendto
分为
connect
send
,则失败的是
send

'\x80\0\0\0\0\0\0\0'
是有效的ICMP6数据包(无数据的回显服务请求标头)。如果我用它代替你的空字符串,它现在可以在所有四台机器上工作


(当然,当我尝试在Internet上点击某个东西时,我仍然会收到
ENETUNREACH
EHOSTUNREACH
,因为我没有IPv6可路由连接。)

你能显示你的IPv4代码吗?我不知道为什么您希望
str(dest[4])
与IPv6的4元组一起工作,而与IPv4的2元组不一起工作。无论哪种方式,这都会创建Python元组的字符串表示形式,看起来与
sendto
想要的完全不同。同时,请显示您得到的实际异常。当我使用
dest[4]
(先使用或不使用
dest[4][1]=port
)尝试这段代码时,它对本地主机地址成功,并为Google返回一个
enetureach
错误(这是有意义的,因为我没有IPv6 internet连接)。最后,你能将IPv6 TCP连接到Google吗?你能从shell中
traceroute6
ping6
它们吗?感谢@abarnert,添加了ipv4代码我第一次将dest[4]转换为string,错误是“函数需要字符串或缓冲区,而不是元组”。我后来才意识到,这是因为我不必要地附加了另一个参数“port”。这都是真的,但如果你看他的代码,如果他只是将
sendto
行修复为使用
send_socket.sendto('',dest[4])
,正如我在一篇评论中所建议的,正如他所说的,他已经做到了(参见他问题中的更新和更新3)。所以,这不可能解决他的问题。谢谢,你是对的。只是想教人们如何正确地编写网络代码:-)我完全忘记在我的示例中指定
数据
,作为一个简单的工作示例,它实际上可能是
'\x80\0\0\0\0'
。固定的!数据部分在这里并不重要。十多年来,我一直在为网络处理器和许多其他网络堆栈编写代码,恕我直言,我不太明白这里要教什么。的确,我主要是用c编写代码的,而且我对python还不熟悉。在上面给出的例子中,我实际上使用了数据[4],它就是sock_addr结构,没有任何改进。在我的实际代码中,我也在构建ip头
01:14:46.763160 IP6 2a00:8640::5ce4 > 2a00:1450:4013:c01::63: ICMP6, echo request, seq 0, length 8
01:14:46.785060 IP6 2a00:1450:4013:c01::63 > 2a00:8640::5ce4: ICMP6, echo reply, seq 0, length 8