测试例子代码netlink 内核建立 socket 过程 内核的代码非常简单,这里给出了核心代码,就这么多,接收函数中直接打印了接收到的消息。 #include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <net/sock.h>#include <asm/types.h>#include <linux/netlink.h>#include <linux/skbuff.h>
#define NETLINK_XUX 31 /* testing */
static struct sock *xux_sock = NULL;
// 接收消息的回调函数,接收参数是 sk_buff
static void recv_netlink(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
nlh = nlmsg_hdr(skb); // 取得消息体
printk("receive data from user process: %s", (char *)NLMSG_DATA(nlh)); // 打印接收的数据内容
...
}
int __init init_link(void)
{
struct netlink_kernel_cfg cfg = {
.input = recv_netlink,
};
xux_sock = netlink_kernel_create(&init_net, NETLINK_XUX, &cfg); // 创建内核 socket
if (!xux_sock){
printk("cannot initialize netlink socket");
return -1;
}
printk("Init OK!\n");
return 0;
}
netlink 用户态建立链接和收发信息... // 上面的就省了
#define NETLINK_USER 31 //self defined
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct msghdr msg;
struct iovec iov;
int sock_fd;
int main(int args, char *argv[])
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); // 建立 socket
if(sock_fd < 0)
return -1;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* 当前进程的 pid */
if(bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){ // 和指定协议进行 socket 绑定
perror("bind() error\n");
close(skfd);
return -1;
}
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
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(); //self pid
nlh->nlmsg_flags = 0;
// 拷贝信息到发送缓冲中
strcpy(NLMSG_DATA(nlh), "Hello this is a msg from userspace");
// 构造发送消息体
iov.iov_base = (void *)nlh; //iov -> nlh
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov; // iov 中存放 netlink 消息头和消息数据
msg.msg_iovlen = 1;
printf("Sending message to kernel\n");
int ret = sendmsg(sock_fd, &msg, 0); // 发送消息到内核
printf("send ret: %d\n", ret);
printf("Waiting for message from kernel\n");
/* 从内核接收消息 */
recvmsg(sock_fd, &msg, 0);
printf("Received message payload: %s\n", NLMSG_DATA(nlh)); // 打印接收到的消息
close(sock_fd);
return 0;
}
总结netlink 目前感觉还是一个比较好用的内核和用户空间的交互方式,但是也是有他的使用场景,适合用户空间和内核空间主动交互的场景。 但是在单机场景下,大多数的主动权在用户进程,用户进程写数据到内核,用户进程主动读取内核数据。这两种场景覆盖了内核的绝大多数场景。 在内核要主动的场景下,netlink 就比较适合。我能想到的就是内核数据审计,安全触发等,这类场景下内核可以实时的告知用户进程内核发生的情况。 我是在看 ipvs 的代码时候看到了里面有 netlink 的使用,发现早期 iptables 就是使用 netlink 来下发配置指令的,内核中 netfilter 和 iptables 中还有这部分的代码,今天也顺便下载大致走读了一遍,大家可以搜索 NETLINK 这个关键字来看。但是 iptables 后来的代码中没有使用这样的方式,而是采用了一个叫做 iptc 的库,其核心思路还是使用 setsockops 的方式,最终还是 copy_from_user。不过这种方式对于 iptables 这种配置下发的场景来说还是非常实用的。
|