Skip to content

Commit

Permalink
Support blocklist in vwifi
Browse files Browse the repository at this point in the history
Enable ability to use blocklist to block packets from specified
interface.

Support blocklist in vwifi kernel module, used as interfaces pair such
as "owl2 blocks owl1", allow maximum blocklist size to be 1024 bytes
now and maintained as global content within struct owl_content.
When we detect the packet's source interface and destination interface
is in the blocklist, we discard the packet.

Using userspace program with netlink to communicate with kernel and allow the ability to dynamically alter the blocklist maintaining
in vwifi kernel module.
  • Loading branch information
vax-r committed Dec 17, 2023
1 parent e5eb949 commit ea22051
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ clean:

check: all
@scripts/verify.sh

CC := gcc
BLOCKLIST := blocklist_user

blocklist:
$(CC) ./$(BLOCKLIST).c -o ./$(BLOCKLIST).o
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,39 @@ PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
rtt min/avg/max/mdev = 0.054/0.141/0.342/0.117 ms
```

### Blocklist test
Additionally, vwifi also supports blocklist ability to allow some interfaces to block packets from certain interfaces.
First, compile blocklist user program
```
$ make blocklist
```
Second, prepare your blocklist.txt and use it as the input of blocklist user program.
```
$ cat <path-of-blocklist>
owl2 blocks owl1
owl1 blocks owl2
```
```
$ ./blocklist_user.o <path-of-blocklist>
```
You should expect the following output:
```
<content-of-blocklist>
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.
## Testing environment (virtio)
Below is our testing environment with virtio feature:

Expand Down
90 changes: 90 additions & 0 deletions blocklist_user.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include <linux/netlink.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

int main(int argc, char *argv[])
{
/* Read interface pair from blocklist.txt */
if (argc != 2) {
printf("Error: Unspecified file name\n");
exit(1);
}
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Error: Couldn't open file %s\n", filename);
exit(1);
}

char buffer[NLMSG_SPACE(MAX_PAYLOAD)];
char read_buf[LINE_LENGTH];
memset(buffer, '\0', sizeof(buffer));

for (int i = 1; fgets(read_buf, LINE_LENGTH, fp); i++) {
if (strlen(read_buf) + strlen(buffer) < NLMSG_SPACE(MAX_PAYLOAD))
strcat(buffer, read_buf);
else {
printf(
"Error: Blocklist size exceeds the maximum size of buffer\n");
exit(1);
}
}
fclose(fp);

printf("%s\n", buffer);

int sock_fd = socket(PF_NETLINK, SOCK_RAW, MAX_LINKS);
if (sock_fd < 0) {
printf("Error: Can't open socket\n");
exit(1);
}

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), buffer, 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 0;
}
2 changes: 2 additions & 0 deletions scripts/blocklist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
owl2 blocks owl1
owl1 blocks owl2
102 changes: 101 additions & 1 deletion vwifi.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include <net/cfg80211.h>
#include <uapi/linux/virtio_net.h>

#include <linux/netlink.h>
#include <net/sock.h>

MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("National Cheng Kung University, Taiwan");
MODULE_DESCRIPTION("virtual cfg80211 driver");
Expand Down Expand Up @@ -60,6 +63,7 @@ struct owl_context {
enum vwifi_state state; /**< indicate the program state */
struct list_head vif_list; /**< maintaining all interfaces */
struct list_head ap_list; /**< maintaining multiple AP */
char *blocklist; /**< maintaining the blocklist */
};

/* SME stands for "station management entity" */
Expand Down Expand Up @@ -150,6 +154,80 @@ MODULE_PARM_DESC(station, "Number of virtual interfaces running in STA mode.");
/* Global context */
static struct owl_context *owl = NULL;

/* Blocklist content */
#define MAX_BLACKLIST_SIZE 1024

static struct sock *nl_sk = NULL;

static int blocklist_check(char *dest, char *source)
{
if (!owl->blocklist || !*(owl->blocklist))
return 0;

char *user_input =
kmalloc(sizeof(char) * (strlen(owl->blocklist) + 1), GFP_KERNEL);
strncpy(user_input, owl->blocklist, strlen(owl->blocklist));

char *token = strsep(&user_input, "\n");
while (token != NULL) {
char *blacklist_dest = strsep(&token, " ");
strsep(&token, " ");
char *blacklist_source = token;
if (!strcmp(dest, blacklist_dest) &&
!strcmp(source, blacklist_source)) {
kfree(user_input);
return 1;
}
token = strsep(&user_input, "\n");
}
kfree(user_input);

return 0;
}

