一 背景
arping 命令是用于发送arp请求到相邻主机的工具,通过目的主机ip获取目的主机mac地址,可用于探测局域网内某个ip是否已经被使用。
前段时间碰到一个问题,源(mask 16)和目的(mask 24)配置的网段长度不一致,且源端使用了*.0 结尾的地址,导致网络不通,arping探测被当作为网络地址返回失败,那么具体流程是什么样的呢。
二 现场模拟
正常情况下,同网段arping返回对方mac地址
指定源ip为网段地址,访问同一server端,返回失败。
三 流程分析
1 server端通过tcpdump抓包
如上图,上半部分为正常arping请求,下半部为sip为*.0情况下报文信息,可以看到该情况下,没有回复reply报文。
2 代码分析
static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
const struct arphdr *arp;
/* do not tweak dropwatch on an ARP we will ignore */
if (dev->flags & IFF_NOARP ||
skb->pkt_type == PACKET_OTHERHOST ||
skb->pkt_type == PACKET_LOOPBACK)
goto consumeskb;
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
goto out_of_mem;
/* ARP header, plus 2 device addresses, plus 2 IP addresses. */
if (!pskb_may_pull(skb, arp_hdr_len(dev)))
goto freeskb;
arp = arp_hdr(skb);
if (arp->ar_hln != dev->addr_len || arp->ar_pln != 4)
goto freeskb;
memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
return NF_HOOK(NFPROTO_ARP, NF_ARP_IN,
dev_net(dev), NULL, skb, dev, NULL,
arp_process);
consumeskb:
consume_skb(skb);
return NET_RX_SUCCESS;
freeskb:
kfree_skb(skb);
out_of_mem:
return NET_RX_DROP;
}
在经过一系列校验和hook函数过滤后,进入arp_process(), 该函数流程比较复杂,正常流程会在通过ip_route_input_noref()一系列路由检查后,返回 reply报文。
下面是ip_route_input_noref调用流程
ip_route_input_noref
ip_route_input_rcu
ip_route_input_slow
fib_validate_source
__fib_validate_source
最终通过fib_lookup(), 进行反向路由查找,其中*.0会被当做网络地址,返回res.type为RTN_BROADCAST
不满足条件,导致__fib_validate_source()返回EINVAL,最终结果就是没有回复reply报文,探测不通。