Как найти повторные передачи TCP при анализе пакетов в C
Я написал простой исходный файл, который может читать pcap-файлы с помощью библиотеки libpcap на C. Я могу разбирать пакеты один за другим и анализировать их до определенной степени. Я хочу иметь возможность определить, является ли проанализированный мной TCP-пакет повторной передачей TCP или нет. После тщательного поиска в Интернете я пришел к выводу, что для этого мне нужно отслеживать поведение трафика, а это означает также анализ ранее полученных пакетов.
Чего я на самом деле хочу достичь, так это сделать на базовом уровне то, что
tcp.analysis.retransmission
фильтр делает в wireshark.
Это MRE, который читает файл pcap и анализирует TCP-пакеты, отправленные по IPv4. Функция
find_retransmissions
Здесь анализируется пакет.
#include <pcap.h>
#include <stdio.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <net/ethernet.h>
#include <string.h>
void process_packet(u_char *,const struct pcap_pkthdr * , const u_char *);
void find_retransmissions(const u_char * , int );
int main()
{
pcap_t *handle;
char errbuff[PCAP_ERRBUF_SIZE];
handle = pcap_open_offline("smallFlows.pcap", errbuff);
pcap_loop(handle, -1, process_packet, NULL);
}
void process_packet(u_char *args,
const struct pcap_pkthdr * header,
const u_char *buffer)
{
int size = header->len;
struct ethhdr *eth = (struct ethhdr *)buffer;
if(eth->h_proto == 8) //Check if IPv4
{
struct iphdr *iph = (struct iphdr*)(buffer +sizeof(struct ethhdr));
if(iph->protocol == 6) //Check if TCP
{
find_retransmissions(buffer,size);
}
}
}
void find_retransmissions(const u_char * Buffer, int Size)
{
static struct iphdr previous_packets[20000];
static struct tcphdr previous_tcp[20000];
static int index = 0;
static int retransmissions = 0;
int retransmission = 0;
struct sockaddr_in source,dest;
unsigned short iphdrlen;
// IP header
struct iphdr *iph = (struct iphdr *)(Buffer + sizeof(struct ethhdr));
previous_packets[index] = *iph;
iphdrlen =iph->ihl*4;
memset(&source, 0, sizeof(source));
source.sin_addr.s_addr = iph->saddr;
memset(&dest, 0, sizeof(dest));
dest.sin_addr.s_addr = iph->daddr;
// TCP header
struct tcphdr *tcph=(struct tcphdr*)(Buffer
+ iphdrlen
+ sizeof(struct ethhdr));
previous_tcp[index]=*tcph;
index++;
int header_size = sizeof(struct ethhdr) + iphdrlen + tcph->doff*4;
unsigned int segmentlength;
segmentlength = Size - header_size;
/* First check if a same TCP packet has been received */
for(int i=0;i<index-1;i++)
{
// Check if packet has been resent
unsigned short temphdrlen;
temphdrlen = previous_packets[i].ihl*4;
// First check IP header
if ((previous_packets[i].saddr == iph->saddr) // Same source IP address
&& (previous_packets[i].daddr == iph->daddr) // Same destination Ip address
&& (previous_packets[i].protocol == iph->protocol) //Same protocol
&& (temphdrlen == iphdrlen)) // Same header length
{
// Then check TCP header
if((previous_tcp[i].source == tcph->source) // Same source port
&& (previous_tcp[i].dest == tcph->dest) // Same destination port
&& (previous_tcp[i].th_seq == tcph->th_seq) // Same sequence number
&& (previous_tcp[i].th_ack==tcph->th_ack) // Same acknowledge number
&& (previous_tcp[i].th_win == tcph->th_win) // Same window
&& (previous_tcp[i].th_flags == tcph->th_flags) // Same flags
&& (tcph->syn==1 || tcph->fin==1 ||segmentlength>0)) // Check if SYN or FIN are
{ // set or if tcp.segment 0
// At this point the packets are almost identical
// Now Check previous communication to check for retransmission
for(int z=index-1;z>=0;z--)
{
// Find packets going to the reverse direction
if ((previous_packets[z].daddr == iph->saddr) // Swapped IP source addresses
&& (previous_packets[z].saddr ==iph->daddr) // Same for IP dest addreses
&& (previous_packets[z].protocol == iph->protocol)) // Same protocol
{
if((previous_tcp[z].dest==tcph->source) // Swapped ports
&& (previous_tcp[z].source==tcph->dest)
&& (previous_tcp[z].th_seq-1 != tcph->th_ack) // Not Keepalive
&& (tcph->syn==1 // Either SYN is set
|| tcph->fin==1 // Either FIN is set
|| (segmentlength>0)) // Either segmentlength >0
&& (previous_tcp[z].th_seq>tcph->th_seq) // Next sequence number is
// bigger than the expected
&& (previous_tcp[z].ack != 1)) // Last seen ACK is set
{
retransmission = 1;
retransmissions++;
break;
}
}
}
}
}
}
if (retransmission == 1)
{
printf("Retransmission: True\n");
printf("\n\n******************IPv4 TCP Packet*************************\n");
printf(" |-IP Version : %d\n",(unsigned int)iph->version);
printf(" |-Source IP : %s\n" , inet_ntoa(source.sin_addr) );
printf(" |-Destination IP : %s\n" , inet_ntoa(dest.sin_addr) );
printf(" |-Source Port : %u\n", ntohs(tcph->source));
printf(" |-Destination Port : %u\n", ntohs(tcph->dest));
printf(" |-Protocol : %d\n",(unsigned int)iph->protocol);
printf(" |-IP Header Length : %d DWORDS or %d Bytes\n",
(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
printf(" |-Payload Length : %d Bytes\n",Size - header_size);
}
printf("Total Retransmissions: %d\n",retransmissions);
}
Этот подход основан на абзаце вики Wireshark о ретрансляции. Я буквально просмотрел каждую страницу, которую Google может предложить, о том, как подойти к этому анализу, но это было единственное, что я смог найти. Результаты, которые я получаю, несколько правильны, некоторые повторные передачи остаются незамеченными, я получаю много пакетов DUP-ACK, а также проходит некоторый нормальный трафик (проверено с помощью wireshark). Я использую файл smallFlows.pcap, найденный здесь, и я считаю, что результаты, которые я должен получить, должны быть такими же, как
tcp.analysis.retransmission && not tcp.analysis.spurious_retransmission
фильтр в wirehark. Что составляет
88
ретрансляции для этого pcap. Запуск этого кода дает 45, и я не могу понять, почему.
Извините за беспорядочные операторы if, я изо всех сил старался их очистить.
2 ответа
Для обнаружения повторной передачи вы должны отслеживать ожидаемый порядковый номер. Если порядковый номер выше ожидаемого, пакет является повторно переданным ( глава « Анализ TCP» документации wireshark,https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html )
Повторная передача TCP
Установите, если выполняются все следующие условия:
- Это не пакет проверки активности.
- В прямом направлении длина сегмента больше нуля или установлен флаг SYN или FIN.
- Следующий ожидаемый порядковый номер больше текущего порядкового номера
Помимо повторной передачи TCP , существует также ложная повторная передачаTCP и быстрая повторная передача TCP.
В основном повторная передача необходима только в том случае, если пакет потерян. Анализ несогласованности потерянных сегментов:
источник графики: http://www.opentextbooks.org.hk/ditatopic/3578
Для обнаружения этого типа неисправности в wireshark используется фильтр. Может быть, попробовать реализовать это.
В wirehark можно применять несколько фильтров для выявления всех типов несоответствий в порядковых номерах, т.е.
tcp.analysis.retransmission
,
tcp.analysis.spurious_retransmission
а также
tcp.analysis.fast_retransmission
, для общего случая проверки потери пакетов на
tcp.analysis.ack_lost_segment
https://superuser.com/questions/828294/how-can-i-get-the-actual-tcp-sequence-number-in-wireshark
По умолчанию Wireshark и TShark будут отслеживать все сеансы TCP и реализовывать собственную грубую версию Sliding_Windows. Это требует, чтобы диссектор хранил некоторую дополнительную информацию о состоянии и память, но позволяет намного лучше обнаруживать интересные события TCP, такие как повторные передачи. Это позволяет гораздо лучше и точнее измерять потери пакетов и повторные передачи, чем это доступно в любом другом анализаторе протоколов. (Но это все еще не идеально)
Эта функция не должна слишком сильно влиять на требования к памяти во время выполнения Wireshark, но при необходимости ее можно отключить.
Когда эта функция включена, мониторинг скользящего окна внутри Wireshark будет обнаруживать и запускать отображение интересных событий для TCP, таких как:
Повторная передача TCP — происходит, когда отправитель повторно передает пакет после истечения срока подтверждения.
Быстрая повторная передача TCP — происходит, когда отправитель повторно передает пакет до истечения таймера подтверждения. Отправители получают некоторые пакеты, порядковый номер которых больше, чем у подтвержденных пакетов. Отправители должны выполнять быструю повторную передачу при получении 3 дубликатов ACK.
...
источник: https://gitlab.com/wireshark/wireshark/-/wikis/TCP_Analyze_Sequence_Numbers
Концепция повторной передачи проста: данные, которые были отправлены, были отправлены повторно.
В TCP каждый передаваемый байт имеет идентификатор. Если в TCP-сегменте 5 байт (всего лишь гипотетический пример, в реальности, конечно, все больше), то идентификатором первого сегмента является порядковый номер в заголовке TCP, +1 для 2-го сегмента,..., +4 за 5-й.
Получатель, когда он хочет подтвердить байт, он просто отправляет ACK с порядковым номером байта +1. Если получатель хочет подтвердить 5 байтов, как в нашем примере, он подтверждает 5-й байт, который равен . В вашем случае вы делаете этот расчет, чтобы получить следующий ожидаемый порядковый номер.
Затем, чтобы определить, произошла ли повторная передача, вы просто узнаете, отправил ли тот же источник TCP-сегмент с порядковым номером, который ниже ожидаемого.
Скажем, вместо получения
seq_num + 4 + 1
в следующем переданном сообщении TCP вы получили
seq_num
. Это означает, что данный сегмент является повторной передачей предыдущего.
Но значит ли это, что этот TCP-сегмент с повторной передачей содержит только повторные передачи? Нет. Он может содержать повторные передачи из предыдущего сегмента, а также дополнительные байты для следующего сегмента. Вот почему вам нужно подсчитать общее количество байтов в сегментах, чтобы определить, сколько байтов является частью повторных передач, а сколько — частью новой передачи. Как видите, повторная передача TCP не является двоичной для каждого сегмента, а может перекрываться между сегментами. Потому что мы действительно повторно передаем байты. Мы просто храним байты в сегментах для уменьшения накладных расходов TCP-заголовка.
Теперь, что, если вы получили
seq_num + 2 + 1
? Это немного странно, поскольку указывает на то, что предыдущий сегмент был повторно передан только частично. В основном это указывает на то, что он только повторно передает из байта 3. Если сегмент имеет только 3 байта, он повторно передает 3-й, 4-й и 5-й байты (т.е. только байты предыдущего сегмента). Но если в нем, скажем, 10 байт, значит, 6-й, 7-й, 8-й, 9-й и 10-й байты — это новые байты (не переданные повторно).
На мой взгляд, вы можете сказать, что пакет TCP является повторной передачей только тогда, когда он несет байты с идентификаторами, которые были отправлены ранее. Но, как было сказано ранее, это может быть не так, поскольку сегмент может содержать несколько байтов, отправленных ранее, плюс еще больше никогда не отправленных, следовательно, это смесь повторных передач и новых передач.