linux 内核一直存在的一个严重问题就是内核态和用户态的交互的问题,对于这个问题内核大佬们一直在研究各种方法,想让内核和用户态交互能够安全高效的进行。如系统调用,proc,sysfs 等内存文件系统,但是这些方式一般都比较简单,只能在用户空间轮询访问内核的变化,内核的变化无法主动的推送出来。
Netlink 套接字接口最初是 Linux 内核 2.2 引入的,名为AF_NETLINK
套接字,提供一种更为灵活的用户空间进程与内核间通信方法,替代IOCTL。它运行模型非常简单,只需要使用套接字 API 打开并注册一个 Netlink 套接字,它就会处理与内核 Netlink 套接字的双向通信。
目前 netlink 的这种机制被广泛使用在各种场景中,在 Linux 内核中使用 netlink 进行应用与内核通信的应用很多;包括:路由 daemon(NETLINK_ROUTE),用户态 socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),netfilter 子系统(NETLINK_NETFILTER),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)等。
优势:
(1)使用 netlink 套接字时不需要轮询,用空间应用程序打开套接字,再调用 recvmsg()
,如果没有来自内核的消息,就进入阻塞状态;
(2)内核可以主动向用户空间发送异步消息,而不需要用户空间来触发;
(3)netlink 套接字支持组播传输。
(4)要在用户空间中创建 netlink 套接字,可使用系统调用 socket()
,netlink 套接字就可以是 SOCK_RAW
套接字,也可以是 SOCK_DGRAM
套接字。
为什么选择netlink套接字?
- netlink 套接字支持多播,且一个进程可以将消息多播到netlink地址组。
- netlink 提供BSD套接字样式的API。
- netlink 套接字是异步的,并提供套接字的消息排队规则。
- 要支持任何新的特征,只需要实现对应的协议类型。
Netlink 协议是一种在 RFC 3549 中定义的进程间通信机制,为用户空间和内核以及内核的有些部分之间提供双向通信信道,是对标准套接字实现的扩展。Netlink 协议实现基本是位于net/netlink
下,包含4个文件:
事实上最常用的是 af_netlink
模块,它提供 netink 内核套接字 APl,而 genetlink
模块提供了新的通用 netlink API。监视接口模块 diag(diag.c
)提供的API用于读写有关的 netlink 套接字信息。
从理论原则来讲,netlink 套接字可用于在用户空间进程间通信(包括发送组播消息),但通常不这样做,而且这也不是开发 netlink 套接字初衷。UNIX 域套接字提供用于进程间通信(IPC) 的 API,被广泛用于两个用户空间进行间的通信。
一般来说用户空间和内核空间的通信方式有三种:/proc
、ioctl
、Netlink
。而前两种都是单向的,而 Netlink 可以实现双工通信。
内核和用户空间中创建 Netlink 套接字
netlink 数据结构
1、创建 netlink套接字源码、注册操作、数据包发送操作
在内核网络栈中,可创建 netlink 套接字如下:
使用 netlink 内核套接字,首先需要注册它:
数据包的实际发送工作是由通用 Netlink 方法nlmsg_notify()
完成:
Netlink消息必须采用特定的格式,此格式是在RFC 3549中进行规定的。Netlink消息 的开头是长度固定的 Netlink报头,其后是有效载荷。
netlink 数据包的开头都是由结构 nlmsghdr
表示 netlink 消息报头,整个长度为16
字节,包括5
个字段如下:
路由选择中添加/删除路由选择条目:
添加:ip route add 参数
;
删除:ip route del ip地址
使用 iproute2
命令ip来监视网络事件:ip monitor route
创建和发送通用 Netlink 消息:
通用 Netlink 消息的开头是一个 Netlink 报头,接下来是通用 Netlink 消息报头以及可选的用户特定报头,然后是可选的有效载荷,内核通用 Netlink 消息 genlmsghdr 结构源码如下
套接字监视接口
netlink 套接字sock_diag
提供一个基于 netlink 子系统,可用于获取有关套接字的信息,在内核中添加它是指在Linux用户空间中支持检查点/恢复功能(CRIU)。要支持这项功能,还需要有关套接字的其他数据。创建netlink内核套接字sock_diag
,具体源码如下
/****************************************
* Author: vico
* Date: 2021-06-26
* Filename: netlink_test.c
* Descript: netlink of kernel
* Kernel: 3.10.0-327.22.2.el7.x86_64
* Warning:
******************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>
#define NETLINK_TEST 30
#define MSG_LEN 125
#define USER_PORT 100
MODULE_LICENSE("GPL");
MODULE_AUTHOR("vico");
MODULE_DESCRIPTION("netlink protocol example");
struct sock *nlsk = NULL;
extern struct net init_net;
int send_usrmsg(char *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh;
int ret;
/* 创建sk_buff 空间 */
nl_skb = nlmsg_new(len, GFP_ATOMIC);
if(!nl_skb)
{
printk("\nError:netlink alloc failure.\n\n");
return -1;
}
/* 设置netlink消息头部 */
nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
if(nlh == NULL)
{
printk("\nError:nlmsg_put failaure. \n\n");
nlmsg_free(nl_skb);
return -1;
}
/* 拷贝数据发送 */
memcpy(nlmsg_data(nlh), pbuf, len);
ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
return ret;
}
static void netlink_rcv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL;
char *umsg = NULL;
char *kmsg = "Hello vico users program.";
if(skb->len >= nlmsg_total_size(0))
{
nlh = nlmsg_hdr(skb);
umsg = NLMSG_DATA(nlh);
if(umsg)
{
printk("kernel recv from user: %s\n", umsg);
send_usrmsg(kmsg, strlen(kmsg));
}
}
}
struct netlink_kernel_cfg cfg = {
.input = netlink_rcv_msg, /* set recv callback */
};
int test_netlink_init(void)
{
/* create netlink socket */
nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
if(nlsk == NULL)
{
printk("\nError:netlink_kernel_create error !\n");
return -1;
}
printk("\ntest_netlink_init\n");
return 0;
}
void test_netlink_exit(void)
{
if (nlsk){
netlink_kernel_release(nlsk); /* release ..*/
nlsk = NULL;
}
printk("test_netlink_exit!\n");
}
module_init(test_netlink_init);
module_exit(test_netlink_exit);
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#define NETLINK_TEST 30
#define MSG_LEN 125
#define MAX_PLOAD 125
typedef struct _user_msg_info
{
struct nlmsghdr hdr;
char msg[MSG_LEN];
} user_msg_info;
int main(int argc, char **argv)
{
int skfd;
int ret;
user_msg_info u_info;
socklen_t len;
struct nlmsghdr *nlh = NULL;
struct sockaddr_nl saddr, daddr;
char *umsg = "Hello Netlink protocol.";
/* 创建NETLINK socket */
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if(skfd == -1)
{
perror("\nError:Create socket error.\n");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK; //AF_NETLINK
saddr.nl_pid = 100; //端口号(port ID)
saddr.nl_groups = 0;
if(bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0)
{
perror("\nError:bind() error.\n");
close(skfd);
return -1;
}
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = saddr.nl_pid; //self port
memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
if(!ret)
{
perror("\nError:sendto error.\n");
close(skfd);
exit(-1);
}
printf("\nApplication-->Send kernel:%s\n\n", umsg);
memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&daddr, &len);
if(!ret)
{
perror("\nError:recv form kernel error.\n");
close(skfd);
exit(-1);
}
printf("\nApplication-->From kernel:%s\n\n", u_info.msg);
close(skfd);
free((void *)nlh);
return 0;
}
首先将编译出来的Netlink内核模块插入到系统当中(insmod netlink_test.ko):insmod netlink_test.ko
报错:insmod: ERROR: could not insert module netlink_test.ko: Operation not permitted
解决方案:http://t.csdnimg.cn/UhMSF
接着运行应用程序:./a.out
https://mp.weixin.qq.com/s/cJ8asff7eCVeFm2L4B3tLw
https://www.cnblogs.com/x_wukong/p/5920437.html
https://zhuanlan.zhihu.com/p/487961245
https://zhuanlan.zhihu.com/p/694289016