入侵检测linux下课程设计指导书3Word格式.docx

上传人:b****5 文档编号:16468495 上传时间:2022-11-23 格式:DOCX 页数:34 大小:40.09KB
下载 相关 举报
入侵检测linux下课程设计指导书3Word格式.docx_第1页
第1页 / 共34页
入侵检测linux下课程设计指导书3Word格式.docx_第2页
第2页 / 共34页
入侵检测linux下课程设计指导书3Word格式.docx_第3页
第3页 / 共34页
入侵检测linux下课程设计指导书3Word格式.docx_第4页
第4页 / 共34页
入侵检测linux下课程设计指导书3Word格式.docx_第5页
第5页 / 共34页
点击查看更多>>
下载资源
资源描述

入侵检测linux下课程设计指导书3Word格式.docx

《入侵检测linux下课程设计指导书3Word格式.docx》由会员分享,可在线阅读,更多相关《入侵检测linux下课程设计指导书3Word格式.docx(34页珍藏版)》请在冰豆网上搜索。

入侵检测linux下课程设计指导书3Word格式.docx

数据包常规的传输路径依次为网卡、设备驱动层、数据链路层、IP层、传输层、最后到达应用程序。

而包捕获机制是在数据链路层增加一个旁路处理,对发送和接收到的数据包做过滤/缓冲等相关处理,最后直接传递到应用程序。

值得注意的是,包捕获机制并不影响操作系统对数据包的网络栈处理。

对用户程序而言,包捕获机制提供了一个统一的接口,使用户程序只需要简单的调用若干函数就能获得所期望的数据包。

这样一来,针对特定操作系统的捕获机制对用户透明,使用户程序有比较好的可移植性。

包过滤机制是对所捕获到的数据包根据用户的要求进行筛选,最终只把满足过滤条件的数据包传递给用户程序。

libpcap应用程序框架

libpcap提供了系统独立的用户级别网络数据包捕获接口,并充分考虑到应用程序的可移植性。

libpcap可以在绝大多数类unix平台下工作,参考资料A中是对基于libpcap的网络应用程序的一个详细列表。

在windows平台下,一个与libpcap很类似的函数包winpcap提供捕获功能,其官方网站是http:

//winpcap.polito.it/。

libpcap软件包可从http:

//www.tcpdump.org/下载,然后依此执行下列三条命令即可安装,但如果希望libpcap能在Linux上正常工作,则必须使内核支持"

packet"

协议,也即在编译内核时打开配置选项CONFIG_PACKET(选项缺省为打开)。

./configure

./make

./makeinstall

libpcap源代码由20多个C文件构成,但在Linux系统下并不是所有文件都用到。

可以通过查看命令make的输出了解实际所用的文件。

本文所针对的libpcap版本号为0.8.3,网络类型为常规以太网。

libpcap应用程序从形式上看很简单,下面是一个简单的程序框架:

char*device;

/*用来捕获数据包的网络接口的名称*/

pcap_t*p;

/*捕获数据包句柄,最重要的数据结构*/

structbpf_programfcode;

/*BPF过滤代码结构*/

/*第一步:

查找可以捕获数据包的设备*/

device=pcap_lookupdev(errbuf);

/*第二步:

创建捕获句柄,准备进行捕获*/

p=pcap_open_live(device,8000,1,500,errbuf);

/*第三步:

如果用户设置了过滤条件,则编译和安装过滤代码*/

pcap_compile(p,&

fcode,filter_string,0,netmask);

pcap_setfilter(p,&

fcode);

/*第四步:

进入(死)循环,反复捕获数据包*/

for(;

;

{

while((ptr=(char*)(pcap_next(p,&

hdr)))==NULL);

/*第五步:

对捕获的数据进行类型转换,转化成以太数据包类型*/

eth=(structlibnet_ethernet_hdr*)ptr;

/*第六步:

对以太头部进行分析,判断所包含的数据包类型,做进一步的处理*/

if(eth->

ether_type==ntohs(ETHERTYPE_IP))

…………

ether_type==ntohs(ETHERTYPE_ARP))

}

/*最后一步:

关闭捕获句柄,一个简单技巧是在程序初始化时增加信号处理函数,

以便在程序退出前执行本条代码*/

pcap_close(p);

检查网络设备

libpcap程序的第一步通常是在系统中找到合适的网络接口设备。

