diff options
Diffstat (limited to 'md/notes/kernel/netlink_socket.md')
-rw-r--r-- | md/notes/kernel/netlink_socket.md | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/md/notes/kernel/netlink_socket.md b/md/notes/kernel/netlink_socket.md new file mode 100644 index 0000000..01e9043 --- /dev/null +++ b/md/notes/kernel/netlink_socket.md @@ -0,0 +1,322 @@ +title: Netlink socket +keywords: kernel,linux,netlink,socket + +# Kernel compile "Hello world" + +Compile minimal linux kernel module. + +## Files + +You need to create to files __Makefile__ and __hello_world.c__. + +__Makefile__ +```Makefile +obj-m += netlink_socket.o + +KDIR ?= /lib/modules/$(shell uname -r)/build + +all: + make -C $(KDIR) M=$(PWD) modules + +clean: + make -C $(KDIR) M=$(PWD) clean + +``` + + +__netlink_socket.c__ +```c +//http://www.tldp.org/LDP/lkmpg/2.4/html/c147.htm +#include <linux/module.h> /* Needed by all modules */ +#include <linux/kernel.h> + +int netlink_socket_init( void ) +{ + printk(KERN_DEBUG "Netlink World!\n"); + return 0; +} + +void netlink_socket_exit( void ) +{ + printk(KERN_DEBUG "Exit Netlink World!\n"); +} + +module_init( netlink_socket_init ); +module_exit( netlink_socket_exit ); + +MODULE_LICENSE("GPL"); +``` + +## Add netlink basic structures + +```c +/* optional Netlink kernel configuration parameters */ +struct netlink_kernel_cfg { + unsigned int groups; + unsigned int flags; + void (*input)(struct sk_buff *skb); + struct mutex *cb_mutex; + int (*bind)(struct net *net, int group); + void (*unbind)(struct net *net, int group); + void (*release) (struct sock *sk, unsigned long *groups); +}; +``` + +```c +... +#include <linux/netlink.h> +#include <linux/skbuff.h> +#include <net/sock.h> + + +#define NETLINK_NEW_SCK 23 //+1 from NETLINK_SMC +struct sock *nlsk = NULL; + +static void netlink_receive(struct sk_buff *skb) { + printk("Receive the message\n"); +} + +struct netlink_kernel_cfg nlsk_cfg = { + .input = netlink_receive, +}; +... +int netlink_socket_init( void ) { + ... + nlsk = netlink_kernel_create(&init_net, NETLINK_NEW_SCK, &nlsk_cfg); + if(nlsk == NULL) + { + printk(KERN_INFO "error creating netlink socket...\n"); + return -1; + } + ... +} +... +``` + +## Add recieve capability for netlink socket +```c +/** + * nlmsg_put - Add a new netlink message to an skb + * @skb: socket buffer to store message in + * @portid: netlink PORTID of requesting application + * @seq: sequence number of message + * @type: message type + * @payload: length of message payload + * @flags: message flags + * + * Returns NULL if the tailroom of the skb is insufficient to store + * the message header and payload. + */ +static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, + int type, int payload, int flags) +``` + +```c +static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 portid); +``` + +The protocol number is choosen +1 after last one that is defined in netlink headers. Protocol number +is 23. + +Protocol with just echo back what was sent. + +```c +... + static void netlink_receive(struct sk_buff *skb) { + struct nlmsghdr *nlh; // Netlink message header + int pid = 0; + + struct sk_buff *skb_out; + char *msg = NULL; + char msg_len = 0; + int ret = 0; + + if (skb == NULL) + { + printk(KERN_ERR "%s: socket buffer is empty...\n", __FUNCTION__); + return; + } + + nlh = (struct nlmsghdr *)skb->data; + pid = nlh->nlmsg_pid; + msg_len = strlen(NLMSG_DATA(nlh)); + msg = NLMSG_DATA(nlh); + printk(KERN_INFO "Received pid: %d, msg: %s, len: %d\n", pid, msg, msg_len); + + // Create a new socket buffer with received message copied: + skb_out = nlmsg_new(msg_len, 0); + if (skb_out == NULL) + { + printk(KERN_ERR "%s: netlink new socket creation failed...\n", __FUNCTION__); + return; + } + nlh = nlmsg_put(skb_out, 0, 0, 0, msg_len, 0); // flag 0, pid kernel 0, seq 0, payload msg_len, flags 0 + strncpy(NLMSG_DATA(nlh), msg, msg_len); + + // Send back the message to app: + ret = nlmsg_unicast(nlsk, skb_out, pid); + if (ret < 0) + { + printk(KERN_ERR "%s: netlink unicast failed...\n", __FUNCTION__); + return; + } + + printk(KERN_INFO "%s: returned...", __FUNCTION__); +} +... +``` + +Add to protocol capitalising lettes, so whats was received is sent back in capitalised letters. + +```c + for (int i=0;i<msg_len; i++) { + if ((msg[i] >= 'a') && (msg[i] <= 'z')) { + msg[i] -= 0x20; + } + } +``` + +Add to module exit function socket release + +```c + void netlink_socket_exit( void ) + { +... + netlink_kernel_release(nlsk); +... + } +``` + +## Create userspace utility + +As netlink is communication interface between kernel and userspace there is possible to +create utility that connects and interacts with netlink socket. + +```c +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <linux/netlink.h> + +#define PAYLOAD_SIZE 1024 /* maximum payload size*/ +#define NETLINK_PROTO 23 + +int main(int argc, char **argv) { + + struct sockaddr_nl src_addr; + struct sockaddr_nl dest_addr; + struct nlmsghdr *nlh; + struct msghdr msg; + struct iovec iov; + int sock_fd; + int ret; + + + sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_PROTO); + if (sock_fd < 0) + { + printf("socket creation for NETLINK failed...\n"); + return -1; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); + + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(PAYLOAD_SIZE)); + memset(nlh, 0, NLMSG_SPACE(PAYLOAD_SIZE)); + nlh->nlmsg_len = NLMSG_SPACE(PAYLOAD_SIZE); + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + + strcpy(NLMSG_DATA(nlh), "Test test Test!"); + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; // Linux Kernel PID + dest_addr.nl_groups = 0; + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ret = sendmsg(sock_fd, &msg, 0); + printf("Message sent, payload: %s\n", (char *)NLMSG_DATA(nlh)); + + // Clear netlink packet payload: + memset(nlh, 0, NLMSG_SPACE(PAYLOAD_SIZE)); + + // Receive msg from kernel: + ret = recvmsg(sock_fd, &msg, 0); + printf("Message received, payload: %s\n", (char *)NLMSG_DATA(nlh)); + + close(sock_fd); + + return 0; +} +``` + +Add to makefile step to compile userspace program + +```makefile +all: + make -C $(KDIR) M=$(PWD) modules + gcc netlink_connect.c -o netlink_connect +``` + +## Compile + +Compile the module and userspace program + +```sh +make +``` + +## Load module + +```sh +sudo insmod netlink_socket.ko +``` + +check that module is running + +```sh +lsmod | grep netlink +``` + +## Unload module + +```sh +rmmod netlink_socket +``` + + +## Test + +Run userspace programm and receive back message with capitalised letters + +```bash +./netlink_connect +Message sent, payload: Test test Test! +Message received, payload: TEST TEST TEST! +``` + +## Links + +1. [https://elixir.bootlin.com/linux/latest/source/include/linux/netlink.h](https://elixir.bootlin.com/linux/latest/source/include/linux/netlink.h) +2. https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/netlink.h#L52 +3. https://gist.github.com/chrizchow/79c53514c80b5994f92f82523d54f67f +4. https://elixir.bootlin.com/linux/v6.6.20/source/include/linux/skbuff.h#L842 +5. https://github.com/mwarning/netlink-examples/tree/master/multicast_example +6. https://github.com/d0u9/examples/tree/master/C/netlink + |