首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第17期->技术专题
期刊号: 类型: 关键词:
libpcap使用举例(1)

作者:小四 < mailto: scz@nsfocus.com >
主页:http://www.nsfocus.com
日期:2001-01-10



我们曾经提供过<<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语言的传奇。

<待续>
版权所有,未经许可,不得转载