深入Linux ARP协议的源码研究

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系统中是如何实现的。

操作系统标签