libpcapソース分析_PACKET_MMAPメカニズム

15997 ワード

PACKET_を使うMMAPメカニズムの原因:PACKETを開かないMMAPの場合のキャプチャプロセスは非常に非効率である、非常に制限されたバッファを使用し、1つのメッセージをキャプチャするたびにシステム呼び出しが必要であり、このメッセージのタイムスタンプを取得するには、システム呼び出しを再実行する必要がある.PACKET_を有効にするMMAPのキャプチャプロセスは非常に効率的である、ユーザ空間にマッピングされた長さの適合可能なリングバッファを提供し、このバッファはメッセージの送受信に用いることができる.このようにしてメッセージを受信する場合、メッセージが来るのを待つだけでよく、ほとんどの場合、システム呼び出しを発行する必要はありません.このようにしてメッセージを送信場合、複数のメッセージは、1つのシステム呼び出しだけで最高帯域幅で送信ことができる.また,ユーザ空間とカーネルが共有キャッシュを用いることで,メッセージのコピーを低減できる.次にlibpcapのactivate_からmmap関数を手がかりに、PACKET_を展開MMAPの使用方法の分析. 
/*      pcap    PACKET_MMAP  
 * @     1      ;0       PACKET_MMAP  ;-1    
 */
int activate_mmap(pcap_t *handle, int *status)
{
    //    pcap       
    struct pcap_linux *handlep = handle->priv;

    //         oneshot   ,      pcap           
    handlep->oneshot_buffer = malloc(handle->snapshot);

    //              2M,       PACKET_MMAP        
    if (handle->opt.buffer_size == 0)
        handle->opt.buffer_size = 2*1024*1024;

    //                    ,      TPACKET_V3
    prepare_tpacket_socket(handle);

    //        ,          
    create_ring(handle, status);

    //            linux PACKET_MMAP       
    switch (handlep->tp_version) {
    case TPACKET_V1:
        handle->read_op = pcap_read_linux_mmap_v1;
        break;
    case TPACKET_V1_64:
        handle->read_op = pcap_read_linux_mmap_v1_64;
        break;
    case TPACKET_V2:
        handle->read_op = pcap_read_linux_mmap_v2;
        break;
    case TPACKET_V3:
        handle->read_op = pcap_read_linux_mmap_v3;
        break;
    }

    //        linux PACKET_MMAP      
    handle->cleanup_op = pcap_cleanup_linux_mmap;
    handle->setfilter_op = pcap_setfilter_linux_mmap;
    handle->setnonblock_op = pcap_setnonblock_mmap;
    handle->getnonblock_op = pcap_getnonblock_mmap;
    handle->oneshot_callback = pcap_oneshot_mmap;
    handle->selectable_fd = handle->fd;
    return 1;
}

int prepare_tpacket_socket(pcap_t *handle)
{
    //    pcap       
    struct pcap_linux *handlep = handle->priv;
    int ret;

    /*    pcap      immediate      ,                 TPACKET_V3
     *          TPACKET_V3                 
     */
    if (!handle->opt.immediate) {
        ret = init_tpacket(handle, TPACKET_V3, "TPACKET_V3");
        if (ret == 0)       //     TPACKET_V3  
            return 1;
        else if (ret == -1) //   TPACKET_V3        kernel      
            return -1;
    }

    //  kernel   TPACKET_V3      ,     TPACKET_V2  
    ret = init_tpacket(handle, TPACKET_V2, "TPACKET_V2");
    if (ret == 0)       //     TPACKET_V2  
        return 1;
    else if (ret == -1) //   TPACKET_V2        kernel      
        return -1;

    /*  kernel   TPACKET_V3、TPACKET_V2      ,        TPACKET_V1  
     *         PACKET_MMAP  ,     TPACKET_V1  
     */
    handlep->tp_version = TPACKET_V1;
    handlep->tp_hdrlen = sizeof(struct tpacket_hdr);

    return 1;
}

