Libpcap三 重要API接口定义和数据结构

一、主要接口函数API: 


pcap_t *pcap_open_live(char *device, int snaplen,
   int promisc, int to_ms, char *ebuf)
   获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开
   的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定
   是否将网络接口置于混杂模式。to_ms参数指定超时时间(毫秒)。
   ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消
   息。
pcap_t *pcap_open_offline(char *fname, char *ebuf)
   打开以前保存捕获数据包的文件,用于读取。fname参数指定打开的文
   件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输
   入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传
   递错误消息。
pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
   打开用于保存捕获数据包的文件,用于写入。fname参数为"-"时表示
   标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或
   pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开
   的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消
   息。
 
char *pcap_lookupdev(char *errbuf)
   用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络
   设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的
   错误消息。
int pcap_lookupnet(char *device, bpf_u_int32 *netp,
   bpf_u_int32 *maskp, char *errbuf)
   获得指定网络设备的网络号和掩码。netp参数和maskp参数都是
   bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相
   关的错误消息。
int pcap_dispatch(pcap_t *p, int cnt,
   pcap_handler callback, u_char *user)
   捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。
   cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有
   数据包,直到产生以下错误之一:读取到EOF;超时读取。callback
   参数指定一个带有三个参数的回调函数,这三个参数为:一个从
   pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构
   的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的
   字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用
   pcap_perror()或pcap_geterr()函数获取错误消息。
int pcap_loop(pcap_t *p, int cnt,
   pcap_handler callback, u_char *user)
   功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包
   被处理或出现错误时才返回,但读取超时不会返回。而如果为
   pcap_open_live()函数指定了一个非零值的超时设置,然后调用
   pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。
   cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。
void pcap_dump(u_char *user, struct pcap_pkthdr *h,
   u_char *sp)
   向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可
   作为pcap_dispatch()函数的回调函数。
int pcap_compile(pcap_t *p, struct bpf_program *fp,
   char *str, int optimize, bpf_u_int32 netmask)
   将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结
   构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果
   代码的优化。netmask参数指定本地网络的网络掩码。
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
   指定一个过滤程序。fp参数是bpf_program结构指针,通常取自
   pcap_compile()函数调用。出错时返回-1;成功时返回0。
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
   返回指向下一个数据包的u_char指针。
int pcap_datalink(pcap_t *p)
   返回数据链路层类型,例如DLT_EN10MB。
int pcap_snapshot(pcap_t *p)
   返回pcap_open_live被调用后的snapshot参数值。
int pcap_is_swapped(pcap_t *p)
   返回当前系统主机字节与被打开文件的字节顺序是否不同。
int pcap_major_version(pcap_t *p)
   返回写入被打开文件所使用的pcap函数的主版本号。
int pcap_minor_version(pcap_t *p)
   返回写入被打开文件所使用的pcap函数的辅版本号。
int pcap_stats(pcap_t *p, struct pcap_stat *ps)
   向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始
   捕获数据以来至今共捕获到的数据包统计。如果出错或不支持
   数据包统计,则返回-1,且可调用pcap_perror()或
   pcap_geterr()函数来获取错误消息。
FILE *pcap_file(pcap_t *p)
   返回被打开文件的文件名。
int pcap_fileno(pcap_t *p)
   返回被打开文件的文件描述字号码。
void pcap_perror(pcap_t *p, char *prefix)
   在标准输出设备上显示最后一个pcap库错误消息。以prefix参
   数指定的字符串为消息头。
char *pcap_geterr(pcap_t *p)
   返回最后一个pcap库错误消息。
char *pcap_strerror(int error)
   如果strerror()函数不可用,则可调用pcap_strerror函数替代。
void pcap_close(pcap_t *p)
   关闭p参数相应的文件,并释放资源。


二、重要数据结构:

 

/*pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息*/
struct pcap_pkthdr {
   struct timeval ts;     /* 时间戳 */
   bpf_u_int32 caplen;     /* 已捕捉部分的长度 */
   bpf_u_int32 len;         /* 该包的脱机长度 */
};


/* 以太网帧头部 */
struct sniff_ethernet {
   u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
   u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
   u_short ether_type; /* IP:0x0800;IPV6:0x86DD; ARP:0x0806;RARP:0x8035 */
};

