浅谈在Linux系统中以太网数据帧的监听与分析

时间:2022-08-05 04:41:04

浅谈在Linux系统中以太网数据帧的监听与分析

摘要:以太网数据传输通过广播实现,在同一网段的所有网卡事实上都可以访问在共享的物理介质上传输的所有数据。但在系统正常工作时,一个合法的网络接口应只响应两种数据帧:一是帧的目标MAC地址与本地MAC地址相符;二是帧的目标地址是广播地址。若要监听所有流经网卡的数据帧,当用于监听的主机连接在共享型以太网集线器上时,采用“混杂”模式可以监听到同一冲突域上传输的数据帧;但当监听的主机连接在交换机上时,可以利用交换机的端口镜像(Port Mirroring)功能实现监听。

关键词:数据帧;MAC地址;“混杂”模式;端口镜像

中图分类号:TP393文献标识码:A文章编号:1009-3044(2008)30-0564-03

On Monitor and Analysis of the Ethernet Data Frame in linux System

YANG Cheng-hui

(Jimen Light Industrial School,Xiamen 361022,China)

Abstract: The transmission of the data on Ethernet is achieved through broadcasting.In fact all the network cards in the same network have access to all the data transmitted by the shared physical agent. However,when the system works normally, a legitimate network interface should respond to only two types of data frames. One is when the target MAC address of the frame is consistent with the local MAC address. The other is when the target address of the frame is the broadcast address. To monitor all the data frames flowing from the network card, promiscuous mode is adopted to monitor the data frame transmitted in the same collision domain, when the host for monitoring connected to the shared Ethernet hub. Whereas when the host for monitoring is connected to the switch, we can use the Port Mirroring of the switch for monitoring.

Key words: Data frame;MAC address;Promiscuous mode;Port mirror

1 引言

当今网络技术的飞速发展,网络安全问题越发凸显重要。在网络中,当信息进行传播的时候,可以利用工具,将网络中正在传播的信息截获或者捕获到,从而进行攻击。目前绝大多数网络都是采用以太网结构,本文通过具体分析以太网中数据的传输与接收原理来帮大家理解网络的安全性问题。

2 监听的实施

以太网数据传输通过广播实现,在同一网段的所有网卡事实上都可以访问在共享的物理介质上传输的所有数据。但在系统正常工作时,一个合法的网络接口应只响应两种数据帧:一是帧的目标MAC地址与本地MAC地址相符;二是帧的目标地址是广播地址,除此之外数据帧将被丢弃不作处理。要监听流经网卡的不属于自己主机的数据,必须绕过系统正常工作的处理机制,直接访问网络底层。

在一个实际的系统中,数据的收发是由网卡来完成的,网卡接收到传输来的数据,网卡内的单片程序接收数据帧的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判断该不该接收,认为该接收就接收后产生中断信号通知CPU,认为不该接收就丢掉不管,所以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收数据后放入信号堆栈让操作系统处理。而对于网卡来说一般有四种接收模式:

广播模式:该模式下的网卡能够接收网络中的广播信息。

组播模式:设置在该模式下的网卡能够接收组播数据。

直接模式:在这种模式下,只有目的网卡才能接收该数据。

混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的。

当用于监听的主机连接在共享型以太网集线器上时,采用“混杂”模式可以监听到同一冲突域上传输的数据帧;但当监听的主机连接在交换机上时,由于交换机通常不会将数据帧广播到所有端口上(除非在交换机的MAC地址端口映射表内找不到相应的表项),因而不能利用以太网络的广播特性进行监听。这时,可以利用交换机的端口镜像(Port Mirroring)功能实现监听,端口镜像可以让用户将所有的流量从一个特定的端口复制到一个镜像端口。例如:当端口A和端口B之间建立镜像关系,流经端口A的所有信息流将同时通过端口B输出,从而可以在端口B监听到端口A的数据。

3 以太网数据帧格式

以太网数据帧格式如图1所示。