int init_tpacket(pcap_t *handle, int version, const char *version_str)
{
    //    pcap       
    struct pcap_linux *handlep = handle->priv;
    int val = version;
    socklen_t len = sizeof(val);

    //                   ,                         
    if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
        //            kernel   
        if (errno == ENOPROTOOPT || errno == EINVAL)
            return 1;

        return -1;
    }
    handlep->tp_hdrlen = val;

    //       ,   pcap                    
    val = version;
    setsockopt(handle->fd, SOL_PACKET, PACKET_VERSION, &val,sizeof(val));
    handlep->tp_version = version;

    //            VLAN_TAG_LEN       ,  VLAN tag  
    val = VLAN_TAG_LEN;
    setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val,sizeof(val));
    
    return 0;
}

int create_ring(pcap_t *handle, int *status)
{
    //    pcap       
    struct pcap_linux *handlep = handle->priv;
    struct tpacket_req3 req;
    socklen_t len;
    unsigned int sk_type,tp_reserve, maclen, tp_hdrlen, netoff, macoff;
    unsigned int frame_size;
    
    //                    
    switch (handlep->tp_version) {
    case TPACKET_V1:
    case TPACKET_V2:
        /* V1、V2                  ,     snapshot,
         *     snapshot           ,                 ,           ,
         *                       
         */
        frame_size = handle->snapshot;
        //                 
        if (handle->linktype == DLT_EN10MB) {
            int mtu;
            int offload;

            //          offload  
            offload = iface_get_offload(handle);
            //      offload     ,        MTU               
            if (!offload) {
                mtu = iface_get_mtu(handle->fd, handle->opt.device,handle->errbuf);
                if (frame_size > (unsigned int)mtu + 18)
                    frame_size = (unsigned int)mtu + 18;
            }
        }

        //        
        len = sizeof(sk_type);
        getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type,&len);
        /*                   
         *   :  V3/V2        ,       VLAN_TAG_LEN       , V1        
         */
        len = sizeof(tp_reserve);
        if (getsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE,&tp_reserve, &len) < 0) {
            if (errno != ENOPROTOOPT)
                return -1;

            tp_reserve = 0;
        }

        //                       
        maclen = (sk_type == SOCK_DGRAM) ? 0 : MAX_LINKHEADER_SIZE;
        tp_hdrlen = TPACKET_ALIGN(handlep->tp_hdrlen) + sizeof(struct sockaddr_ll) ;
        netoff = TPACKET_ALIGN(tp_hdrlen + (maclen < 16 ? 16 : maclen)) + tp_reserve;
        macoff = netoff - maclen;
        req.tp_frame_size = TPACKET_ALIGN(macoff + frame_size);     //                   
        req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;//        ,                         
        break;
    case TPACKET_V3:
        //    V1/V2,V3     ,              
        req.tp_frame_size = MAXIMUM_SNAPLEN;
        req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
        break;
    }

    /*   V1/V2/V3      ,        PAGE_SIZE * 2^n,         1  
     *   :  V3        MAXIMUM_SNAPLEN    PAGE_SIZE,    V3   1        1  
     */
    req.tp_block_size = getpagesize();
    while (req.tp_block_size < req.tp_frame_size)
        req.tp_block_size <<= 1;

    frames_per_block = req.tp_block_size/req.tp_frame_size;

retry:
    //            ,              ,                 
    req.tp_block_nr = req.tp_frame_nr / frames_per_block;
    req.tp_frame_nr = req.tp_block_nr * frames_per_block;

    //           
    req.tp_retire_blk_tov = (handlep->timeout>=0)?handlep->timeout:0;
    //            
    req.tp_sizeof_priv = 0;
    //             
    req.tp_feature_req_word = 0;

    //          
    if (setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,(void *) &req, sizeof(req))) {
        //            ,              
        if ((errno == ENOMEM) && (req.tp_block_nr > 1)) {
            if (req.tp_frame_nr < 20)
                req.tp_frame_nr -= 1;
            else
                req.tp_frame_nr -= req.tp_frame_nr/20;

            goto retry;
        }
        //   kernel   PACKET_MMAP     
        if (errno == ENOPROTOOPT)
            return 0;
    }

    //                      
    //                        
    handlep->mmapbuflen = req.tp_block_nr * req.tp_block_size;
    handlep->mmapbuf = mmap(0, handlep->mmapbuflen,PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);

    //          pcap                 /     
    handle->cc = req.tp_frame_nr;
    handle->buffer = malloc(handle->cc * sizeof(union thdr *));

    //                       buffer 
    handle->offset = 0;
    for (i=0; immapbuf[i*req.tp_block_size];
        for (j=0; joffset) {
            RING_GET_CURRENT_FRAME(handle) = base;
            base += req.tp_frame_size;
        }
    }

    handle->bufsize = req.tp_frame_size;
    //   PACKET_MMAP   ,offset                 
    handle->offset = 0;
    return 1;
}

