aboutsummaryrefslogtreecommitdiff
path: root/analyzer/utils
diff options
context:
space:
mode:
Diffstat (limited to 'analyzer/utils')
-rw-r--r--analyzer/utils/pcap.c54
-rw-r--r--analyzer/utils/pcap.h56
-rw-r--r--analyzer/utils/structures/heap.h3
-rw-r--r--analyzer/utils/tcp.c172
-rw-r--r--analyzer/utils/tcp.h68
-rw-r--r--analyzer/utils/tcp_seq_math.c41
-rw-r--r--analyzer/utils/tcp_seq_math.h28
7 files changed, 312 insertions, 110 deletions
diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c
deleted file mode 100644
index d38cbff..0000000
--- a/analyzer/utils/pcap.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "pcap.h"
-
-#include <netinet/if_ether.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
-
-const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) {
- // get Ethernet header
- const struct ether_header *eth_header = (const struct ether_header *)packet;
- int eth_header_len = sizeof(struct ether_header);
-
- // check if it's IP
- if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) {
- *payload_len = -1;
- return NULL;
- }
-
- // get IP header
- const struct ip *ip_header = (const struct ip *)(packet + eth_header_len);
- int ip_header_len = ip_header->ip_hl * 4;
-
- // check if it's TCP
- if (ip_header->ip_p != IPPROTO_TCP) {
- *payload_len = -1;
- return NULL;
- }
-
- // get TCP header
- const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len);
- int tcp_header_len = tcp_header->th_off * 4;
-
- // get TCP sequence number
- *tcp_seq = ntohl(tcp_header->seq);
-
- // get payload
- int total_header_len = eth_header_len + ip_header_len + tcp_header_len;
- *payload_len = packet_data->caplen - total_header_len;
- return packet + total_header_len;
-}
-
-PendingPayload new_pending(const byte_t *payload, int payload_len) {
- PendingPayload tmp;
- return tmp;
-}
-
-byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len) {
- // TODO: impement
- return NULL;
-}
-
-int add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending) {
- // TODO: impement
- return 0;
-}
diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h
deleted file mode 100644
index 10f01f1..0000000
--- a/analyzer/utils/pcap.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// utils/pcap.h
-// helper functions for usage of libpcap and for handling TCP segments in general
-
-#include <pcap.h>
-
-#include "hashmap.h"
-
-typedef unsigned char byte_t;
-
-// get payload from a TCP/IP packet
-// params:
-// - packet - captured packet
-// - packet_data - metadata about packet
-// - payload_len - pointer to save the payload length to or -1 if not a correct packet
-// - tcp_seq - pointer to save the TCP sequence number
-// returns:
-// pointer to the payload or NULL if not a correct packet
-extern const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq);
-
-// struct to persistently store pending payloads
-typedef struct {
- byte_t *payload;
- int payload_len;
-} PendingPayload;
-
-// store persistently and return a PendingPayload, the payload is malloced and needs to be freed
-// params:
-// - payload - payload to store
-// - payload_len - length of the payload to store
-// returns:
-// PendingPayload struct with stored payload
-extern PendingPayload new_pending(const byte_t *payload, int payload_len);
-
-// remove parsed part of the stream from memory and return only unparsed part
-// params:
-// - stream - stream to free a part of
-// - stream_seq - pointer to the current TCP sequence number at the beginning of the stream, updated to the new one in the function
-// - stream_len - pointer to the current stream length, updated to the new length in the function
-// - parsed_len - count of n first bytes of the stream that were parsed
-// returns:
-// pointer to the new, reduced stream
-extern byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len);
-
-// add TCP segment to stream
-// params:
-// - stream - current stream
-// - stream_seq - TCP sequence number at the beginning of the stream
-// - stream_len - current length of the stream
-// - payload - payload to add to stream
-// - payload_seq - payload TCP sequence number
-// - payload_len - length of the payload
-// - pending - hashmap (seq -> PendingPayload) of pending payloads to be added to stream
-// returns:
-// 1 if the payload was added to the stream, 0 if it was discarded or saved to pending
-extern int
-add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending);
diff --git a/analyzer/utils/structures/heap.h b/analyzer/utils/structures/heap.h
index e960fb7..eb3430b 100644
--- a/analyzer/utils/structures/heap.h
+++ b/analyzer/utils/structures/heap.h
@@ -6,6 +6,8 @@
#define INIT_HEAP_DEPTH 7
#define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1
+#ifndef HEAP_DEFINED
+#define HEAP_DEFINED
typedef struct {
void **data;
size_t n_elements;
@@ -14,6 +16,7 @@ typedef struct {
int (*compare)(void *element1, void *element2);
void (*element_cleanup)(void *element);
} Heap;
+#endif
// create a new heap
// params:
diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c
new file mode 100644
index 0000000..56df842
--- /dev/null
+++ b/analyzer/utils/tcp.c
@@ -0,0 +1,172 @@
+#include "tcp.h"
+
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tcp_seq_math.h"
+
+// initalize default TCP segment
+TCPSegment new_segment() {
+ TCPSegment segment;
+ segment.payload = NULL;
+ segment.len = -1;
+ segment.seq = 0;
+ return segment;
+}
+
+// segments compare function to use with heap
+int segments_compare(void *segment1, void *segment2) {
+ TCPSegment *seg1 = segment1;
+ TCPSegment *seg2 = segment2;
+ return seq_before(seg1->seq, seg2->seq);
+}
+
+// segment cleanup function to use with heap
+void segment_cleanup(void *segment) {
+ TCPSegment *seg = segment;
+ free(seg->payload);
+}
+
+TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data) {
+ TCPSegment segment = new_segment();
+
+ // get Ethernet header
+ const struct ether_header *eth_header = (const struct ether_header *)packet;
+ int eth_header_len = sizeof(struct ether_header);
+
+ // check if it's IP
+ if (ntohs(eth_header->ether_type) != ETHERTYPE_IP)
+ return segment;
+
+ // get IP header
+ const struct ip *ip_header = (const struct ip *)(packet + eth_header_len);
+ int ip_header_len = ip_header->ip_hl * 4;
+
+ // check if it's TCP
+ if (ip_header->ip_p != IPPROTO_TCP)
+ return segment;
+
+ // get TCP header
+ const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len);
+ int tcp_header_len = tcp_header->th_off * 4;
+
+ // get TCP sequence number
+ segment.seq = ntohl(tcp_header->seq);
+
+ // get payload
+ int total_header_len = eth_header_len + ip_header_len + tcp_header_len;
+ segment.len = packet_data->caplen - total_header_len;
+ segment.payload = malloc(segment.len);
+ memcpy(segment.payload, packet + total_header_len, segment.len);
+ return segment;
+}
+
+TCPStream new_stream() {
+ TCPStream stream;
+ stream.data = NULL; // NULL to differentiate between first and other segments
+ stream.len = 0;
+ stream.capacity = INIT_STREAM_CAPACITY;
+ stream.seq = 0;
+ stream.pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup);
+ return stream;
+}
+
+void destroy_stream(TCPStream stream) {
+ free(stream.data);
+ heap_free(stream.pending);
+}
+
+void remove_parsed_data(TCPStream *stream, size_t parsed_len) {
+ memmove(stream->data, stream->data + parsed_len, stream->len - parsed_len);
+ stream->len -= parsed_len;
+ stream->seq += parsed_len;
+}
+
+// get the index of the first relevant byte in segment (its tcp sequence number is the one stream is expecting)
+// return the index of the first relevant byte in payload or -1 if segment isn't directly connected to stream
+int get_first_relevant_byte_index(TCPStream stream, TCPSegment segment) {
+ // expected tcp sequence numbers for stream and segment
+ uint32_t stream_expected = stream.seq + stream.len;
+ uint32_t segment_expected = segment.seq + segment.len;
+ if (seq_in_range_ie(stream_expected, segment.seq, segment_expected))
+ return stream_expected - segment.seq;
+ return -1;
+}
+
+// check if the segment is in acceptable range to the stream
+int in_acceptable_range(TCPStream stream, TCPSegment segment) {
+ // expected tcp sequence number for stream and max acceptable sequence number for segment
+ uint32_t stream_expected = stream.seq + stream.len;
+ uint32_t max_acceptable_seq = stream_expected + MAX_SEQ_OFFSET;
+ return seq_in_range_ei(segment.seq, stream_expected, max_acceptable_seq);
+}
+
+// expand stream capacity exponentially to fit new_len
+void expand_stream_capacity(TCPStream *stream, size_t new_len) {
+ while (stream->capacity < new_len)
+ stream->capacity <<= 1;
+ stream->data = realloc(stream->data, stream->capacity);
+}
+
+// add segment directly to stream
+void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) {
+ size_t new_stream_len = stream->len + segment.len - data_start;
+ if (new_stream_len > stream->capacity)
+ expand_stream_capacity(stream, new_stream_len);
+ memcpy(stream->data + stream->len, segment.payload + data_start, segment.len - data_start);
+ stream->len = new_stream_len;
+}
+
+// add pending segments to the stream
+void add_pending_segments(TCPStream *stream) {
+ int top_changed = 1;
+ TCPSegment *top;
+ while (top_changed && (top = heap_top_get(stream->pending))) {
+ top_changed = 0;
+
+ // add pending segment it it became directly connected
+ int data_start = get_first_relevant_byte_index(*stream, *top);
+ if (data_start != -1) {
+ add_segment(stream, *top, data_start);
+ top_changed = 1;
+ }
+
+ // remove top if it was just added or no longer is in acceptable range (e.g. was contained within another one pending)
+ if (top_changed || !in_acceptable_range(*stream, *top)) {
+ heap_top_remove(stream->pending);
+ }
+ }
+}
+
+int handle_segment(TCPStream *stream, TCPSegment segment) {
+ // add the segment if it's the first one
+ if (stream->data == NULL) {
+ stream->data = malloc(stream->capacity);
+ stream->seq = segment.seq;
+ add_segment(stream, segment, 0);
+ free(segment.payload);
+ return 1;
+ }
+
+ // add segment directly to the stream if it's directly connected
+ int data_start = get_first_relevant_byte_index(*stream, segment);
+ if (data_start != -1) {
+ add_segment(stream, segment, data_start);
+ free(segment.payload);
+ add_pending_segments(stream);
+ return 1;
+ }
+
+ // add to pending if segment is in acceptable range:
+ if (in_acceptable_range(*stream, segment)) {
+ heap_insert(stream->pending, &segment);
+ return 0;
+ }
+
+ // if neither, discard entirely
+ free(segment.payload);
+ return 0;
+}
diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h
new file mode 100644
index 0000000..37ab4bc
--- /dev/null
+++ b/analyzer/utils/tcp.h
@@ -0,0 +1,68 @@
+// utils/tcp.h
+// functions for handling TCP streams/segments
+
+#include <pcap.h>
+
+#include "structures/heap.h"
+
+typedef unsigned char byte_t;
+
+// struct to persistently store TCP segments
+// payload field is malloced and needs to be freed
+#ifndef TCP_SEGMENT_DEFINED
+#define TCP_SEGMENT_DEFINED
+typedef struct {
+ byte_t *payload;
+ size_t len;
+ uint32_t seq;
+} TCPSegment;
+#endif
+
+// get TCP segment from captured packet
+// params:
+// - packet - captured packet
+// - packet_data - metadata about packet
+// returns:
+// TCPSegment from packet, payload is NULL and len is -1 if the received packet was incorrect
+extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data);
+
+// struct that stores all the information about stream
+#ifndef TCP_STREAM_DEFINED
+#define TCP_STREAM_DEFINED
+typedef struct {
+ byte_t *data; // bytes in the stream
+ size_t len; // length of the stream
+ size_t capacity; // capacity of the data buffer
+ uint32_t seq; // TCP sequence number of the first byte in the stream
+ Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number)
+} TCPStream;
+#endif
+
+#define INIT_STREAM_CAPACITY 256
+
+// initialize new stream
+// returns:
+// newly created TCPStream structure with initial values
+extern TCPStream new_stream();
+
+// cleanup memory allocations within stream
+// params:
+// stream - stream to destroy
+extern void destroy_stream(TCPStream stream);
+
+// remove parsed data from the stream
+// params:
+// - stream - pointer to the stream to remove a part of
+// - parsed_len - count of n first bytes of the stream that were parsed
+extern void remove_parsed_data(TCPStream *stream, size_t parsed_len);
+
+// max difference between expected TCP seq and received TCP seq to still accept it into pending
+#define MAX_SEQ_OFFSET 100000
+
+// add TCP segment to stream, pending or completely discard it
+// params:
+// - stream - pointer to the current stream
+// - segment - segment to handle
+// returns:
+// 1 if the segment was added to the stream, 0 if it was discarded or saved to pending
+extern int handle_segment(TCPStream *stream, TCPSegment segment);
diff --git a/analyzer/utils/tcp_seq_math.c b/analyzer/utils/tcp_seq_math.c
new file mode 100644
index 0000000..272cb5a
--- /dev/null
+++ b/analyzer/utils/tcp_seq_math.c
@@ -0,0 +1,41 @@
+#include "tcp_seq_math.h"
+
+int seq_after(uint32_t a, uint32_t b) {
+ return (int32_t)(a - b) > 0;
+}
+
+int seq_before(uint32_t a, uint32_t b) {
+ return (int32_t)(a - b) < 0;
+}
+
+int seq_after_eq(uint32_t a, uint32_t b) {
+ return (int32_t)(a - b) >= 0;
+}
+
+int seq_before_eq(uint32_t a, uint32_t b) {
+ return (int32_t)(a - b) <= 0;
+}
+
+int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end) {
+ int after_eq = seq_after_eq(value, range_start);
+ int before_eq = seq_before_eq(value, range_end);
+ return after_eq && before_eq;
+}
+
+int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end) {
+ int after = seq_after(value, range_start);
+ int before = seq_before(value, range_end);
+ return after && before;
+}
+
+int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end) {
+ int after_eq = seq_after_eq(value, range_start);
+ int before = seq_before(value, range_end);
+ return after_eq && before;
+}
+
+int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end) {
+ int after = seq_after(value, range_start);
+ int before_eq = seq_before_eq(value, range_end);
+ return after && before_eq;
+}
diff --git a/analyzer/utils/tcp_seq_math.h b/analyzer/utils/tcp_seq_math.h
new file mode 100644
index 0000000..c4da5f9
--- /dev/null
+++ b/analyzer/utils/tcp_seq_math.h
@@ -0,0 +1,28 @@
+// utils/tcp_seq_math.h
+// helper functions for math regarding tcp sequence numbers which correctly handle overflow
+
+#include <stdint.h>
+
+// check if a is after b
+extern int seq_after(uint32_t a, uint32_t b);
+
+// check if a is before b
+extern int seq_before(uint32_t a, uint32_t b);
+
+// check if a is after or equal to b
+extern int seq_after_eq(uint32_t a, uint32_t b);
+
+// check if a is before or equal to b
+extern int seq_before_eq(uint32_t a, uint32_t b);
+
+// check if value is in [range_start; range_end]
+extern int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end);
+
+// check if value is in (range_start; range_end)
+extern int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end);
+
+// check if value is in [range_start; range_end)
+extern int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end);
+
+// check if value is in (range_start; range_end]
+extern int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end);