libpcap steps (2) 简单的抓包实现。

用libpcap抓包的简单流程:
1. 获得设备名:pcap_findalldevs列出所有设备——选择列出的网络设备,在链表中找到。得到设备名以后把链表释放。
或者直接存在字符串里
2. pcap_open_live用设备名打开设备(另一个函数是pcap_open_dead,略猎奇)

3. pcap_loop开始抓包。这里没有使用混杂模式。(第二个参数是0)

4. 利用回调函数处理捕获的数据包。这里直接写在pcap_loop里了。传进来的数据不需要free掉。

note:

其实在linux下 最合适的语言是C。不过libpcap与c++的相性也还不错,没有出现任何坑爹的情况。

本来这个程序是纯C的(所以有scanf/printf),为了精简,使用了lambda函数,也就改成了c++。

revision 3:尝试使用getopt_long处理命令行参数。原来用"%02x"就可以正确输出hex了,而且前面补0.原来一直都没想到怎么在前面补0,输出都是自己写的超复杂函数的说大哭= =。去掉了显示设备名称,改成用-i或者 --iface指定。所以仍然压缩到了50行。

#include "iostream"
#include "ctime"
#include "cstdlib"
#include "cstring"

#include "getopt.h"

#ifndef __USE_BSD
	#define __USE_BSD
#endif
#include <sys/types.h>		//u_short types
#include "pcap.h"

#define str_(x) x		//这样可以解决c++处理format的问题
#define Debug(format, ...) 	fprintf(stderr, "%s:%d: " str_(format) "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define assert_(expr_, extra_op)	do { if (!(expr_)) { Debug("在函数 `%s'中: 断言错误: " #expr_, __FUNCTION__);\
							extra_op;exit (-1);} } while (0)
#define pcap_try(func_) 	assert_(func_!=-1, Debug("Message: %s", errbuf))
#define pcap_ptr_try(func_)  assert_((func_) != 0, Debug("Message: %s", errbuf))

using namespace std;
static char buf[10000], errbuf[10000];
//options
option options[]={	//长选项
	{"iface", required_argument, 0, 'i'},
};
//global args
char *ifname;
int main(int argc, char **argv)
{
	char opt;
	while (-1!=( opt=getopt_long(argc, argv, "i:", options, 0) ))
	{
		switch(opt)
		{
			case 'i': ifname=optarg; break;
		}
	}
	pcap_t *hdev;
	pcap_ptr_try(hdev = pcap_open_live(ifname, 0xffff, 0, 1000, errbuf));
	Debug("Start capture on %s...", ifname);
	
	pcap_loop(hdev, 0, [buf](u_char *param, const struct pcap_pkthdr *header, const u_char *data){
		strftime(buf, sizeof(buf), "%H:%M:%S", localtime(&header->ts.tv_sec));		 
		Debug("%s,%.6d us, len:%d", buf, header->ts.tv_usec, header->len);
		for(int i=0; i<header->len; i++)
			printf("%02x%c", data[i], i<header->len-1?'-':'\n');
	}, 0);
	return 0;
}
运行:

$ sudo ./capture -i eth0
capture.cpp:41: Start capture on eth0...
capture.cpp:45: 14:30:31,550031 us, len:121
00-1e-c9-37-c8-10-a0-21-b7-ae-3e-fa-08-00-45-00-00-6b-05-8f-40-00-7b-06-49-ac-de-14-fc-cf-c0-a8-14-c5-0d-3d-a0-76-4a-89-f9-09-74-d1-8b-50-80-18-01-01-5c-22-00-00-01-01-08-0a-00-1f-1c-a0-00-15-f4-a0-80-37-86-6a-4c-a9-3a-b3-65-94-f1-92-a7-ed-0e-bd-0a-f6-4c-48-6d-75-8e-88-4f-7a-33-e4-99-1d-54-f1-ab-53-44-64-72-9c-8d-34-45-4b-53-04-69-f8-fd-9e-a7-74-f6-e1-ca-e9-ad
capture.cpp:45: 14:30:31,550702 us, len:66
a0-21-b7-ae-3e-fa-00-1e-c9-37-c8-10-08-00-45-00-00-34-34-bb-40-00-40-06-55-b7-c0-a8-14-c5-de-14-fc-cf-a0-76-0d-3d-74-d1-8b-50-4a-89-f9-40-80-10-39-f3-b0-78-00-00-01-01-08-0a-00-15-f4-b9-00-1f-1c-a0



revision 2:修改了宏定义里面的"#format"错误。见文章最后,老版本程序后面的note。

#include <cstdio>
#include <cstdlib>
#include <ctime>

#ifndef __USE_BSD
	#define __USE_BSD
#endif
#include <sys/types.h>		//u_short types
#include "pcap.h"