小結:これでPACKETを正常にオープンしました.MMAP機能は、コア部分がリングバッファの構成にあることが明らかであり、次にpcap_read_linux_mmap_v 3は、PACKETを開く方法を手がかりとして分析する.MMAPの場合のキャプチャ
int pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback,u_char *user)
{
    //    pcap       
    struct pcap_linux *handlep = handle->priv;
    int pkts = 0;       //             
    union thdr h;
    
again:
    //                      
    if (handlep->current_packet == NULL) {
        h.raw = RING_GET_CURRENT_FRAME(handle);
        //                  ,        
        if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) {
            pcap_wait_for_frames_mmap(handle);
        }
    }

    /*                          ,    ,          ;
     *    pcap          1           
     */
    h.raw = RING_GET_CURRENT_FRAME(handle);
    if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) {
        if (pkts == 0 && handlep->timeout == 0)
            goto again;

        return pkts;
    }

    //                           
    /*           ,                        ,    ,
     *                           
     */
    while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
        int packets_to_read;

        //                  current_packet packets_left
        if (handlep->current_packet == NULL) {
            //                  ,     
            h.raw = RING_GET_CURRENT_FRAME(handle);
            if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) 
                break;

            handlep->current_packet = h.raw + h.h3->hdr.bh1.offset_to_first_pkt;
            handlep->packets_left = h.h3->hdr.bh1.num_pkts;
        }

        /*                                     ,
         *                    
         */
        packets_to_read = handlep->packets_left;
        if (!PACKET_COUNT_IS_UNLIMITED(max_packets) && packets_to_read > (max_packets - pkts)) 
            packets_to_read = max_packets - pkts;

        /*            packets_to_read    ,             :
         *                ;
         *          
         */
        while (packets_to_read-- && !handle->break_loop) {
            //                   
            struct tpacket3_hdr* tp3_hdr = (struct tpacket3_hdr*) handlep->current_packet;
            //              
            ret = pcap_handle_packet_mmap(
                    handle,
                    callback,
                    user,
                    handldp->current_packet,
                    tp3_hdr->tp_len,
                    tp3_hdr->tp_mac,
                    tp3_hdr->tp_snaplen,
                    tp3_hdr->tp_sec,
                    handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO ? tp3_hdr->tp_nsec : tp3_hdr->tp_nsec / 1000,
                    (tp3_hdr->hv1.tp_vlan_tci || (tp3_hdr->tp_status & TP_STATUS_VLAN_VALID)),
                    tp3_hdr->hv1.tp_vlan_tci,
                    VLAN_TPID(tp3_hdr, &tp3_hdr->hv1));
            if (ret == 1) {
                //               
                pkts++;
                handlep->packets_read++;
            } else if (ret < 0) {
                //                    
                handlep->current_packet = NULL;
                return ret;
            }

            //              
            handlep->current_packet += tp3_hdr->tp_next_offset;
            handlep->packets_left--;
        }

        //                ,           
        if (handlep->packets_left <= 0) {
            h.h3->hdr.bh1.block_status = TP_STATUS_KERNEL;

            //           
            if (++handle->offset >= handle->cc) 
                handle->offset = 0;

            handlep->current_packet = NULL;
        }

        //                       
        if (handle->break_loop) {
            handle->break_loop = 0;
            return PCAP_ERROR_BREAK;
        }
    }

    //    pcap        0,                
    if (pkts == 0 && handlep->timeout == 0)
        goto again;

    return pkts;
}