每一帧都从一个8B的前同步码开始,前同步码可以使得接收方的时钟与发送方的时钟很方便地同步到一起。目标地址和源地址都是6B的MAC地址,目标地址的最高位若是0,则表示普通地址;若是1,则表示组地址,但如果一帧的目标地址中包含全部的“1”,则表示是广播帧,网络上所有的站都会接受该帧。帧类型是帧中载荷的协议类型代码。作为链路层数据帧的帧头,其帧类型标识主要有:

0x0800:IP;

0x0806:ARP(地址解析协议);

0x8035:RARP(反向地址解析协议)。

IP、ARP、RARP协议数据单元的报头则存放在帧承载的数据里。由于以太网最小帧长为64B(含4B CRC校验码),最大帧长为1518B,而帧头加校验码为18B,因此帧中数据为46~1500B(含0~46B的填充字段)。

4 在Linux系统中编程实现的要点

4.1 设置套接口以捕获链路帧

在Linux系统中编写网络监听程序,比较简单的方法是在超级用户模式下,利用类型为SOCK_PACKET的套接口(socket函数)来捕获链路帧。为此,需要引用下列文件:

#include

#include

#include

#include

socket函数的语法是:

intsocket(intdomain,inttype,intprotocol);

socket函数用来建立一个新的socket,也就是向系统注册,通知系统建立一通信端口,本函数包含3个输入参数:

domain参数:表示所使用的协议簇;

type参数:表示套接口的类型;

protocol参数:表示所使用的协议簇中某个特定的协议。

如果函数调用成功,套接口的描述符(非负整数)就作为函数的返回值,假如返回值为-1,就表明有错误发生。

Linux C扩展了套接口函数,增加了SOCK_PACKET类型,使之可以用于监听链路层的数据帧,进而得以分析各层的协议数据单元。使用socket函数捕获链路层数据帧,domain参数应指定为AF_INET协议簇,表示采用internet协议簇;type参数指定为SOCK_PACKET,表示获取链路层数据,不作处理;而protocol参数采用htons(0x0003),表示使用的特定协议是截取所有类型的数据帧。这里htons函数用于短整数的字节顺序转换。计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。下面是几个字节顺序转换函数:

htonl():把32位值从主机字节序转换成网络字节序;

htons():把16位值从主机字节序转换成网络字节序;

ntohl():把32位值从网络字节序转换成主机字节序;

ntohs():把16位值从网络字节序转换成主机字节序。

监听时socket函数调用形式为:

intfd;

fd = socket(AF_INET,SOCK_PACKET,htons(0x003) );

要设置网卡工作于“混杂”模式,使用ioctl函数,原型如下:

intioctl(intd,intrequest,……)

其中d就是用户程序打开设备时使用open函数返回的文件标示符,request就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和request的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。

ioctl系统调用用于对文件进行底层控制,这里的文件包含套接口、网卡、终端、磁带机等软硬件设施,实际的操作来自各个设备自己提供的ioctl接口。设置网卡于“混杂”模式的Linux C程序段如下:

structifreqifr;

strcpy(ifr.ifr_name,dev); // (char *)dev标识设备名如”eth0”

i=ioctl(fd,SIOCGIFFLAGS,&ifr); // SIOCGIFFLAGS(0x8913)表示//要求取出工作方式

