利用pcap编写自己的sniffer程序

转自:http://blog.csdn.net/dangdanding/archive/2004/05/17/22419.aspx

 

Programming with pcap
Tim Carstens
timcarst at yahoo dot com
The latest version of this document can be found at http://broker.dhs.org/pcap.html

 

原文: http://www.tcpdump.org/pcap.htm

翻译: dangdanding@163.com

 

本文读者对象:需要基本的 C 语言基础知识,否则除非你只是想了解 pcap 编程的基本理论知识也可以阅读此文。当然你也不一定必须是网络编程的高手,因为本文所涉及的领域仅需要为有丰富网络编程经验的人所理解(言下之意是如果你对这方面不感兴趣或无意于向这方面发展就无所谓了)。本文中的所有代码示例均在缺省内核版本 BSD4.3 下经过测试(我在 RedHat 6.2 with kernel-2.2.14-5 下亦测试通过)。

 

Get Started: The format of a pcap application

首先让我们了解一个 pcap 应用程序的常用设计。代码的流程如下所示:

1、   首先决定将要用来 sniff 的网络接口。在 Linux 下可能是 eth0 BSD 下可能是 xl1 等。我们要么在一个字符串( char * )中定义这个设备,或用用户在命令行直接指定用来 sniff 的设备接口名。

2、   初始化 pcap 。这是明确指定用来 sniff 的的网络接口的地方。当然我们可以在多个接口设备上 sniff 。通过句柄( handle )我们可以区分这些不同的 sniff 设备接口。就像我们打开一个用来读或写的文件一样,我们必须命名我们的 sniffer session 以便于区别这些不同的 session

3、   通常情况下我们只希望 sniff 特定的网络通信(比如: tcp 数据包,所有发往 23 端口的 tcp 数据包)。通常我们制定这样一个定义特定网络通信的规则集,将其编译以后加载( apply to pcap 引擎上。这是编写 pcap 应用程序最主要的步骤,而且必须紧密关联。规则被保存在一个字符串中,通过编译被转换成 pcap 引擎能够识别的格式。事实上所谓的编译不过是在我们自己的程序中调用特定的函数就可以完成,并不涉及到任何外部的应用程序。然后我们可以告诉 pcap 引擎应用编译的规则作为我们 sniff 的规则( filter )。

4、   最后我们通知 pcap 引擎进入主要的处理流程: pcap 接受并处理匹配指定规则的制定数目的数据包。每当捕获一个新的数据包, pcap 调用自定义的回调函数进行相应的处理。在回调函数中可以做任何我们想做的事情:解剖捕获的数据包并打印到用户控制台,或者保存到文件中,当然也可以什么也不做(如果什么也不做,我们为什么要写这些代码呢?如果。。。那么。。。呢,呵呵已经很多人开始呕吐并晕倒了)

5、   结束 sniff 并关闭 pcap 会话句柄。

 

事实上,是用 pcap 编程是一个非常简单的过程,一共 5 个步骤,而且令你备感困惑的第 3 步还是可选的。详细实现如下。

 

Setting the Device

这是一个极其简单的操作(原文: This is terribly simple )。有两种方法可以设置用来 sniff 的网络接口。

1、 用户在命令行指定定监听的网络接口:

#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{
    char *dev = argv[1];
    printf("Device: %s/n", dev);
    return(0);
}

用户通过命令行参数传入监听接口。

译注:在实际的项目开发中务必对命令行参数进行判断:

      if (argc < 2) {

            printf(“Usage: %s <option>/n”, argv[0]);

            exit(1);

      }

 

2、   通过 pcap 引擎设定监听的网络接口:

#include <stdio.h>
#include <pcap.h>
int main()
{
    char *dev, errbuf[PCAP_ERRBUF_SIZE];
    dev = pcap_lookupdev(errbuf);
    printf("Device: %s/n", dev);
    return(0);
}

