ARP协议图文详解,包含完整测试代码-程序员宅基地

技术标签: Linux网络编程  c语言  linux  物联网  网络协议  tcp/ip  

目录

1.ARP协议简介

2.ARP工作原理

2.1 局域网通信

2.2 跨网段通信

3.ARP协议解析

3.1 ARP报文结构

3.2 ARP请求

3.3 ARP响应

4.ARP攻击

4.1 ARP欺骗

4.2 ARP投毒

4.3 如何预防ARP攻击?

5.ARP编程示例

5.1 ARP请求示例代码

5.2 ARP响应示例代码

6.Linux ARP调试

6.1 查看ARP表

6.2 手动添加ARP表项

6.3 删除ARP表项


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

如果觉得本文对你有帮助,希望给个一键三连,支持博主,谢谢!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_28673511/article/details/130795407

智能推荐

安卓系统源码、内核下载_安卓内核源码下载-程序员宅基地

文章浏览阅读1.9k次。环境ubuntu16.041、在用户根目录下面新建文件夹bin2、PATH=~/bin:$PATH 也就是让环境变量包含用户根目录下的bin文件夹_安卓内核源码下载

退出qemu快捷键_qemu如何退出-程序员宅基地

文章浏览阅读1k次。先按下Ctrl + A,再按下x键(注意此处x应为小写,大写无效;Ctrl + A中A大小写均可)。_qemu如何退出

斑马线分析_儿童理发店将斑马线搬了进来,孩子都争着来当“交警”-程序员宅基地

文章浏览阅读337次。食堂君:设计儿童理发店似乎是一个少见的案例,我们能以下案例中,找出一些关于儿童的设计要点。itD首次对儿童理发品牌进行空间设计。设计之初,我们思考的一个要点是:如何将空间元素与斑马线儿童理发品牌自然地连接在一起,并通过整个空间故事让顾客对斑马线产生品牌记忆点?经过前期调研分析,我们发现大多儿童理发品牌的空间多使用动物和汽车元素,且无故事脉络串联,仅填塞各种儿童可能会喜欢的形象,而缺乏空间..._斑马线儿童理发品牌终端空间设计

caffe学习:calssfication示例解析_clasiffication_final-程序员宅基地

文章浏览阅读693次。结合前面学习的内容,整理一下caffe的官方示例_clasiffication_final

Webstorm的安装和使用基础教程-程序员宅基地

文章浏览阅读1.8k次。说一下使用Webstorm的原因,在此之前,我一直使用的是HBuilder编辑器,最近使用小程序框架mpvue开发,由于微信开发者工具对vue不太友好,vue编写界面无法查看,大家就推荐了Webstorm。于是,使用了大家推荐的这一款号称前端开发领域最好用的编辑器,果不其然,从此就不想再用别的了。1:安装官网地址:http://www...._idea怎么使用webstorm

【计算机毕业设计】基于SpringBoot+Vue高校食堂物流管理系统的设计与实现_高校食堂管理系统的设计与实现-程序员宅基地

文章浏览阅读199次,点赞3次,收藏3次。高校食堂物流管理系统,主要的模块包括管理员;首页、个人中心、管理员管理、用户管理、采购人员管理、财务管理、供应商信息管理、商品管理、出入库管理、基础数据管理,财务;首页、个人中心、财务管理、供应商信息管理、商品管理、出入库管理,仓库管理员;首页、个人中心、采购人员管理、财务管理、供应商信息管理、商品管理、出入库管理,采购;首页、个人中心、采购人员管理、供应商信息管理、商品管理、出入库管理等功能。_高校食堂管理系统的设计与实现

随便推点

HTML的列表标签,表格table和表单标签_html用ul写表格-程序员宅基地

文章浏览阅读710次。名词1名词1解释1...名词2名词2解释1名词2解释2..._html用ul写表格

HDU - 4333 Revolving Digits(扩展KMP)-程序员宅基地

文章浏览阅读187次。题目链接:点击查看题目大意:给出一个由 n 个数位组成的数字,现在可以通过将其不同的后缀移到前面来组成 n 个新的数字,现在要求出 n 个新数字进行去重后,有多少个新数字分别大于、等于、小于原数字如:1234进行上述转移可以得到的四个新数字分别为:1234,4123,3412,2341题目分析:如果暴力的比较虽然看似只需要枚举 n 个新的字符串,但是每个字符串的比较还需要花费O(n)的..._hdu - 4333

教练式辅导-GROW模型的分析与运用_用grow模型对下属进行辅导时,为了帮助下属看到更多的可能性,开启思路,管理者常用-程序员宅基地

文章浏览阅读2.1w次。http://pengtyao.iteye.com/blog/1462045_用grow模型对下属进行辅导时,为了帮助下属看到更多的可能性,开启思路,管理者常用

网线:568A 568B线序-程序员宅基地

文章浏览阅读10w+次,点赞23次,收藏111次。1.网线水晶头为什么要分568A、568B?这要从平行线交叉线说起。平行线:网线2头都做成568B标准,就叫平行线。用于双机不同级连接,比如交换机连电脑。交换机连路由器。交叉线:网线一头做成568B,另一头做成568A,就叫交叉线。用于双机同级连接,比如电脑连电脑,交换机连交换机。现在都是平行线做法,设备能够自己识别。2.568A和568B的区别区别在线序上。568A:白绿,绿,白橙,蓝,白蓝,橙,白棕,棕568B:白橙,橙,白绿,蓝,白蓝,绿,白棕,棕3.为什么不是颜色一致就可以?很多_568b

城市按A-Z json表_全国字母检索城市json-程序员宅基地

文章浏览阅读823次。城市按A-Z转载于https://blog.csdn.net/qq_36856163/article/details/97109683citys:{“hot”:[{“id”: 1,“spell”: “ab”,“name”: “北京”}, {“id”: 2,“spell”: “ab”,“name”: “上海”}],“A”: [{“id”: “161”,“spell”: “anshan”,“name”: “鞍山”}, {“id”: “226”,“spell”: “anqing_全国字母检索城市json

安装 Sublime-程序员宅基地

文章浏览阅读75次。2018 05 224月30更新版本 Build 3176Version 3.1 3176Sublime Text Build 3176 ( 3.1 )--------------- 安装 Sublime --------------官网: https://www.sublimetext.com/3中文官网: http://sublimetextcn.com/3/..._also available as a portable version

推荐文章

热门文章

相关标签