<repo_name>fudgemcgroobs/Packet-Sniffer<file_sep>/dispatch.c
#include "dispatch.h"

#include <pcap.h>
#include <signal.h>
#include <stdlib.h>
#include <pthread.h>

#include "analysis.h"

#define MAX_THREAD_NUM 10

pthread_mutex_t muxlock_queue = PTHREAD_MUTEX_INITIALIZER;  //used to change queue by threads and 'dispatch'
struct packet_queue *queue; //queue used to store packets for thread
pthread_t threads[MAX_THREAD_NUM];      //stores threads to be used
pthread_cond_t added;

void * thread_code(void *arg) {
    //struct thread_args *args = (struct thread_args *) arg;    //stores the thread's number (when passed by thread_createh)
    struct packet_queue_elem *tmp;
    //set cancel state enabled so that signal can terminate all threads
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    
    while(1) {
        if(queue->head == NULL) {
            pthread_cond_wait(&added, &muxlock_queue);  //wait for signal from dispatch saying that a packet has been added to the queue
        }
        
        //at this point, the queue is already locked
        //set cancel state disabled so that if thread is processing on termination, will wait until it is done before canceling
        //the reason for this is the need to free the memory allocated to the packet queue element extracted by the current thread
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        tmp = queue->head;  //retrieve the oldest packet in queue
        if(tmp == NULL) {   //just a sanity check - should never be the case...
            queue->tail = NULL;
        } else if(tmp->next == NULL) {
            //the extracted packet queue element was the last one in the queue (head and tail must now not point to anything)
            queue->head = NULL;
            queue->tail = NULL;
        } else {
            //there are elements still in the queue. set head to the next element for the following thread to extract
            queue->head = queue->head->next;
        }
        pthread_mutex_unlock(&muxlock_queue);   //unlock so that other thread can use the queue
        
        //if there was actual packet in queue - another sanity check
        if(tmp != NULL) {
            analyse(tmp->header, tmp->packet, tmp->verbose);
            //free((unsigned char *) tmp->packet);
            free(tmp);  //must free memory allocated in 'dispatch'
            
        }
        //set cancel state back to enabled so that termination cancels thread
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    }
}

void thread_create() {
    //initializing queue that will store packets for threads to process
    queue = malloc(sizeof(struct packet_queue));
    queue->head = NULL;
    queue->tail = NULL;
    unsigned int i;
    for (i = 0; i < MAX_THREAD_NUM; i++) {
        //struct thread_args *args = malloc(sizeof(struct thread_args));
        //args->threadnum = i;
        pthread_create(&threads[i], NULL, &thread_code, NULL);
    }
}


void sig_handler(int signo) {
    //Ctrl + C signal 
    if(signo == SIGINT) {
        unsigned int i;
        for (i = 0; i < MAX_THREAD_NUM; ++i) {
            while(!pthread_cancel(threads[i]));  //terminate regardless of current activity. if the state is disabled, the cancel request is queued and the command returns 0
        }
        
        //free memory used by all packet structures in queue - allocated during execution
        while(queue->head != NULL) {
            if(queue->head->next == NULL) {
                free((unsigned char *) queue->head->packet);    //memory was allocated for the packet data each time a queue element was created - need to free this memory
                free(queue->head);                              //memory is allocated for these packet elements - need to free this memory
                break;
            } else {
                struct packet_queue_elem *tmp = queue->head;
                queue->head = queue->head->next;
                free((unsigned char *) tmp->packet);    //memory was allocated for the packet data each time a queue element was created - need to free this memory
                free(tmp);                              //memory is allocated for these packet elements - need to free this memory
            }
        }
        free(queue);    //memory was allocated for this structure upon creation - need to free 
        pthread_mutex_destroy(&muxlock_queue);  //no longer required because no threads will be using queue and 'dispatch' will not add anymore elemnts
        
        report();       //method in 'analyse' used to output data collected during run
        exit(signo);
    }
}

void dispatch(struct pcap_pkthdr *header, const unsigned char *packet, int verbose) {
    // TODO: Your part 2 code here
    // This method should handle dispatching of work to threads. At present
    // it is a simple passthrough as this skeleton is single-threaded.
    
    //create temporary structure before locking queue to prevent excessive wait time for anything else trying to use queue
    struct packet_queue_elem *tmp = malloc(sizeof(struct packet_queue_elem));   //the new packet to be queued
    tmp->next = NULL;       //this will be tail so the next element must be set to NULL
    tmp->header = header;
    tmp->packet = packet;
    tmp->verbose = verbose;
    
    while(pthread_mutex_trylock(&muxlock_queue));  //wait until the mutex is unlocked
    
    if(queue->head == NULL) {
        //queue is empty so the only element will be the new one, also head and tail at the same time
        queue->head = tmp;
        queue->tail = tmp;
        pthread_cond_signal(&added);    //signal threads that a packet has been added
    } else {
        //there are elements in queue, add to tail, then this will be the new tail
        queue->tail->next = tmp;
        queue->tail = queue->tail->next;
        pthread_cond_signal(&added);    //signal threads that a packet has been added
    }
    pthread_mutex_unlock(&muxlock_queue);    //unlock so that threads can use queue
}<file_sep>/analysis.h
#ifndef CS241_ANALYSIS_H
#define CS241_ANALYSIS_H

#include <pcap.h>