在这种情况下, pcap 引擎自己设置用来监听的接口。但是 errbuf 字符串用来做什么呢?大多数的 pcap 函数允许我们传递这样一个字符串作为其参数。这个字符串参数用来在 pcap 函数调用失败以后用来设置出错信息。在上面的例子中,如果 pcap_lookup 函数调用失败,出错信息将被保存在 errbuf 中。

译注:增加的错误检查的代码如下:

       if (NULL == (dev = pcap_lookupdev(errbuf))) {

              fprintf(stderr, “pcap_lookupdev() error: %s/n”, errbuf);

              exit(-1);

       }

       printf(“Device: %s/n”, dev);

 

Opening the device for sniffing

创建 sniff 会话的任务非常简单。我们使用 pcap_open_live() 创建 sniff 会话。函数原型:

pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)

       device :上节中我们制定的监听设备接口;

       snaplen :制定 pcap 捕获的最大数目的网络数据包;

       promisc >0 指定 device 接口工作在混杂模式( promiscous Mode );

       to_ms :制定经过特定时间( ms )后读超时; 0 表示遇到错误退出, -1 指定永不超时;

       ebuf :制定用来存储出错信息的字符串

       pcap_t :返回值为用于监听的 pcap 会话。

示例代码:

#include <pcap.h>
    ...
    pcap_t *handle;
    handle = pcap_open_live(somedev, BUFSIZ, 1, 0, errbuf);

上面的代码打开 somedev 指定的设备并读取(捕获) BUFSIZ 字节,同时我们设置接口工作在混杂模式,一直监听到有任何错误发生则退出,并将出错信息保存在 errbuf 指定的字符串中。

 

关于混杂模式 vs. 非混杂模式:通常情况在非混杂模式下仅监听直接发往主机的数据包:发往、源自或通过主机路由的数据包都将被 pcap 捕 获;混杂模式下,所有发送到物理链路上的数据包都将被捕获。在一个共享式的网络环境中,这将导致整个网络的数据流被监听。混合监听模式是可以被检测的:可 以通过测试强可靠性来发现网络中是否有主机正在以混合模式监听,另外混杂工作模式仅仅在非交换式的网络中有效,而且在一个高负载的网络环境中,混杂模式将 消耗大量的系统资源。

 

Filter traffic

通常我们只对特定网络通信感兴趣。比如我们只打算监听 Telnet 服务( port 23 )以捕获用户名和口令信息。获知对 FTP port 21 )或 DNS UDP port 53 )数据流感兴趣。可以通过 pcap_compile() pcap_setfilter 来设置数据流过滤规则( filter

函数原型:

       int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)

p :表示 pcap 会话句柄;

fp :存放编译以后的规则;

str :规则表达式格式的过滤规则( filter ),同 tcpdump 中的 filter

optimize :制定优化选项: 0 false, 1 true

netmask :监听接口的网络掩码;

返回值: -1 表示操作失败,其他值表成功。

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

              p :表示 pcap 的会话句柄;

              fp :表示经过编译后的过滤规则;

              返回值: -1 表示操作失败,其他值表成功。

 

示例代码:

#include <pcap.h>
    ...
    pcap_t *handle;                           /* Session handle */
    char dev[] = "rl0";                        /* Device to sniff on */
    char errbuf[PCAP_ERRBUF_SIZE];    /* Error string */
    struct bpf_program filter;               /* The compiled filter expression */
    char filter_app[] = "port 23";          /* The filter expression */
    bpf_u_int32 mask;                        /* The netmask of our sniffing device */
    bpf_u_int32 net;                           /* The IP of our sniffing device */
    pcap_lookupnet(dev, &net, &mask, errbuf);
    handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
    pcap_compile(handle, &filter, filter_app, 0, net);
    pcap_setfilter(handle, &filter);
      

上面的代码设备 rl0 上以混杂模式监听所有发往或源自端口 23 的数据包。 Pcap_lookupnet() 函数返回给定接口的 IP 地址和子网掩码。

 

The actual sniffing

