网络协议分析软件编写
前一阵子要写一个简单的arp协议的分析程序,在翻阅了一些资料以后,决定使用libpcap库来实现,但是后来涉及到写链路层数据的缘故(另外一个程序,这个程序就是发送一个假冒的arp request,在本文没有实现,今后有空再吧),所以放弃了libpcap。
由于本人使用的是solaris环境,所以无法使用bpf,但是sun
公司仍然为开发者提供了一个与设备底层无关的接口DLPI,DLPI的全称是Data Link Provider Interface,通过DLPI开发者可以访问数据链路层的数据包,在早期的sunos系统中基本上采用的是NIT设备,但是现在solaris系统都使用了DLPI.关于DLPI的具体介绍大家可以访问网站.opengroup/pubs/catalog/c811.htm,我这里就不多说了。
在搜索了许多资料之后发现目前关于DLPI的编程资料不多,没有具体的过程,后来翻阅了Neal Nuckolls写的一篇文章How toUse theSTREAMS DataLinkProviderInterface(DLPI),根据例子做了修改(主要是提供了协议分析的部分),现在把编写一个DLPI过程共享一下,希望能对大家有所帮助。
建议大家可以先看看Neal Nuckolls的文章,其中有部分涉及到流编程的,可以参考docs.sun./app/docs/doc/816-4855的streams programmingguide(不过这不是必须的)。
使用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 strbufctl;int
flags;attach_req.dl_primitive=DL_ATTACH_REQ;attach_req.dl_p pa=ppa;ctl.maxlen=0;ctl.len=sizeof(attach_req);ctl.buf=(cha r*)&attach_req;flags=0;if(putmsg(fd,&ctl,(struct
strbuf*)NULL,flags)
使用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 structtcphdr*tcph;register struct
ether_header*eth;char*head=cp;static longline_count=0;//计
数器,用来记录接收的数据次数u_short EtherType=ntohs(((struct ether_header*)cp)->ether_type);//如果EtherType小于0x600说明这是一个符合802.3标准的数据格式,应当对数据作出调整
if(EtherTypeether_shost);//Mac_info函数打印出物理地址
fprintf(LOG,"(");Ip_info(&ip->ip_src);//Ip_info函数打印出
IP地址
fprintf(LOG,")");fprintf(LOG,"--->");Mac_info(e->ether_dhos t);fprintf(LOG,"(");Ip_info(&ip->ip_dst);fprintf(LOG,")");f
printf(LOG,"\n");}else if(EtherType==ARP_PROTO)//如果协议类型是ARP{cp+=SZETH;struct ether_arp*arp=(struct ether_arp*)cp;switch(ntohs(a rp->ea_hdr.ar_op))//检查arp的操作,case ARPOP_REQUEST://如果是arp请求fprintf(LOG,"arp request:who has");
arp_ip_info(arp->arp_tpa);//打印arp报文信息中的地址fprintf(LOG,"tells");
arp_ip_info(arp->arp_spa);fprintf(LOG,"\n");
break;case ARPOP_REPLY://arp应答
fprintf(LOG,"arp reply:");
arp_ip_info(arp->arp_spa);fprintf(LOG,"is at");
Mac_info((struct ether_addr*)&arp->arp_sha);
fprintf(LOG,"\n");break;-
//可以在这里添加代码打印出arp数据报的具体内容-}程序的具体实现代码如下/*程序sniffer.c的代码清单
*/#include#include#include#include#include#include#include# include#include#include#include#include#include#include#inc lude#include#include#include#include#include#include#includ e#include#include#include#include#include#define MAXDLBUF32768#define MAXWAIT15#define MAXDLADDR1024#define BITSPERBYTE8#define
bcopy(s1,s2,len)memcpy(s2,s1,len)#define