|
| 1 | +# Parallel Firewall |
| 2 | + |
| 3 | +## Objectives |
| 4 | + |
| 5 | +- Learn how to design and implement parallel programs |
| 6 | +- Get experienced at utilizing the POSIX threading API |
| 7 | +- Learn how to convert a serial program into a parallel one |
| 8 | + |
| 9 | +## Statement |
| 10 | + |
| 11 | +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. |
| 12 | + |
| 13 | +**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. |
| 14 | +The firewall will decide if the packets are to be dropped or not and, if not, passes them further. |
| 15 | + |
| 16 | +**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. |
| 17 | +And instead of the network card providing the packets, we'll have a **producer thread** creating these packets. |
| 18 | + |
| 19 | +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. |
| 20 | + |
| 21 | +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. |
| 22 | + |
| 23 | +The purpose of this assignment is to: |
| 24 | + |
| 25 | +- implement the circular buffer, along with synchronization mechanisms for it to work in a multithreaded program |
| 26 | + |
| 27 | +- implement the consumer threads, which consume packets and process them |
| 28 | + |
| 29 | +- provide the log file containing the result of the packet processing |
| 30 | + |
| 31 | +## Support Code |
| 32 | + |
| 33 | +The support code consists of the directories: |
| 34 | + |
| 35 | +- `src/` contains the skeleton for the parallelized firewall and the already implemented serial code in `src/serial.c`. |
| 36 | + You will have to implement the missing parts marked as `TODO` |
| 37 | + |
| 38 | +- `utils/` contains utility files used for debugging and logging. |
| 39 | + |
| 40 | +- `tests/` contains tests used to validate and grade the assignment. |
| 41 | + |
| 42 | +## Implementation |
| 43 | + |
| 44 | +### Firewall Threads |
| 45 | + |
| 46 | +In order to parallelize the firewall we have to distribute the packets to multiple threads. |
| 47 | +The packets will be added to a shared data structure (visible to all threads) by a `producer` thread and processed by multiple `consumer` threads. |
| 48 | +Each `consumer` thread picks a packet from the shared data structure, checks it against the filter function and writes the packet hash together with the drop/accept decision to a log file. |
| 49 | +`consumer` threads stop waiting for new packets from the `producer` thread and exit when the `producer` thread closes the connection to the shared data structure. |
| 50 | + |
| 51 | +The `consumer` threads **must not do any form of busy waiting**. |
| 52 | +When there are new packets that need to be handled, the `consumer` threads must be **notified**. |
| 53 | +**Waiting in a `while()` loop or sleeping is not considered a valid synchronization mechanism and points will be deducted.** |
| 54 | + |
| 55 | +Implement the `consumer` related functions marked with `TODO` in the `src/consumer.c` file. |
| 56 | +**The number of consumer threads will be passed as the 3rd command-line argument** |
| 57 | + |
| 58 | +### Ring Buffers |
| 59 | + |
| 60 | +A ring buffer (or a circular buffer) is a data structure that stores its elements in a circular fixed size array. |
| 61 | +One of the advantages of using such a data structure as opposed to an array is that it acts as a FIFO, without the overhead of moving the elements to the left as they are consumed. |
| 62 | +Thus, the shared ring buffer offers the following fields: |
| 63 | + |
| 64 | +- `write_pos` index in the buffer used by the `producer` thread for appending new packets. |
| 65 | +- `read_pos` index in the buffer used by the `consumer` threads to pick packets. |
| 66 | +- `cap` the size of the internal buffer. |
| 67 | +- `data` pointer to the internal buffer. |
| 68 | + |
| 69 | +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. |
| 70 | +You can use mutexes, semaphores, conditional variables and other synchronization mechanisms offered by the `pthread` library. |
| 71 | + |
| 72 | +You will have to implement the following interface for the ring buffer: |
| 73 | + |
| 74 | +- `ring_buffer_init()`: initialize the ring buffer (allocate memory and synchronization primitives). |
| 75 | +- `ring_buffer_enqueue()`: add elements to the ring buffer. |
| 76 | +- `ring_buffer_dequeue()`: remove elements from the ring buffer. |
| 77 | +- `ring_buffer_destroy()`: free up the memory used by the ring_buffer. |
| 78 | +- `ring_buffer_stop()`: finish up using the ring buffer for the calling thread. |
| 79 | + |
| 80 | +### Log File |
| 81 | + |
| 82 | +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. |
| 83 | +The actual format can be found in the serial implementation (at `src/serial.c`). |
| 84 | + |
| 85 | +When processing the packets in parallel the threads will finish up the work in a non deterministic order. |
| 86 | +The packet processing functions are already implemented in `src/packet.c` |
| 87 | + |
| 88 | +We would like the logs to be sorted by the packet timestamp, the order that they came in from the producer. |
| 89 | +Thus, the `consumers` should insert the packet information to the log file such as the result is ordered by timestamp. |
| 90 | +The printing format can be found in `./src/serial.c` |
| 91 | + |
| 92 | +The logs must be written to the file in ascending order during packet processing. |
| 93 | +**Sorting the log file after the consumer threads have finished processing is not considered a valid synchronization mechanism and points will be deducted.** |
| 94 | + |
| 95 | +## Operations |
| 96 | + |
| 97 | +### Building |
| 98 | + |
| 99 | +To build both the serial and the parallel versions, run `make` in the `src/` directory: |
| 100 | + |
| 101 | +```console |
| 102 | +student@so:~/.../content/assignments/parallel-firewall$ cd src/ |
| 103 | + |
| 104 | +student@so:~/.../assignments/parallel-firewall/src$ make |
| 105 | +``` |
| 106 | + |
| 107 | +That will create the `serial` and `firewall` binaries. |
| 108 | + |
| 109 | +## Testing and Grading |
| 110 | + |
| 111 | +Testing is automated. |
| 112 | +Tests are located in the `tests/` directory. |
| 113 | + |
| 114 | +To test and grade your assignment solution, enter the `tests/` directory and run `grade.sh`. |
| 115 | + |
| 116 | +```console |
| 117 | +student@so:~/.../content/assignments/parallel-firewall$ cd tests/ |
| 118 | +``` |
| 119 | + |
| 120 | +```console |
| 121 | +student@so:~/.../content/assignments/parallel-firewall/tests$ ./grade.sh |
| 122 | +``` |
| 123 | + |
| 124 | +Note that this requires linters being available. |
| 125 | +The easiest way to test the project is to use a Docker-based setup with everything installed and configured (see the [README.checker.md](README.checker.md) file for instructions). |
| 126 | + |
| 127 | +To create the tests, run: |
| 128 | + |
| 129 | +```console |
| 130 | +student@so:~/.../content/assignments/parallel-firewall/tests$ make check |
| 131 | +``` |
| 132 | + |
| 133 | +To remove the tests, run: |
| 134 | + |
| 135 | +```console |
| 136 | +student@so:~/.../content/assignments/parallel-firewall/tests$ make distclean |
| 137 | +``` |
| 138 | + |
| 139 | +When using `grade.sh` you will get a maximum of 90/100 points for general correctness and a maximum of 10/100 points for coding style. |
| 140 | + |
| 141 | +### Restrictions |
| 142 | + |
| 143 | +- Threads must yield the cpu when waiting for empty/full buffers i.e. not doing `busy waiting`. |
| 144 | +- The logs must be written as they are processed and not after the processing is done, in ascending order by the timestamp. |
| 145 | +- 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. |
| 146 | + |
| 147 | +### Grades |
| 148 | + |
| 149 | +- 10 points are awarded for a single consumer solution that also implements the ring buffer |
| 150 | +- 50 points are awarded for a multi consumer solution |
| 151 | +- 30 points are awarded for a multi consumer solution that writes the logs in the sorted manner (bearing in mind the above restrictions) |
| 152 | + |
| 153 | +### Running the Checker |
| 154 | + |
| 155 | +Each test is worth a number of points. |
| 156 | +The maximum grade is `90`. |
| 157 | + |
| 158 | +A successful run will show the output: |
| 159 | + |
| 160 | +```console |
| 161 | +student@so:~/.../assignments/parallel-firewall/tests$ make check |
| 162 | +[...] |
| 163 | +Test [ 10 packets, sort False, 1 thread ] ...................... passed ... 3 |
| 164 | +Test [ 1,000 packets, sort False, 1 thread ] ...................... passed ... 3 |
| 165 | +Test [20,000 packets, sort False, 1 thread ] ...................... passed ... 4 |
| 166 | +Test [ 10 packets, sort True , 2 threads] ...................... passed ... 5 |
| 167 | +Test [ 10 packets, sort True , 4 threads] ...................... passed ... 5 |
| 168 | +Test [ 100 packets, sort True , 2 threads] ...................... passed ... 5 |
| 169 | +Test [ 100 packets, sort True , 4 threads] ...................... passed ... 5 |
| 170 | +Test [ 1,000 packets, sort True , 2 threads] ...................... passed ... 5 |
| 171 | +Test [ 1,000 packets, sort True , 4 threads] ...................... passed ... 5 |
| 172 | +Test [10,000 packets, sort True , 2 threads] ...................... passed ... 5 |
| 173 | +Test [10,000 packets, sort True , 4 threads] ...................... passed ... 5 |
| 174 | +Test [20,000 packets, sort True , 2 threads] ...................... passed ... 5 |
| 175 | +Test [20,000 packets, sort True , 4 threads] ...................... passed ... 5 |
| 176 | +Test [ 1,000 packets, sort False, 4 threads] ...................... passed ... 5 |
| 177 | +Test [ 1,000 packets, sort False, 8 threads] ...................... passed ... 5 |
| 178 | +Test [10,000 packets, sort False, 4 threads] ...................... passed ... 5 |
| 179 | +Test [10,000 packets, sort False, 8 threads] ...................... passed ... 5 |
| 180 | +Test [20,000 packets, sort False, 4 threads] ...................... passed ... 5 |
| 181 | +Test [20,000 packets, sort False, 8 threads] ...................... passed ... 5 |
| 182 | + |
| 183 | +Checker: 90/100 |
| 184 | +``` |
| 185 | + |
| 186 | +### Running the Linters |
| 187 | + |
| 188 | +To run the linters, use the `make lint` command in the `tests/` directory: |
| 189 | + |
| 190 | +```console |
| 191 | +student@so:~/.../assignments/parallel-firewall/tests$ make lint |
| 192 | +[...] |
| 193 | +cd .. && checkpatch.pl -f checker/*.sh tests/*.sh |
| 194 | +[...] |
| 195 | +cd .. && cpplint --recursive src/ tests/ checker/ |
| 196 | +[...] |
| 197 | +cd .. && shellcheck checker/*.sh tests/*.sh |
| 198 | +``` |
| 199 | + |
| 200 | +Note that the linters have to be installed on your system: [`checkpatch.pl`](https://.com/torvalds/linux/blob/master/scripts/checkpatch.pl), [`cpplint`](https://github.com/cpplint/cpplint), [`shellcheck`](https://www.shellcheck.net/). |
| 201 | +They also need to have certain configuration options. |
| 202 | +It's easiest to run them in a Docker-based setup with everything configured. |
| 203 | + |
| 204 | +### Fine-Grained Testing |
| 205 | + |
| 206 | +Input tests cases are located in `tests/in/` and are generated by the checker. |
| 207 | +The expected results are generated by the checker while running the serial implementation. |
| 208 | +If you want to run a single test, use the below commands while in the `src/` directory: |
| 209 | + |
| 210 | +```console |
| 211 | +student@so:~/.../assignments/parallel-firewall/src$ ./firewall ../tests/in/test_<num_packets>.in <output_file> <number_of_consumers> |
| 212 | +``` |
| 213 | + |
| 214 | +Results provided by the serial and parallel implementation must be the same for the test to successfully pass. |
0 commit comments