现在我们开始准备捕获数据包:有两种方法可以用来捕获数据包。要么一次捕获一个满足条件的数据包,要么进入一个循环过程捕获指定数量数据包然后退出。首先来了解使用 pcap_next() 一次捕获单一数据包。

函数原型:

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

       p pcap 会话句柄;

       h :指向 pcap_pkthdr 接口的指针,在此结构中保存了所捕获的数据包的通用信息。包括:时间信息、数据包的长度和包头部分的长度(结构定义在后面定义)。

       返回值:返回指向实际捕获的数据包的 u_char * 型指针。

代码示例:

#include <pcap.h>
    #include <stdio.h>
    int main()
    {
        pcap_t *handle;                        /* Session handle */
        char *dev;                                /* The device to sniff on */
        char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
        struct bpf_program filter;            /* The compiled filter */
        char filter_app[] = "port 23";       /* The filter expression */
        bpf_u_int32 mask;                     /* Our netmask */
        bpf_u_int32 net;                        /* Our IP */
        struct pcap_pkthdr header;          /* The header that pcap gives us */
        const u_char *packet;                 /* The actual packet */
        /* Define the device */
        dev = pcap_lookupdev(errbuf);
        /* Find the properties for the device */
        pcap_lookupnet(dev, &net, &mask, errbuf);
        /* Open the session in promiscuous mode */
        handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
        /* Compile and apply the filter */
        pcap_compile(handle, &filter, filter_app, 0, net);
        pcap_setfilter(handle, &filter);
        /* Grab a packet */
        packet = pcap_next(handle, &header);
        /* Print its length */
        printf("Jacked a packet with length of [%d]/n", header.len);
        /* And close the session */
        pcap_close(handle);
        return(0);
    }

上面的代码将所有从 pcap_lookupdev() 返回的接口置于混杂模式监听状态。 Pcap 捕获端口 23 的一个数据包并打印该包的长度。然后调用 pcap_close() 关闭 pcap 会话。

 

当然我们可以使用更复杂和更强大的功能 pcap_loop pcap_dispatch 。通常很少有 sniffer 使用 pcap_next ,他们更通常的使用 pcap_loop pcap_dispatch 。为便于理解这两个函数,需要现了解回调函数的概念。

 

回调函数并不是一个新概念,在很多的 API 中都使用了回调函数的概念。可以通过 pcap_loop pcap_dispatch 定义用户自己的回调函数。事实上 pcap_loop pcap_dispatch 的功能非常相似,当 pcap 捕获的满足规则的数据包时,着两个函数将调用我们自己定义的回调函数执行我们自己的处理。

 

函数原型:

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

p pcap 会话句柄;

cnt :定义 sniff 捕获的数据包的数目;

callback :自定义的回调函数句柄;

user :传递给回调函数的参数,如没有参数可以设为 NULL

函数 pcap_dispatch pcap_loop 的用法几乎相同,两者之间的唯一的差别是处理超时的方式不同(在 pcap_open_live() 中设置的超时参数将在这里起作用: pcap_loop 将忽略超时参数而 pcap_dispatch 在制定时间到时将产生读超时的错误)。查阅 pcap 的帮助获得更多信息。

 

回调函数的原型:

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

       args :对应于 pcap_loop 中的最后一个参数;

       header :指向 pcap 数据包包头的指针;

       packet :指向 pcap 捕获到的数据包的指针, packet 指针指向的字符串包含了整个数据包;

       返回值:回调函数不能返回任何值。

定义回调函数时,需要严格遵守原型定义,否则 pcap_loop 将不能正确调用回调函数。

       pcap_pkthdr 结构的定义如下:

       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) */

};

 

       怎样使用(处理) packet 指针变量呢?一个 packet 指针所指的结构包含了很多属性,它并不是一个真正的字符串,而是多个结构组成的集合(比如:一个 TCP/IP 数据包包括以太网头、 IP 包头、 TCP 头和数据包中有效的数据负载)。首先需要定义这些结构:

/* Ethernet header */

 

...

