ARP平时的使用比较多,原理也比较简单,今天来系统的全面的看一下ARP,以及在SDN中的ARP可以是怎么样的。
ARP全称是地址解析协议(Address Resolution Protocol)。它由RFC826定义,这是个不太长的RFC,标题比较有意思:
An Ethernet Address Resolution Protocol -- or -- Converting Network Protocol Addresses to 48.bit Ethernet Address for Transmission on Ethernet Hardware
一个以太网地址解析协议 --或者是-- 将网络协议地址转换成48bit的以太网地址,以便在以太网设备上传输
够直观!
目前最广泛的应用是通过IP地址,查询MAC地址。如无特殊说明,本文之后说网络协议地址,就是IP地址,以太网地址,就是MAC地址。ARP是IPv4中必不可少的协议,不过在IPv6中已经被NDP代替。
为什么需要ARP?
在TCP/IP协议中,主机到主机之间的通讯是通过IP地址来建立逻辑路径。例如,ping的是IP地址,访问网络服务,访问的也是IP地址。刚刚说的逻辑路径可以包含多个数据链路。举个例子,当你在看这篇文章的时候,实际上是建立了由你的电脑的IP到知乎服务器IP之间的逻辑路径。而数据包沿着这个逻辑路径传输时,经过了多个交换机和路由器的转发,这中间对应了多个数据链路。当数据包在一个独立的数据链路中传送时,需要把数据包封装在以太网帧(Ethernet Frame)当中,为了让Frame到达目的节点,需要知道目的设备的数据链路标识,也就是MAC地址。因此,对于一个数据链路中的各个设备,只有知道彼此的标识,才能完成网络数据传输(也就是常说的2层数据传输)。ARP的作用就是促成彼此之间数据链路标识的识别,进而完成网络数据的传输。
ARP协议格式
了解ARP的应用之前,先来看看ARP的协议格式。
图中的前14个字节是以太网报头,这个是所有网络数据通用的。ARP对应的帧类型是0x0806。
之后的28个字节是ARP协议的具体内容。主要就是描述了对应的网络协议地址和数据链路标识。
- 其中硬件类型和协议类型:分别对应的是数据链路的类型和网络协议的类型。最常见的硬件类型就是0x0001,对应以太网;而最常见的协议类型是0x8000,对应IPv4.
- 硬件地址长度和协议地址长度:分别对应的是数据链路标识和网络协议地址的长度。以太网的标识长度是6,即6个字节的MAC地址;IPv4的长度是4个字节。
- 操作码:表明了当前ARP数据包的类型。ARP请求对应1,ARP响应对应2。详细的操作码用IANA定义。
- 源硬件地址(SHA,Source Hardware Address)和源协议地址(SPA,Source Protocol Address):分别对应的源设备的MAC地址和IP地址。
- 目的硬件地址(THA,Target Hardware Address)和目的协议地址(TPA,Target Protocol Address):分别对应的是目的设备的MAC地址和IP地址。
ARP的基本行为模式
后面将介绍ARP的多种应用,虽然有的看起来比较复杂。但是他们都遵循着ARP协议的基本行为模式,可以概括如下:
- ARP的数据包含在SHA和SPA当中。也就是说各个设备获取IP地址到MAC地址的对应取自SHA和SPA。当SHA或者SPA为空时,ARP数据无效。
- ARP的传输模式由以太网报头决定,例如单播还是广播,由目标以太网地址决定,与THA无关。
- 任何设备仅会处理TPA与自身IP地址匹配的ARP数据。
ARP如何获取邻居设备的MAC地址
前面说过ARP的作用就是获得邻居设备的MAC地址,这一节看看这个过程具体是怎样。
现在有两个设备A和B在一个数据链路中。A现在要ping B(10.0.0.22)。虽然现在知道了B设备的IP地址,但是ICMP数据包最终还是要封装在以太网报头中,也就是说网络数据的传输还需要知道目的设备的MAC地址。为了完成ICMP封包,A设备先从自己的ARP cache中去查找10.0.0.22的记录,如果找到了,直接使用记录中的MAC地址来填到以太网报头中。如果没有找到,设备A会发送一个ARP数据包,这个数据包有以下特点:
- 它包含了源设备A的IP地址和MAC地址(SHA,SPA)
- 它包含了目的设备B的IP地址(TPA),由于不知道设备B的MAC地址,THA被填成全0
- 它的以太网报头中,目的地址是广播地址:ff:ff:ff:ff:ff:ff
- 它的操作码是1,表明这是一个ARP请求
由于以太网报头中的目的地址是广播地址,整个数据链路上的设备都将收到该帧,只有设备B自身的IP能与TPA匹配,其他设备不能匹配都会丢弃数据包。设备B收到这个ARP请求之后,会回送一个ARP数据包,这个数据包具有如下特点:
- 它包含了设备B的IP地址和MAC地址(SHA,SPA)
- 它包含了设备A的IP地址和MAC地址(THA,TPA)
- 它的以太网报头中,目的地址是设备A的MAC地址
- 它的操作码是2,表明这是一个ARP响应
由B设备发出的ARP数据包不再是一个广播数据,而是单播给了设备A。并且该ARP响应中的SHA和SPA包含了设备B的IP和MAC地址,当设备A收到了该ARP响应,就知道了设备B的MAC地址,可以继续进行ICMP的封包,大致过程跟下面这个动图描述的一样。
ARP cache
前面已经提到了ARP cache。试想一下,如果每个ICMP封包都需要ARP广播一次获取目的设备的MAC地址,那网络上必定泛滥着ARP广播,而各个设备也必定疲于应付广播数据,即使没有任何实际数据包发送到设备本身。ARP cache就是为了解决这个问题提出的,这是设备本身的一个缓存,用来记录已知的IP地址和MAC地址的对应关系。根据RFC826,任何设备收到了任何ARP数据包,都必须更新其本地的ARP cache,更新的内容来自于ARP数据包的SPA和SHA。因此,在上面的过程中,设备A和设备B的ARP cache同时得到了更新。
ARP cache可以通过arp命令查看:
[~]$ arp -a
也可以通过ip neighbor命令查看。确切的说,arp命令只能查看IPv4地址和MAC地址的对应,而ip neighbor 还包含了IPv6的对应关系。
[~]$ ip neighbor
192.168.31.203 dev eth0 lladdr 10:48:b1:b6:0a:a7 STALE
192.168.31.1 dev eth0 lladdr 8c:be:be:28:40:75 REACHABLE
fe80::122a:b3ff:fea1:73a2 dev eth0 lladdr 10:2a:b3:a1:73:a2 STALE
那新的问题来了,随着时间的运行,设备上的ARP cache必定越来越大,消耗设备的资源也必定越来越多。为了解决这个问题,ARP cache中每条entry都有对应的存活时间。在Linux下,这个存活时间不是简单的过了一定时间就删除,具体过程比较复杂。stackoverflow上的解释比较到位,感兴趣可以看看。简单概括:
- ARP数据包能创建一条新,或者更新已有的ARP entry。刚刚创建或者更新的ARP entry,状态是REACHABLE。
- 较短时间内ARP entry如果没有更新,状态会变成STALE,STALE的ARP entry仍然会被用来进行以太网报头填充。如果STALE状态的ARP entry被使用了,操作系统会异步的发送一个单播的ARP请求去验证并更新这条ARP entry。所以我们在ping的时候,常常可以看到夹杂着的单播ARP请求。
- ARP entry如果一定时间没有被使用,会被清除。
ARP spoof 和 proxy ARP
从ARP的协议格式和ARP的工作过程来看,ARP本身没有提供一个认证机制。因此ARP应答存在仿冒(或者说ARP代答)的可能。在不同的应用场景下,有不同的效果。
ARP spoof
这是恶意攻击的场景。
前面说过任何设备仅会处理TPA与自身IP地址匹配的ARP数据,但是ARP spoof system会修改这个行为。对于想拦截的逻辑连接,ARP spoof system会匹配含有相应的TPA的ARP请求,并且回送一个带有自身的MAC地址的ARP响应。这样,发送ARP请求的源设备会以为ARP spoof system是期望的目的设备,所有数据包都会发送到ARP spoof system。
通常ARP spoof system会伪装成默认网关,这样所有出外网的数据包都会被拦截。简单的攻击是直接丢包,造成断网,稍微复杂点的会篡改包的内容,再转发出去。ARP spoof通常是网络攻击的开始。
proxy ARP
这是正常网络规划的场景。原理其实与ARP spoof类似,只是这是设计好的网络特性。具体可以参考RFC925和RFC1027。
当目的设备不能够或者不愿意(出于安全或者性能角度)响应ARP请求时。可以由另一个设备将自己的MAC地址回送给源设备,之后源设备将所有的数据发送到该设备,再由该设备转发到实际的目的设备。这实际上也是欺骗了源设备,只不过这里的设备是一个设计好的,不会干坏事的中继设备。
proxy ARP可以在路由器上配置,从而代替默认网关,为子网实现三层转发。而子网内的设备仍然会认为自己在一个二层网络中。这种用途现在比较少。
proxy ARP还有个用途就是用于NAT设备的ARP应答。设备X访问设备Y,当网络包走到路由器C,路由器C首先也会发送ARP广播请求,来查询目的设备的MAC地址。但是由于Y设备与路由器C不在一个数据链路中,Y设备收不到路由器C发出的ARP广播请求,因此二手手机靓号转让地图需要NAT设备代答ARP。这里也可以理解成,22为一个虚拟的端口(72.3.4.55)提供ARP响应功能。这样数据包能接着发送到NAT设备,进而转发到设备Y。
Inverse ARP and Reverse ARP
Inverse ARP由RFC2390定义,用来通过数据链路标识(MAC地址)查找网络协议地址(IP地址),对应的ARP协议操作码分别是8,9。Reverse ARP用于主机启动的时候通过向RARP server请求自身的IP地址,对应的ARP协议操作码分别是3,4。不过Reverse ARP已经被DHCP替代了(想当年学计算机网络的时候还是考试内容)。这两者的区别是Reverse ARP只能通过自己的MAC地址查询IP地址,而Inverse ARP可以查询别人的。
Gratuitous ARP
ARP数据传输本身是无状态的。因此,就算设备没有发出ARP请求,如果该设备收到了其他设备发来的ARP数据包(包括ARP请求和响应),那么该设备还是会更新自身的ARP cache。这就是GARP(Gratuitous ARP)的实现背景。Gratuitous翻译过来可以是无故的,免费的。GARP的作用就是告知别的设备来更新它们自身ARP cache中关于GARP中的SPA和SHA的对应关系。就像前面说过的一样,任何ARP的数据都来自SHA和SPA。
前面说过,任何设备收到了任何ARP数据包,都必须更新其本地的ARP cache。因此,GARP可以是一个ARP请求(操作码1),也可以是一个ARP响应(操作码2)。这两种情况的共同点:
- SPA和TPA都必须设置成需要更新的ARP entry的IP地址
- SHA都设置成需要更新的ARP entry的MAC地址
- ARP数据包都必须在数据链路中广播。即以太网报头的目的MAC地址必须是ff:ff:ff:ff:ff:ff。
不同点在于:
- 如果是ARP响应,THA也必须设置成需要更新的ARP entry的MAC地址。而如果是ARP请求,THA被忽略。
由于TPA被设置成了源设备的IP地址,即使GARP是一个ARP请求,它也不期望会得到任何响应。因为不会有设备的IP能匹配TPA。
GARP的实际应用,除了让别的设备更新相应的ARP entry。还有个重要的作用就是用在冗余IP(或者说虚拟IP)的场景下。
现在假设有一个虚拟IP 10.0.0.1作为网络的默认网关,而上行线路有两个路由器做主备。当路由器A发生故障,路由器B得知之后,会广播一个GARP出来,让网络中的设备更新ARP cache,使得默认网关的MAC地址指向路由器B,这样路由器B将作为主路由器提供服务。这就是VRRP协议的基础,而OpenStack Neutron中的HA router正是基于VRRP实现的。
ARP probe
ARP探测,这个由RFC5227定义。当一个设备以任何方式获取了IP地址,想在一个数据链路中上线时,如果该数据链路中已经存在了一个相同IP地址的设备,那该设备上线将导致两个设备网络都不可用。为了防止该情况的发生,可以使用ARP probe判断当前网络是否已经有指定IP地址。
ARP探测包本身也是一个普通的ARP请求(操作码1),但是其SPA需要设置成0.0.0.0。前面说过,ARP cache的更新内容来自于SPA和SHA,由于SPA为空,ARP探测包中的ARP数据是无效的,因此ARP probe数据包不会更新任何设备的ARP cache。这是合理的,因为ARP 探测本身就是一个不确定的行为,如果数据链路中已经有了相应的IP地址设备,自然也不希望在ARP探测时修改已有的ARP entry。
ARP探测报文是一个广播报文,也就是说它的以太网报头中的目的MAC地址是ff:ff:ff:ff:ff:ff
ARP探测报的TPA设置成希望探测的IP地址,这样如果数据链路中已经有同IP的设备,收到探测包之后会给源设备发送ARP响应,而源设备也就知道了数据链路中存在相同IP地址的设备。
ARP探测如果没有发现IP被占用,通常会接着发送一个GARP来告知数据链路中的其他设备有关自己的IP地址和MAC地址。根据RFC5227,这个时候的GARP应该是一个ARP请求(操作码1)。
ARP在SDN中的应用
普通设备的ARP应答
设备通过ARP获取邻居设备的MAC地址,第一步就是发送ARP广播。由于ARP cache有老化时间,如果一个数据链路足够大,那在实际的网络链路中,ARP广播数量也不容小觑。
在SDN中,由于SDN的控制层面掌握着所有端口的信息,包括了端口的IP地址和MAC地址,可以在数据层面下发ARP代答流表。
priority=100,arp,arp_tpa=10.0.0.1,arp_op=1 actions=load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],mod_dl_src:fa:16:3e:1d:53:34,load:0xfa163e1d5334->NXM_NX_ARP_SHA[],load:0xa000001->NXM_OF_ARP_SPA[],IN_PORT
例如,这条流表中,对目的地址是10.0.0.1的ARP请求,直接生成ARP响应:
- 操作码修改为2,表示是ARP响应
- 源SHA,SPA移动到THA,TPA
- 将已知的10.0.0.1的MAC地址写入SHA,将10.0.0.1写入SPA
- 将源MAC写成已知的10.0.0.1的MAC地址,最后回送到输入端口
从源设备的角度来说,这就是一个再正常不过的ARP请求响应过程。但是通过OpenFlow的实现,可以带来以下好处:
- 避免了ARP广播,减轻了数据链路的负担。
- 快速响应,ARP响应不再需要经过广播,单播,而是直接由交换机回送给发送设备。这在实际封包的时候能降低网络延时时间。
- 避免了ARP spoof。IP地址与MAC地址被静态的写入流表,篡改难度加大。
为虚拟IP发送GARP
SDN管理的环境中,虚拟IP可以配置在多个端口上,例如OpenStack Neutron 里的allowed address pair,可以将一个虚拟IP在多个端口上浮动配置。当虚拟IP从端口A移动到端口B时,需要通知数据链路中的其他端口虚拟IP对应的MAC地址发生变化,这个时候可以由SDN控制器代为发送GARP来更新数据链路中的设备的ARP cache。
为floatingIP做proxy ARP
前面说过NAT设备需要为对应的外网IP地址做ARP代答,否则数据包在最后一个链路无法获取MAC地址。SDN可以为floatingIP下发ARP代答流表,这样目的地址是floatingIP的数据包最终能发送到SDN 交换机并转发到实际的端口。
总结
传统的ARP协议的大部分实现都基于操作系统协议栈的响应。SDN的出现增加了网络实现的灵活性,通过SDN对ARP进行优化,能降低网络延时,减轻数据链路的负担,增加网络的安全性。