if (i

{

close(fd);

perror("can'tget flags\n");

exit(0);

}

ifr.ifr_flags|=IFF_PROMISC;//在标志中加入“混杂”方式

i=ioctl(fd,SIOCSIFFLAGS,&ifr);// SIOCSIFFLAGS(0x8914)表//示要求设定工作方式

if (i

{

perror("can'tset promiscuous\n");

exit(0);

}

4.2 从套接口读取链路帧

套接口建立以后,就可以从中循环读取链路层数据帧。因此,建立数据帧的缓冲区,并把帧头结构的指针指向这一缓冲区的首地址:

char ep[ETH_FRAME_LEN];

structethhdr*eh;

intfl;

eh=(structethhdr *)ep; //eh指向帧头

fl=read(fd, ep, sizeof(ep)); //fl为截取的数据帧帧长

这里帧头结构类型ethhdr在/usr/include/linux/if_ether.h中定义:

structethhdr

{

unsigned char h_dest[ETH_ALEN];

unsigned char h_source[ETH_ALEN];

unsigned short h_proto;// 帧中数据协议类型代码

}

基于帧头结构定义,一旦ep读入帧中数据,随即可以通过eh->h_dest、eh->h_source、eh->h_proto获取帧头信息。

4.3 捕获IP报头

一旦捕获的帧中h_proto的取值为0x800, 将类型为iphdr的结构指针指向帧头后面负载数据的起始位置,则IP信包基本报头的数据结构将一览无余。以下程序段表明这一定位过程:

char ep[ETH_FRAME_LEN];

structethhdr*eh;

structiphdr*iph;

eh=(structethhdr*)ep; //eh指向帧头

iph=(struct iphdr *)((unsigned long)ep+ETH_HLEN) // ETH_HLEN为帧头长(14)

fl=read(fd, ep, sizeof(ep)); // fl为帧长

//取出IP分组协议类型、源和目标IP地址:

iph->protocol// ==1时为ICMP包

inet_ntoa(iph->saddr);

inet_ntoa(iph->daddr);

5 附录简单的实验程序代码

实验实现的内容是:捕获通过网卡的每一数据帧,分析其帧头和IP报头结构,如果到达的是一ICMP包(IP报头协议字段的值为1),则显示其源和目标的MAC地址和IP地址。

int main()

{

int fd; //fd是套接口的描述符

int i;

struct ifreq ifr;

structiphdr*iph;

char ep[ETH_FRAME_LEN];

char *dev;

struct ethhdr *eh;

int fl;

dev = "eth0"; //eth0是网卡设备名

fd = socket(AF_INET,SOCK_PACKET,htons(0x0003)); //建立套接口

strcpy(ifr.ifr_name,dev);//(char *)dev标识设备名如“eth0”

i = ioctl(fd,SIOCGIFFLAGS,&ifr); //SIOCGIFFLAGS(0x8913)

// 表示要求取出工作方式

if(i

{

close(fd);

perror("can't get flags\n");

exit(0);

}

ifr.ifr_flags |= IFF_PROMISC; //在标志中加入“混杂”方式

i = ioctl(fd,SIOCSIFFLAGS,&ifr); //SIOCSIFFLAGS(0x8913)

//表示要求设定工作方式

if(i

{

perror("can't set promiscuous\n");

exit(0);

}

eh = (struct ethhdr *) ep;//eh指向帧头

iph=(struct iphdr *)((unsigned long)ep+ETH_HLEN) // ETH_HLEN为帧头长(14)

while(1)

{

fl = read(fd,ep,sizeof(ep)); //fl为截取的数据帧帧长

printf("\ndestination mac:");

for(i=0;i

printf("%3x ",eh->h_dest[i]); //打印目标MAC地址

printf("\nsource mac:");

for(i=0;i

printf("%3x ",eh->h_source[i]);//打印源MAC地址

printf("\nport:\t%d\n",eh->h_proto);//打印帧中数据协议类型代码

printf("\nsource ip: ");

printf("%s",inet_ntoa(iph->saddr));//打印源IP地址

printf("\ndestination ip:");

printf("%s",inet_ntoa(iph->daddr));//打印目标IP地址

}

return 0;

}

6 结束语

通过具体的事例分析,我们可以更清楚地了解数据在网络中是怎么传输和接收的,以便提高我们对网络信息安全性的认识。

参考文献:

[1] 李名世.计算机网络实验教程[M].北京:机械工业出版社,2003.

[2] Andrew S,Tanenbaum.计算机网络[M].4版.北京:清华大学出版社,2004.

[3] 鲁宾尼.Linux 设备驱动程序[M].北京:中国电力出版社,2000.

注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文

上一篇:VB环境下拼图游戏的开发 下一篇:高职高专程序设计类课程考核评价体系改革的探...