以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 安全理论 』  (http://bbs.xml.org.cn/list.asp?boardid=65)
----  [原创]Winpcap学习第六天  (http://bbs.xml.org.cn/dispbbs.asp?boardid=65&rootid=&id=33467)


--  作者: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()来删除之,这将释放与该发送队列相关的所有缓冲区。


--  作者:001liujie
--  发布时间:1/15/2007 3:47:00 PM

--  
libpcap 0.9.1可以发包啦!
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
62.012ms