使用广播和回声进行主机发现

网络主机发现是试图获取连接到网络的计算机的地址。上周我写了一篇关于使用Perl进行单播方法的文章,该方法遍历网络子网中的每个地址,逐个发送消息到每个地址,以查看是否有任何主机响应。本周我一直在使用广播和回声的替代方法进行研究。
ICMP和回声
Internet控制消息协议(ICMP)是一种网络协议,网络设备使用它来相互协调。ICMP消息包含类型和代码,这些代码具有预定义的含义。
类型为8的ICMP消息表示回声请求,主机应使用类型为0(回声响应)的ICMP消息进行响应。为了发现网络上的主机,我可以向网络发送回声请求,并捕获收到的任何回声响应的IP地址。而不是遍历子网中可能的所有IP地址,我可以向广播IP地址:255.255.255.255
发送回声请求,消息将自动发送到网络上的每个主机。
如果你正在运行现代Linux,你可以在命令行中使用ping
测试此功能(其他版本可能不需要“-b”开关)
$ ping -b 255.255.255.255
WARNING: pinging broadcast address
PING 255.255.255.255 (255.255.255.255) 56(84) bytes of data.
64 bytes from 192.168.1.4: icmp_seq=1 ttl=64 time=92.9 ms
64 bytes from 192.168.1.4: icmp_seq=2 ttl=64 time=2.04 ms
64 bytes from 192.168.1.4: icmp_seq=3 ttl=64 time=136 ms
...
在这里,你可以看到我的网络上的另一个主机正在地址192.168.1.4
上响应
在Perl中实现回声
可以使用核心Perl模块实现ping。也就是说,如果安装了Perl,这个脚本应该可以工作
#!/usr/bin/perl
use strict;
use warnings;
use Socket;
use Net::Ping;
# the checksum must be correct else hosts will ignore the request
my $msg_checksum = Net::Ping->checksum(pack("C2 n3",8,0,0,0,1));
my $msg = pack("C2 n3", 8, 0, $msg_checksum, 0, 1);
socket(my $socket, AF_INET, SOCK_RAW, getprotobyname('icmp'));
setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 1);
send($socket, $msg, 0, sockaddr_in(0, inet_aton('255.255.255.255')));
bind($socket,sockaddr_in(0,inet_aton(0)));
while (1)
{
my $addr = recv($socket, my $data, 1024, 0);
my ($tmp, $tos, $len, $id, $offset, $tt, $proto, $checksum,
$src_ip, $dest_ip, $options) = unpack('CCnnnCCnNNa*', $data);
if ($dest_ip != 4294967295) # destination != 255.255.255.255
{
my ($port, $peer) = sockaddr_in($addr);
printf "%s bytes from %s\n", length($data), inet_ntoa($peer);
}
}
此脚本首先导入Socket
和Net::Ping
模块——这两者都是Perl核心分发的一部分。它使用来自Net::Ping
的checksum
函数来计算消息校验和。校验和很重要,因为如果它不正确,主机将不会响应。脚本将代码、类型、校验和偏移量打包到$msg
中。
然后脚本创建一个广播套接字,并将消息发送到广播地址(255.255.255.255
)。然后套接字绑定到网络地址,脚本进入一个while循环,尝试使用recv
从套接字读取数据。任何接收到的数据都会被解包,并将数据包地址保存到$addr
中。
解包消息中的源和目的IP字段被存储为32位整数,因此脚本忽略与广播地址整数匹配的目的地址的包,因为这个消息是由脚本发送的。然后脚本解码数据包地址并打印结果。
在我的网络上运行此脚本,我可以看到与ping返回相同的同一主机
$ sudo ./livehost_echo
28 bytes from 192.168.1.4
指纹识别主机
这种技术的主要问题是它只能发现对广播请求做出响应的主机,而许多主机并不这样做。例如,Chromebook、智能手机和Linux机器通常不会响应(OSX机器和许多Windows版本则会)。这可以是一个优势:因为广播的响应率与单播不同,回声脚本可以与单播结合使用来指纹主机。如果一个机器对单播消息做出响应但不对广播做出响应,我们可以了解到有关该主机的某些信息。例如,如果我在我的家庭网络中使用livehost_scanner脚本
sudo $(which perl) livehost_scanner
Gateway IP: 192.168.1.1
Starting scan
192.168.1.1 10:0d:7f:81:31:c2
192.168.1.2 5c:c5:d4:47:0a:13 (this machine)
192.168.1.7 38:e7:d8:00:9a:d5
192.168.1.4 e0:ac:cb:5e:d5:da
192.168.1.10 cc:3d:82:60:4b:95
我发现还有2个其他活动主机(不包括路由器)出现,但没有响应回显请求。回显脚本可以调整以发送其他类型的ICMP消息,例如时间戳和子网掩码,这些消息可以进一步识别主机。
进一步思考
回显脚本使用广播技术,这仅在IPv4网络上有效。IPv6网络支持多播,但需要修改脚本。有趣的是,单个IPv6子网中潜在地址的数量(我认为)使得单播技术变得多余。
回显脚本另一个问题是,因为它打开原始套接字,需要root权限才能运行。另一方面,ping
工具带有setuserid权限安装,并作为root运行,而不管用户的自身权限如何。
有用资源
在编写此脚本的过程中,我学到了很多关于套接字和网络编程的知识。Lincoln Stein的《Perl网络编程》是理解套接字及其使用的神秘调用的宝贵资源。如果您考虑使用套接字,IO::Socket模块比Socket模块(也是核心部分)提供了一个更干净的接口。优秀的NetPacket分发的源代码对于理解如何解析数据包很有用。
本文最初发布在PerlTricks.com。
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。
- More commenting... maybe?
github.polettix.it - Perl Weekly Challenge 121: Invert Bit
blogs.perl.org - Web nostalgia: MojoX::Mechanize
github.polettix.it - On the eve of CPAN Testers
blogs.perl.org - PWC121 - The Travelling Salesman
github.polettix.it - PWC121 - Invert Bit
github.polettix.it - Floyd-Warshall algorithm implementations
github.polettix.it - Perl Weekly Challenge 120: Swap Odd/Even Bits and Clock Angle
blogs.perl.org - How I Uploaded a CPAN Module
blogs.perl.org - App::Easer released on CPAN
github.polettix.it