1
votes

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).

1
I don't understand if you're copying the data and you seem to know (or set) an upper bound, why would you have an array of pointers and mallocing the data, when simply you could just have an array of the structs. Also, an idea would be to make the buffers circular.vmt
The upper bound I set is arbitrary and is in accordance to the pcap size, since I don't know how or even if I can dynamically allocate an array in c. What you say is that I could have an array of structs instead of an array with pointers to structs?Konstantinos Zafeiris
Yes. The upper bound will not change whether you have data allocated for N x T* or N x T. So, simply have static struct iphdr previous_packets[MAX_PACKETS] and static 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 this malloc nonsensevmt
However, if you're effectively only using 6 variables (3 from iphdr and 3 from tcphdr), you could consider defining your own struct which would only hold the necessary data to determine a retransmission, and only store those instead of storing all of the data.vmt
Good Idea, thanks, your suggestion worked and the retransmission control is working properly now, even though it produces wrong results because I am probably comparing wrong values. Do you want to answer the question or should I answer my question?Konstantinos Zafeiris

1 Answers

1
votes

Addressing this question in comments:
"can dynamically allocate an array in c"

Since I do not have the exact struct definitions, let me use approximations of what you have depicted to illustrate how you can "can dynamically allocate an array in c", thus providing "array of structs instead of an array with pointers to structs". The method I am illustrating is actually not an array per se, but conceptually works the same. It is actually an allocated segment of contiguous memory dedicated in this case to 20000 instances of your struct.

I am going to introduce typedef into these examples, but that is an option you can choose to use or not, it is not a requirement...

Given the following for example:

//following defines typedef of struct that can be used to create single instances or pointer instances that can be dynamically allocated to whatever size you need


typedef struct {
  ...
} iphdr_s; 

Now, for example you can change this line:

static struct iphdr * previous_packets[20000];

To this:

static iphdr_s *iphdr = malloc(20000*sizeof(*iphdr));
if(!iphdr)
{
    //handle error 
}

When, at some point the need for iphdr is no longer needed, simply free it:

free(iphdr);

If question on this adaptation, please comment me.

Edit
Address questions in comments regarding using normal struct array, and dynamically allocated pointer to array, and copying elements from one to another of both types:

Following is a simple demo:

typedef struct {
    int val;
} array_s;

int main()//-7,8,6,88,2,-7,10,-5
{
    
    array_s arr1[] = {1,2,3,4};//normal array
    array_s *arr2 = malloc(10*sizeof(*arr2));//pointer to allocated memory
    if(arr2)
    {
        for(int i=0;i<10;i++)
        {
            arr2[i].val = i+1;
        }
        arr1[3] = arr2[7];//copy element from allocated to normal array
    }
    array_s *arr3 = malloc(10*sizeof(*arr3));
    if(arr3)
    {
        arr3[0] = arr2[7];//copy element from allocated to allocated array           
    }
    
    printf("%d\n", arr1[3].val);
    printf("%d\n", arr3[0].val);
    

    return 0;
}