一、前言

尽管WinPcap从名字上来看表明他的主要目的是捕获数据包,但是他还为原始网络提供了一些其他的功能,其中之一就是用户可以发送数据包,这也就是本节的主要内容。需要指出的是原来的libpcap并不提供数据包的发送功能,这里所说的功能都是WinPcap的扩展功能,所以并不能够工作在UNIX下。


二、代码详解

pcap_sendpacket()来发送一个数据包,这个函数需要参数:

  • 一个装有要发送数据的缓冲区;
  • 要发送的长度;
  • 一个适配器;

注意缓冲区中的数据将不被内核协议处理,只是作为最原始的数据流被发送,所以我们必须填充好正确的协议头以便正确的将数据发送

#include "mainwindow.h"
#include <QApplication>
#include <QDebug>

#define HAVE_REMOTE
#include "pcap.h"

#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();



pcap_t* fp; //定义文件句柄
char errbuf[PCAP_ERRBUF_SIZE];
u_char packet[100];
int i;

//检查命令行参数,是否带有文件名
if(argc != 2) {
qDebug()<<"usage: %s filename"<<argv[0];
}

//打开指定网卡
if((fp = pcap_open_live(argv[1],100,1,1000,errbuf)) == NULL) {
qDebug()<<stderr<<"Error opening adapter: "<<errbuf;
}

//假设网络环境为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(int i=12; i<100; ++i) {
packet[i] = i%256;
}

//发送包
pcap_sendpacket(fp,packet,100);

return a.exec();
}

​pcap_sendpacket()​​只是提供了一个简单的直接的发送数据的方法,而发送队列提供了一个高级的、强大的、和最优的机制来发送一组数据包,队列实际上是一个装有要发送数据的一个容器,他又一个最大值来表明它能够容纳的最大比特数。

​pcap_sendqueue_alloc()​​用来创建爱你一个队列,并指定该队列的大小。

一旦队列被创建就可以调用​​pcap_sendqueue_queue()​​​来将数据存储到队列中,这个函数接受一个带有时间戳和长度的​​pcap_pkthdr​​​结构和一个装有数据报的缓冲区。这些参数同样也应用于​​pcap_next_ex()​​​和​​pcap_handler()​​​中,所以给要捕获的数据包或要从文件读取的数据包排队就是​​pcap_sendqueue_queue()​​的事情了。

WinPcap调用​​pcap_sendqueue_transmit()​​来发送数据包,注意,第三个参数如果非零,那么发送将是同步的,这将站用很大的CPU资源,因为发生在内核驱动的同步发送是通过"brute force"loops的,但是一般情况下能够精确到微秒。

需要指出的是用​​pcap_sendqueue_transmit()​​​来发送比用​​pcap_sendpacket()​​来发送一系列的数据要高效的多,因为他的数据是在内核级上被缓冲。

当不再需要队列时可以用​​pcap_sendqueue_destroy()​​来释放掉所有的队列资源。

下面的代码演示了如何用发送队列来发送数据,该示例用pcap_open_offline()打开了一个文件,然后将数据从文件移动到已分配的队列,这时就同步地传送队列(如果用户指定为同步的话)。

#include <stdlib.h>
#include <stdio.h>

#include <pcap.h>

void usage();

void main(int argc, char **argv) {
pcap_t *indesc,*outdesc;
char error[PCAP_ERRBUF_SIZE];
FILE *capfile;
int caplen, sync;
u_int res;
pcap_send_queue *squeue;
struct pcap_pkthdr *pktheader;
u_char *pktdata;

/* Check the validity of the command line */
if (argc <= 2 || argc >= 5)
{
usage();
return;
}

/* 得到文件长度 */
capfile=fopen(argv[1],"rb");
if(!capfile){
printf("Capture file not found!\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;

/* Open the capture */
if((indesc = pcap_open_offline(argv[1], error)) == NULL){
fprintf(stderr,"\nError opening the input file: %s\n", error);
return;
}

/* Open the output adapter */
if((outdesc = pcap_open_live(argv[2], 100, 1, 1000, error) ) == NULL)
{
fprintf(stderr,"\nError opening adapter: %s\n", error);
return;
}

/* 检测MAC类型 */
if(pcap_datalink(indesc) != pcap_datalink(outdesc)){
printf("Warning: the datalink of the capture differs from the one of the selected interface.\n");
printf("Press a key to continue, or CTRL+C to stop.\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;
}
}

if(res == -1){
printf("Corrupted input file.\n");
pcap_sendqueue_destroy(squeue);
return;
}

/* 传送队列数据 */

if((res = pcap_sendqueue_transmit(outdesc, squeue, sync)) < squeue->len)
{
printf("An error occurred sending the packets: %s. Only %d bytes were sent\n", error, res);
}

/* free the send queue */
pcap_sendqueue_destroy(squeue);

return;
}


void usage()
{
printf("\nSendcap, sends a libpcap/tcpdump capture file to the net. Copyright (C) 2002 Loris Degioanni.\n");
printf("\nUsage:\n");
printf("\t sendcap file_name adapter [s]\n");
printf("\nParameters:\n");
printf("\nfile_name: the name of the dump file that will be sent to the network\n");
printf("\nadapter: the device to use. Use \"WinDump -D\" for a list of valid devices\n");
printf("\ns: if present, forces the packets to be sent synchronously, i.e. respecting thetimestamps in the dump file. This option will work only under Windows NTx.\n\n");

exit(0);
}