I want to check for TCP packets retransmission using the libpcap library in C. According to my research the only way to deduce whether a packet is a retransmission or not, I need access to previous traffic behaviour, since one single packet is not enough to reach this conclusion.
So my way to approach the problem was the following:
This is a MRE that reads a pcap file and analyzes any TCP packets over IPv4 if it finds any.
#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>
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);
}
}
}
In the find_retransmissions
function I have created two static arrays of structs of an arbitrary length in order to store previously captured packets' headers, and use them to find possible retransmissions.
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;
int retransmission = 0;
unsigned short iphdrlen;
struct iphdr *iph = (struct iphdr *)(Buffer + sizeof(struct ethhdr));
previous_packets[index] = malloc(sizeof(struct iphdr));
*previous_packets[index] = *iph;
iphdrlen =iph->ihl*4;
struct tcphdr *tcph=(struct tcphdr*)(Buffer + iphdrlen + sizeof(struct ethhdr));
previous_tcp[index]= malloc(sizeof(struct tcphdr));
*previous_tcp[index]=*tcph;
index++;
for(int i=0;i<index-1;i++)
{
if ((previous_packets[i]->saddr == iph->saddr) && (previous_packets[i]->daddr == iph->daddr)
&& (previous_packets[i]->protocol == iph->protocol))
{
if((previous_tcp[i]->source == tcph->source) && (previous_tcp[i]->dest == tcph->dest)
&& (previous_tcp[i]->th_seq == tcph->th_seq))
{
retransmission = 1;
break;
}
}
}
if (retransmission == 1)
{
printf("Retransmission: True");
}
}
Using the pcap file named "smallFlows.pcap" found here.
I am pretty sure this method is more than sub-optimal to do something like this, and I am also open to new suggestions on how to approach this. The current problem with my code, as I think I have understood from debugging, is that in the lines *previous_packets[index] = *iph;
and *previous_tcp[index]=*tcph;
my approach for a "deep" struct copy is failing and, I think, the source struct is overwritten over all elements of the previous_packets
and previous_tcp
arrays respectively. So my actual question is, how can I reliably copy the found packet headers structs to these arrays? I've tried more approaches to deep struct copies I found in SO but to no avail.
The particular pcap contains around 120 TCP retransmissions (checked with wireshark).
static struct iphdr previous_packets[MAX_PACKETS]
andstatic struct tcphdr previous_tcp[MAX_PACKETS]
. You can then simply copy the data as such:previous_packets[index] = *iph
. Same for the other data, avoiding all thismalloc
nonsense – vmt