当前位置:文档之家› 网络协议分析软件编写

网络协议分析软件编写

前一阵子要写一个简单的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协议{ip=(struct ip *)(cp+SZETH);//调整指针的位置,SZETH是以太网首部长度Mac_info(e->ether_shost);//Mac_info函数打印出物理地址fprintf(LOG,"(");Ip_info(&ip->ip_src);//Ip_info函数打印出IP地址fprintf(LOG,")");fprintf(LOG,"--->");Mac_info(e->ether_dhost);fprintf(LOG,"(");Ip_info(&ip->ip_dst);fprintf(LOG,")");fprintf(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_tpa); //打印arp报文信息中的地址arp_ip_info(arpfprintf(LOG," tells ");->arp_spa);arp_ip_info(arp\n");fprintf(LOG,"break;应答case ARPOP_REPLY: //arpfprintf(LOG,"arp reply: ");->arp_spa);arp_ip_info(arpfprintf(LOG," is at ");->arp_sha);Mac_info((struct ether_addr*)&arp\n");fprintf(LOG,"break;-//可以在这里添加代码打印出arp数据报的具体内容 -}程序的具体实现代码如下:/* 程序sniffer.c的代码清单*/#include <sys/stream.h>#include <sys/dlpi.h>#include <sys/bufmod.h>#include <stdio.h>#include <ctype.h>#include <string.h>#include <sys/time.h>#include <sys/file.h>#include <sys/stropts.h>#include <sys/signal.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <net/if.h>#include <net/if_arp.h>#include <netinet/in.h>#include <netinet/if_ether.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/udp.h>#include <netinet/ip_var.h>#include <netinet/udp_var.h>#include <netinet/in_systm.h>#include <netinet/tcp.h>#include <netinet/ip_icmp.h>#include <netdb.h>#include <arpa/inet.h>#define MAXDLBUF 32768#define MAXWAIT 15#define MAXDLADDR 1024#define BITSPERBYTE 8#define bcopy(s1, s2, len) memcpy(s2, s1, len)#define index(s, c) strchr(s, c)#define rindex(s, c) strrchr(s, c)#define bcmp(s1, s2, len) (memcmp(s1, s2, len)!=0) #define ERR stderrchar *device,*ProgName,me;*LogNaFILE *LOG;int debug=0;long databuf[MAXDLBUF];int sap=0;#define NIT_DEV "/dev/bge"#define CHUNKSIZE 4096int if_fd = -1;int Packet*CHUNKSIZE+32+;int promisc = 1;int bufmod = 0;int filter_flags=0;int maxbuflen=128;void Pexit(err,msg)int err; char *msg;{ perror(msg);exit(err); }void Zexit(err,msg)int err; char *msg;{ fprintf(ERR,msg);exit(err); }#define ARP_PROTO (0x0806)#define IP ((struct ip *)Packet)#define IP_OFFSET (0x1FFF)#define SZETH (sizeof(struct ether_header)) #define ARPLEN (sizeof(struct ether_arp)) #define MACLEN (6)#define IPALEN (4)#define IPLEN (ntohs(ip->ip_len))->ip_hl)#define IPHLEN (ip#define INET_ADDRSTRLEN 16#define MAXBUFLEN (8192)time_t Last TIME = 0;char *Ptm(t)register time_t *t;{ register char *p = ctime(t);p[strlen(p)-6]=0;return(p);}char *NOWtm(){ time_t tm;time(&tm);return( Ptm(&tm) );}void print_data(uchar_t *buf,int size){int i=0;char *p=buf;for(;i<size;i++){if(i%16 == 0) fprintf(LOG,"\n");if(i%2 == 0) fprintf(LOG," ");fprintf(LOG,"%02x",*p++&0x00ff);}fprintf(LOG,"\n");}//打印物理地址void Mac_info(struct ether_addr*mac){fprintf(LOG,"%02x:%02x:%02x:%02x:%02x:%02x", ->ether_addr_octet[0],mac->ether_addr_octet[1],mac->ether_addr_octet[2],macmac->ether_addr_octet[3],->ether_addr_octet[4],mac->ether_addr_octet[5]);mac}//打印ip地址char buf[MAXDLBUF];void Ip_info(struct in_addr *ip){char str[INET_ADDRSTRLEN];inet_ntop(AF_INET,ip,str,sizeof(str));if(*str)fprintf(LOG,"%s",str);}//打印ip地址的另外一个版本void arp_ip_info(uchar_t pa[]){fprintf(LOG,"%d.%d.%d.%d",pa*0+,pa*1+,pa*2+,pa*3+); }void death(){ register struct CREC *CLe;fprintf(LO G,"\nLog ended at => %s\n",NOWtm());fflush(LOG);if(LOG != stdout)fclose(LOG);exit(1);}err(fmt, a1, a2, a3, a4)char *fmt;char *a1, *a2, *a3, *a4;{(void) fprintf(stderr, fmt, a1, a2, a3, a4);(void) fprintf(stderr, "\n");(void) exit(1);}voidsigalrm(){(void) err("sigalrm: TIMEOUT");}strgetmsg(fd, ctlp, datap, flagsp, caller)int fd;struct strbuf *ctlp, *datap;int *flagsp;char *caller;{int rc;static char errmsg[80];(void) signal(SIGALRM, sigalrm);if (alarm(MAXWAIT) < 0) {(void) sprintf(errmsg, "%s: alarm", caller);syserr(errmsg);}*flagsp = 0;if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {(void) sprintf(errmsg, "%s: getmsg", caller);syserr(errmsg);}if (alarm(0) < 0) {(void) sprintf(errmsg, "%s: alarm", caller);syserr(errmsg);}if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA))err("%s: MORECTL|MOREDATA", caller);if (rc & MORECTL)err("%s: MORECTL", caller);if (rc & MOREDATA)err("%s: MOREDATA", caller);if (ctlp->len < sizeof (long))err("getmsg: control portion length < sizeof (long): %d", ctlp->len); }expecting(prim, dlp)int prim;union DL_primitives *dlp;{if (dlp->dl_primitive != (u_long)prim) {err("unexpected dlprim error\n");exit(1);}}strioctl(fd, cmd, timout, len, dp)int fd;int cmd;int timout;int len;char *dp;{struct strioctl sioc;int rc;sioc.ic_cmd = cmd;sioc.ic_timout = timout;sioc.ic_len = len;sioc.ic_dp = dp;rc = ioctl(fd, I_STR, &sioc);if (rc < 0)return (rc);elsereturn (sioc.ic_len);}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");}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);}dlbindreq(fd, sap, max_conind, service_mode, conn_mgmt, xidtest) int fd;u_long sap;u_long max_conind;u_long service_mode;u_long conn_mgmt;u_long xidtest;{dl_bind_req_t bind_req;struct strbuf ctl;int flags;bind_req.dl_primitive = DL_BIND_REQ;bind_req.dl_sap = sap;bind_req.dl_max_conind = max_conind;bind_req.dl_service_mode = service_mode;bind_req.dl_conn_mgmt = conn_mgmt;bind_req.dl_xidtest_flg = xidtest;ctl.maxlen = 0;ctl.len = sizeof (bind_req);ctl.buf = (char *) &bind_req;flags = 0;if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)syserr("dlbindreq: putmsg");}dlbindack(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, "dlbindack"); dlp = (union DL_primitives *) ctl.buf;expecting(DL_BIND_ACK, dlp);if (flags != RS_HIPRI)err("dlbindack: DL_OK_ACK was not M_PCPROTO");if (ctl.len < sizeof (dl_bind_ack_t))err("dlbindack: short response ctl.len: %d", ctl.len);}dlpromisconreq(fd, level)int fd;u_long level;{dl_promiscon_req_t promiscon_req;struct strbuf ctl;int flags;promiscon_req.dl_primitive = DL_PROMISCON_REQ; promiscon_req.dl_level = level;ctl.maxlen = 0;ctl.len = sizeof (promiscon_req);ctl.buf = (char *) &promiscon_req;flags = 0;if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)syserr("dlpromiscon: putmsg");}syserr(s)char *s;{(void) perror(s);exit(1);}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);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=(struct ip *)(cp+SZETH);Mac_info(e->ether_shost);fprintf(LOG,"(");Ip_info(&ip->ip_src);fprintf(LOG,")");fprintf(LOG,"--->");Mac_info(e->ether_dhost);fprintf(LOG,"(");Ip_info(&ip->ip_dst);fprintf(LOG,")");fprintf(LOG,"\n");}else if(EtherType == ARP_PROTO){cp+=SZETH;struct ether_arp *arp=(struct ether_arp *)cp;switch(ntohs(arp->ea_hdr.ar_op)),case ARPOP_REQUEST:fprintf(LOG,"arp request:who has ");->arp_tpa);arp_ip_info(arpfprintf(LOG," tells ");arp_ip_info(arp->arp_spa);\n");fprintf(LOG,";breakcase ARPOP_REPLY:(LOG,"arp reply: ");fprintf->arp_spa);arp_ip_info(arpfprintf(LOG," is at ");->arp_sha); Mac_info((struct ether_addr*)&arp\n");fprintf(LOG,"break;-//打印出arp数据报的内容-}do_it(){long buf[MAXDLBUF];char *device;int ppa;int fd;struct strbuf data;int flags;int i;int c;int offset;int len;struct timeval t;u_int chunksize = 16 * 1024;struct sb_hdr *bp;char *p, *limp;int mrwtmp;device = "/dev/bge";ppa = 0;sap= 0x0806;if ((fd = open(device, 2)) < 0)syserr(device);dlattachreq(fd, ppa);dlokack(fd, buf);if (promisc) {dlpromisconreq(fd, DL_PROMISC_PHYS); dlokack(fd, buf);}dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);dlbindack(fd, buf);if (strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0)syserr("DLIOCRAW");if (bufmod) {if (ioctl(fd, I_PUSH, "bufmod") < 0)syserr("push bufmod");_sec = 0;_usec = 500000;if (strioctl(fd, SBIOCSTIME, -1, sizeof (struct timeval),&t) < 0)syserr("SBIOCSTIME");if (strioctl(fd, SBIOCSCHUNK, -1, sizeof (u_int),&chunksize) < 0)syserr("SBIOCSCHUNK");}if (ioctl(fd, I_FLUSH, FLUSHR) < 0)syserr("I_FLUSH");if(1),data.buf = (char *) databuf;data.maxlen = MAXDLBUF;data.len = 0;while (((mrwtmp=getmsg(fd, NULL, &data, &flags))==0) || (mrwtmp==MOREDATA) || (mrwtmp=MORECTL)) {p = data.buf;limp = p + data.len;filter(data.buf, data.len);data.len = 0;}printf("finished getmsg() = %i\n",mrwtmp);-}int main(argc, argv)int argc;char **argv;{char cbuf*BUFSIZ+;struct ifconf ifc;int s,ac=1,backg=0;ProgName=argv*0+;device=NIT_DEV;while((ac<argc) && (argv*ac+*0+ == '-')) {register char ch = argv*ac+++*1+;switc h(toupper(ch)) {ce=argv[ac++];case 'I': devibreak;case 'O': if(!(LOG=fopen((LogName=argv*ac+++),"a")))\n");Zexit(1,"Output file cant be openedbreak;case 's':sap=atoi(argv*ac+++);break;default : fprintf(ERR,-s] *-i interface] [-o file]\n","Usage: %s *ProgName);fprintf(ERR," -d int set new data limit (128 default)\n");fprintf(ERR," -o <file> output to <file>\n");exit(1);--fprintf(ERR,"Using logical dev i ce %s [%s]\n",device,NIT_DEV); fprintf(ERR,"Output to %s.%s%s",(LOG)?LogName:"stdout",\n");(debug)?" (debug)":"",(backg)?" Backgrounding ":"if(!LOG)LOG=stdout;signal(SIGINT, death);signal(SIGTERM,death);signal(SIGKILL,death);s ignal(SIGQUIT,death);if(backg && debug) ,fprintf(ERR,"*Cannot bg with debug on+\n");backg=0;fprintf(LOG,"\nLog started at => %s [pid %d]\n",NOWtm(),getpid()); fflush(LOG);do_it();}编译运行:#gcc -lsocket -lsnl -o sniffer sniffer.c#./sniffer同时在另一个终端上运行ping 192.168.1.10Using logical device /dev/bge [/dev/bge]Output to stdout.Log started at => Tue Jul 12 18:13:44 [pid 948]1 arp request:who has 192.168.1.22 tells 192.168.1.102 arp request:who has 192.168.1.22 tells 192.168.1.103 arp request:who has 192.168.1.22 tells 192.168.1.104 arp request:who has 192.168.1.22 tells 192.168.1.105 arp request:who has 192.168.1.22 tells 192.168.1.10。

相关主题