网络接口在Linux网络体系中是一个很重要的概念,它是对具体网络硬件设备的一个抽象,在它的下面是具体的网卡驱动程序,而其上则是网络协议层。

Linux中最常见的接口设备名eth0和lo。

Lo称为回路设备,是一种逻辑意义上的设备,其主要目的是为了调试网络程序之间的通讯功能。

eth0对应了实际的物理网卡,在真实网络环境下,数据包的发送和接收都要通过eht0。

如果计算机有多个网卡,则还可以有更多的网络接口,如eth1,eth2等等。

调用命令ifconfig可以列出当前所有活跃的接口及相关信息,注意对eth0的描述中既有物理网卡的MAC地址,也有网络协议的IP地址。

查看文件/proc/net/dev也可获得接口信息。

libpcap调用pcap_lookupdev()函数获得可用网络接口的设备名。

首先利用函数getifaddrs()获得所有网络接口的地址,以及对应的网络掩码、广播地址、目标地址等相关信息,再利用add_addr_to_iflist()、add_or_find_if()、get_instance()把网络接口的信息增加到结构链表pcap_if中,最后从链表中提取第一个接口作为捕获设备。

其中get_instanced()的功能是从设备名开始,找第一个是数字的字符,做为接口的实例号。

网络接口的设备号越小,则排在链表的越前面,因此,通常函数最后返回的设备名为eth0。

虽然libpcap可以工作在回路接口上,但显然libpcap开发者认为捕获本机进程之间的数据包没有多大意义。

在检查网络设备操作中,主要用到的数据结构和代码如下:

/*libpcap自定义的接口信息链表[pcap.h]*/

structpcap_if

structpcap_if*next;

char*name;

/*接口设备名*/

char*description;

/*接口描述*/

/*接口的IP地址,地址掩码,广播地址,目的地址*/

structpcap_addraddresses;

bpf_u_int32flags;

/*接口的参数*/

};

char*pcap_lookupdev(registerchar*errbuf)

pcap_if_t*alldevs;

……

pcap_findalldevs(&

alldevs,errbuf);

strlcpy(device,alldevs->

name,sizeof(device));

打开网络设备

当设备找到后,下一步工作就是打开设备以准备捕获数据包。

libpcap的包捕获是建立在具体的操作系统所提供的捕获机制上,而Linux系统随着版本的不同,所支持的捕获机制也有所不同。

2.0及以前的内核版本使用一个特殊的socket类型SOCK_PACKET,调用形式是socket(PF_INET,SOCK_PACKET,intprotocol),但Linux内核开发者明确指出这种方式已过时。

Linux在2.2及以后的版本中提供了一种新的协议簇PF_PACKET来实现捕获机制。

PF_PACKET的调用形式为socket(PF_PACKET,intsocket_type,intprotocol),其中socket类型可以是SOCK_RAW和SOCK_DGRAM。

SOCK_RAW类型使得数据包从数据链路层取得后,不做任何修改直接传递给用户程序,而SOCK_DRRAM则要对数据包进行加工(cooked),把数据包的数据链路层头部去掉,而使用一个通用结构sockaddr_ll来保存链路信息。

使用2.0版本内核捕获数据包存在多个问题:

首先,SOCK_PACKET方式使用结构sockaddr_pkt来保存数据链路层信息,但该结构缺乏包类型信息;

其次,如果参数MSG_TRUNC传递给读包函数recvmsg()、recv()、recvfrom()等,则函数返回的数据包长度是实际读到的包数据长度,而不是数据包真正的长度。

libpcap的开发者在源代码中明确建议不使用2.0版本进行捕获。

相对2.0版本SOCK_PACKET方式,2.2版本的PF_PACKET方式则不存在上述两个问题。

在实际应用中,用户程序显然希望直接得到"

原始"

的数据包,因此使用SOCK_RAW类型最好。

但在下面两种情况下,libpcap不得不使用SOCK_DGRAM类型,从而也必须为数据包合成一个"

伪"

链路层头部(sockaddr_ll)。

某些类型的设备数据链路层头部不可用:

例如Linux内核的PPP协议实现代码对PPP数据包头部的支持不可靠。

在捕获设备为"

any"

时:

所有设备意味着libpcap对所有接口进行捕获,为了使包过滤机制能在所有类型的数据包上正常工作,要求所有的数据包有相同的数据链路头部。

