【linux环境编程】 ARP编程

(注:部分摘自”Linux C编程一站式学习“)

以太网(RFC 894)帧格式

图一 以太网数据包类型

其中的源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位,是在网卡出厂时固化的。用ifconfig命令看一下,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地址。协议字段有三种值,分别对应IP、ARP、RARP。帧末尾是CRC校验码。

以太网帧中的数据长度规定最小46字节,最大1500字节,ARP和RARP数据包的长度不够46字节,要在后面补填充位。最大值1500称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU了,则需要对数据包进行分片(fragmentation)。ifconfig命令的输出中也有“MTU:1500”。注意,MTU这个概念指数据帧中有效载荷的最大长度,不包括帧首部的长度。

在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。

每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间(一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?

图二 arp数据包类型

注意到源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型,1为以太网,协议类型指要转换的地址类型,0x0800为IP地址,后面两个地址长度对于以太网地址和IP地址分别为6和4(字节),op字段为1表示ARP请求,op字段为2表示ARP应答。

有了上面的基础知识,下面我们就来实战编程:

1、确定socket的参数

根据图一,我们可以看出arp,rarp和ip虽然同属于网络层(又名IP层),但是他们的数据包装是独立的。虽然icmp和igmp也处在IP层,但是它们又需要ip数据报的包装。所以我们在为arp和rarp建立socket的时候,就不能利用ip的原始数据报(SOCK_RAW)了,我们需要最原始的以太网帧的数包(SOCK_PACKET);在对于网络类型的选择上,可以根据需要选择IPv4(AF_INET)或IPv6(AF_INET6);arp的协议类型跟以太网帧数据类型一样,所以应该是0x0806,在linux的”Ethernet
Protocol ID“中定义了

#define ETH_P_ARP 0x0806

那么,我们就可以简单的以为socket套接字的创建是:sfd = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP));

2、确定ARP数据包的结构

根据图二,确定如下结构体

struct arp_packet
{
    //以太网首部
    unsigned char ap_dstmac[6];  //6字节
    unsigned char ap_srcmac[6];  //6字节
    unsigned short ap_frame;     //2字节
    //arp
    unsigned short ap_hwtype;    //2字节:硬件地址类型
    unsigned short ap_prototype; //2字节:软件地址类型
    unsigned char  ap_hwlen;     //1字节:硬件地址长度
    unsigned char  ap_prolen;    //1字节:软件地址长度
    unsigned short ap_op;        //2字节:操作类型
    unsigned char  ap_frommac[6];//6字节
    unsigned char  ap_fromip[4]; //4字节
    unsigned char  ap_tomac[6];  //6字节
    unsigned char  ap_toip[4];   //4字节
    //18字节:填充字节,因为以太网数据最少要46字节
    unsigned char  ap_padding[18];
};

3、获取本地ip地址和mac地址

linux里面提供了mac地址和ip地址获取的ioctl参数;

#define SIOCGIFADDR 0x8915      /* get PA address       */
#define SIOCSIFADDR 0x8916      /* set PA address       */
#define SIOCSIFHWADDR   0x8924      /* set hardware address     */
#define SIOCGIFHWADDR   0x8927      /* Get hardware address     */

在获取本机mac地址和ip地址之前,我们需要告诉ioctl我们要获取那个网卡的参数:

struct ifreq  eth;
strcpy(eth.ifr_name,"eth0");
/* Get eth0 Hardware Address */
int ret = ioctl(fds,SIOCGIFHWADDR, ð);
if(ret < 0){
	perror("Get Hardware Address Fail:");
	goto close_socket;
}
/* Get eth0 IP Address */
ret = ioctl(fds, SIOCGIFADDR, ð);
if(ret < 0){
	perror("Get IP Address Fail:");
	goto close_socket;
}
unsigned char mac_addr[6];
memcpy(mac_addr,eth.ifr_hwaddr.sa_data,6);
/* In "struct sockaddr",ip address starts from 'sa_data' two bytes later*/
unsigned char ip_addr[4];
memcpy(ip_addr,eth.ifr_addr.sa_data+2,4);

4、数据包数据填写

struct arp_packet arp_in;

