邻居子系统-概念
概念
连接在同一LAN上的主机具有相同的L3网络配置互为邻居。
另一种定义邻居的方式是主机到其邻居有且只有一个L3跃点。并且它的路由表必须提供可以直接和其邻居通信的路径。不是邻居的主机通信必须经过网管或者路由器。
物理子网(LAN)和逻辑子网(IP子网)不总是一对一的。在同一个LAN上可以存在多个IP子网,或者多个LAN位于同一个IP子网内。
邻居协议作用
L3地址转换成L2地址,根据硬件不同存在多种L2协议,而L3协议不用关心使用哪种L2协议。对于以太网,主机使用广播包作为传输介质网络,如果主机A给主机B发送数据报文,主机A将报文发送到介质上,所有共享这个传输介质上的系统都可以收到该数据。A必须在报文中指明L2地址以便将数据发送给需要发送的地址。其他主机会检查这个地址并忽略这个数据。邻居协议负责选择L3对应的L2地址。
L3地址是逻辑地址(如IP地址),任意合法的L3地址可以分配给物理接口。L2地址被绑定到网卡上,由生产商指定全球唯一。在以下情况中会改变L3地址同时L3到L2的映射关系也会改变。
- 动态配置IP(DHCP)
- 网卡故障,替换新的网卡后希望L3地址保持不变而L2地址已经改变。
- L3地址移动
Solicitation请求和应答
- Solicitation请求表示在网络上发出一个报文询问所有主机,是否有主机知道该L3地址对应的L2地址。
- Solicitation应答作为请求的响应报文,正常条件下请求中查询的目的IP主机产生应答,在邻居协议代理场景下也有可能由另一台主机替代应答。
Linux实现
Linux内核提供邻居基础结构,L3协议与邻居层交互通过一个通用接口,该接口根据请求映射的L3地址使用正确的邻居协议(ARP、ND等)。
下图给出邻居接口层在内核中的位置。
发送一个报文时,需要执行下面的步骤:
- 本地主机路由子系统选择到达L3目的地址的下一跳。
- 根据路由子系统返回的下一跳地址,如果该地址和本机在同一个网络中(邻居关系),邻居层就把L3地址解析成L2地址。这个关联会被放到缓存中,以便将来再次使用。
- 使用dev_queue_xmit完成发送,将报文传递给流量控制(Traffic Control)或者Qos(Quality of Service)。如果需要L2层信息,邻居层必须在调用dev_queue_xmit函数前将头信息加到报文上。
邻居协议
现在的IP网络中使用两个邻居协议。
- IPV4的ARP协议
- IPV6的ND协议,ND协议在IPV6作为ICMP的一部分。
Linux内核提供邻居基础结构提供一下功能。
- 为每个协议提供L3到L2转换结果的缓存。
- 提供缓存操作功能,查找、添加、更新、删除。
- 为缓存提供老化机制。
- 缓存慢时,需要提供替换策略。
- 每个邻居提供一个请求队列,当准备发送一个报文其L2地址还不在地址缓存中时,必须把这个报文放入这个队列,直到收到Solicitation应答。
邻居协议代理
邻居协议的代理服务器是一台主机,对请求不属于自己地址的solicitation的请求能够代替拥有这些地址的主机做出应答。有了代理可以让不同LAN主机互相通信。
Linux内核支持的邻居协议中,只有IPV4和IPV6可以使用代理。
使用代理的条件
代理服务器只有在下列条件时才会对solicitation请求做应答:
- 请求地址和代理服务器收到报文接口上配置的地址不属于同一子网。
- 启动代理功能。
代理可以基于设备和基于目的地址进行配置使用。判断代理是否需要处理一个报文的逻辑如下:
ARP solicitation请求总是发往L2广播地址,这样可以使所有共享介质上的主机可以收到请求报文。对请求报文进行可达性确认时,ARP使用单播。
发送和处理Solicitation请求
当接收到solicitation请求的主机启动桥接功能时,该主机按照网桥配置对该报文进行处理转发出去。桥接报文处理先于邻居功能的作用,具体逻辑如下图所示。
Linux关闭桥接功能,下面因素影响Linux是否应答接收的Solicitation请求:
- 逻辑子网,请求解析地址和接收请求接口上地址属于同一逻辑子网时,可以直接通信,否则需要路由器的帮助才能完成通信。
- 物理子网,当两台主机属于同一LAN时,理论上可以直接通信,但是实际上还需要查看L3配置。
- 代理服务器的要求。
邻居状态和网络不可达探测。
传送一个报文时,L3工作流程:
可到达性
如果内核有有证据表明接受者可以接收到报文,则认为这台主机是可达主机。
NUD状态转换
IPv6定义一种NUD机制,可用于快速判断邻居释放断线或者死机。
每个邻居项可以由以下事件创建:
- 传送solicitation请求
- 收到来自邻居的请求
基本状态
- NUD_NONE邻居刚被创建,还没有状态可用。
- NUD_INCOMPLETE:solicitation请求已经发出,但是还没有收到应答。这个状态下不使用任何硬件地址。
- NUD_REACHABLE 邻居的地址放入缓存,并且知道该邻居可到达。
- NUD_FAILED由于solicitation请求失败,将邻居标记位不可达。
- NUD_STALE、NUD_DELAY、NUD_PROBE状态转换阶段。
- NUD_NOARP该状态标记不进行任何L3到L2地址映射的邻居。
- NUD_PERMANENT邻居L2地址静态配置。
可到达性确认
L3地址到L2地址的映射时可以改变的,因此需要对缓存中的信息进行定期确认,看是否有信息在一定时间内没有使用。这个过程叫做可达性确认。在进行可达性确认时,缓存中的信息仍然可以使用。
NUD_STALE、NUD_DELAY和NUD_PROBE三个状态,都支持可到达性确认。使用这些状态的原因是,只有一个报文要发送到相关邻居时,才启动可到达确认。
- NUD_STALE缓存中记录的邻居地址已经有一段时间没有进行确认了,下一次有报文要发送到这个邻居时,需要启动可到达性确认过程。
- NUD_DELAY当需要发送报文到该邻居,且该邻居处于NUD_STALE时,就进入这个状态。NUD_DELAY状态表示一个时间窗口,窗口期内可以通过外部主机证实邻居可到达性,比如处于该状态的邻居发来一个报文。为了进行可达性确认,该状态给网络层一个时间期限,期限内内核不发出solicitation请求以降低网络带宽和CPU利用率,如果没有得到确认,缓存就进入NUD_PROBE桩体。
- NUD_PROBE在NUD_DELAY期间内,内核没有收到可达性确认,其状态转变为NUD_PROBE,开始solicitation处理。
Solicitation应答的接收可以使任何状态转换到NUD_REACHABLE状态。而外部认证只有在NUD_STALE状态时才可以。