打开网络设备的主函数是pcap_open_live()[pcap-Linux.c],其任务就是通过给定的接口设备名,获得一个捕获句柄:

结构pcap_t。

pcap_t是大多数libpcap函数都要用到的参数,其中最重要的属性则是上面讨论到的三种socket方式中的某一种。

首先我们看看pcap_t的具体构成。

structpcap[pcap-int.h]

{

intfd;

/*文件描述字,实际就是socket*/

/*在socket上,可以使用select()和poll()等I/O复用类型函数*/

intselectable_fd;

intsnapshot;

/*用户期望的捕获数据包最大长度*/

intlinktype;

/*设备类型*/

inttzoff;

/*时区位置,实际上没有被使用*/

intoffset;

/*边界对齐偏移量*/

intbreak_loop;

/*强制从读数据包循环中跳出的标志*/

structpcap_sfsf;

/*数据包保存到文件的相关配置数据结构*/

structpcap_mdmd;

/*具体描述如下*/

intbufsize;

/*读缓冲区的长度*/

u_charbuffer;

/*读缓冲区指针*/

u_char*bp;

intcc;

u_char*pkt;

/*相关抽象操作的函数指针,最终指向特定操作系统的处理函数*/

int(*read_op)(pcap_t*,intcnt,pcap_handler,u_char*);

int(*setfilter_op)(pcap_t*,structbpf_program*);

int(*set_datalink_op)(pcap_t*,int);

int(*getnonblock_op)(pcap_t*,char*);

int(*setnonblock_op)(pcap_t*,int,char*);

int(*stats_op)(pcap_t*,structpcap_stat*);

void(*close_op)(pcap_t*);

/*如果BPF过滤代码不能在内核中执行,则将其保存并在用户空间执行*/

structbpf_programfcode;

/*函数调用出错信息缓冲区*/

charerrbuf[PCAP_ERRBUF_SIZE+1];

/*当前设备支持的、可更改的数据链路类型的个数*/

intdlt_count;

/*可更改的数据链路类型号链表,在Linux下没有使用*/

int*dlt_list;

/*数据包自定义头部,对数据包捕获时间、捕获长度、真实长度进行描述[pcap.h]*/

structpcap_pkthdrpcap_header;

/*包含了捕获句柄的接口、状态、过滤信息[pcap-int.h]*/

structpcap_md{

/*捕获状态结构[pcap.h]*/

structpcap_statstat;

intuse_bpf;

/*如果为1,则代表使用内核过滤*/

u_longTotPkts;

u_longTotAccepted;

/*被接收数据包数目*/

u_longTotDrops;

/*被丢弃数据包数目*/

longTotMissed;

/*在过滤进行时被接口丢弃的数据包数目*/

longOrigMissed;

/*在过滤进行前被接口丢弃的数据包数目*/

#ifdefLinux

intsock_packet;

/*如果为1,则代表使用2.0内核的SOCK_PACKET模式*/

inttimeout;

/*pcap_open_live()函数超时返回时间*/

intclear_promisc;

/*关闭时设置接口为非混杂模式*/

intcooked;

/*使用SOCK_DGRAM类型*/

intlo_ifindex;

/*回路设备索引号*/

char*device;

/*接口设备名称*/

/*以混杂模式打开SOCK_PACKET类型socket的pcap_t链表*/

structpcap*next;

#endif

函数pcap_open_live()的调用形式是pcap_t*pcap_open_live(constchar*device,intsnaplen,intpromisc,intto_ms,char*ebuf),其中如果device为NULL或"

,则对所有接口捕获,snaplen代表用户期望的捕获数据包最大长度,promisc代表设置接口为混杂模式(捕获所有到达接口的数据包,但只有在设备给定的情况下有意义),to_ms代表函数超时返回的时间。

本函数的代码比较简单,其执行步骤如下:

*为结构pcap_t分配空间并根据函数入参对其部分属性进行初试化。

*分别利用函数live_open_new()或live_open_old()尝试创建PF_PACKET方式或SOCK_PACKET方式的socket,注意函数名中一个为"

new"

,另一个为"

old"

*根据socket的方式,设置捕获句柄的读缓冲区长度,并分配空间。

*为捕获句柄pcap_t设置Linux系统下的特定函数,其中最重要的是读数据包函数和设置过滤器函数。

(注意到这种从抽象模式到具体模式的设计思想在Linux源代码中也多次出现,如VFS文件系统)handle->

read_op=pcap_read_Linux;

handle->

setfilter_op=pcap_setfilter_Linux;

下面我们依次分析2.2和2.0内核版本下的socket创建函数。

staticint

live_open_new(pcap_t*handle,constchar*device,intpromisc,

intto_ms,char*ebuf)

/*如果设备给定,则打开一个RAW类型的套接字,否则,打开DGRAM类型的套接字*/

sock_fd=device?

socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))

