ARP(Address Resolution Protocol)表是用来存储本地主机与通信对象MAC地址的映射关系的表格。ARP协议是网络层和数据链路层之间的接口协议,因此ARP表是在数据链路层上实现的。
一、ARP表的基本结构
在正式介绍ARP表之前,我们需要先了解一下ARP表的基本结构。ARP表主要由以下几个部分组成:
struct arp_table_entry { struct net_device *dev; __be32 ip; unsigned char haddr[ETH_ALEN]; };
其中:
- dev:是一个指针,指向本地主机的网络接口设备;
- ip:是本地主机与通信对象的IP地址;
- haddr:是本地主机与通信对象的MAC地址。
可以看出,ARP表的每一个条目都是一个二元组(IP地址,MAC地址),并且与本地主机的网络接口设备有关联。
二、ARP表的更新方式
ARP表一般由以下两种方式进行更新:
1、ARP请求/响应方式更新表项
当本地主机需要向另一个主机发送数据时,首先需要确定通信对方的MAC地址。此时,本地主机会在ARP表中查找对应的IP地址有没有对应的MAC地址。如果找到了对应的MAC地址,则直接使用该MAC地址进行通信;如果没有找到对应的MAC地址,则会以广播的方式发送一个ARP请求包,要求网络中所有主机返回其MAC地址。通常情况下,在网络中的其他主机接收到ARP请求包后,会根据请求包中的IP地址判断是否为自己,如果是,则向请求包中的源地址发送一个ARP响应包,告诉其自己的MAC地址是多少。
2、静态添加方式更新表项
在某些情况下,我们可以手动向ARP表中添加某些特定主机的IP地址和MAC地址的映射关系。这种方式可以通过一些管理工具来完成,使用静态添加方式更新的ARP表项通常不会主动被删除,直到手动删除为止。
三、ARP表的使用注意事项
1、ARP缓存时间的设置
ARP表中的每个条目都有一个存活时间,用来表示该条目的有效期限。当ARP表中的某个条目过期后,就需要重新发送ARP请求包,重新获得对应的MAC地址。在Linux系统中,ARP缓存时间一般可以通过以下几种方式进行设置:
- 通过/sys/class/net/ethX/arp_cache_timo文件进行设置;
- 通过net.ipv4.neigh.ethX.gc_stale_time内核参数进行设置;
- 通过ip命令进行设置(例如:ip neigh change 192.168.1.1 lladdr AA:BB:CC:DD:EE:FF nud permanent)
2、ARP欺骗攻击
ARP欺骗攻击是指攻击者伪造某个主机的MAC地址,并向网络中的其他主机发送虚假的ARP响应包,使其将攻击者的MAC地址与该主机的IP地址进行映射。这样,攻击者就可以窃取网络中的数据或者进行中间人攻击等各种恶意行为。为了防止ARP欺骗攻击,我们可以采取以下几个措施:
- 使用静态ARP表项,手动添加IP地址和MAC地址的映射关系;
- 在网络中增加一些入侵检测系统,监测网络中是否存在异常ARP请求/响应包;
- 限制网络中ARP响应包的广播范围,设置网络中的各个子网。
四、代码示例
下面是一个简单的示例程序,用于获取本地主机的ARP表:
#include #include #include #include #include #include #include #include #include #include #include #define ARP_TABLE_SIZE 1024 int main(int argc, char *argv[]) { struct arpreq arpreq; struct sockaddr_in *sin; struct ifreq ifreq; struct arp_table_entry { struct in_addr ip; unsigned char mac[ETH_ALEN]; } arp_table[ARP_TABLE_SIZE]; int sock = socket(AF_INET, SOCK_DGRAM, 0); memset(arp_table, 0, sizeof(arp_table)); memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, "eth0", IFNAMSIZ); if (ioctl(sock, SIOCGIFADDR, &ifreq) sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = inet_addr("192.168.1.1"); strncpy(arpreq.arp_dev, ifreq.ifr_name, IF_NAMESIZE); arpreq.arp_ha.sa_family = ARPHRD_ETHER; if (ioctl(sock, SIOCGARP, &arpreq) < 0) { perror("ioctl"); exit(1); } for (int i = 0; i sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = htonl(i); strncpy(arpreq.arp_dev, ifreq.ifr_name, IF_NAMESIZE); arpreq.arp_ha.sa_family = ARPHRD_ETHER; if (ioctl(sock, SIOCGARP, &arpreq) >= 0) { memcpy(arp_table[i].mac, arpreq.arp_ha.sa_data, ETH_ALEN); printf("%s == %02x:%02x:%02x:%02x:%02x:%02xn", inet_ntoa(sin->sin_addr), arp_table[i].mac[0], arp_table[i].mac[1], arp_table[i].mac[2], arp_table[i].mac[3], arp_table[i].mac[4], arp_table[i].mac[5]); } } close(sock); return 0; }