aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMosder <xmosder12@gmail.com>2026-07-10 22:50:12 +0200
committerMosder <xmosder12@gmail.com>2026-07-10 22:50:12 +0200
commit0e9be0d902b44894ed747cabb82ea2120f2e4b45 (patch)
tree8bbf583d44e135c1f5ccdcbfe113b855be41386a
parent4422f92de05948187eeb0b0067dd947e673f8f1d (diff)
feat: added functionallity for TCP stream reassembly
-rw-r--r--Makefile9
-rw-r--r--analyzer/analyzer.c45
-rw-r--r--analyzer/parser/fight_results.c10
-rw-r--r--analyzer/parser/fight_results.h9
-rw-r--r--analyzer/parser/parser.c19
-rw-r--r--analyzer/parser/parser.h6
-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
13 files changed, 375 insertions, 145 deletions
diff --git a/Makefile b/Makefile
index f298b4d..e5a9c54 100644
--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,15 @@ compile: utils capture analyzer parser
gcc *.o main.c -lpcap -o branal
rm *.o
-utils:
+utils: structures
gcc -c analyzer/utils/fs.c
gcc -c analyzer/utils/time.c
- gcc -c analyzer/utils/pcap.c
+ gcc -c analyzer/utils/tcp.c
+ gcc -c analyzer/utils/tcp_seq_math.c
+
+structures:
+ gcc -c analyzer/utils/structures/hashmap.c
+ gcc -c analyzer/utils/structures/heap.c
capture:
gcc -c analyzer/capture.c
diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c
index 7db377e..2e5caf8 100644
--- a/analyzer/analyzer.c
+++ b/analyzer/analyzer.c
@@ -1,7 +1,32 @@
#include "analyzer.h"
#include "parser/parser.h"
-#include "utils/pcap.h"
+#include "utils/tcp.h"
+
+// loop through packets received from pcap_handle and analyze the TCP stream
+void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) {
+ // read packets from handle
+ struct pcap_pkthdr *packet_data;
+ const byte_t *packet;
+ while (pcap_next_ex(pcap_handle, &packet_data, &packet) == 1) {
+ // get new segment
+ TCPSegment segment = get_segment_from_packet(packet, packet_data);
+
+ // handle the new segment
+ if (handle_segment(stream, segment)) {
+ // if it was added to the stream - parse it
+ size_t parsed_len;
+ size_t n_parsed_data;
+ ParsedData *parsed_data = parse_stream(stream, &parsed_len, &n_parsed_data);
+
+ // if some bytes were parsed - remove them from the stream
+ if (parsed_len > 0)
+ remove_parsed_data(stream, parsed_len);
+
+ // TODO: handle parsed data
+ }
+ }
+}
void analyze_file(char *path) {
// open the file
@@ -14,19 +39,9 @@ void analyze_file(char *path) {
return;
}
- struct pcap_pkthdr *packet_data;
- const byte_t *packet;
- const byte_t *payload;
-
- // read packets from the file
- while (pcap_next_ex(handle, &packet_data, &packet) == 1) {
- int payload_len;
- uint32_t tcp_seq;
- payload = get_payload_from_packet(packet, packet_data, &payload_len, &tcp_seq);
- // TODO: Stream reassembly using TCP sequence number
- int parsed_len;
- parse_stream((char *)payload, payload_len, &parsed_len);
- }
-
+ // analyze the file
+ TCPStream stream = new_stream();
+ analyzer_loop(&stream, handle);
+ destroy_stream(stream);
pcap_close(handle);
}
diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c
index c6ad7cc..1db79ea 100644
--- a/analyzer/parser/fight_results.c
+++ b/analyzer/parser/fight_results.c
@@ -3,16 +3,16 @@
#include <stdio.h>
#include <string.h>
-char *find_fight_results(char *stream, int stream_len, int *length) {
- char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START));
- char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END));
+byte_t *find_fight_results(TCPStream *stream, int *length) {
+ byte_t *start = memmem(stream->data, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START));
+ byte_t *end = memmem(stream->data, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END));
*length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1;
return start;
}
-FightResults parse_fight_results(char *data, int length) {
+FightResults parse_fight_results(byte_t *data, size_t length) {
// TODO: actually parse that data
- printf("len=%d\n%.*s\n----------------\n", length, length, data);
+ printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data);
FightResults tmp;
return tmp;
}
diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h
index 270aba8..b275f2b 100644
--- a/analyzer/parser/fight_results.h
+++ b/analyzer/parser/fight_results.h
@@ -1,6 +1,8 @@
// parser/fight_results.h
// utilities for parsing the fight results data
+#include "../utils/tcp.h"
+
// Payload structure:
// FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END
//
@@ -87,11 +89,10 @@ typedef struct {
// find the first fight results data in stream
// params:
// - stream - stream to find the fight results in
-// - stream_len - length of the stream
// - length - length of the fight results data or -1 if there's no end yet
// returns:
-// pointer to the beggining of fight results data or NULL if there's none
-extern char *find_fight_results(char *stream, int stream_len, int *length);
+// pointer to the beginning of fight results data or NULL if there's none
+extern byte_t *find_fight_results(TCPStream *stream, int *length);
// parse given fight results
// params:
@@ -99,4 +100,4 @@ extern char *find_fight_results(char *stream, int stream_len, int *length);
// - length - length of fight results data
// returns:
// FightResults struct containing parsed data
-extern FightResults parse_fight_results(char *data, int length);
+extern FightResults parse_fight_results(byte_t *data, size_t length);
diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c
index b551d8e..7fb155f 100644
--- a/analyzer/parser/parser.c
+++ b/analyzer/parser/parser.c
@@ -2,14 +2,19 @@
#include <stdlib.h>
-ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) {
+ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) {
// TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data
- if (stream_len > 0) {
- int length;
- char *start = find_fight_results(stream, stream_len, &length);
- if (start != NULL) {
- parse_fight_results(start, length);
- }
+ int length;
+ byte_t *start = find_fight_results(stream, &length);
+ if (start == NULL) {
+ *parsed_len = stream->len;
+ }
+ else if (length == -1) {
+ *parsed_len = 0;
+ }
+ else {
+ parse_fight_results(start, length);
+ *parsed_len = start - stream->data + length;
}
return NULL;
}
diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h
index 18b4b83..088bb5d 100644
--- a/analyzer/parser/parser.h
+++ b/analyzer/parser/parser.h
@@ -3,6 +3,8 @@
#include "fight_results.h"
+#include "../utils/tcp.h"
+
// "generic" type for parsed data
typedef struct {
enum { FIGHT_RESULTS } data_type;
@@ -14,8 +16,8 @@ typedef struct {
// parse a stream of TCP data
// params:
// - stream - the stream to parse
-// - stream_len - length of the stream to parse
// - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet)
+// - n_parsed_data - pointer to the count of parsed data that was retrieved from the stream
// returns:
// pointer to the array of parsed data
-extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len);
+extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data);
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);