怎样使用(处理)packet指针变量呢?一个packet指针所指的结构包含了很多属性,它并不是一个真正的字符串,而是多个结构组成的集合(比如:一个TCP/IP数据包包括以太网头、IP包头、TCP头和数据包中有效的数据负载)。首先需要定义这些结构:

/* Ethernet header */
struct sniff_ethernet {
  u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
  u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
  u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP header */
struct sniff_ip {
  #if BYTE_ORDER == LITTLE_ENDIAN
  u_int ip_hl:4, /* header length */
  ip_v:4; /* version */
  #if BYTE_ORDER == BIG_ENDIAN
  u_int ip_v:4, /* version */
  ip_hl:4; /* header length */
  #endif
  #endif /* not _IP_VHL */
  u_char ip_tos; /* type of service */
  u_short ip_len; /* total length */
  u_short ip_id; /* identification */
  u_short ip_off; /* fragment offset field */
  #define IP_RF 0x8000 /* reserved fragment flag */
  #define IP_DF 0x4000 /* dont fragment flag */
  #define IP_MF 0x2000 /* more fragments flag */
  #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
  u_char ip_ttl; /* time to live */
  u_char ip_p; /* protocol */
  u_short ip_sum; /* checksum */
  struct in_addr ip_src,ip_dst; /* source and dest address */
};
/* TCP header */
struct sniff_tcp {
  u_short th_sport; /* source port */
  u_short th_dport; /* destination port */
  tcp_seq th_seq; /* sequence number */
  tcp_seq th_ack; /* acknowledgement number */
  #if BYTE_ORDER == LITTLE_ENDIAN
  u_int th_x2:4, /* (unused) */
  th_off:4; /* data offset */
  #endif
  #if BYTE_ORDER == BIG_ENDIAN
  u_int th_off:4, /* data offset */
  th_x2:4; /* (unused) */
  #endif
  u_char th_flags;
  #define TH_FIN 0x01
  #define TH_SYN 0x02
  #define TH_RST 0x04
  #define TH_PUSH 0x08
  #define TH_ACK 0x10
  #define TH_URG 0x20
  #define TH_ECE 0x40
  #define TH_CWR 0x80
  #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
  u_short th_win; /* window */
  u_short th_sum; /* checksum */
  u_short th_urp; /* urgent pointer */
};

注:这些结构定义在不同的系统实现中可能存在差异,请查阅相关文档。

另:搞不懂作者什么意思,为什么非要自己定义这些结构,干嘛不用系统自己定义的实现呢?



省略了原作者关于定义这些结构的描述…



假设我们通过以太网处理TCP/IP数据包(其他的物理网络类似),如下代码将packet指针所指的结构分解为不同的结构体:

const struct sniff_ethernet *ethernet; /* The ethernet header */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */
/* For readability, we'll make variables for the sizes of each of the structures */
int size_ethernet = sizeof(struct sniff_ethernet);
int size_ip = sizeof(struct sniff_ip);
int size_tcp = sizeof(struct sniff_tcp);

And now we do our magical typecasting:
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + size_ethernet);
tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);

如果packet值(该指针变量所指的地址)为X,则上面所述的结构在内存中的布局如下所示:

Variable
Location (in bytes)

sniff_ethernet
X

sniff_ip
X + 14

sniff_tcp
X + 14 + 20

payload
X + 14 + 20 + 20




Wrapping up

到 此为止,我们已经可以用pcap编写一个sniffer应用程序了。我们已经了解了pcap编程的基础知识,包括打开一个pcap会话句柄,处理pcap 会话句柄的属性,监听数据包,应用过滤规则,并使用回调函数定义我们自己的处理过程。随原文提供的示例程序:sniffer.c



This document is Copyright 2002 Tim Carstens. All rights reserved. Redistribution and use, with or without modification, are permitted provided that the following conditions are met: 1. Redistribution must retain the above copyright notice and this list of conditions. 2. The name of Tim Carstens may not be used to endorse or promote products derived from this document without specific prior written permission.
/* Insert 'wh00t' for the BSD license here */



