-- 作者:binaryluo
-- 发布时间:6/1/2006 9:25:00 AM
-- [原创]Winpcap学习第六天
尽管Winpcap清楚地指出了它的目的是数据包的截获,但是它还提供了一些对于原始网络(raw networking)的有用特性。用户可以找到一组完整的发包(send packets)函数。需要注意的是,libpcap目前并没有提供任何的发包的方法。 用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()来删除之,这将释放与该发送队列相关的所有缓冲区。
|