libpcapソース分析_PACKET_MMAPメカニズム
15997 ワード
PACKET_を使うMMAPメカニズムの原因:PACKETを開かないMMAPの場合のキャプチャプロセスは非常に非効率である、非常に制限されたバッファを使用し、1つのメッセージをキャプチャするたびにシステム呼び出しが必要であり、このメッセージのタイムスタンプを取得するには、システム呼び出しを再実行する必要がある.PACKET_を有効にするMMAPのキャプチャプロセスは非常に効率的である、ユーザ空間にマッピングされた長さの適合可能なリングバッファを提供し、このバッファはメッセージの送受信に用いることができる.このようにしてメッセージを受信する場合、メッセージが来るのを待つだけでよく、ほとんどの場合、システム呼び出しを発行する必要はありません.このようにしてメッセージを送信場合、複数のメッセージは、1つのシステム呼び出しだけで最高帯域幅で送信ことができる.また,ユーザ空間とカーネルが共有キャッシュを用いることで,メッセージのコピーを低減できる.次にlibpcapのactivate_からmmap関数を手がかりに、PACKET_を展開MMAPの使用方法の分析.
小結:これでPACKETを正常にオープンしました.MMAP機能は、コア部分がリングバッファの構成にあることが明らかであり、次にpcap_read_linux_mmap_v 3は、PACKETを開く方法を手がかりとして分析する.MMAPの場合のキャプチャ
まとめ:これで、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は現在、受信リングバッファのみをサポートする.
関連データ構造:
/* 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];
}