1. ARP协议的概述
ARP(Address Resolution Protocol),地址解析协议,是用于将一个IP地址解析为对应的物理MAC地址的协议。在Linux系统中,ARP协议的实现主要在内核中进行。在本文中,我们将深入研究Linux内核中ARP协议源码的实现细节。
2. ARP协议的工作原理
ARP协议的工作原理主要包括ARP请求和ARP响应两个基本步骤。当主机A需要与主机B进行通信时,如果A不知道B的MAC地址,A就会发送一个ARP请求广播,要求网络上的所有主机回应自己的MAC地址。主机B收到ARP请求后,会将自己的MAC地址封装在ARP响应中发送给主机A。这样,主机A就获得了B的MAC地址,并可以建立起通信连接。
2.1 ARP请求的处理
当Linux内核接收到一个ARP请求报文时,会触发一个中断处理函数,该函数通常为arp_rcv()。在这个函数中,内核会对ARP请求进行处理,并生成相应的ARP响应报文。
static void arp_rcv(struct sk_buff *skb)
{
struct arphdr *arp;
arp = (struct arphdr *)skb->data;
switch (ntohs(arp->ar_op)) {
case ARPOP_REQUEST:
arp_process(skb); // 处理ARP请求
break;
case ARPOP_REPLY:
arp_recv_reply(skb); // 处理ARP响应
break;
default:
kfree_skb(skb); // 丢弃无效的ARP报文
break;
}
}
在arp_process()函数中,内核会获取ARP请求中的目标IP地址,并检查目标IP地址是否为本机IP地址。如果目标IP地址和本机IP地址相匹配,内核就会生成相应的ARP响应报文,并通过网络接口发送给发送ARP请求的主机。
static int arp_process(struct sk_buff *skb)
{
struct arphdr *arp;
struct net_device *dev;
arp = (struct arphdr *)(skb->data);
dev = skb->dev;
if (!arp_is_valid(dev, skb))
goto out;
if (arp->ar_tip == dev->ip_ptr->ifa_list->ifa_address) {
arp_send_reply(skb); // 生成ARP响应报文
}
out:
kfree_skb(skb);
return 0;
}
2.2 ARP响应的处理
当Linux内核接收到一个ARP响应报文时,会触发一个中断处理函数,该函数通常为arp_rcv()。在这个函数中,内核会对ARP响应进行处理,并更新本机的ARP缓存表。
static void arp_rcv(struct sk_buff *skb)
{
struct arphdr *arp;
arp = (struct arphdr *)skb->data;
switch (ntohs(arp->ar_op)) {
case ARPOP_REQUEST:
arp_process(skb);
break;
case ARPOP_REPLY:
arp_recv_reply(skb); // 处理ARP响应
break;
default:
kfree_skb(skb);
break;
}
}
在arp_rcv_reply()函数中,内核会根据ARP响应报文中的源IP地址和源MAC地址更新本机的ARP缓存表。如果ARP缓存表中已经存在相应的条目,且MAC地址不一致,内核会更新该条目的MAC地址为新的值。
static void arp_recv_reply(struct sk_buff *skb)
{
struct arphdr *arp;
arp = (struct arphdr *)(skb->data);
if (arp->ar_sip == dev->ip_ptr->ifa_list->ifa_address) {
update_arptable(arp->ar_sip, arp->ar_sha); // 更新ARP缓存表
}
kfree_skb(skb);
}
3. ARP缓存表的管理
在Linux内核中,ARP缓存表用于保存已解析的IP地址和对应的MAC地址。当主机需要与某个IP地址通信时,会首先在ARP缓存表中查找该IP地址的MAC地址。如果找到对应的条目,则直接使用该MAC地址进行通信;如果没有找到对应的条目,则需要发送ARP请求进行地址解析。
3.1 ARP缓存表的数据结构
Linux内核中的ARP缓存表使用一个哈希表来组织,每个哈希桶中保存了多个ARP缓存项。每个ARP缓存项包含了目标IP地址、目标MAC地址和对应的网络接口等信息。
struct arptable {
struct hlist_head *arptable;
unsigned int buckets;
rwlock_t lock;
};
struct arp_cache {
struct hlist_node node;
struct net_device *dev;
__be32 ip;
__be16 flags;
unsigned char haddr[ETH_ALEN];
};
3.2 添加ARP缓存项
当内核收到一个ARP响应报文时,会使用源IP地址和源MAC地址创建一个ARP缓存项,并将它添加到ARP缓存表中。
static void update_arptable(__be32 ip, unsigned char *mac)
{
struct arp_cache *entry;
entry = arp_create_entry(ip, mac); // 创建新的ARP缓存项
if (!entry)
return;
write_lock_bh(&arptable.lock);
arp_add_entry(entry); // 添加到ARP缓存表
write_unlock_bh(&arptable.lock);
}
在arp_create_entry()函数中,内核会申请内存并初始化一个新的ARP缓存项。然后,将源IP地址、源MAC地址和网络接口等信息填充进去。
static struct arp_cache *arp_create_entry(__be32 ip, unsigned char *mac)
{
struct arp_cache *entry;
entry = kmalloc(sizeof(struct arp_cache), GFP_ATOMIC);
if (!entry)
return NULL;
entry->ip = ip;
entry->flags = ARPTABLE_FLAG_RESOLVED;
memcpy(entry->haddr, mac, ETH_ALEN);
entry->dev = skb->dev;
return entry;
}
3.3 查找ARP缓存项
当主机需要与某个IP地址通信时,内核会在ARP缓存表中查找该IP地址的MAC地址。如果找到对应的条目,内核就可以直接使用MAC地址进行通信。
static struct arp_cache *arp_find_entry(__be32 ip)
{
struct arp_cache *entry;
struct hlist_node *p;
unsigned int hash = arp_hash(ip);
read_lock_bh(&arptable.lock);
hlist_for_each_entry(entry, p, &arptable.arptable[hash], node) {
if (entry->ip == ip) {
read_unlock_bh(&arptable.lock);
return entry; // 找到IP地址对应的ARP缓存项
}
}
read_unlock_bh(&arptable.lock);
return NULL; // 没有找到IP地址对应的ARP缓存项
}
4. 总结
本文深入研究了Linux内核中ARP协议的源码实现。我们了解了ARP协议的工作原理,包括ARP请求和ARP响应的处理,以及ARP缓存表的管理。通过深入研究源码,我们更加清楚了ARP协议在Linux系统中是如何实现的。