From b32140f984a0a7ab618a05c59ac87357b7e33169 Mon Sep 17 00:00:00 2001 From: vax-r Date: Fri, 8 Dec 2023 22:55:57 +0800 Subject: [PATCH] Support blocklist in vwifi Use vwifi-tool to display the status of vwifi and provide 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. According to #48, we set blocklist in order to filter packet which is unwanted and discard it when AP is forwarding the package. So far the discarded package are simply being ignored by the program, we may need future improvement to delete them thoroughly. Resolves: #48 --- Makefile | 10 +++- README.md | 59 +++++++++++++++++++++ vwifi-tool.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ vwifi.c | 102 +++++++++++++++++++++++++++++++++++- 4 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 vwifi-tool.c diff --git a/Makefile b/Makefile index f7e4693..1546ba5 100644 --- a/Makefile +++ b/Makefile @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index 6a0ff90..0787a16 100644 --- a/README.md +++ b/README.md @@ -344,7 +344,66 @@ 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 set of tools which supports more utilization for vwifi. +Currently supporting feature: +* display the status of vwifi driver +* Use netlink socket to communicate with vwifi driver to configure block list +#### Status checking +Simply utilize `vwifi-tool` without any options +``` +$ ./vwifi-tool +vwifi status : not loaded +``` +After vwifi driver is loaded into kernel, you should expect the following output +``` +$ ./vwifi-tool +vwifi status : live +``` +#### Blocklist test +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: diff --git a/vwifi-tool.c b/vwifi-tool.c new file mode 100644 index 0000000..f92f4d4 --- /dev/null +++ b/vwifi-tool.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PAYLOAD 1024 +#define LINE_LENGTH 20 +#define MAX_BLOCKLIST_PAIR 5 + +int main(int argc, char *argv[]) +{ + /* Check the status of vwifi kernel module */ + char *kmod_status_file = "/sys/module/vwifi/initstate"; + FILE *fp = fopen(kmod_status_file, "r"); + if (!fp) { + printf("vwifi status : not loaded\n"); + exit(1); + } + char read_buf[LINE_LENGTH]; + fgets(read_buf, LINE_LENGTH, fp); + read_buf[strcspn(read_buf, "\n")] = 0; + if (strcmp("live", read_buf) == 0) + printf("vwifi status : live\n"); + else { + printf("vwifi status : %s\n", read_buf); + exit(1); + } + + /* 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 d_ind = 0, s_ind = 0, clear = 0; + int c; + + while ((c = getopt(argc, argv, "d:s:c")) != -1) { + switch (c) { + case 'd': + dest[d_ind++] = optarg; + break; + case 's': + src[s_ind++] = optarg; + break; + case 'c': + clear = 1; + break; + default: + printf("Invalid arguments\n"); + break; + } + } + + /* When no options are specified, simply display the status of vwifi */ + if (!d_ind && !s_ind && !c) + return 0; + + if (d_ind != s_ind) { + printf("Destination number doesn't match with Source number\n"); + exit(1); + } else if (clear) { + /* When clear bit is set, we'll send a blank blocklist to vwifi so we + * can unset all blocklist pair */ + printf("Unset blocklist for vwifi...\n"); + d_ind = 0; + } else { + for (int i = 0; i < d_ind; i++) { + memset(blocklist_pair[i], '\0', sizeof(blocklist_pair[i])); + snprintf(blocklist_pair[i], sizeof(blocklist_pair[i]), "%s %s %s", + dest[i], "blocks", src[i]); + } + printf("blocklist:\n"); + for (int i = 0; i < d_ind; i++) { + printf("%s\n", blocklist_pair[i]); + } + } + + /* copy blocklist pair into message buffer */ + char buffer[NLMSG_SPACE(MAX_PAYLOAD)]; + memset(buffer, '\0', sizeof(buffer)); + + for (int i = 0; i < d_ind; i++) { + if (strlen(blocklist_pair[i]) + strlen(buffer) + 1 < + NLMSG_SPACE(MAX_PAYLOAD)) { + strcat(buffer, blocklist_pair[i]); + strcat(buffer, "\n"); + } else { + printf( + "Error: Blocklist size exceeds the maximum size of buffer\n"); + exit(1); + } + } + + /* Use netlink socket to communicate with kernel module */ + int sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); + 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; +} diff --git a/vwifi.c b/vwifi.c index f3ffbfd..7f1e370 100644 --- a/vwifi.c +++ b/vwifi.c @@ -14,6 +14,9 @@ #include #include +#include +#include + MODULE_LICENSE("Dual MIT/GPL"); MODULE_AUTHOR("National Cheng Kung University, Taiwan"); MODULE_DESCRIPTION("virtual cfg80211 driver"); @@ -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" */ @@ -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) { + 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"); +} + +static struct netlink_kernel_cfg nl_config = { + .input = blocklist_nl_recv, +}; + /** * enum virtio_vqs - queues for virtio frame transmission and receivement * @@ -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) { @@ -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++; } @@ -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; } @@ -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); } @@ -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(); @@ -2841,6 +2934,12 @@ static int __init vwifi_init(void) goto interface_add; } + nl_sk = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &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; @@ -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);