Netlink套接字是用以实现内核进程和用户进程通信的一种特殊的进程间通信(IPC),从linux 2.2开始引入内核,当时名为AF_NETLINK,旨在提供一种更灵活的内核和用户空间的通信方法,用以替换笨拙的IOCTL.
IOCTL 方式通信,需要定义IOCTL号,而且只能从用户空间到内核空间空间单向通信,相比之下netlink具有一下有点。
- netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST 20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换);
- netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
- 使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖;
- netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
- 内核可以使用 netlink 首先发起会
netlink可以用于用户进程通信,不过一般推荐使用,如果进程之间使用socket通信,一般是 Unix域socket
1 netlink 内核
Netlink协议是一种在RFC3549 (Linux Netlinkasan IP Services Protocol)中定义的进程间通信(Inter Process Communication,IPC)机制,为用户空间和内核以及内核的有些部分之间提供了双向通信信道,是对标准套接字实现的扩展。
netlink 主要实现位于内核 net/netlink/下,主要包含和几个头文件
├── af_netlink.c
├── af_netlink.h
├── diag.c
├── genetlink.c
├── Kconfig
├── policy.c
af_netlink 是比较常用模块,提供创建使用等常用接口
diag 监视模块,用于读写netlink有关信息
genetlink 新的通用api,由于netlink协议最多支持32个协议簇,目前Linux5.10的内核中已经使用其中23个,对于用户需要定制特殊的协议类型略显不够,而且用户还需自行在include/linux/netlink.h中添加簇定义,但有时不方便,为此Linux设计了这种通用Netlink协议簇,用户可在此之上定义更多类型的子协议。Generic Netlink使用NETLINK_GENERIC类型协议簇,同样基于netlink子系统,如下为内核中已经定义的协议
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
#define NETLINK_SMC 22 /* SMC monitoring */#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG#define MAX_LINKS 32
policy 用于向用户空间发布策略
1.1 创建流程
netlink模块的初始化是在系统启动阶段进行的,它位于af_netlink.c 的netlink_proto_init函数,
static int __init netlink_proto_init(void)
{int i;//向内核注册netlink协议int err = proto_register(&netlink_proto, 0);if (err != 0)goto out;#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)err = bpf_iter_register();if (err)goto out;
#endifBUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof_field(struct sk_buff, cb));//申请netlink table MAX_LINKS如上所示为32nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);if (!nl_table)goto panic;//初始化netlink tablefor (i = 0; i < MAX_LINKS; i++) {if (rhashtable_init(&nl_table[i].hash,&netlink_rhashtable_params) < 0) {while (--i > 0)rhashtable_destroy(&nl_table[i].hash);kfree(nl_table);goto panic;}}//初始化NETLINK_USERSOCK类型的套接字netlink_add_usersock_entry();//向系统注册AF_NETLINK协议族sock_register(&netlink_family_ops);//协议命名空间相关初始化,其中会创建/proc/net/netlink文件register_pernet_subsys(&netlink_net_ops);register_pernet_subsys(&netlink_tap_net_ops);/* The netlink device handler may be needed early. */rtnetlink_init();
out:return err;
panic:panic("netlink_init: Cannot allocate nl_table\n");
}
比较关键是nl_table表数组,这个表是整个netlink实现的最关键的一步,每种协议类型占数组中的一项,后续内核中创建的不同种协议类型的netlink都将保存在这个表中,由该表统一维护,如下为其定义和结构
struct netlink_table {struct rhashtable hash;// 哈希表,以pid为key,保存了该协议所有的已绑定传输控制块对象struct hlist_head mc_list;// 组织所有监听该协议的多播数据的传输控制块对象struct listeners __rcu *listeners;// 标记该协议哪些多播组被监听,被监听多播组对应bit为1unsigned int flags;unsigned int groups;//协议支持的最大多播组数量struct mutex *cb_mutex;struct module *module;int (*bind)(struct net *net, int group);void (*unbind)(struct net *net, int group);bool (*compare)(struct net *net, struct sock *sock);int registered;// 标识该协议对象是否已经完成注册
};extern struct netlink_table *nl_table;
extern rwlock_t nl_table_lock; //访问保护锁
rtnetlink_init()创建NETLINK_ROUTE协议类型的netlink,该种类型的netlink才是当初内核设计netlink的初衷,它用来传递网络路由子系统、邻居子系统、接口设置、防火墙等消息。