bzero(&arp_in,sizeof(struct arp_packet));
unsigned char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
memcpy(arp_in.ap_dstmac,broadcast_mac,6);
memcpy(arp_in.ap_srcmac,mac_addr,6);
arp_in.ap_frame = htons(ETH_P_ARP);
arp_in.ap_hwtype = htons(0x0001);
arp_in.ap_prototype = htons(ETH_P_IP);
arp_in.ap_hwlen = 6;
arp_in.ap_prolen = 4;
arp_in.ap_op = htons(0x0001);//0x0001-ARP req 0x0002-ARP Reply
memcpy(arp_in.ap_frommac,mac_addr,6);
memcpy(arp_in.ap_fromip,ip_addr,4);

5、数据与接收

由于在这种情况下,对方的mac地址都是未知的,而且数据的发送也是作为广播模式发送。

所以这个时候我们只需告诉底层我们需要用那个网卡发送就可以了。

struct sockaddr eth;
eth.sa_family = AF_INET;
strcpy(eth.sa_data,"eth0");

ret = sendto(fds,&arp_in,sizeof(struct arp_packet),0,
		 (struct sockaddr *)ð,sizeof(struct sockaddr));
if(ret < 0){
	perror("Send Reqire ARP Packet Fail:");
	goto close_socket;
}
struct arp_packet arp_rc;
socklen_t slen = sizeof(struct sockaddr);
bzero(&arp_rc,sizeof(struct arp_packet));
ret = recvfrom(fds,&arp_rc,sizeof(struct arp_packet),0,
		   (struct sockaddr *)ð,&slen);
if(ret < 0){
	perror("Receive Replay ARP Packet Fail:");
	goto close_socket;
}

....

close_socket;
close(fds);
return (ret > 0 ? 1 : ret);

6、问题到此结束,测试后的结果如下:

-----------------------------Sendto----------------------------
Dest MAC:FF:FF:FF:FF:FF:FF
SRC  MAC:00:22:15:67:F8:16
HW  TYPE:0806
From    :210.42.158.204
To      :210.42.158.212
ARP   OP:0100
-----------------------------recvfrom-------------------------
Dest MAC:00:22:15:67:F8:16
SRC  MAC:00:E0:4C:DC:AA:1E
HW  TYPE:0806
From    :210.42.158.212
To      :210.42.158.204
ARP   OP:0200

至于ARP攻击,我小测试了一把,还是可以的。但是许多电脑有arp防护,或者防火墙,arp攻击时没有用的。

为了社会的安宁,这里我就不把arp攻击的代码贴出来了。

如果有什么问题,请各位留言指点!谢谢!

时间: 2024-08-01 10:43:40

【linux环境编程】 ARP编程的相关文章

Linux环境下网络编程杂谈&lt;&lt;转&gt;&gt;

今天我们说说“Pre-网络编程”.内容比较杂,但都是在做网络应用程序开发过程中经常要遇到的问题. 一.大端.小端和网络字节序 小端字节序:little-endian,将低字节存放在内存的起始地址: 大端字节序:big-endian,将高字节存放在内存的其实地址. 例如,数字index=0x11223344,在大小端字节序方式下其存储形式为: 上图一目了然的可以看出大小端字节序的区别. 还有另外一个概念就是网络字节序.网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型.操作

【Linux&amp;C++】Linux环境下C++编程

在阅读的过程中有任何问题,欢迎一起交流 邮箱:[email protected]   QQ:1494713801 在linux下,开发工具被切割成一个个独立的小工具.各自处理不同的问题.例如: 编辑器(emacs, vim)用来进行编辑程序的 调试器(gdb) 用来调试程序 编译器(GCC) 用来编译和链接程序的 性能分析工具(gcov, gprof) 用来优化程序的 文档生成器(doxygen) 用来生成文档的 同时,还有一些系统工具和系统知识,我们是很有必要了解的: makefile  程序

Linux下C++的编程——开发环境搭建与第一个程序

上一篇文章Linux下C++的编程--开偏介绍中我们已经介绍了GUN.GCC.G++等一些重要的概念,现在应该开始动手实践了! 开发工具的安装 环境 Distributions版本:CentOS 6.7 Linux内核片:2.6.32-573.3.1.el6.i686 一般Linux安装完之后默认就已经安装了GCC(GNU Compiler Collection),你可以查看一下gcc和g++的版本号检查gcc和g++是否已经安装. [luowf@luoweifu ~]$ gcc -v gcc