static void blocklist_load(char *blist)
{
if (!owl->blocklist) {
pr_info("owl->blocklist have to be kmalloc first\n");
return;
}
memset(owl->blocklist, '\0', MAX_BLACKLIST_SIZE); /* clear the blocklist */
strncpy(owl->blocklist, blist, strlen(blist));
}

static void blocklist_nl_recv(struct sk_buff *skb)
{
struct nlmsghdr *nlh; /* netlink message header */
int pid;
struct sk_buff *skb_out;
char *msg = "vwifi has received your blocklist";
int msg_size = strlen(msg);

nlh = (struct nlmsghdr *) skb->data;

blocklist_load((char *) nlmsg_data(nlh));

/* pid of sending process */
pid = nlh->nlmsg_pid;

skb_out = nlmsg_new(msg_size, 0);
if (!skb_out) {
pr_info("netlink: Failed to allocate new skb\n");
return;
}

nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; /* unicast group */
strncpy(nlmsg_data(nlh), msg, msg_size);

if (nlmsg_unicast(nl_sk, skb_out, pid) < 0)
pr_info("netlink: Error while sending back to user\n");
}

struct netlink_kernel_cfg nl_config = {
.input = blocklist_nl_recv,
};

/**
* enum virtio_vqs - queues for virtio frame transmission and receivement
*
Expand Down Expand Up @@ -713,6 +791,13 @@ static netdev_tx_t owl_ndo_start_xmit(struct sk_buff *skb,
}
/* TX by interface of AP mode */
else if (vif->wdev.iftype == NL80211_IFTYPE_AP) {
/* Find the source interface */
struct owl_vif *src_vif;
list_for_each_entry (src_vif, &vif->bss_list, bss_list) {
if (ether_addr_equal(eth_hdr->h_source, src_vif->ndev->dev_addr))
break;
}

/* Check if the packet is broadcasting */
if (is_broadcast_ether_addr(eth_hdr->h_dest)) {
list_for_each_entry (dest_vif, &vif->bss_list, bss_list) {
Expand All @@ -722,6 +807,10 @@ static netdev_tx_t owl_ndo_start_xmit(struct sk_buff *skb,
dest_vif->ndev->dev_addr))
continue;

/* Don't send packet from dest_vif's blocklist */
if (blocklist_check(dest_vif->ndev->name, src_vif->ndev->name))
continue;

if (__owl_ndo_start_xmit(vif, dest_vif, skb))
count++;
}
Expand All @@ -731,7 +820,9 @@ static netdev_tx_t owl_ndo_start_xmit(struct sk_buff *skb,
list_for_each_entry (dest_vif, &vif->bss_list, bss_list) {
if (ether_addr_equal(eth_hdr->h_dest,
dest_vif->ndev->dev_addr)) {
if (__owl_ndo_start_xmit(vif, dest_vif, skb))
if (!blocklist_check(dest_vif->ndev->name,
src_vif->ndev->name) &&
__owl_ndo_start_xmit(vif, dest_vif, skb))
count++;
break;
}
Expand Down Expand Up @@ -1782,6 +1873,7 @@ static void owl_free(void)
list_for_each_entry_safe (vif, safe, &owl->vif_list, list)
owl_delete_interface(vif);

kfree(owl->blocklist);
kfree(owl);
}

Expand Down Expand Up @@ -2832,6 +2924,7 @@ static int __init vwifi_init(void)
mutex_init(&owl->lock);
INIT_LIST_HEAD(&owl->vif_list);
INIT_LIST_HEAD(&owl->ap_list);
owl->blocklist = kmalloc(sizeof(char) * MAX_BLACKLIST_SIZE, GFP_KERNEL);

for (int i = 0; i < station; i++) {
struct wiphy *wiphy = owcfg80211_add();
Expand All @@ -2841,6 +2934,12 @@ static int __init vwifi_init(void)
goto interface_add;
}

nl_sk = netlink_kernel_create(&init_net, MAX_LINKS, &nl_config);
if (!nl_sk) {
pr_info("Error creating netlink socket\n");
goto cfg80211_add;
}

err = register_virtio_driver(&virtio_vwifi);
if (err)
goto err_register_virtio_driver;
Expand All @@ -2864,6 +2963,7 @@ static void __exit vwifi_exit(void)

unregister_virtio_driver(&virtio_vwifi);
owl_free();
netlink_kernel_release(nl_sk);
}

module_init(vwifi_init);
Expand Down

0 comments on commit ea22051

Please sign in to comment.