附录:例用pcap编写的示例程序

/*

    编译:gcc –Wall –o testpcap testpcap.c -lpcap

*/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <pcap.h>

#include <sys/types.h>

#include <netinet/ip.h>

#include <netinet/ether.h>

#include <net/ethernet.h>

#include <netinet/udp.h>

#include <netinet/tcp.h>

#include <netinet/in.h>

#include <arpa/inet.h>



/* MACRO to print debug info */

//#define DEBUG 1

#ifdef DEBUG 

#define debug(stderr, msg) fprintf(stderr, msg)

#define _ ,

#else /* if no define DEBUG */

#define debug(stderr, msg)

#endif /* end of BEBUG */





#define LOOKUPDEV_ERR -1

#define OPEN_LIVE_ERR -2

#define COMPILE_ERR  -3



/* protocol ID's */

#define IPPRO 8 /* IP protocol */



/* call back function invoke by pcap_loop, major process for ourselves */

void

got_packet(u_char *args, const struct pcap_pkthdr *header,

     const u_char *packet);

/* handle ethernet header */

u_int16_t

handle_ethernet(u_char *args,const struct pcap_pkthdr* pkthdr,

     const u_char* packet);

/* handle IP header */

void

handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);

    

   



int

main(int argc, char *argv[])

{

   char *dev = NULL; /* device to sniff on */

   char errbuf[PCAP_ERRBUF_SIZE]; /* buffer to store error msg */

   pcap_t *handle = NULL; /* pcap session handle */



   struct bpf_program filter; //compiled filter expression

   char filter_app[] = "port 23"; /* filter ruler for sniffing */

   bpf_u_int32 mask; //netmask of our sniffing device

   bpf_u_int32 net; //the ip of our sniffing device

  

   int num = 0; /* number of packets captured */



   /* variables for getopt */

   long total = -1; /* total packets to sniff */

   char *flter = filter_app; /* filter ruler for sniffing */

   int c; /* temprory char variable */



   while ((c = getopt(argc, argv, "n:f:")) != -1) {

     switch(c) {

       case 'n':

          total = atoi(optarg);

          break;



       case 'f':

          flter = optarg;

          break;

    

       case '?':

          fprintf(stderr, "Usage: %s -n <num> -f <filter string>/n", argv[0]);

          exit(1);

       default:

          fprintf(stdout, "Using fitler: port 23 and sniffing util interrupt by console!/n");

     }

   }



   if (NULL == (dev = pcap_lookupdev(errbuf))) {

     fprintf(stderr, "pcap_lookupdev() error: %s/n", errbuf);

     exit(LOOKUPDEV_ERR);

   }

   fprintf(stdout, "Sniffing on device: %s/n/n", dev);



   pcap_lookupnet(dev, &net, &mask, errbuf);



   /* open a new pcap session */

   if (NULL == (handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf))) {

     fprintf(stderr, "pcap_open_live() error: %s/n", errbuf);

     exit(OPEN_LIVE_ERR);

   }

   /* compile capture rule */

   if (-1 == pcap_compile(handle, &filter, flter, 1, net)) {

     fprintf(stderr, "pcap_compile() error!/n");

     exit(COMPILE_ERR);

   }

   pcap_setfilter(handle, &filter);



/* using while + pcap_next instead of pcap_loop or pcap_dispatch */

/*  while (1) {

     debug(stderr, "in pcap_next while/n");

     packet = pcap_next(handle, &header);

     printf("Captured a packet with lengthen of [%d]/n", header.len);

     debug(stderr, "The packet captured: %s/n" _ packet + header.caplen);

   }

*/



   num = pcap_loop(handle, total, got_packet, NULL);

   if (-1 == num) {

     pcap_perror(handle, "pcap_loop error: ");

   }  

   if (-2 == num) {

     pcap_perror(handle, "pcap_loop break by pcap_breakloop: ");

   }



   pcap_close(handle);



   return 0;

}



void

got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)

