验证安装libpca

引用pcap.h,确认是否能正常用libpcap开发

#include

#include

int main(int argc, char *argv[])

{

char *dev = argv[1];

printf("Device: %s\n", dev);

return(0);

}

然后用-lpcap选项看能不能连接上

g++ -o a a.cc a.h -lpcap

用pcap_lookupdev获取默认设备

int main(int argc, char *argv[])

{

char *dev, errbuf[PCAP_ERRBUF_SIZE];

dev = pcap_lookupdev(errbuf);

if (dev == NULL) {

fprintf(stderr, "%s\n", errbuf);

return(2);

}

printf("Device: %s\n", dev);

return(0);

}

打开设备,返回一个pcap_t类型的“handle”。调用这个函数叫做“开启一个嗅探会话(sniffing session)”

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

参数

device:pcap_lookupdev返回的设备名,类似ethx的字符串

snaplen:抓包的buffer字节数

promisc:是否开启混杂模式

to_ms:超时,0表述无超时

ebuf:如果出错,会存入错误信息

errbuf[PCAP_ERRBUF_SIZE];

char*   dev    = pcap_lookupdev(errbuf);

pcap_t* handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);

if (handle == NULL) {

fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);

return(2);

}

过滤器

先要调用pcap_compile来编译过滤器

int pcap_compile(

pcap_t *p,  由pcap_open_live返回的“handle”

struct bpf_program *fp,   存放编译好的过滤器的数据结构

char *str,    过滤器表达式,如“port 23”

int optimize,  表达式是否需要被“优化”, 取值0或1

bpf_u_int32 netmask  有pcap_lookupnet函数获取的netmask

)

过滤器编译好之后就可以设置到pcap_t里

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

pcap_t *handle;    /* Session handle */

char dev[] = "rl0";    /* Device to sniff on */

char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */

struct bpf_program fp;   /* The compiled filter expression */

char filter_exp[] = "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 */

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {

fprintf(stderr, "Can't get netmask for device %s\n", dev);

net = 0;

mask = 0;

}

handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);

if (handle == NULL) {

fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);

return(2);

}

if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {

fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));

return(2);

}

if (pcap_setfilter(handle, &fp) == -1) {

fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));

return(2);

}

实际抓包

struct pcap_pkthdr header;  /* The header that pcap gives us */

handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);

packet = pcap_next(handle, &header);

pcap_close(handle);

调用上面的pcap_next函数可以得到header和packet

但是pcap_next只能抓一个包,libpcap提供了循环抓包的函数

int pcap_loop(

pcap_t *p, //由pcap_open_live返回的“handle”

int cnt,   //抓多少个包

pcap_handler callback, //抓到包后的回调函数

u_char *user   // 这个参数是用户自定义的,libpcap会自动将这个值传给回调函数

)

回调函数的形式

重点是header和packet两个参数

void got_packet(

u_char *args,  // 这个就是pcap_loop的最后一个参数u_char *user

const struct pcap_pkthdr *header,

const u_char *packet

);

header的内容

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是一个u_char*,执行一块数据,这块数据可以被转换为下面几个结构体

#define SIZE_ETHERNET 14

const struct sniff_ethernet /* The ethernet header */

const struct sniff_ip /* The IP header */

const struct sniff_tcp /* The TCP header */

以及

const char *payload; /* Packet payload */

这些数据结构介绍如下

packet其实就是包头的二进制内容,因此结构体需要按包头的格式编排各变量的大小和顺序,然后强制类型转换即可。

下面的结构体来自tcpdump官网的文章。

/* Ethernet addresses are 6 bytes */

#define ETHER_ADDR_LEN  6

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

u_char ip_vhl;    /* version << 4 | header length >> 2 */

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

};

#define IP_HL(ip)   (((ip)->ip_vhl) & 0x0f)

#define IP_V(ip)    (((ip)->ip_vhl) >> 4)

/* TCP header */

typedef u_int tcp_seq;

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

u_char th_offx2;  /* data offset, rsvd */

#define TH_OFF(th)  (((th)->th_offx2 & 0xf0) >> 4)

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

};

将u_char *packet转换成这些结构体的方法:

#define SIZE_ETHERNET 14

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

u_int size_ip;

u_int size_tcp;

ethernet = (struct sniff_ethernet*)(packet);     // 先是链路层的头

ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);  // 链路层头的后面是变长的ip包头

size_ip = IP_HL(ip)*4;   // IP_HL这个宏的作用是去ip头第一个字节的后4位,这4位表示IP头的长度,单位是4byte

if (size_ip < 20) {

printf("   * Invalid IP header length: %u bytes\n", size_ip);

return;

}

tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip); // 再后面是tcp包头

size_tcp = TH_OFF(tcp)*4;

if (size_tcp < 20) {

printf("   * Invalid TCP header length: %u bytes\n", size_tcp);

return;

}

payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);

Variable

Location (in bytes)

sniff_ethernet

X

sniff_ip

X + SIZE_ETHERNET

sniff_tcp

X + SIZE_ETHERNET + {IP header length}

payload

X + SIZE_ETHERNET + {IP header length} + {TCP header length}

把前面介绍的组合起来,jius简单的抓包程序

[root@localhost cc]# cat cap.c

#include "a.h"

#define SIZE_ETHERNET 14

char errbuf[PCAP_ERRBUF_SIZE];

int addr_ntoa(int i_addr, char* str_addr)

{

if(str_addr == NULL)

{

return -1;

}

struct in_addr addr;

addr.s_addr = i_addr;

char* tmp = inet_ntoa(addr);

strcpy(str_addr, tmp);

return 0;

}

int errstr(int ret, char* str)

{

if(str != NULL) fprintf(stderr, "%s\n", str);

exit(ret);

}

int err(int ret)

{

errstr(ret, errbuf);

}

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

{

u_int size_ip;

u_int size_tcp;

const struct sniff_ethernet *ethernet;

const struct sniff_ip *ip;

const struct sniff_tcp *tcp;

const char *payload;

ethernet = (struct sniff_ethernet*)(packet);

ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);

if((size_ip = IP_HL(ip)*4) < 20) return;

tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);

if((size_tcp = TH_OFF(tcp)*4) < 20) return;

char ip_src[16];

char ip_dst[16];

addr_ntoa(ip->ip_src.s_addr, ip_src);

addr_ntoa(ip->ip_dst.s_addr, ip_dst);

printf("%s:%d>> %s:%d\n", ip_src, ntohs(tcp->th_sport), ip_dst, ntohs(tcp->th_dport));

struct ether_header *eptr = (struct ether_header *) packet;

u_int8_t* ptr;

ptr = eptr->ether_shost;

int i = ETHER_ADDR_LEN;

printf("src MAC: [");

do

{

printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);

}

while(--i>0);

printf("]\n");

ptr = eptr->ether_dhost;

i = ETHER_ADDR_LEN;

printf("dest MAC: [");

do

{

printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);

}

while(--i>0);

printf("]\n");

struct tm* local = localtime(&(header->ts));

printf("time: %d:%d:%d\n",local->tm_hour,local->tm_min,local->tm_sec);

printf("package length: %d\n", header->len);

printf("TTL: %d\n", ip->ip_ttl);

printf("\n");

}

int main()

{

char* dev;

bpf_u_int32 mask;

bpf_u_int32 net;

pcap_t *handle;

if ((dev = pcap_lookupdev(errbuf)) == NULL) err(-1);

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) err(-1);

char strnet[16];

char strmask[16];

addr_ntoa(net, strnet);

addr_ntoa(mask, strmask);

printf("\nMonitoring: %s/%s/%s\n\n", dev, strnet, strmask);

if((handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf)) == NULL) err(-1);

if((pcap_loop(handle, 10, got_packet, "test1")) < 0) err(-1);

return 0;

}

头文件里的结构体来自tcpdump官网的文章,其实就是按照包头的格式设置变量大小和顺序。

[root@localhost cc]# cat cap.h

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/* Ethernet addresses are 6 bytes */

//#define ETHER_ADDR_LEN  6

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

u_char ip_vhl;    /* version << 4 | header length >> 2 */

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

};

#define IP_HL(ip)   (((ip)->ip_vhl) & 0x0f)

#define IP_V(ip)    (((ip)->ip_vhl) >> 4)

/* TCP header */

typedef u_int tcp_seq;

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

u_char th_offx2;  /* data offset, rsvd */

#define TH_OFF(th)  (((th)->th_offx2 & 0xf0) >> 4)

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

};

执行效果

[root@localhost cc]# ./cap

Monitoring: enp0s8/192.168.1.0/255.255.255.0

192.168.1.66:22>> 192.168.1.1:52672

src MAC: [ 8:0:27:80:ff:b0]

dest MAC: [ a:0:27:0:0:0]

time: 15:25:4

package length: 154

TTL: 64

192.168.1.1:52672>> 192.168.1.66:22

src MAC: [ a:0:27:0:0:0]

dest MAC: [ 8:0:27:80:ff:b0]

time: 15:25:4

package length: 60

TTL: 64

192.168.1.66:22>> 192.168.1.1:52672

src MAC: [ 8:0:27:80:ff:b0]

