【笔记】wincap发送数据包

 

  尽管从 WinPcap 的名字上看,这个库的目标应该是数据捕捉(Packet Capture),然而,它也提供了针对很多其它有用的特性。在其中可以找到一组很完整的用于发送数据包的函数。

  请注意:原始的libpcap库是不支持发送数据包的,因此,这里展示的函数都属于是WinPcap的扩展,并且它们不能运行于Unix平台下

  一、使用pcap_sendpacket()发送单个数据包

  下面的代码展示了发送一个数据包的最简单的方式。打开适配器以后,调用pcap_sendpacket()来发送手工制作的数据包。pcap_sendpacket()的参数有一个要包涵发送数据的缓冲区,缓冲的长度,以及用来发送数据的适配器。

  注意,缓冲数据将直接发送到网络,而不会进行任何加工和处理。这就意味着应用程序需要创建一个正确的协议首部,来使这个数据包更有意义。

  程序源代码:

///////////////////////////////////////////////////////////////////////////// // Name: SendPackage.cpp // Purpose: wincap发送数据包。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") void main( int argc, char **argv ) { pcap_t *fp; char error[PCAP_ERRBUF_SIZE]; u_char packet[100]; int i; /* 检查命令行参数的合法性 */ if (argc != 2) { printf_s( "usage: %s inerface", argv[0] ); return; } /* 打开指定网卡 */ if ( ( fp = pcap_open_live( argv[1], 100, 1, 1000, error ) ) == NULL ) { fprintf_s( stderr, "/n适配器打开失败: %s/n", error ); return; } /* 假设网络环境为ethernet,我门把目的MAC设为1:1:1:1:1:1*/ packet[0]=1; packet[1]=1; packet[2]=1; packet[3]=1; packet[4]=1; packet[5]=1; /* 假设源MAC为 2:2:2:2:2:2 */ packet[6]=2; packet[7]=2; packet[8]=2; packet[9]=2; packet[10]=2; packet[11]=2; /* 填充发送包的剩余部分 */ for( i = 12; i < 100; i++ ) { packet[i]=i%256; } /* 发送包 */ pcap_sendpacket(fp, packet, 100); return; } 

  二、发送队列

 

  pcap_sendpacket() 提供了一种简单而直接的方法来发送单个数据包,而send queues则提供了一种高级的,强大的,结构更优的方法来发送一组数据包。发送队列是一个容器,它能容纳不同数量的数据包,这些数据包将被发送到网络上。队列有大小,它代表了它能存储的数据包的最大数量。

  发送队列通过调用pcap_sendqueue_alloc()函数创建,并且需要指定队列的大小。

  一旦发送队列被创建,pcap_sendqueue_queue()就可以将数据包添加到发送队列中。这个函数的参数包含一个 pcap_pkthdr 的结构体,它包含时间戳和长度,同时,参数还包含一个指向数据包数据的缓冲。这些参数和那些被 pcap_next_ex() 和 pcap_handler()接收到的数据相同,因此,为那些刚刚捕获到的,或是从文件读取出来的数据包排队,就相当于把三个参数传递给pcap_sendqueue_queue()。 

  WinPcap提供了pcap_sendqueue_transmit()函数来发送一个队列。请注意第三个参数:如果非零,那么发送过程将是同步进行,也就是说,只有时间戳相符的数据包才会被处理。这个操作需要消耗大量的CPU资源,因为同步操作由内核驱动中的"忙等 (busy wait)"循环来实现的。尽管这个操作对CPU的要求很高,但它对包传送的处理结果,通常是很精确的。(通常在数微秒左右,或更小)

  请注意,使用pcap_sendqueue_transmit()要比pcap_sendpacket()来发送一系列数据更加有效率,因为发送队列保存在内核级的缓冲区,因此,减少了上下文交换的次数。

  当队列不再需要时,我们可以使用pcap_sendqueue_destroy()来释放它所占用的内存。

  下一个程序将演示如何使用发送队列。先用pcap_open_offline()打开一个捕获文件,然后,将文件中的数据包移到已分配的发送队列。这时,就可以发送队列了,如果用户指定了同步,那么它将同步发送队列。

  注意,堆文件的链路层将会那些发送数据包接口中的一个进行比较,那些接口使用pcap_datalink()发送数据包。当比较的结果不相同,那么就会打印出警告信息。捕获文件的链路层和适配器的链路层相一致是非常重要的,不然,发送将变得毫无意义。

  程序源代码:
///////////////////////////////////////////////////////////////////////////// // Name: SendPackage.cpp // Purpose: wincap发送数据包。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #include <remote-ext.h> #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") void usage(); void main(int argc, char **argv) { pcap_t *indesc,*outdesc; char errbuf[PCAP_ERRBUF_SIZE]; char source[PCAP_BUF_SIZE]; FILE *capfile; int caplen, sync; u_int res; pcap_send_queue *squeue; struct pcap_pkthdr *pktheader; const u_char *pktdata; float cpu_time; u_int npacks = 0; /* 检查命令行参数的合法性 */ if (argc <= 2 || argc >= 5) { usage(); return; } /* 获取捕获文件长度 */ capfile = fopen( argv[1], "rb" ); if( !capfile ) { printf_s( "没有找到文件!/n" ); return; } fseek( capfile , 0, SEEK_END ); caplen = ftell(capfile)- sizeof( struct pcap_file_header ); fclose( capfile ); /* 检查时间戳是否合法 */ if( argc == 4 && argv[3][0] == 's' ) sync = TRUE; else sync = FALSE; /* 开始捕获 */ /* 根据WinPcap的新语法创建一个源字符串 */ if ( pcap_createsrcstr( source, // 源字符串 PCAP_SRC_FILE, // 我们要打开的文件 NULL, // 远程主机 NULL, // 远程主机的端口 argv[1], // 我们要打开的文件名 errbuf // 错误缓冲 ) != 0) { fprintf_s( stderr, "/n源串创建失败./n" ); return; } /* 打开捕获文件 */ if ( ( indesc= pcap_open( source, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf ) ) == NULL ) { fprintf_s( stderr,"/n无法打开文件 %s./n", source ); return; } /* 打开要输出的适配器 */ if ( ( outdesc = pcap_open( argv[2], 100, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL ) { fprintf_s( stderr,"/n无法打开适配器 %s./n", source ); return; } /* 检查MAC的类型 */ if ( pcap_datalink( indesc ) != pcap_datalink( outdesc ) ) { printf_s("警告: the datalink of the capture differs from the one of the selected interface./n"); printf_s("按任意键继续, 或 CTRL+C 退出./n"); getchar(); } /* 分配发送队列 */ squeue = pcap_sendqueue_alloc( caplen ); /* 从文件中将数据包填充到发送队列 */ while ((res = pcap_next_ex( indesc, &pktheader, &pktdata)) == 1) { if (pcap_sendqueue_queue(squeue, pktheader, pktdata) == -1) { printf("Warning: packet buffer too small, not all the packets will be sent./n"); break; } npacks++; } if (res == -1) { printf("Corrupted input file./n"); pcap_sendqueue_destroy(squeue); return; } /* 发送队列 */ cpu_time = (float)clock (); if ((res = pcap_sendqueue_transmit(outdesc, squeue, sync)) < squeue->len) { printf("An error occurred sending the packets: %s. Only %d bytes were sent/n", pcap_geterr(outdesc), res); } cpu_time = (clock() - cpu_time)/CLK_TCK; printf ("/n/nElapsed time: %5.3f/n", cpu_time); printf ("/nTotal packets generated = %d", npacks); printf ("/nAverage packets per second = %d", (int)((double)npacks/cpu_time)); printf ("/n"); /* 释放发送队列 */ pcap_sendqueue_destroy(squeue); /* 关闭输入文件 */ pcap_close(indesc); /* * 释放输出适配器 * IMPORTANT: 记得一定要关闭适配器,不然就不能保证 * 所有的数据包都回被发送出去 */ pcap_close(outdesc); return; } void usage() { printf_s("/nUsage:/n"); printf_s("/t sendcap file_name adapter [s]/n"); printf_s("/nParameters:/n"); printf_s("/nfile_name: the name of the dump file that will be sent to the network/n"); printf_s("/nadapter: the device to use. Use /"WinDump -D/" for a list of valid devices/n"); printf_s("/ns: if present, forces the packets to be sent synchronously, i.e. respecting the timestamps in the dump file. This option will work only under Windows NTx./n/n"); exit(0); }  

 

 

你可能感兴趣的:(struct,网络,File,null,float,compiler)