/* IP数据包的头部 */
struct sniff_ip {
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int ip_hl:4, /* 头部长度 */
   ip_v:4; /* 版本号 */
   #if BYTE_ORDER == BIG_ENDIAN
   u_int ip_v:4, /* 版本号 */
   ip_hl:4; /* 头部长度 */
   #endif
   #endif /* not _IP_VHL */
   u_char ip_tos; /* 服务的类型 */
   u_short ip_len; /* 总长度 */
   u_short ip_id; /*包标志号 */
   u_short ip_off; /* 碎片偏移 */
   #define IP_RF 0x8000 /* 保留的碎片标志 */
   #define IP_DF 0x4000 /* dont fragment flag */
   #define IP_MF 0x2000 /* 多碎片标志*/
   #define IP_OFFMASK 0x1fff /*分段位 */
   u_char ip_ttl; /* 数据包的生存时间 */
   u_char ip_p; /* 所使用的协议:1 ICMP;2 IGMP;4 IP;6 TCP;17 UDP;89 OSPF */
   u_short ip_sum; /* 校验和 */
   struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
};


/* TCP 数据包的头部 */
struct sniff_tcp {
   u_short th_sport; /* 源端口 */
   u_short th_dport; /* 目的端口 */
   tcp_seq th_seq; /* 包序号 */
   tcp_seq th_ack; /* 确认序号 */
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int th_x2:4, /* 还没有用到 */
   th_off:4; /* 数据偏移 */
   #endif
   #if BYTE_ORDER == BIG_ENDIAN
   u_int th_off:4, /* 数据偏移*/
   th_x2:4; /*还没有用到 */
   #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_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
   u_short th_win; /* TCP滑动窗口 */
   u_short th_sum; /* 头部校验和 */
   u_short th_urp; /* 紧急服务位 */
};


/* UDP header */
 struct sniff_udp
 {
  uint16_t sport;        /* source port */
  uint16_t dport;        /* destination port */
  uint16_t udp_length;
  uint16_t udp_sum;        /* checksum */
};

 

三、数据解析简介:


以u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)函数为例:
   返回指向下一个数据包的u_char指针。u_char型指针依次填入以太网协议、IP协议、TCP/UDP协议、负载数据。
   
   接下来,它简单的创建一个u_char字符串并且将这些结构体填入。那么我们怎样才能区分它们呢?预备好见证指针最实用的好处之一吧。
我们再一次假定要对以太网上的TCP/IP包进行处理。同样的手段可以应用于任何数据包,唯一的区别是你实际所使用的结构体的类型。让我们从声明分解u_char包的变量开始:
   const struct sniff_ethernet *ethernet; /* 以太网帧头部*/
   const struct sniff_ip *ip; /* IP包头部 */
   const struct sniff_tcp *tcp; /* TCP包头部 */
   const char *payload; /* 数据包的有效载荷*/
   /*为了让它的可读性好,我们计算每个结构体中的变量大小*/
   int size_ethernet = sizeof(struct sniff_ethernet);
   int size_ip = sizeof(struct sniff_ip);
   int size_tcp = sizeof(struct sniff_tcp);
   现在我们开始让人感到有些神秘的匹配:
   ethernet = (struct sniff_ethernet*)(packet);                //ethernet->ether_type 解析IPV4;IPV6
   ip = (struct sniff_ip*)(packet + size_ethernet);            //ip->ip_p 解析TCP UDP等
   tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
   payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);
  
此处如何工作?考虑u_char在内存中的层次。基本的,当pcap将这些结构体填入u_char的时候是将这些数据存入一个字符串中,那个字符串将被送入我们的回调函数中。反向转换是这样的,不考虑这些结构体制中的值,它们的大小将是一致的。例如在我的平台上,一个sniff_ethernet结构体的大小是14字节。一个sniff_ip结构体是20字节,一个sniff_tcp结构体也是20字节。 u_char指针正是包含了内存地址的一个变量,这也是指针的实质,它指向内存的一个区域。简单而言,我们说指针指向的地址为x,假如三个结构体恰好线性排列,第一个(sniff_ethernet)被装载到内存地址的x处则我们很轻易的发现其他结构体的地址,让我们以表格显示之:
   Variable Location (in bytes)
   sniff_ethernet X
   sniff_ip X + 14
   sniff_tcp X + 14 + 20
   payload X + 14 + 20 + 20
结构体sniff_ethernet正好在x处,紧接着它的sniff_ip则位于x加上它本身占用的空间(此例为14字节),依此类推可得全部地址。
注重:你没有假定你的变量也是同样大小是很重要的。你应该总是使用sizeof()来确保尺寸的正确。这是因为这些结构体中的每个成员在不同平台下可以有不同的尺寸。

如果对数据解析不理解的,不要着急可以参考下一篇文章。libcap基本示例演示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值