capture datapacket details descript

本文介绍了在Linux下如何使用SOCK_PACKET类型的套接字捕获和处理数据链路层的数据帧,包括设置套接口、读取链路帧、定位IP、TCP、UDP报头及应用层数据的方法。通过示例代码展示了如何编写ARP请求程序,涉及网络接口设置、以太网帧结构、IP和TCP/UDP头部解析等知识点。
摘要由CSDN通过智能技术生成

http://book.csdn.net/bookfiles/1213/index.html  转载地址

11.4  数据链路层访问

在Linux下数据链路层的访问通常是通过编写内核驱动程序来实现的,在应用层使用SOCK_PACKET类型的协议族可以实现部分功能。

11.4.1  SOCK_PACKET类型

建立套接字的时候选择SOCK_PACKET类型,内核将不对网络数据进行处理而直接交给用户,数据直接从网卡的协议栈交给用户。建立一个SOCK_PACKET类型的套接字使用如下方式:

socket (AF_INET, SOCK_PACKET,htons(0x0003));

其中AF_INET=2表示因特网协议族,SOCK_PACKET=10表示截取数据帧的层次在物理层,网络协议栈对数据不做处理。值0x0003表示截取的数据帧的类型为不确定,处理所有的包。

使用SOCK_PACKET进行程序设计的时候,需要注意的主要方面包括协议族选择、获取原始包、定位IP包、定位TCP包、定位UDP包、定位应用层数据几个部分,下面几节中将进行详细的介绍。

11.4.2  设置套接口以捕获链路帧的编程方法

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

#include <sys/socket.h>

#include <sys/ioctl.h>              /*ioctl 命令*/

#include <Linux/if_ether.h>         /*ethhdr 结构*/

#include <net/if.h>                 /*ifreq 结构*/

#include <netinet/in.h>             /*in_addr结构*/

#include <Linux/ip.h>               /*iphdr 结构*/

#include <Linux/udp.h>              /*udphdr 结构*/

#include <Linux/tcp.h>              /*tcphdr 结构*/

建立SOCK_PACKET类型套接字的方法在11.4.1中已经进行了介绍。如果要监视所有类型的包,则需要采用如下代码:

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

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

侦听其他主机网络的数据在局域网诊断中经常使用。如果要监听其他网卡的数据,需要将本地的网卡设置为“混杂”模式;当然还需要一个都连接于同一HUB的局域网或者具有“镜像”功能的交换机才可以,否则,只能接收到其他主机的广播包。

char*ethname = "eth0";                   /*对网卡eth0进行混杂设置*/

struct ifreq  ifr;                      /*网络接口结构*/

strcpy(ifr.ifr_name, ethname);         /*“eth0”写入ifr结构的一个字段中*/

i = ioctl (fd, SIOCGIFFLAGS, &ifr);     /*获得eth0的标志位值*/

if (i<0)                                /*判断是否取出出错*/

{

    close(fd);

    perror("can’t get flags /n");

    return -1;

}

ifr.ifr_flags|=IFF_PROMISC; /*保留原来设置的情况下,在标志位中加入“混杂”方式*/

i = ioctl(fd, SIOCSIFFLAGS, &ifr);      /*将标志位设置写入*/

if (i<0)                                /*判断是否写入出错*/

{

    perror("promiscuous set error/n");

    return -2;

}

上面的代码使用了ioctl()的SIOCGIFFLAGS和SIOCSIFFLAGS命令,用来取出和写入网络接口的标志设置。注意,在修改网络接口标志的时候,务必要先将之前的标志取出,与想设置的位进行“位或”计算后再写入;不要直接将设置的位值写入,因为直接写入会覆盖之前的设置,造成网络接口混乱。遵循如下的步骤:

(1)取出标志位。

(2)目标标志位=取出的标志位|设置的标志位。

(3)写入目标标志位。

11.4.3  从套接口读取链路帧的编程方法

以太网的数据结构如图11.10所示,总长度最大为1518字节,最小为64字节,其中目标地址的MAC为6字节,源地址MAC为6字节,协议类型为2字节,含有46~1500字节的数据,尾部为4个字节的CRC校验和。以太网的CRC校验和一般由硬件自动设置或者剥离,应用层不用考虑。

图11-10  以太网帧示意图

在头文件<Linux/if_ether.h>中定义了如下的常量:

#define ETH_ALEN    6               /*以太网地址,即MAC地址,6字节*/

#define ETH_HLEN    14              /*以太网头部的总长度*/

#define ETH_ZLEN    60              /*不含CRC校验的数据最小长度*/

#define ETH_DATA_LEN    1500        /*帧内数据的最大长度*/

#define ETH_FRAME_LEN   1514        /*不含CRC校验和的最大以太网数据长度*/

以太网头部结构定义为如下的形式:

struct ethhdr {

    unsigned char   h_dest[ETH_ALEN];       /*目的以太网地址*/

    unsigned char   h_source[ETH_ALEN];     /*源以太网地址*/

    __be16      h_proto;                    /*包类型*/

};

套接字文件描述符建立后,就可以从此描述符中读取数据,数据的格式为上述的以太网数据,即以太网帧。套接口建立以后,就可以从中循环读取捕获的链路层以太帧。要建立一个以太网缓冲区可以建立一个大小为ETH_FRAME_LEN的缓冲区,并将以太网的头部指向此缓冲区,例如:

char ef[ETH_FRAME_LEN];             /*以太帧缓冲区*/

struct ethhdr*p_ethhdr;         /*以太网头部指针*/

int n;         

p_ethhdr = (struct ethhdr*)ef; /*使p_ethhdr指向以太网帧的帧头*/

                            /*读取以太网数据,n为返回的实际捕获的以太帧的帧长*/

n = read(fd, ef, ETH_FRAME_LEN);  

接收数据以后,缓冲区ef与以太网头部的对应关系如图11.11所示。

图11.11  以太网帧缓冲区与以太网头部结构ethhdr的映射关系

因此要获得以太网帧的目的MAC地址、源MAC地址和协议的类型,可以通过p_ethhdr->h_dest、p_ethhdr->h_source和p_ethhdr->h_proto获得。下面的代码将以太网的信息打印出来:

/*打印以太网帧中的MAC地址和协议类型*/

/*目的MAC地址*/

printf("dest MAC: ");

for(i=0; i< ETH_ALEN-1; i++){

    printf("%02x-", p_ethhdr->h_dest[i]);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值