实验7 Linux下以太帧的捕获与分析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
三、以太网数据帧、IP数据报、TCP首部格式
1、以太帧数据帧格式如下图所示:
前同步码
目标地址
源地址
帧类型ቤተ መጻሕፍቲ ባይዱ
帧中数据
CRC校验和
目的地址和源地址都是6字节的MAC地址(网卡地址),帧类型是帧中载荷的协议类型代码,其帧类型标识主要有:
0x0800: IP(网际协议);
0x0806: ARP(地址解析协议);
gcc–g–o file file.c(例如gcc–g–o example example.c) //编译程序
./file (例如./example) //执行程序
二、监听的实施
以太网上进行数据传输时,在同一网段上所接的所有网卡事实上都可以收到在共享的物理介质上传输的所有数据。但在系统正常工作时,某个主机的网络接口只响应两种数据帧:一是帧的目标MAC地址与本主机网卡地址相符;二是帧的目标地址是广播地址,除此之外的数据帧都将被丢弃不作处理。要监听流经网卡的不属于自己主机的数据,必须绕过系统正常工作的处理机制,直接访问网络底层。
perror(“can’t get flags \n”);
exit(0);
}
ifr.ifr_flags |= IFF_PROMISC; //在标志位中加入“混杂”方式
i= ioctl(fd, SIOCSIFFLAGS, &ifr); // SIOCSIFFLAGS(0x8914)表示要求保存接口标志位
2. 参考本报告中提供的部分实现代码改编为一程序以获取以太帧,并对获得的以太帧加以分析;
实验内容:(1)写出程序的源代码如下,并对主要语句作注释:
(2)运行、测试该程序,记录测试结果,分析遇到的问题与解决的办法。
编程背景材料:
一、Linux下C语言编程环境介绍
1、使用gedit编辑器输入程序代码
Step1:单击“主菜单”(桌面左下方的图标)—>“附件”->“文本编辑器”,进入gedit编辑界面,输入程序。
}
4、若IP报头的协议域取值为6,那么紧跟在IP报头之后的就是TCP报头。IP报头的长度可以通过ihl域取得。这样,假如接收缓冲区ep存放监听得到的以太帧,iph是指向其中IP基本报头结构的指针,而tcph是指向TCP报头结构的指针,那么,定位TCP报头的结构信息就尽在*tcph中。
if(ip->protocol==6)
int fd; //fd是套接口的描述符
fd = socket(AF_INET, SOCK_PACKET, htons(0x0003));
要使建立的套接口能够真正监听到同一网段其他站点的数据,还必须使用ioctl函数设置网卡工作于“混杂”模式,相应的Linux C程序段如下:
char *dev =“eth0”; //(char *)dev标识设备名,eth0表示系统中的第一块以太网卡
printf("protocol:0x%04x", ntohs(eh->h_proto));
3、若捕获的以太帧中h_proto的取值为0x0800,将类型为iphdr的结构指针指向帧头后面负载数据的起始位置,则IP信包基本报头的数据结构将一览无余。以下程序段表明这一定位过程:
if(ntohs(eh->h_proto)==0x0800) //0x0800 : IP Packet
printf("src MAC:");
for(i=0; i<5; i++)
printf("%02x-", eh->h_source[i]);
printf("%02x\n ", eh->h_source[5]);
//displayprotocol: 0x0800 IP, 0x0806 ARP, 0x8035 RARP
0x8035: RARP(反向地址解析协议);
IP、ARP或RARP协议数据单元的报头则位于帧载荷数据中。由于以太网最小帧长为64字节,最大帧长为1518字节,而帧头加幀尾共18字节,故帧中数据为46~1500字节。
2、IP数据报格式及首部中的各字段
3、T C P首部的数据格式
四、Linux编程要点
1、设置套接口以捕获链路帧。在Linux下编写网络监听程序,比较简单的方法是在超级用户模式下,利用类型为SOCK_PACKET的套接口(用socket函数创建)来捕获链路帧。程序中需引用如下头文件:
#include <sys/socket.h>
#include <sys/ioctl.h> /*ioctl command*/
{
struct tcphdr* tcph;
tcph=(struct tcphdr*)(ip+ip->ihl*4);
printf("src port:%d\n", ntohs(tcph->source));
printf("dest port:%d\n", ntohs(tcph->dest));
}
{
struct iphdr *ip;
ip = (struct iphdr ) ((unsigned long)ep + ETH_HLEN);//ETH_HLEN为帧头长(14)
printf("srcip:%s\n", inet_ntoa(ip->saddr));
printf("destip:%s\n", inet_ntoa(ip->daddr));//取出源和目标IP地址
#include <netinet/udp.h>/* udphdr struct */
#include <netinet/tcp.h>/*tcphdr struct */
socket函数的语法是:int socket(int domain, int type, int protocol);其中domain参数表示所使用的协议族,type参数表示套接口的类型,protocol参数表示所使用的协议族中某个特定的协议。如果函数调用成功,套接口的描述符(非负整数)就作为函数的返回值,若为-1,就表明有错误发生。
//displaydestination MAC Adress
printf("dest MAC:");
for(i=0; i<5; i++)
printf("%02x-", eh->h_dest[i]);
printf("%02x\n ", eh->h_dest[5]);
//displaysource MAC Adress
if (i<0)
{
perror(“can’t set promiscuous \n”);
exit(0);
}
2、从套接口读取链路帧。套接口建立以后,就可以从中循环读取链路层以太帧。因此建立以太帧的缓冲区,并把帧头结构的指针指向这一缓冲区的首地址:
char ep[ETH_FRAME_LEN]; //以太帧缓冲区
Step2:保存文件的方法与Windows中类似,注意保存位置,建议将保存位置设为:/home,文件名为:file.c。
2、使用gcc编译器
在桌面上单击右键,选择“新建终端”,弹出一对话框,在命令提示符#后依次输入:
cd /home //进入程序所在目录
ls //显示该目录下的文件及文件夹(应能见file.c)
struct ethhdr *eh;
int fl;
eh = (struct ethhdr *)ep; //eh指向帧头
fl = read(fd, (etherpacket *)ep, sizeof(ep)); //fl为返回的实际捕获的以太帧的帧长
这里幀头结构类型ethhdr在/usr/include/linux/if_ether.h中定义:
#include <netinet/if_ether.h> /* ethhdr struct */
#include <net/if.h> /* ifreq struct */
#include <netinet/in.h>/* in_addr structure */
#include <netinet/ip.h>/* iphdr struct */
Linux C扩展了套接口函数,增加了SOCK_PACKET类型,使之可以用于监听链路层的数据帧,进而得以分析各层的协议数据单元。使用socket函数捕获链路层数据帧时,domain参数应指定为AF_INET协议族,表示采用internet协议族;type参数指定为SOCK_PACKET,表示获取链路层数据,不作处理;protocol参数采用htons(0x0003),表示使用的特定协议是截取所有类型的数据帧。此处需用htons函数,用于短整数的字节顺序转换。监听捕获时,socket函数调用形式为:
实验七、Linux下以太帧的捕获与分析
实验目的及要求:掌握网络监听的实现技术,使得能够实时捕获所在以太网内正在传输的数据帧,并分析帧的结构。学会在Linux下编写网络程序的基本方法;
实验步骤:1. 阅读本实验后附录的背景材料,熟悉以太网数据帧的格式,以及获取以太帧的编程原理。了解Linux环境下编程的基本方法:
struct ethhdr
{
unsigned char h_dest[ETH_ALEN]; //目标MAC地址
unsigned char h_source[ETH_ALEN]; //源MAC地址
unsigned short h_proto; //帧中数据协议类型代码
}
基于上述定义,一旦以太帧缓冲区ep中读入帧的各字节,随即可以通过eh->h_dest、eh->h_source、eh->h_proto获取幀头信息。
struct ifreq ifr;
strcpy(ifr.ifr_name, dev); //“eth0”写入ifr结构的一个字段中
i = ioctl (fd, SIOCGIFFLAGS, &ifr); // SIOCGIFFLAGS(0x8913)表示要求取出接口标志位
if (i<0)
{
close(fd);
1、以太帧数据帧格式如下图所示:
前同步码
目标地址
源地址
帧类型ቤተ መጻሕፍቲ ባይዱ
帧中数据
CRC校验和
目的地址和源地址都是6字节的MAC地址(网卡地址),帧类型是帧中载荷的协议类型代码,其帧类型标识主要有:
0x0800: IP(网际协议);
0x0806: ARP(地址解析协议);
gcc–g–o file file.c(例如gcc–g–o example example.c) //编译程序
./file (例如./example) //执行程序
二、监听的实施
以太网上进行数据传输时,在同一网段上所接的所有网卡事实上都可以收到在共享的物理介质上传输的所有数据。但在系统正常工作时,某个主机的网络接口只响应两种数据帧:一是帧的目标MAC地址与本主机网卡地址相符;二是帧的目标地址是广播地址,除此之外的数据帧都将被丢弃不作处理。要监听流经网卡的不属于自己主机的数据,必须绕过系统正常工作的处理机制,直接访问网络底层。
perror(“can’t get flags \n”);
exit(0);
}
ifr.ifr_flags |= IFF_PROMISC; //在标志位中加入“混杂”方式
i= ioctl(fd, SIOCSIFFLAGS, &ifr); // SIOCSIFFLAGS(0x8914)表示要求保存接口标志位
2. 参考本报告中提供的部分实现代码改编为一程序以获取以太帧,并对获得的以太帧加以分析;
实验内容:(1)写出程序的源代码如下,并对主要语句作注释:
(2)运行、测试该程序,记录测试结果,分析遇到的问题与解决的办法。
编程背景材料:
一、Linux下C语言编程环境介绍
1、使用gedit编辑器输入程序代码
Step1:单击“主菜单”(桌面左下方的图标)—>“附件”->“文本编辑器”,进入gedit编辑界面,输入程序。
}
4、若IP报头的协议域取值为6,那么紧跟在IP报头之后的就是TCP报头。IP报头的长度可以通过ihl域取得。这样,假如接收缓冲区ep存放监听得到的以太帧,iph是指向其中IP基本报头结构的指针,而tcph是指向TCP报头结构的指针,那么,定位TCP报头的结构信息就尽在*tcph中。
if(ip->protocol==6)
int fd; //fd是套接口的描述符
fd = socket(AF_INET, SOCK_PACKET, htons(0x0003));
要使建立的套接口能够真正监听到同一网段其他站点的数据,还必须使用ioctl函数设置网卡工作于“混杂”模式,相应的Linux C程序段如下:
char *dev =“eth0”; //(char *)dev标识设备名,eth0表示系统中的第一块以太网卡
printf("protocol:0x%04x", ntohs(eh->h_proto));
3、若捕获的以太帧中h_proto的取值为0x0800,将类型为iphdr的结构指针指向帧头后面负载数据的起始位置,则IP信包基本报头的数据结构将一览无余。以下程序段表明这一定位过程:
if(ntohs(eh->h_proto)==0x0800) //0x0800 : IP Packet
printf("src MAC:");
for(i=0; i<5; i++)
printf("%02x-", eh->h_source[i]);
printf("%02x\n ", eh->h_source[5]);
//displayprotocol: 0x0800 IP, 0x0806 ARP, 0x8035 RARP
0x8035: RARP(反向地址解析协议);
IP、ARP或RARP协议数据单元的报头则位于帧载荷数据中。由于以太网最小帧长为64字节,最大帧长为1518字节,而帧头加幀尾共18字节,故帧中数据为46~1500字节。
2、IP数据报格式及首部中的各字段
3、T C P首部的数据格式
四、Linux编程要点
1、设置套接口以捕获链路帧。在Linux下编写网络监听程序,比较简单的方法是在超级用户模式下,利用类型为SOCK_PACKET的套接口(用socket函数创建)来捕获链路帧。程序中需引用如下头文件:
#include <sys/socket.h>
#include <sys/ioctl.h> /*ioctl command*/
{
struct tcphdr* tcph;
tcph=(struct tcphdr*)(ip+ip->ihl*4);
printf("src port:%d\n", ntohs(tcph->source));
printf("dest port:%d\n", ntohs(tcph->dest));
}
{
struct iphdr *ip;
ip = (struct iphdr ) ((unsigned long)ep + ETH_HLEN);//ETH_HLEN为帧头长(14)
printf("srcip:%s\n", inet_ntoa(ip->saddr));
printf("destip:%s\n", inet_ntoa(ip->daddr));//取出源和目标IP地址
#include <netinet/udp.h>/* udphdr struct */
#include <netinet/tcp.h>/*tcphdr struct */
socket函数的语法是:int socket(int domain, int type, int protocol);其中domain参数表示所使用的协议族,type参数表示套接口的类型,protocol参数表示所使用的协议族中某个特定的协议。如果函数调用成功,套接口的描述符(非负整数)就作为函数的返回值,若为-1,就表明有错误发生。
//displaydestination MAC Adress
printf("dest MAC:");
for(i=0; i<5; i++)
printf("%02x-", eh->h_dest[i]);
printf("%02x\n ", eh->h_dest[5]);
//displaysource MAC Adress
if (i<0)
{
perror(“can’t set promiscuous \n”);
exit(0);
}
2、从套接口读取链路帧。套接口建立以后,就可以从中循环读取链路层以太帧。因此建立以太帧的缓冲区,并把帧头结构的指针指向这一缓冲区的首地址:
char ep[ETH_FRAME_LEN]; //以太帧缓冲区
Step2:保存文件的方法与Windows中类似,注意保存位置,建议将保存位置设为:/home,文件名为:file.c。
2、使用gcc编译器
在桌面上单击右键,选择“新建终端”,弹出一对话框,在命令提示符#后依次输入:
cd /home //进入程序所在目录
ls //显示该目录下的文件及文件夹(应能见file.c)
struct ethhdr *eh;
int fl;
eh = (struct ethhdr *)ep; //eh指向帧头
fl = read(fd, (etherpacket *)ep, sizeof(ep)); //fl为返回的实际捕获的以太帧的帧长
这里幀头结构类型ethhdr在/usr/include/linux/if_ether.h中定义:
#include <netinet/if_ether.h> /* ethhdr struct */
#include <net/if.h> /* ifreq struct */
#include <netinet/in.h>/* in_addr structure */
#include <netinet/ip.h>/* iphdr struct */
Linux C扩展了套接口函数,增加了SOCK_PACKET类型,使之可以用于监听链路层的数据帧,进而得以分析各层的协议数据单元。使用socket函数捕获链路层数据帧时,domain参数应指定为AF_INET协议族,表示采用internet协议族;type参数指定为SOCK_PACKET,表示获取链路层数据,不作处理;protocol参数采用htons(0x0003),表示使用的特定协议是截取所有类型的数据帧。此处需用htons函数,用于短整数的字节顺序转换。监听捕获时,socket函数调用形式为:
实验七、Linux下以太帧的捕获与分析
实验目的及要求:掌握网络监听的实现技术,使得能够实时捕获所在以太网内正在传输的数据帧,并分析帧的结构。学会在Linux下编写网络程序的基本方法;
实验步骤:1. 阅读本实验后附录的背景材料,熟悉以太网数据帧的格式,以及获取以太帧的编程原理。了解Linux环境下编程的基本方法:
struct ethhdr
{
unsigned char h_dest[ETH_ALEN]; //目标MAC地址
unsigned char h_source[ETH_ALEN]; //源MAC地址
unsigned short h_proto; //帧中数据协议类型代码
}
基于上述定义,一旦以太帧缓冲区ep中读入帧的各字节,随即可以通过eh->h_dest、eh->h_source、eh->h_proto获取幀头信息。
struct ifreq ifr;
strcpy(ifr.ifr_name, dev); //“eth0”写入ifr结构的一个字段中
i = ioctl (fd, SIOCGIFFLAGS, &ifr); // SIOCGIFFLAGS(0x8913)表示要求取出接口标志位
if (i<0)
{
close(fd);