MAC地址唯一标识一台主机,当系统要发送数据到其他主机时,必须事先知道它的MAC地址,并且上层应用协议并不关心MAC地址,然而在数据链接层,必须要获取发送方和接收方的MAC地址,这样数据才能正确到达接收方。
邻居子系统的作用就是把 IP 地址转换成对应的MAC地址。如果目的主机不是和发送发位于同一局域网时,解析的MAC地址就是下一跳网关地址。【在第 2 层发生数据包时,为创建 L2 层包头,需要使用 L2 目标地址,使用邻居子系统进行请求和应答,便可以根据主机的 L3 地址获悉其 L2 层地址(或者获悉这样的L3地址不存在)。在常用的数据链路层(L2)-----以太网中,主机的 L2 地址为 MAC 地址】。
有时不需要邻居子系统也能够获得目标地址。比如发送广播 FF:FF:FF:FF:FF:FF
, 或者发送目标地址是组播地址时,L3 组播地址和 L2 组播的映射关系是固定的。
Linux 邻接子系统负责发现当前链路上的结点,并且将 L3(网络层)地址转换为 L2(数据链路层)地址。
在 IPv4 当中,实现这种转换的协议为地址解析协议(Address Resolution Protocol,ARP),而在IPv6则为邻居发现协议(Neighbour Discovery Protocol,NDISC 或ND),邻接子系统为执行 L3 到 L2 映射提供了独立于协议的基础设施。
NDISC 协议用来避免重复 IPv4 地址的机制。
在 IPv4 中,使用邻接协议为 ARP,相应的请求和应答分别被称为 ARP 请求 和 ARP 应答,在 IPv6 中,使用的邻接协议为 NDISC,相应的请求和应答分别称为邻居请求 和 邻居通告。
Linux 邻接系统的基本数据结构是邻居,表示与当前链路相连的网络结点,用结构
neighbour
来表示。
为避免在每次传输数据包时都发送请求,内核将 L3 地址和 L2 地址之间的映射存储在邻接表的数据结构中。在 IPv4 中,这个表就是 ARP 表,有时也称为 ARP 缓存。在IPv6中,邻接表是NDISC表(也叫NDISC缓存),ARP 表和 NDISC 表都是结构neigh_table
实例。
使用邻接子系统的每种 L3 协议都还注册一个协议处理程序。对于 IPv4 来讲,ARP数据包处理程序方法为 arp_rcv()
。
每个邻居对象结构neigh_ops
中定义一组方法,它包含一个协议簇成员和4个函数指针,
邻居创建是由__neigh_create()
处理,
邻居删除是由neigh_release()
处理,
在方法 neigh_create()
的最后,将dead
标志初始化为0
,并将邻居对象添加到邻居散列表中,方法neigh_release()
将邻居的引用计数器减1
。如果它变成0
,请调用方法neigh_destroy()
将邻居对象释放。
管理 ARP 表,可使用 iproute2
包中的命令 ip neigh
,也可以使用net_tools
包中的命令
arp
。
ARP 协议
在以太网中,硬件地址称为 MAC,长度 48 位,MAC 地址独一无二。
发送 IPv4 数据包时,目标 IPv4 地址是已知的,但需要创建以太网报头,其中包含目标 MAC 地址,根据给定 IPv4 地址确定 MAC 地址的工作由 ARP 协议完成。ARP 表是一个neigh_table 结构实例,
紧跟在ar_op
后面的是 发送方的硬件(MAC)地址 和 IPv4地址,以 及目标硬件(MAC)地址 和 IPv4地址。这些地址并非 ARP 报头(结构arphdr
)的组成部分。我们可以在方法 arp_process()
中,通过读取 ARP
报头相应的偏移量来提取它们。
1、ARP(发送请求)
最常见的场景是在传输路径中,在离开网络层(L3)进入数据链路层(L2)之前,在方法
ip_finish_output2
中,首先调用方法__ipv4_neigh_lookup_noref
,在 ARP 表中查找下一跳 IPv4 地址。
2、ARP(接收请求和应答)
在 IPv4 中,方法 arp_rcv()
负责处理ARP数据包,
在方法 arp_process()
中,只处理 ARP 请求 和 ARP 响应操作。
对 ARP 请求,将使用方法ip_route_input _noref()
执行路由选择子系统查找。如果ARP数据包是发送给当前主机的(路由选择条目的rt_type
为RTN_LOCAL
),就接着检查一些条件。如果所有检查全部通过,就使用方法arp_send()
发回 ARP 应答。