{

  u_int16_t type;



   type = handle_ethernet(args, header, packet);



   debug(stderr, "protocol type: %i/n" _ type);



  switch(type) {

     case IPPRO:

       debug(stderr, "protocol type: IP/n");

       handle_IP(args, header, packet);

       break;



     case ETHERTYPE_ARP:

       /* handle arp protocol */ 

       break;

     case ETHERTYPE_REVARP:

       /* handle rarp protocol */ 

       break;

     default:

       fprintf(stdout, "Protocol is ignored/n");

   }



     fprintf(stdout,"/n");

   return;

} //end of got_packet



u_int16_t

handle_ethernet(u_char *args,const struct pcap_pkthdr* pkthdr,

     const u_char* packet)

{

   struct ether_header *eptr; /* net/ethernet.h */



  /* lets start with the ether header... */

  eptr = (struct ether_header *) packet;



  fprintf(stdout,"ETH: %s --> "

      ,ether_ntoa((struct ether_addr *)(eptr->ether_shost)));

  fprintf(stdout,"%s "

      ,ether_ntoa((struct ether_addr *)(eptr->ether_dhost)));



  /* check to see if we have an ip packet */

  if (ntohs (eptr->ether_type) == ETHERTYPE_IP)

  {

    fprintf(stdout,"(IP)");

  }else if (ntohs (eptr->ether_type) == ETHERTYPE_ARP)

  {

    fprintf(stdout,"(ARP)");

  }else if (ntohs (eptr->ether_type) == ETHERTYPE_REVARP)

  {

    fprintf(stdout,"(RARP)");

  }else {

    fprintf(stdout,"(?)");

    exit(1);

  }



  return eptr->ether_type;

} //end of handle_ethernet



void

handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet)