Linux环境编程之共享内存区(一):共享内存区简介

Spark生态圈,也就是BDAS(伯克利数据分析栈),是伯克利APMLab实验室精心打造的,力图在算法(Algorithms).机器(Machines).人(People)之间通过大规模集成,来展现大数据应用的一个平台,其核心引擎就是Spark,其计算基础是弹性分布式数据集,也就是RDD.通过Spark生态圈,AMPLab运用大数据.云计算.通信等各种资源,以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界.Spark生态圈已经涉及到机器学习.数据挖掘.

Linux环境编程之共享内存区(二):Posix共享内存区

现在将共享内存区的概念扩展到将无亲缘关系进程间共享的内存区包括在内.Posix提供了两种在无亲缘关系进程间共享内存区的方法: 1.内存映射文件:由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件.(上一节就是这种技术) 2.共享内存区对象:由shm_open打开一个Posix名字(也许是在文件系统中的一个路径名),所返回的描述符由mmap函数映射到当前进程的地址空间.(本节内容) Posix共享内存区涉及以下两个步骤要求: 1.指定一个名字参数调用shm_open

Linux环境编程之文件I/O(六):文件属性

引言: 在Linux中使用ls -l filename命令查看filename的属性时,会列出文件的9种属性,例如:ls -l /etc/fstab -rw-r--r-- 1 root root 1102 2013-10-12 02:33 /etc/fstab 从左到右分别是类型与权限.文件个数.该文件或目录的拥有者.所属的组.文件大小.创建时间.文件名 以上这些文件属性的信息,都存放在一个stat的结构体中.下面就来分析一下这个结构体. 要想查看一个文件的stat结构体,可以通过stat类函数

【原创】Linux环境的图形系统和AMD显卡驱动编程(2)——Framebuffer、DRM、EXA和Mesa简介

1. Framebuffer Framebuffer驱动提供基本的显示,framebuffer驱动操作的硬件就是一个显示控制器和帧缓存(一片位于系统主存或者显卡显存).Framebuffer驱动向应用程序提供/dev/fbx的设备接口,应用程序通过读写这个设备节点实现对显示控制器和帧缓存. 下面这个程序显示了应用程序操作操作framebuffer节点的过程.运行这个程序,将在屏幕上方显示一个正方形(这里省略了错误检查代码). 1 #include <stdio.h> 2 #include &l

Linux环境编程之文件I/O(四):文件I/O的数据结构

(一) Linux系统支持不同进程间共享打开的文件.内核使用三种数据结构表示打开的文件:进程表项.文件表项.v节点表. 1.进程表项:每个进程在进程表中都有一个记录项,记录项中年包含有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项.与每个文件描述符相关联的是: a.文件描述符标志 b.指向一个文件表项的指针 2.内核为所有打开文件维持一张文件表.每个文件表项包含: a.文件状态标志,如读写.添加.同步和非阻塞等. b.当前文件偏移量 c.指向该文件v节点表项的指针 3.每个打开的文

【原创】Linux环境的图形系统和AMD显卡驱动编程(1)——Linux环境下的图形系统简介

Linux/Unix环境下最早的图形系统是Xorg图形系统,Xorg图形系统通过扩展的方式以适应显卡和桌面图形发展的需要,然而随着软硬件的发展,特别是嵌入式系统的发展,Xorg显得庞大而落后.开源社区开发开发了一些新的图形系统,比如Wayland图形系统. 由于图形系统.3D图形本身的复杂以及历史原因,Linux下的图形系统相关的源码庞大而且复杂,而且缺少学习的资料(所有源代码分析或者驱动编程的书籍都很少介绍显卡驱动).在后续一系列文章中,笔者将从对AMD硬件编程的角度出发对部分问题做一个简单的

Golang在Linux环境下的POSIX风格socket编程

这里给出一个服务端和客户端,服务端可以接受多个连接,并且利用Go的杀手特性go和channel来替代select进行数据的接收. 服务端: package main import ( "fmt" . "syscall" ) func RecvRoutine(sockfd int, session chan string) { var buffer []byte = make([]byte, 3000) for { if length, err := Read(soc