libpcap使用总结!
libpcap 是一个开发sniffer的工具包。pcap:packet capture!
libpcap的数据类型定义:
struct pcap_addr:网卡地址描述
{
pcap_addr * next;
sockaddr * addr;
sockaddr * netmask;
sockaddr *broadaddr;
sockaddr *dstaddr;
};
pcap_addr * next;
如果非空,指向链表中一个元素的指针;空表示链表中的最后一个元素。
sockaddr * addr;
指向包含一个地址的sockaddr的结构的指针。
sockaddr * netmask;
如果非空,指向包含相对于addr指向的地址的一个网络掩码的结构。
sockaddr * broadaddr;
如果非空,指向包含相对于addr指向的地址的一个广播地址,如果网络不支持广播可能为空。
sockaddr * dstaddr;
如果非空,指向一个相对于addr指向的源地址的目的地址,如果网络不支持点对点通讯,则为空。
struct pcap_file_header {
bpf_u_int32 magic;
u_short version_major;
u_short version_minor;
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
bpf_u_int32 magic;
u_short version_major;
Libpcap的主版本号。
u_shart version_minor;
Libpcap的从版本号。
bpf_u_int32 sigfigs;
时间戳描述。
bpf_u_int32 snaplen;
保存的每个pkt的分片号的最大值。
bpf_u_int32 linktype;
数据链的类型。
细节说明:
libpcap dump文件头;
libpcap dump文件中的第一个记录包含了一些标志的保存值,这些标志在打印阶段用到。这儿的很多域都是32位的int,所以compilers不用进行转化;这些文件需要具有跨层次的可交换性。
无论如何不要改变结构的层次(包括仅仅改变这个结构中域的长度);
struct pcap_if { /*网卡数据链的一个元素*/
struct pcap_if *next;
char *name; /* name to hand to "pcap_open_live()" */
char *description; /* textual description of interface, or NULL */
struct pcap_addr *addresses;
u_int flags; /* PCAP_IF_ interface flags */
};
pcap_if *next;
如果非空,指向链的下一个元素。如果为空是链的最后一个元素。
char * name;
指向一个字符串,该字符串是传给pcap_open_live()函数的设备名;
char * description;
如果非空,指向一个对设备的人性化的描述字符串。
pcap_addr *addresses;
指向网卡地址链中的第一个元素。
u_int flags;
PCAP_IF_ 网卡的标志。现在唯一可用的标识是PCAP_IF_LOOKBACK,它被用来标识网卡是不是lookback网卡。
struct pcap_pkthdr { /*dump 文件中的数据包头*/
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
timeval ts;
数据报时间戳;
bpf_u_int32 caplen;
当前分片的长度;
dpf_u_int32 len;
这个数据报的长度;
细节描述:
在dump文件中的每个数据报都有这样一个报头。它用来处理不同数据报网卡的不同报头问题。
struct pcap_stat { /*用来保存网卡静态变量的结构*/
u_int ps_recv; /* number of packets received */
u_int ps_drop; /* number of packets dropped */
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
};
u_int ps_recv;
接受数据报的数目;
u_int ps_drop;
被驱动程序丢弃的数据报的数目;
u_int ps_ifdrop;
被网卡丢弃的数据报的数目;
lipcap的声明:
#define PCAP_VERSION_MAJOR 2
libpcap dump文件的主版本号;
#define PCAP_VERSION_MINOR 4
libpcap dump文件的从版本号;
#define PCAP_ERRBUF_SIZE 256
用来存放libpcap出错信息的缓冲区的大小;
#define PCAP_IF_LOOPBACK 0x00000001
网卡是回环网卡;
#define MODE_CAPT 0
抓报模式,在调用pcap_setmode()时使用;
#define MODE_STAT 1
静态模式,在调用pcap_setmode()时使用;
libpcap的类型定义:
typedef int bpf_int32
32bit 的整形;
typedef u_int bpf_u_int32
32bit 的无类型整形;
typedef pcap pcap_t
Descriptor of an open capture instance(一个打开的捕获实例的描述符?)这个结构对用户是不透明的。
typedef pcap_dumper pcap_dumper_t
libpcap保存文件的描述符。
typedef pcap_if pcap_if_t
网卡链表的一个元素;
typedef pcap_addr pcap_addr_t
网卡地址的表示;
libpcap函数描述:
char *pcap_lookupdev(char * errbuf);
描述:这个函数用于获取一个合适的网卡描述,以供pcap_open_liver函数和pcap_lookupnet函数使用。如果找不到网卡或者所有网卡为 off,则返回null。如果一个系统中有多个网卡,那么该函数返回找到的第一个on的网卡。最后才是回环接口。回环网卡一直被忽略;
参数:
char * errbuf 存放pcap_lookupdev函数的出错信息,只有在pcap_lookup失败是才有值。
返回值: 如果函数执行成功,则返回一个用于描述系统上的一个网卡的描述符的指针。如果失败,返回null,errbuf中存放出错信息。
int pcap_lookupnet(char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp,char * errbuf);
描述:该函数用于监测网卡所在网络的网络地址和子网掩码。
参数:
char *devic:网卡的描述符指针,由pcap_looupdev函数获取;
bpf_u_int32 *netp:存放网络地址;
bpf_u_int32 *maskp:存放子网掩码;
char * errbuf: 存放出错信息;
返回值:如果函数执行成功,则返回值为0,否则返回值为-1,并在errbuf中存放出错信息。
pcap_t *pcap_open_live(char * device, int snaplen,int promisc, int to_ms, char * ebuf);
描述:该函数用于打开网卡用于捕获数据报。单词live的意思就是表示一个运行的网卡(相对于offline而言)被打开了,如同一个保存有被抓数据报的文件被打开一样。在捕获数据报之前这个函数必须被执行。所有的其他的用于处理数据报捕获的函数用到的捕获数据报的描述符由该函数产生。查看 pcap_open_offlin()函数的定义,了解如何打开一个预先保存的包含数据报的文件的细节。
参数:
char *device:网卡的描述符指针,由pcap_looupdev函数获取;
int snaplen:规定捕获的每个数据报的最大字节数;
int promisc:1为混杂模式;0为非混杂模式;
int to_ms:规定读超时的微秒(milliseconds)数;
char *ebuf:存放错误信息,只有在pcap_open_live失败时才被设置;
返回值:如果函数成功执行,则返回一个指向数据报捕获的指针;如果错误,返回null,ebuf存放出错信息;
int pcap_compile(pcap_t * p, struct bpf_ program *fp, char * str,int optimize, bpf_u_int32 netmask);
描述:该函数用于将str指定的规则整合到fp过滤程序中去,并生成过滤程序入口地址,用于过滤选择期望的数据报;
参数:
pcap_t *p:pcap_open_live返回的数据报捕获的指针;
struct bpf_program *fp:指向一个子函数用于过滤,在pcap_compile()函数中被赋值;
char *str:该字符串规定过滤规则;
int optimize:规定了在结果代码上的选择是否被执行;
bpf_u_int32 netmask:该网卡的子网掩码,可以通过pcap_lookupnet()获取;
返回值:
如果成功执行,返回0,否则返回-1;
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback,u_char * user);
描述:
该函数用于读取和处理数据报。既可以用来处理事先捕获的保存在文件中的数据报,也可以用来处理实时捕获的数据报;
这个函数类似于pcap_dispatch函数,除了它继续读取数据报直至完成cnt个报的处理,或者文件处理完(在offline情况下),或者有错误发生为止。它不会在实时读超时时返回(而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用
pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。)
注意第三个参数,callback是pcap_handler类型的变量。这是一个用户提供的有着三个参数的子函数。定义为:
void user_routine(u_char *user, struct pcap_pkthdr *phrd, u_char *pdata)
这三个参数中,user,是传递给pcap_dispatch()的那个参数;phdr,是个pcap_pkthdr类型的指针,是savefile中的数据报的头指针,pdata,指向数据报数据;这个函数允许用户定义子集的数据报过滤程序;
参数:
pcap_t * pcap_open_live返回的数据报捕获的指针;
int cnt:规定了函数返回前应处理的数据报数目;
pcap_handler callback:指向一个用户自定义的函数,在处理每个报后自动调用该函数进行再处理;
u_char *user:该指针用于传递给callback.(不知道有什么用?)
返回值:
如果函数成功执行(包括读文件时读到EOF),则返回0.否则返回-1,那么错误信息将由函数pcap_geterr或pcap_perror给出;
libpcap使用举例(1)
发布日期:2001-01-23
作者:小四 < mailto: scz@nsfocus.com >
主页:http://www.nsfocus.com
日期:2000-12-16
我们曾经提供过<<libnet使用举例(1-12)>>,比较详细地介绍了报文发送编程。始终
没有介绍libpcap报文捕捉编程的原因很多,tcpdump、snort等著名软件包都是基于
libpcap,加上W.Richard.Stevens的<<Unix Network Programming Vol I>>第26章推
波助澜,实在觉得没有必要继续介绍libpcap编程。更真实的原因可能是BPF、DLPI、
SOCK_PACKET三种接口编程已经被演练得太多太滥。
今天讨论的不是效率,而是可能的移植性要求。没办法,第一次使用libpcap库,举
例能深入到什么地步,不知道。如果你也是第一次用这个库,跟我来,第N次使用?
那还是忙你的去吧,别来看这篇无聊的灌水,:-P。我无聊是因为有程序要广泛可移
植,你无聊是为什么。
char * pcap_lookupdev ( char * errbuf );
该函数返回一个网络设备接口名,类似libnet_select_device(),对于Linux就是
"eth0"一类的名字。pcap_open_live()、pcap_lookupnet()等函数将用到这个网络设
备接口名。失败时返回NULL,errbuf包含了失败原因。errbuf一般定义如下:
/usr/include/pcap.h
#define PCAP_ERRBUF_SIZE 256
char errbuf[ PCAP_ERRBUF_SIZE ];
pcap_t * pcap_open_live ( char * device, int snaplen, int promisc,
int to_ms, char * errbuf );
该函数用于获取一个抽象的包捕捉句柄,后续很多libpcap函数将使用该句柄,类似
文件操作函数频繁使用文件句柄。device指定网络接口设备名,比如"eth0。snaplen
指定单包最大捕捉字节数,为了保证包捕捉不至于太低效率,snaplen尽量适中,以
恰能获得所需协议层数据为准。promisc指定网络接口是否进入混杂模式,注意即使
该参数为false(0),网络接口仍然有可能因为其他原因处在混杂模式。to_ms指定毫
秒级读超时,man手册上并没有指明什么值意味着永不超时,测试下来的结论,0可能
代表永不超时。如果调用失败返回NULL,errbuf包含失败原因。
--------------------------------------------------------------------------
/usr/include/pcap.h
typedef struct pcap pcap_t;
pcap-int.h里定义了struct pcap {}
struct pcap
{
int fd;
int snapshot;
int linktype;
int tzoff; /* timezone offset */
int offset; /* offset for proper alignment */
struct pcap_sf sf;
struct pcap_md md;
int bufsize; /* Read buffer */
u_char * buffer;
u_char * bp;
int cc;
u_char * pkt; /* Place holder for pcap_next() */
struct bpf_program fcode; /* Placeholder for filter code if bpf not in kernel. */
char errbuf[PCAP_ERRBUF_SIZE];
};
--------------------------------------------------------------------------
int pcap_lookupnet ( char * device, bpf_u_int32 * netp,
bpf_u_int32 * maskp, char * errbuf );
该函数用于获取指定网络接口的IP地址、子网掩码。不要被netp的名字所迷惑,它对
应的就是IP地址,maskp对应子网掩码。
/usr/include/pcap.h
typedef u_int bpf_u_int32;
显然简单理解成32-bit即可。如果调用失败则返回-1,errbuf包含失败原因。
int pcap_compile ( pcap_t * p, struct bpf_program * fp, char * str,
int optimize, bpf_u_int32 netmask );
该函数用于解析过滤规则串,填写bpf_program结构。str指向过滤规则串,格式参看
tcpdump的man手册,比如:
tcpdump -x -vv -n -t ip proto //tcp and dst 192.168.8.90 and tcp[13] /& 2 = 2
这条过滤规则将捕捉所有携带SYN标志的到192.168.8.90的TCP报文。过滤规则串可以
是空串(""),表示抓取所有过路的报文。
optimize为1表示对过滤规则进行优化处理。netmask指定子网掩码,一般从
pcap_lookupnet()调用中获取。返回值小于零表示调用失败。
这个函数可能比较难于理解,涉及的概念源自BPF,Linux系统没有这种概念,但是
libpcap采用pcap_compile()和pcap_setfilter()结合的办法屏蔽了各种链路层支持
的不同,无论是SOCK_PACKET、DLPI。曾在华中Security版上写过一篇
<<内核包捕获过滤机制介绍>>,参看该文加强理解。
--------------------------------------------------------------------------
# tcpdump -d ip proto //tcp and dst 192.168.8.90 and tcp[13] /& 2 = 2
(000) ldh [-4096]
(001) jeq #0x800 jt 2 jf 13
(002) ldb [9]
(003) jeq #0x6 jt 4 jf 13
(004) ld [16]
(005) jeq #0xc0a8085a jt 6 jf 13
(006) ldh [6]
(007) jset #0x1fff jt 13 jf 8
(008) ldxb 4*([0]&0xf)
(009) ldb [x + 13]
(010) and #0x2
(011) jeq #0x2 jt 12 jf 13
(012) ret #65535
(013) ret #0
#
/usr/include/net/bpf.h
/* Structure for BIOCSETF. */
struct bpf_program
{
u_int bf_len;
struct bpf_insn * bf_insns;
};
/*
* The instruction data structure.
*/
struct bpf_insn
{
u_short code;
u_char jt;
u_char jf;
bpf_int32 k;
};
/*
* Macros for insn array initializers.
*/
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
--------------------------------------------------------------------------
int pcap_setfilter ( pcap_t * p, struct bpf_program * fp );
该函数用于设置pcap_compile()解析完毕的过滤规则,如果你足够聪明(愚公?),完
全可以自己提供过滤规则,无须pcap_compile()介入,就象你写
Password Sniffer For I386/FreeBSD时常做的那样。成功返回0,失败返回-1。
int pcap_dispatch ( pcap_t * p, int cnt, pcap_handler callback, u_char * user );
该函数用于捕捉报文、分发报文到预先指定好的处理函数(回调函数)。
pcap_dispatch()接收够cnt个报文便返回,如果cnt为-1意味着所有报文集中在一个
缓冲区中。如果cnt为0,仅当发生错误、读取到EOF或者读超时到了(pcap_open_live
中指定)才停止捕捉报文并返回。callback指定如下类型的回调函数,用于处理
pcap_dispatch()所捕获的报文:
typedef void ( *pcap_handler ) ( u_char *, const struct pcap_pkthdr *, const u_char * );
pcap_dispatch()返回捕捉到的报文个数,如果在读取静态文件(以前包捕捉过程中存
储下来的)时碰到EOF则返回0。返回-1表示发生错误,此时可以用pcap_perror()、
pcap_geterr()显示错误信息。
下面来看看那个回调函数,总共有三个参数,第一个形参来自pcap_dispatch()的第
三个形参,一般我们自己的包捕捉程序不需要提供它,总是为NULL。第二个形参指向
pcap_pkthdr结构,该结构位于真正的物理帧前面,用于消除不同链路层支持的差异。
最后的形参指向所捕获报文的物理帧。
--------------------------------------------------------------------------
/usr/include/pcap.h
/*
* Each packet in the dump file is prepended with this generic header.
* This gets around the problem of different headers for different
* packet interfaces.
*/
struct pcap_pkthdr
{
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
/usr/include/net/bpf.h
/*
* Structure prepended to each packet.
*/
struct bpf_hdr
{
struct timeval bh_tstamp; /* time stamp */
bpf_u_int32 bh_caplen; /* length of captured portion */
bpf_u_int32 bh_datalen; /* original length of packet */
u_short bh_hdrlen; /* length of bpf header (this struct
plus alignment padding) */
};
/*
* Because the structure above is not a multiple of 4 bytes, some compilers
* will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work.
* Only the kernel needs to know about it; applications use bh_hdrlen.
*/
#ifdef KERNEL
#define SIZEOF_BPF_HDR 18
#endif
--------------------------------------------------------------------------
void pcap_close ( pcap_t * p );
该函数用于关闭pcap_open_live()获取的包捕捉句柄,释放相关资源。
void pcap_perror ( pcap_t * p, char * prefix );
第一形参来自pcap_open_live(),第二行参的作用类似perror()的形参,指定错误信
息的前缀,与perror()一样,结尾自动输出一个换行。
pcap_perror( p, "pcap_compile" )的输出类似这个效果:
pcap_compile: unknown ip proto ...
pcap_perror并不自动exit(),与perror()一样,如果需要,应该显式调用exit()。
介绍到这里,已经可以写简单的sniffer。出于完整演示目的,提供这样一个sample
code。请勿询问任何关于该代码的问题,烦了。
--------------------------------------------------------------------------
/*
* File : sniffer program for I386/Linux using libpcap
* Version: 0.01 aleph
* Author : Anonymous ( Don't ask anything about this program, please. )
* Complie: gcc -O3 -o pcap pcap_sniffer.c -lpcap `libnet-config --defines --cflags` -Wall
* : strip pcap
* Usage : ./pcap -h
* Date : 2000-12-15 16:35
*/
/*******************************************************************
* *
* Head File *
* *
*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pcap.h>
#include <libnet.h> /* for LIBNET_TCP_H */
/*******************************************************************
* *
* Macro *
* *
*******************************************************************/
#define SUCCESS 0
#define FAILURE -1
typedef void Sigfunc ( int ); /* for signal handlers */
/*******************************************************************
* *
* Static Global Var *
* *
*******************************************************************/
static pcap_t * pcap_fd = NULL; /* 抽象的包捕捉句柄 */
/*******************************************************************
* *
* Function Prototype *
* *
*******************************************************************/
static void Atexit ( void ( * func ) ( void ) );
static void bpf_dump ( struct bpf_program * p, int option );
char * bpf_image ( struct bpf_insn * p, int n );
static void outputBinary ( const u_char * byteArray, const size_t byteArrayLen );
static void pcap_callback ( u_char * none, const struct pcap_pkthdr * pcap_head, const u_char * packet );
static pcap_t * pcap_init ( char * dev, char * filter, int snaplen, int timeout, int dumplevel );
static void pcap_read ( pcap_t * p );
static void sig_end ( int signo );
Sigfunc * signal ( int signo, Sigfunc * func );
static Sigfunc * Signal ( int signo, Sigfunc * func ); /* for our signal() function */
static void terminate ( void );
static void usage ( char * arg );
/*----------------------------------------------------------------------*/
static void Atexit ( void ( * func ) ( void ) )
{
if ( atexit( func ) != 0 )
{
exit( FAILURE );
}
return;
} /* end of Atexit */
static void bpf_dump ( struct bpf_program * p, int option )
{
struct bpf_insn * insn;
int i;
int n = p->bf_len;
insn = p->bf_insns;
if ( option > 2 )
{
fprintf( stderr, "%d/n", n );
for ( i = 0; i < n; ++insn, ++i )
{
fprintf( stderr, "%u %u %u %u/n", insn->code,
insn->jt, insn->jf, insn->k );
}
return;
}
if ( option > 1 )
{
for ( i = 0; i < n; ++insn, ++i )
{
fprintf( stderr, "{ 0x%x, %d, %d, 0x%08x },/n",
insn->code, insn->jt, insn->jf, insn->k );
}
return;
}
for ( i = 0; i < n; ++insn, ++i )
{
puts( bpf_image( insn, i ) );
}
} /* end of bpf_dump */
char * bpf_image ( struct bpf_insn * p, int n )
{
int v;
char * fmt;
char * op;
static char image[256];
char operand[64];
v = p->k;
switch ( p->code )
{
default:
op = "unimp";
fmt = "0x%x";
v = p->code;
break;
case BPF_RET|BPF_K:
op = "ret";
fmt = "#%d";
break;
case BPF_RET|BPF_A:
op = "ret";
fmt = "";
break;
case BPF_LD|BPF_W|BPF_ABS:
op = "ld";
fmt = "[%d]";
break;
case BPF_LD|BPF_H|BPF_ABS:
op = "ldh";
fmt = "[%d]";
break;
case BPF_LD|BPF_B|BPF_ABS:
op = "ldb";
fmt = "[%d]";
break;
case BPF_LD|BPF_W|BPF_LEN:
op = "ld";
fmt = "#pktlen";
break;
case BPF_LD|BPF_W|BPF_IND:
op = "ld";
fmt = "[x + %d]";
break;
case BPF_LD|BPF_H|BPF_IND:
op = "ldh";
fmt = "[x + %d]";
break;
case BPF_LD|BPF_B|BPF_IND:
op = "ldb";
fmt = "[x + %d]";
break;
case BPF_LD|BPF_IMM:
op = "ld";
fmt = "#0x%x";
break;
case BPF_LDX|BPF_IMM:
op = "ldx";
fmt = "#0x%x";
break;
case BPF_LDX|BPF_MSH|BPF_B:
op = "ldxb";
fmt = "4*([%d]&0xf)";
break;
case BPF_LD|BPF_MEM:
op = "ld";
fmt = "M[%d]";
break;
case BPF_LDX|BPF_MEM:
op = "ldx";
fmt = "M[%d]";
break;
case BPF_ST:
op = "st";
fmt = "M[%d]";
break;
case BPF_STX:
op = "stx";
fmt = "M[%d]";
break;
case BPF_JMP|BPF_JA:
op = "ja";
fmt = "%d";
v = n + 1 + p->k;
break;
case BPF_JMP|BPF_JGT|BPF_K:
op = "jgt";
fmt = "#0x%x";
break;
case BPF_JMP|BPF_JGE|BPF_K:
op = "jge";
fmt = "#0x%x";
break;
case BPF_JMP|BPF_JEQ|BPF_K:
op = "jeq";
fmt = "#0x%x";
break;
case BPF_JMP|BPF_JSET|BPF_K:
op = "jset";
fmt = "#0x%x";
break;
case BPF_JMP|BPF_JGT|BPF_X:
op = "jgt";
fmt = "x";
break;
case BPF_JMP|BPF_JGE|BPF_X:
op = "jge";
fmt = "x";
break;
case BPF_JMP|BPF_JEQ|BPF_X:
op = "jeq";
fmt = "x";
break;
case BPF_JMP|BPF_JSET|BPF_X:
op = "jset";
fmt = "x";
break;
case BPF_ALU|BPF_ADD|BPF_X:
op = "add";
fmt = "x";
break;
case BPF_ALU|BPF_SUB|BPF_X:
op = "sub";
fmt = "x";
break;
case BPF_ALU|BPF_MUL|BPF_X:
op = "mul";
fmt = "x";
break;
case BPF_ALU|BPF_DIV|BPF_X:
op = "div";
fmt = "x";
break;
case BPF_ALU|BPF_AND|BPF_X:
op = "and";
fmt = "x";
break;
case BPF_ALU|BPF_OR|BPF_X:
op = "or";
fmt = "x";
break;
case BPF_ALU|BPF_LSH|BPF_X:
op = "lsh";
fmt = "x";
break;
case BPF_ALU|BPF_RSH|BPF_X:
op = "rsh";
fmt = "x";
break;
case BPF_ALU|BPF_ADD|BPF_K:
op = "add";
fmt = "#%d";
break;
case BPF_ALU|BPF_SUB|BPF_K:
op = "sub";
fmt = "#%d";
break;
case BPF_ALU|BPF_MUL|BPF_K:
op = "mul";
fmt = "#%d";
break;
case BPF_ALU|BPF_DIV|BPF_K:
op = "div";
fmt = "#%d";
break;
case BPF_ALU|BPF_AND|BPF_K:
op = "and";
fmt = "#0x%x";
break;
case BPF_ALU|BPF_OR|BPF_K:
op = "or";
fmt = "#0x%x";
break;
case BPF_ALU|BPF_LSH|BPF_K:
op = "lsh";
fmt = "#%d";
break;
case BPF_ALU|BPF_RSH|BPF_K:
op = "rsh";
fmt = "#%d";
break;
case BPF_ALU|BPF_NEG:
op = "neg";
fmt = "";
break;
case BPF_MISC|BPF_TAX:
op = "tax";
fmt = "";
break;
case BPF_MISC|BPF_TXA:
op = "txa";
fmt = "";
break;
} /* end of switch */
( void )sprintf( operand, fmt, v );
( void )sprintf( image, ( BPF_CLASS( p->code ) == BPF_JMP && BPF_OP( p->code ) != BPF_JA ) ?
"(%03d) %-8s %-16s jt %d/tjf %d" : "(%03d) %-8s %s",
n, op, operand, n + 1 + p->jt, n + 1 + p->jf );
return image;
} /* end of bpf_image */
static void outputBinary ( const u_char * byteArray, const size_t byteArrayLen )
{
u_long offset;
int i, j, k;
fprintf( stderr, "byteArray [ %lu bytes ] ----> /n", ( long unsigned int )byteArrayLen );
if ( byteArrayLen <= 0 )
{
return;
}
i = 0;
offset = 0;
for ( k = byteArrayLen / 16; k > 0; k--, offset += 16 )
{
fprintf( stderr, "%08X ", ( unsigned int )offset );
for ( j = 0; j < 16; j++, i++ )
{
if ( j == 8 )
{
fprintf( stderr, "-%02X", byteArray[i] );
}
else
{
fprintf( stderr, " %02X", byteArray[i] );
}
}
fprintf( stderr, " " );
i -= 16;
for ( j = 0; j < 16; j++, i++ )
{
/* if ( isprint( (int)byteArray[i] ) ) */
if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 ) )
{
fprintf( stderr, "%c", byteArray[i] );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "/n" );
} /* end of for */
k = byteArrayLen - i;
if ( k <= 0 )
{
return;
}
fprintf( stderr, "%08X ", ( unsigned int )offset );
for ( j = 0 ; j < k; j++, i++ )
{
if ( j == 8 )
{
fprintf( stderr, "-%02X", byteArray[i] );
}
else
{
fprintf( stderr, " %02X", byteArray[i] );
}
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
fprintf( stderr, " " );
}
fprintf( stderr, " " );
for ( j = 0; j < k; j++, i++ )
{
if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 ) )
{
fprintf( stderr, "%c", byteArray[i] );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "/n" );
return;
} /* end of outputBinary */
static void pcap_callback ( u_char * none, const struct pcap_pkthdr * pcap_head, const u_char * packet )
{
outputBinary( ( u_char * )packet, ( size_t )( pcap_head->caplen ) );
return;
} /* end of pcap_callback */
static pcap_t * pcap_init ( char * dev, char * filter, int snaplen, int timeout, int dumplevel )
{
pcap_t * p = NULL;
char errbuf[ PCAP_ERRBUF_SIZE ];
struct bpf_program bpf;
bpf_u_int32 ip, mask;
if ( dev == NULL )
{
if ( ( dev = pcap_lookupdev( errbuf ) ) == NULL )
{
fprintf( stderr, "%s/n", errbuf );
exit( FAILURE );
}
}
fprintf( stderr, "[ device --> %s ]/n", dev );
/* 1表示进入混杂模式 */
if ( ( p = pcap_open_live( dev, snaplen, 1, timeout, errbuf ) ) == NULL )
{
fprintf( stderr, "%s/n", errbuf );
exit( FAILURE );
}
if ( pcap_lookupnet( dev, &ip, &mask, errbuf ) == -1 )
{
exit( FAILURE );
}
/* 1表示优化过滤规则 */
if ( pcap_compile( p, &bpf, filter, 1, mask ) < 0 )
{
/* for example, pcap_compile: unknown ip proto ... */
pcap_perror( p, "pcap_compile" );
exit( FAILURE );
}
if ( dumplevel >= 0 )
{
bpf_dump( &bpf, dumplevel );
exit( SUCCESS );
}
else if ( pcap_setfilter( p, &bpf ) == -1 )
{
exit( FAILURE );
}
return( p );
} /* end of pcap_init */
static void pcap_read ( pcap_t * p )
{
// static u_long count = 0;
while ( 1 )
{
pcap_dispatch( p, 1, pcap_callback, NULL );
// fprintf( stderr, "count = %lu/n", ( long unsigned int )count );
// count++;
} /* end of while */
return;
} /* end of pcap_read */
static void sig_end ( int signo )
{
fprintf( stderr, "/n/nsig_end = %d/n", signo );
exit( SUCCESS );
} /* end of sig_end */
Sigfunc * signal ( int signo, Sigfunc * func )
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset( &act.sa_mask );
act.sa_flags = 0;
if ( signo == SIGALRM )
{
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
#endif
}
else
{
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
}
if ( sigaction( signo, &act, &oact ) < 0 )
{
return( SIG_ERR );
}
return( oact.sa_handler );
} /* end of signal */
static Sigfunc * Signal ( int signo, Sigfunc * func ) /* for our signal() function */
{
Sigfunc * sigfunc;
if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR )
{
exit( FAILURE );
}
return( sigfunc );
} /* end of Signal */
static void terminate ( void )
{
if ( pcap_fd != NULL )
{
pcap_close( pcap_fd );
}
fprintf( stderr, "/n" );
return;
} /* end of terminate */
static void usage ( char * arg )
{
fprintf( stderr, " Usage: %s [-h] [-d dumplevel] [-i interface] [-s snaplen] [-t timeout]/n", arg );
exit( FAILURE );
} /* end of usage */
int main ( int argc, char * argv[] )
{
char * dev = NULL;
char filter[300] = ""; /* "ip proto //tcp and dst 192.168.8.90 and tcp[13] & 2 = 2" */
int snaplen = LIBNET_ETH_H + LIBNET_IP_H + LIBNET_TCP_H;
int timeout = 0; /* 值为0是否表示不设置读超时 */
int dumplevel = -1;
int c, i;
opterr = 0; /* don't want getopt() writing to stderr */
while ( ( c = getopt( argc, argv, "d:hi:s:t:" ) ) != EOF )
{
switch ( c )
{
case 'd':
dumplevel = atoi( optarg );
break;
case 'i':
dev = optarg; /* 指定网络接口设备 */
break;
case 's':
snaplen = atoi( optarg );
case 't':
timeout = atoi( optarg );
break;
case 'h':
case '?':
usage( argv[0] );
break;
} /* end of switch */
} /* end of while */
argc -= optind;
argv += optind;
if ( argc > 0 )
{
for ( i = 0; i < argc; i++ )
{
if ( ( strlen( filter ) + strlen( argv[i] ) ) > 256 )
{
fprintf( stderr, "Checking your filter./n" );
return( FAILURE );
}
strcat( filter, argv[i] );
strcat( filter, " " );
}
}
fprintf( stderr, "[ filter --> %s ]/n", filter );
Atexit( terminate );
for ( i = 1; i < 9; i++ )
{
Signal( i, sig_end );
}
Signal( SIGTERM, sig_end );
pcap_fd = pcap_init( dev, filter, snaplen, timeout, dumplevel );
pcap_read( pcap_fd );
return( SUCCESS );
} /* end of main */
/*----------------------------------------------------------------------*/
--------------------------------------------------------------------------
Usage: ./pcap [-h] [-d dumplevel] [-i interface] [-s snaplen] [-t timeout]
libpcap的好处还是很多,比如不需要为解析过滤规则耗费精力。这个程序再次演示
了很多经典Unix编程技巧,比如getopt()、signal()、atexit(),回调函数部分没有
做什么实际工作,看你自己发挥了。顺便提一句,即使是个小程序,也应该保持良好
的风格,在华中看到太多不负责任的提问中的垃圾代码,实在是有辱C语言的传奇。
libpcap应用
最新推荐文章于 2023-06-27 10:04:30 发布