dest MAC: [ a:0:27:0:0:0]

time: 15:25:4

package length: 378

TTL: 64

192.168.1.66:22>> 192.168.1.1:52672

src MAC: [ 8:0:27:80:ff:b0]

dest MAC: [ a:0:27:0:0:0]

time: 15:25:4

package length: 234

TTL: 64

...

...

linux c实现函数回调,c语言实现linux抓包相关推荐

  1. Go语言用GoPacket抓包分析

    前言 离线pcap包解析 离线数据包分析源码分析 新建packetSource 读取数据包 注意点 前言 最近有了一个抓取网络数据包来分析的需求,最近在使用go语言,于是乎,决定是用go语言来进行抓包 ...

  2. linux i2c detect函数,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  3. go移植linux内核书名叫啥,Go语言移植Linux内核数据结构hlist

    hlist(哈希链表)可以通过相应的Hash算法,迅速找到相关的链表Head及节点. 在有些应用场景,比Go标准库提供的list(一种双向链表)更合适. 依照list.h中的源码,我实现了一个Go语言 ...

  4. linux调用python函数,python脚本里执行linux命令的时候如何调用python的函数?

    本菜鸟有一个可以获取ip地址的脚本,如下: def get_local_ip(ifname = 'eth1'): import socket, fcntl, struct s = socket.soc ...

  5. linux 查看系统函数库,教你在Linux操作系统中如何创建函数库

    函数库分为静态库和动态库两种.静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库.动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存 ...

  6. linux添加音乐的代码,C语言实现linux系统下的MP3播放器源代码

    [实例简介] 能识别本地的MP3歌曲文件,能根据路径添加入播放器中. 能识别本地的播放列表信息. 具有播放列表功能,能根据用户的需求随意创建.删除播放列表. 用户能往指定的播放列表中添加.删除 ...

  7. c语言无线网络抓包程序,c语言实现抓包

    1.程序代码 #include #include #include #include #include #define PCAP_ERRBUF_SIZE 1024 #define pcap_t voi ...

  8. vss2010c语言怎么运行,在Linux下使用gcc运行C语言程序

    Linux下使用最广泛的C/C++编译器是GCC,大多数的Linux发行版本都默认安装,不管是开发人员还是初学者,一般都将GCC作为Linux下首选的编译工具.本教程毫不犹豫地使用GCC来编译C程序. ...

  9. linux 纪元时间转换,[转]Linux下时间函数time gettimeofday

    Linux下时间函数time & gettimeofday UNIX及Linux的时间系统是由「新纪元时间」Epoch开始计算起,单位为秒.Epoch是指定为1970年1月1日凌晨零点零分零秒 ...

最新文章

  1. 逗比讲算法:什么是冒泡排序?
  2. memcached的最佳实践方案(转)
  3. mac 推荐一款本机截屏找latex公式软件Mathpix 亲测有效
  4. GBDT分类和回归例子
  5. 【Python3网络爬虫开发实战】1.7.2-mitmproxy的安装
  6. 考上985能改变命运吗_2021艺考生:文化课成绩一般,有机会考上985、211吗?
  7. 一些很好的工具软件~
  8. HDOJ 2013 蟠桃记
  9. html倒计时代码执行操作,JS倒计时两种实现方式代码实例
  10. 华为交换机冗余链路(VRRP)和vlan负载均衡
  11. 前端H5面试题Js: Javascript 创建对象的几种方式? (必会)
  12. 干货|读完这篇,再也不担心基金从业考试!
  13. 关于灵魂,意识,自我和死亡
  14. echart各种显示数据的格式化
  15. 英飞凌XC2000系列——汽车ECU Bootloader 设计详解
  16. 天津工业大学计算机学院调剂信息,天津工业大学2019年硕士研究生调剂公告
  17. requests登录知乎新版
  18. 熤星传媒:抖音这些方面要注意!
  19. 手算梯度下降法,详解神经网络迭代训练过程
  20. select2中文帮助文档_5款实用办公app , 石墨文档、收趣 | 发现有趣app

热门文章

  1. 基于点云强度的3D激光雷达与相机的外参标定
  2. Udacity机器人软件工程师课程笔记(八)-ROS Turtlesim 包的相关命令
  3. 在CentOS 6.6 64bit上为vim 7.4安装并配置vim-airline插件
  4. 数据结构 -- 图与图存储
  5. Mybatis学习记录-使用问题总结之一DISTINCT
  6. keras系列︱图像多分类训练与利用bottleneck features进行微调(三)
  7. 浏览器加载、解析、渲染的过程
  8. python -socket -client
  9. MySQL 的“root”用户修改密码
  10. Json.Net学习笔记