linux 内核中Netlink

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 内核中Netlink相关的知识,希望对你有一定的参考价值。

目录

套接字Netlink地址 sockaddr_nl 

协议簇

常使用的宏 

内核常用的函数

创建流程

Netlink套接字

uevent内核事件

套接字监视接口

demo


Netlink套接字接口最初是Linux内核2.2引入的,作用用户空间进程与内核间通信方法。

相对于ioctl,sysfs,proc的优势

优势:

  • IOCTL处理程序不能从内核向用户空间发送异步消息,而Netlink套接字则可以。
  • 用户与内核间的通信方式,不需要轮询,用户空间应用程序打开套接字,调用recvmsg(),如果没有来自内核的消息,就进入阻塞状态。
  • 内核可以主动向用户空间发送异步消息,而不需要用户空间来触发。
  • 支持组播传输。

命令iproute2包含命令(ip tc ss lnstat bridge)主要使用netlink套接字从用户空间向内核空间发送请求并获得应答。

套接字Netlink地址 sockaddr_nl 

struct sockaddr_nl 
	__kernel_sa_family_t	nl_family;	/* AF_NETLINK	*/
	unsigned short	nl_pad;		/* zero		*/
	__u32		nl_pid;		/* port ID	*/
       	__u32		nl_groups;	/* multicast groups mask */
;

消息头 

struct nlmsghdr 
	__u32		nlmsg_len;	/* Length of message including header */
	__u16		nlmsg_type;	/* Message content */
	__u16		nlmsg_flags;	/* Additional flags */
	__u32		nlmsg_seq;	/* Sequence number */
	__u32		nlmsg_pid;	/* Sending process port ID */
;

协议簇

#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 NLMSG_ALIGNTO	4U
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
#define NLMSG_HDRLEN	 ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
#define NLMSG_NEXT(nlh,len)	 ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \\
				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \\
			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \\
			   (nlh)->nlmsg_len <= (len))
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

内核常用的函数

//内核创建socket
static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)


//nlmsg_new - Allocate a new netlink message
static inline struct sk_buff *nlmsg_new(size_t payload, gfp_t flags)


//Add a new netlink message to an skb
static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
					 int type, int payload, int flags)


//单播
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);


//多播
extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
                 __u32 group, gfp_t allocation);

创建流程

Netlink套接字

在内核网络栈中,可以创建多种Netlink套接字,每种套接字处理不同类型消息。如NETLINK_ROUTE消息的套接字创建过程

static int __net_init rtnetlink_net_init(struct net *net)

	struct sock *sk;
	struct netlink_kernel_cfg cfg = 
		.groups		= RTNLGRP_MAX,
		.input		= rtnetlink_rcv,
		.cb_mutex	= &rtnl_mutex,
		.flags		= NL_CFG_F_NONROOT_RECV,
		.bind		= rtnetlink_bind,
	;

	sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
	if (!sk)
		return -ENOMEM;
	net->rtnl = sk;
	return 0;
  • 在netlink_kernel_create种第二个参数 NETLINK_ROUTE表示rtnetlink消息,此外还有NETLINK_XFRM表示IPsec子系统,NETLINK_AUDIT表示审计子系统。
  • 成员input函数用于指定回调函数,rtnetlink_rcv用于接收用户空间的信息。

uevent内核事件

只需要从内核向用户发送数据即可,初始化为

static int uevent_net_init(struct net *net)

	struct uevent_sock *ue_sk;
	struct netlink_kernel_cfg cfg = 
		.groups	= 1,
		.input = uevent_net_rcv,
		.flags	= NL_CFG_F_NONROOT_RECV
	;

	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
	if (!ue_sk)
		return -ENOMEM;

	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
	if (!ue_sk->sk) 
		pr_err("kobject_uevent: unable to create netlink socket!\\n");
		kfree(ue_sk);
		return -ENODEV;
	
...

套接字监视接口

//支持SS 监视接口 NETLINK_SOCK_DIAG
static int __net_init diag_net_init(struct net *net)

	struct netlink_kernel_cfg cfg = 
		.groups	= SKNLGRP_MAX,
		.input	= sock_diag_rcv,
		.bind	= sock_diag_bind,
		.flags	= NL_CFG_F_NONROOT_RECV,
	;

	net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
	return net->diag_nlsk == NULL ? -ENOMEM : 0;

demo

内核程序实例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define NETLINK_USER  22
#define USER_MSG    (NETLINK_USER + 1)
#define USER_PORT   50


static struct sock *netlinkfd = NULL;

int send_msg(int8_t *pbuf, uint16_t len)

    struct sk_buff *nl_skb;
    struct nlmsghdr *nlh;

    int ret;
    //创建sk_buffer
    nl_skb = nlmsg_new(len, GFP_ATOMIC);
    if(!nl_skb)
    
        printk("nlmsg_new error\\n");
        return -1;
    

    //设置netlink头
    nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0);
    if(nlh == NULL)
    
        printk("netlink header error\\n");
        nlmsg_free(nl_skb);
        return -1;
    
    //拷贝数据
    memcpy(nlmsg_data(nlh), pbuf, len);

    //单播发送数据
    ret = netlink_unicast(netlinkfd, nl_skb, USER_PORT, MSG_DONTWAIT);

    return ret;



//接收数据
static void recv_cb(struct sk_buff *skb)

    struct nlmsghdr *nlh = NULL;
    void *data = NULL;
    //打印接收数据的长度
    printk("recv_datalen:%u\\n", skb->len);
    if(skb->len >= nlmsg_total_size(0))
    
        
        nlh = nlmsg_hdr(skb);
        //宏 获取数据
        data = NLMSG_DATA(nlh);
        if(data)
        
            
            printk("kernel receive data: %s\\n", (int8_t *)data);
            //将数据发送给用户
            send_msg(data, nlmsg_len(nlh));
        
    
 

//cfg参数 注册了input
struct netlink_kernel_cfg cfg = 

    .input = recv_cb,
;

//初始化
static int __init netlink_init(void)

    //创建socket
    netlinkfd = netlink_kernel_create(&init_net, USER_MSG, &cfg);
    if(!netlinkfd)
    
        printk(KERN_ERR "create a netlink socket error!\\n");
        return -1;
    

    printk("init netlink  ok!\\n");
    return 0;

//退出
static void __exit netlink_exit(void)

    sock_release(netlinkfd->sk_socket);
    printk(KERN_DEBUG "netlink exit\\n!");


module_init(netlink_init);
module_exit(netlink_exit);

MODULE_LICENSE("GPL");  
MODULE_AUTHOR("wy");  
MODULE_DESCRIPTION("netlink");

参考

https://course.0voice.com/v1/course/intro?courseId=2&agentId=0


以上是关于linux 内核中Netlink的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核通信之netlink机制

Linux用户与内核空间交互—netlink

Linux用户与内核空间交互—netlink

在 Linux 内核中使用 netlink 套接字在用户空间应用程序和字符设备之间进行通信时出错

linux 内核与用户空间通信之netlink使用方法

从socket can中断到netlink用户态内核态通信