原始套接字发送ARP数据包

什么是ARP协议

    ARP协议是Address Resolution Protocol(地址解析协议)的缩写。在局域网中,网络中实际传输的是数据帧,数据帧里面有目的主机的MAC地址。但这个目的MAC地址是如何获取的呢?就是通过地址解析协议获得的。所谓“地址解析”就是主机在发送数据帧之前将目标IP地址转化成目标MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。

ARP协议的封装

图1 ARP数据报格式

1.硬件类型字段:2字节。用来定义运行ARP的网络类型。每一个局域网基于其类型被指定一个整数。例如,以太网的类型为1。

2.协议类型字段:2字节。用来定义高层协议的类型。例如,对IPv4协议,这个字段的值为0x0806。

3.硬件地址长度:1字节。用来定义以字节为单位的物理地址长度。例如,对以太网这个值为6。

4.协议地址长度:1字节。用来定义以字节为单位的协议地址长度。例如,对IPv4这个值是4。

5.操作字段:占2字节。用来定义分组的类型。例如,ARP请求分组为1,ARP响应分组为2。

6.发送端硬件地址:6字节。
7.发送端协议地址:4字节。
8.目标端硬件地址:6字节。
9.目标端协议地址:4字节。

    如图2所示,ARP数据报是直接封装在数据链路层的数据帧中。

图2 ARP数据报的封装

原始套接字发送ARP数据包

    通过以下建立发送ARP数据包的原始套接字:

socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) )

    第一个参数协议族为PF_PACKET,定义数据链路层上封包;第二个参数socket类型为SOCK_RAW,定义为原始套接字;第三个参数协议类型不能为0,此处定义为ARP协议。
    由于是数据链路层上封包,地址定义需要使用协议无关的sockaddr_ll结构,填写其中的接口索引字段。
    按照以太网帧的头部和ARP数据包定义构造发送数据,用sendto函数发送。具体的代码见附录1

实验

    实验采用两台虚拟机,IP地址分别为192.168.182.133和192.168.182.132,使用192.168.182.133作为ARP请求端。
    先在ARP请求端删除关于192.168.182.132的ARP记录:

arp -d 192.168.182.132

    然后打开wireshark进行抓包,最后执行编写的ARP发送程序,观察wireshark结果。如图3所示,对方进行了ARP应答。

图3 wireshark抓包结果

附录1

#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>

int main()
{
	int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
	if (sockfd == -1)
	{
		printf("socket error\n"); return 0;
	}

	//获取网卡信息
	sockaddr_ll addr_ll;
	memset(&addr_ll, 0, sizeof(sockaddr_ll));
	addr_ll.sll_family = PF_PACKET;

	ifreq ifr;
	strcpy(ifr.ifr_name, "ens33");
	if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFINDEX\n"); return 0;
	}
	addr_ll.sll_ifindex = ifr.ifr_ifindex; //接口索引


	if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFADDR\n"); return 0;
	}
	char* ipSrc = inet_ntoa(((struct sockaddr_in*)(&(ifr.ifr_addr)))->sin_addr);
	printf("ip address : %s\n", ipSrc); //source ip

	if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFHWADDR\n"); return 0;
	}
	unsigned char macSrc[ETH_ALEN];
	memcpy(macSrc, ifr.ifr_hwaddr.sa_data, ETH_ALEN); //mac address
	printf("mac address");
	for (int i = 0; i < ETH_ALEN; i++)
		printf(":%02x", macSrc[i]);
	printf("\n");

	//填充以太网首部 和 ARP信息
	unsigned char macDst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	ether_header header;
	memcpy(header.ether_dhost, macDst, ETH_ALEN);
	memcpy(header.ether_shost, macSrc, ETH_ALEN);
	header.ether_type = htons(ETHERTYPE_ARP);

	ether_arp arp;
	arp.arp_hrd = htons(ARPHRD_ETHER);
	arp.arp_pro = htons(ETHERTYPE_IP);
	arp.arp_hln = ETH_ALEN;
	arp.arp_pln = 4; //IPv4
	arp.arp_op = htons(ARPOP_REQUEST);

	in_addr src_in_addr, dst_in_addr;
	inet_pton(AF_INET, ipSrc, &src_in_addr);
	inet_pton(AF_INET, "192.168.182.132", &dst_in_addr);

	memcpy(arp.arp_sha, macSrc, ETH_ALEN);
	memcpy(arp.arp_spa, &src_in_addr, 4);
	memcpy(arp.arp_tha, macDst, ETH_ALEN);
	memcpy(arp.arp_tpa, &dst_in_addr, 4);

	unsigned char sendBuf[sizeof(ether_header) + sizeof(ether_arp) ];
	memcpy(sendBuf, &header, sizeof(ether_header) );
	memcpy(sendBuf + sizeof(ether_header), &arp, sizeof(ether_arp));
	int len = sendto(sockfd, sendBuf, sizeof(sendBuf), 0, (const sockaddr*)&addr_ll, sizeof(addr_ll) );
	if (len > 0)
	{
		printf("send success\n");
	}

	return 0;
}

参考

刘兵,刘欣等编著.计算机网络基础[M].北京:中国水利水电出版社.2006.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值