From 4e0d9af81fcce2ce4e777cb82a1990d7c2ca7c1e Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Fri, 20 Apr 2018 17:51:17 +0100 Subject: [PATCH] netlink --- README.adoc | 2 +- build-all | 2 +- kernel_module/README.adoc | 28 ++++++------ kernel_module/netlink.c | 78 ++++++++++++++++++++++++++++++++++ kernel_module/user/README.adoc | 3 +- kernel_module/user/netlink.c | 49 +++++++++++++++++++++ 6 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 kernel_module/netlink.c create mode 100644 kernel_module/user/netlink.c diff --git a/README.adoc b/README.adoc index 427dcf3..2e23da4 100644 --- a/README.adoc +++ b/README.adoc @@ -2439,7 +2439,7 @@ Can also be activated with the `panic_on_warn` boot parameter. ==== CONFIG_PROC_EVENTS -Logs proc events such as process creation to a link:https://en.wikipedia.org/wiki/Netlink[netlink socket]. +Logs proc events such as process creation to a link:kernel_module/netlink.c[netlink socket]. We then have a userland program that listens to the events and prints them out: diff --git a/build-all b/build-all index bde125e..895b512 100755 --- a/build-all +++ b/build-all @@ -16,6 +16,6 @@ shift "$(($OPTIND - 1))" for arch in x86_64 arm aarch64; do ./build -a "$arch" -klq if "$gem5"; then - ./build -a "$arch" -g -Gkl + ./build -a "$arch" -Gkl fi done diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index c5112c9..4f6f37a 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -10,37 +10,39 @@ .. link:oops.c[] .. link:warn_on.c[] .. link:warn_on.c[] -. Module utils +. Modules .. link:params.c[] .. link:vermagic.c[] .. link:vermagic_fail.c[] .. link:module_info.c[] +.. Module dependencies +... link:dep.c[] +... link:dep2.c[] . Pseudo filesystems +.. link:anonymous_inode.c[] +.. link:character_device.c[] +.. link:character_device_create.c[] .. link:debugfs.c[] -.. link:procfs.c[] -.. link:sysfs.c[] .. link:fops.c[] .. link:ioctl.c[] -.. link:poll.c[] .. link:mmap.c[] -.. link:anonymous_inode.c[] +.. link:poll.c[] +.. link:procfs.c[] .. link:seq_file.c[] .. link:seq_file_inode.c[] +.. link:sysfs.c[] . Asynchronous -.. link:workqueue_cheat.c[] -.. link:sleep.c[] +.. link:irq.c[] .. link:kthread.c[] .. link:kthreads.c[] .. link:schedule.c[] +.. link:sleep.c[] .. link:timer.c[] .. link:work_from_work.c[] -.. link:irq.c[] -. Module dependencies -.. link:dep.c[] -.. link:dep2.c[] -.. link:character_device.c[] -.. link:character_device_create.c[] +.. link:workqueue_cheat.c[] +. Misc .. link:virt_to_phys.c[] +.. link:netlink.c[] . Utilities .. link:kstrto.c[] . Arch diff --git a/kernel_module/netlink.c b/kernel_module/netlink.c new file mode 100644 index 0000000..76500a2 --- /dev/null +++ b/kernel_module/netlink.c @@ -0,0 +1,78 @@ +/* +https://en.wikipedia.org/wiki/Netlink + +https://stackoverflow.com/questions/3299386/how-to-use-netlink-socket-to-communicate-with-a-kernel-module +*/ + +#include /* usleep_range */ +#include +#include +#include +#include +#include + +/* Socket identifier, matches userland. TODO can be anything? + * Is there a more scalable way to do it? E.g. ioctl device, + * kernel generates one on the fly, then give it back and connect? + * https://stackoverflow.com/questions/32898173/can-i-have-more-than-32-netlink-sockets-in-kernelspace */ +#define NETLINK_USER 31 + +struct sock *nl_sk = NULL; + +static void callback(struct sk_buff *skb) +{ + char readbuf[1024]; + size_t readbuflen; + int pid; + int res; + struct nlmsghdr *nlh; + struct sk_buff *skb_out; + + /* Read user message. */ + nlh = (struct nlmsghdr *)skb->data; + pr_info("kernel received: %s\n", (char *)nlmsg_data(nlh)); + + /* Add an artificial sleep to see what happens when + * multiple requests come in at the same time. + * + * Try this out (it works): + * for i in `seq 16`; do /netlink.out & done */ + usleep_range(1000000, 1000001); + + /* Reply with jiffies. */ + readbuflen = snprintf(readbuf, sizeof(readbuf), "%llu", (unsigned long long)jiffies); + pid = nlh->nlmsg_pid; + skb_out = nlmsg_new(readbuflen, 0); + if (!skb_out) { + pr_err("nlmsg_new\n"); + return; + } + nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, readbuflen, 0); + NETLINK_CB(skb_out).dst_group = 0; + strncpy(nlmsg_data(nlh), readbuf, readbuflen); + res = nlmsg_unicast(nl_sk, skb_out, pid); + if (res < 0) + pr_info("nlmsg_unicast\n"); +} + +static int myinit(void) +{ + struct netlink_kernel_cfg cfg = { + .input = callback, + }; + nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); + if (!nl_sk) { + pr_err("netlink_kernel_create\n"); + return -10; + } + return 0; +} + +static void myexit(void) +{ + netlink_kernel_release(nl_sk); +} + +module_init(myinit); +module_exit(myexit); +MODULE_LICENSE("GPL"); diff --git a/kernel_module/user/README.adoc b/kernel_module/user/README.adoc index e6c33fa..eb623c2 100644 --- a/kernel_module/user/README.adoc +++ b/kernel_module/user/README.adoc @@ -25,5 +25,6 @@ These programs can also be compiled and used on host. ... link:ring0.c[] . Module tests .. link:anonymous_inode.c[] -.. link:poll.c[] .. link:ioctl.c[] +.. link:netlink.c[] +.. link:poll.c[] diff --git a/kernel_module/user/netlink.c b/kernel_module/user/netlink.c new file mode 100644 index 0000000..d96b152 --- /dev/null +++ b/kernel_module/user/netlink.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include + +#define MAX_PAYLOAD 1024 +#define NETLINK_USER 31 + +/* Some of these structs fields must be zeroed. + * We could brute force memset them, but + * TODO determine exactly which, and move into main. */ +int sock_fd; +struct iovec iov; +struct msghdr msg; +struct nlmsghdr *nlh; +struct sockaddr_nl src_addr, dest_addr; + +int main() +{ + sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); + if (sock_fd < 0) { + perror("socket"); + return EXIT_FAILURE; + } + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)); + dest_addr.nl_family = AF_NETLINK; + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); + memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + strcpy(NLMSG_DATA(nlh), "user request"); + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + printf("before sendmsg\n"); + sendmsg(sock_fd, &msg, 0); + printf("after sendmsg\n"); + recvmsg(sock_fd, &msg, 0); + printf("userland received: %s\n", (char *)NLMSG_DATA(nlh)); + close(sock_fd); +}