Netlink通信机制

1、Netlink 机制简介

为了完成内核空间与用户空间通信,Linux提供了基于socket的Netlink通信机制,可以实现内核与用户空间数据的及时交换。Netlink是基于socket的通信机制,由于socket本身的双共性、突发性、不阻塞特点,因此能够很好的满足内核与用户空间小量数据的及时交互,因此在Linux 2.6内核中广泛使用,例如SELinux,Linux系统的防火墙分为内核态的netfilter和用户态的iptables,netfilter与iptables的数据交换就是通过Netlink机制完成。

netlink作为一种用户空间和内核空间通信的机制,它不光为了内核和用户通信,还可以作为IPC机制进行进程间通信。它也用到了sk_buff结构体,和网络套接字一样,更好的事情是它并没 有触及sk_buff里面的标准字段,而仅仅用了一个扩展的cb字段,cb在 sk_buff里面的定义是char cb[40];在netlink模块里面NETLINK_CB宏就是取cb字段的,也就是netlink所用的私有字段,这样的话你就可以用 netlink向任何执行实体传输任何数据了,不限于本机。

Linux操作系统中当CPU处于内核状态时,可以分为有用户上下文的状态和执行硬件、软件中断两种。其中当处于有用户上下文时,由于内核态和用户态的内 存映射机制不同,不可直接将本地变量传给用户态的内存区;处于硬件、软件中断时,无法直接向用户内存区传递数据,代码执行不可中断。针对传统的进程间通信 机制,他们均无法直接在内核态和用户态之间使用,原因如下:

通信方法 无法介于内核态与用户态的原因

管道(不包括命名管道) 局限于父子进程间的通信。

消息队列 在硬、软中断中无法无阻塞地接收数据。

信号量 无法介于内核态和用户态使用。

内存共享 需要信号量辅助,而信号量又无法使用。

套接字 在硬、软中断中无法无阻塞地接收数据。

Netlink相对于其他的通信机制具有以下优点:

(1)使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。

(2)Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。

(3)Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。

(4)Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。

2、源码分析

2.1 linux\netlink.h

2.1.1 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	/* Firewalling hook				*/
#define NETLINK_INET_DIAG	4	/* INET 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
可以根据自己的应用,创建新的通信协议。新协议的定义不能和内核已经定义的冲突,同时不能超过MAX_LINKS这个宏的限定,MAX_LINKS = 32。

2.1.2 netlink 地址格式

struct sockaddr_nl
{
	sa_family_t	nl_family;	/* AF_NETLINK	*/
	unsigned short	nl_pad;		/* zero		*/
	__u32		nl_pid;		/* port ID	*/
       	__u32		nl_groups;	/* multicast groups mask */
};
nl_pad 当前没有使用,因此要总是设置为 0,

nl_pid就是一个约定的通信端口,用户态使用的时候需要用一个非0的数字,一般来说可以直接采用上层应用的进程ID(未必是进程 ID,它只是用于区分不同的接收者或发送者的一个标识,用户可以根据自己需要设置该字段,只要是系统中不冲突的一个数字即可)。对于内核的地址,该值必须用0,即上层通过sendto向内核发送netlink消息,peer addr中nl_pid必须填写0。

nl_groups用于一个消息同时分发给不同的接收者,是一种组播应用。绑定时用于指定绑定者所要加入的多播组,这样绑定者就可以接收多播消息,发送 消息时可以用于指定多播组,这样就可以将消息发给多个接收者。这里nl_groups 为32位的无符号整形,所以可以指定32个多播组,每个进程可以加入多个多播组, 因为多播组是通过“或”操作,如果设置为 0,表示调用者不加入任何多播组。

本质上,nl_pid就是netlink的通信地址。除了通信地址,netlink还提供“协议”来标示通信实体,在创建socket的时候,需要指定netlink的通信协议号。每个协议号代表一种“应用”,上层可以用内核已经定义的协议和内核进行通信,获得内核已经提供的信息。netlink采用“协议号 + 通信端口”的方式构建自己的地址体系。

2.1.3 netlink 头部信息

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 */
};
nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小。

nlmsg_type 用于应用内部定义消息的类型,它对netlink 内核实现是透明的,因此大部分情况下设置为 0。

nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID。其中pid是Netlink分配的一个ID,不同的值代表不同的socket通道,默认的值是进程PID。在某些情况下,这个值被设置为0,比如消息来自内核空间,或者想要Netlink来设置这个值。

nlmsg_flags 用于设置消息标志,可用的标志包括:

/* Flags values */
#define NLM_F_REQUEST		1	/* It is request message. 	*/
#define NLM_F_MULTI		2	/* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK		4	/* Reply with ack, with zero or error code */
#define NLM_F_ECHO		8	/* Echo this request 		*/

/* Modifiers to GET request */
#define NLM_F_ROOT	0x100	/* specify tree	root	*/
#define NLM_F_MATCH	0x200	/* return all matching	*/
#define NLM_F_ATOMIC	0x400	/* atomic GET		*/
#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)

/* Modifiers to NEW request */
#define NLM_F_REPLACE	0x100	/* Override existing		*/
#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
#define NLM_F_APPEND	0x800	/* Add to end of list		*/

2.1.4 netlink 在skb_buff中的对应信息

struct netlink_skb_parms
{
	struct ucred		creds;		/* Skb credentials	*/
	__u32			pid;
	__u32			dst_group;
	kernel_cap_t		eff_cap;
	__u32			loginuid;	/* Login (audit) uid */
	__u32			sessionid;	/* Session id (audit) */
	__u32			sid;		/* SELinux security id */
};

内核态发送数据的时候,在skb_buff存储对应的发送地址等信息,供用户态需要时使用。

2.1.5 netlink 相关的宏

#define NLMSG_ALIGNTO	4
#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_ALIGN(NLMSG_HDRLEN))
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))	//获取nlmsghdr头部之后的数据
#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)))
2.2 net\netlink\af_netlink.c

netlink的实现方法主要在 /net/netlink/af_netlink.c文件中。

2.2.1 netlink 套接字结构

struct netlink_sock {
	/* struct sock has to be the first member of netlink_sock */
	struct sock		sk;
	u32			pid;				//内核态pid
	u32			dst_pid;
	u32			dst_group;
	u32			flags;
	u32			subscriptions;
	u32			ngroups;			//组数
	unsigned long		*groups;		//组号
	unsigned long		state;
	wait_queue_head_t	wait;	//等待队列
	struct netlink_callback	*cb;
	struct mutex		*cb_mutex;
	struct mutex		cb_def_mutex;
	void			(*netlink_rcv)(struct sk_buff *skb);	//内核态接收用户态信息后的处理函数
	struct module		*module;
};
Groups : 对于每一个netlink协议类型,可以使用多播的概念,最多可以有 32个多播组,每一个多播组用一个位表示,netlink 的多播特性使得发送消息给同一个组仅需要一次系统调用,因而对于需要多播消息的应用而言,大大地降低了系统调用的次数。

注意:用户态可以使用标准的socket APIs, socket(), bind(), sendmsg(), recvmsg() 和 close() 等函数就能很容易地使用 netlink socket,在用户空间可以直接通过socket函数来使用Netlink通信,区别是:

(1)netlink有自己的地址;

创建socket,socket(PF_NETLINK, SOCK_DGRAM, NETLINK_XXX);第一个参数必须是PF_NETLINK或者AF_NETLINK(Linux中实际为一个东西,它表示要使用netlink),第二个参数用SOCK_DGRAM和SOCK_RAW都没问题,第三个参数就是netlink的协议号。

(2)netlink接收到的消息带一个netlink自己的消息头;

用户态发送、接收的时候,需要在自己附带的消息前面要加上一个netlink的消息头:

struct tag_rcv_buf  
    {  
            struct nlmsghdr hdr;            //netlink的消息头  
            netlink_notify_s my_msg;        //通信实体消息  
    }st_snd_buf;
2.2.2 创建netlink socket
struct sock *
netlink_kernel_create(struct net *net, int unit, unsigned int groups,
		      void (*input)(struct sk_buff *skb),
		      struct mutex *cb_mutex, struct module *module)
{
	struct socket *sock;
	struct sock *sk;
	struct netlink_sock *nlk;
	unsigned long *listeners = NULL;

	BUG_ON(!nl_table);

	if (unit < 0 || unit >= MAX_LINKS)
		return NULL;

