Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement userspace tool to display driver status and specify blocklist #53

Merged
merged 1 commit into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ ccflags-y := -std=gnu99 -Wno-declaration-after-statement
KDIR ?= /lib/modules/$(shell uname -r)/build
GIT_HOOKS := .git/hooks/applied

all:
all: kmod vwifi-tool

kmod:
$(MAKE) -C $(KDIR) M=$(shell pwd) modules

vwifi-tool: vwifi-tool.c
$(CC) $(ccflags-y) -o $@ $<

clean:
$(MAKE) -C $(KDIR) M=$(shell pwd) clean
$(RM) vwifi-tool

check: all
@scripts/verify.sh
@scripts/verify.sh
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,70 @@ PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
4 packets transmitted, 4 received, 0% packet loss, time 3058ms
rtt min/avg/max/mdev = 0.054/0.141/0.342/0.117 ms
```
### vwifi-tool
A userspace tool which supports more user-specific utilization for vwifi.
Aiming to provide more flexibility and customization for users of vwifi.
Currently supporting feature:
* display the status of vwifi driver
* Use netlink socket to communicate with vwifi driver allowing user to configure user-specific block list

#### Status checking
We can use `vwifi-tool` to check the status of vwifi driver by executing the following command:
```
$ ./vwifi-tool
```
If vwifi is loaded into kernel, you should see the following output:
```
vwifi status : live
```
Otherwise, vwifi isn't loaded into kernel yet, the output will be:
```
vwifi status : not loaded
```

#### Blocklist test
jserv marked this conversation as resolved.
Show resolved Hide resolved
vwifi also supports blocklist ability to allow some interfaces to block packets from certain interfaces.
We can use `vwifi-tool` to set or unset blocklist for vwifi, multiple options are explained as below
* `-d` : specify the destination interface for a blocklist pair
* `-s` : specify the source interface for a blocklist pair
* `-c` : `1` means to unset the blocklist in vwifi, default as `0`

Set the blocklist pair using vwifi-tool like the following
```
$ ./vwifi-tool -d owl2 -s owl1
```
You should see the following output, including your blocklist which will be sent to vwifi
```
vwifi status : live
blocklist:
owl2 blocks owl1
Configuring blocklist for vwifi...
Message from vwifi: vwifi has received your blocklist
```
Then you can try to do the ping test again
```
$ sudo ip netns exec ns1 ping -c 4 10.0.0.3
```
You should see the following output:
```
PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
--- 10.0.0.3 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3053ms
```
You can adjust the content of your blacklist and load it into vwifi anytime.

If you want to unset the blocklist in vwifi, simply add the option `-c` with vwifi-tool
```
$ ./vwifi-tool -c
```
You'll see the following output
```
vwifi status : live
Unset blocklist for vwifi...
Configuring blocklist for vwifi...
Message from vwifi: vwifi has received your blocklist
```
## Testing environment (virtio)
Below is our testing environment with virtio feature:

Expand Down
191 changes: 191 additions & 0 deletions vwifi-tool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#include <getopt.h>
#include <linux/netlink.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define MAX_PAYLOAD 1024
#define LINE_LENGTH 20
#define MAX_BLOCKLIST_PAIR 5
#define VWIFI_STATUS_FILE "/sys/module/vwifi/initstate"


/* The function aims to check the status of vwifi kernel module */
bool vwifi_status_check()
{
FILE *fp = fopen(VWIFI_STATUS_FILE, "r");
if (!fp) {
printf("vwifi status : not loaded\n");
return false;
}

char read_buf[LINE_LENGTH];
fgets(read_buf, LINE_LENGTH, fp);
read_buf[strcspn(read_buf, "\n")] =
0; /* Remove newline character from string */
if (!strcmp("live", read_buf))
printf("vwifi status : live\n");
else {
printf("vwifi status : %s\n", read_buf);
return false;
}
return true;
}

/* Check if command line options are specified */
bool opt_set(int d, int s, int c)
{
return d || s || c;
}

/* Check whether the number of source interfaces matches with the number of
* destination interfaces */
bool blocklist_pair_check(int src_len, int dest_len)
{
return src_len == dest_len;
}

/* Copy destination and source interface pair into blocklist buffer */
bool blocklist_make(char *blocklist,
char *dest[],
char *src[],
int blocklist_len)
{
for (int i = 0; i < blocklist_len; i++) {
char tmp[LINE_LENGTH] = {'\0'};
snprintf(tmp, LINE_LENGTH, "%s %s %s\n", dest[i], "blocks", src[i]);
if (strlen(tmp) + strlen(blocklist) < NLMSG_SPACE(MAX_PAYLOAD))
strcat(blocklist, tmp);
else {
printf(
"Error: Blocklist size exceeds the maximum size of buffer\n");
return false;
}
}
return true;
}

/* Send blocklist to kernel using netlink socket */
bool blocklist_send(char *blocklist)
{
int sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
if (sock_fd < 0) {
printf("Error: Can't open socket\n");
return false;
}

struct sockaddr_nl src_addr = {
.nl_family = AF_NETLINK,
.nl_pid = getpid(),
};

bind(sock_fd, (struct sockaddr *) &src_addr, sizeof(src_addr));

struct sockaddr_nl dest_addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0,
.nl_groups = 0,
};

struct nlmsghdr *nlh =
(struct nlmsghdr *) calloc(1, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;

strncpy(NLMSG_DATA(nlh), blocklist, NLMSG_SPACE(MAX_PAYLOAD));

struct iovec iov = {
.iov_base = (void *) nlh,
.iov_len = nlh->nlmsg_len,
};

struct msghdr msg = {
.msg_name = (void *) &dest_addr,
.msg_namelen = sizeof(dest_addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};

printf("Configuring blocklist for vwifi...\n");
sendmsg(sock_fd, &msg, 0);

recvmsg(sock_fd, &msg, 0);
printf("Message from vwifi: %s\n", (char *) NLMSG_DATA(nlh));

close(sock_fd);

return true;
}

int main(int argc, char *argv[])
{
/* Get opt arguments from command line to configure blocklist */
char *dest[MAX_BLOCKLIST_PAIR], *src[MAX_BLOCKLIST_PAIR],
blocklist_pair[MAX_BLOCKLIST_PAIR][LINE_LENGTH];
int blocklist_len = 0, dest_len = 0, src_len = 0, clear = 0;
int c;

while ((c = getopt(argc, argv, "d:s:ch")) != -1) {
switch (c) {
case 'd':
dest[dest_len++] = optarg;
break;
case 's':
src[src_len++] = optarg;
break;
case 'c':
clear = 1;
break;
case 'h':
printf(
"vwifi-tool: A userspace tool which supports more "
"user-specific utilization for vwifi\n\n");
printf("Usage:\n\n");
printf("\tvwifi-tool [arguments]\n\n");
printf("The arguments are:\n\n");
printf("\t-d Destination interface name\n");
printf("\t-s Source interface name\n");
printf("\t-c Clear blocklist\n");
return 0;
default:
jserv marked this conversation as resolved.
Show resolved Hide resolved
printf("Invalid arguments\n");
break;
}
}

if (!vwifi_status_check())
exit(1);

/* When no options are specified, simply display the status of vwifi */
if (!opt_set(dest_len, src_len, clear))
return 0;

if (!clear && !blocklist_pair_check(src_len, dest_len)) {
printf("Destination number doesn't match with Source number\n");
exit(1);
}

blocklist_len =
clear ? 0
: (dest_len < MAX_BLOCKLIST_PAIR ? dest_len : MAX_BLOCKLIST_PAIR);

/* Copy blocklist pair into message buffer */
char buffer[NLMSG_SPACE(MAX_PAYLOAD)];
memset(buffer, '\0', sizeof(buffer));

if (!blocklist_make(buffer, dest, src, blocklist_len))
exit(1);

if (!clear)
printf("blocklist:\n%s", buffer);

/* Send blocklist buffer to kernel */
if (!blocklist_send(buffer))
exit(1);

return 0;
}
Loading