大家好,欢迎来到IT知识分享网。
目录
1.ARP协议简介
ARP(Address Resolution Protocol)协议是一种在局域网中解析MAC地址的协议。
当主机要向局域网中的另一台主机发送数据时,需要知道目标主机的MAC地址。ARP协议就是用来解析目标主机的MAC地址的。主机会广播一个ARP请求包,请求目标主机回应自己的MAC地址。目标主机接收到请求后会返回一个ARP响应包,包括自己的MAC地址。这样,请求主机就可以通过MAC地址向目标主机发送数据了。
2.ARP工作原理
图 1 ARP工作原理
2.1 局域网通信
局域网主机A和主机B通信,如果双方原来没有通信过,主机A只知道主机B的IP地址,不知道主机B的MAC地址,此时主机A和主机B无法正常通信。
局域网通信的基础通信双方都得知道对方的MAC地址,MAC地址通常是存储在主机的ARP表中。
此时主机A会尝试去获取主机B的MAC地址,获取的MAC地址的方式是发送ARP请求(以广播形式发送),主机B如果收到ARP请求会回复ARP响应给主机A。
主机A收到响应后会把主机B的IP和MAC地址记录在ARP表中,此时主机A能够发送数据给主机B,主机B因为收到主机A的ARP请求,通过ARP请求知道主机A的MAC地址,把主机A的IP和MAC地址记录在ARP表中,主机B也能给主机A发送数据,正阳主机A和主机B就能正常通信。
2.2 跨网段通信
主机A要访问一个公网主机,由于主机A和公网主机IP地址不再同一网段,此时需要用到路由功能,路由功能后续章节会详细介绍。
我们只要记住一个核心点,路由功能核心作用就是找到去往目的IP的网关IP地址。
网关IP地址必须和主机A处于同一局域网。找到网关IP地址的目的是为了获取网关MAC地址,获取到网关MAC地址,可以通过网关MAC地址把数据包发送给网关,让网关转发数据包至公网。
如果通过网关IP获取到网关MAC地址可以参考局域网通信。
跨网段通信流程:
步骤1:通过路由查找到网关IP地址
步骤2:如果不知道网关MAC地址,需要通过ARP协议获取到网关MAC地址。
步骤3:把网关MAC地址填入以太网报文头部,将数据包发给网关。
步骤4:网关转发数据包至公网。
3.ARP协议解析
3.1 ARP报文结构
图 2 ARP报文结构
- 硬件类型:2字节,表示使用的网络类型,例如以太网、令牌环等。
- 协议类型:2字节,表示使用的协议类型,例如IPv4、IPX等。
- 硬件地址长度:1字节,表示硬件地址的长度,例如以太网地址长度为6字节。
- 协议地址长度:1字节,表示协议地址的长度,例如IPv4地址长度为4字节。
- 操作码:2字节,表示ARP请求(数值为1)或ARP响应(数值为2)。
- 发送方硬件地址:6字节,表示发送方的硬件地址。
- 发送方协议地址:4字节,表示发送方的协议地址。
- 目标硬件地址:6字节,表示目标的硬件地址。
- 目标协议地址:4字节,表示目标的协议地址。
3.2 ARP请求
图 3 ARP请求报文
ARP请求各个字段如何填充如上图:
硬件类型:1,ARPHRD_ETHER类型
协议类型:0x8000,IPv4协议
硬件地址长度:6字节,MAC地址长度
协议地址长度:4字节,IP地址长度
操作码:1,ARP请求码
发送方MAC地址:14:23:0a:39:e3:75
发送方IP地址:192.168.1.1
目的MAC地址:00:00:00:00:00:00
目的IP地址:192.168.1.19
3.3 ARP响应
图 4 ARP响应报文
ARP响应各个字段如何填充如上图:
硬件类型:1,ARPHRD_ETHER类型
协议类型:0x8000,IPv4协议
硬件地址长度:6字节,MAC地址长度
协议地址长度:4字节,IP地址长度
操作码:2,ARP响应
发送方MAC地址:00:93:37:5b:d9:2f
发送方IP地址:192.168.1.19
目的MAC地址:14:23:0a:39:e3:75
目的IP地址:192.168.1.1
4.ARP攻击
图 5 ARP攻击
ARP攻击(Address Resolution Protocol attack)是一种网络攻击方式,它利用ARP协议进行攻击,通过伪造或欺骗网络中的ARP响应包,将合法网络设备的IP地址映射到攻击者所控制的设备上,从而使攻击者可以窃取或篡改网络中的数据流量。
ARP攻击可以分为两种类型,一种是ARP欺骗(ARP Spoofing),另一种是ARP投毒(ARP Poisoning)。
4.1 ARP欺骗
攻击者会向网络中的设备发送伪造的ARP响应包,使其将攻击者所控制的设备的MAC地址当作目标设备的MAC地址,从而使攻击者可以拦截、篡改或窃取网络中的数据流量。
4.2 ARP投毒
攻击者会向网络中的设备发送大量的伪造ARP响应包,从而使网络设备的ARP缓存被攻击者所控制的设备所替换,这样一来,攻击者就可以直接访问目标设备,而无需再进行ARP欺骗攻击。
4.3 如何预防ARP攻击?
a.使用静态ARP表:将网络设备的IP地址和MAC地址的映射关系手动输入到静态ARP表中,可以防止攻击者篡改ARP响应包。
b.使用端口安全(Port Security):在交换机上配置端口安全,限制每个端口能够连接的MAC地址数量,可以有效地防止ARP攻击。
c.使用ARP防火墙(ARP Firewall):ARP防火墙可以监控网络流量中的ARP请求和响应包,并根据预设的规则进行过滤和阻断,从而有效地防止ARP攻击。
d.加密网络流量:使用加密技术(如SSL、TLS等)对网络流量进行加密,可以防止数据被窃听和篡改,从而有效地保护网络安全。
5.ARP编程示例
5.1 ARP请求示例代码
#include <stdio.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/udp.h> #include <netinet/ip.h> #include <linux/in.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_arp.h> uint8_t src_mac[ETH_ALEN] = {0x08,0x00,0x27,0x06,0x38,0xba}; uint8_t nexthop_mac[ETH_ALEN] = {0x08,0x00,0x27,0xc1,0xdf,0xea}; #define LOCAL_IP "192.168.1.18" #define PEER_IP "192.168.1.19" #define MAX_BUF_SIZE (2048) #define IP_ADDR_LEN (4) struct _arphdr { __be16 ar_hrd; __be16 ar_pro; unsigned char ar_hln; unsigned char ar_pln; __be16 ar_op; unsigned char ar_sha[ETH_ALEN]; unsigned char ar_sip[4]; unsigned char ar_tha[ETH_ALEN]; unsigned char ar_tip[4]; }; uint32_t create_pack(char *buf) { struct ethhdr *eh = (struct ethhdr *)buf; memcpy(eh->h_dest, nexthop_mac, ETH_ALEN); memcpy(eh->h_source, src_mac, ETH_ALEN); eh->h_proto = htons(ETH_P_ARP); struct _arphdr *ah = (struct _arphdr *)(buf + ETH_HLEN); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ETH_ALEN; ah->ar_pln = IP_ADDR_LEN; ah->ar_op = htons(ARPOP_REQUEST); memcpy(ah->ar_sha, src_mac, ETH_ALEN); uint32_t src_ip = inet_addr(LOCAL_IP); memcpy(ah->ar_sip, &src_ip, IP_ADDR_LEN); memcpy(ah->ar_tha, nexthop_mac, ETH_ALEN); uint32_t peer_ip = inet_addr(PEER_IP); memcpy(ah->ar_tip, &peer_ip, IP_ADDR_LEN); return ETH_HLEN + sizeof(struct _arphdr); } int main(int argc , char *argv[]) { int ret; int sockfd; char send_buf[MAX_BUF_SIZE] = {0}; struct sockaddr_ll local; struct sockaddr_ll peer; sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if (sockfd == -1) { perror("socket error"); return -1; } bzero(&peer, sizeof(struct sockaddr_ll)); peer.sll_family = PF_PACKET; peer.sll_protocol = htons(ETH_P_ARP); peer.sll_ifindex = 2; peer.sll_hatype = ARPHRD_ETHER; peer.sll_pkttype = PACKET_OTHERHOST; peer.sll_halen = ETH_ALEN; memcpy(peer.sll_addr, nexthop_mac, ETH_ALEN); uint32_t slen = create_pack(send_buf); while(1) { ret = sendto(sockfd, send_buf, slen, 0, (struct sockaddr *)&peer, sizeof(peer)); if (ret <= 0) { printf("sendto ret:%d, errno:%d(%s)\n", ret, errno, strerror(errno)); break; } sleep(1); } close(sockfd); return 0; }
5.2 ARP响应示例代码
#include <stdio.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <linux/in.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_arp.h> uint8_t src_mac[ETH_ALEN] = {0x08,0x00,0x27,0xc1,0xdf,0xea}; uint8_t nexthop_mac[ETH_ALEN] = {0x08,0x00,0x27,0x06,0x38,0xba}; #define LOCAL_IP "192.168.1.19" #define PEER_IP "192.168.1.18" #define IP_ADDR_LEN (4) #define MAX_BUF_SIZE (2048) struct _arphdr { __be16 ar_hrd; __be16 ar_pro; unsigned char ar_hln; unsigned char ar_pln; __be16 ar_op; unsigned char ar_sha[ETH_ALEN]; unsigned char ar_sip[4]; unsigned char ar_tha[ETH_ALEN]; unsigned char ar_tip[4]; }; void print_arp(char *msg, const char *buf) { struct _arphdr *ah = (struct _arphdr *)(buf + ETH_HLEN); printf("%s\n" "硬件类型:%u\n" "协议类型:%u\n" "硬件地址长度:%u\n" "协议地址长度:%u\n" "操作码:%u\n" "发送方硬件地址:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n" "发送方协议地址:%02hx.%02hx.%02hx.%02hx\n" "接收方硬件地址:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n" "接收方协议地址:%02hx.%02hx.%02hx.%02hx\n", msg, ntohs(ah->ar_hrd), ntohs(ah->ar_pro), ah->ar_hln, ah->ar_pln, ntohs(ah->ar_op), ah->ar_sha[0], ah->ar_sha[1], ah->ar_sha[2], ah->ar_sha[3], ah->ar_sha[4], ah->ar_sha[5], ah->ar_sip[0], ah->ar_sip[1], ah->ar_sip[2], ah->ar_sip[3], ah->ar_tha[0], ah->ar_tha[1], ah->ar_tha[2], ah->ar_tha[3], ah->ar_tha[4], ah->ar_tha[5], ah->ar_tip[0], ah->ar_tip[1], ah->ar_tip[2], ah->ar_tip[3]); } int parse_pack(char *msg, char *buf) { print_arp(msg, buf); return 0; } uint32_t create_pack(char *sbuf, char *rbuf) { struct ethhdr *eh = (struct ethhdr *)sbuf; memcpy(eh->h_dest, nexthop_mac, ETH_ALEN); memcpy(eh->h_source, src_mac, ETH_ALEN); eh->h_proto = htons(ETH_P_ARP); struct _arphdr *rah = (struct _arphdr *)(rbuf + ETH_HLEN); struct _arphdr *ah = (struct _arphdr *)(sbuf + ETH_HLEN); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ETH_ALEN; ah->ar_pln = IP_ADDR_LEN; ah->ar_op = htons(ARPOP_REPLY); memcpy(ah->ar_sha, src_mac, ETH_ALEN); uint32_t src_ip = inet_addr(LOCAL_IP); memcpy(ah->ar_sip, &src_ip, IP_ADDR_LEN); memcpy(ah->ar_tha, rah->ar_sha, ETH_ALEN); memcpy(ah->ar_tip, rah->ar_sip, IP_ADDR_LEN); return ETH_HLEN + sizeof(struct _arphdr); } bool isvalid(const char *buf) { struct ethhdr *eh = (struct ethhdr *)buf; if (ntohs(eh->h_proto) == ETH_P_ARP) return true; return false; } int main(int argc , char *argv[]) { int ret; int sockfd; char send_buf[MAX_BUF_SIZE] = {0}; char recv_buf[MAX_BUF_SIZE] = {0}; struct sockaddr_ll peer; socklen_t peerlen = 0; sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if (sockfd == -1) { perror("socket error"); return -1; } bzero(&peer, sizeof(struct sockaddr_ll)); peer.sll_family = PF_PACKET; peer.sll_protocol = htons(ETH_P_ARP); peer.sll_ifindex = 2; peer.sll_hatype = ARPHRD_ETHER; peer.sll_pkttype = PACKET_OTHERHOST; peer.sll_halen = ETH_ALEN; memcpy(peer.sll_addr, nexthop_mac, ETH_ALEN); while(1) { memset(recv_buf, 0, MAX_BUF_SIZE); ret = recvfrom(sockfd, recv_buf, MAX_BUF_SIZE, 0, NULL, NULL); if (ret <= 0) { printf("recvfrom ret:%d error\n", ret); break; } else { bool bret = isvalid(recv_buf); printf("ret:%d, bret:%d\n", ret, bret); if (bret == false) { continue; } parse_pack("recv buf", recv_buf); memset(send_buf, 0, MAX_BUF_SIZE); int slen = create_pack(send_buf, recv_buf); slen += 18; printf("sendbuf len:%d\n", slen); ret = sendto(sockfd, send_buf, slen, 0, (struct sockaddr *)&peer, sizeof(peer)); if (ret <= 0) { printf("sendto ret:%d error:%d(%s)\n", ret, errno, strerror(errno)); break; } } } close(sockfd); return 0; }
6.Linux ARP调试
6.1 查看ARP表
ip neigh
6.2 手动添加ARP表项
ip neigh add 192.168.1.19 dev enp0s3 lladdr 08:00:27:c1:df:ea
6.3 删除ARP表项
ip neigh del 192.168.1.19 dev enp0s3 lladdr 08:00:27:c1:df:ea
如果觉得本文对你有帮助,希望给个一键三连,支持博主,谢谢!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/136983.html