	if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))	//创建socket 结构
		return NULL;

	/*
	 * We have to just have a reference on the net from sk, but don't
	 * get_net it. Besides, we cannot get and then put the net here.
	 * So we create one inside init_net and the move it to net.
	 */

	if (__netlink_create(&init_net, sock, cb_mutex, unit) < 0)	//创建sock 结构
		goto out_sock_release_nosk;

	sk = sock->sk;
	sk_change_net(sk, net);

	if (groups < 32)
		groups = 32;

	listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
			    GFP_KERNEL);
	if (!listeners)
		goto out_sock_release;

	sk->sk_data_ready = netlink_data_ready;
	if (input)
		nlk_sk(sk)->netlink_rcv = input;	//设置内核接收Netlink消息的函数

	if (netlink_insert(sk, net, 0))
		goto out_sock_release;

	nlk = nlk_sk(sk);	//取得sock嵌入的netlink_sock结构体
	nlk->flags |= NETLINK_KERNEL_SOCKET;

	netlink_table_grab();
	if (!nl_table[unit].registered) {
		nl_table[unit].groups = groups;
		nl_table[unit].listeners = listeners;
		nl_table[unit].cb_mutex = cb_mutex;
		nl_table[unit].module = module;
		nl_table[unit].registered = 1;		//更新netlink_table结构体信息,每种协议对应一个netlink_table结构
	} else {
		kfree(listeners);
		nl_table[unit].registered++;
	}
	netlink_table_ungrab();
	return sk;

out_sock_release:
	kfree(listeners);
	netlink_kernel_release(sk);
	return NULL;

out_sock_release_nosk:
	sock_release(sock);
	return NULL;
}
内核态创建netlink socket的时候,调用 netlink_kernel_create() 函数。

2.2.3 内核态发送数据

内核态向用户态发送数据的时候,调用netlink_unicast() 函数。

int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
		    u32 pid, int nonblock)
{
	struct sock *sk;
	int err;
	long timeo;

	skb = netlink_trim(skb, gfp_any());

	timeo = sock_sndtimeo(ssk, nonblock);
retry:
	sk = netlink_getsockbypid(ssk, pid); //找到内核中对应该pid和SOCK_DGRAM协议的sock结构,挂载在哈希表中
	if (IS_ERR(sk)) {
		kfree_skb(skb);
		return PTR_ERR(sk);
	}
	if (netlink_is_kernel(sk))	//若另一端也为内核端,调用其netlink_sock的input函数接受
		return netlink_unicast_kernel(sk, skb);
	if (sk_filter(sk, skb)) {
		err = skb->len;
		kfree_skb(skb);
		sock_put(sk);
		return err;
	}

	err = netlink_attachskb(sk, skb, &timeo, ssk);
	if (err == 1)
		goto retry;
	if (err)
		return err;

	return netlink_sendskb(sk, skb);	//挂载到用户态sock结构的receive_queue队列中, 然后调用sk_data_ready函数,唤醒用户态睡眠函数
}
2.2.4 内核态接收数据

在netlink_kernel_create函数中,可以设置接收函数。

2.2.5 用户态发送数据

static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
			   struct msghdr *msg, size_t len)
{
	struct sock_iocb *siocb = kiocb_to_siocb(kiocb);
	struct sock *sk = sock->sk;
	struct netlink_sock *nlk = nlk_sk(sk);
	struct sockaddr_nl *addr = msg->msg_name;	//目的地址信息
	u32 dst_pid;
	u32 dst_group;
	struct sk_buff *skb;
	int err;
	struct scm_cookie scm;

	if (msg->msg_flags&MSG_OOB)
		return -EOPNOTSUPP;

	if (NULL == siocb->scm)
		siocb->scm = &scm;
	err = scm_send(sock, msg, siocb->scm);
	if (err < 0)
		return err;

	if (msg->msg_namelen) {
		if (addr->nl_family != AF_NETLINK)
			return -EINVAL;
		dst_pid = addr->nl_pid;
		dst_group = ffs(addr->nl_groups);
		if ((dst_group || dst_pid) &&
		    !netlink_capable(sock, NL_NONROOT_SEND))
			return -EPERM;
	} else {
		dst_pid = nlk->dst_pid;
		dst_group = nlk->dst_group;
	}

	if (!nlk->pid) {
		err = netlink_autobind(sock);
		if (err)
			goto out;
	}

