我试图以root身份运行 icmpv6 ping数据包(Linux上的python 2.7)
我知道 sendto 在ipv4的情况下使用两个元组结构(并且它可以工作)并且知道ipv6使用4元组结构。我仍然无法让它发挥作用。
导致“无效参数”或“socket.gaierror:[Errno -2]名称或服务未知”
以下是一个显示我正在尝试的最基本的示例。如果我能在ipv6 ie :: 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代码(如评论中所述)
def main(dest_name):
dest_addr = socket.gethostbyname(dest_name)
icmp = socket.getprotobyname('icmp')
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
print "sent to " + dest_name#str(dest[4])
send_socket.sendto('', (dest_addr, 0))
send_socket.close()
if __name__ == '__main__':
main('www.google.com')
更新3:
当我运行带有dest [4]作为唯一参数的v6版本(没有字符串,只有元组和NO端口)时,在我的机器(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
我不确定为什么它仍会产生无效的参数
答案 0 :(得分:2)
您最初的问题是像2元组这样奇怪的东西,其第一个成员是4元组地址的Python字符串表示,甚至不能接近指定地址的有效方法。
你可以通过使用dest[4]
本身 - 也就是你作为getaddrinfo
的sockaddr部分得到的元组 - 作为地址来解决这个问题。 (正如Sander Steffann的回答所解释的那样,你并没有完全按照这种方式做到这一点。但在你的情况下,至少对于'::1'
或'localhost'
以及你指定的其他值,你会得到返回要使用的正确值。)您还应该使用addrs[0]
而不是addrs[2]
。
无论如何,在您的更新3 中,您似乎已经完成了这一步,并且您获得了socket.error: [Errno 22] Invalid argument
。但是sendto
有两个参数,而其他的参数是无效的:''
不是有效的ICMP6数据包,因为它没有ICMP6报头中。
您可以通过connect
dest[4]
send
来轻松测试,这将成功,然后执行简单的EINVAL
,这将失败并出现相同的错误。
出于某种原因,在Fedora 10(古老的Linux)上,无论如何这个电话似乎都成功了。我不知道电线上发生了什么(如果有的话)。但是在Ubuntu 13.10(当前的linux)上,它与ENOBUFS
失败了,完全一样。在OS X 10.7.5和10.9.0上,它以sendto
失败,这很奇怪。在所有这三种情况下,如果我将connect
分为send
和send
,那么'\x80\0\0\0\0\0\0\0'
就会失败。
ENETUNREACH
是有效的ICMP6数据包(没有数据的Echo服务请求标头)。如果我使用它而不是空字符串,它现在适用于所有四台机器。
(当我尝试在互联网上点击某些东西时,我仍然会得到EHOSTUNREACH
或{{1}},因为我没有IPv6可路由的连接。)
答案 1 :(得分:1)
您正在寻找的所有答案几乎都在manual。
首先,端口号是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