#define str_(x) x		//这样可以解决c++处理format的问题
#define Debug(format, ...) 	fprintf(stderr, "%s:%d: " str_(format) "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define assert_(expr_, extra_op)	do { if (!(expr_)) { Debug("在函数 `%s'中: 断言错误: " #expr_, __FUNCTION__);\
							extra_op;exit (-1);} } while (0)
#define pcap_try(func_) 	assert_(func_!=-1, Debug("Message: %s", errbuf))
#define pcap_ptr_try(func_)  assert_((func_) != 0, Debug("Message: %s", errbuf))
static char buf[10000], errbuf[10000];

int main()
{
	int i=0, devid;
	pcap_t *hdev;
	pcap_if_t *alldevs, *pdev;
	
	//get device list
	pcap_try(pcap_findalldevs(&alldevs, errbuf));
	for (pdev = alldevs; pdev; pdev=pdev->next)
		printf("#%d: %s %s\n", ++i, pdev->name, pdev->description? pdev->description:"");
	
	//select device
	printf("select a device: "); scanf("%d", &devid);
	pdev=alldevs; while (--devid) pdev=pdev->next;
	printf("Selected %s", pdev->name);
	
	//open device
	pcap_ptr_try(hdev = pcap_open_live(pdev->name, 0xffff, 0, 1000, errbuf));
	Debug("Start capture on %s...", pdev->name);

	//free device list
	pcap_freealldevs(alldevs);
	
	//start capture
	pcap_loop(hdev, 0, [buf](u_char *param, const struct pcap_pkthdr *header, const u_char *data){
			/* 将时间戳转变为易读的标准格式*/
			strftime(buf, sizeof(buf), "%H:%M:%S", localtime(&header->ts.tv_sec));		 
			Debug("%s,%.6d us, len:%d", buf, header->ts.tv_usec, header->len);
		}, 0);
	
	return 0;
}

运行:sudo运行

viktor@buxiang-OptiPlex-330:~/proj/pcap$ sudo ./capture 
#1: peth0 
#2: eth0 
#3: usbmon1 USB bus number 1
#4: usbmon2 USB bus number 2
#5: usbmon3 USB bus number 3
#6: usbmon4 USB bus number 4
#7: usbmon5 USB bus number 5
#8: any Pseudo-device that captures on all interfaces
#9: lo 
select a device: 2
capture_short.cpp:42: Start capture on �b...               //擦 内存
capture_short.cpp:46: 09:35:39,720743 us, len:79
capture_short.cpp:46: 09:35:39,721226 us, len:66
capture_short.cpp:46: 09:35:39,829933 us, len:79
capture_short.cpp:46: 09:35:39,830169 us, len:66
capture_short.cpp:46: 09:35:39,853439 us, len:60
capture_short.cpp:46: 09:35:39,939339 us, len:79


昨天的程序(rev1)的相关部分:

#define Debug(format, ...) 	fprintf(stderr, "%s:%d: " #format "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define assert_(expr_, extra_op)	do { if (!(expr_)) { Debug("在函数 `%s'中: 断言错误: " #expr_, __FUNCTION__);\
							extra_op;exit (-1);} } while (0)
#define pcap_assert(func_) 	assert_(func_!=-1, Debug("Message: %s", errbuf))
#define pcap_ptr_assert(func_)  assert_((func_) != 0, Debug("Message: %s", errbuf))

在宏定义的地方出了一个问题:

fprintf(stderr, "%s:%d: " #format "\n", __FILE__, __LINE__, ##__VA_ARGS__)

这里如果写成"%s:%d: "format"\n",C编译正常(我一开始的C代码里面就是这样写的),但是g++会报错:undefined string literal override (operator "" format)

似乎是触发了c++0x的特性“自定义的字面量后缀” 例如 356f, 356adk——这里好像把我准备连接的识别成了一个字面量【 "%s:%d:"format】

如果写成"%s:%d:"#format (也就是上面这样)那么输出时候会多出一堆引号,很难看

如果写成 ##format 他又会把"%s:%d:"format连接成一个标识符,仍然不对。

note:正确的修改办法是

#define str_(x) x
#define Debug(format, ...) 	fprintf(stderr, "%s:%d: " str_(format) "\n", __FILE__, __LINE__, ##__VA_ARGS__)

用一个宏把它套起来,避免在同一遍parse。


精简以后数了数,刚好50行。果然自己一天 只能写50行的有效代码啊……


note:以上代码的几个宏,应该叫做try而不是assert。

如果是assert的话,应该把"!=-1"这个判断条件写在调用里面,这样读程序显得更加清晰,像这样

pcap_assert(func != -1)

但是我的写法完全是为了代替以下的麻烦写法:

retvar = func(...);
if (retvar == -1)
{
  fprintf(stderr, "error message!\n");
  exit(-1)
}

我的写法:

pcap_try(func(...));

所以为了处理不同类型的函数的不同的出错值范围(例如这里用到了==-1和==null),我要定义不同类型的宏。完全是为了调用方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值