一、netlink机制简介
netlink是一种基于网络的机制,允许在内核内部以及内核与用户之间进行通信。正式定义见RFC3549。手册见netlink(3)和netlink(7)。netlink(3)描述了内核中用于操作、访问、创建netlink数据报的宏。netlink(7)包含了netlink套接字的一般性信息,并给出了这里使用的数据结构的文档。
该机制不仅仅用于网络通信,更重要的用户是通用对象类型。
netlink机制比procfs或sysfs中文件想必有下列优势:
1. 任何一方都不需要轮询,如果使用文件传递状态消息,那么用户层需要不断的检查是否有新消息到达。
2. 系统调用和ioctl也能够从用户态向内核传递信息,但比简单的netlink连接更难与实现
3. 内核可以直接向用户层发送消息,无需用户层事先请求。使用文件也可以做到,但系统调用和ioctl是不可能的。
4. 除了简单的套接字,用户空间应用程序不需要使用其他东西来与内核交互。
二、数据结构
指定地址
类似于其他网络协议,每个netlink套接字都需要分配一个地址。struct sockaddr:
<netlink.h>
struct sockaddr_nl
{
sa_family_t nl_family; /* AF_NETLINK *//*区分内核不同部分使用各个不同的netlink通道*/
unsigned short nl_pad; /* 0 *//**/
__u32 nl_pid; /* 端口ID *//*nl_pid此类套接字唯一标示符,对内核自身来讲,该字段始终是0,用户应用程序通常使用其线程组ID,不强求,可以是任何唯一值*/
__u32 nl_groups; /* 多播组掩码 *//*nl_groups是一个位图,属于该套接字所属的多播地址,如果不允许多播,该字段为0*/
};
消息格式
每个消息由两部分组成:首部和净荷。首部表示为struct nlmsghdr,而净荷可以是任意的。netlink消息的各部分,对齐到NLMSG_ALIGNTO(通常是4)字节边界。查询netlink(3)正确计算边界。一个消息的长度不应该超过一页,这样对内存分配的压力较小。
1 <netlink.h> 2 struct nlmsghdr 3 { 4 __u32 nlmsg_len; /*消息长度,包含首部在内*/ 5 __u32 nlmsg_type; /*消息内容的类型*/ 6 __u32 nlmsg_flags; /*附加的标志*/ 7 __u32 nlmsg_seq; /*序列号*/ 8 __u32 nlmsg_pid; /*发送进程的端口ID*/ 9 };
各种标志可以保存在nlmsg_flags,所有可能的值都在netlink.h中。主要关注两个标志:如果消息包含一个请求,要求执行某个特定的操作(而不是传输一些状态信息),那么NLM_F_REQUEST将置位,而NLM_F_ACK要求在接收到上述消息并成功处理请求后发送一个确认消息。
nlmsg_seq包含一个序列号,表示一系列消息之间在时间上的前后关系。
二、编程接口
netlink套接字既可以从内核打开,也可以从用户层打开。前一种使用了netlink_kernel_create,在后一种情况下,将通过标准的网络编程接口出发netlink_ops的bind方法。
1 net/netlink/af_netlink.c 2 struct sock * 3 netlink_kernel_create(struct net *net, int unit, unsigned int groups,
void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex, struct module *module);
net表示网络命名空间,unit制定所属协议族成员,而input是一个回调函数,在数据到达该套接字将调用input。如果对input指定了NULL指针,那么套接字将只能从内核向用户层传输数据,发过来就不行了。
1 kernel/audit.c 2 3 static void audit_receive_skb(struct sk_buff *skb) 4 { 5 int err; 6 u32 rlen; 7 struct nlmsghdr *nlh; 8 9 while(skb->len >= NLMSG_SPACE(0)) 10 { 11 nlh = nlmsg_hdr(skb); 12 …… 13 rlen = NLMSG_ALIGN(nlh->nlmsg_len); 14 …… 15 if((err = audit_receive_msg(skb, nlh))) 16 { 17 netlink_ack(skb, nlh, err); 18 } 19 else if (nlh->nlmsg_flags & NLM_F_ACK) 20 netlink_ack(skb, nlh, 0); 21 skb_pull(skb, rlen); 22 } 24 }