1、[保留] 使用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。
2、由于本人使用的是solaris环境,所以无法使用bpf,但是sun公司仍然为开发者提供了一个与设备底层无关的接口DLPI,DLPI的全称是Data Link Provider Interface,通过DLPI开发者可以访问数据链路层的数据包,在早期的sunos系统中基本上采用的是NIT设备,但是现在solaris系统都使用了DLPI.关于DLPI的具体介绍大家可以访问网站www.opengroup.org/pubs/catalog/c811.htm,我这里就不多说了。 在搜索了许多资料之后发现目前关于DLPI的编程资料不多,没有具体的过程,后来翻阅了Neal Nuckolls写的一篇文章H
3、ow to Use the STREAMS Data Link Provider Interface (DLPI),根据例子做了修改(主要是提供了协议分析的部分),现在把编写一个DLPI过程共享一下,希望能对大家有所帮助。建议大家可以先看看Neal Nuckolls的文章,其中有部分涉及到流编程的,可以参考 programming guide(不过这不是必须的)。 使用DLPI来访问数据链路层有几个步骤: 1、打开网络设备 2、将一个流 attach到一个特定的设备上,这里就是我们刚才打开的设备 3、将设备设置为混杂模式(可选) 4、把数据链路层sap绑定到流 5、调用
4、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 strbu
5、f 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_att
6、ach_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,
7、 &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");
8、 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, DL
9、IOCRAW, -1, 0, NULL) 第六步,配置其他模块(在详细代码中给出) 第七步,刷新数据,这是通过ioctl调用实现的 ioctl(fd, I_FLUSH, FLUSHR) 第八步,这是我们最关心的步骤,实际上,前面的这些步骤我们都可以忽略,大致明白有这么个过程就可以了,到时候写代码的时候照搬这个框架就可以。使用DLPI编程并不难,关键在于大家要了解它的框架,没必要非得自己去写一个框架来,本文就是利用了Michael R. Widner的代码,今后如果要增加功能只需要往这个框架里填就可以了。 协议分析的过程是在函数filter完成的,函数申明如下 voi
10、d 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。通过检查类型字
11、段来区别接收到的数据是属于哪一种协议,函数实现代码如下 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
12、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(EtherTyp
13、e == ETHERTYPE_IP) //检查协议类型是否IP协议 { ip=(struct ip *)(cp+SZETH);//调整指针的位置,SZETH是以太网首部长度 Mac_info(ð->;ether_shost);//Mac_info函数打印出物理地址 fprintf(LOG,"("); Ip_info(&ip->;ip_src);//Ip_info函数打印出IP地址 fprintf(LOG,")"); fprintf(LOG,"--->;"); Mac_info(ð->;ether_dho
14、st); 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(arp->;ea_hdr.ar_op))//检查arp的操作 {
15、 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; ca
16、se 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; } //可以在这里
17、添加代码打印出arp数据报的具体内容
}
}
程序的具体实现代码如下:
/* 程序sniffer.c的代码清单 */
#include
18、
#include 19、tinet/udp.h>;
#include 20、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 stderr
char *device,
*ProgName,
*LogName 21、
FILE *LOG;
int debug=0;
long databuf[MAXDLBUF];
int sap=0;
#define NIT_DEV "/dev/bge"
#define CHUNKSIZE 4096
int if_fd = -1;
int Packet[CHUNKSIZE+32];
int promisc = 1;
int bufmod = 0;
int filter_flags=0;
int maxbuflen=128;
void Pexit(e 22、rr,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_ 23、header))
#define ARPLEN (sizeof(struct ether_arp))
#define MACLEN (6)
#define IPALEN (4)
#define IPLEN (ntohs(ip->;ip_len))
#define IPHLEN (ip->;ip_hl)
#define INET_ADDRSTRLEN 16
#define MAXBUFLEN (8192)
time_t LastTIME = 0;
char *Ptm(t)
24、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 25、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",
mac->;ether_addr_octet[0],
mac->;ether_addr_octet[1],
26、mac->;ether_addr_octet[2],
mac->;ether_addr_octet[3],
mac->;ether_addr_octet[4],
mac->;ether_addr_octet[5]);
}
//打印ip地址char buf[MAXDLBUF];
void Ip_info(struct in_addr *ip)
{
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET,ip,str,sizeof( 27、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(LOG,"\nLog ended at =>; %s\n",NOWtm());
fflus 28、h(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);
}
void
sigalrm()
{
(void) err("sigalrm: 29、 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( 30、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 31、 | 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) 32、
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;
33、
sioc.ic_timout = timout;
sioc.ic_len = len;
sioc.ic_dp = dp;
rc = ioctl(fd, I_STR, &sioc);
if (rc < 0)
return (rc);
else
return (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_pri 34、mitive = 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;
{ 35、
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 36、))
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 37、 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_ 38、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");
}
39、
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_B 40、IND_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;
struc 41、t 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("dlpromisco 42、n: 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 lo 43、ng 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 = 44、 ETHERTYPE_IP)
{
ip=(struct ip *)(cp+SZETH);
Mac_info(ð->;ether_shost);
fprintf(LOG,"(");
Ip_info(&ip->;ip_src);
fprintf(LOG,")");
fprintf(LOG,"--->;");
Mac_info(ð->;ether_dhost);
fprintf(LOG,"(");
Ip_info(&ip->;ip_dst);
45、
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 46、request:who has ");
arp_ip_info(arp->;arp_tpa);
fprintf(LOG," tells ");
arp_ip_info(arp->;arp_spa);
fprintf(LOG,"\n");
break;
case ARPOP_REPLY:
fprintf(LOG,"arp reply: ");
arp_ip_info(arp->;ar 47、p_spa);
fprintf(LOG," is at ");
Mac_info((struct ether_addr*)&arp->;arp_sha);
fprintf(LOG,"\n");
break;
}
//打印出arp数据报的内容
}
}
do_it()
{
long buf[MAXDLBUF];
char *devic 48、e;
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) 49、
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");
t.tv_sec = 0;
t.tv_usec = 500000;
if (strioctl(fd, SBIOCSTIME, -1, sizeof (struct timeval),
&t) < 0)
syserr("SBIOCSTIME");
if






