From b5da76720f57a331c1491dccb601444b0f0bde7f Mon Sep 17 00:00:00 2001 From: Andrei Stan <andreistan2003@gmail.com> Date: Thu, 14 Nov 2024 00:02:49 +0200 Subject: [PATCH] {README,src}: Update statement and fix linter Signed-off-by: Andrei Stan <andreistan2003@gmail.com> --- README.md | 79 ++++++++++++++++++++++++++++++----------------- src/Makefile | 2 +- src/consumer.c | 4 ++- src/consumer.h | 9 +++--- src/firewall.c | 2 +- src/packet.h | 11 +++++-- src/producer.h | 2 +- src/ring_buffer.h | 2 +- 8 files changed, 69 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index dba22a0..b7b33e4 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,31 @@ ## Statement -A firewall is a program that checks network packets against a series of filters which provide a decision regarding dropping or allowing the packets to continue to the uppper level in the TCP/IP stack. -In the case of this project, instead of a network card, there will be a producer thread that buffers packets into a circular buffer, out of which consumer threads will take packets and process them in order to decide whether they advance to the upper levels of the stack. -You will have to implement the circular buffer and the consumer threads in order to provide a log file with the firewall's decisions ordered by a timestamp. +A firewall is a program that checks network packets against a series of filters which provide a decision regarding dropping or allowing the packets to continue to their intended destination. + +**In a real setup**, the network card will receive real packets (e.g. packets having [`Ethernet`](https://en.wikipedia.org/wiki/Ethernet_frame), [`IP`](https://en.wikipedia.org/wiki/IPv4) headers plus payload) from the network and will send them to the firewall for processing. +The firewall will decide if the packets are to be dropped or not and, if not, passes them further. + +**In this assignment**, instead of real network packets, we'll deal with made up packets consisting of a made up source (a number), a made up destination (also a number), a timestamp (also a number) and some payload. +And instead of the network card providing the packets, we'll have a **producer thread** creating these packets. + +The created packets will be inserted into a [`circular buffer`](https://en.wikipedia.org/wiki/Circular_buffer), out of which **consumer threads** (which implement the firewall logic) will take packets and process them in order to decide whether they advance to the destination. + +The result of this processing is a log file in which the firewall will record the decision taken (PASS or DROP) for each packet, along with other information such as timestamp. + +The purpose of this assignment is to: + +- implement the circular buffer, along with synchronization mechanisms for it to work in a multithreaded program + +- implement the consumer threads, which consume packets and process them + +- provide the log file containing the result of the packet processing ## Support Code The support code consists of the directories: -- `src/` contains the skeleton for the parallelized firewall and the already implemented serial code. +- `src/` contains the skeleton for the parallelized firewall and the already implemented serial code in `src/serial.c`. You will have to implement the missing parts marked as `TODO` - `utils/` contains utility files used for debugging and logging. @@ -37,6 +53,7 @@ When there are new packets that need to be handled, the `consumer` threads must **Waiting in a `while()` loop or sleeping is not considered a valid synchronization mechanism and points will be deducted.** Implement the `consumer` related functions marked with `TODO` in the `src/consumer.c` file. +**The number of consumer threads will be passed as the 3rd command-line argument** ### Ring Buffers @@ -49,12 +66,12 @@ Thus, the shared ring buffer offers the following fields: - `cap` the size of the internal buffer. - `data` pointer to the internal buffer. -Apart from these fields you have to add syncronization primitives in order to allow multiple threads to access the ring buffer in a deterministic manner. +Apart from these fields you have to add synchronization primitives in order to allow multiple threads to access the ring buffer in a deterministic manner. You can use mutexes, semaphores, conditional variables and other synchronization mechanisms offered by the `pthread` library. You will have to implement the following interface for the ring buffer: -- `ring_buffer_init()`: initialize the ring buffer (allocate memory and syncronization primitives). +- `ring_buffer_init()`: initialize the ring buffer (allocate memory and synchronization primitives). - `ring_buffer_enqueue()`: add elements to the ring buffer. - `ring_buffer_dequeue()`: remove elements from the ring buffer. - `ring_buffer_destroy()`: free up the memory used by the ring_buffer. @@ -65,12 +82,15 @@ You will have to implement the following interface for the ring buffer: The output of the firewall will be a log file with the rows containing the firewall's decision, the hash of the packet and its timestamp. The actual format can be found in the serial implementation (at `src/serial.c`). -When parallelly processing the packets the threads will finish up the work in a non deterministic order. +When processing the packets in parallel the threads will finish up the work in a non deterministic order. +The packet processing functions are already implemented in `src/packet.c` + We would like the logs to be sorted by the packet timestamp, the order that they came in from the producer. Thus, the `consumers` should insert the packet information to the log file such as the result is ordered by timestamp. +The printing format can be found in `./src/serial.c` The logs must be written to the file in ascending order during packet processing. -**Sorting the log file after the consumer threads have finshed processing is not considered a valid synchronization mechanism and points will be deducted.** +**Sorting the log file after the consumer threads have finished processing is not considered a valid synchronization mechanism and points will be deducted.** ## Operations @@ -121,7 +141,8 @@ When using `grade.sh` you will get a maximum of 90/100 points for general correc ### Restrictions - Threads must yield the cpu when waiting for empty/full buffers i.e. not doing `busy waiting`. -- The logs must be written as they are processed and not after the processing is done and ordered scendingly by the timestamp. +- The logs must be written as they are processed and not after the processing is done, in ascending order by the timestamp. +- The number of running threads must be at least `num_consumers + 1`, where `num_consumers` is the 3rd command-line argument of the `firewall` binary. ### Grades @@ -139,26 +160,26 @@ A successful run will show the output: ```console student@so:~/.../assignments/parallel-firewall/tests$ make check [...] -Test [ 10 packets, sort False, 1 thread ] ...................... passed ... 3 -Test [ 1,000 packets, sort False, 1 thread ] ...................... passed ... 3 -Test [20,000 packets, sort False, 1 thread ] ...................... passed ... 4 -Test [ 10 packets, sort True , 2 threads] ...................... passed ... 5 -Test [ 10 packets, sort True , 4 threads] ...................... passed ... 5 -Test [ 100 packets, sort True , 2 threads] ...................... passed ... 5 -Test [ 100 packets, sort True , 4 threads] ...................... passed ... 5 -Test [ 1,000 packets, sort True , 2 threads] ...................... passed ... 5 -Test [ 1,000 packets, sort True , 4 threads] ...................... passed ... 5 -Test [10,000 packets, sort True , 2 threads] ...................... passed ... 5 -Test [10,000 packets, sort True , 4 threads] ...................... passed ... 5 -Test [20,000 packets, sort True , 2 threads] ...................... passed ... 5 -Test [20,000 packets, sort True , 4 threads] ...................... passed ... 5 -Test [ 1,000 packets, sort False, 4 threads] ...................... passed ... 5 -Test [ 1,000 packets, sort False, 8 threads] ...................... passed ... 5 -Test [10,000 packets, sort False, 4 threads] ...................... passed ... 5 -Test [10,000 packets, sort False, 8 threads] ...................... passed ... 5 -Test [20,000 packets, sort False, 4 threads] ...................... passed ... 5 -Test [20,000 packets, sort False, 8 threads] ...................... passed ... 5 - +Test [ 10 packets, sort False, 1 thread ] ...................... passed ... 3 +Test [ 1,000 packets, sort False, 1 thread ] ...................... passed ... 3 +Test [20,000 packets, sort False, 1 thread ] ...................... passed ... 4 +Test [ 10 packets, sort True , 2 threads] ...................... passed ... 5 +Test [ 10 packets, sort True , 4 threads] ...................... passed ... 5 +Test [ 100 packets, sort True , 2 threads] ...................... passed ... 5 +Test [ 100 packets, sort True , 4 threads] ...................... passed ... 5 +Test [ 1,000 packets, sort True , 2 threads] ...................... passed ... 5 +Test [ 1,000 packets, sort True , 4 threads] ...................... passed ... 5 +Test [10,000 packets, sort True , 2 threads] ...................... passed ... 5 +Test [10,000 packets, sort True , 4 threads] ...................... passed ... 5 +Test [20,000 packets, sort True , 2 threads] ...................... passed ... 5 +Test [20,000 packets, sort True , 4 threads] ...................... passed ... 5 +Test [ 1,000 packets, sort False, 4 threads] ...................... passed ... 5 +Test [ 1,000 packets, sort False, 8 threads] ...................... passed ... 5 +Test [10,000 packets, sort False, 4 threads] ...................... passed ... 5 +Test [10,000 packets, sort False, 8 threads] ...................... passed ... 5 +Test [20,000 packets, sort False, 4 threads] ...................... passed ... 5 +Test [20,000 packets, sort False, 8 threads] ...................... passed ... 5 + Checker: 90/100 ``` diff --git a/src/Makefile b/src/Makefile index b26921e..30cc2bb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CPPFLAGS := -I$(UTILS_PATH) CFLAGS := -Wall -Wextra CFLAGS += -ggdb -O0 -LDLIBS := -lpthread +LDLIBS := -lpthread SRCS:= ring_buffer.c producer.c consumer.c packet.c $(UTILS_PATH)/log/log.c HDRS := $(patsubst %.c,%.h,$(SRCS)) diff --git a/src/consumer.c b/src/consumer.c index 192d852..4c349d2 100644 --- a/src/consumer.c +++ b/src/consumer.c @@ -26,7 +26,9 @@ int create_consumers(pthread_t *tids, (void) out_filename; for (int i = 0; i < num_consumers; i++) { - /* TODO: Launch consumer threads */ + /* + * TODO: Launch consumer threads + **/ } return num_consumers; diff --git a/src/consumer.h b/src/consumer.h index 891681c..70a911c 100644 --- a/src/consumer.h +++ b/src/consumer.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause */ #ifndef __SO_CONSUMER_H__ #define __SO_CONSUMER_H__ @@ -13,9 +13,8 @@ typedef struct so_consumer_ctx_t { } so_consumer_ctx_t; int create_consumers(pthread_t *tids, - int num_consumers, - so_ring_buffer_t *rb, - const char *out_filename); + int num_consumers, + so_ring_buffer_t *rb, + const char *out_filename); #endif /* __SO_CONSUMER_H__ */ - diff --git a/src/firewall.c b/src/firewall.c index 3b4b4a4..10c7e31 100644 --- a/src/firewall.c +++ b/src/firewall.c @@ -68,7 +68,7 @@ int main(int argc, char **argv) /* start publishing data */ publish_data(&ring_buffer, argv[1]); - /* TODO: wait for child processes to finish execution*/ + /* TODO: wait for child threads to finish execution*/ (void) threads; free(thread_ids); diff --git a/src/packet.h b/src/packet.h index d2dac42..6a45f8a 100644 --- a/src/packet.h +++ b/src/packet.h @@ -1,8 +1,13 @@ -// SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause */ #ifndef __SO_PACKET_H__ #define __SO_PACKET_H__ +/* For pleasing the checkpatch. */ +#ifndef __packed +#define __packed __attribute((__packed__)) +#endif /* __packed */ + #define PKT_SZ 256 typedef enum { @@ -12,13 +17,13 @@ typedef enum { #define RES_TO_STR(decision) ((decision == PASS) ? "PASS" : "DROP") -typedef struct __attribute__((packed)) so_hdr_t { +typedef struct __packed so_hdr_t { unsigned int source; unsigned int dest; unsigned long timestamp; } so_hdr_t; -typedef struct __attribute__((packed)) so_packet_t { +typedef struct __packed so_packet_t { so_hdr_t hdr; char payload[PKT_SZ - sizeof(so_hdr_t)]; } so_packet_t; diff --git a/src/producer.h b/src/producer.h index 7cef1dc..1ee9663 100644 --- a/src/producer.h +++ b/src/producer.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause */ #ifndef __SO_PRODUCER_H__ #define __SO_PRODUCER_H__ diff --git a/src/ring_buffer.h b/src/ring_buffer.h index ca0717f..b3b66af 100644 --- a/src/ring_buffer.h +++ b/src/ring_buffer.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause */ #ifndef __SO_RINGBUFFER_H__ #define __SO_RINGBUFFER_H__ -- GitLab