ARP 简介
ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。在局域网中,网络中实际传输的是“帧”,帧里面是有目标主机的MAC地址的。在以太网中,一个主机要和另一个主机进行直接通信,必须要知道目标主机的MAC地址。但这个目标MAC地址是如何获得的呢?它就是通过地址解析协议获得的。所谓“地址解析”就是主机在发送数据帧前将目标IP地址转换成目标MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。
正常情况下,每台主机都会在自己的ARP缓冲区中建立一个 ARP列表,以表示IP地址和MAC地址的对应关系。源主机需要将一个数据包要发送到目的主机时,首先检查自己 ARP列表中是否存在该 IP地址对应的MAC地址,如果有﹐就直接将数据包发送到这个MAC地址;如果没有,就向本地网段发起一个ARP请求的广播包,查询此目的主机对应的MAC地址。此ARP请求数据包里包括源主机的IP地址、硬件地址、以及目的主机的IP地址。网络中所有的主机收到这个ARP请求后,会检查数据包中的目的IP是否和自己的IP地址一致。如果不相同就忽略此数据包;如果相同,该主机首先将发送端的MAC地址和IP地址添加到自己的ARP列表中,如果ARP表中已经存在该IP的信息,则将其覆盖,然后给源主机发送一个 ARP响应数据包,告诉对方自己是它需要查找的MAC地址;源主机收到这个ARP响应数据包后,将得到的目的主机的IP地址和MAC地址添加到自己的ARP列表中。
① 要发送网络包给172.20.1.2,但不知MAC地址?
② 在局域网发出广播包“172.20.1.2的MAC地址是什么?”
③ 其他主机不会回应,只有172.20.1.2主机进行回应
④ 回应的包“172.120.1.2的MAC地址是08:00:20:74:CE:EC”
根据上图我们看到,主机A发送的请求广播包同时被其他主机收到,然后除主机B之外的其他主机收到之后(发现不是问自己)则丢弃。而主机B收到之后,根据请求包里面的信息(有自己的IP地址),判断是给自己的,所以不会做丢弃动作,而是返回ARP回应包。
ARP请求是通过广播方式来实现的,那么,PC2返回ARP回应包,是否也需要通过广播来实现呢?答案是否定的。大部分网络协议在设计的时候,都需要保持极度克制,不需要的交互就砍掉,能合并的信息就合并,能不用广播就用单播,以此让带宽变得更多让网络变得更快。
那么,ARP回应包是如何处理的?这里需要特别关注ARP请求包的内容,在上面的图解里面,ARP请求包的完整信息是:我的IP地址是IP1,MAC地址是MAC1,请问谁是PC2,你的IP2对应的MAC地址是多少?
为了让大家更好的理解ARP协议以及广播和单播的概念,我们来看一下用Wireshark抓取到的真实网络中的ARP过程,通过数据包的方式来呈现,地址信息如下,部分MAC信息隐去。
ARP请求包
ARP回应包
ARP协议的报文帧结构
> Hardware type : 硬件类型,标识链路层协议
> Protocol type: 协议类型,标识网络层协议
> Hardware size :硬件地址大小,标识MAC地址长度,这里是6个字节(48bti)
> Protocol size: 协议地址大小,标识IP地址长度,这里是4个字节(32bit)
> Opcode: 操作代码,标识ARP数据包类型,1表示请求,2表示回应
> Sender MAC address :发送者MAC
> Sender IP address :发送者IP
> Target MAC address :目标MAC,此处全0表示在请求
> Target IP address: 目标IP
在次通过Wireshark 的抓包验证ARP协议的报文帧结构
ARP请求包
ARP回应包
ARP高速缓存
如果每发送一个IP数据报都要进行一次ARP请求,以此确定MAC地址,那将会造成不必要的网络流量,因此,通常的做法是把获取到的MAC地址缓存一段时间。即把第一次通过ARP获取到的MAC地址作为IP对MAC的映射关系记忆到一个ARP缓存表中,下一次再向这个IP地址发送数据报时不需要重新发送ARP请求,而是直接使用这个缓存表当中的MAC地址进行数据报的发送。这样,再一定程度上也防止了ARP包在网络上被大量广播的可能性。
Python 实现ARP协议
Python先安装Scapy模块,然后利用Scapy提供函数功能来实现ARP请求报文帧的铸造。
Scapy 铸造ARP数据包的方法:
arp_request=Ether(dst='FF:FF:FF:FF:FF:FF')/ARP(op=1,hwdst='00:00:00:00:00:00',pdst='192.168.219.180’)
arp_request.show()可以查看铸造的包的信息:
铸造好的ARP 请求包可以通过srp()函数发送。srp()可以发送一个以太二层的数据包并等待回复,这里的回复是一个复杂的数据结构。
这里srp()函数的返回值是一个特殊的类,分为收到结果的包,和未收到结果的包。这里可以看到, srp()将返回值做成了一个元组。arp[0]就是收到的数据包,在收到的数据包中,又是列表,列表中的每一元素都是有发送的数据包和接收到的数据包组成,arp[0].res可以产生这个列表作为一个清单。清单的第一个元素(就是第一个响应的数据包)的第二个位置就是保存的接收到的数据包。在此位置查看此包的hwsrc字段,就是问题的答案,就是目标的mac地址。
Python3 源代码实现ARP的请求发送
代码执行结果
Python3 源代码实现本地局域网的地址扫描
代码执行结果
通过Python代码实现ARP协议可以使我们对ARP协议有更深入的了解。