Огромная потеря пакетов при использовании PACKET_RX_RING из PACKET_MMAP
При захвате пакетов Ethernet с использованием PACKET_MMAP (PACKET_RX_RING) у меня потеря пакетов составляет более 50% при скорости передачи данных 100 КБ / с и выше. Это общее с такой технологией?
Есть ли шанс или возможности для улучшения кода / параметров / логики, чтобы уменьшить потерю пакетов при использовании PACKET_MMAP с PACKET_RX_RING
#include <stdio.h>
#include <stdio.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <poll.h>
#include <signal.h>
void handle_frame(struct tpacket_hdr* tphdr, struct sockaddr_ll* addr, char* l2content, char * l3content){
if(tphdr->tp_status & TP_STATUS_USER){
fwrite(l2content,tphdr->tp_snaplen,1,pcapfile);
tphdr->tp_status = TP_STATUS_KERNEL;
}
}
int main(){
file1 = fopen("file1.cap","a+");
int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
perror("socket");
exit(1);
}
struct tpacket_req req = {0};
req.tp_frame_size = TPACKET_ALIGN(TPACKET_HDRLEN + ETH_HLEN) + TPACKET_ALIGN(1500);
req.tp_block_size = sysconf(_SC_PAGESIZE);
while (req.tp_block_size < req.tp_frame_size) {
req.tp_block_size <<= 1;
}
req.tp_block_nr = 4;
size_t frames_per_buffer = req.tp_block_size / req.tp_frame_size;
req.tp_frame_nr = req.tp_block_nr * frames_per_buffer;
int version = TPACKET_V1;
(setsockopt(fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version));
setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void*)&req, sizeof(req));
size_t rx_ring_size = req.tp_block_nr * req.tp_block_size;
char* rx_ring = mmap(0, rx_ring_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
struct pollfd fds[1] = {0};
fds[0].fd = fd;
fds[0].events = POLLIN;
size_t frame_idx = 0;
char* frame_ptr = rx_ring;
while (1) {
struct tpacket_hdr* tphdr = (struct tpacket_hdr*)frame_ptr;
while (!(tphdr->tp_status & TP_STATUS_USER)) {
if (poll(fds, 1, -1) == -1) {
perror("poll");
exit(1);
}
}
struct sockaddr_ll* addr = (struct sockaddr_ll*)(frame_ptr + TPACKET_HDRLEN - sizeof(struct sockaddr_ll));
char* l2content = frame_ptr + tphdr->tp_mac;
char* l3content = frame_ptr + tphdr->tp_net;
handle_frame(tphdr, addr, l2content, l3content);
frame_idx = (frame_idx + 1) % req.tp_frame_nr;
int buffer_idx = frame_idx / frames_per_buffer;
char* buffer_ptr = rx_ring + buffer_idx * req.tp_block_size;
int frame_idx_diff = frame_idx % frames_per_buffer;
frame_ptr = buffer_ptr + frame_idx_diff * req.tp_frame_size;
}
fflush(pcapfile);
fclose(pcapfile);
}
0 ответов
Msgboardpana ,
Проверьте настройки кольца RX:tp_block_size = page size
(он равен нескольким кадрам Ethernet со стандартным MTU = 1512 байт, для меня со страницей 2 КБ это один кадр)tp_block_nr = 4
- frame num 4 - обратите внимание, что это физически несмежное пространство! Я думаю, что у вас буферное кольцо просто переполнилось. Я очень рекомендую вам увеличитьtp_block_size
(я использую следующий, моя страница 2Кб:)
tp.tp_block_size = BLOCK_SIZE; //(PAGE_2K * PAGE_2K)
tp.tp_block_nr = BLOCK_NR; //BLOCK_NR (1)
tp.tp_frame_size = PAGE_2K; /* Max size eth frame is 1522 bytes */
tp.tp_frame_nr = (tp.tp_block_size * tp.tp_block_nr) / tp.tp_frame_size;
И уменьшите количество блоков.
И, кроме того, попробуйте уменьшить системные вызовы в цикле цикла - я бы порекомендовал вам писать в файл в отдельном потоке, из-за этого очень тяжелый системный вызов (на всякий случай, вы можете проверить тайминги), также я советую вам включить режим promisc на eth - добавить в код инициализации:
struct packet_mreq mreq = {0};
mreq.mr_ifindex = if_idx.ifr_ifindex;
mreq.mr_type = PACKET_MR_PROMISC;
if (setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
{
perror("setsockopt");
goto closefd;
}
и если вы решите разделить потоки для записи и захвата файлов, политика sched_fifo для потока захвата:
ret = pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
С уважением, Булат