当前位置:文档之家› 使用DLPI来编写协议分析工具

使用DLPI来编写协议分析工具

[保留] 使用DLPI来编写协议分析工具

作者:stevens_wu发表于:2007-12-13 17:10:35 【发表评论】【查看原文】【C/C++讨论区】【关闭】

系统环境:solaris 10 for x86,gcc 3.4.3,100M快速以太网

(偶是个初学者,本文难免存在错误,希望大家多多指教)

前一阵子要写一个简单的arp协议的分析程序,在翻阅了一些资料以后,决定使用libpcap库来实现,但是后来涉及到写链路层数据的缘故(另外一个程序,这个程序就是发送一个假冒的arp request,在本文没有实现,今后有空再整理吧),所以放弃了libpcap。由于本人使用的是solaris环境,所以无法使用bpf,但是sun

公司仍然为开发者提供了一个与设备底层无关的接口DLPI,DLPI的全称是

Data Link Provider Interface,通过DLPI开发者可以访问数据链路层的数据包,在早期的sunos系统中基本上采用的是NIT设备,但是现在solaris系统都使用了DLPI.关于DLPI的具体介绍大家可以访问网站

/pubs/catalog/c811.htm,我这里就不多说了。

在搜索了许多资料之后发现目前关于DLPI的编程资料不多,没有具体的过程,后来翻阅了Neal Nuckolls写的一篇文章

How to Use the STREAMS Data Link Provider Interface (DLPI),根据例子做了修改(主要是提供了协议分析的部分),现在把编写一个DLPI过程共享一下,希望能对大家有所帮助。建议大家可以先看看Neal Nuckolls的文章,其中有部分涉及到流编程的,可以参考/app/docs/doc/816-4855的

streams programming guide(不过这不是必须的)。

使用DLPI来访问数据链路层有几个步骤:

1、打开网络设备

2、将一个流 attach到一个特定的设备上,这里就是我们刚才打开的设备

3、将设备设置为混杂模式(可选)

4、把数据链路层sap绑定到流

5、调用ioctl,设置raw模式

6、配置其他模块(可选)

7、刷新缓存

8、接收数据进入分析阶段

第一步,我们首先打开一个网络设备,在本例中我们打开的是/dev/bge设备,这是本机的网络接口,注意不是/dev/bge0,通过open调用打开,并且返回一个描述符fd=open(device, 2)

第二步,attach一个流到设备上,这是通过发送DL_ATTACH_REQ原语来完成的dlattachreq(fd, ppa)

int fd;

u_long ppa;

{

dl_attach_req_t attach_req;

struct strbuf ctl;

int flags;

attach_req.dl_primitive = DL_ATTACH_REQ;

attach_req.dl_ppa = ppa;

ctl.maxlen = 0;

ctl.len = sizeof (attach_req);

ctl.buf = (char *) &attach_req;

flags = 0;

if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)

syserr("dlattachreq: putmsg");

}

dl_attach_req_t是一个定义在dlpi.h中的结构体,我们通过填写结构体来发布原语,putmsg将消息发送到一个流,以上这个函数是DLPI中发布原语的主要格式

发布了DL_ATTACH_REQ原语之后,还要确认是否成功,

dlokack(fd, bufp)

int fd;

char *bufp;

{

union DL_primitives *dlp;

struct strbuf ctl;

int flags;

ctl.maxlen = MAXDLBUF;

ctl.len = 0;

ctl.buf = bufp;

strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack");

dlp = (union DL_primitives *) ctl.buf;

expecting(DL_OK_ACK, dlp);

if (ctl.len < sizeof (dl_ok_ack_t))

err("dlokack: response ctl.len too short: %d", ctl.len);

if (flags != RS_HIPRI)

err("dlokack: DL_OK_ACK was not M_PCPROTO");

if (ctl.len < sizeof (dl_ok_ack_t))

err("dlokack: short response ctl.len: %d", ctl.len);

}

第三步,将设备设置为混杂模式下工作(可选)

dlpromisconreq(fd, DL_PROMISC_PHYS);

这一个步骤也是通过发布DLPI原语来实现的,具体代码后面给出

第四步,绑定流

dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);

dlbindack(fd, buf);

第五步,设置raw模式

strioctl(fd, DLIOCRAW, -1, 0, NULL)

第六步,配置其他模块(在详细代码中给出)

第七步,刷新数据,这是通过ioctl调用实现的

ioctl(fd, I_FLUSH, FLUSHR)

第八步,这是我们最关心的步骤,实际上,前面的这些步骤我们都可以忽略,大致明白有这么个过程就可以了,到时候写代码的时候照搬这个框架就可以。使用DLPI 编程并不难,关键在于大家要了解它的框架,没必要非得自己去写一个框架来,本文就是利用了Michael R. Widner的代码,今后如果要增加功能只需要往这个框架里填就可以了。

协议分析的过程是在函数filter完成的,函数申明如下

void filter(register char *cp,register u_int pktlen);

该函数接收两个参数,cp是直接从设备缓存里拷贝过来的待分析数据,是链路层的封装数据,pktlen是数据的长度。在本文中由于操作环境是以太网,因此接收的数据链路层数据是以太网封装格式,如不清楚以太网封装的可以参考《TCP/IP详解卷一:协议》,以太网封装三种标准的协议类型:IP协议、ARP协议和RARP协议。14字节的以太网首部包括了6字节的目的地址,6字节的源地址和2字节的类型字段,IP的类型值为0x0800,ARP的类型值为0x0806,RARP的类型值为0x8035。通过检查类型字段来区别接收到的数据是属于哪一种协议,函数实现代码如下

void filter(cp, pktlen)

register char *cp;

register u_int pktlen;

{

register struct ip *ip;

register struct tcphdr *tcph;

register struct ether_header *eth;

char *head=cp;

static long line_count=0;//计数器,用来记录接收的数据次数

u_short EtherType=ntohs(((struct ether_header *)cp)->;ether_type);

//如果EtherType小于0x600说明这是一个符合802.3标准的数据格式,应当对数据作出调整

if(EtherType < 0x600) {

EtherType = *(u_short *)(cp + SZETH + 6);

cp+=8; pktlen-=8;

}

eth=(struct ether_header*)cp;

fprintf(LOG,"%-5d",++line_count);

if(EtherType == ETHERTYPE_IP) //检查协议类型是否IP协议

相关主题