Спасибо, что нашли время посмотреть на это.Это проблема, с которой я сталкиваюсь уже неделю или около того, и я ошеломлен.Любой ввод был бы хорош.
В настоящее время я использую PACKET_MMAP TX_RING V3 (я пробовал V2 и V1, но у меня та же проблема с ними. Я решил использовать V3, потому что я могу сохранитьнесколько кадров eth в одном блоке, вместо того, чтобы иметь возможность иметь только один кадр на блок tp_block в V1 и V2), чтобы избежать чрезмерных системных вызовов в процессе отправки моего сканера портов.Я не настраиваю RX_RING, так что это не является частью картины.
Сейчас я тестирую это только с одним хостом, и я подключаюсь только к портам, которые, как я знаю, активны.
Итак ... вот ситуация:
У меня есть TX_RING: [1 блок |размер блока 4096 |32 кадра |размер кадра 128]
код для настройки TX_RING:
int prepare_packetmmap_tx_ring(t_thread *thread)
{
int tpacket_v;
struct sockaddr_ll sll_loc;
/* Step 1 Create PF_PACKET socket.*/
thread->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (thread->sock == -1)
{
toggle_thread_alive(thread);
return (hermes_error(FAILURE, "socket() %s", strerror(errno)));
}
memset(&sll_loc, 0, sizeof(struct sockaddr_ll));
memcpy(&sll_loc.sll_addr, thread->pool->iface.if_hwaddr, ETH_ALEN);
sll_loc.sll_ifindex = thread->pool->iface.inx;
sll_loc.sll_family = AF_PACKET;
sll_loc.sll_protocol = htons(ETH_P_ALL);
/* Bind our socket to the interface */
if (bind(thread->sock, (sockaddr *)&sll_loc, sizeof(struct sockaddr_ll)) == -1)
{
toggle_thread_alive(thread);
return (hermes_error(FAILURE, "bind() %s", strerror(errno)));
}
tpacket_v = TPACKET_V3;
if (setsockopt(thread->sock, SOL_PACKET, PACKET_VERSION, &tpacket_v, sizeof(tpacket_v)) < 0)
{
toggle_thread_alive(thread);
return (hermes_error(FAILURE, "setsockopt() PACKET_VERSION %s", strerror(errno)));
}
/* Step 3 determine sizes for PACKET_TX_RING and allocate txring via setsockopt() */
thread->txring.tpr.tp_block_size = (uint)getpagesize();
thread->txring.tpr.tp_frame_size = TPACKET3_HDRLEN + sizeof(struct ethhdr) + sizeof(struct iphdr) + DEF_TRAN_HDRLEN + thread->pool->env->cpayload_len;
thread->txring.tpr.tp_frame_size = pow2_round(thread->txring.tpr.tp_frame_size);
thread->txring.tpr.tp_block_nr = (THRD_HSTGRP_MAX / (thread->txring.tpr.tp_block_size / thread->txring.tpr.tp_frame_size));
thread->txring.tpr.tp_frame_nr = thread->txring.tpr.tp_block_nr * (thread->txring.tpr.tp_block_size / thread->txring.tpr.tp_frame_size);
thread->txring.size = thread->txring.tpr.tp_block_size * thread->txring.tpr.tp_block_nr;
thread->txring.doffset = sizeof(struct tpacket3_hdr);
if (setsockopt(thread->sock, SOL_PACKET, PACKET_TX_RING, (void *)&thread->txring.tpr, sizeof(thread->txring.tpr)) < 0)
{
toggle_thread_alive(thread);
return (hermes_error(FAILURE, "setsockopt() PACKET_TX_RING %s", strerror(errno)));
}
printf("frame size: %d | blocksize: %d | block count %d | frame count %d\n", thread->txring.tpr.tp_frame_size, thread->txring.tpr.tp_block_size, thread->txring.tpr.tp_block_nr, thread->txring.tpr.tp_frame_nr);
extra_sock_opts(thread->sock, thread->txring.size);
/* Step 4 actually map ring to user space */
if (!(thread->txring.ring = mmap(0, thread->txring.size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
thread->sock, 0)))
{
toggle_thread_alive(thread);
return (hermes_error(FAILURE, "mmap() TX_RING %s", strerror(errno)));
}
return (SUCCESS);
}
Так что ... все хорошо и хорошо, верно?Давайте перейдем к процессу заполнения моего кольца:
nt fill_tx_ring(t_thread *thread, t_frame *ethframe)
{
struct tpacket3_hdr *frame;
void *data;
uint16_t *srcports;
uint16_t *dstports;
uint ring_i;
uint hst_i;
int ret;
ring_i = 0;
hst_i = 0;
ret = 0;
srcports = thread->pool->env->ports.flat;
dstports = thread->pool->env->dstports;
while (hst_i < thread->hstgrpsz && ring_i < thread->txring.tpr.tp_frame_nr)
{
if (thread->hstgrp[hst_i].health.done == true)
{
hst_i++;
continue;
}
frame = (struct tpacket3_hdr *)(thread->txring.ring + (thread->txring.tpr.tp_frame_size * ring_i));
switch((volatile uint32_t)frame->tp_status)
{
case TP_STATUS_WRONG_FORMAT:
return(hermes_error(FAILURE, "TX_RING wrong format in frame %i of thread %d", ring_i, thread->id));
case TP_STATUS_AVAILABLE:
data = (uint8_t *)frame + thread->txring.doffset;
ethframe->ip->daddr = thread->hstgrp[hst_i].result->ip.s_addr;
ip_checksum(ethframe->ip);
ethframe->tcp->source = htons(dstports[thread->hstgrp[hst_i].health.portinx]);
ethframe->tcp->dest = htons(srcports[thread->hstgrp[hst_i].health.portinx]);
tcp_checksum(ethframe->ip, (uint16_t *)ethframe->tcp);
memcpy(data, ethframe->buffer, ethframe->size);
frame->tp_next_offset = 0;
frame->tp_len = ethframe->size;
frame->tp_status = TP_STATUS_SEND_REQUEST;
printf("setup a frame\n");
ret++; hst_i++; ring_i++;
break;
default:
printf("skipped ring with status %d\n", frame->tp_status);
ring_i++;
break;
}
}
return (ret);
}
(правка / примечание: я также пытался опрашивать POLL_OUT на моем сокете до тех пор, пока tp_status не изменит кадр, это приведет к тому же результату.)
Таким образом, этот процесс заполнения будет заполнять один кадр, потому что я сканирую только один хост, и я отправляю хосту только одно сообщение за раз.Первая отправка отлично работает!Он отправляет кадр, я вижу его на Wireshark, я получаю ответ, обрабатываю его, а затем мы возвращаемся к процедуре fill_tx_ring ().Ниже приведена моя функция сканирования, поэтому вы можете видеть порядок, в котором это происходит:
void syn_scan(t_thread *thread)
{
struct timeval sent = {0};
struct timeval now = {0};
long ms;
int ret;
t_frame frame;
memset(&frame, 0, sizeof(t_frame));
init_ethframe(thread, &frame);
thread->scancnt = thread->hstgrpsz;
while (thread->scancnt > 0)
{
gettimeofday(&now, NULL);
if ((ms = timediff_ms(&now, &sent)) >= DEF_INIT_RTT_TIMEOUT)
{
fill_tx_ring(thread, &frame);
send_task(thread);
gettimeofday(&sent, NULL);
thread->rxfilter.fd.events = POLL_IN;
if ((ret = poll(&thread->rxfilter.fd, 1,
DEF_INIT_RTT_TIMEOUT)) < 0)
hermes_error(FAILURE, "poll() %s", strerror(errno));
if (ret > 0)
pcap_dispatch(thread->rxfilter.handle,
thread->hstgrpsz, handle_packet,
(u_char*)thread);
else
printf("didn't get anything\n");
}
else
{
usleep((useconds_t) ms * 1000);
}
}
free(frame.buffer);
frame.buffer = NULL;
}
Теперь реальная проблема: каждый второй вызов send () возвращает 0. При последующих вызовах fill_tx_ring (), яобнаруживает, что все заполненные ранее кадры не обновляются ядром (из-за сбоя send (), кажется, что ядро никогда даже не получает запрос или не просматривает кольцо) ... их поля tp_status по-прежнемуустановите значение TP_STATUS_SEND_REQUEST.
Даже после того, как мы успешно осуществим send () (скажем, третий успешный), вы заметите, что кадры, которые уже использовались для передачи кадра, все еще установлены в 1 (TP_STATUS_SEND_REQUEST)).
Зарегистрированный вывод моей программы выглядит следующим образом:
index 2 | mac: a8:7d:12:1:b6:46:| ip: 10.43.1.162 | gwip: 10.43.1.254
| gwmac: 0:35:1a:54:ca:17:
frame size: 128 | blocksize: 4096 | block count 1 | frame count 32
ports total 5
setup a frame
sent 1 packets
packet processed
a8 7d 12 01 b6 46 00 35
1a 54 ca 17 08 00 45 00 00 2c 00 00 40 00
3f 06 1f 7c 0a 72 06 12 0a 2b 01 a2 16 00
b1 f8 52 03 f6 a0 00 00 00 02 60 12 ff ff
81 11 00 00 02 04 05 b4 00 00
setup a frame
didn't send anything.
didn't get anything
skipped ring with status 1
setup a frame
sent 1 packets
packet processed
a8 7d 12 01 b6 46 00 35
1a 54 ca 17 08 00 45 00 00 2c 00 00 40 00
3f 06 1f 7c 0a 72 06 12 0a 2b 01 a2 58 00
ac ac 06 32 5f 44 00 00 00 02 60 12 ff ff
69 49 00 00 02 04 05 b4 00 00
skipped ring with status 1
setup a frame
didn't send anything.
didn't get anything
skipped ring with status 1
skipped ring with status 1
setup a frame
sent 1 packets
packet processed
a8 7d 12 01 b6 46 00 35
1a 54 ca 17 08 00 45 00 00 2c 00 00 40 00
3f 06 1f 7c 0a 72 06 12 0a 2b 01 a2 d3 0c
e7 f1 14 3c 13 c4 00 00 00 02 60 12 ff ff
5e ff 00 00 02 04 05 b4 00 00
skipped ring with status 1
skipped ring with status 1
setup a frame
didn't send anything.
didn't get anything
skipped ring with status 1
skipped ring with status 1
skipped ring with status 1
setup a frame
sent 1 packets
packet processed
a8 7d 12 01 b6 46 00 35
1a 54 ca 17 08 00 45 00 00 2c 00 00 40 00
3f 06 1f 7c 0a 72 06 12 0a 2b 01 a2 ea 0c
8a 10 f0 d0 aa 9b 00 00 00 02 60 12 ff ff
49 5d 00 00 02 04 05 b4 00 00
skipped ring with status 1
skipped ring with status 1
skipped ring with status 1
setup a frame
didn't send anything.
didn't get anything
ms 1000
skipped ring with status 1
skipped ring with status 1
skipped ring with status 1
skipped ring with status 1
setup a frame
sent 1 packets
packet processed
a8 7d 12 01 b6 46 00 35
1a 54 ca 17 08 00 45 00 00 2c 00 00 40 00
3f 06 1f 7c 0a 72 06 12 0a 2b 01 a2 0c 17
ee 5f a7 98 89 b2 00 00 00 02 60 12 ff ff
45 0d 00 00 02 04 05 b4 00 00
thread 1 closing 0 still alive
Так почему же ядро не возвращает мне мои фреймы, и почему все остальные вызовы send () терпят неудачу?чтобы отправить кадр, который я установил на ринге?
Заранее большое спасибо!
Вы можете увидеть полный код:
Проект: https://github.com/dauie/hermes.git
tx setup: https://github.com/Dauie/hermes/blob/master/src/thread.c
tx_fill / send: https://github.com/Dauie/hermes/blob/master/src/scan.c