This project is due on Thursday, May 12, at 11:59:59 PM (Eastern daylight time). You must use the submit to turn in your assignment like so: submit cs421_jtang proj2 babbler2.c babbler2-test.c
Your driver code must be named babbler2.c, and it will be compiled against a 4.4 Linux kernel source tree, via the Kbuild supplied below. It must not have any compilation warnings; warnings will result in grading penalties. This module code must be properly indented and have a file header comment, as described on the coding conventions page. Prior to submission, use the kernel's indentation script to reformat your code, like so:
~/linux/scripts/Lindent babbler2.c
In addition, you will write a unit test program, babbler2-test.c, and it will be compiled on Ubuntu 14.04 as follow:
gcc --std=c99 -Wall -O2 -pthread -o babbler2-test babbler2-test.cThere must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, this code must also have a file header comment and be properly indented. You will submit this test code along with your driver code.
In this project, you will write a Linux driver that extends the Babbler system introduced in the first project, by adding support support for networking, multitopics, and privacy.
All instructions hereforth assume you successfully completed the first project. If you have not done so, go back and finish the assignment before proceeding. You have been warned.
To begin, create a directory for your project and download the following files into that directory via wget:
After downloading all of these files, you need to then recompile your kernel:
Now run make to compile everything. Upon success, you should now have the kernel driver babbler2.ko. Run proj2_start.sh (it may ask for your password, as that it runs some sudo commands). This will set up your VM for this project. Rerun this script if you ever need to reboot your VM.
In this part, you will add networking support to your Babbler system. Read the code xt_babblenet.c. When this Netfilter module is installed, via the proj2_start.sh script, it will raise an interrupt every time your computer receives network packets on TCP port 4210. It is your driver's responsibility to handle those interrupts and treat the packets as if a user were writing to /dev/babbler.
In babbler_init()
, install a threaded interrupt handler
for interrupt number BABBLENET_IRQ
. Remove that handler
in
babbler_exit()
. Implement your interrupt handler as
follow:
/** * babblenet_top() - top-half of BabbleNet ISR * @irq: IRQ that was invoked (ignored) * @cookie: Pointer to data that was passed into * request_threaded_irq() (ignored) * * If @irq is BABBLENET_IRQ, then wake up the bottom-half. Otherwise, * return IRQ_NONE. */ static irqreturn_t babblenet_top(int irq, void *cookie); /** * babblenet_bottom() - bottom-half to BabbleNet ISR * @irq: IRQ that was invoked (ignore) * @cookie: Pointer that was passed into request_threaded_irq() * (ignored) * * Fetch the incoming packet, via babblenet_get_babble(), and * overwrite any previously stored babble. As per babbler_write(), * truncate the payload to the first 140 characters. Remember to add * appropriate spin lock calls in this function. * * Note that the incoming payload is NOT a string; you can NOT use * strcpy() or strlen() on it. * * Return: always IRQ_HANDLED */ static irqreturn_t babblenet_bottom(int irq, void *cookie);
As that babblenet_bottom()
will be manipulating your
internal babbler buffer, be sure to guard the code with a spin
lock. (Because the bottom half is not running in interrupt context,
you do not need spin_lock_irqsave()
).
Once you are confident your ISR works,
call babblenet_enable()
in babbler_init()
(and likewise disable network integration
via babblenet_disable()
in babbler_exit()
). Install your module, and
check /proc/interrupts to ensure your ISR was registered.
To test this enhancement, ensure you earlier ran proj2_start.sh. In a second terminal, run this command: telnet localhost 4210. Use this second terminal to send messages to your driver. For example, suppose that you had previously written the topic #cs421 to /dev/babbler_ctl. If you then send this input in the telnet session:
I <3 networked #cs421!Then reading from /dev/babbler should return that babble.
You can close the telnet session by pressing control-] (right bracket) and then entering close.
The next task is to add support for multiple topics. In the first
project, a write to /dev/babbler_ctl overwrite the previous
topic. Now you must modify babbler_ctl_write()
to
append topics to a linked list.
At the top of babbler2.c, declare a a kernel linked list to hold the topics. Then declare a global variable to hold a pointer to the beginning of the linked list. Because this list can be accessed by multiple threads, guard accesses to the list using a spin lock.
Next, modify babbler_ctl_write()
to allocate a new list
entry for each incoming topic. Add that entry to
the global topics list with the list_add_tail()
macro. Assume the user will never add duplicate topics.
Then update the contents of topics_buffer
. For each
topic in the global topics list, write that topic
to topics_buffer
, using newlines to separate each
topic. Do not put a newline after the last topic. Assume that the
entire topic list can fit within a PAGE_SIZE
.
Update babbler_read()
to match against the topics
list. If the babble contains any of the topics, then return it to
the user.
In babbler_exit()
, free all memory that was allocated
for the list.
The final new feature for this project is to add a privacy option. A user can request privacy by prefixing the babble with the "@" symbol followed by digits and then a space, like so:
@1000 I >3 #cs421The number (in this case, 1000) indicates the UID of the recipient. If the UID of the process reading from /dev/babbler does not match the babble's privacy UID, then no babble is returned, regardless if any topics matched or not. Given the above example, only user 1000 can retrieve the babble; all other users would get nothing.
Modify babbler_read()
to support this privacy
option. If the first character is not "@" proceed as
normal. Otherwise, compare the babble's required UID against the
calling process's UID. If the two match, then check against the
topics list and otherwise execute as before. Otherwise, if the UIDs
do not match, return 0 and do not clear the babble.
When a babble begins with "@", you may assume that there
will be at least one digit following, there will be a space after
the final digit, and that the entire number fits within
an unsigned long
. You may not assume
how many digits will be in the UID.
You can retrieve your own UID via the shell command id or
programmatically via getuid()
. Within the kernel, be
aware
of various blog
posts describing the current_uid()
call. Kernel
UIDs have changed recently, and thus older posts are inaccurate. In
modern kernels, you have
to use
the val
field to get the numeric identifier; also,
see
the
kernel code itself.
Now that you have (in theory) a working driver, you must then write
your own unit tests. Read the networking code
in babbler2-test.c. You can
use babblenet_send()
to send arbitrary babbles to the
BabbleNet server. Modify the rest of babbler2-test.c to
exercise all functionality new to this assignment. This includes a
mix of inputs when writing to the device nodes and network socket,
confirming the memory map contents, and verifying that only matched
topics and UIDs are returned.
The unit tests must have comments that explain what things are being tested. As before, your goal is to test boundary conditions of your driver's interfaces. You will be graded based upon the thoroughness of the tests.
You may earn an additional 20% credit for this assignment by
augmenting your babble and topic handling. Whenever a babble is
received, either via babbler_write()
or from BabbleNet,
record the current hour, minute, and
second. Modify babbler_read()
to return the timestamp,
using the format HH:MM:SS, in addition to the babble. For
example, a read from /dev/babbler could return the
following:
20:19:07 #hamlet said: To be or not to be -- that is the #quest ion: 20:19:12 The #slings and arrows of outrageous #fortune 20:19:20 And by opposing end them. #hamlet #act3
In addition, keep track of the most recent appearance of each
topic. For each processed babble, update the time of each matched
topic. Report those times (as ASCII strings) while generating the
contents of topics_buffer
, by appending to each topic a
colon and and then the time (also in HH:MM:SS format). If a
topic has never been used, show its time as merely a dash. For
example, suppose the user had registered these topics:
topics_buffer
would be:
#fortune: 20:19:12 #hamlet: 20:19:20 #macbeth: - #quest: 20:19:07 #slings: 20:19:12You will need to update
topics_buffer
not just in
babbler_ctl_write()
, but also
in babbler_write()
and babblenet_bottom()
.
Afterwards, update babbler2-test.c to test this new functionality.
If you choose to perform this extra credit, put a comment at the top of your file, alerting the grader.