ARP(Address Resolution Protocol)
即地址解析协议, 用于将网络层L3的IP地址转换成数据链路层的L2地址(MAC地址), 常用在诸如以太网, 无线网络等局域网中, 但对如点对点(P2P)网络, 组播与多播IP地址, 都无需使用ARP
协议.
一般来说, L3-L2地址的转换通常被成为邻区协议(neighboring protocol
), 这种发现邻居节点(neighbor
)的协议被统称为邻居发现协议(Neighbor Discovery Protocol
, ND
). ARP
可以看作是ND
的ipv4版本(参考RFC826), 在ipv6中则直接称为ND
(参考RFC4861).
邻居节点(
neighbor
)指的是跟主机在同一局域网(LAN
)的其他节点
ND
协议有两种消息类型:
Solicitation Request
(也称为Neighbor Solicitation
): 用于主机发送消息到网络中查询是否有主机拥有某个L3的IP地址, 该消息可以是单播, 组播或者广播形式.Solicitation Reply
(又称为Neighbor Advertisement
): 收到Solicitation Request
包时发出的回应包(有可能是HOST本身发出的, 也有可能是ARP
代理服务器发送的)
那么, 什么是Gratuitous ARP
(简称GARP
, 免费ARP)了? 简单来说, GARP
是一种用于告知网络中其他节点某些特定信息的ARP请求包, 但无须其他节点发送回应包, 常用于如下三种情况:
- 检测局域网内的IP地址冲突(参考RFC5227)
- L2地址(MAC地址)发生变化时
- 虚拟IP(
virtual IP
), 用于服务器的冗余
接下来就看看GARP
在这三种场景的具体应用.
IP冲突检测
当局域网中采用静态IP与动态(基于DHCP
)IP配置结合的方式时, 有可能两个节点之间的IP会相同;这时需要使用GARP
来检测IP冲突: 网络节点在配置IP时, 发送一个GARP
到其他节点, 如果有节点的IP与之相同, 则会回应一个ARP
包, 告诉该IP已经有节点使用了; 如果该IP没有被使用, 则不会收到任何ARP
的回应.
MAC地址变更
当某个网络节点的L2地址(MAC地址)发生变化时, 网络中的其他节点是无法知道的, 因此需要一个协议来同步这个变化, 以便于其他的节点及时更新ARP
缓存. 这样比每个节点自己主动更新会来的更省事方便(也不会导致短暂的网络黑洞, 参见上一篇文章ARP cache不更新导致的网络问题)
服务器冗余
在某些情况下, 为了实现在多个服务器之间的故障恢复(failover
), 通常会使用GARP
来到达服务器冗余(redundancy
)的目的, 这时两个服务器会共享IP或MAC地址, 当某个服务器发生故障(死机, 物理故障等)时, 服务器群里的各个机器通过心跳机制来定时侦测故障, 当备用服务器侦测到与其共享IP的服务器故障时, 会发送一个GARP
包, 告诉各个服务器更新对应的ARP缓存, 确保服务能快速恢复.
下面我们就来看一看Linux内核(4.14)是如何处理GARP
的.
Linux如何处理GARP
Linux提供了ARP/GARP
相关的配置选项, 在/proc/sys/net/ipv4/conf/
中有如下几个节点(参考Linux文档ip-sysctl.txt
):
arp_announce
: 控制ARP
请求时发送的本地网卡源IP地址的类型,0
(默认)表示可以使用任何本地IP地址,1
表示只使用同一子网内的IP地址,2
表示只使用网卡的首要IP地址(primary ip
)arp_igonre
: 控制收到ARP
请求后发送回包时的目标IP地址类型:0
(默认)表示发送任何本地的IP地址,1
只发送当前收到ARP
包网卡的本地地址,2
只回送接收ARP
包的网卡与ARP
请求方在同一子网的IP地址,3
回送域(scope)为global/link
的地址, 不回送host
的地址;4-7
保留值,8
表示不回送任何地址arp_notify
: 网卡状态变化时是否发送GARP
,0
表示什么都不做,1
表示设备UP或MAC地址变化时发送GARP
arp_accept
: 收到GARP
时是否创建ARP
缓存,0
不创建,1
表示创建(如果ARP
缓存已经包含了GARP
包的IP地址, 不管是否开启该选项, 都会更新ARP
缓存)drop_gratuitous_arp
: 丢弃所有GARP
包, 默认是0
(关闭)
最后, 不妨看下内核是如何实现GARP
的发送的.
当网卡UP或者MAC地址发生变化时(前提是arp_notify
处于开启状态)会发送GARP
, 设备驱动也可以通过发送NETDEV_NOTIFY_PEERS
事件来触发GARP
告知其他节点状态的变化:
1 |
|
内核初始化时注册的通知回调inetdev_event
在接收到事件通知后, 就会发送GARP
:
1 |
|
当收到GARP
包是内核是如何处理的, 可以参考net/ipv4/arp.c
中的函数arp_process
.