TCP/IP的排头兵――地址解析协议(ARP) (转载)
转自:http://blog.csdn.net/wangxg_7520/article/details/2488442
一、引言
古人行军打仗,都要有一个可以引领队伍前进方向的排头兵,在TCP/IP网络大军中,也存在这种排头兵,为TCP/IP传输指明方向,它就是地址解析协议――ARP。
二、ARP协议及应用
地址解析协议(ARP),是TCP/IP协议簇中较底层的一个,它是TCP/IP应用的基础,但又不完全隶属于TCP/IP,所以在名著《TCP/IP详解》中,它被单独的划分出来,与IP一起并列于以太网驱动程序以上。之所以要把它分离出来,是因为, 其一,它不像IP数据报那样,使用IP地址和端口号收发数据;其二, 它与IP一样,使用了单独的以太网帧类型字段(0x0806)。下面我们就来看看它是怎么工作的。
我们知道,连接在同一网络上的两台主机,要通过TCP/IP进行通信,必须要知道彼此的IP地址(我们这里不打算讨论端口和IP广播)。但是仅仅知道IP 地址还是不够的,因为所有的IP数据报最终都要封装为以太网帧,才能发送出去。而以太网卡用的是另一种地址――MAC地址来标识每一台主机(这里假设每台主机只有一张网卡)。所以在IP数据报传输之前,必须要首先知道目的IP所对应的MAC地址。
那么怎么才能得到IP地址所对应的MAC地址呢?地址解析协议,它就是用来在传输IP数据报之前,建立起从IP地址到MAC地址之间的动态映射,为TCP/IP数据传输指引方向,就如行军中的排头兵。
下面我们就通过一个简单的例子来看ARP是怎么工作的。
假设主机A和B连接在同一以太网上,他们的IP地址分别是192.168.1.2和192.168.1.3,MAC地址分别是02:10:18:01:00:02和02:10:18:01:00:03。
现在我们先使用工具 arp -d 清除系统内所有ARP缓存,然后在主机A输入命令:
ping 192.168.1.3
接下来将会发生以下事情:
1、ping程序产生ICMP包,将要通过TCP/IP传输给主机B 192.168.1.3;
2、TCP/IP查找ARP缓存,看有没有主机B 192.168.1.3所对应的MAC地址;
3、如果没有(此时肯定没有),就会将ping发送过来的ICMP包放到一个待发送队列里等待,然后就向整个网段广播ARP请求,询问谁拥有IP地址192.168.1.3;
4、接到改以太网上的所有主机都将收到此ARP请求,但只有拥有IP地址192.168.1.3的主机B会给出应答,告诉主机A自己的MAC地址,并同时把主机A的IP地址和MAC地址的映射保存到自己的ARP缓存中,因为主机B可能很快就要同主机A进行TCP/IP通信了,这样可以避免网络内有过多的ARP 请求;
5、主机A收到来自主机B的ARP应答后,更新ARP缓存,然后从待发送队列中取出等待的ICMP包,使用主机B的MAC地址封装成以太网帧后发送给驱动程序;
6、方向已经确定,接下来主机A和B就可以使用TCP/IP正常的通信了。
三、ARP缓存
从以上分析可以看到,ARP缓存在TCP/IP中扮演着重要的角色,所以对ARP缓存的处理将会对TCP/IP的性能产生重要的影响。一方面ARP缓存加快了TCP/IP的传输,但另一方面,如果ARP缓存过时了(缓存中的MAC地址所对应的主机已经断开,IP地址现在可能已经为另一个主机所用),就会使 TCP/IP数据包永远得不到应答,直到ARP缓存超时,就像先锋部队修了一座桥,但这桥不够坚固,部队没过完桥就塌了,所以后面的同志们都得落水,直到有人通知后面的长官停止前进;或者有人再重新把桥修好,但桥的方向就不能保证了。所以对ARP缓存的超时限制是必不可少的。这个超时限制应该是多少呢?一般是20分钟,但如果ARP缓存都在20 分钟后超时的话,势必会引起整个网络的阻塞,所以需要对ARP缓冲做一下优化(refinement)。
我们知道,ARP请求都是广播出去的,所以同一网络上的所有主机都将收到任何其他主机发出的ARP请求。利用这一特性,我们就可以对ARP缓存做一些优化。如前面例子所说,主机A广播ARP请求给整个网络,而只有主机B会给出应答,其他主机都保持沉默。但保持沉默并不意味着其他主机只能不闻不问,他们会接收 ARP请求,然后拿出发送请求者的IP地址和MAC地址,并与自己的ARP缓存相比较,如果发现自己缓存中存在该主机的匹配,就会更新ARP缓存,并重置 ARP缓存超时时间。这样既保证不会ARP广播而引起网络阻塞,也能使ARP缓存得到及时的更新。但是依然会有搭错桥的问题,如主机B掉线或当机了,这时主机C刚好上线,并抢了主机B的IP地址,这样主机C发出的ARP请求将会使网络上所有主机的ARP缓存更新为主机C的MAC地址,结果要发送给B的数据就会发往C了。就像部队本来要攻打南京,过桥之后发现竟然是芜湖…… 这就是传说中的ARP欺骗(ARP Snoofing),虽然你不是故意的:)
四、ARP数据包格式
ARP数据包没有固定长度,它的长度是由两个字段控制的,一个是硬件地址长度字段(Hardware Len),另一个是协议地址长度字段(Protocol Len)。如下图所示:
图一 ARP数据包格式
Hardware Type指硬件接口的类型,对以太网,其值为1;
Ptotocol Type指高层协议,0x0800指IP地址;
Hardware Len指硬件地址长度,对以太网为6;
Protocol Len指协议地址长度,对TCP/IP为4(这里之对IPv4);
Operation指操作类型,1为ARP请求,2为ARP应答,3为RARP请求,4为RARP应答;
Sender/Target Hardware/IP Addr指发送/应答者的MAC/IP地址
下面我们给出上面所说ARP过程的具体数据包内容,从中可以清楚的看到ARP传请求/应答的过程。在ARP数据包前面我们还给出了以太网帧头,意在表明ARP包所用的以太网帧类型,同时还忽略了对我们没用的以太网帧最后的填充位(对少于64字节的帧)和CRC校验和。
ARP请求: host A 192.168.1.2 (02:10:18:01:00:02) -> host B 192.168.1.3 (00:00:00:00:00:00)
由于此时我们还不知道主机B的MAC地址,所以用0填充。
图二 ARP请求帧
ARP应答: hostB 192.168.1.3 (02:10:18:01:00:03) -> hostA 192.168.1.2 (02:10:18:01:00:02)
图三 ARP应答帧
从上图中不难看出,在主机B发出的ARP应答中,已经由对网络的多播改为只对主机A的单播。另外把ARP包中的操作(Operation)由请求(1)改为应答(2),硬件地址已经填入主机B的MAC地址,并且发送/接收者的硬件地址和协议地址发生了互换。
五、ARP代理
以 上我们讨论的都是在同一网络内的情况,那么如果ARP请求的IP地址不在本网络内,将会发生什么情况呢?例如上例中,如果主机A ping另一个网络内的 主机b(192.168.2.3),结果会怎样呢?很可能ping程序会给出形如host unreachable的失败信息。这是因为,主机b在另一个 网段,而ARP请求的广播只对本网段有效,送往其他网段的会被路由器丢弃,所以没有主机会对它作出回应。要想让ARP能够穿过路由器到达另一网段,就需要 使用ARP代理。如下图所示,ARP代理会接收来自一个网段主机A发往另一网段主机b的ARP请求,然后将ARP请求转发给另一网段的主机b,最后把从主 机b收到的应答再回传给发送ARP请求的主机A。这样对主机A来说,就像直接从主机b收到ARP应答一样,完全感觉不到ARP代理的存在。
图四 ARP代理
六、逆向地址解析协议――RARP
与 ARP相对应的还有一个RARP,称为逆向地址解析协议,它的包格式与ARP相同,但是它使用以太网帧类型0x8035。RARP的主要应用是为无盘系统 启动时提供从MAC地址到IP地址的映射,但它的使用需要RARP服务器的支持。在硬盘价格如大白菜的今天,(当然,嵌入式系统除外),我想应该不会有几 个人会使用无盘系统了吧!所以这里不对RARP做过多的讨论。
七、ARP命令
Windows和Linux系统都提供了ARP命令,用这些命令可以很方便的管理系统的ARP缓存。ARP命令比较简单,有兴趣的朋友可以直接查看在线帮助,我们这里只给出几个简单的命令以供参考。
Windows:
显示所有ARP缓存:arp -a
清除所有ARP缓存:arp -d
添加一条静态ARP映射:arp -s 192.168.1.20 00-40-4C-34-01-20
清除刚才添加的ARP:arp -d 192.168.1.2
Linux:
显示所有ARP缓存: arp -a
添加一条静态缓存: arp -s 192.168.1.20 00-40-4C-34-01-20
清除刚才添加的缓存: arp -d 192.168.1.20
Linux不会真正清除ARP缓存,它只是对要清除的条目设一个标志,详见下节。另外Linux中上面的IP地址也可以用有效的主机名替代。
八、关于/proc/net/arp
Linux内核会把当前ARP缓存以可读的形式存于文件/proc/net/arp中。现在我们就来研究一下/proc/net/arp及其与arp命令之间的关系。
首先查看一下系统ARP缓存:
# arp -a
? (192.168.1.254) at 00:30:0A:BD:6B:25 [ether] on eth0
ads (10.1.8.11) at 00:03:FF:64:51:A6 [ether] on eth1
atm (10.1.10.26) at 00:90:CC:D1:46:0F [ether] PERM on eth1
? (10.1.8.254) at 00:B0:64:6F:B9:A0 [ether] on eth1
从中可以看到,我的系统两张网卡上共有四条ARP缓存,对没有主机名的条目,主机名一栏用?表示。下面再来看一下/proc/net/arp:
# cat /proc/net/arp
IP address HW type Flags HW address Mask Device
192.168.1.254 0x1 0x2 00:30:0A:BD:6B:25 * eth0
10.1.8.11 0x1 0x2 00:03:FF:64:51:A6 * eth1
10.1.10.26 0x1 0x6 00:90:CC:D1:46:0F * eth1
10.1.8.254 0x1 0x2 00:B0:64:6F:B9:A0 * eth1
在
该文件中保存有IP地址,网络类型,ARP标志,MAC地址,网络掩码,和网络接口名等ARP缓存信息。其中IP地址,MAC地址和网络接口意思很明显;
网络掩码用于指定ARP代理,2.2之后的内核已经不再支持;网络类型参见RFC826,0x1指以太网;我们需要重点说明的是Flags。首先我们删除
10.1.8.11的ARP缓存:
# arp -d 10.1.8.11
# arp -a
? (192.168.1.254) at 00:30:0A:BD:6B:25 [ether] on eth0
ads (10.1.8.11) at <incomplete> on eth1
atm (10.1.10.26) at 00:90:CC:D1:46:0F [ether] PERM on eth1
? (10.1.8.254) at 00:B0:64:6F:B9:A0 [ether] on eth1
#cat /proc/net/arp
IP address HW type Flags HW address Mask Device
192.168.1.254 0x1 0x2 00:30:0A:BD:6B:25 * eth0
10.1.8.11 0x1 0x0 00:03:FF:64:51:A6 * eth1
10.1.10.26 0x1 0x6 00:90:CC:D1:46:0F * eth1
10.1.8.254 0x1 0x2 00:B0:64:6F:B9:A0 * eth1
删 除10.1.8.11之后,arp -a会显示<incomplete>,/proc/net/arp中将它的Flags设为0x0,以表明该ARP cache暂时无效,等待更新,但该条目并没有从ARP cache中彻底删除。收到ARP应答后,该条目会自动更新,我们并不一定要发出ARP请求,详见第三节。
我们还可以用ARP命令给网络接口指定一个ARP代理:
# arp -i eth0 -s 202.22.30.10 00:40:78:90:66:02 pub
# arp -a
? (192.168.1.254) at 00:30:0A:BD:6B:25 [ether] on eth0
ads (10.1.8.11) at 00:03:FF:64:51:A6 [ether] on eth1
atm (10.1.10.26) at 00:90:CC:D1:46:0F [ether] PERM on eth1
? (10.1.8.254) at 00:B0:64:6F:B9:A0 [ether] on eth1
pubarpproxy.somedomain.com (202.22.30.10) at * PERM PUP on eth0
#cat /proc/net/arp
IP address HW type Flags HW address Mask Device
192.168.1.254 0x1 0x2 00:30:0A:BD:6B:25 * eth0
10.1.8.11 0x1 0x2 00:03:FF:64:51:A6 * eth1
10.1.10.26 0x1 0x6 00:90:CC:D1:46:0F * eth1
10.1.8.254 0x1 0x2 00:B0:64:6F:B9:A0 * eth1
202.22.30.10 0x1 0xc 00:00:00:00:00:00 * eth0
但是Linux2.2之后,kernel会根据路由表自动选择ARP代理,所以我们现在已经不必再指定任何ARP代理了,此例仅用于说明问题。在文件/usr/include/linux/if_arp.h中,ARP标志定义如下:
/* ARP Flag values. */
#define ATF_COM 0x02 /* completed entry (ha valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* publish entry */
#define ATF_USETRAILERS 0x10 /* has requested trailers */
#define ATF_NETMASK 0x20 /* want to use a netmask (only for proxy entries) */
#define ATF_DONTPUB 0x40 /* don't answer this addresses */
也许你会发现,从上面的定义里,我们找不到/proc/net/arp中诸如0x6,0xc之类的Flag。这是因为在/proc/net/arp中使用的是几种Flag按位或的结果,所以0x6 = ATF_COM | ATF_PERM,0xc = ATF_PERM | ATF_PUBL。
九、小节
说了这么多,你应该对ARP的工作原理比较了解了吧?呵呵,其实很简单, 由于TCP/IP所使用的地址机制与以太网所使用的硬件地址机制不一样,所以 ARP/RARP提供了一种地址映射机制,目的就是使以太网地址能与IP地址对应起来,让TCP/IP数据能够通过以太网正常传输。只要它工作正常,你可能根本感觉不到它的存在,但一旦它罢工了,你可能就死无葬身之地了……
参考文献:
[1] W. Rechard Stevens 《TCP/IP详解·卷1:协议》机械工业出版社
[2] Douglas E. Comer 《TCP/IP网络互联·卷1:原理、协议和体系结构》英文版 第5版 人民邮电出版社
[3] RFC826 - Ethernet Address Resolution Protocol
[4] RFC903 - Reverse Address Resolution Protocol
声明:
本文仅供学习和交流,转载请注明作者和出处,谢谢!