int pcap_handle_packet_mmap(...)
{
    struct sockaddr_ll *sll; 
    unsigned char *bp;
    struct pcap_pkthdr pcaphdr;

    //          
    if (tp_mac + tp_snaplen > handle->bufsize)
        return -1;

    bp = frame + tp_mac;
    sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
    //          ,           
    if (handlep->cooked) {
        //  
    }

    //    pcap           ,        
    if (handlep->filter_in_userland && handle->fcode.bf_insns) {
        //  
    }

    //          
    if (!linux_check_direction(handle, sll))
        return 0;

    //         
    pcaphdr.ts.tv_sec = tp_sec;
    pcaphdr.ts.tv_usec = tp_usec;
    pcaphdr.caplen = tp_snaplen;
    pcaphdr.len = tp_len;
    //          ,             
    if (handlep->cooked) {
        pcaphdr.caplen += SLL_HDR_LEN;
        pcaphdr.len += SLL_HDR_LEN;
    }

    //   TPACKET_V2 TPACKET_V3     vlan  ,     vlan      
    if (tp_vlan_tci_valid && handlep->vlan_offset != -1 && tp_snaplen >= (unsigned int) handlep->vlan_offset) {
        struct vlan_tag *tag;
        bp -= VLAN_TAG_LEN;
        memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset);
        tag = (struct vlan_tag *)(bp + handlep->vlan_offset);
        tag->vlan_tpid = htons(tp_vlan_tpid);
        tag->vlan_tci = htons(tp_vlan_tci); 
        pcaphdr.caplen += VLAN_TAG_LEN;
        pcaphdr.len += VLAN_TAG_LEN;
    }

    //                  ,   
    if (pcaphdr.caplen > (bpf_u_int32)handle->snapshot)
        pcaphdr.caplen = handle->snapshot;

    //                
    callback(user, &pcaphdr, bp);

    return 1;
}

まとめ:これで、TPACKET_に基づくV 3モードのPACKET_MMAPメカニズムの分析が完了し、libpcapにおけるTPACKET_についてV1、TPACKET_V 3モードのキャプチャフローは分析しない.ここではV 1,V 2,V 3の3つのパターンの違いのみが列挙されている:TPACKET_V 1:これはデフォルトのリングバッファバージョンTPACKET_です.V 2:V 1の改良に比べて、以下の点32ビットのユーザ空間リングバッファは、64ビットカーネルに基づいて動作することができる.タイムスタンプの精度はmsからnsに向上する.VLAN情報の携帯をサポートする(これは、V 1で受信したvlanパケットがvlan情報を失うことを意味する).            TPACKET_V 3:V 2の改良に比べて、可変フレーム長(V 1、V 2のフレーム長はいずれもtpacket_req.tp_frame_size固定値)に構成できるメモリブロックがいくつかある.read/pollはblock-level(V 1、V 2はpacket_level)に基づく.pollタイムアウトパラメータのサポートを開始します.ユーザー設定可能オプションが追加されました:tp_retire_blk_tovなど;RX Hashデータはユーザによって使用することができる.注記:V 3は現在、受信リングバッファのみをサポートする.
 
関連データ構造:
/*   TPACKET_V3               
 *   : tpacket_req3   tpacket_req     ,                        ,V1/V2            
 */
struct tpacket_req3 {
    unsigned int    tp_block_size;      //             (    PAGE_SIZE * 2^n )
    unsigned int    tp_block_nr;        //      
    unsigned int    tp_frame_size;      //       (  V3        ,                 )
    unsigned int    tp_frame_nr;        //      (               *     )
    unsigned int    tp_retire_blk_tov;  //       (ms),                      ,0       
    unsigned int    tp_sizeof_priv;     //             ,0         
    unsigned int    tp_feature_req_word;//      (     1    TP_FT_REQ_FILL_RXHASH)
}

//   PACKET_MMAP libpcap V1、V2、V3              
union thdr {
    struct tpacket_hdr          *h1;
    struct tpacket2_hdr         *h2;
    struct tpacket_block_desc   *h3;     
    void *raw;
}

// TPACKET_V3             
struct tpacket3_hdr {
    __u32       tp_next_offset; //               
    __u32       tp_sec;         //    (s)
    __u32       tp_nsec;        //    (ns)
    __u32       tp_snaplen;     //          
    __u32       tp_len;         //       
    __u32       tp_status;      //     
    __u16       tp_mac;         //    MAC          
    __u16       tp_net;
    union {
        struct tpacket_hdr_variant1 hv1;    //   vlan      
    };
    __u8        tp_padding[8];
}