最近在学习linux netlink相关内容,发现这是一个用户空间和内核空间通信的好办法,于是写一个demo体验一下,一下代码基于linux kernel 3.19。
kernel mode的代码netlink_demo_kmode.c如下:
----------------------------------------------------------------------------------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/net_namespace.h>
#include <linux/netlink.h>
#include <net/sock.h>
#define NETLINK_TEST 20
static struct sock *netlink_test_sk;
static void netlink_test_rcv(struct sk_buff *skb)
{
struct sk_buff *skb_in, *skb_out;
struct nlmsghdr *nlh;
char message[32] = {0};
int seq, pid;
unsigned int datalength;
skb_in = skb_get(skb);
if (skb_in->len >= nlmsg_total_size(0)) {
nlh = nlmsg_hdr(skb_in);
pid = nlh->nlmsg_pid;
seq = nlh->nlmsg_seq;
printk("message received from process %d: %s\n", pid, (char*)NLMSG_DATA(nlh));
sprintf(message, "hello, process %d", pid);
datalength = strlen(message) + 1;
skb_out = nlmsg_new(NLMSG_LENGTH(datalength), GFP_KERNEL);
nlh = nlmsg_put(skb_out, pid, seq, 0, NLMSG_ALIGN(datalength), 0);
memcpy(nlmsg_data(nlh), message, datalength);
netlink_unicast(netlink_test_sk, skb_out, pid, MSG_DONTWAIT);
}
kfree_skb(skb_in);
}
int __init netlink_test_init(void)
{
struct netlink_kernel_cfg cfg = {
.groups
= 0,
.input
= netlink_test_rcv,
};
netlink_test_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
if (!netlink_test_sk)
return -ENOMEM;
return 0;
}
void __exit netlink_test_exit(void)
{
netlink_kernel_release(netlink_test_sk);
}
module_init(netlink_test_init);
module_exit(netlink_test_exit);
MODULE_LICENSE("GPL");
----------------------------------------------------------------------------------------------------------------
Makefile内容如下:
----------------------------------------------------------------------------------------------------------------
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
rm -rf *.o *.mod.c *.mod.o *.symvers *.order
clean:
rm -rf *.ko
.PHONY: clean
else
# called from kernel build system: just declare what our modules are
obj-m := netlink_demo_kmode.o
endif
----------------------------------------------------------------------------------------------------------------
User mode的代码netlink_demo_umode.c如下:
----------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#define NETLINK_TEST 20
#define MSG_LEN 100
int main(int argc, char* argv[])
{
char *data = "hello, kernel";
struct sockaddr_nl local, dest;
int skfd, ret;
unsigned int datalength, addrlength;
struct nlmsghdr *msg_out, *msg_in;
skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
if(skfd < 0){
perror("create socket failed");
return 0;
}
memset(&local, 0, sizeof(struct sockaddr_nl));
local.nl_family = AF_NETLINK;
local.nl_pid = getpid();
local.nl_groups = 0;
if(bind(skfd, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) != 0){
perror("bind failed");
close(skfd);
return 0;
}
memset(&dest, 0, sizeof(struct sockaddr_nl));
dest.nl_family = AF_NETLINK;
dest.nl_pid = 0;
dest.nl_groups = 0;
datalength = strlen(data) + 1;
msg_out = (struct nlmsghdr*)malloc(NLMSG_SPACE(datalength));
memset(msg_out, 0, NLMSG_SPACE(datalength));
msg_out->nlmsg_len = NLMSG_SPACE(datalength);
msg_out->nlmsg_flags = 0;
msg_out->nlmsg_type = 0;
msg_out->nlmsg_seq = 0;
msg_out->nlmsg_pid = local.nl_pid;
memcpy(NLMSG_DATA(msg_out), data, strlen(data));
ret = sendto(skfd, msg_out, msg_out->nlmsg_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr_nl));
if(!ret){
perror("send failed");
free(msg_out);
close(skfd);
return 0;
}
free(msg_out);
msg_in = (struct nlmsghdr*)malloc(NLMSG_SPACE(MSG_LEN));
memset(msg_in, 0, NLMSG_SPACE(MSG_LEN));
addrlength = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, msg_in, NLMSG_SPACE(MSG_LEN), 0, (struct sockaddr*)&dest, &addrlength);
if(!ret){
perror("recv failed");
free(msg_in);
close(skfd);
return 0;
}
printf("message received from kernel: %s\n", (char*)NLMSG_DATA(msg_in));
free(msg_in);
close(skfd);
return 0;
}
分别编译kernel mode和user mode的代码,得到netlink_demo_kmode.ko和netlink_demo_umode两个文件,用insmod命令加载netlink_demo_kmode.ko后,运行netlink_demo_umode,效果如下:
/ # insmod netlink_demo_kmode.ko
/ # ./netlink_demo_umode
[ 14.258242] message received from process 951: hello, kernel
message received from kernel: hello, process 951