用pcap_sendpacket()发送单个数据包
下面的代码片断表现了最简单的发送一个数据包的过程。打开适配器后,pcap_sendpacket()被用来发送一个手工的(hand
-crafted)数据包。
试验代码:
#include <stdlib.h>
#include <stdio.h>
#include <pcap.h>
#include <remote-ext.h>
void main(int argc, char** argv){
pcap_t* fp;
char errbuf[PCAP_ERRBUF_SIZE];
u_char packet[100];
int i;
/* Check the validity of the command line*/
if (argc != 2)
{
printf("/tusage: %s interface (e.g. 'rpcap://eth0')", argv[0]);
return;
}
/* Open the output device */
if ((fp = pcap_open(argv[1], /* name of the device */
100, /* portion of the packet to capture (only the first 100 bytes)*/
PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */
1000, /* read timeout */
NULL, /* authentication on the remote machine */
errbuf /* error buffer */
)) == NULL)
{
fprintf(stderr, "/nUnable to open the adapter. %s is not supported by Winpcap/n", argv[1]);
return;
}
/* Supposing to be on ethernet, set mac destinat to 1:1:1:1:1:1 */
packet[0] = 1;
packet[1] = 1;
packet[2] = 1;
packet[3] = 1;
packet[4] = 1;
packet[5] = 1;
/* set mac source to 2:2:2:2:2:2 */
packet[6] = 2;
packet[7] = 2;
packet[8] = 2;
packet[9] = 2;
packet[10] = 2;
packet[11] = 2;
/* Fill the rest of the packet */
for (i = 12; i < 100; ++ i)
{
packet[i] = i % 256;
}
/* Send down the packet */
if (pcap_sendpacket(fp, packet, 100 /* size */) != 0)
{
fprintf(stderr, "/nError sending the packet: /n", pcap_geterr(fp));
return;
}
return;
}
函数1:
int pcap_sendpacket(pcap_t* p,
u_char* buf,
int size)
发送一个原始数据包(raw packet)到网络上。p是用来发送数据包的那个接口,buf包含着要发送的数据包的数据(包括各种各样的协议头),size是buf所指的缓冲区的尺寸,也就是要发送的数据包的大小。MAC循环冗余码校验不必被包含,因为它很容易被计算出来并被网络接口驱动添加。如果数据包被成功发送,返回0;否则,返回-1。
发送队列(Send queues)
pcap_sendpacket()提供了一个简单快捷的发送单个数据包的方法,发送队列(send queues)提供了一个高级的,强大的,优化的发送一组数据包的机制。发送队列是一个用来保存将要发送到网络上的的众多数据包的容器。它有一个大小,描述了它所能容纳的最大字节数。
通过指定发送队列的大小,pcap_sendqueue_alloc()函数创建一个发送队列。一旦发送队列被创建好,pcap_sendqueue_queue()可以把一个数据包添加到发送队列里。函数pcap_sendqueue_alloc()的参数必须与pcap_next_ex()和pcap_handler()的相同,因此,从一个文件捕获或读取数据包的时候,如何进行pcap_sendqueue_alloc()的参数传递是一个问题。
试验代码:
#include <stdlib.h>
#include <stdio.h>
#include <pcap.h>
#include <remote-ext.h>
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;
u_char *pktdata;
float cpu_time;
u_int npacks = 0;
/* Check the validity of the command line */
if (argc <= 2 || argc >= 5)
{
usage();
return;
}
/* Retrieve the length of the capture file */
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);
/* Chek if the timestamps must be respected */
if(argc == 4 && argv[3][0] == 's')
sync = TRUE;
else
sync = FALSE;
/* Open the capture */
/* Create the source string according to the new WinPcap syntax */
if ( pcap_createsrcstr( source, // variable that will keep the source string
PCAP_SRC_FILE, // we want to open a file
NULL, // remote host
NULL, // port on the remote host
argv[1], // name of the file we want to open
errbuf // error buffer
) != 0)
{
fprintf(stderr,"/nError creating a source string/n");
return;
}
/* Open the capture file */
if ( (indesc= pcap_open(source, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL)
{
fprintf(stderr,"/nUnable to open the file %s./n", source);
return;
}
/* Open the output adapter */
if ( (outdesc= pcap_open(argv[2], 100, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL)
{
fprintf(stderr,"/nUnable to open adapter %s./n", source);
return;
}
/* Check the MAC type */
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();
}
/* Allocate a send queue */
squeue = pcap_sendqueue_alloc(caplen);
/* Fill the queue with the packets from the file */
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;
}
/* Transmit the queue */
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");
/* free the send queue */
pcap_sendqueue_destroy(squeue);
/* Close the input file */
pcap_close(indesc);
/*
* lose the output adapter
* IMPORTANT: remember to close the adapter, otherwise there will be no guarantee that all the
* packets will be sent!
*/
pcap_close(outdesc);
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 the timestamps in the dump file. This option will work only under Windows NTx./n/n");
exit(0);
}
函数1:
pcap_send_queue* pcap_sendqueue_alloc(u_int memsize)
为一个发送队列分配空间,即创建一个用来存储一组原始数据包(raw packet)的缓冲区,这些数据包将用pcap_sendqueue_transmit()提交到网络上。memsize是队列容纳的字节数,因此它决定了队列所能容纳的最大数据量。使用pcap_sendqueue_queue()可以在发送队列中插入数据包。
函数2:
int pcap_sendqueue_queue(pcap_send_queue* queue,
const struct pcap_pkthdr* pkt_header,
const u_char* pkt_data)
添加一个数据包到发送队列中。queue指向发送队列的尾部;pkt_header指向一个pcap_pkthdr结构体,该结构体包含时间戳和数据包的长度;pkt_data指向存放数据包数据部分的缓冲区。
为了提交一个发送队列,Winpcap提供了pcap_sendqueue_transmit()函数。
函数3:
u_int pcap_sendqueue_transmit(pcap_t* p,
pcap_send_queue* queue,
int sync)
该函数将队列里的内容提交到线路上。p是一个指向适配器的指针,数据包将在这个适配器上被发送;queue指向pcap_send_queue结构体,它包含着要发送的所有数据包;sync决定了发送操作是否被同步:如果它是非0(non-zero),发送数据包关系到时间戳,否则,他们将以最快的速度发送(即不考虑时间戳)。
返回值是发送的字节数。如果它小于size参数,将发生一个错误。该错误可能是由于驱动/适配器(driver/adapter)问题或发送队列的不一致/伪造(inconsistent/bogus)引起。
注意:
l 使用该函数的效率比使用pcap_sendpacket()发送一系列数据包的效率高,因为数据包在核心态(kernel-level)被缓冲,所以降低了上下文的交换次数。因此,使用pcap_sendqueue_transmit()更好。
l 当sync被设置为TRUE时,随着一个高精度的时间戳,数据包将在内核伴被同步。这就要求CPU的数量是不可忽略的,通常允许以一个微秒级的精度发送数据包(这依赖于机器性能计数器的准确度)。然而,用pcap_sendpacket()发送数据包不能达到这样一个精确度。
如果第三个参数非0,发送将被同步(synchronized),即相关的时间戳将被注意。这个操作要求注意CPU的数量,因为使用“繁忙 等待(busy wait)”循环,同步发生在内核驱动。
当不再需要一个队列时,可以用pcap_sendqueue_destroy()来删除之,这将释放与该发送队列相关的所有缓冲区。