void analyse(struct pcap_pkthdr *header, const unsigned char *packet, int verbose);
void report();  //method inside analysis called at end of execution

#endif<file_sep>/README.md
# Packet Sniffer
## Context 
Coursework for module CS241 Operating Systems and Networks which teachins about the functions of operating systems and different types of networks and protocols.

## Purpose
To develop a packet sniffer to identify potential attacks or recon packets.
The intrusion methods required to be detected were ARP Cache Poisoning, Xmas Tree Scans and Blacklis Violations.
Marks were awarded for coursework which used some model of threading to process received packets.
Implementation is described in coursework report.

## Mark
84% for coursework.
74% module total.
<file_sep>/analysis.c
#include "analysis.h"

#include <pcap.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t muxlock_xmas = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t muxlock_arp = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t muxlock_blacklist = PTHREAD_MUTEX_INITIALIZER;

volatile int xmas_count = 0;
volatile int arp_count  = 0;
volatile int blacklist_count = 0;

void report() {
    printf("\nIntrusion Detection Report:\n");
    printf("%d Xmas scans (host fingerprinting)\n", xmas_count);
    printf("%d  ARP responses (cache poisoning)\n", arp_count);
    printf("%d  URL Blacklist violations\n", blacklist_count);
    
    pthread_mutex_destroy(&muxlock_xmas);
    pthread_mutex_destroy(&muxlock_arp);
    pthread_mutex_destroy(&muxlock_blacklist);
}

void analyse(struct pcap_pkthdr *head, const unsigned char *packet, int verbose) {
    // TODO your part 2 code here
    //Parsing ethernet packet to check the payload type (IP or ARP)
    struct ether_header *eth_header = (struct ether_header*) packet;
    unsigned short eth_type = ntohs(eth_header->ether_type);        //will store type: IP or ARP
    
    if(eth_type == ETHERTYPE_ARP) {
        const unsigned char *arp_data = packet + ETH_HLEN;              //strip of ethernet header
        struct ether_arp *arp_packet = (struct ether_arp *) arp_data;   //parse ARP packet data
        
        //check for ARP poisoning - record any ARP response (op code 2) received
        if(ntohs(arp_packet->ea_hdr.ar_op) == 2) {
            while(pthread_mutex_trylock(&muxlock_arp) != 0);    //wait until no threads editing arp_count
            arp_count++;                                        //count this response for report at end of execution
            pthread_mutex_unlock(&muxlock_arp);                 //unlock so threads can edit arp_count
        }
    } else if(eth_type == ETHERTYPE_IP) {
        const unsigned char *ip_data = packet + ETH_HLEN;   //strip of ethernet header
        struct ip *ip_header = (struct ip *) ip_data;       //parse IP packet data
        int ip_header_len = ip_header->ip_hl * 4;           //ip_hl represents the header length in 32-bit words
        
        const unsigned char *tcp_data = ip_data + ip_header_len;    //strip IP header
        struct tcphdr *tcp_header = (struct tcphdr *) tcp_data;     //parse TCP packet data
        int tcp_header_len = tcp_header->doff * 4;                  //doff represents the header length in 32-bit words
        
        //Xmass scan detection - checking for set flags
        if( (tcp_header->fin == 1) && (tcp_header->psh == 1) && (tcp_header->urg == 1) ) {
            while(pthread_mutex_trylock(&muxlock_xmas) != 0);    //wait until no threads editing xmas_count
            xmas_count++;                                        //count this suspicious packet for report at end of execution
            pthread_mutex_unlock(&muxlock_xmas);                 //unlock so threads can edit xmas_count
        }
        
        //only checking packets passed outward through port 80
        if(ntohs(tcp_header->dest) == 80) {
            const unsigned char *payload = tcp_data + tcp_header_len;   //strip of TCP header
            char *begin = strstr((const char *)payload, "Host: www.bbc.co.uk");       //if communicating with www.bbc.co.uk
            if(begin != NULL) {                                         //if there is a pointer to the locatoion of the point in which the searched string begins in payload
                while(pthread_mutex_trylock(&muxlock_blacklist) != 0);      //wait until no threads editing blacklist_count
                blacklist_count++;                                          //count this blacklist violation for report at end of execution
                pthread_mutex_unlock(&muxlock_blacklist);                   //unlock so threads can edit blacklist_count
            }
        }
    }
    free((unsigned char *) packet);                               //must free memory which was allocated in before dispatch (in sniff)
    //printf("\n");
}<file_sep>/dispatch.h
#ifndef CS241_DISPATCH_H
#define CS241_DISPATCH_H

#include <pcap.h>

struct packet_queue_elem {
    //stores all the packet data required by 'analyse' in 'analysis'
    struct packet_queue_elem *next; //next element in queue; NULL if tail
    struct pcap_pkthdr *header;
    const unsigned char *packet;
    int verbose;
};

struct packet_queue {
    struct packet_queue_elem *head; //for packet extraction by threads
    struct packet_queue_elem *tail; //for packet queuing by dispach
};

struct thread_args {
    //used for debugging purposes. stores thread number for each thread in thraedpool and can be passed to the thread in 'thread_create' in 'dispatch'
    unsigned int threadnum;
};

void dispatch(struct pcap_pkthdr *header, const unsigned char *packet, int verbose);
void thread_create();           //called in 'sniff' to initialize threads and the packet queue used by these
void sig_handler(int signo);    //called in order to terminate. initialized in 'sniff'
#endif