{

   const struct iphdr *ip = (const struct iphdr *)(packet + sizeof(struct ether_header));



   u_int length = pkthdr->len;

   u_int hlen,off,version;

   int len;

   struct in_addr in;



   debug(stderr, "total pcap pkt length: %i/n" _ length);

   debug(stderr, "total pcap pkt header length: %i/n" _ pkthdr->caplen);



  /* jump pass the ethernet header */

  length =- sizeof(struct ether_header);



  /* check to see we have a packet of valid length */

  if (length < sizeof(struct iphdr))

  {

    printf("truncated ip %d",length);

    return;

  }



  len   = ntohs(ip->tot_len);

   debug(stderr, "total ip pkt length: %i/n" _ len);

  hlen  = ip->ihl; /* header length */

   debug(stderr, "ip header length: %i/n" _ hlen);

  version = ip->version;/* ip version */

   debug(stderr, "ip version: %i/n" _ version);



  /* check version */

  if(version != 4)

  {

   fprintf(stdout,"Unknown version %d/n",version);

   return ;

  }



  /* check header length */

  if(hlen < 5 )

  {

    fprintf(stdout,"bad-hlen %d /n",hlen);

  }



  /* see if we have as much packet as we should */

  if(length < len)

    printf("/ntruncated IP - %d bytes missing/n",len - length);



  /* Check to see if we have the first fragment */

  off = ntohs(ip->frag_off);

  if((off & 0x1fff) == 0 )/* aka no 1's in first 13 bits */

  {/* print SOURCE DESTINATION hlen version len offset */

    fprintf(stdout,"/nIP: ");

   in.s_addr = ip->saddr;

    fprintf(stdout,"%s ",inet_ntoa(in));

   in.s_addr = ip->daddr;

    fprintf(stdout,"%s %d %d %d %d/n",

        inet_ntoa(in),

        hlen,version,len,off);

  }
  return;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用pcap编写程序,可以通过设置过滤规则来根据IP地址和IP协议来过滤捕获的网络数据包。具体实现可以通过调用pcap库中的函数来设置过滤规则,例如使用pcap_compile函数编译过滤规则,使用pcap_setfilter函数设置过滤规则。通过这些函数的调用,可以实现根据IP地址和IP协议过滤捕获的网络数据包。 ### 回答2: pcap是一种在Unix和Linux等操作系统上捕获、过滤和分析网路数据包的库,它能够通过API来调用网路数据包捕获的底层实现。使用pcap编写程序可以实现捕获所有流过本机网卡的数据包,并对其进行过滤和解析。 在使用pcap编写程序进行IP地址和协议过滤时,需要注意以下几点: 1. 在程序中需要调用pcap库的相关函数来捕获数据包并进行过滤。一般来说,程序需要使用pcap_open_live函数来开始捕获数据包,并使用pcap_compile函数来编译BPF(Berkeley Packet Filter)过滤器。 2. IP地址过滤可以通过BPF语言中的“host”关键字来实现。例如,要过滤源IP地址为192.168.1.1的数据包,可以使用类似于“host 192.168.1.1”的BPF过滤器。 3. IP协议过滤可以通过BPF语言中的“proto”关键字来实现。例如,要过滤ICMP协议的数据包,可以使用类似于“proto icmp”的BPF过滤器。 4. 在编写程序时,需要注意BPF过滤器的语法。特别是在使用复杂过滤条件时,可能需要使用括号进行优先级控制。 5. 编写程序时需要注意数据包的处理效率。对于高速网络,如果处理效率不够高,可能会丢包或者影响正常的应用程序。 综上所述,使用pcap编写程序并实现IP地址和协议过滤是一项比较复杂的任务。需要对pcap库和BPF过滤器有深入的了解,并且需要考虑程序效率、可扩展性等多个因素。但是,一旦完成这项任务,就可以方便地捕获网络数据包并对其进行分析和监控,为网络安全和性能优化等方面提供很大的帮助。 ### 回答3: pcap库是一个用于捕获和处理网络数据包的库,在数据包的处理过程中,经常需要按照特定的规则对数据包进行过滤,这时候就需要使用pcap库提供的过滤功能。通过设置不同的过滤规则,可以只捕获和处理特定的数据包,避免无效数据的干扰,提高程序效率。 要根据ip地址和ip协议过滤捕获的网络数据包,需要先了解ip地址和ip协议的基本概念和格式。IP地址是网络上的唯一标识符,用于标识网络中不同主机的位置。IP协议是用于在网络上传输数据包的协议,它提供了逐跳传输的机制和数据包路由的功能。 在pcap程序中,可以使用过滤表达式来实现数据包的过滤规则,过滤表达式有两种格式:BPF(Berkeley Packet Filter)和BPF+。BPF过滤表达式是一种机器指令格式,可以直接在内核态运行,因此效率较高。BPF+过滤表达式是一种更高级的格式,支持更多的数据包属性和操作符,比BPF过滤表达式更灵活。 对于IP地址和IP协议的过滤,可以使用BPF+格式的过滤表达式来实现。例如,要捕获源IP地址为192.168.1.100的TCP数据包,可以使用下面的过滤表达式: tcp src host 192.168.1.100 其中,tcp表示捕获TCP协议的数据包,src host指定了源IP地址为192.168.1.100。同理,如果要捕获目标IP地址为192.168.1.100的UDP数据包,可以使用如下过滤表达式: udp dst host 192.168.1.100 还可以根据IP协议的类型来进行过滤,例如,如果要捕获所有的ICMP数据包,可以使用如下过滤表达式: icmp 如果要同时过滤多个IP地址或协议类型,可以使用逻辑运算符AND和OR连接多个过滤条件,例如: tcp and src host 192.168.1.100 or icmp 这样就可以同时捕获源IP地址为192.168.1.100的TCP数据包和所有的ICMP数据包了。 通过使用pcap库提供的过滤功能,可以实现对捕获的网络数据包进行有效的过滤,提高程序效率和可用性。同时,还可以根据具体的需求,设置不同的过滤条件和逻辑运算符,实现更加灵活的过滤策略。因此,掌握pcap过滤的相关知识,对于进行网络安全监控和数据包分析等方面的工作都具有重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值