	err = -EMSGSIZE;
	if (len > sk->sk_sndbuf - 32)
		goto out;
	err = -ENOBUFS;
	skb = alloc_skb(len, GFP_KERNEL);		//分配一个sk_buff结构,目的是将msghdr结构转化为sk_buff结构
	if (skb == NULL)
		goto out;

	NETLINK_CB(skb).pid	= nlk->pid;	//本地的pid信息
	NETLINK_CB(skb).dst_group = dst_group;
	NETLINK_CB(skb).loginuid = audit_get_loginuid(current);
	NETLINK_CB(skb).sessionid = audit_get_sessionid(current);
	security_task_getsecid(current, &(NETLINK_CB(skb).sid));
	memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));

	/* What can I do? Netlink is asynchronous, so that
	   we will have to save current capabilities to
	   check them, when this message will be delivered
	   to corresponding kernel module.   --ANK (980802)
	 */

	err = -EFAULT;
	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {	//将数据拷贝到sk_buff中
		kfree_skb(skb);
		goto out;
	}

	err = security_netlink_send(sk, skb);
	if (err) {
		kfree_skb(skb);
		goto out;
	}

	if (dst_group) {
		atomic_inc(&skb->users);
		netlink_broadcast(sk, skb, dst_pid, dst_group, GFP_KERNEL);
	}
	err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT);

out:
	return err;
}
2.2.6 用户态接收数据
static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
			   struct msghdr *msg, size_t len,
			   int flags)
{
	struct sock_iocb *siocb = kiocb_to_siocb(kiocb);
	struct scm_cookie scm;
	struct sock *sk = sock->sk;
	struct netlink_sock *nlk = nlk_sk(sk);
	int noblock = flags&MSG_DONTWAIT;
	size_t copied;
	struct sk_buff *skb, *data_skb;
	int err;

	if (flags&MSG_OOB)
		return -EOPNOTSUPP;

	copied = 0;

	skb = skb_recv_datagram(sk, flags, noblock, &err);	//从等待队列中接受一个数据包
	if (skb == NULL)
		goto out;

	data_skb = skb;

#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
	if (unlikely(skb_shinfo(skb)->frag_list)) {
		/*
		 * If this skb has a frag_list, then here that means that we
		 * will have to use the frag_list skb's data for compat tasks
		 * and the regular skb's data for normal (non-compat) tasks.
		 *
		 * If we need to send the compat skb, assign it to the
		 * 'data_skb' variable so that it will be used below for data
		 * copying. We keep 'skb' for everything else, including
		 * freeing both later.
		 */
		if (flags & MSG_CMSG_COMPAT)
			data_skb = skb_shinfo(skb)->frag_list;
	}
#endif

	copied = data_skb->len;	//接收的数据包长度
	if (len < copied) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}

	skb_reset_transport_header(data_skb);	//将数据包从sk_buff中拷贝到msghdr结构中
	err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied);

	if (msg->msg_name) {	//如果要求得到kernel的struct netlink_nl结构
		struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name;
		addr->nl_family = AF_NETLINK;
		addr->nl_pad    = 0;
		addr->nl_pid	= NETLINK_CB(skb).pid;	//内核态pid
		addr->nl_groups	= netlink_group_mask(NETLINK_CB(skb).dst_group);	//发送分组
		msg->msg_namelen = sizeof(*addr);
	}

	if (nlk->flags & NETLINK_RECV_PKTINFO)
		netlink_cmsg_recv_pktinfo(msg, skb);

	if (NULL == siocb->scm) {
		memset(&scm, 0, sizeof(scm));
		siocb->scm = &scm;
	}
	siocb->scm->creds = *NETLINK_CREDS(skb);
	if (flags & MSG_TRUNC)
		copied = data_skb->len;

	skb_free_datagram(sk, skb);	//释放sk_buff数据包信息

	if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
		netlink_dump(sk);

	scm_recv(sock, msg, siocb->scm, flags);
out:
	netlink_rcv_wake(sk);	//接受唤醒
	return err ? : copied;
}
内核态将数据发送到用户态对应的sock的sk_receive_queue中,并且唤醒睡眠的进程。

3、测试

3.1 用户态与内核态通信

3.2.1 内核代码实现

