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地址变化时发送GARParp_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.