:

socket(PF_PACKET,SOCK_DGRAM,htons(ETH_P_ALL));

/*取得回路设备接口的索引*/

handle->

md.lo_ifindex=iface_get_id(sock_fd,"

lo"

ebuf);

/*如果设备给定,但接口类型未知或是某些必须工作在加工模式下的特定类型,则使用加工模式*/

if(device){

/*取得接口的硬件类型*/

arptype=iface_get_arptype(sock_fd,device,ebuf);

/*Linux使用ARPHRD_xxx标识接口的硬件类型,而libpcap使用DLT_xxx

来标识。

本函数是对上述二者的做映射变换,设置句柄的链路层类型为

DLT_xxx,并设置句柄的偏移量为合适的值,使其与链路层头部之和为4的倍数,目的是边界对齐*/

map_arphrd_to_dlt(handle,arptype,1);

/*如果接口是前面谈到的不支持链路层头部的类型,则退而求其次,使用SOCK_DGRAM模式*/

if(handle->

linktype==xxx)

close(sock_fd);

sock_fd=socket(PF_PACKET,SOCK_DGRAM,htons(ETH_P_ALL));

/*获得给定的设备名的索引*/

device_id=iface_get_id(sock_fd,device,ebuf);

/*把套接字和给定的设备绑定,意味着只从给定的设备上捕获数据包*/

iface_bind(sock_fd,device_id,ebuf);

}else{/*现在是加工模式*/

md.cooked=1;

/*数据包链路层头部为结构sockaddr_ll,SLL大概是结构名称的简写形式*/

linktype=DLT_Linux_SLL;

device_id=-1;

}

/*设置给定设备为混杂模式*/

if(device&

&

promisc)

memset(&

mr,0,sizeof(mr));

mr.mr_ifindex=device_id;

mr.mr_type=PACKET_MR_PROMISC;

setsockopt(sock_fd,SOL_PACKET,PACKET_ADD_MEMBERSHIP,

mr,sizeof(mr));

/*最后把创建的socket保存在句柄pcap_t中*/

fd=sock_fd;

}

/*2.0内核下函数要简单的多,因为只有唯一的一种socket方式*/

live_open_old(pcap_t*handle,constchar*device,intpromisc,

/*首先创建一个SOCK_PACKET类型的socket*/

fd=socket(PF_INET,SOCK_PACKET,htons(ETH_P_ALL));

/*2.0内核下,不支持捕获所有接口,设备必须给定*/

if(!

device){

strncpy(ebuf,

"

pcap_open_live:

The"

deviceisn'

t

supportedon2.0[.x]-kernelsystems"

PCAP_ERRBUF_SIZE);

break;

/*把socket和给定的设备绑定*/

iface_bind_old(handle->

fd,device,ebuf);

/*以下的处理和2.2版本下的相似,有所区别的是如果接口链路层类型未知,则libpcap直接退出*/

arptype=iface_get_arptype(handle->

fd,device,ebuf);

map_arphrd_to_dlt(handle,arptype,0);

linktype==-1){

snprintf(ebuf,PCAP_ERRBUF_SIZE,"

unknownarptype%d"

arptype);

if(promisc){

ifr,0,sizeof(ifr));

strncpy(ifr.ifr_name,device,sizeof(ifr.ifr_name));

ioctl(handle->

fd,SIOCGIFFLAGS,&

ifr);

ifr.ifr_flags|=IFF_PROMISC;

fd,SIOCSIFFLAGS,&

比较上面两个函数的代码,还有两个细节上的区别。

首先是socket与接口绑定所使用的结构:

老式的绑定使用了结构sockaddr,而新式的则使用了2.2内核中定义的通用链路头部层结构sockaddr_ll。

iface_bind_old(intfd,constchar*device,char*ebuf)

structsockaddrsaddr;

saddr,0,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 数学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1