#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/types.h>
#include <net/sock.h>
#include <net/netlink.h>

#define NETLINK_TEST 25
#define MAX_MSGSIZE 1024
int stringlength(char *s);
void sendnlmsg(char * message);
int pid;
int err;
struct sock *nl_sk = NULL;
int flag = 0;

void sendnlmsg(char *message)
{
    struct sk_buff *skb_1;
    struct nlmsghdr *nlh;
    int len = NLMSG_SPACE(MAX_MSGSIZE);
int slen = 0;
    if(!message || !nl_sk)
    {
        return ;
    }
    skb_1 = alloc_skb(len,GFP_KERNEL);
    if(!skb_1)
    {
        printk(KERN_ERR "my_net_link:alloc_skb_1 error\n");
    }
    slen = stringlength(message);
    nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);

    NETLINK_CB(skb_1).pid = 0;
    NETLINK_CB(skb_1).dst_group = 0;

    message[slen]= '\0';
    memcpy(NLMSG_DATA(nlh),message,slen+1);
    printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));

    netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);
}

int stringlength(char *s)
{
    int slen = 0;
    for(; *s; s++){
        slen++;
    }
    return slen;
}

void nl_data_ready(struct sk_buff *__skb)
 {
     struct sk_buff *skb;
     struct nlmsghdr *nlh;
     char str[100];
     struct completion cmpl;
     int i=10;
     skb = skb_get (__skb);
     if(skb->len >= NLMSG_SPACE(0))
     {
         nlh = nlmsg_hdr(skb);

         memcpy(str, NLMSG_DATA(nlh), sizeof(str));
         printk("Message received:%s\n",str) ;
         pid = nlh->nlmsg_pid;
         while(i--)
         {
                init_completion(&cmpl);
                wait_for_completion_timeout(&cmpl,3 * HZ);
                sendnlmsg("I am from kernel!");
         }
         flag = 1;
         kfree_skb(skb);
     }

 }
int netlink_init(void)
{

    nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0,
                                 nl_data_ready, NULL, THIS_MODULE);

    if(!nl_sk){
        printk(KERN_ERR "my_net_link: create netlink socket error.\n");
        return 1;
    }

    printk("my_net_link: create netlink socket ok.\n");

    return 0;
}

static void netlink_exit(void)
{
    if(nl_sk != NULL){
        sock_release(nl_sk->sk_socket);
    }

    printk("my_net_link: module exited.\n");
}

module_init(netlink_init);
module_exit(netlink_exit);

MODULE_AUTHOR("LRQ");
MODULE_LICENSE("GPL");
3.2.2 用户态代码实现
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>

#define NETLINK_TEST 25
#define MAX_PAYLOAD 1024 // maximum payload size

int main(int argc, char* argv[])
{
    int state;
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    struct msghdr msg;
    int sock_fd, retval;
    int state_smg = 0;
    // Create a socket

    sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
    if(sock_fd == -1){
        printf("error getting socket: %s", strerror(errno));
        return -1;
}
// To prepare binding

    memset(&msg,0,sizeof(msg));
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); // self pid

    src_addr.nl_groups = 0; // multi cast

    retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
    if(retval < 0){
        printf("bind failed: %s", strerror(errno));
        close(sock_fd);
        return -1;
    }

    // To prepare recvmsg

nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    if(!nlh){
        printf("malloc nlmsghdr error!\n");
        close(sock_fd);
        return -1;
    }

    memset(&dest_addr,0,sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;
    dest_addr.nl_groups = 0;

    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_flags = 0;
    strcpy(NLMSG_DATA(nlh),"Hello!");

    iov.iov_base = (void *)nlh;
    iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
// iov.iov_len = nlh->nlmsg_len;

    memset(&msg, 0, sizeof(msg));

    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    printf("state_smg\n");
    state_smg = sendmsg(sock_fd,&msg,0);

    if(state_smg == -1)
    {
        printf("get error sendmsg = %s\n",strerror(errno));
    }

    memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
    printf("waiting received!\n");
// Read message from kernel
    while(1){
        printf("In while recvmsg\n");
        state = recvmsg(sock_fd, &msg, 0);
        if(state<0)
        {
            printf("state<1");
        }
        printf("In while\n");
        printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
    }

    close(sock_fd);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值